Statistics
| Revision:

root / src / quick_search.c @ 2733

History | View | Annotate | Download (12.2 kB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2009 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
39
static 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
};
53
54
static GdkColor text_color;
55
static GdkColor dim_color = {0, COLOR_DIM, COLOR_DIM, COLOR_DIM};
56
57
static void menu_activated                (GtkWidget        *menuitem,
58
                                         QuickSearch        *qsearch);
59
static gboolean entry_focus_in                (GtkWidget        *entry,
60
                                         GdkEventFocus        *event,
61
                                         QuickSearch        *qsearch);
62
static gboolean entry_focus_out                (GtkWidget        *entry,
63
                                         GdkEventFocus        *event,
64
                                         QuickSearch        *qsearch);
65
static void entry_changed                (GtkWidget        *entry,
66
                                         QuickSearch        *qsearch);
67
static void entry_activated                (GtkWidget        *entry,
68
                                         QuickSearch        *qsearch);
69
static gboolean entry_key_pressed        (GtkWidget        *treeview,
70
                                         GdkEventKey        *event,
71
                                         QuickSearch        *qsearch);
72
static void clear_clicked                (GtkWidget        *button,
73
                                         QuickSearch        *qsearch);
74
75
76
QuickSearch *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
        GtkStyle *style;
92
93
        qsearch = g_new0(QuickSearch, 1);
94
95
        hbox = gtk_hbox_new(FALSE, 0);
96
        gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
97
98
        optmenu = gtk_option_menu_new();
99
        gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
100
101
#define COND_MENUITEM_ADD(str, action)                                        \
102
{                                                                        \
103
        MENUITEM_ADD(menu, menuitem, str, action);                        \
104
        g_signal_connect(G_OBJECT(menuitem), "activate",                \
105
                         G_CALLBACK(menu_activated), qsearch);                \
106
}
107
108
        menu = gtk_menu_new();
109
        COND_MENUITEM_ADD(_("All"), QS_ALL);
110
        COND_MENUITEM_ADD(_("Unread"), QS_UNREAD);
111
        COND_MENUITEM_ADD(_("Marked"), QS_MARK);
112
        COND_MENUITEM_ADD(_("Have color label"), QS_CLABEL);
113
        COND_MENUITEM_ADD(_("Have attachment"), QS_MIME);
114
        MENUITEM_ADD(menu, menuitem, NULL, 0);
115
        COND_MENUITEM_ADD(_("Within 1 day"), QS_W1DAY);
116
        COND_MENUITEM_ADD(_("Last 5 days"), QS_LAST5);
117
        COND_MENUITEM_ADD(_("Last 7 days"), QS_LAST7);
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, -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
        style = gtk_widget_get_style(entry);
191
        text_color = style->text[GTK_STATE_NORMAL];
192
        entry_focus_out(entry, NULL, qsearch);
193
194
        return qsearch;
195
}
196
197
void quick_search_clear_entry(QuickSearch *qsearch)
198
{
199
        qsearch->entry_entered = FALSE;
200
        if (GTK_WIDGET_HAS_FOCUS(qsearch->entry))
201
                entry_focus_in(qsearch->entry, NULL, qsearch);
202
        else
203
                entry_focus_out(qsearch->entry, NULL, qsearch);
204
205
        gtk_label_set_text(GTK_LABEL(qsearch->status_label), "");
206
        gtk_widget_hide(qsearch->clear_btn);
207
}
208
209
GSList *quick_search_filter(QuickSearch *qsearch, QSearchCondType type,
210
                           const gchar *key)
211
{
212
        SummaryView *summaryview = qsearch->summaryview;
213
        FilterCondType ftype;
214
        FilterRule *status_rule = NULL;
215
        FilterRule *rule = NULL;
216
        FilterCond *cond;
217
        FilterInfo fltinfo;
218
        GSList *cond_list = NULL;
219
        GSList *flt_mlist = NULL;
220
        GSList *cur;
221
        gint count = 0, total = 0;
222
        gchar status_text[1024];
223
        gboolean dmode;
224
225
        if (!summaryview->all_mlist)
226
                return NULL;
227
228
        debug_print("quick_search_filter: filtering summary (type: %d)\n",
229
                    type);
230
231
        switch (type) {
232
        case QS_UNREAD:
233
        case QS_MARK:
234
        case QS_CLABEL:
235
        case QS_MIME:
236
                ftype = qsearch_cond_types[type].ftype;
237
                cond = filter_cond_new(ftype, 0, 0, NULL, NULL);
238
                cond_list = g_slist_append(cond_list, cond);
239
                status_rule = filter_rule_new("Status filter rule", FLT_OR,
240
                                              cond_list, NULL);
241
                break;
242
        case QS_W1DAY:
243
                cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH,
244
                                       NULL, "1");
245
                cond_list = g_slist_append(cond_list, cond);
246
                status_rule = filter_rule_new("Status filter rule", FLT_OR,
247
                                              cond_list, NULL);
248
                break;
249
        case QS_LAST5:
250
                cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH,
251
                                       NULL, "5");
252
                cond_list = g_slist_append(cond_list, cond);
253
                status_rule = filter_rule_new("Status filter rule", FLT_OR,
254
                                              cond_list, NULL);
255
                break;
256
        case QS_LAST7:
257
                cond = filter_cond_new(FLT_COND_AGE_GREATER, 0, FLT_NOT_MATCH,
258
                                       NULL, "7");
259
                cond_list = g_slist_append(cond_list, cond);
260
                status_rule = filter_rule_new("Status filter rule", FLT_OR,
261
                                              cond_list, NULL);
262
                break;
263
        case QS_IN_ADDRESSBOOK:
264
                cond = filter_cond_new(FLT_COND_HEADER, FLT_IN_ADDRESSBOOK, 0,
265
                                       "From", NULL);
266
                cond_list = g_slist_append(cond_list, cond);
267
                status_rule = filter_rule_new("Status filter rule", FLT_OR,
268
                                              cond_list, NULL);
269
                break;
270
        case QS_ALL:
271
        default:
272
                break;
273
        }
274
275
        cond_list = NULL;
276
277
        if (key && *key != '\0') {
278
                cond = filter_cond_new(FLT_COND_HEADER, FLT_CONTAIN, 0,
279
                                       "Subject", key);
280
                cond_list = g_slist_append(cond_list, cond);
281
                cond = filter_cond_new(FLT_COND_HEADER, FLT_CONTAIN, 0,
282
                                       "From", key);
283
                cond_list = g_slist_append(cond_list, cond);
284
                if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) {
285
                        cond = filter_cond_new(FLT_COND_TO_OR_CC, FLT_CONTAIN,
286
                                               0, NULL, key);
287
                        cond_list = g_slist_append(cond_list, cond);
288
                }
289
                rule = filter_rule_new("Quick search rule", FLT_OR, cond_list,
290
                                       NULL);
291
        }
292
293
        memset(&fltinfo, 0, sizeof(FilterInfo));
294
        dmode = get_debug_mode();
295
        set_debug_mode(FALSE);
296
297
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
298
                MsgInfo *msginfo = (MsgInfo *)cur->data;
299
                GSList *hlist = NULL;
300
301
                total++;
302
303
                if (status_rule) {
304
                        if (type == QS_IN_ADDRESSBOOK)
305
                                hlist = procheader_get_header_list_from_msginfo
306
                                        (msginfo);
307
                        if (!filter_match_rule(status_rule, msginfo, hlist,
308
                                               &fltinfo)) {
309
                                if (hlist)
310
                                        procheader_header_list_destroy(hlist);
311
                                continue;
312
                        }
313
                }
314
315
                if (rule) {
316
                        if (!hlist)
317
                                hlist = procheader_get_header_list_from_msginfo
318
                                        (msginfo);
319
                        if (filter_match_rule(rule, msginfo, hlist, &fltinfo)) {
320
                                flt_mlist = g_slist_prepend(flt_mlist, msginfo);
321
                                count++;
322
                        }
323
                } else {
324
                        flt_mlist = g_slist_prepend(flt_mlist, msginfo);
325
                        count++;
326
                }
327
328
                if (hlist)
329
                        procheader_header_list_destroy(hlist);
330
        }
331
        flt_mlist = g_slist_reverse(flt_mlist);
332
333
        set_debug_mode(dmode);
334
335
        if (status_rule || rule) {
336
                if (count > 0)
337
                        g_snprintf(status_text, sizeof(status_text),
338
                                   _("%1$d in %2$d matched"), count, total);
339
                else
340
                        g_snprintf(status_text, sizeof(status_text),
341
                                   _("No messages matched"));
342
                gtk_label_set_text(GTK_LABEL(qsearch->status_label),
343
                                   status_text);
344
        } else
345
                gtk_label_set_text(GTK_LABEL(qsearch->status_label), "");
346
347
        filter_rule_free(rule);
348
        filter_rule_free(status_rule);
349
350
        return flt_mlist;
351
}
352
353
static void menu_activated(GtkWidget *menuitem, QuickSearch *qsearch)
354
{
355
        summary_qsearch(qsearch->summaryview);
356
}
357
358
static gboolean entry_focus_in(GtkWidget *entry, GdkEventFocus *event,
359
                               QuickSearch *qsearch)
360
{
361
        GtkStyle *style;
362
363
        if (!qsearch->entry_entered) {
364
                g_signal_handlers_block_by_func(entry, entry_changed, qsearch);
365
                gtk_entry_set_text(GTK_ENTRY(entry), "");
366
                style = gtk_widget_get_style(entry);
367
                gtk_widget_modify_text(entry, GTK_STATE_NORMAL, &text_color);
368
                g_signal_handlers_unblock_by_func(entry, entry_changed, qsearch);
369
        }
370
371
        return FALSE;
372
}
373
374
static gboolean entry_focus_out(GtkWidget *entry, GdkEventFocus *event,
375
                                QuickSearch *qsearch)
376
{
377
        GtkStyle *style;
378
379
        if (!qsearch->entry_entered) {
380
                g_signal_handlers_block_by_func(entry, entry_changed, qsearch);
381
                style = gtk_widget_get_style(entry);
382
                gtk_widget_modify_text(entry, GTK_STATE_NORMAL, &dim_color);
383
                gtk_entry_set_text(GTK_ENTRY(entry), _("Search for Subject or From"));
384
                g_signal_handlers_unblock_by_func(entry, entry_changed, qsearch);
385
        }
386
387
        return FALSE;
388
}
389
390
static void entry_changed(GtkWidget *entry, QuickSearch *qsearch)
391
{
392
        const gchar *text;
393
394
        text = gtk_entry_get_text(GTK_ENTRY(entry));
395
        if (text && *text != '\0') {
396
                gtk_widget_show(qsearch->clear_btn);
397
                qsearch->entry_entered = TRUE;
398
        } else {
399
                gtk_widget_hide(qsearch->clear_btn);
400
                qsearch->entry_entered = FALSE;
401
        }
402
}
403
404
static void entry_activated(GtkWidget *entry, QuickSearch *qsearch)
405
{
406
        gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
407
        summary_qsearch(qsearch->summaryview);
408
}
409
410
static gboolean entry_key_pressed(GtkWidget *treeview, GdkEventKey *event,
411
                                  QuickSearch *qsearch)
412
{
413
        if (event && event->keyval == GDK_Escape) {
414
                summary_qsearch_clear_entry(qsearch->summaryview);
415
                return TRUE;
416
        }
417
        return FALSE;
418
}
419
420
static void clear_clicked(GtkWidget *button, QuickSearch *qsearch)
421
{
422
        summary_qsearch_clear_entry(qsearch->summaryview);
423
}