displaying of headers in the mail viewer - customization
[claws.git] / src / prefs_display_headers.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_display_headers.h"
38 #include "prefs_common.h"
39 #include "mainwindow.h"
40 #include "foldersel.h"
41 #include "manage_window.h"
42 #include "utils.h"
43 #include "gtkutils.h"
44 #include "alertpanel.h"
45 #include "folder.h"
46 #include "headers_display.h"
47
48 static struct Headers {
49         GtkWidget *window;
50
51         GtkWidget *close_btn;
52
53         GtkWidget *hdr_combo;
54         GtkWidget *hdr_entry;
55         GtkWidget *key_check;
56         GtkWidget *headers_clist;
57 } headers;
58
59 /*
60    parameter name, default value, pointer to the prefs variable, data type,
61    pointer to the widget pointer,
62    pointer to the function for data setting,
63    pointer to the function for widget setting
64  */
65
66 #define VSPACING                12
67 #define VSPACING_NARROW         4
68 #define DEFAULT_ENTRY_WIDTH     80
69 #define PREFSBUFSIZE            1024
70
71 /* widget creating functions */
72 static void prefs_display_headers_create                (void);
73
74 static void prefs_display_headers_set_dialog    (void);
75 static void prefs_display_headers_set_list      (void);
76 static gint prefs_display_headers_clist_set_row (gint    row);
77
78 /* callback functions */
79 static void prefs_display_headers_select_dest_cb        (void);
80 static void prefs_display_headers_register_cb   (void);
81 static void prefs_display_headers_substitute_cb (void);
82 static void prefs_display_headers_delete_cb     (void);
83 static void prefs_display_headers_up            (void);
84 static void prefs_display_headers_down          (void);
85 static void prefs_display_headers_select                (GtkCList       *clist,
86                                          gint            row,
87                                          gint            column,
88                                          GdkEvent       *event);
89
90 static void prefs_display_headers_dest_radio_button_toggled     (void);
91 static void prefs_display_headers_notrecv_radio_button_toggled  (void);
92
93 static void prefs_display_headers_key_pressed   (GtkWidget      *widget,
94                                          GdkEventKey    *event,
95                                          gpointer        data);
96 static void prefs_display_headers_close         (GtkButton      *button);
97
98 GSList * prefs_display_headers = NULL;
99
100 static char * defaults[] =
101 {
102         "Sender",
103         "From",
104         "Reply-To",
105         "To",
106         "Cc",
107         "Subject",
108         "Date",
109         "-Received",
110         "-Message-Id",
111         "-In-Reply-To",
112         "-References",
113         "-Mime-Version",
114         "-Content-Type",
115         "-Content-Transfer-Encoding",
116         "-X-UIDL",
117         "-Precedence",
118         "-Status",
119         "-Priority"
120 };
121
122 static void prefs_display_headers_set_default(void)
123 {
124         int i;
125
126         for(i = 0 ; i < sizeof(defaults) / sizeof(char *) ; i++) {
127                 HeaderDisplayProp * dp =
128                         header_display_prop_read_str(defaults[i]);
129                 prefs_display_headers =
130                         g_slist_append(prefs_display_headers, dp);
131         }
132 }
133
134 void prefs_display_headers_open(void)
135 {
136         if (!headers.window) {
137                 prefs_display_headers_create();
138         }
139
140         manage_window_set_transient(GTK_WINDOW(headers.window));
141         gtk_widget_grab_focus(headers.close_btn);
142
143         prefs_display_headers_set_dialog();
144
145         gtk_widget_show(headers.window);
146 }
147
148 static void prefs_display_headers_create(void)
149 {
150         GtkWidget *window;
151         GtkWidget *vbox;
152         GtkWidget *close_btn;
153         GtkWidget *confirm_area;
154
155         GtkWidget *vbox1;
156
157         GtkWidget *table1;
158         GtkWidget *hdr_label;
159         GtkWidget *hdr_combo;
160         GtkWidget *key_label;
161         GtkWidget *key_check;
162
163         GtkWidget *reg_hbox;
164         GtkWidget *btn_hbox;
165         GtkWidget *arrow;
166         GtkWidget *reg_btn;
167         GtkWidget *subst_btn;
168         GtkWidget *del_btn;
169
170         GtkWidget *ch_hbox;
171         GtkWidget *ch_scrolledwin;
172         GtkWidget *headers_clist;
173
174         GtkWidget *btn_vbox;
175         GtkWidget *up_btn;
176         GtkWidget *down_btn;
177
178         gchar *title[] = {_("Custom headers")};
179
180         debug_print(_("Creating headers setting window...\n"));
181
182         window = gtk_window_new (GTK_WINDOW_DIALOG);
183         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
184         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
185         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
186         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
187
188         vbox = gtk_vbox_new (FALSE, 6);
189         gtk_widget_show (vbox);
190         gtk_container_add (GTK_CONTAINER (window), vbox);
191
192         gtkut_button_set_create (&confirm_area, &close_btn, _("Close"),
193                                  NULL, NULL, NULL, NULL);
194         gtk_widget_show (confirm_area);
195         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
196         gtk_widget_grab_default (close_btn);
197
198         gtk_window_set_title (GTK_WINDOW(window),
199                               _("Headers setting"));
200         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
201                             GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete), NULL);
202         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
203                             GTK_SIGNAL_FUNC(prefs_display_headers_key_pressed),
204                             NULL);
205         gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
206                             GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
207         gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
208                             GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
209         gtk_signal_connect (GTK_OBJECT(close_btn), "clicked",
210                             GTK_SIGNAL_FUNC(prefs_display_headers_close),
211                             NULL);
212
213         vbox1 = gtk_vbox_new (FALSE, VSPACING);
214         gtk_widget_show (vbox1);
215         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
216         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
217
218         table1 = gtk_table_new (2, 2, FALSE);
219         gtk_widget_show (table1);
220         gtk_box_pack_start (GTK_BOX (vbox1), table1,
221                             FALSE, FALSE, 0);
222         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
223         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
224
225         hdr_label = gtk_label_new (_("Header"));
226         gtk_widget_show (hdr_label);
227         gtk_table_attach (GTK_TABLE (table1), hdr_label, 0, 1, 0, 1,
228                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
229                           0, 0, 0);
230         gtk_misc_set_alignment (GTK_MISC (hdr_label), 0, 0.5);
231         
232         hdr_combo = gtk_combo_new ();
233         gtk_widget_show (hdr_combo);
234         gtk_table_attach (GTK_TABLE (table1), hdr_combo, 0, 1, 1, 2,
235                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
236                           0, 0, 0);
237         gtk_widget_set_usize (hdr_combo, 150 /* 96 */, -1);
238         gtkut_combo_set_items (GTK_COMBO (hdr_combo),
239                                "From", "To", "Subject", "Date", NULL);
240
241         key_label = gtk_label_new (_("Hide"));
242         gtk_widget_show (key_label);
243         gtk_table_attach (GTK_TABLE (table1), key_label, 1, 2, 0, 1,
244                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
245                           0, 0, 0);
246         gtk_misc_set_alignment (GTK_MISC (key_label), 0, 0.5);
247         
248         //      key_entry = gtk_entry_new ();
249         key_check = gtk_check_button_new();
250         gtk_widget_show (key_check);
251         gtk_table_attach (GTK_TABLE (table1), key_check, 1, 2, 1, 2,
252                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
253                           0, 0, 0);
254
255         /* register / substitute / delete */
256
257         reg_hbox = gtk_hbox_new (FALSE, 4);
258         gtk_widget_show (reg_hbox);
259         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox,
260                             FALSE, FALSE, 0);
261
262         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
263         gtk_widget_show (arrow);
264         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
265         gtk_widget_set_usize (arrow, -1, 16);
266
267         btn_hbox = gtk_hbox_new (TRUE, 4);
268         gtk_widget_show (btn_hbox);
269         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
270
271         reg_btn = gtk_button_new_with_label (_("Add"));
272         gtk_widget_show (reg_btn);
273         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
274         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
275                             GTK_SIGNAL_FUNC
276                             (prefs_display_headers_register_cb), NULL);
277
278         subst_btn = gtk_button_new_with_label (_(" Substitute "));
279         gtk_widget_show (subst_btn);
280         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
281         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
282                             GTK_SIGNAL_FUNC
283                             (prefs_display_headers_substitute_cb),
284                             NULL);
285
286         del_btn = gtk_button_new_with_label (_("Delete"));
287         gtk_widget_show (del_btn);
288         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
289         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
290                             GTK_SIGNAL_FUNC (prefs_display_headers_delete_cb),
291                             NULL);
292
293
294         ch_hbox = gtk_hbox_new (FALSE, 8);
295         gtk_widget_show (ch_hbox);
296         gtk_box_pack_start (GTK_BOX (vbox1), ch_hbox,
297                             TRUE, TRUE, 0);
298
299         ch_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
300         gtk_widget_set_usize (ch_scrolledwin, -1, 100);
301         gtk_widget_show (ch_scrolledwin);
302         gtk_box_pack_start (GTK_BOX (ch_hbox), ch_scrolledwin,
303                             TRUE, TRUE, 0);
304         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (ch_scrolledwin),
305                                         GTK_POLICY_AUTOMATIC,
306                                         GTK_POLICY_AUTOMATIC);
307
308         headers_clist = gtk_clist_new_with_titles(1, title);
309         gtk_widget_show (headers_clist);
310         gtk_container_add (GTK_CONTAINER (ch_scrolledwin), headers_clist);
311         gtk_clist_set_column_width (GTK_CLIST (headers_clist), 0, 80);
312         gtk_clist_set_selection_mode (GTK_CLIST (headers_clist),
313                                       GTK_SELECTION_BROWSE);
314         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (headers_clist)->column[0].button,
315                                 GTK_CAN_FOCUS);
316         gtk_signal_connect (GTK_OBJECT (headers_clist), "select_row",
317                             GTK_SIGNAL_FUNC (prefs_display_headers_select),
318                             NULL);
319
320
321         btn_vbox = gtk_vbox_new (FALSE, 8);
322         gtk_widget_show (btn_vbox);
323         gtk_box_pack_start (GTK_BOX (ch_hbox), btn_vbox, FALSE, FALSE, 0);
324
325         up_btn = gtk_button_new_with_label (_("Up"));
326         gtk_widget_show (up_btn);
327         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
328         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
329                             GTK_SIGNAL_FUNC (prefs_display_headers_up), NULL);
330
331         down_btn = gtk_button_new_with_label (_("Down"));
332         gtk_widget_show (down_btn);
333         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
334         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
335                             GTK_SIGNAL_FUNC (prefs_display_headers_down), NULL);
336
337
338         gtk_widget_show_all(window);
339
340         headers.window    = window;
341         headers.close_btn = close_btn;
342
343         headers.hdr_combo  = hdr_combo;
344         headers.hdr_entry  = GTK_COMBO (hdr_combo)->entry;
345         headers.key_check  = key_check;
346         headers.headers_clist   = headers_clist;
347 }
348
349 void prefs_display_headers_read_config(void)
350 {
351         gchar *rcpath;
352         FILE *fp;
353         gchar buf[PREFSBUFSIZE];
354         HeaderDisplayProp *dp;
355
356         debug_print(_("Reading configuration for displaying of headers...\n"));
357
358         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
359                              HEADERS_DISPLAY_RC, NULL);
360         if ((fp = fopen(rcpath, "r")) == NULL) {
361                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
362                 g_free(rcpath);
363                 prefs_display_headers = NULL;
364                 prefs_display_headers_set_default();
365                 return;
366         }
367         g_free(rcpath);
368
369         /* remove all previous headers list */
370         while (prefs_display_headers != NULL) {
371                 dp = (HeaderDisplayProp *)prefs_display_headers->data;
372                 header_display_prop_free(dp);
373                 prefs_display_headers =
374                         g_slist_remove(prefs_display_headers, dp);
375         }
376  
377         while (fgets(buf, sizeof(buf), fp) != NULL) {
378                 g_strchomp(buf);
379                 dp = header_display_prop_read_str(buf);
380                 if (dp) {
381                         prefs_display_headers =
382                                 g_slist_append(prefs_display_headers, dp);
383                 }
384         }
385  
386         fclose(fp);
387 }
388
389 void prefs_display_headers_write_config(void)
390 {
391         gchar *rcpath;
392         PrefFile *pfile;
393         GSList *cur;
394         gchar buf[PREFSBUFSIZE];
395         FILE * fp;
396         HeaderDisplayProp *dp;
397
398         debug_print(_("Writing configuration for displaying of headers...\n"));
399
400         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
401                              HEADERS_DISPLAY_RC, NULL);
402
403         if ((pfile = prefs_write_open(rcpath)) == NULL) {
404                 g_warning(_("failed to write configuration to file\n"));
405                 g_free(rcpath);
406                 return;
407         }
408
409         for (cur = prefs_display_headers; cur != NULL; cur = cur->next) {
410                 HeaderDisplayProp *hdr = (HeaderDisplayProp *)cur->data;
411                 gchar *dpstr;
412
413                 dpstr = header_display_prop_get_str(hdr);
414                 if (fputs(dpstr, pfile->fp) == EOF ||
415                     fputc('\n', pfile->fp) == EOF) {
416                         FILE_OP_ERROR(rcpath, "fputs || fputc");
417                         prefs_write_close_revert(pfile);
418                         g_free(rcpath);
419                         g_free(dpstr);
420                         return;
421                 }
422                 g_free(dpstr);
423         }
424
425         g_free(rcpath);
426
427         if (prefs_write_close(pfile) < 0) {
428                 g_warning(_("failed to write configuration to file\n"));
429                 return;
430         }
431 }
432
433 static void prefs_display_headers_set_dialog()
434 {
435         GtkCList *clist = GTK_CLIST(headers.headers_clist);
436         GSList *cur;
437         gchar *dp_str[1];
438         gint row;
439
440         gtk_clist_freeze(clist);
441         gtk_clist_clear(clist);
442
443         dp_str[0] = _("(New)");
444         row = gtk_clist_append(clist, dp_str);
445         gtk_clist_set_row_data(clist, row, NULL);
446
447         for (cur = prefs_display_headers; cur != NULL; cur = cur->next) {
448                 HeaderDisplayProp *dp = (HeaderDisplayProp *)cur->data;
449
450                 if (dp->hidden)
451                         dp_str[0] = g_strdup_printf("(%s)", dp->name);
452                 else
453                         dp_str[0] = g_strdup_printf("%s", dp->name);
454                 row = gtk_clist_append(clist, dp_str);
455                 gtk_clist_set_row_data(clist, row, dp);
456
457                 g_free(dp_str[0]);
458         }
459
460         gtk_clist_thaw(clist);
461 }
462
463 static void prefs_display_headers_set_list()
464 {
465         gint row = 1;
466         HeaderDisplayProp *dp;
467
468         g_slist_free(prefs_display_headers);
469         prefs_display_headers = NULL;
470
471         while ((dp = gtk_clist_get_row_data(GTK_CLIST(headers.headers_clist),
472                 row)) != NULL) {
473                 prefs_display_headers = g_slist_append(prefs_display_headers,
474                                                        dp);
475                 row++;
476         }
477 }
478
479 #define GET_ENTRY(entry) \
480         entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
481
482 static gint prefs_display_headers_clist_set_row(gint row)
483 {
484         GtkCList *clist = GTK_CLIST(headers.headers_clist);
485         HeaderDisplayProp *dp;
486         gchar *entry_text;
487         gchar *dp_str[1];
488
489         g_return_val_if_fail(row != 0, -1);
490
491         GET_ENTRY(headers.hdr_entry);
492         if (entry_text[0] == '\0') {
493                 alertpanel_error(_("Header name is not set."));
494                 return -1;
495         }
496
497         dp = g_new0(HeaderDisplayProp, 1);
498
499         dp->name = g_strdup(entry_text);
500
501         dp->hidden = gtk_toggle_button_get_active
502                 (GTK_TOGGLE_BUTTON(headers.key_check));
503
504         if (dp->hidden)
505                 dp_str[0] = g_strdup_printf("(%s)", dp->name);
506         else
507                 dp_str[0] = g_strdup_printf("%s", dp->name);
508
509         if (row < 0)
510                 row = gtk_clist_append(clist, dp_str);
511         else {
512                 HeaderDisplayProp *tmpdp;
513
514                 gtk_clist_set_text(clist, row, 0, dp_str[0]);
515                 tmpdp = gtk_clist_get_row_data(clist, row);
516                 if (tmpdp)
517                         header_display_prop_free(tmpdp);
518         }
519
520         gtk_clist_set_row_data(clist, row, dp);
521
522         g_free(dp_str[0]);
523
524         prefs_display_headers_set_list();
525
526         return row;
527 }
528
529 static void prefs_display_headers_register_cb(void)
530 {
531         prefs_display_headers_clist_set_row(-1);
532 }
533
534 static void prefs_display_headers_substitute_cb(void)
535 {
536         GtkCList *clist = GTK_CLIST(headers.headers_clist);
537         HeaderDisplayProp *dp;
538         gint row;
539
540         if (!clist->selection) return;
541
542         row = GPOINTER_TO_INT(clist->selection->data);
543         if (row == 0) return;
544
545         dp = gtk_clist_get_row_data(clist, row);
546         if (!dp) return;
547
548         prefs_display_headers_clist_set_row(row);
549 }
550
551 static void prefs_display_headers_delete_cb(void)
552 {
553         GtkCList *clist = GTK_CLIST(headers.headers_clist);
554         HeaderDisplayProp *dp;
555         gint row;
556
557         if (!clist->selection) return;
558         row = GPOINTER_TO_INT(clist->selection->data);
559         if (row == 0) return;
560
561         if (alertpanel(_("Delete header"),
562                        _("Do you really want to delete this header?"),
563                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
564                 return;
565
566         dp = gtk_clist_get_row_data(clist, row);
567         header_display_prop_free(dp);
568         gtk_clist_remove(clist, row);
569         prefs_display_headers = g_slist_remove(prefs_display_headers, dp);
570 }
571
572 static void prefs_display_headers_up(void)
573 {
574         GtkCList *clist = GTK_CLIST(headers.headers_clist);
575         gint row;
576
577         if (!clist->selection) return;
578
579         row = GPOINTER_TO_INT(clist->selection->data);
580         if (row > 1) {
581                 gtk_clist_row_move(clist, row, row - 1);
582                 prefs_display_headers_set_list();
583         }
584 }
585
586 static void prefs_display_headers_down(void)
587 {
588         GtkCList *clist = GTK_CLIST(headers.headers_clist);
589         gint row;
590
591         if (!clist->selection) return;
592
593         row = GPOINTER_TO_INT(clist->selection->data);
594         if (row > 0 && row < clist->rows - 1) {
595                 gtk_clist_row_move(clist, row, row + 1);
596                 prefs_display_headers_set_list();
597         }
598 }
599
600 #define ENTRY_SET_TEXT(entry, str) \
601         gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
602
603 static void prefs_display_headers_select(GtkCList *clist, gint row,
604                                          gint column, GdkEvent *event)
605 {
606         HeaderDisplayProp *dp;
607         HeaderDisplayProp default_dp = { "", 0 };
608  
609         dp = gtk_clist_get_row_data(clist, row);
610         if (!dp)
611                 dp = &default_dp;
612  
613         ENTRY_SET_TEXT(headers.hdr_entry, dp->name);
614         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(headers.key_check),
615                                      dp->hidden);
616 }
617
618 static void prefs_display_headers_key_pressed(GtkWidget *widget,
619                                               GdkEventKey *event,
620                                               gpointer data)
621 {
622         if (event && event->keyval == GDK_Escape)
623                 gtk_widget_hide(headers.window);
624 }
625
626 static void prefs_display_headers_close(GtkButton *button)
627 {
628         prefs_display_headers_write_config();
629         gtk_widget_hide(headers.window);
630 }