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