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