further merging: 0.4.67cvs6
[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
488 static void prefs_scoring_substitute_cb(void)
489 {
490         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
491         gint row;
492         MatcherList * cond;
493         gchar * cond_str;
494         gchar * score_str;
495         ScoringProp * prop;
496         gint score;
497         gchar * tmp;
498
499         if (!clist->selection) return;
500
501         row = GPOINTER_TO_INT(clist->selection->data);
502         if (row == 0) return;
503
504         cond_str = gtk_entry_get_text(GTK_ENTRY(scoring.cond_entry));
505         if (*cond_str == '\0') {
506                 alertpanel_error(_("Score is not set."));
507                 return;
508         }
509
510         score_str = gtk_entry_get_text(GTK_ENTRY(scoring.score_entry));
511         if (*score_str == '\0') {
512                 alertpanel_error(_("Match string is not set."));
513                 return;
514         }
515
516         score = atoi(score_str);
517         tmp = cond_str;
518         cond = matcherlist_parse(&tmp);
519
520         if (tmp == NULL) {
521                 alertpanel_error(_("Match string is not valid."));
522                 return;
523         }
524
525         prop = scoringprop_new(cond, score);
526
527         prefs_scoring_clist_set_row(row, prop);
528
529         scoringprop_free(prop);
530 }
531
532 static void prefs_scoring_delete_cb(void)
533 {
534         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
535         gint row;
536
537         if (!clist->selection) return;
538         row = GPOINTER_TO_INT(clist->selection->data);
539         if (row == 0) return;
540
541         if (alertpanel(_("Delete rule"),
542                        _("Do you really want to delete this rule?"),
543                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
544                 return;
545
546         gtk_clist_remove(clist, row);
547 }
548
549 static void prefs_scoring_up(void)
550 {
551         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
552         gint row;
553
554         if (!clist->selection) return;
555
556         row = GPOINTER_TO_INT(clist->selection->data);
557         if (row > 1) {
558                 gtk_clist_row_move(clist, row, row - 1);
559         }
560 }
561
562 static void prefs_scoring_down(void)
563 {
564         GtkCList *clist = GTK_CLIST(scoring.cond_clist);
565         gint row;
566
567         if (!clist->selection) return;
568
569         row = GPOINTER_TO_INT(clist->selection->data);
570         if (row > 0 && row < clist->rows - 1) {
571                 gtk_clist_row_move(clist, row, row + 1);
572         }
573 }
574
575 static void prefs_scoring_select_set_dialog(ScoringProp * prop)
576 {
577         gchar * matcher_str;
578         gchar * score_str;
579
580         if (prop == NULL)
581                 return;
582
583         matcher_str = matcherlist_to_string(prop->matchers);
584         if (matcher_str == NULL) {
585                 scoringprop_free(prop);
586                 return;
587         }
588
589         score_str = itos(prop->score);
590
591         gtk_entry_set_text(GTK_ENTRY(scoring.cond_entry), matcher_str);
592         gtk_entry_set_text(GTK_ENTRY(scoring.score_entry), score_str);
593
594         g_free(matcher_str);
595 }
596
597 static void prefs_scoring_select(GtkCList *clist, gint row, gint column,
598                                 GdkEvent *event)
599 {
600         ScoringProp * prop;
601         gchar * tmp;
602
603         gchar * scoring_str;
604
605         if (!gtk_clist_get_text(GTK_CLIST(scoring.cond_clist),
606                                 row, 0, &scoring_str))
607                 return;
608         
609         tmp = scoring_str;
610         prop = scoringprop_parse(&tmp);
611         if (tmp == NULL)
612                 return;
613
614         prefs_scoring_select_set_dialog(prop);
615
616         scoringprop_free(prop);
617 }
618
619 static gint prefs_scoring_deleted(GtkWidget *widget, GdkEventAny *event,
620                                  gpointer data)
621 {
622         prefs_scoring_cancel();
623         return TRUE;
624 }
625
626 static void prefs_scoring_key_pressed(GtkWidget *widget, GdkEventKey *event,
627                                      gpointer data)
628 {
629         if (event && event->keyval == GDK_Escape)
630                 prefs_scoring_cancel();
631 }
632
633 static void prefs_scoring_ok(void)
634 {
635         prefs_scoring_set_list();
636         prefs_scoring_write_config();
637         gtk_widget_hide(scoring.window);
638 }
639
640 static void prefs_scoring_cancel(void)
641 {
642         prefs_scoring_read_config();
643         gtk_widget_hide(scoring.window);
644 }