chiark / gitweb /
Start reimplenting search in Disobedience choose tabs. Results are
authorRichard Kettlewell <rjk@greenend.org.uk>
Thu, 12 Jun 2008 16:24:47 +0000 (17:24 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Thu, 12 Jun 2008 16:24:47 +0000 (17:24 +0100)
highlighted correctly but only if they are already visible.  Focus is
still a bit wonky and ^A doesn't reliably go to the right place.

disobedience/Makefile.am
disobedience/choose-search.c [new file with mode: 0644]
disobedience/choose.c
disobedience/choose.h

index 90a0bd6d0b8876c38190f693854538695db80f27..08e465c8660a3c99a91a37df361876a749b8f5c3 100644 (file)
@@ -27,9 +27,9 @@ PNGS:=$(shell export LC_COLLATE=C;echo ${top_srcdir}/images/*.png)
 
 disobedience_SOURCES=disobedience.h disobedience.c client.c queue.c    \
        recent.c added.c queue-generic.c queue-generic.h queue-menu.c   \
-       choose.c choose-menu.c popup.c misc.c control.c properties.c    \
-       menu.c log.c progress.c login.c rtp.c help.c ../lib/memgc.c     \
-       settings.c users.c lookup.c
+       choose.c choose-menu.c choose-search.c popup.c misc.c           \
+       control.c properties.c menu.c log.c progress.c login.c rtp.c    \
+       help.c ../lib/memgc.c settings.c users.c lookup.c
 disobedience_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBGC) $(LIBGCRYPT) \
        $(LIBASOUND) $(COREAUDIO) $(LIBDB)
 disobedience_LDFLAGS=$(GTK_LIBS)
diff --git a/disobedience/choose-search.c b/disobedience/choose-search.c
new file mode 100644 (file)
index 0000000..ed76532
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2008 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include "disobedience.h"
+#include "choose.h"
+
+static GtkWidget *choose_search_entry;
+static GtkWidget *choose_next;
+static GtkWidget *choose_prev;
+static GtkWidget *choose_clear;
+
+/** @brief True if a search command is in flight */
+static int choose_searching;
+
+/** @brief True if in-flight search is now known to be obsolete */
+static int choose_search_obsolete;
+
+/** @brief Hash of all search result */
+static hash *choose_search_hash;
+
+static void choose_search_entry_changed(GtkEditable *editable,
+                                        gpointer user_data);
+
+int choose_is_search_result(const char *track) {
+  return choose_search_hash && hash_find(choose_search_hash, track);
+}
+
+/** @brief Called when the cancel search button is clicked */
+static void choose_clear_clicked(GtkButton attribute((unused)) *button,
+                                 gpointer attribute((unused)) userdata) {
+  gtk_entry_set_text(GTK_ENTRY(choose_search_entry), "");
+  /* The changed signal will do the rest of the work for us */
+}
+
+/** @brief Called with search results */
+static void choose_search_completed(void attribute((unused)) *v,
+                                    const char *error,
+                                    int nvec, char **vec) {
+  if(error) {
+    popup_protocol_error(0, error);
+    return;
+  }
+  choose_searching = 0;
+  /* If the search was obsoleted initiate another one */
+  if(choose_search_obsolete) {
+    choose_search_entry_changed(0, 0);
+    return;
+  }
+  //fprintf(stderr, "%d search results\n", nvec);
+  choose_search_hash = hash_new(1);
+  for(int n = 0; n < nvec; ++n)
+    hash_add(choose_search_hash, vec[n], "", HASH_INSERT);
+  /* TODO arrange visibility of all search results */
+  event_raise("search-results-changed", 0);
+}
+
+/** @brief Called when the search entry changes */
+static void choose_search_entry_changed
+    (GtkEditable attribute((unused)) *editable,
+     gpointer attribute((unused)) user_data) {
+  /* If a search is in flight don't initiate a new one until it comes back */
+  if(choose_searching) {
+    choose_search_obsolete = 1;
+    return;
+  }
+  char *terms = xstrdup(gtk_entry_get_text(GTK_ENTRY(choose_search_entry)));
+  /* Strip leading and trailing space */
+  while(*terms == ' ') ++terms;
+  char *e = terms + strlen(terms);
+  while(e > terms && e[-1] == ' ') --e;
+  *e = 0;
+  if(!*terms) {
+    /* Nothing to search for.  Fake a completion call. */
+    choose_search_completed(0, 0, 0, 0);
+    return;
+  }
+  if(disorder_eclient_search(client, choose_search_completed, terms, 0)) {
+    /* Bad search terms.  Fake a completion call. */
+    choose_search_completed(0, 0, 0, 0);
+    return;
+  }
+  choose_searching = 1;
+}
+
+static void choose_next_clicked(GtkButton attribute((unused)) *button,
+                                gpointer attribute((unused)) userdata) {
+  fprintf(stderr, "next\n");            /* TODO */
+}
+
+static void choose_prev_clicked(GtkButton attribute((unused)) *button,
+                                gpointer attribute((unused)) userdata) {
+  fprintf(stderr, "prev\n");            /* TODO */
+}
+
+/** @brief Create the search widget */
+GtkWidget *choose_search_widget(void) {
+
+  /* Text entry box for search terms */
+  choose_search_entry = gtk_entry_new();
+  gtk_widget_set_style(choose_search_entry, tool_style);
+  g_signal_connect(choose_search_entry, "changed",
+                   G_CALLBACK(choose_search_entry_changed), 0);
+  gtk_tooltips_set_tip(tips, choose_search_entry,
+                       "Enter search terms here; search is automatic", "");
+
+  /* Cancel button to clear the search */
+  choose_clear = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+  gtk_widget_set_style(choose_clear, tool_style);
+  g_signal_connect(G_OBJECT(choose_clear), "clicked",
+                   G_CALLBACK(choose_clear_clicked), 0);
+  gtk_tooltips_set_tip(tips, choose_clear, "Clear search terms", "");
+
+  /* Up and down buttons to find previous/next results; initially they are not
+   * usable as there are no search results. */
+  choose_prev = iconbutton("up.png", "Previous search result");
+  g_signal_connect(G_OBJECT(choose_prev), "clicked",
+                   G_CALLBACK(choose_prev_clicked), 0);
+  gtk_widget_set_style(choose_prev, tool_style);
+  gtk_widget_set_sensitive(choose_prev, 0);
+  choose_next = iconbutton("down.png", "Next search result");
+  g_signal_connect(G_OBJECT(choose_next), "clicked",
+                   G_CALLBACK(choose_next_clicked), 0);
+  gtk_widget_set_style(choose_next, tool_style);
+  gtk_widget_set_sensitive(choose_next, 0);
+  
+  /* Pack the search tools button together on a line */
+  GtkWidget *hbox = gtk_hbox_new(FALSE/*homogeneous*/, 1/*spacing*/);
+  gtk_box_pack_start(GTK_BOX(hbox), choose_search_entry,
+                     TRUE/*expand*/, TRUE/*fill*/, 0/*padding*/);
+  gtk_box_pack_start(GTK_BOX(hbox), choose_prev,
+                     FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/);
+  gtk_box_pack_start(GTK_BOX(hbox), choose_next,
+                     FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/);
+  gtk_box_pack_start(GTK_BOX(hbox), choose_clear,
+                     FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/);
+
+  return hbox;
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 31535b3c00b9fed16187e7a355c86cbce3c81ae8..ce3f754d6b36032a8da326b6603f9e16cd5f868e 100644 (file)
@@ -123,6 +123,16 @@ static gboolean choose_set_state_callback(GtkTreeModel attribute((unused)) *mode
                        LENGTH_COLUMN, length,
                        STATE_COLUMN, queued(track),
                        -1);
+    if(choose_is_search_result(track))
+      gtk_tree_store_set(choose_store, it,
+                         BG_COLUMN, "yellow",
+                         FG_COLUMN, "black",
+                         -1);
+    else
+      gtk_tree_store_set(choose_store, it,
+                         BG_COLUMN, (char *)0,
+                         FG_COLUMN, (char *)0,
+                         -1);
   }
   return FALSE;                         /* continue walking */
 }
@@ -335,6 +345,8 @@ GtkWidget *choose_widget(void) {
                                     G_TYPE_STRING,
                                     G_TYPE_BOOLEAN,
                                     G_TYPE_STRING,
+                                    G_TYPE_STRING,
+                                    G_TYPE_STRING,
                                     G_TYPE_STRING);
 
   /* Create the view */
@@ -349,6 +361,8 @@ GtkWidget *choose_widget(void) {
       ("Track",
        r,
        "text", NAME_COLUMN,
+       "background", BG_COLUMN,
+       "foreground", FG_COLUMN,
        (char *)0);
     gtk_tree_view_column_set_resizable(c, TRUE);
     gtk_tree_view_column_set_reorderable(c, TRUE);
@@ -397,15 +411,24 @@ GtkWidget *choose_widget(void) {
 
   event_register("queue-list-changed", choose_set_state, 0);
   event_register("playing-track-changed", choose_set_state, 0);
+  event_register("search-results-changed", choose_set_state, 0);
   
   /* Fill the root */
   disorder_eclient_files(client, choose_files_completed, "", NULL, NULL); 
   disorder_eclient_dirs(client, choose_dirs_completed, "", NULL, NULL); 
-  
+
   /* Make the widget scrollable */
   GtkWidget *scrolled = scroll_widget(choose_view);
-  g_object_set_data(G_OBJECT(scrolled), "type", (void *)&choose_tabtype);
-  return scrolled;
+
+  /* Pack vertically with the search widget */
+  GtkWidget *vbox = gtk_vbox_new(FALSE/*homogenous*/, 1/*spacing*/);
+  gtk_box_pack_start(GTK_BOX(vbox), scrolled,
+                     TRUE/*expand*/, TRUE/*fill*/, 0/*padding*/);
+  gtk_box_pack_end(GTK_BOX(vbox), choose_search_widget(),
+                   FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/);
+  
+  g_object_set_data(G_OBJECT(vbox), "type", (void *)&choose_tabtype);
+  return vbox;
 }
 
 /*
index 16414a8c02299efba2b9c3dcc76fa9d293b2361c..dfd2a7380a909339ca81870617974a88a4987618 100644 (file)
@@ -30,6 +30,8 @@ enum {
   ISFILE_COLUMN,                /* TRUE for a track, FALSE for a directory */
   TRACK_COLUMN,                 /* Full track name, "" for placeholder */
   SORT_COLUMN,                  /* Sort key */
+  BG_COLUMN,                    /* Background color */
+  FG_COLUMN,                    /* Foreground color */
 
   CHOOSE_COLUMNS                /* column count */
 };
@@ -51,6 +53,8 @@ char *choose_get_sort(GtkTreeIter *iter);
 int choose_is_file(GtkTreeIter *iter);
 int choose_is_dir(GtkTreeIter *iter);
 int choose_is_placeholder(GtkTreeIter *iter);
+GtkWidget *choose_search_widget(void);
+int choose_is_search_result(const char *track);
 
 #endif /* CHOOSE_H */