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 */