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