managesieve: ensure space above Refresh button
[claws.git] / src / plugins / managesieve / sieve_manager.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2004-2015 the Claws Mail team
4  * Copyright (C) 2014-2015 Charles Lehner
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  * 
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #include "claws-features.h"
24 #endif
25
26 #include <gtk/gtk.h>
27 #include <glib.h>
28 #include <glib/gi18n.h>
29
30 #include "defs.h"
31 #include "gtk/gtkutils.h"
32 #include "gtk/combobox.h"
33 #include "gtk/inputdialog.h"
34 #include "gtk/manage_window.h"
35 #include "alertpanel.h"
36 #include "utils.h"
37 #include "prefs.h"
38 #include "account.h"
39 #include "mainwindow.h"
40 #include "managesieve.h"
41 #include "sieve_editor.h"
42 #include "sieve_prefs.h"
43 #include "sieve_manager.h"
44
45 enum {
46         FILTER_NAME,
47         FILTER_ACTIVE,
48         N_FILTER_COLUMNS
49 };
50
51 typedef struct {
52         SieveManagerPage *page;
53         gchar *name_old;
54         gchar *name_new;
55 } CommandDataRename;
56
57 typedef struct {
58         SieveManagerPage *page;
59         gchar *filter_name;
60 } CommandDataName;
61
62 static void filter_got_load_error(SieveSession *session, gpointer data);
63 static void account_changed(GtkWidget *widget, SieveManagerPage *page);
64 static void sieve_manager_close(GtkWidget *widget, SieveManagerPage *page);
65 static gboolean sieve_manager_deleted(GtkWidget *widget, GdkEvent *event,
66                 SieveManagerPage *page);
67 static void filter_set_active(SieveManagerPage *page, gchar *filter_name);
68 gboolean filter_find_by_name (GtkTreeModel *model, GtkTreeIter *iter,
69                 gchar *filter_name);
70 static void got_session_error(SieveSession *session, const gchar *msg,
71                 SieveManagerPage *page);
72
73 static GSList *manager_pages = NULL;
74
75 /*
76  * Perform a command on all manager pages for a given session
77  */
78 #define manager_sessions_foreach(cur, session, page) \
79         for(cur = manager_pages; cur != NULL; cur = cur->next) \
80                 if ((page = (SieveManagerPage *)cur->data) && \
81                         page->active_session == session)
82
83 void sieve_managers_done()
84 {
85         GSList *list = manager_pages;
86         manager_pages = NULL;
87         g_slist_free_full(list, (GDestroyNotify)sieve_manager_done);
88 }
89
90 static void filters_list_clear(SieveManagerPage *page)
91 {
92         GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list)));
93         gtk_list_store_clear(list_store);
94         page->got_list = FALSE;
95 }
96
97 static void filters_list_delete_filter(SieveManagerPage *page, gchar *name)
98 {
99         GtkTreeIter iter;
100         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list));
101
102         if (!filter_find_by_name(model, &iter, name))
103                 return;
104
105         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
106 }
107
108
109 static void filters_list_rename_filter(SieveManagerPage *page,
110                 gchar *name_old, char *name_new)
111 {
112         GtkTreeIter iter;
113         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list));
114
115         if (!filter_find_by_name(model, &iter, name_old))
116                 return;
117
118         gtk_list_store_set(GTK_LIST_STORE(model), &iter,
119                         FILTER_NAME, name_new,
120                         -1);
121 }
122
123 static void filters_list_insert_filter(SieveManagerPage *page,
124                 SieveScript *filter)
125 {
126         GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list)));
127         GtkTreeIter iter;
128
129         gtk_list_store_append(list_store, &iter);
130         gtk_list_store_set(list_store, &iter,
131                         FILTER_NAME, filter->name,
132                         FILTER_ACTIVE, filter->active,
133                         -1);
134 }
135
136 static gchar *filters_list_get_selected_filter(GtkWidget *list_view)
137 {
138         GtkTreeSelection *selector;
139         GtkTreeModel *model;
140         GtkTreeIter iter;
141         gchar *res = NULL;
142
143         selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
144
145         if (!gtk_tree_selection_get_selected(selector, &model, &iter))
146                 return NULL;
147
148         gtk_tree_model_get(model, &iter, FILTER_NAME, &res, -1);
149
150         return res;
151 }
152
153 static void filter_add(GtkWidget *widget, SieveManagerPage *page)
154 {
155         SieveSession *session = page->active_session;
156         SieveEditorPage *editor;
157         if (!session)
158                 return;
159         gchar *filter_name = input_dialog(_("Add Sieve script"),
160                         _("Enter name for a new Sieve filter script."), "");
161         if (!filter_name || !filter_name[0])
162                 return;
163
164         editor = sieve_editor_get(session, filter_name);
165         if (editor) {
166                 /* TODO: show error that filter already exists */
167                 sieve_editor_present(editor);
168                 g_free(filter_name);
169                 sieve_editor_load(editor,
170                         (sieve_session_cb_fn)filter_got_load_error, page);
171         } else {
172                 editor = sieve_editor_new(session, filter_name);
173                 editor->is_new = TRUE;
174                 sieve_editor_show(editor);
175         }
176 }
177
178 static void filter_got_load_error(SieveSession *session, gpointer data)
179 {
180         SieveManagerPage *page = data;
181
182         got_session_error(session, _("Unable to get script contents"), page);
183 }
184
185 static void filter_edit(GtkWidget *widget, SieveManagerPage *page)
186 {
187         SieveEditorPage *editor;
188         SieveSession *session = page->active_session;
189
190         if (!session)
191                 return;
192
193         gchar *filter_name = filters_list_get_selected_filter(page->filters_list);
194         if (!filter_name)
195                 return;
196
197         editor = sieve_editor_get(session, filter_name);
198         if (editor) {
199                 sieve_editor_present(editor);
200         } else {
201                 editor = sieve_editor_new(session, filter_name);
202                 sieve_editor_load(editor,
203                         (sieve_session_cb_fn)filter_got_load_error, page);
204         }
205 }
206
207 static void filter_renamed(SieveSession *session, gboolean abort,
208                 gboolean success, CommandDataRename *data)
209 {
210         SieveManagerPage *page = data->page;
211         GSList *cur;
212
213         if (abort) {
214         } else if (!success) {
215                 got_session_error(session, "Unable to rename script", page);
216         } else {
217                 manager_sessions_foreach(cur, session, page) {
218                         filters_list_rename_filter(page, data->name_old,
219                                         data->name_new);
220                 }
221         }
222         g_free(data->name_old);
223         g_free(data->name_new);
224         g_free(data);
225 }
226
227 static void filter_rename(GtkWidget *widget, SieveManagerPage *page)
228 {
229         CommandDataRename *cmd_data;
230         gchar *name_old, *name_new;
231         SieveSession *session;
232
233         name_old = filters_list_get_selected_filter(page->filters_list);
234         if (!name_old)
235                 return;
236
237         session = page->active_session;
238         if (!session)
239                 return;
240
241         name_new = input_dialog(_("Add Sieve script"),
242                         _("Enter new name for the script."), name_old);
243         if (!name_new)
244                 return;
245
246         cmd_data = g_new(CommandDataRename, 1);
247         cmd_data->name_new = name_new;
248         cmd_data->name_old = name_old;
249         cmd_data->page = page;
250         sieve_session_rename_script(session, name_old, name_new,
251                         (sieve_session_data_cb_fn)filter_renamed, (gpointer)cmd_data);
252 }
253
254 static void filter_activated(SieveSession *session, gboolean abort,
255                 const gchar *err, CommandDataName *cmd_data)
256 {
257         SieveManagerPage *page = cmd_data->page;
258         GSList *cur;
259
260         if (abort) {
261         } else if (err) {
262                 got_session_error(session, err, page);
263         } else {
264                 manager_sessions_foreach(cur, session, page) {
265                         filter_set_active(page, cmd_data->filter_name);
266                 }
267         }
268         g_free(cmd_data->filter_name);
269         g_free(cmd_data);
270 }
271
272 static void sieve_set_active_filter(SieveManagerPage *page, gchar *filter_name)
273 {
274         SieveSession *session;
275         CommandDataName *cmd_data;
276
277         session = page->active_session;
278         cmd_data = g_new(CommandDataName, 1);
279         cmd_data->filter_name = filter_name;
280         cmd_data->page = page;
281
282         sieve_session_set_active_script(session, filter_name,
283                         (sieve_session_data_cb_fn)filter_activated, cmd_data);
284 }
285
286 static void filter_deleted(SieveSession *session, gboolean abort,
287                 const gchar *err_msg,
288                 CommandDataName *cmd_data)
289 {
290         SieveManagerPage *page = cmd_data->page;
291         GSList *cur;
292
293         if (abort) {
294         } else if (err_msg) {
295                 got_session_error(session, err_msg, page);
296         } else {
297                 manager_sessions_foreach(cur, session, page) {
298                         filters_list_delete_filter(page,
299                                         cmd_data->filter_name);
300                 }
301         }
302         g_free(cmd_data->filter_name);
303         g_free(cmd_data);
304 }
305
306
307 static void filter_delete(GtkWidget *widget, SieveManagerPage *page)
308 {
309         gchar buf[256];
310         gchar *filter_name;
311         SieveSession *session;
312         CommandDataName *cmd_data;
313
314         filter_name = filters_list_get_selected_filter(page->filters_list);
315         if (filter_name == NULL)
316                 return;
317
318         session = page->active_session;
319         if (!session)
320                 return;
321
322         g_snprintf(buf, sizeof(buf),
323                    _("Do you really want to delete the filter '%s'?"), filter_name);
324         if (alertpanel_full(_("Delete filter"), buf,
325                                 GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, FALSE,
326                                 NULL, ALERT_WARNING, G_ALERTDEFAULT) != G_ALERTALTERNATE)
327                 return;
328
329         cmd_data = g_new(CommandDataName, 1);
330         cmd_data->filter_name = filter_name;
331         cmd_data->page = page;
332
333         sieve_session_delete_script(session, filter_name,
334                         (sieve_session_data_cb_fn)filter_deleted, cmd_data);
335 }
336
337 /*
338  * select a filter in the list
339  *
340  * return TRUE is successfully selected, FALSE otherwise
341  */
342
343 static gboolean filter_select (GtkWidget *list_view, GtkTreeModel *model,
344                 GtkTreeIter *iter)
345 {
346         GtkTreeSelection *selection;
347         GtkTreePath* path;
348
349         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
350         gtk_tree_selection_select_iter(selection, iter);
351         path = gtk_tree_model_get_path(model, iter);
352         if (path == NULL) return FALSE;
353         gtk_tree_view_set_cursor(GTK_TREE_VIEW(list_view), path, NULL, FALSE);
354         gtk_tree_path_free(path);
355         return TRUE;
356 }
357
358 /*
359  * find matching filter. return FALSE on match
360  */
361 static gboolean filter_search_equal_fn (GtkTreeModel *model, gint column,
362                 const gchar *key, GtkTreeIter *iter, gpointer search_data)
363 {
364         SieveManagerPage *page = (SieveManagerPage *)search_data;
365         gchar *filter_name;
366
367         if (!key) return TRUE;
368
369         gtk_tree_model_get (model, iter, FILTER_NAME, &filter_name, -1);
370
371         if (strncmp (key, filter_name, strlen(key)) != 0) return TRUE;
372         return !filter_select(page->filters_list, model, iter);
373 }
374
375 /*
376  * search for a filter row by its name. return true if found.
377  */
378 gboolean filter_find_by_name (GtkTreeModel *model, GtkTreeIter *iter,
379                 gchar *filter_name)
380 {
381         gchar *name;
382
383         if (!gtk_tree_model_get_iter_first (model, iter))
384                 return FALSE;
385
386         do {
387                 gtk_tree_model_get (model, iter, FILTER_NAME, &name, -1);
388                 if (strcmp(filter_name, name) == 0) {
389                         return TRUE;
390                 }
391         } while (gtk_tree_model_iter_next (model, iter));
392         return FALSE;
393 }
394
395 static gboolean filter_set_inactive(GtkTreeModel *model,
396                 GtkTreePath *path, GtkTreeIter *iter, gpointer data)
397 {
398         gtk_list_store_set(GTK_LIST_STORE(model), iter,
399                         FILTER_ACTIVE, FALSE,
400                         -1);
401         return FALSE;
402 }
403
404 /*
405  * Set the active filter in the table.
406  * @param filter_name The filter to make active (may be null)
407  */
408 static void filter_set_active(SieveManagerPage *page, gchar *filter_name)
409 {
410         GtkTreeIter iter;
411         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list));
412
413         /* Deactivate all filters */
414         gtk_tree_model_foreach(model, filter_set_inactive, NULL);
415
416         /* Set active filter */
417         if (filter_name) {
418                 if (!filter_find_by_name (model, &iter, filter_name))
419                         return;
420
421                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
422                                 FILTER_ACTIVE, TRUE,
423                                 -1);
424         }
425 }
426
427 static void filter_active_toggled(GtkCellRendererToggle *widget,
428                                     gchar *path,
429                                     SieveManagerPage *page)
430 {
431         GtkTreeIter iter;
432         gchar *filter_name;
433         gboolean active;
434         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list));
435
436         if (!gtk_tree_model_get_iter_from_string(model, &iter, path))
437                 return;
438
439         /* get existing value */
440         gtk_tree_model_get(model, &iter,
441                         FILTER_NAME, &filter_name,
442                         FILTER_ACTIVE, &active,
443                         -1);
444
445         sieve_set_active_filter(page, active ? NULL : filter_name);
446 }
447
448 static void filter_double_clicked(GtkTreeView *list_view,
449                                    GtkTreePath *path, GtkTreeViewColumn *column,
450                                    SieveManagerPage *page)
451 {
452         filter_edit(GTK_WIDGET(list_view), page);
453 }
454
455 static void filters_create_list_view_columns(SieveManagerPage *page,
456                 GtkWidget *list_view)
457 {
458         GtkTreeViewColumn *column;
459         GtkCellRenderer *renderer;
460         GtkWidget *label;
461
462         /* Name */
463         renderer = gtk_cell_renderer_text_new();
464         column = gtk_tree_view_column_new_with_attributes
465                 (_("Name"), renderer,
466                  "text", FILTER_NAME,
467                  NULL);
468         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
469         gtk_tree_view_column_set_expand(column, TRUE);
470
471         /* Active */
472         renderer = gtk_cell_renderer_toggle_new();
473         g_object_set(renderer,
474                      "radio", TRUE,
475                      "activatable", TRUE,
476                       NULL);
477         column = gtk_tree_view_column_new_with_attributes
478                 (_("Active"), renderer,
479                  "active", FILTER_ACTIVE,
480                  NULL);
481         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
482         gtk_tree_view_column_set_alignment (column, 0.5);
483
484         /* the column header needs a widget to have a tooltip */
485         label = gtk_label_new(gtk_tree_view_column_get_title(column));
486         gtk_widget_show(label);
487         gtk_tree_view_column_set_widget(column, label);
488         CLAWS_SET_TIP(label,
489                         _("An account can only have one active script at a time."));
490
491         g_signal_connect(G_OBJECT(renderer), "toggled",
492                          G_CALLBACK(filter_active_toggled), page);
493
494         gtk_tree_view_set_search_column(GTK_TREE_VIEW(list_view), FILTER_NAME);
495         gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(list_view),
496                         filter_search_equal_fn, page, NULL);
497 }
498
499
500 static GtkListStore* filters_create_data_store(void)
501 {
502         return gtk_list_store_new(N_FILTER_COLUMNS,
503                         G_TYPE_STRING,  /* FILTER_NAME */
504                         G_TYPE_BOOLEAN, /* FILTER_ACTIVE */
505                         -1);
506 }
507
508 static GtkWidget *filters_list_view_create(SieveManagerPage *page)
509 {
510         GtkTreeView *list_view;
511         GtkTreeSelection *selector;
512         GtkListStore *store = filters_create_data_store();
513
514         list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
515         g_object_unref(G_OBJECT(store));
516
517         selector = gtk_tree_view_get_selection(list_view);
518         gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
519
520         /* create the columns */
521         filters_create_list_view_columns(page, GTK_WIDGET(list_view));
522
523         /* set a double click listener */
524         g_signal_connect(G_OBJECT(list_view), "row_activated",
525                         G_CALLBACK(filter_double_clicked),
526                         page);
527
528         return GTK_WIDGET(list_view);
529 }
530
531 static gboolean manager_key_pressed(GtkWidget *widget, GdkEventKey *event,
532                                     gpointer data)
533 {
534         SieveManagerPage* page = (SieveManagerPage *) data;
535
536         if (event && event->keyval == GDK_KEY_Escape)
537                 sieve_manager_done(page);
538
539         return FALSE;
540 }
541
542 static void size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation)
543 {
544         cm_return_if_fail(allocation != NULL);
545
546         sieve_config.manager_win_width = allocation->width;
547         sieve_config.manager_win_height = allocation->height;
548 }
549
550 static void got_session_error(SieveSession *session, const gchar *msg,
551                 SieveManagerPage *page)
552 {
553         if (!g_slist_find(manager_pages, page))
554                 return;
555         if (page->active_session != session)
556                 return;
557         gtk_label_set_text(GTK_LABEL(page->status_text), msg);
558 }
559
560 static void sieve_manager_on_error(SieveSession *session,
561                 const gchar *msg, gpointer user_data)
562 {
563         SieveManagerPage *page = (SieveManagerPage *)user_data;
564         got_session_error(session, msg, page);
565 }
566
567 static void sieve_manager_on_connected(SieveSession *session,
568                 gboolean connected, gpointer user_data)
569 {
570         SieveManagerPage *page = (SieveManagerPage *)user_data;
571         if (page->active_session != session)
572                 return;
573         if (!connected) {
574                 gtk_widget_set_sensitive(GTK_WIDGET(page->vbox_buttons), FALSE);
575                 gtk_label_set_text(GTK_LABEL(page->status_text),
576                                 _("Unable to connect"));
577         }
578 }
579
580 static void got_filter_listed(SieveSession *session, gboolean abort,
581                 SieveScript *script, SieveManagerPage *page)
582 {
583         if (abort)
584                 return;
585         if (!script) {
586                 got_session_error(session, "Unable to list scripts", page);
587                 return;
588         }
589         if (!script->name) {
590                 /* done receiving list */
591                 page->got_list = TRUE;
592                 gtk_widget_set_sensitive(GTK_WIDGET(page->vbox_buttons), TRUE);
593                 gtk_label_set_text(GTK_LABEL(page->status_text), "");
594                 return;
595         }
596         filters_list_insert_filter(page, script);
597 }
598
599 /*
600  * An account was selected from the menu. Get its list of scripts.
601  */
602 static void account_changed(GtkWidget *widget, SieveManagerPage *page)
603 {
604         gint account_id;
605         PrefsAccount *account;
606         SieveSession *session;
607
608         account_id = combobox_get_active_data(GTK_COMBO_BOX(page->accounts_menu));
609         account = account_find_from_id(account_id);
610         if (!account)
611                 return;
612         session = page->active_session = sieve_session_get_for_account(account);
613         sieve_session_handle_status(session,
614                         sieve_manager_on_error,
615                         sieve_manager_on_connected,
616                         page);
617         filters_list_clear(page);
618         if (session_is_connected(SESSION(session))) {
619                 gtk_label_set_text(GTK_LABEL(page->status_text),
620                                 _("Listing scripts..."));
621         } else {
622                 gtk_label_set_text(GTK_LABEL(page->status_text),
623                                 _("Connecting..."));
624         }
625         sieve_session_list_scripts(session,
626                         (sieve_session_data_cb_fn)got_filter_listed, (gpointer)page);
627 }
628
629 static SieveManagerPage *sieve_manager_page_new()
630 {
631         SieveManagerPage *page;
632         GtkWidget *window;
633         GtkWidget *hbox, *vbox, *vbox_allbuttons, *vbox_buttons;
634         GtkWidget *accounts_menu;
635         GtkWidget *label;
636         GtkWidget *scrolledwin;
637         GtkWidget *list_view;
638         GtkWidget *btn;
639         GtkWidget *status_text;
640         GtkTreeIter iter;
641         GtkListStore *menu;
642         GList *account_list, *cur;
643         PrefsAccount *ap;
644         SieveAccountConfig *config;
645         PrefsAccount *default_account = NULL;
646
647         static GdkGeometry geometry;
648
649         page = g_new0(SieveManagerPage, 1);
650
651         /* Manage Window */
652
653         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "sievemanager");
654         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
655         gtk_window_set_title (GTK_WINDOW (window), _("Manage Sieve Filters"));
656         MANAGE_WINDOW_SIGNALS_CONNECT (window);
657
658         g_signal_connect (G_OBJECT (window), "key_press_event",
659                         G_CALLBACK (manager_key_pressed), page);
660         g_signal_connect (G_OBJECT(window), "size_allocate",
661                          G_CALLBACK (size_allocate_cb), NULL);
662         g_signal_connect (G_OBJECT(window), "delete_event",
663                          G_CALLBACK (sieve_manager_deleted), page);
664
665         if (!geometry.min_height) {
666                 geometry.min_width = 350;
667                 geometry.min_height = 280;
668         }
669
670         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
671                                       GDK_HINT_MIN_SIZE);
672         gtk_widget_set_size_request(window, sieve_config.manager_win_width,
673                         sieve_config.manager_win_height);
674         gtk_window_set_type_hint(GTK_WINDOW(window),
675                         GDK_WINDOW_TYPE_HINT_DIALOG);
676
677         vbox = gtk_vbox_new (FALSE, 10);
678         gtk_container_add (GTK_CONTAINER (window), vbox);
679
680         hbox = gtk_hbox_new (FALSE, 8);
681         gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
682
683         /* Accounts list */
684
685         label = gtk_label_new (_("Account"));
686         gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
687
688         accounts_menu = gtkut_sc_combobox_create(NULL, FALSE);
689         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(accounts_menu)));
690         gtk_box_pack_start (GTK_BOX (hbox), accounts_menu, FALSE, FALSE, 0);
691         g_signal_connect (G_OBJECT(accounts_menu), "changed",
692                           G_CALLBACK (account_changed), page);
693
694         account_list = account_get_list();
695         for (cur = account_list; cur != NULL; cur = cur->next) {
696                 ap = (PrefsAccount *)cur->data;
697                 config = sieve_prefs_account_get_config(ap);
698                 if (config->enable) {
699                         COMBOBOX_ADD (menu, ap->account_name, ap->account_id);
700                         if (!default_account || ap->is_default)
701                                 default_account = ap;
702                 }
703         }
704
705         if (!default_account) {
706                 gtk_widget_destroy(label);
707                 gtk_widget_destroy(accounts_menu);
708         }
709
710         /* status */
711         status_text = gtk_label_new ("");
712         gtk_box_pack_start (GTK_BOX (hbox), status_text, FALSE, FALSE, 0);
713         gtk_label_set_justify (GTK_LABEL (status_text), GTK_JUSTIFY_LEFT);
714
715         /* Filters list */
716
717         hbox = gtk_hbox_new (FALSE, 8);
718         gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
719         gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
720
721         /* Table */
722
723         scrolledwin = gtk_scrolled_window_new (NULL, NULL);
724         gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
725         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
726                                         GTK_POLICY_AUTOMATIC,
727                                         GTK_POLICY_AUTOMATIC);
728
729         list_view = filters_list_view_create(page);
730         gtk_container_add(GTK_CONTAINER(scrolledwin), list_view);
731
732         /* Buttons */
733
734         vbox_allbuttons = gtk_vbox_new (FALSE, 8);
735         gtk_box_pack_start (GTK_BOX (hbox), vbox_allbuttons, FALSE, FALSE, 0);
736
737         /* buttons that depend on there being a connection */
738         vbox_buttons = gtk_vbox_new (FALSE, 8);
739         gtk_widget_set_sensitive(vbox_buttons, FALSE);
740         gtk_box_pack_start (GTK_BOX (vbox_allbuttons), vbox_buttons, FALSE, FALSE, 0);
741
742         /* new */
743         btn = gtk_button_new_from_stock(GTK_STOCK_NEW);
744         gtk_box_pack_start (GTK_BOX (vbox_buttons), btn, FALSE, FALSE, 0);
745         g_signal_connect (G_OBJECT(btn), "clicked",
746                           G_CALLBACK (filter_add), page);
747
748         /* edit */
749         btn = gtk_button_new_from_stock (GTK_STOCK_EDIT);
750         gtk_box_pack_start (GTK_BOX (vbox_buttons), btn, FALSE, FALSE, 0);
751         g_signal_connect (G_OBJECT(btn), "clicked",
752                         G_CALLBACK (filter_edit), page);
753
754         /* delete */
755         btn = gtk_button_new_from_stock(GTK_STOCK_DELETE);
756         gtk_box_pack_start (GTK_BOX (vbox_buttons), btn, FALSE, FALSE, 0);
757         g_signal_connect (G_OBJECT(btn), "clicked",
758                         G_CALLBACK (filter_delete), page);
759
760         /* rename */
761         btn = gtk_button_new_with_label(_("Rename"));
762         gtk_box_pack_start (GTK_BOX (vbox_buttons), btn, FALSE, FALSE, 0);
763         g_signal_connect (G_OBJECT(btn), "clicked",
764                         G_CALLBACK (filter_rename), page);
765
766         /* refresh */
767         btn = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
768         gtk_box_pack_end (GTK_BOX (vbox_allbuttons), btn, FALSE, FALSE, 0);
769         g_signal_connect (G_OBJECT(btn), "clicked",
770                         G_CALLBACK (account_changed), page);
771
772         /* bottom area stuff */
773
774         gtkut_stock_button_set_create(&hbox,
775                         &btn, GTK_STOCK_CLOSE,
776                         NULL, NULL, NULL, NULL);
777
778         /* close */
779         gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
780         gtk_widget_grab_default (btn);
781         g_signal_connect (G_OBJECT (btn), "clicked",
782                           G_CALLBACK (sieve_manager_close), page);
783
784         page->window = window;
785         page->accounts_menu = accounts_menu;
786         page->filters_list = list_view;
787         page->status_text = status_text;
788         page->vbox_buttons = vbox_buttons;
789
790         /* select default (first) account */
791         if (default_account) {
792                 combobox_select_by_data(GTK_COMBO_BOX(accounts_menu),
793                                 default_account->account_id);
794         } else {
795                 gtk_label_set_text(GTK_LABEL(status_text),
796                                 _("To use Sieve, enable it in an account's preferences."));
797         }
798
799         return page;
800 }
801
802 static gboolean sieve_manager_deleted(GtkWidget *widget, GdkEvent *event,
803                 SieveManagerPage *page)
804 {
805         sieve_manager_done(page);
806         return FALSE;
807 }
808
809 static void sieve_manager_close(GtkWidget *widget, SieveManagerPage *page)
810 {
811         sieve_manager_done(page);
812 }
813
814 void sieve_manager_done(SieveManagerPage *page)
815 {
816         manager_pages = g_slist_remove(manager_pages, page);
817         sieve_sessions_discard_callbacks(page);
818         gtk_widget_destroy(page->window);
819         g_free(page);
820 }
821
822 void sieve_manager_show()
823 {
824         SieveManagerPage *page = sieve_manager_page_new();
825         manager_pages = g_slist_prepend(manager_pages, page);
826         gtk_widget_show_all(page->window);
827 }
828
829 void sieve_manager_script_created(SieveSession *session, const gchar *name)
830 {
831         SieveManagerPage *page;
832         SieveScript script = {.name = (gchar *)name};
833         GSList *cur;
834
835         manager_sessions_foreach(cur, session, page) {
836                 filters_list_insert_filter(page, &script);
837         }
838 }