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
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.
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.
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/>.
23 #include "claws-features.h"
28 #include <glib/gi18n.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"
39 #include "mainwindow.h"
40 #include "managesieve.h"
41 #include "sieve_editor.h"
42 #include "sieve_prefs.h"
43 #include "sieve_manager.h"
52 SieveManagerPage *page;
58 SieveManagerPage *page;
63 SieveManagerPage *page;
65 SieveEditorPage *editor_page;
67 } CommandDataGetScript;
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,
75 static void got_session_error(SieveSession *session, const gchar *msg,
76 SieveManagerPage *page);
78 static GSList *manager_pages = NULL;
81 * Perform a command on all manager pages for a given session
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)
88 static void filters_list_clear(SieveManagerPage *page)
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;
95 static void filters_list_delete_filter(SieveManagerPage *page, gchar *name)
98 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list));
100 if (!filter_find_by_name(model, &iter, name))
103 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
107 static void filters_list_rename_filter(SieveManagerPage *page,
108 gchar *name_old, char *name_new)
111 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list));
113 if (!filter_find_by_name(model, &iter, name_old))
116 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
117 FILTER_NAME, name_new,
121 static void filters_list_insert_filter(SieveManagerPage *page,
124 GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list)));
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,
134 static gchar *filters_list_get_selected_filter(GtkWidget *list_view)
136 GtkTreeSelection *selector;
141 selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
143 if (!gtk_tree_selection_get_selected(selector, &model, &iter))
146 gtk_tree_model_get(model, &iter, FILTER_NAME, &res, -1);
151 static void filter_add(GtkWidget *widget, SieveManagerPage *page)
153 SieveSession *session = page->active_session;
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])
161 sieve_editor_new(session, filter_name);
163 sieve_session_add_script(session, filter_name
164 (sieve_session_data_cb_fn)filter_added, (gpointer)page);
168 static void filter_got_data(SieveSession *session, gchar *contents,
169 CommandDataGetScript *cmd_data)
171 SieveManagerPage *page = cmd_data->page;
172 SieveEditorPage *editor;
177 } else if (contents == (void *)-1) {
178 got_session_error(session, _("Unable to get script contents"), page);
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;
187 editor = cmd_data->editor_page;
188 sieve_editor_append_text(editor, "\n", 1);
190 sieve_editor_append_text(editor, contents, strlen(contents));
193 static void filter_edit(GtkWidget *widget, SieveManagerPage *page)
195 SieveEditorPage *editor;
196 CommandDataGetScript *cmd_data;
197 SieveSession *session = page->active_session;
200 gchar *filter_name = filters_list_get_selected_filter(page->filters_list);
204 editor = sieve_editor_get(session, filter_name);
206 sieve_editor_present(editor);
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;
213 sieve_session_get_script(session, filter_name,
214 (sieve_session_data_cb_fn)filter_got_data, cmd_data);
218 static void filter_renamed(SieveSession *session, gboolean success,
219 CommandDataRename *data)
221 SieveManagerPage *page = data->page;
225 got_session_error(session, "Unable to rename script", page);
229 manager_sessions_foreach(cur, session, page) {
230 filters_list_rename_filter(page, data->name_old, data->name_new);
235 static void filter_rename(GtkWidget *widget, SieveManagerPage *page)
237 CommandDataRename *cmd_data;
238 gchar *name_old, *name_new;
239 SieveSession *session;
241 name_old = filters_list_get_selected_filter(page->filters_list);
245 session = page->active_session;
249 name_new = input_dialog(_("Add Sieve script"),
250 _("Enter new name for the script."), name_old);
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);
262 static void filter_activated(SieveSession *session, gboolean success,
263 CommandDataName *cmd_data)
265 SieveManagerPage *page = cmd_data->page;
269 got_session_error(session, "Unable to set active script", page);
273 manager_sessions_foreach(cur, session, page) {
274 filter_set_active(page, cmd_data->filter_name);
279 static void sieve_set_active_filter(SieveManagerPage *page, gchar *filter_name)
281 SieveSession *session;
282 CommandDataName *cmd_data;
284 session = page->active_session;
285 cmd_data = g_new(CommandDataName, 1);
286 cmd_data->filter_name = filter_name;
287 cmd_data->page = page;
289 sieve_session_set_active_script(session, filter_name,
290 (sieve_session_data_cb_fn)filter_activated, cmd_data);
294 * activate button clicked
296 static void filter_activate(GtkWidget *widget, SieveManagerPage *page)
298 gchar *filter_name = filters_list_get_selected_filter(page->filters_list);
301 sieve_set_active_filter(page, filter_name);
304 static void filter_deleted(SieveSession *session, const gchar *err_msg,
305 CommandDataName *cmd_data)
307 SieveManagerPage *page = cmd_data->page;
311 got_session_error(session, err_msg, page);
315 manager_sessions_foreach(cur, session, page) {
316 filters_list_delete_filter(page, cmd_data->filter_name);
322 static void filter_delete(GtkWidget *widget, SieveManagerPage *page)
326 SieveSession *session;
327 CommandDataName *cmd_data;
329 filter_name = filters_list_get_selected_filter(page->filters_list);
330 if (filter_name == NULL)
333 session = page->active_session;
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)
344 cmd_data = g_new(CommandDataName, 1);
345 cmd_data->filter_name = filter_name;
346 cmd_data->page = page;
348 sieve_session_delete_script(session, filter_name,
349 (sieve_session_data_cb_fn)filter_deleted, cmd_data);
353 * select a filter in the list
355 * return TRUE is successfully selected, FALSE otherwise
358 static gboolean filter_select (GtkWidget *list_view, GtkTreeModel *model,
361 GtkTreeSelection *selection;
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);
374 * find matching filter. return FALSE on match
376 static gboolean filter_search_equal_fn (GtkTreeModel *model, gint column,
377 const gchar *key, GtkTreeIter *iter, gpointer search_data)
379 SieveManagerPage *page = (SieveManagerPage *)search_data;
382 if (!key) return TRUE;
384 gtk_tree_model_get (model, iter, FILTER_NAME, &filter_name, -1);
386 if (strncmp (key, filter_name, strlen(key)) != 0) return TRUE;
387 return !filter_select(page->filters_list, model, iter);
391 * search for a filter row by its name. return true if found.
393 gboolean filter_find_by_name (GtkTreeModel *model, GtkTreeIter *iter,
398 if (!gtk_tree_model_get_iter_first (model, iter))
402 gtk_tree_model_get (model, iter, FILTER_NAME, &name, -1);
403 if (strcmp(filter_name, name) == 0) {
406 } while (gtk_tree_model_iter_next (model, iter));
410 static gboolean filter_set_inactive(GtkTreeModel *model,
411 GtkTreePath *path, GtkTreeIter *iter, gpointer data)
413 gtk_list_store_set(GTK_LIST_STORE(model), iter,
414 FILTER_ACTIVE, FALSE,
420 * Set the active filter in the table.
421 * @param filter_name The filter to make active (may be null)
423 static void filter_set_active(SieveManagerPage *page, gchar *filter_name)
426 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list));
428 /* Deactivate all filters */
429 gtk_tree_model_foreach(model, filter_set_inactive, NULL);
431 /* Set active filter */
433 if (!filter_find_by_name (model, &iter, filter_name))
436 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
442 static void filter_active_toggled(GtkCellRendererToggle *widget,
444 SieveManagerPage *page)
449 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(page->filters_list));
451 if (!gtk_tree_model_get_iter_from_string(model, &iter, path))
454 /* get existing value */
455 gtk_tree_model_get(model, &iter,
456 FILTER_NAME, &filter_name,
457 FILTER_ACTIVE, &active,
460 sieve_set_active_filter(page, active ? NULL : filter_name);
463 static void filter_double_clicked(GtkTreeView *list_view,
464 GtkTreePath *path, GtkTreeViewColumn *column,
465 SieveManagerPage *page)
467 filter_edit(GTK_WIDGET(list_view), page);
470 static void filters_create_list_view_columns(SieveManagerPage *page,
471 GtkWidget *list_view)
473 GtkTreeViewColumn *column;
474 GtkCellRenderer *renderer;
477 renderer = gtk_cell_renderer_text_new();
478 column = gtk_tree_view_column_new_with_attributes
479 (_("Name"), renderer,
482 gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
483 gtk_tree_view_column_set_expand(column, TRUE);
486 renderer = gtk_cell_renderer_toggle_new();
487 g_object_set(renderer,
491 column = gtk_tree_view_column_new_with_attributes
492 (_("Active"), renderer,
493 "active", FILTER_ACTIVE,
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);
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);
508 static GtkListStore* filters_create_data_store(void)
510 return gtk_list_store_new(N_FILTER_COLUMNS,
511 G_TYPE_STRING, /* FILTER_NAME */
512 G_TYPE_BOOLEAN, /* FILTER_ACTIVE */
516 static GtkWidget *filters_list_view_create(SieveManagerPage *page)
518 GtkTreeView *list_view;
519 GtkTreeSelection *selector;
520 GtkListStore *store = filters_create_data_store();
522 list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
523 g_object_unref(G_OBJECT(store));
525 selector = gtk_tree_view_get_selection(list_view);
526 gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
528 /* create the columns */
529 filters_create_list_view_columns(page, GTK_WIDGET(list_view));
531 /* set a double click listener */
532 g_signal_connect(G_OBJECT(list_view), "row_activated",
533 G_CALLBACK(filter_double_clicked),
536 return GTK_WIDGET(list_view);
539 static gboolean manager_key_pressed(GtkWidget *widget, GdkEventKey *event,
542 SieveManagerPage* page = (SieveManagerPage *) data;
544 if (event && event->keyval == GDK_KEY_Escape)
545 sieve_manager_done(page);
550 static void got_session_error(SieveSession *session, const gchar *msg,
551 SieveManagerPage *page)
553 if (page->active_session != session)
555 gtk_label_set_text(GTK_LABEL(page->status_text), msg);
558 static void sieve_manager_on_error(SieveSession *session,
559 const gchar *msg, gpointer user_data)
561 SieveManagerPage *page = (SieveManagerPage *)user_data;
562 got_session_error(session, msg, page);
565 static void sieve_manager_on_connected(SieveSession *session,
566 gboolean connected, gpointer user_data)
568 SieveManagerPage *page = (SieveManagerPage *)user_data;
569 if (page->active_session != session)
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"));
578 static void got_filter_listed(SieveSession *session, SieveScript *script,
579 SieveManagerPage *page)
582 got_session_error(session, "Unable to list scripts", page);
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), "");
592 filters_list_insert_filter(page, script);
596 * An account was selected from the menu. Get its list of scripts.
598 static void account_changed(GtkWidget *widget, SieveManagerPage *page)
601 PrefsAccount *account;
602 SieveSession *session;
604 account_id = combobox_get_active_data(GTK_COMBO_BOX(page->accounts_menu));
605 account = account_find_from_id(account_id);
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,
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..."));
618 gtk_label_set_text(GTK_LABEL(page->status_text),
621 sieve_session_list_scripts(session,
622 (sieve_session_data_cb_fn)got_filter_listed, (gpointer)page);
625 static SieveManagerPage *sieve_manager_page_new()
627 SieveManagerPage *page;
629 GtkWidget *hbox, *vbox, *vbox_allbuttons, *vbox_buttons;
630 GtkWidget *accounts_menu;
632 GtkWidget *scrolledwin;
633 GtkWidget *list_view;
635 GtkWidget *status_text;
638 GList *account_list, *cur;
640 SieveAccountConfig *config;
641 PrefsAccount *default_account = NULL;
643 page = g_new0(SieveManagerPage, 1);
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);
655 vbox = gtk_vbox_new (FALSE, 10);
656 gtk_container_add (GTK_CONTAINER (window), vbox);
658 hbox = gtk_hbox_new (FALSE, 8);
659 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
663 label = gtk_label_new (_("Account"));
664 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
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);
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;
683 if (!default_account) {
684 gtk_widget_destroy(label);
685 gtk_widget_destroy(accounts_menu);
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);
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);
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);
707 list_view = filters_list_view_create(page);
708 gtk_container_add(GTK_CONTAINER(scrolledwin), list_view);
712 vbox_allbuttons = gtk_vbox_new (FALSE, 0);
713 gtk_box_pack_start (GTK_BOX (hbox), vbox_allbuttons, FALSE, FALSE, 0);
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);
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);
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);
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);
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);
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);
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);
756 /* bottom area stuff */
758 gtkut_stock_button_set_create(&hbox,
759 &btn, GTK_STOCK_CLOSE,
760 NULL, NULL, NULL, NULL);
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);
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;
774 /* select default (first) account */
775 if (default_account) {
776 combobox_select_by_data(GTK_COMBO_BOX(accounts_menu),
777 default_account->account_id);
779 gtk_label_set_text(GTK_LABEL(status_text),
780 _("To use Sieve, enable it in an account's preferences."));
786 void sieve_manager_close(GtkWidget *widget, SieveManagerPage *page)
788 sieve_manager_done(page);
791 void sieve_manager_done(SieveManagerPage *page)
793 manager_pages = g_slist_remove(manager_pages, page);
794 gtk_widget_destroy(page->window);
798 void sieve_manager_show()
800 SieveManagerPage *page = sieve_manager_page_new();
801 manager_pages = g_slist_prepend(manager_pages, page);
802 gtk_widget_show_all(page->window);