Statistics
| Revision:

root / src / summary_search.c @ 241

History | View | Annotate | Download (13.8 kB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2005 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
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23
24
#include "defs.h"
25
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <gdk/gdkkeysyms.h>
29
#include <gtk/gtkwidget.h>
30
#include <gtk/gtkwindow.h>
31
#include <gtk/gtkvbox.h>
32
#include <gtk/gtktable.h>
33
#include <gtk/gtkoptionmenu.h>
34
#include <gtk/gtklabel.h>
35
#include <gtk/gtkentry.h>
36
#include <gtk/gtkhbox.h>
37
#include <gtk/gtkcheckbutton.h>
38
#include <gtk/gtkhbbox.h>
39
#include <gtk/gtkbutton.h>
40
#include <gtk/gtkmenuitem.h>
41
#include <gtk/gtkstock.h>
42
#include <gtk/gtktreemodel.h>
43
#include <gtk/gtktreeselection.h>
44
#include <stdio.h>
45
#include <stdlib.h>
46
#include <string.h>
47
48
#include "main.h"
49
#include "summary_search.h"
50
#include "summaryview.h"
51
#include "messageview.h"
52
#include "mainwindow.h"
53
#include "menu.h"
54
#include "utils.h"
55
#include "gtkutils.h"
56
#include "manage_window.h"
57
#include "alertpanel.h"
58
59
static GtkWidget *window;
60
static GtkWidget *bool_optmenu;
61
static GtkWidget *from_entry;
62
static GtkWidget *to_entry;
63
static GtkWidget *subject_entry;
64
static GtkWidget *body_entry;
65
static GtkWidget *case_checkbtn;
66
static GtkWidget *backward_checkbtn;
67
static GtkWidget *all_checkbtn;
68
static GtkWidget *search_btn;
69
static GtkWidget *clear_btn;
70
static GtkWidget *close_btn;
71
72
static void summary_search_create(SummaryView *summaryview);
73
static void summary_search_execute(GtkButton *button, gpointer data);
74
static void summary_search_clear(GtkButton *button, gpointer data);
75
static void from_activated(void);
76
static void to_activated(void);
77
static void subject_activated(void);
78
static void body_activated(void);
79
static void all_clicked(GtkButton *button);
80
static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
81
                            gpointer data);
82
83
void summary_search(SummaryView *summaryview)
84
{
85
        if (!window)
86
                summary_search_create(summaryview);
87
        else
88
                gtk_widget_hide(window);
89
90
        gtk_widget_grab_focus(search_btn);
91
        gtk_widget_grab_focus(subject_entry);
92
        gtk_widget_show(window);
93
}
94
95
static void summary_search_create(SummaryView *summaryview)
96
{
97
        GtkWidget *vbox1;
98
        GtkWidget *bool_hbox;
99
        GtkWidget *bool_menu;
100
        GtkWidget *menuitem;
101
        GtkWidget *table1;
102
        GtkWidget *from_label;
103
        GtkWidget *to_label;
104
        GtkWidget *subject_label;
105
        GtkWidget *body_label;
106
        GtkWidget *checkbtn_hbox;
107
        GtkWidget *confirm_area;
108
109
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
110
        gtk_window_set_title(GTK_WINDOW (window), _("Search messages"));
111
        gtk_widget_set_size_request(window, 450, -1);
112
        gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE);
113
        gtk_container_set_border_width(GTK_CONTAINER (window), 8);
114
        g_signal_connect(G_OBJECT(window), "delete_event",
115
                         G_CALLBACK(gtk_widget_hide_on_delete), NULL);
116
        g_signal_connect(G_OBJECT(window), "key_press_event",
117
                         G_CALLBACK(key_pressed), NULL);
118
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
119
120
        vbox1 = gtk_vbox_new (FALSE, 0);
121
        gtk_widget_show (vbox1);
122
        gtk_container_add (GTK_CONTAINER (window), vbox1);
123
124
        bool_hbox = gtk_hbox_new(FALSE, 4);
125
        gtk_widget_show(bool_hbox);
126
        gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0);
127
128
        bool_optmenu = gtk_option_menu_new();
129
        gtk_widget_show(bool_optmenu);
130
        gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0);
131
132
        bool_menu = gtk_menu_new();
133
        MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"), 0);
134
        MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"), 1);
135
        gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_optmenu), bool_menu);
136
137
        table1 = gtk_table_new (4, 3, FALSE);
138
        gtk_widget_show (table1);
139
        gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
140
        gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
141
        gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
142
        gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
143
144
        from_entry = gtk_entry_new ();
145
        gtk_widget_show (from_entry);
146
        gtk_table_attach (GTK_TABLE (table1), from_entry, 1, 3, 0, 1,
147
                          GTK_EXPAND|GTK_FILL, 0, 0, 0);
148
        g_signal_connect(G_OBJECT(from_entry), "activate",
149
                         G_CALLBACK(from_activated), summaryview);
150
151
        to_entry = gtk_entry_new ();
152
        gtk_widget_show (to_entry);
153
        gtk_table_attach (GTK_TABLE (table1), to_entry, 1, 3, 1, 2,
154
                          GTK_EXPAND|GTK_FILL, 0, 0, 0);
155
        g_signal_connect(G_OBJECT(to_entry), "activate",
156
                         G_CALLBACK(to_activated), summaryview);
157
158
        subject_entry = gtk_entry_new ();
159
        gtk_widget_show (subject_entry);
160
        gtk_table_attach (GTK_TABLE (table1), subject_entry, 1, 3, 2, 3,
161
                          GTK_EXPAND|GTK_FILL, 0, 0, 0);
162
        g_signal_connect(G_OBJECT(subject_entry), "activate",
163
                         G_CALLBACK(subject_activated), summaryview);
164
165
        body_entry = gtk_entry_new ();
166
        gtk_widget_show (body_entry);
167
        gtk_table_attach (GTK_TABLE (table1), body_entry, 1, 3, 3, 4,
168
                          GTK_EXPAND|GTK_FILL, 0, 0, 0);
169
        g_signal_connect(G_OBJECT(body_entry), "activate",
170
                         G_CALLBACK(body_activated), summaryview);
171
172
        from_label = gtk_label_new (_("From:"));
173
        gtk_widget_show (from_label);
174
        gtk_table_attach (GTK_TABLE (table1), from_label, 0, 1, 0, 1,
175
                          GTK_FILL, 0, 0, 0);
176
        gtk_label_set_justify (GTK_LABEL (from_label), GTK_JUSTIFY_RIGHT);
177
        gtk_misc_set_alignment (GTK_MISC (from_label), 1, 0.5);
178
179
        to_label = gtk_label_new (_("To:"));
180
        gtk_widget_show (to_label);
181
        gtk_table_attach (GTK_TABLE (table1), to_label, 0, 1, 1, 2,
182
                          GTK_FILL, 0, 0, 0);
183
        gtk_label_set_justify (GTK_LABEL (to_label), GTK_JUSTIFY_RIGHT);
184
        gtk_misc_set_alignment (GTK_MISC (to_label), 1, 0.5);
185
186
        subject_label = gtk_label_new (_("Subject:"));
187
        gtk_widget_show (subject_label);
188
        gtk_table_attach (GTK_TABLE (table1), subject_label, 0, 1, 2, 3,
189
                          GTK_FILL, 0, 0, 0);
190
        gtk_label_set_justify (GTK_LABEL (subject_label), GTK_JUSTIFY_RIGHT);
191
        gtk_misc_set_alignment (GTK_MISC (subject_label), 1, 0.5);
192
193
        body_label = gtk_label_new (_("Body:"));
194
        gtk_widget_show (body_label);
195
        gtk_table_attach (GTK_TABLE (table1), body_label, 0, 1, 3, 4,
196
                          GTK_FILL, 0, 0, 0);
197
        gtk_label_set_justify (GTK_LABEL (body_label), GTK_JUSTIFY_RIGHT);
198
        gtk_misc_set_alignment (GTK_MISC (body_label), 1, 0.5);
199
200
        checkbtn_hbox = gtk_hbox_new (FALSE, 8);
201
        gtk_widget_show (checkbtn_hbox);
202
        gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, TRUE, TRUE, 0);
203
        gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox), 8);
204
205
        case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive"));
206
        gtk_widget_show (case_checkbtn);
207
        gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn,
208
                            FALSE, FALSE, 0);
209
210
        backward_checkbtn =
211
                gtk_check_button_new_with_label (_("Backward search"));
212
        gtk_widget_show (backward_checkbtn);
213
        gtk_box_pack_start (GTK_BOX (checkbtn_hbox), backward_checkbtn,
214
                            FALSE, FALSE, 0);
215
216
        all_checkbtn =
217
                gtk_check_button_new_with_label (_("Select all matched"));
218
        gtk_widget_show (all_checkbtn);
219
        gtk_box_pack_start (GTK_BOX (checkbtn_hbox), all_checkbtn,
220
                            FALSE, FALSE, 0);
221
        g_signal_connect(G_OBJECT(all_checkbtn), "clicked",
222
                         G_CALLBACK(all_clicked), summaryview);
223
224
        gtkut_stock_button_set_create(&confirm_area,
225
                                      &search_btn, GTK_STOCK_FIND,
226
                                      &clear_btn, GTK_STOCK_CLEAR,
227
                                      &close_btn, GTK_STOCK_CLOSE);
228
        gtk_widget_show (confirm_area);
229
        gtk_box_pack_start (GTK_BOX (vbox1), confirm_area, FALSE, FALSE, 0);
230
        gtk_widget_grab_default(search_btn);
231
232
        g_signal_connect(G_OBJECT(search_btn), "clicked",
233
                         G_CALLBACK(summary_search_execute), summaryview);
234
        g_signal_connect(G_OBJECT(clear_btn), "clicked",
235
                         G_CALLBACK(summary_search_clear), summaryview);
236
        g_signal_connect_closure
237
                (G_OBJECT(close_btn), "clicked",
238
                 g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide),
239
                                     window, NULL),
240
                 FALSE);
241
}
242
243
static void summary_search_execute(GtkButton *button, gpointer data)
244
{
245
        SummaryView *summaryview = data;
246
        GtkTreeModel *model;
247
        GtkTreeIter iter;
248
        MsgInfo *msginfo;
249
        gboolean bool_and;
250
        gboolean case_sens;
251
        gboolean backward;
252
        gboolean search_all;
253
        gboolean all_searched = FALSE;
254
        gboolean matched;
255
        gboolean body_matched;
256
        const gchar *from_str, *to_str, *subject_str, *body_str;
257
        StrFindFunc str_find_func;
258
        gboolean valid;
259
260
        if (summary_is_locked(summaryview)) return;
261
        summary_lock(summaryview);
262
263
        model = GTK_TREE_MODEL(summaryview->store);
264
265
        bool_and = menu_get_option_menu_active_index
266
                (GTK_OPTION_MENU(bool_optmenu));
267
        case_sens = gtk_toggle_button_get_active
268
                (GTK_TOGGLE_BUTTON(case_checkbtn));
269
        backward = gtk_toggle_button_get_active
270
                (GTK_TOGGLE_BUTTON(backward_checkbtn));
271
        search_all = gtk_toggle_button_get_active
272
                (GTK_TOGGLE_BUTTON(all_checkbtn));
273
274
        if (case_sens)
275
                str_find_func = str_find;
276
        else
277
                str_find_func = str_case_find;
278
279
        from_str    = gtk_entry_get_text(GTK_ENTRY(from_entry));
280
        to_str      = gtk_entry_get_text(GTK_ENTRY(to_entry));
281
        subject_str = gtk_entry_get_text(GTK_ENTRY(subject_entry));
282
        body_str    = gtk_entry_get_text(GTK_ENTRY(body_entry));
283
284
        if (search_all) {
285
                summary_unselect_all(summaryview);
286
                valid = gtk_tree_model_get_iter_first(model, &iter);
287
                backward = FALSE;
288
        } else if (!summaryview->selected) {
289
                if (backward)
290
                        valid  = gtkut_tree_model_get_iter_last(model, &iter);
291
                else
292
                        valid = gtk_tree_model_get_iter_first(model, &iter);
293
                if (!valid) {
294
                        summary_unlock(summaryview);
295
                        return;
296
                }
297
        } else {
298
                valid = gtkut_tree_row_reference_get_iter
299
                        (model, summaryview->selected, &iter);
300
                if (!valid) {
301
                        summary_unlock(summaryview);
302
                        return;
303
                }
304
305
                if (backward)
306
                        valid = gtkut_tree_model_prev(model, &iter);
307
                else
308
                        valid = gtkut_tree_model_next(model, &iter);
309
        }
310
311
        if (*body_str)
312
                main_window_cursor_wait(summaryview->mainwin);
313
314
        for (;;) {
315
                if (!valid) {
316
                        gchar *str;
317
                        AlertValue val;
318
319
                        if (search_all) {
320
                                break;
321
                        }
322
323
                        if (all_searched) {
324
                                alertpanel_message
325
                                        (_("Search failed"),
326
                                         _("Search string not found."),
327
                                         ALERT_WARNING);
328
                                break;
329
                        }
330
331
                        if (backward)
332
                                str = _("Beginning of list reached; continue from end?");
333
                        else
334
                                str = _("End of list reached; continue from beginning?");
335
336
                        val = alertpanel(_("Search finished"), str,
337
                                         GTK_STOCK_YES, GTK_STOCK_NO, NULL);
338
                        if (G_ALERTDEFAULT == val) {
339
                                if (backward)
340
                                        valid  = gtkut_tree_model_get_iter_last
341
                                                (model, &iter);
342
                                else
343
                                        valid = gtk_tree_model_get_iter_first
344
                                                (model, &iter);
345
                                all_searched = TRUE;
346
                                manage_window_focus_in(window, NULL, NULL);
347
                        } else
348
                                break;
349
                }
350
351
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
352
                body_matched = FALSE;
353
354
                if (bool_and) {
355
                        matched = TRUE;
356
                        if (*from_str) {
357
                                if (!msginfo->from ||
358
                                    !str_find_func(msginfo->from, from_str))
359
                                        matched = FALSE;
360
                        }
361
                        if (matched && *to_str) {
362
                                if (!msginfo->to ||
363
                                    !str_find_func(msginfo->to, to_str))
364
                                        matched = FALSE;
365
                        }
366
                        if (matched && *subject_str) {
367
                                if (!msginfo->subject ||
368
                                    !str_find_func(msginfo->subject, subject_str))
369
                                        matched = FALSE;
370
                        }
371
                        if (matched && *body_str) {
372
                                if (procmime_find_string(msginfo, body_str,
373
                                                         str_find_func))
374
                                        body_matched = TRUE;
375
                                else
376
                                        matched = FALSE;
377
                        }
378
                        if (matched && !*from_str && !*to_str &&
379
                            !*subject_str && !*body_str)
380
                                matched = FALSE;
381
                } else {
382
                        matched = FALSE;
383
                        if (*from_str && msginfo->from) {
384
                                if (str_find_func(msginfo->from, from_str))
385
                                        matched = TRUE;
386
                        }
387
                        if (!matched && *to_str && msginfo->to) {
388
                                if (str_find_func(msginfo->to, to_str))
389
                                        matched = TRUE;
390
                        }
391
                        if (!matched && *subject_str && msginfo->subject) {
392
                                if (str_find_func(msginfo->subject, subject_str))
393
                                        matched = TRUE;
394
                        }
395
                        if (!matched && *body_str) {
396
                                if (procmime_find_string(msginfo, body_str,
397
                                                         str_find_func)) {
398
                                        matched = TRUE;
399
                                        body_matched = TRUE;
400
                                }
401
                        }
402
                }
403
404
                if (matched) {
405
                        if (search_all) {
406
                                gtk_tree_selection_select_iter
407
                                        (summaryview->selection, &iter);
408
                        } else {
409
                                if (messageview_is_visible
410
                                        (summaryview->messageview)) {
411
                                        summary_unlock(summaryview);
412
                                        summary_select_row
413
                                                (summaryview, &iter,
414
                                                 TRUE, TRUE);
415
                                        summary_lock(summaryview);
416
                                        if (body_matched) {
417
                                                messageview_search_string
418
                                                        (summaryview->messageview,
419
                                                         body_str, case_sens);
420
                                        }
421
                                } else {
422
                                        summary_select_row
423
                                                (summaryview, &iter,
424
                                                 FALSE, TRUE);
425
                                }
426
                                break;
427
                        }
428
                }
429
430
                if (backward)
431
                        valid = gtkut_tree_model_prev(model, &iter);
432
                else
433
                        valid = gtkut_tree_model_next(model, &iter);
434
        }
435
436
        if (*body_str)
437
                main_window_cursor_normal(summaryview->mainwin);
438
439
        summary_unlock(summaryview);
440
}
441
442
static void summary_search_clear(GtkButton *button, gpointer data)
443
{
444
        gtk_editable_delete_text(GTK_EDITABLE(from_entry),    0, -1);
445
        gtk_editable_delete_text(GTK_EDITABLE(to_entry),      0, -1);
446
        gtk_editable_delete_text(GTK_EDITABLE(subject_entry), 0, -1);
447
        gtk_editable_delete_text(GTK_EDITABLE(body_entry),    0, -1);
448
}
449
450
static void from_activated(void)
451
{
452
        gtk_widget_grab_focus(to_entry);
453
}
454
455
static void to_activated(void)
456
{
457
        gtk_widget_grab_focus(subject_entry);
458
}
459
460
static void subject_activated(void)
461
{
462
        gtk_button_clicked(GTK_BUTTON(search_btn));
463
}
464
465
static void body_activated(void)
466
{
467
        gtk_button_clicked(GTK_BUTTON(search_btn));
468
}
469
470
static void all_clicked(GtkButton *button)
471
{
472
        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
473
                gtk_widget_set_sensitive(backward_checkbtn, FALSE);
474
        else
475
                gtk_widget_set_sensitive(backward_checkbtn, TRUE);
476
}
477
478
static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
479
                            gpointer data)
480
{
481
        if (event && event->keyval == GDK_Escape)
482
                gtk_widget_hide(window);
483
        return FALSE;
484
}