chiark / gitweb /
Start reimplenting search in Disobedience choose tabs. Results are
[disorder] / disobedience / choose-search.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2008 Richard Kettlewell
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20 #include "disobedience.h"
21 #include "choose.h"
22
23 static GtkWidget *choose_search_entry;
24 static GtkWidget *choose_next;
25 static GtkWidget *choose_prev;
26 static GtkWidget *choose_clear;
27
28 /** @brief True if a search command is in flight */
29 static int choose_searching;
30
31 /** @brief True if in-flight search is now known to be obsolete */
32 static int choose_search_obsolete;
33
34 /** @brief Hash of all search result */
35 static hash *choose_search_hash;
36
37 static void choose_search_entry_changed(GtkEditable *editable,
38                                         gpointer user_data);
39
40 int choose_is_search_result(const char *track) {
41   return choose_search_hash && hash_find(choose_search_hash, track);
42 }
43
44 /** @brief Called when the cancel search button is clicked */
45 static void choose_clear_clicked(GtkButton attribute((unused)) *button,
46                                  gpointer attribute((unused)) userdata) {
47   gtk_entry_set_text(GTK_ENTRY(choose_search_entry), "");
48   /* The changed signal will do the rest of the work for us */
49 }
50
51 /** @brief Called with search results */
52 static void choose_search_completed(void attribute((unused)) *v,
53                                     const char *error,
54                                     int nvec, char **vec) {
55   if(error) {
56     popup_protocol_error(0, error);
57     return;
58   }
59   choose_searching = 0;
60   /* If the search was obsoleted initiate another one */
61   if(choose_search_obsolete) {
62     choose_search_entry_changed(0, 0);
63     return;
64   }
65   //fprintf(stderr, "%d search results\n", nvec);
66   choose_search_hash = hash_new(1);
67   for(int n = 0; n < nvec; ++n)
68     hash_add(choose_search_hash, vec[n], "", HASH_INSERT);
69   /* TODO arrange visibility of all search results */
70   event_raise("search-results-changed", 0);
71 }
72
73 /** @brief Called when the search entry changes */
74 static void choose_search_entry_changed
75     (GtkEditable attribute((unused)) *editable,
76      gpointer attribute((unused)) user_data) {
77   /* If a search is in flight don't initiate a new one until it comes back */
78   if(choose_searching) {
79     choose_search_obsolete = 1;
80     return;
81   }
82   char *terms = xstrdup(gtk_entry_get_text(GTK_ENTRY(choose_search_entry)));
83   /* Strip leading and trailing space */
84   while(*terms == ' ') ++terms;
85   char *e = terms + strlen(terms);
86   while(e > terms && e[-1] == ' ') --e;
87   *e = 0;
88   if(!*terms) {
89     /* Nothing to search for.  Fake a completion call. */
90     choose_search_completed(0, 0, 0, 0);
91     return;
92   }
93   if(disorder_eclient_search(client, choose_search_completed, terms, 0)) {
94     /* Bad search terms.  Fake a completion call. */
95     choose_search_completed(0, 0, 0, 0);
96     return;
97   }
98   choose_searching = 1;
99 }
100
101 static void choose_next_clicked(GtkButton attribute((unused)) *button,
102                                 gpointer attribute((unused)) userdata) {
103   fprintf(stderr, "next\n");            /* TODO */
104 }
105
106 static void choose_prev_clicked(GtkButton attribute((unused)) *button,
107                                 gpointer attribute((unused)) userdata) {
108   fprintf(stderr, "prev\n");            /* TODO */
109 }
110
111 /** @brief Create the search widget */
112 GtkWidget *choose_search_widget(void) {
113
114   /* Text entry box for search terms */
115   choose_search_entry = gtk_entry_new();
116   gtk_widget_set_style(choose_search_entry, tool_style);
117   g_signal_connect(choose_search_entry, "changed",
118                    G_CALLBACK(choose_search_entry_changed), 0);
119   gtk_tooltips_set_tip(tips, choose_search_entry,
120                        "Enter search terms here; search is automatic", "");
121
122   /* Cancel button to clear the search */
123   choose_clear = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
124   gtk_widget_set_style(choose_clear, tool_style);
125   g_signal_connect(G_OBJECT(choose_clear), "clicked",
126                    G_CALLBACK(choose_clear_clicked), 0);
127   gtk_tooltips_set_tip(tips, choose_clear, "Clear search terms", "");
128
129   /* Up and down buttons to find previous/next results; initially they are not
130    * usable as there are no search results. */
131   choose_prev = iconbutton("up.png", "Previous search result");
132   g_signal_connect(G_OBJECT(choose_prev), "clicked",
133                    G_CALLBACK(choose_prev_clicked), 0);
134   gtk_widget_set_style(choose_prev, tool_style);
135   gtk_widget_set_sensitive(choose_prev, 0);
136   choose_next = iconbutton("down.png", "Next search result");
137   g_signal_connect(G_OBJECT(choose_next), "clicked",
138                    G_CALLBACK(choose_next_clicked), 0);
139   gtk_widget_set_style(choose_next, tool_style);
140   gtk_widget_set_sensitive(choose_next, 0);
141   
142   /* Pack the search tools button together on a line */
143   GtkWidget *hbox = gtk_hbox_new(FALSE/*homogeneous*/, 1/*spacing*/);
144   gtk_box_pack_start(GTK_BOX(hbox), choose_search_entry,
145                      TRUE/*expand*/, TRUE/*fill*/, 0/*padding*/);
146   gtk_box_pack_start(GTK_BOX(hbox), choose_prev,
147                      FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/);
148   gtk_box_pack_start(GTK_BOX(hbox), choose_next,
149                      FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/);
150   gtk_box_pack_start(GTK_BOX(hbox), choose_clear,
151                      FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/);
152
153   return hbox;
154 }
155
156 /*
157 Local Variables:
158 c-basic-offset:2
159 comment-column:40
160 fill-column:79
161 indent-tabs-mode:nil
162 End:
163 */