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