summaryrefslogtreecommitdiff
path: root/src/quick_search.c
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-26 11:23:00 +0100
committerSimeon Simeonov2018-02-26 11:23:00 +0100
commit0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch)
treeaf916a30553c78ce9d4f2d8658656a175c5b6921 /src/quick_search.c
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'src/quick_search.c')
-rw-r--r--src/quick_search.c447
1 files changed, 447 insertions, 0 deletions
diff --git a/src/quick_search.c b/src/quick_search.c
new file mode 100644
index 0000000..c4a3ca8
--- /dev/null
+++ b/src/quick_search.c
@@ -0,0 +1,447 @@
1/*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2014 Hiroyuki Yamamoto
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,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU 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, USA.
18 */
19
20#include "defs.h"
21
22#include <glib.h>
23#include <glib/gi18n.h>
24#include <gdk/gdkkeysyms.h>
25#include <gtk/gtkwidget.h>
26#include <gtk/gtkhbox.h>
27#include <gtk/gtkvbox.h>
28#include <gtk/gtkoptionmenu.h>
29#include <gtk/gtkimage.h>
30#include <gtk/gtkentry.h>
31
32#include "summaryview.h"
33#include "quick_search.h"
34#include "filter.h"
35#include "procheader.h"
36#include "menu.h"
37#include "addressbook.h"
38
39static const struct {
40 QSearchCondType type;
41 FilterCondType ftype;
42} qsearch_cond_types[] = {
43 {QS_ALL, -1},
44 {QS_UNREAD, FLT_COND_UNREAD},
45 {QS_MARK, FLT_COND_MARK},
46 {QS_CLABEL, FLT_COND_COLOR_LABEL},
47 {QS_MIME, FLT_COND_MIME},
48 {QS_W1DAY, -1},
49 {QS_LAST5, -1},
50 {QS_LAST7, -1},
51 {QS_IN_ADDRESSBOOK, -1},
52 {QS_LAST30, -1}
53};
54
55static GdkColor dim_color = {0, COLOR_DIM, COLOR_DIM, COLOR_DIM};
56
57static void menu_activated (GtkWidget *menuitem,
58 QuickSearch *qsearch);
59static gboolean entry_focus_in (GtkWidget *entry,
60 GdkEventFocus *event,
61 QuickSearch *qsearch);
62static gboolean entry_focus_out (GtkWidget *entry,
63 GdkEventFocus *event,
64 QuickSearch *qsearch);
65static void entry_changed (GtkWidget *entry,
66 QuickSearch *qsearch);
67static void entry_activated (GtkWidget *entry,
68 QuickSearch *qsearch);
69static gboolean entry_key_pressed (GtkWidget *treeview,
70 GdkEventKey *event,
71 QuickSearch *qsearch);
72static void clear_clicked (GtkWidget *button,
73 QuickSearch *qsearch);
74
75
76QuickSearch *quick_search_create(SummaryView *summaryview)
77{
78 QuickSearch *qsearch;
79 GtkWidget *hbox;
80 GtkWidget *optmenu;
81 GtkWidget *menu;
82 GtkWidget *menuitem;
83 GtkWidget *hbox2;
84 GtkWidget *label;
85 GtkWidget *entry;
86 GtkTooltips *tip;
87 GtkWidget *vbox;
88 GtkWidget *clear_btn;
89 GtkWidget *image;
90 GtkWidget *status_label;
91
92 qsearch = g_new0(QuickSearch, 1);
93
94 hbox = gtk_hbox_new(FALSE, 0);
95 gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
96
97 optmenu = gtk_option_menu_new();
98 gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
99
100#define COND_MENUITEM_ADD(str, action) \
101{ \
102 MENUITEM_ADD(menu, menuitem, str, action); \
103 g_signal_connect(G_OBJECT(menuitem), "activate", \
104 G_CALLBACK(menu_activated), qsearch); \
105}
106
107 menu = gtk_menu_new();
108 COND_MENUITEM_ADD(_("All"), QS_ALL);
109 COND_MENUITEM_ADD(_("Unread"), QS_UNREAD);
110 COND_MENUITEM_ADD(_("Marked"), QS_MARK);
111 COND_MENUITEM_ADD(_("Have color label"), QS_CLABEL);
112 COND_MENUITEM_ADD(_("Have attachment"), QS_MIME);
113 MENUITEM_ADD(menu, menuitem, NULL, 0);
114 COND_MENUITEM_ADD(_("Within 1 day"), QS_W1DAY);
115 COND_MENUITEM_ADD(_("Last 5 days"), QS_LAST5);
116 COND_MENUITEM_ADD(_("Last 7 days"), QS_LAST7);
117 COND_MENUITEM_ADD(_("Last 30 days"), QS_LAST30);
118 MENUITEM_ADD(menu, menuitem, NULL, 0);
119 COND_MENUITEM_ADD(_("In addressbook"), QS_IN_ADDRESSBOOK);
120 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
121
122#undef COND_MENUITEM_ADD
123
124 hbox2 = gtk_hbox_new(FALSE, 0);
125 gtk_widget_set_size_request(hbox2, 8, -1);
126 gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0);
127
128 label = gtk_label_new(_("Search:"));
129 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
130
131 hbox2 = gtk_hbox_new(FALSE, 0);
132 gtk_widget_set_size_request(hbox2, 4, -1);
133 gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0);
134
135 entry = gtk_entry_new();
136 gtk_widget_set_size_request(entry, 200 * gtkut_get_dpi_multiplier(), -1);
137 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
138 g_signal_connect(G_OBJECT(entry), "focus-in-event",
139 G_CALLBACK(entry_focus_in), qsearch);
140 g_signal_connect(G_OBJECT(entry), "focus-out-event",
141 G_CALLBACK(entry_focus_out), qsearch);
142 g_signal_connect(G_OBJECT(entry), "changed",
143 G_CALLBACK(entry_changed), qsearch);
144 g_signal_connect(G_OBJECT(entry), "activate",
145 G_CALLBACK(entry_activated), qsearch);
146 g_signal_connect(G_OBJECT(entry), "key_press_event",
147 G_CALLBACK(entry_key_pressed), qsearch);
148
149 tip = gtk_tooltips_new();
150 gtk_tooltips_set_tip(tip, entry, _("Search for Subject or From"), NULL);
151
152 hbox2 = gtk_hbox_new(FALSE, 0);
153 gtk_widget_set_size_request(hbox2, 2, -1);
154 gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0);
155
156 vbox = gtk_vbox_new(FALSE, 0);
157 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
158
159 clear_btn = gtk_button_new();
160 gtk_button_set_relief(GTK_BUTTON(clear_btn), GTK_RELIEF_NONE);
161 gtk_widget_set_size_request(clear_btn, 20, 20);
162 image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
163 gtk_container_add(GTK_CONTAINER(clear_btn), image);
164 GTK_WIDGET_UNSET_FLAGS(clear_btn, GTK_CAN_FOCUS);
165 gtk_box_pack_start(GTK_BOX(vbox), clear_btn, TRUE, FALSE, 0);
166 g_signal_connect(G_OBJECT(clear_btn), "clicked",
167 G_CALLBACK(clear_clicked), qsearch);
168
169 hbox2 = gtk_hbox_new(FALSE, 0);
170 gtk_widget_set_size_request(hbox2, 4, -1);
171 gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0);
172
173 status_label = gtk_label_new("");
174 gtk_box_pack_start(GTK_BOX(hbox), status_label, FALSE, FALSE, 0);
175
176 qsearch->hbox = hbox;
177 qsearch->optmenu = optmenu;
178 qsearch->menu = menu;
179 qsearch->label = label;
180 qsearch->entry = entry;
181 qsearch->clear_btn = clear_btn;
182 qsearch->status_label = status_label;
183 qsearch->summaryview = summaryview;
184 summaryview->qsearch = qsearch;
185 qsearch->entry_entered = FALSE;
186
187 gtk_widget_show_all(hbox);
188 gtk_widget_hide(clear_btn);
189
190 entry_focus_out(entry, NULL, qsearch);
191
192 return qsearch;
193}
194
195void quick_search_clear_entry(QuickSearch *qsearch)
196{
197 qsearch->entry_entered = FALSE;
198 if (GTK_WIDGET_HAS_FOCUS(qsearch->entry))
199 entry_focus_in(qsearch->entry, NULL, qsearch);
200 else
201 entry_focus_out(qsearch->entry, NULL, qsearch);
202
203 gtk_label_set_text(GTK_LABEL(qsearch->status_label), "");
204 gtk_widget_hide(qsearch->clear_btn);
205}
206
207GSList *quick_search_filter(QuickSearch *qsearch, QSearchCondType type,
208 const gchar *key)
209{
210 SummaryView *summaryview = qsearch->summaryview;
211 FilterCondType ftype;
212 FilterRule *status_rule = NULL;
213 FilterRule *rule = NULL;
214 FilterCond *cond;
215 FilterInfo fltinfo;
216 GSList *cond_list = NULL;
217 GSList *rule_list = NULL;
218 GSList *flt_mlist = NULL;
219 GSList *cur;
220 gint count = 0, total = 0;
221 gchar status_text[1024];
222 gboolean dmode;
223
224 if (!summaryview->all_mlist)
225 return NULL;
226
227 debug_print("quick_search_filter: filtering summary (type: %d)\n",
228 type);
229
230 switch (type) {
231 case QS_UNREAD:
232 case QS_MARK:
233 case QS_CLABEL:
234 case QS_MIME:
235 ftype = qsearch_cond_types[type].ftype;
236 cond = filter_cond_new(ftype, 0, 0, NULL, NULL);
237 cond_list = g_slist_append(cond_list, cond);
238 status_rule = filter_rule_new("Status filter rule", FLT_OR,
239 cond_list, NULL);
240 break;
241 case QS_W1DAY:
242 cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH,
243 NULL, "1");
244 cond_list = g_slist_append(cond_list, cond);
245 status_rule = filter_rule_new("Status filter rule", FLT_OR,
246 cond_list, NULL);
247 break;
248 case QS_LAST5:
249 cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH,
250 NULL, "5");
251 cond_list = g_slist_append(cond_list, cond);
252 status_rule = filter_rule_new("Status filter rule", FLT_OR,
253 cond_list, NULL);
254 break;
255 case QS_LAST7:
256 cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH,
257 NULL, "7");
258 cond_list = g_slist_append(cond_list, cond);
259 status_rule = filter_rule_new("Status filter rule", FLT_OR,
260 cond_list, NULL);
261 break;
262 case QS_LAST30:
263 cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH,
264 NULL, "30");
265 cond_list = g_slist_append(cond_list, cond);
266 status_rule = filter_rule_new("Status filter rule", FLT_OR,
267 cond_list, NULL);
268 break;
269 case QS_IN_ADDRESSBOOK:
270 cond = filter_cond_new(FLT_COND_HEADER, FLT_IN_ADDRESSBOOK, 0,
271 "From", NULL);
272 cond_list = g_slist_append(cond_list, cond);
273 status_rule = filter_rule_new("Status filter rule", FLT_OR,
274 cond_list, NULL);
275 break;
276 case QS_ALL:
277 default:
278 break;
279 }
280
281 if (key) {
282 gchar **keys;
283 gint i;
284
285 keys = g_strsplit(key, " ", -1);
286 for (i = 0; keys[i] != NULL; i++) {
287 cond_list = NULL;
288
289 if (keys[i] == '\0')
290 continue;
291
292 cond = filter_cond_new(FLT_COND_HEADER, FLT_CONTAIN, 0,
293 "Subject", keys[i]);
294 cond_list = g_slist_append(cond_list, cond);
295 cond = filter_cond_new(FLT_COND_HEADER, FLT_CONTAIN, 0,
296 "From", keys[i]);
297 cond_list = g_slist_append(cond_list, cond);
298 if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) {
299 cond = filter_cond_new(FLT_COND_TO_OR_CC, FLT_CONTAIN,
300 0, NULL, keys[i]);
301 cond_list = g_slist_append(cond_list, cond);
302 }
303
304 if (cond_list) {
305 rule = filter_rule_new("Quick search rule",
306 FLT_OR, cond_list, NULL);
307 rule_list = g_slist_append(rule_list, rule);
308 }
309 }
310 g_strfreev(keys);
311 }
312
313 memset(&fltinfo, 0, sizeof(FilterInfo));
314 dmode = get_debug_mode();
315 set_debug_mode(FALSE);
316
317 for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
318 MsgInfo *msginfo = (MsgInfo *)cur->data;
319 GSList *hlist = NULL;
320 gboolean matched = TRUE;
321
322 total++;
323
324 if (status_rule) {
325 if (type == QS_IN_ADDRESSBOOK)
326 hlist = procheader_get_header_list_from_msginfo
327 (msginfo);
328 if (!filter_match_rule(status_rule, msginfo, hlist,
329 &fltinfo)) {
330 if (hlist)
331 procheader_header_list_destroy(hlist);
332 continue;
333 }
334 }
335
336 if (rule_list) {
337 GSList *rcur;
338
339 if (!hlist)
340 hlist = procheader_get_header_list_from_msginfo
341 (msginfo);
342
343 /* AND keyword match */
344 for (rcur = rule_list; rcur != NULL; rcur = rcur->next) {
345 rule = (FilterRule *)rcur->data;
346 if (!filter_match_rule(rule, msginfo, hlist, &fltinfo)) {
347 matched = FALSE;
348 break;
349 }
350 }
351 }
352
353 if (matched) {
354 flt_mlist = g_slist_prepend(flt_mlist, msginfo);
355 count++;
356 }
357
358 if (hlist)
359 procheader_header_list_destroy(hlist);
360 }
361 flt_mlist = g_slist_reverse(flt_mlist);
362
363 set_debug_mode(dmode);
364
365 if (status_rule || rule) {
366 if (count > 0)
367 g_snprintf(status_text, sizeof(status_text),
368 _("%1$d in %2$d matched"), count, total);
369 else
370 g_snprintf(status_text, sizeof(status_text),
371 _("No messages matched"));
372 gtk_label_set_text(GTK_LABEL(qsearch->status_label),
373 status_text);
374 } else
375 gtk_label_set_text(GTK_LABEL(qsearch->status_label), "");
376
377 filter_rule_list_free(rule_list);
378 filter_rule_free(status_rule);
379
380 return flt_mlist;
381}
382
383static void menu_activated(GtkWidget *menuitem, QuickSearch *qsearch)
384{
385 summary_qsearch(qsearch->summaryview);
386}
387
388static gboolean entry_focus_in(GtkWidget *entry, GdkEventFocus *event,
389 QuickSearch *qsearch)
390{
391 if (!qsearch->entry_entered) {
392 g_signal_handlers_block_by_func(entry, entry_changed, qsearch);
393 gtk_entry_set_text(GTK_ENTRY(entry), "");
394 gtk_widget_modify_text(entry, GTK_STATE_NORMAL, NULL);
395 g_signal_handlers_unblock_by_func(entry, entry_changed, qsearch);
396 }
397
398 return FALSE;
399}
400
401static gboolean entry_focus_out(GtkWidget *entry, GdkEventFocus *event,
402 QuickSearch *qsearch)
403{
404 if (!qsearch->entry_entered) {
405 g_signal_handlers_block_by_func(entry, entry_changed, qsearch);
406 gtk_widget_modify_text(entry, GTK_STATE_NORMAL, &dim_color);
407 gtk_entry_set_text(GTK_ENTRY(entry), _("Search for Subject or From"));
408 g_signal_handlers_unblock_by_func(entry, entry_changed, qsearch);
409 }
410
411 return FALSE;
412}
413
414static void entry_changed(GtkWidget *entry, QuickSearch *qsearch)
415{
416 const gchar *text;
417
418 text = gtk_entry_get_text(GTK_ENTRY(entry));
419 if (text && *text != '\0') {
420 gtk_widget_show(qsearch->clear_btn);
421 qsearch->entry_entered = TRUE;
422 } else {
423 gtk_widget_hide(qsearch->clear_btn);
424 qsearch->entry_entered = FALSE;
425 }
426}
427
428static void entry_activated(GtkWidget *entry, QuickSearch *qsearch)
429{
430 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
431 summary_qsearch(qsearch->summaryview);
432}
433
434static gboolean entry_key_pressed(GtkWidget *treeview, GdkEventKey *event,
435 QuickSearch *qsearch)
436{
437 if (event && event->keyval == GDK_Escape) {
438 summary_qsearch_clear_entry(qsearch->summaryview);
439 return TRUE;
440 }
441 return FALSE;
442}
443
444static void clear_clicked(GtkWidget *button, QuickSearch *qsearch)
445{
446 summary_qsearch_clear_entry(qsearch->summaryview);
447}