new filtering action : forward - some fixes
[claws.git] / src / prefs_scoring.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 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 <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include "intl.h"
35 #include "main.h"
36 #include "prefs.h"
37 #include "prefs_matcher.h"
38 #include "prefs_scoring.h"
39 #include "prefs_common.h"
40 #include "mainwindow.h"
41 #include "foldersel.h"
42 #include "manage_window.h"
43 #include "inc.h"
44 #include "utils.h"
45 #include "gtkutils.h"
46 #include "alertpanel.h"
47 #include "folder.h"
48 #include "scoring.h"
49
50 static struct Scoring {
51         GtkWidget *window;
52
53         GtkWidget *ok_btn;
54         GtkWidget *cond_entry;
55         GtkWidget *score_entry;
56
57         GtkWidget *cond_clist;
58 } scoring;
59
60 /*
61    parameter name, default value, pointer to the prefs variable, data type,
62    pointer to the widget pointer,
63    pointer to the function for data setting,
64    pointer to the function for widget setting
65  */
66
67 #define VSPACING                12
68 #define VSPACING_NARROW         4
69 #define DEFAULT_ENTRY_WIDTH     80
70 #define PREFSBUFSIZE            1024
71
72 /* widget creating functions */
73 static void prefs_scoring_create                (void);
74
75 static void prefs_scoring_set_dialog    (ScoringProp * prop);
76 static void prefs_scoring_set_list      (void);
77
78 /* callback functions */
79 /* static void prefs_scoring_select_dest_cb     (void); */
80 static void prefs_scoring_register_cb   (void);
81 static void prefs_scoring_substitute_cb (void);
82 static void prefs_scoring_delete_cb     (void);
83 static void prefs_scoring_up            (void);
84 static void prefs_scoring_down          (void);
85 static void prefs_scoring_select                (GtkCList       *clist,
86                                          gint            row,
87                                          gint            column,
88                                          GdkEvent       *event);
89
90 static gint prefs_scoring_deleted       (GtkWidget      *widget,
91                                          GdkEventAny    *event,
92                                          gpointer        data);
93 static void prefs_scoring_key_pressed   (GtkWidget      *widget,
94                                          GdkEventKey    *event,
95                                          gpointer        data);
96 static void prefs_scoring_cancel                (void);
97 static void prefs_scoring_ok            (void);
98
99 static void prefs_scoring_condition_define      (void);
100 static gint prefs_scoring_clist_set_row(gint row, ScoringProp * prop);
101 static void prefs_scoring_select_set_dialog(ScoringProp * prop);
102 static void prefs_scoring_reset_dialog(void);
103
104 void prefs_scoring_open(void)
105 {
106         inc_autocheck_timer_remove();
107
108         if (!scoring.window) {
109                 prefs_scoring_create();
110         }
111
112         manage_window_set_transient(GTK_WINDOW(scoring.window));
113         gtk_widget_grab_focus(scoring.ok_btn);
114
115         prefs_scoring_set_dialog(NULL);
116
117         gtk_widget_show(scoring.window);
118 }
119
120 void prefs_scoring_open_with_scoring(ScoringProp * prop)
121 {
122         inc_autocheck_timer_remove();
123
124         if (!scoring.window) {
125                 prefs_scoring_create();
126         }
127
128         manage_window_set_transient(GTK_WINDOW(scoring.window));
129         gtk_widget_grab_focus(scoring.ok_btn);
130
131         prefs_scoring_set_dialog(prop);
132
133         gtk_widget_show(scoring.window);
134 }
135
136 static void prefs_scoring_create(void)
137 {
138         GtkWidget *window;
139         GtkWidget *vbox;
140         GtkWidget *ok_btn;
141         GtkWidget *cancel_btn;
142         GtkWidget *confirm_area;
143
144         GtkWidget *vbox1;
145         GtkWidget *hbox1;
146         GtkWidget *reg_hbox;
147         GtkWidget *arrow;
148         GtkWidget *btn_hbox;
149
150         GtkWidget *cond_label;
151         GtkWidget *cond_entry;
152         GtkWidget *cond_btn;
153         GtkWidget *score_label;
154         GtkWidget *score_entry;
155
156         GtkWidget *reg_btn;
157         GtkWidget *subst_btn;
158         GtkWidget *del_btn;
159
160         GtkWidget *cond_hbox;
161         GtkWidget *cond_scrolledwin;
162         GtkWidget *cond_clist;
163
164         GtkWidget *btn_vbox;
165         GtkWidget *up_btn;
166         GtkWidget *down_btn;
167
168         gchar *title[] = {_("Registered rules")};
169
170         debug_print(_("Creating scoring setting window...\n"));
171
172         window = gtk_window_new (GTK_WINDOW_DIALOG);
173         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
174         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
175         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
176         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
177
178         vbox = gtk_vbox_new (FALSE, 6);
179         gtk_widget_show (vbox);
180         gtk_container_add (GTK_CONTAINER (window), vbox);
181
182         gtkut_button_set_create(&confirm_area, &ok_btn, _("OK"),
183                                 &cancel_btn, _("Cancel"), NULL, NULL);
184         gtk_widget_show (confirm_area);
185         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
186         gtk_widget_grab_default (ok_btn);
187
188         gtk_window_set_title (GTK_WINDOW(window),
189                               _("Scoring setting"));
190         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
191                             GTK_SIGNAL_FUNC(prefs_scoring_deleted), NULL);
192         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
193                             GTK_SIGNAL_FUNC(prefs_scoring_key_pressed), NULL);
194         gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
195                             GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
196         gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
197                             GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
198         gtk_signal_connect (GTK_OBJECT(ok_btn), "clicked",
199                             GTK_SIGNAL_FUNC(prefs_scoring_ok), NULL);
200         gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
201                             GTK_SIGNAL_FUNC(prefs_scoring_cancel), NULL);
202
203         vbox1 = gtk_vbox_new (FALSE, VSPACING);
204         gtk_widget_show (vbox1);
205         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
206         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
207
208         cond_label = gtk_label_new (_("Condition"));
209         gtk_widget_show (cond_label);
210         gtk_misc_set_alignment (GTK_MISC (cond_label), 0, 0.5);
211         gtk_box_pack_start (GTK_BOX (vbox1), cond_label, FALSE, FALSE, 0);
212
213         hbox1 = gtk_hbox_new (FALSE, VSPACING);
214         gtk_widget_show (vbox1);
215         gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0);
216         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
217
218         cond_entry = gtk_entry_new ();
219         gtk_widget_show (cond_entry);
220         gtk_widget_set_usize (cond_entry, 300, -1);
221         gtk_box_pack_start (GTK_BOX (hbox1), cond_entry, TRUE, TRUE, 0);
222
223         cond_btn = gtk_button_new_with_label (_("Define ..."));
224         gtk_widget_show (cond_btn);
225         gtk_box_pack_start (GTK_BOX (hbox1), cond_btn, FALSE, FALSE, 0);
226         gtk_signal_connect (GTK_OBJECT (cond_btn), "clicked",
227                             GTK_SIGNAL_FUNC (prefs_scoring_condition_define),
228                             NULL);
229
230         hbox1 = gtk_hbox_new (FALSE, VSPACING);
231         gtk_widget_show (vbox1);
232         gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0);
233         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
234
235         score_label = gtk_label_new (_("Score"));
236         gtk_widget_show (score_label);
237         gtk_misc_set_alignment (GTK_MISC (score_label), 0, 0.5);
238         gtk_box_pack_start (GTK_BOX (hbox1), score_label, FALSE, FALSE, 0);
239
240         score_entry = gtk_entry_new ();
241         gtk_widget_show (score_entry);
242         gtk_widget_set_usize (score_entry, 50, -1);
243         gtk_box_pack_start (GTK_BOX (hbox1), score_entry, FALSE, FALSE, 0);
244
245         /* register / substitute / delete */
246
247         reg_hbox = gtk_hbox_new (FALSE, 4);
248         gtk_widget_show (reg_hbox);
249         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
250
251         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
252         gtk_widget_show (arrow);
253         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
254         gtk_widget_set_usize (arrow, -1, 16);
255
256         btn_hbox = gtk_hbox_new (TRUE, 4);
257         gtk_widget_show (btn_hbox);
258         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
259
260         reg_btn = gtk_button_new_with_label (_("Register"));
261         gtk_widget_show (reg_btn);
262         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
263         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
264                             GTK_SIGNAL_FUNC (prefs_scoring_register_cb), NULL);
265
266         subst_btn = gtk_button_new_with_label (_(" Substitute "));
267         gtk_widget_show (subst_btn);
268         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
269         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
270                             GTK_SIGNAL_FUNC (prefs_scoring_substitute_cb),
271                             NULL);
272
273         del_btn = gtk_button_new_with_label (_("Delete"));
274         gtk_widget_show (del_btn);
275         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
276         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
277                             GTK_SIGNAL_FUNC (prefs_scoring_delete_cb), NULL);
278
279         cond_hbox = gtk_hbox_new (FALSE, 8);
280         gtk_widget_show (cond_hbox);
281         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
282
283         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
284         gtk_widget_show (cond_scrolledwin);
285         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
286         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
287                             TRUE, TRUE, 0);
288         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
289                                         GTK_POLICY_AUTOMATIC,
290                                         GTK_POLICY_AUTOMATIC);
291
292         cond_clist = gtk_clist_new_with_titles(1, title);
293         gtk_widget_show (cond_clist);
294         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_clist);
295         gtk_clist_set_column_width (GTK_CLIST (cond_clist), 0, 80);
296         gtk_clist_set_selection_mode (GTK_CLIST (cond_clist),
297                                       GTK_SELECTION_BROWSE);
298         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (cond_clist)->column[0].button,
299                                 GTK_CAN_FOCUS);
300         gtk_signal_connect (GTK_OBJECT (cond_clist), "select_row",
301                             GTK_SIGNAL_FUNC (prefs_scoring_select), NULL);
302
303         btn_vbox = gtk_vbox_new (FALSE, 8);
304         gtk_widget_show (btn_vbox);
305         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
306
307         up_btn = gtk_button_new_with_label (_("Up"));
308         gtk_widget_show (up_btn);
309         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
310         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
311                             GTK_SIGNAL_FUNC (prefs_scoring_up), NULL);
312
313         down_btn = gtk_button_new_with_label (_("Down"));
314         gtk_widget_show (down_btn);
315         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
316         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
317                             GTK_SIGNAL_FUNC (prefs_scoring_down), NULL);
318
319         gtk_widget_show_all(window);
320
321         scoring.window    = window;
322         scoring.ok_btn = ok_btn;
323
324         scoring.cond_entry = cond_entry;
325         scoring.score_entry = score_entry;
326
327         scoring.cond_clist   = cond_clist;
328 }
329
330 static void prefs_scoring_set_dialog(ScoringProp * cond)
331 {
332         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
333         GSList *cur;
334
335         if (cond == NULL)
336                 prefs_scoring_reset_dialog();
337         else
338                 prefs_scoring_select_set_dialog(cond);
339
340         gtk_clist_freeze(clist);
341         gtk_clist_clear(clist);
342
343         prefs_scoring_clist_set_row(-1, NULL);
344         for(cur = prefs_scoring ; cur != NULL ; cur = g_slist_next(cur)) {
345                 ScoringProp * prop = (ScoringProp *) cur->data;
346
347                 prefs_scoring_clist_set_row(-1, prop);
348         }
349
350         gtk_clist_thaw(clist);
351 }
352
353 static void prefs_scoring_reset_dialog(void)
354 {
355         gtk_entry_set_text(GTK_ENTRY(scoring.cond_entry), "");
356         gtk_entry_set_text(GTK_ENTRY(scoring.score_entry), "");
357 }
358
359 static void prefs_scoring_set_list(void)
360 {
361         gint row = 1;
362         ScoringProp *prop;
363         GSList * cur;
364         gchar * scoring_str;
365         gchar * tmp;
366
367         for(cur = prefs_scoring ; cur != NULL ; cur = g_slist_next(cur))
368                 scoringprop_free((ScoringProp *) cur->data);
369         g_slist_free(prefs_scoring);
370         prefs_scoring = NULL;
371
372         while (gtk_clist_get_text(GTK_CLIST(scoring.cond_clist),
373                                   row, 0, &scoring_str)) {
374                 if (strcmp(scoring_str, _("(New)")) != 0) {
375                         tmp = scoring_str;
376                         prop = scoringprop_parse(&tmp);
377                         if (prop != NULL)
378                                 prefs_scoring = g_slist_append(prefs_scoring,
379                                                                prop);
380                 }
381                 row++;
382         }
383 }
384
385 static gint prefs_scoring_clist_set_row(gint row, ScoringProp * prop)
386 {
387         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
388         gchar * str;
389         gchar *cond_str[1];
390
391         if (prop == NULL) {
392                 cond_str[0] = _("(New)");
393                 return gtk_clist_append(clist, cond_str);
394         }
395
396         str = scoringprop_to_string(prop);
397         if (str == NULL) {
398                 return -1;
399         }
400         cond_str[0] = str;
401
402         if (row < 0)
403                 row = gtk_clist_append(clist, cond_str);
404         else
405                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
406         g_free(str);
407
408         return row;
409 }
410
411 static void prefs_scoring_condition_define_done(MatcherList * matchers)
412 {
413         gchar * str;
414
415         if (matchers == NULL)
416                 return;
417
418         str = matcherlist_to_string(matchers);
419
420         if (str != NULL) {
421                 gtk_entry_set_text(GTK_ENTRY(scoring.cond_entry), str);
422                 g_free(str);
423         }
424 }
425
426 static void prefs_scoring_condition_define(void)
427 {
428         gchar * cond_str;
429         MatcherList * matchers = NULL;
430
431         cond_str = gtk_entry_get_text(GTK_ENTRY(scoring.cond_entry));
432
433         if (*cond_str != '\0') {
434                 gchar * tmp;
435                 
436                 tmp = cond_str;
437                 matchers = matcherlist_parse(&tmp);
438                 if (tmp == NULL)
439                         alertpanel_error(_("Match string is not valid."));
440         }
441
442         prefs_matcher_open(matchers, prefs_scoring_condition_define_done);
443
444         if (matchers != NULL)
445                 matcherlist_free(matchers);
446 }
447
448
449 /* register / substitute delete buttons */
450
451 static void prefs_scoring_register_cb(void)
452 {
453         MatcherList * cond;
454         gchar * cond_str;
455         gchar * score_str;
456         ScoringProp * prop;
457         gint score;
458         gchar * tmp;
459
460         cond_str = gtk_entry_get_text(GTK_ENTRY(scoring.cond_entry));
461         if (*cond_str == '\0') {
462                 alertpanel_error(_("Score is not set."));
463                 return;
464         }
465
466         score_str = gtk_entry_get_text(GTK_ENTRY(scoring.score_entry));
467         if (*score_str == '\0') {
468                 alertpanel_error(_("Match string is not set."));
469                 return;
470         }
471
472         score = atoi(score_str);
473         tmp = cond_str;
474         cond = matcherlist_parse(&tmp);
475
476         if (tmp == NULL) {
477                 alertpanel_error(_("Match string is not valid."));
478                 return;
479         }
480
481         prop = scoringprop_new(cond, score);
482
483         prefs_scoring_clist_set_row(-1, prop);
484
485         scoringprop_free(prop);
486
487         prefs_scoring_reset_dialog();
488 }
489
490 static void prefs_scoring_substitute_cb(void)
491 {
492         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
493         gint row;
494         MatcherList * cond;
495         gchar * cond_str;
496         gchar * score_str;
497         ScoringProp * prop;
498         gint score;
499         gchar * tmp;
500
501         if (!clist->selection) return;
502
503         row = GPOINTER_TO_INT(clist->selection->data);
504         if (row == 0) return;
505
506         cond_str = gtk_entry_get_text(GTK_ENTRY(scoring.cond_entry));
507         if (*cond_str == '\0') {
508                 alertpanel_error(_("Score is not set."));
509                 return;
510         }
511
512         score_str = gtk_entry_get_text(GTK_ENTRY(scoring.score_entry));
513         if (*score_str == '\0') {
514                 alertpanel_error(_("Match string is not set."));
515                 return;
516         }
517
518         score = atoi(score_str);
519         tmp = cond_str;
520         cond = matcherlist_parse(&tmp);
521
522         if (tmp == NULL) {
523                 alertpanel_error(_("Match string is not valid."));
524                 return;
525         }
526
527         prop = scoringprop_new(cond, score);
528
529         prefs_scoring_clist_set_row(row, prop);
530
531         scoringprop_free(prop);
532
533         prefs_scoring_reset_dialog();
534 }
535
536 static void prefs_scoring_delete_cb(void)
537 {
538         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
539         gint row;
540
541         if (!clist->selection) return;
542         row = GPOINTER_TO_INT(clist->selection->data);
543         if (row == 0) return;
544
545         if (alertpanel(_("Delete rule"),
546                        _("Do you really want to delete this rule?"),
547                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
548                 return;
549
550         gtk_clist_remove(clist, row);
551 }
552
553 static void prefs_scoring_up(void)
554 {
555         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
556         gint row;
557
558         if (!clist->selection) return;
559
560         row = GPOINTER_TO_INT(clist->selection->data);
561         if (row > 1) {
562                 gtk_clist_row_move(clist, row, row - 1);
563         }
564 }
565
566 static void prefs_scoring_down(void)
567 {
568         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
569         gint row;
570
571         if (!clist->selection) return;
572
573         row = GPOINTER_TO_INT(clist->selection->data);
574         if (row > 0 && row < clist->rows - 1) {
575                 gtk_clist_row_move(clist, row, row + 1);
576         }
577 }
578
579 static void prefs_scoring_select_set_dialog(ScoringProp * prop)
580 {
581         gchar * matcher_str;
582         gchar * score_str;
583
584         if (prop == NULL)
585                 return;
586
587         matcher_str = matcherlist_to_string(prop->matchers);
588         if (matcher_str == NULL) {
589                 scoringprop_free(prop);
590                 return;
591         }
592
593         score_str = itos(prop->score);
594
595         gtk_entry_set_text(GTK_ENTRY(scoring.cond_entry), matcher_str);
596         gtk_entry_set_text(GTK_ENTRY(scoring.score_entry), score_str);
597
598         g_free(matcher_str);
599 }
600
601 static void prefs_scoring_select(GtkCList *clist, gint row, gint column,
602                                 GdkEvent *event)
603 {
604         ScoringProp * prop;
605         gchar * tmp;
606
607         gchar * scoring_str;
608
609         if (row == 0) {
610                 prefs_scoring_reset_dialog();
611                 return;
612         }
613
614         if (!gtk_clist_get_text(GTK_CLIST(scoring.cond_clist),
615                                 row, 0, &scoring_str))
616                 return;
617
618         tmp = scoring_str;
619         prop = scoringprop_parse(&tmp);
620         if (tmp == NULL)
621                 return;
622
623         prefs_scoring_select_set_dialog(prop);
624
625         scoringprop_free(prop);
626 }
627
628 static gint prefs_scoring_deleted(GtkWidget *widget, GdkEventAny *event,
629                                  gpointer data)
630 {
631         prefs_scoring_cancel();
632         return TRUE;
633 }
634
635 static void prefs_scoring_key_pressed(GtkWidget *widget, GdkEventKey *event,
636                                      gpointer data)
637 {
638         if (event && event->keyval == GDK_Escape)
639                 prefs_scoring_cancel();
640 }
641
642 static void prefs_scoring_ok(void)
643 {
644         prefs_scoring_set_list();
645         prefs_scoring_write_config();
646         gtk_widget_hide(scoring.window);
647 }
648
649 static void prefs_scoring_cancel(void)
650 {
651         prefs_scoring_read_config();
652         gtk_widget_hide(scoring.window);
653 }