2004-12-08 [colin] 0.9.13cvs6.1
[claws.git] / src / gtk / prefswindow.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto and the Sylpheed-Claws Team
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 <string.h>
25 #include <gtk/gtk.h>
26 #include <gdk/gdkkeysyms.h>
27
28 #include "intl.h"
29 #include "utils.h"
30 #include "prefswindow.h"
31 #include "gtkutils.h"
32
33 typedef struct _PrefsWindow PrefsWindow;
34 typedef struct _PrefsTreeNode PrefsTreeNode;
35
36 struct _PrefsWindow
37 {
38         GtkWidget *window;
39         GtkWidget *table1;
40         GtkWidget *scrolledwindow1;
41         GtkWidget *ctree;
42         GtkWidget *table2;
43         GtkWidget *pagelabel;
44         GtkWidget *labelframe;
45         GtkWidget *frame;
46         GtkWidget *notebook;
47         GtkWidget *confirm_area;
48         GtkWidget *ok_btn;
49         GtkWidget *cancel_btn;
50         GtkWidget *apply_btn;
51
52         GtkWidget *empty_page;
53
54         gpointer         data;
55         GSList          *prefs_pages;
56         GtkDestroyNotify func;
57 };
58
59 struct _PrefsTreeNode
60 {
61         PrefsPage *page;
62         gfloat     treeweight;
63 };
64
65 static gboolean ctree_select_row(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
66 {
67         PrefsTreeNode *prefsnode;
68         PrefsPage *page;
69         PrefsWindow *prefswindow = (PrefsWindow *) user_data;
70         gchar *labeltext;
71         gint pagenum, i;
72
73         prefsnode = gtk_ctree_node_get_row_data(GTK_CTREE(ctree), GTK_CTREE_NODE(node));
74         page = prefsnode->page;
75
76         debug_print("%f\n", prefsnode->treeweight);
77
78         if (page == NULL) {
79                 gtk_label_set_text(GTK_LABEL(prefswindow->pagelabel), "");
80                 pagenum = gtk_notebook_page_num(GTK_NOTEBOOK(prefswindow->notebook),
81                                                 prefswindow->empty_page);
82                 gtk_notebook_set_page(GTK_NOTEBOOK(prefswindow->notebook), pagenum);
83                 return FALSE;
84         }
85
86         if (!page->page_open) {
87                 page->create_widget(page, GTK_WINDOW(prefswindow->window), prefswindow->data);
88                 gtk_container_add(GTK_CONTAINER(prefswindow->notebook), page->widget);
89                 page->page_open = TRUE;
90         }
91
92         i = 0;
93         while (page->path[i + 1] != 0)
94                 i++;
95         labeltext = page->path[i];
96
97         gtk_label_set_text(GTK_LABEL(prefswindow->pagelabel), labeltext);
98
99         pagenum = gtk_notebook_page_num(GTK_NOTEBOOK(prefswindow->notebook),
100                                         page->widget);
101         gtk_notebook_set_page(GTK_NOTEBOOK(prefswindow->notebook), pagenum);
102
103         return FALSE;
104 }
105
106 static void save_all_pages(GSList *prefs_pages)
107 {
108         GSList *cur;
109
110         for (cur = prefs_pages; cur != NULL; cur = g_slist_next(cur)) {
111                 PrefsPage *page = (PrefsPage *) cur->data;
112
113                 if (page->page_open) {
114                         page->save_page(page);
115                 }
116         }
117 }
118
119 static void close_all_pages(GSList *prefs_pages)
120 {
121         GSList *cur;
122
123         for (cur = prefs_pages; cur != NULL; cur = g_slist_next(cur)) {
124                 PrefsPage *page = (PrefsPage *) cur->data;
125
126                 if (page->page_open) {
127                         page->destroy_widget(page);
128                         page->page_open = FALSE;
129                 }
130         }       
131 }
132
133 static void apply_button_released(GtkButton *button, gpointer user_data)
134 {
135         PrefsWindow *prefswindow = (PrefsWindow *) user_data;
136
137         save_all_pages(prefswindow->prefs_pages);
138 }
139
140 static void close_prefs_window(PrefsWindow *prefswindow)
141 {
142         debug_print("prefs window closed\n");
143
144         close_all_pages(prefswindow->prefs_pages);
145         gtk_widget_destroy(prefswindow->window);
146         g_slist_free(prefswindow->prefs_pages);
147         if(prefswindow->func != NULL)
148                 prefswindow->func(prefswindow->data);
149         g_free(prefswindow);
150 }
151
152 static void ok_button_released(GtkButton *button, gpointer user_data)
153 {
154         PrefsWindow *prefswindow = (PrefsWindow *) user_data;
155
156         save_all_pages(prefswindow->prefs_pages);
157         close_prefs_window(prefswindow);
158 }
159
160 static void cancel_button_released(GtkButton *button, gpointer user_data)
161 {
162         PrefsWindow *prefswindow = (PrefsWindow *) user_data;
163
164         close_prefs_window(prefswindow);
165 }
166
167 static gboolean window_closed(GtkWidget *widget, GdkEvent *event, gpointer user_data)
168 {
169         PrefsWindow *prefswindow = (PrefsWindow *) user_data;
170
171         close_prefs_window(prefswindow);
172         return FALSE;
173 }
174
175 struct name_search
176 {
177         gchar *text;
178         GtkCTreeNode *node;
179 };
180
181 static gboolean find_child_by_name(GtkCTree *ctree, GtkCTreeNode *node, struct name_search *name_search)
182 {
183         gchar *text = NULL;
184
185         text = GTK_CELL_TEXT(GTK_CTREE_ROW(node)->row.cell[0])->text;
186         if (text == NULL)
187                 return FALSE;
188
189         if (strcmp(text, name_search->text) == 0)
190                 name_search->node = node;
191
192         return FALSE;
193 }
194
195 gint compare_func(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
196 {
197         PrefsTreeNode *prefsnode1 = ((GtkCListRow *)ptr1)->data;
198         PrefsTreeNode *prefsnode2 = ((GtkCListRow *)ptr2)->data;
199
200         if (prefsnode1 == NULL || prefsnode2 == NULL)
201                 return 0;
202
203         return prefsnode1->treeweight > prefsnode2->treeweight ? -1 : 
204                prefsnode1->treeweight < prefsnode2->treeweight ?  1 : 
205                                                                   0;
206 }
207
208 static gboolean prefswindow_key_pressed(GtkWidget *widget, GdkEventKey *event,
209                                     gpointer data)
210 {
211         if (event) {
212                 switch (event->keyval) {
213                         case GDK_Escape :
214                                 cancel_button_released(NULL, data);
215                                 break;
216                         case GDK_Return : 
217                         case GDK_KP_Enter :
218                                 ok_button_released(NULL, data);
219                                 break;
220                         default:
221                                 break;
222                 }
223         }
224         return FALSE;
225 }
226
227 void prefswindow_open_full(const gchar *title, GSList *prefs_pages, gpointer data, GtkDestroyNotify func)
228 {
229         static gchar *titles[1];
230         GSList *cur;
231         gint optsize;
232         PrefsWindow *prefswindow;
233
234         titles[0] = _("Page Index");
235
236         prefswindow = g_new0(PrefsWindow, 1);
237
238         prefswindow->data = data;
239         prefswindow->func = func;
240         prefswindow->prefs_pages = g_slist_copy(prefs_pages);
241
242         prefswindow->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
243         gtk_window_set_title(GTK_WINDOW(prefswindow->window), title);
244         gtk_window_set_default_size(GTK_WINDOW(prefswindow->window), 600, 340);
245         gtk_window_set_position (GTK_WINDOW(prefswindow->window), GTK_WIN_POS_CENTER);
246         gtk_window_set_modal (GTK_WINDOW (prefswindow->window), TRUE);
247         gtk_window_set_resizable (GTK_WINDOW(prefswindow->window), TRUE);
248         gtk_container_set_border_width(GTK_CONTAINER(prefswindow->window), 4);
249
250         prefswindow->table1 = gtk_table_new(2, 2, FALSE);
251         gtk_widget_show(prefswindow->table1);
252         gtk_container_add(GTK_CONTAINER(prefswindow->window), prefswindow->table1);
253
254         prefswindow->scrolledwindow1 = gtk_scrolled_window_new(NULL, NULL);
255         gtk_widget_show(prefswindow->scrolledwindow1);
256         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(prefswindow->scrolledwindow1), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
257         gtk_table_attach(GTK_TABLE(prefswindow->table1), prefswindow->scrolledwindow1, 0, 1, 0, 1, GTK_FILL, GTK_FILL | GTK_EXPAND, 2, 2);
258
259         prefswindow->ctree = gtk_ctree_new_with_titles(1, 0, titles);
260         gtk_widget_show(prefswindow->ctree);
261         gtk_container_add(GTK_CONTAINER(prefswindow->scrolledwindow1), prefswindow->ctree);
262
263         prefswindow->frame = gtk_frame_new(NULL);
264         gtk_widget_show(prefswindow->frame);
265         gtk_frame_set_shadow_type(GTK_FRAME(prefswindow->frame), GTK_SHADOW_IN);
266         gtk_table_attach(GTK_TABLE(prefswindow->table1), prefswindow->frame, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 2, 2);
267
268         prefswindow->table2 = gtk_table_new(1, 2, FALSE);
269         gtk_widget_show(prefswindow->table2);
270         gtk_container_add(GTK_CONTAINER(prefswindow->frame), prefswindow->table2);
271
272         prefswindow->labelframe = gtk_frame_new(NULL);
273         gtk_widget_show(prefswindow->labelframe);
274         gtk_frame_set_shadow_type(GTK_FRAME(prefswindow->labelframe), GTK_SHADOW_OUT);
275         gtk_table_attach(GTK_TABLE(prefswindow->table2), prefswindow->labelframe, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 1, 1);
276
277         prefswindow->pagelabel = gtk_label_new("");
278         gtk_widget_show(prefswindow->pagelabel);
279         gtk_label_set_justify(GTK_LABEL(prefswindow->pagelabel), GTK_JUSTIFY_LEFT);
280         gtk_misc_set_alignment(GTK_MISC(prefswindow->pagelabel), 0, 0.0);
281         gtk_container_add(GTK_CONTAINER(prefswindow->labelframe), prefswindow->pagelabel);
282
283         prefswindow->notebook = gtk_notebook_new();
284         gtk_widget_show(prefswindow->notebook);
285         gtk_notebook_set_scrollable(GTK_NOTEBOOK(prefswindow->notebook), TRUE);
286         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(prefswindow->notebook), FALSE);
287         gtk_notebook_set_show_border(GTK_NOTEBOOK(prefswindow->notebook), FALSE);
288         gtk_table_attach(GTK_TABLE(prefswindow->table2), prefswindow->notebook, 0, 1, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 8, 8);
289
290         prefswindow->empty_page = gtk_label_new("");
291         gtk_widget_show(prefswindow->empty_page);
292         gtk_container_add(GTK_CONTAINER(prefswindow->notebook), prefswindow->empty_page);
293
294         /* actually we should create a tree here */
295         for (cur = prefs_pages; cur != NULL; cur = g_slist_next(cur)) {
296                 PrefsPage *page = (PrefsPage *)cur->data;
297                 GtkCTreeNode *node = NULL;
298                 gchar *text[2], *part;
299                 int i;
300                 struct name_search name_search;
301                 PrefsTreeNode *prefsnode;
302                 for (i = 0; page->path[i] != NULL; i++) {
303                         part = page->path[i];
304                         name_search.text = part;
305                         name_search.node = NULL;
306
307                         gtk_ctree_post_recursive_to_depth(GTK_CTREE(prefswindow->ctree), node, node != NULL ? GTK_CTREE_ROW(node)->level + 1 : 1, GTK_CTREE_FUNC(find_child_by_name), &name_search);
308
309                         if (name_search.node) {
310                                 node = name_search.node;
311                         } else {
312                                 text[0] = part;
313                                 node = gtk_ctree_insert_node(GTK_CTREE(prefswindow->ctree), node, NULL, text, 0, NULL, NULL, NULL, NULL, FALSE, TRUE);
314
315                                 prefsnode = g_new0(PrefsTreeNode, 1);
316                                 prefsnode->treeweight = 0.0;
317                                 gtk_ctree_node_set_row_data_full(GTK_CTREE(prefswindow->ctree), node, prefsnode, g_free);
318                         }
319                 }
320
321                 prefsnode = (PrefsTreeNode *) GTK_CTREE_ROW(node)->row.data;
322                 prefsnode->page = page;
323
324                 for (; node != NULL; node = GTK_CTREE_ROW(node)->parent) {
325                         PrefsTreeNode *curnode = (PrefsTreeNode *) GTK_CTREE_ROW(node)->row.data;
326
327                         if (page->weight > curnode->treeweight)
328                                 curnode->treeweight = page->weight;
329                 }
330         }
331         g_signal_connect(G_OBJECT(prefswindow->ctree), "tree-select-row", 
332                          G_CALLBACK(ctree_select_row), prefswindow);
333
334         gtk_clist_set_selection_mode(GTK_CLIST(prefswindow->ctree), GTK_SELECTION_BROWSE);
335         gtk_clist_column_titles_passive(GTK_CLIST(prefswindow->ctree));
336         optsize = gtk_clist_optimal_column_width(GTK_CLIST(prefswindow->ctree), 0);
337         gtk_clist_set_column_resizeable(GTK_CLIST(prefswindow->ctree), 0, TRUE);
338         gtk_clist_set_column_auto_resize(GTK_CLIST(prefswindow->ctree), 0, FALSE);
339         gtk_clist_set_column_width(GTK_CLIST(prefswindow->ctree), 0, optsize);
340         gtk_clist_set_column_min_width(GTK_CLIST(prefswindow->ctree), 0, optsize);
341         gtk_clist_set_column_max_width(GTK_CLIST(prefswindow->ctree), 0, optsize);
342         gtk_clist_set_compare_func(GTK_CLIST(prefswindow->ctree), compare_func);
343         gtk_ctree_sort_recursive(GTK_CTREE(prefswindow->ctree), NULL);
344         gtk_widget_grab_focus(GTK_WIDGET(prefswindow->ctree));
345
346         gtkut_button_set_create(&prefswindow->confirm_area,
347                                 &prefswindow->ok_btn,           _("OK"),
348                                 &prefswindow->cancel_btn,       _("Cancel"),
349                                 &prefswindow->apply_btn,        _("Apply"));
350
351         gtk_widget_show_all(prefswindow->confirm_area);
352
353         gtk_table_attach(GTK_TABLE(prefswindow->table1), prefswindow->confirm_area, 0, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 2, 2);
354
355         g_signal_connect(G_OBJECT(prefswindow->ok_btn), "released", 
356                          G_CALLBACK(ok_button_released), prefswindow);
357         g_signal_connect(G_OBJECT(prefswindow->cancel_btn), "released", 
358                          G_CALLBACK(cancel_button_released), prefswindow);
359         g_signal_connect(G_OBJECT(prefswindow->apply_btn), "released", 
360                          G_CALLBACK(apply_button_released), prefswindow);
361         g_signal_connect(G_OBJECT(prefswindow->window), "delete_event", 
362                          G_CALLBACK(window_closed), prefswindow);
363         g_signal_connect(G_OBJECT(prefswindow->window), "key_press_event",
364                            G_CALLBACK(prefswindow_key_pressed), &(prefswindow->window));
365
366         gtk_widget_show(prefswindow->window);
367 }
368
369 void prefswindow_open(const gchar *title, GSList *prefs_pages, gpointer data)
370 {
371         prefswindow_open_full(title, prefs_pages, data, NULL);
372 }