diff options
Diffstat (limited to 'mail/em-vfolder-rule.c')
-rw-r--r-- | mail/em-vfolder-rule.c | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/mail/em-vfolder-rule.c b/mail/em-vfolder-rule.c new file mode 100644 index 0000000000..ec4f9bbb2a --- /dev/null +++ b/mail/em-vfolder-rule.c @@ -0,0 +1,643 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright(C)2000-2002 Ximian Inc. + * + * Author: Not Zed <notzed@lostzed.mmc.com.au> + * Jeffrey Stedfast <fejj@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <libgnome/gnome-i18n.h> + +#include "camel/camel-url.h" +#include "em-vfolder-context.h" +#include "em-vfolder-rule.h" +#include "mail/em-utils.h" +#include "mail/em-folder-tree.h" +#include "mail/em-folder-selector.h" +#include "mail/mail-component.h" +#include "widgets/misc/e-error.h" + +#define d(x) + +static int validate(FilterRule *); +static int vfolder_eq(FilterRule *fr, FilterRule *cm); +static xmlNodePtr xml_encode(FilterRule *); +static int xml_decode(FilterRule *, xmlNodePtr, RuleContext *f); +static void rule_copy(FilterRule *dest, FilterRule *src); +/*static void build_code(FilterRule *, GString *out);*/ +static GtkWidget *get_widget(FilterRule *fr, RuleContext *f); + +static void em_vfolder_rule_class_init(EMVFolderRuleClass *klass); +static void em_vfolder_rule_init(EMVFolderRule *vr); +static void em_vfolder_rule_finalise(GObject *obj); + +/* DO NOT internationalise these strings */ +const char *with_names[] = { + "specific", + "local", + "remote_active", + "local_remote_active" +}; + +static FilterRuleClass *parent_class = NULL; + +GType +em_vfolder_rule_get_type(void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof(EMVFolderRuleClass), + NULL, /* base_class_init */ + NULL, /* base_class_finalize */ + (GClassInitFunc)em_vfolder_rule_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(EMVFolderRule), + 0, /* n_preallocs */ + (GInstanceInitFunc)em_vfolder_rule_init, + }; + + type = g_type_register_static(FILTER_TYPE_RULE, "EMVFolderRule", &info, 0); + } + + return type; +} + +static void +em_vfolder_rule_class_init(EMVFolderRuleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FilterRuleClass *fr_class =(FilterRuleClass *)klass; + + parent_class = g_type_class_ref(FILTER_TYPE_RULE); + + object_class->finalize = em_vfolder_rule_finalise; + + /* override methods */ + fr_class->validate = validate; + fr_class->eq = vfolder_eq; + fr_class->xml_encode = xml_encode; + fr_class->xml_decode = xml_decode; + fr_class->copy = rule_copy; + /*fr_class->build_code = build_code;*/ + fr_class->get_widget = get_widget; +} + +static void +em_vfolder_rule_init(EMVFolderRule *vr) +{ + vr->with = EM_VFOLDER_RULE_WITH_SPECIFIC; +} + +static void +em_vfolder_rule_finalise(GObject *obj) +{ + EMVFolderRule *vr =(EMVFolderRule *)obj; + + g_list_foreach(vr->sources, (GFunc)g_free, NULL); + g_list_free(vr->sources); + + G_OBJECT_CLASS(parent_class)->finalize(obj); +} + +/** + * em_vfolder_rule_new: + * + * Create a new EMVFolderRule object. + * + * Return value: A new #EMVFolderRule object. + **/ +EMVFolderRule * +em_vfolder_rule_new(void) +{ + return (EMVFolderRule *)g_object_new(em_vfolder_rule_get_type(), NULL, NULL); +} + +void +em_vfolder_rule_add_source(EMVFolderRule *vr, const char *uri) +{ + g_assert(EM_IS_VFOLDER_RULE(vr)); + + vr->sources = g_list_append(vr->sources, g_strdup(uri)); + + filter_rule_emit_changed((FilterRule *)vr); +} + +const char * +em_vfolder_rule_find_source(EMVFolderRule *vr, const char *uri) +{ + GList *l; + + g_assert(EM_IS_VFOLDER_RULE(vr)); + + /* only does a simple string or address comparison, should + probably do a decoded url comparison */ + l = vr->sources; + while (l) { + if (l->data == uri || !strcmp(l->data, uri)) + return l->data; + l = l->next; + } + + return NULL; +} + +void +em_vfolder_rule_remove_source(EMVFolderRule *vr, const char *uri) +{ + char *found; + + g_assert(EM_IS_VFOLDER_RULE(vr)); + + found =(char *)em_vfolder_rule_find_source(vr, uri); + if (found) { + vr->sources = g_list_remove(vr->sources, found); + g_free(found); + filter_rule_emit_changed((FilterRule *)vr); + } +} + +const char * +em_vfolder_rule_next_source(EMVFolderRule *vr, const char *last) +{ + GList *node; + + if (last == NULL) { + node = vr->sources; + } else { + node = g_list_find(vr->sources, (char *)last); + if (node == NULL) + node = vr->sources; + else + node = g_list_next(node); + } + + if (node) + return (const char *)node->data; + + return NULL; +} + +static int +validate(FilterRule *fr) +{ + g_return_val_if_fail(fr != NULL, FALSE); + + if (!fr->name || !*fr->name) { + /* FIXME: set a aprent window? */ + e_error_run(NULL, "mail:no-name-vfolder", NULL); + return 0; + } + + /* We have to have at least one source set in the "specific" case. + Do not translate this string! */ + if (((EMVFolderRule *)fr)->with == EM_VFOLDER_RULE_WITH_SPECIFIC && ((EMVFolderRule *)fr)->sources == NULL) { + /* FIXME: set a parent window? */ + e_error_run(NULL, "mail:vfolder-no-source", NULL); + return 0; + } + + return FILTER_RULE_CLASS(parent_class)->validate(fr); +} + +static int +list_eq(GList *al, GList *bl) +{ + int truth = TRUE; + + while (truth && al && bl) { + char *a = al->data, *b = bl->data; + + truth = strcmp(a, b)== 0; + al = al->next; + bl = bl->next; + } + + return truth && al == NULL && bl == NULL; +} + +static int +vfolder_eq(FilterRule *fr, FilterRule *cm) +{ + return FILTER_RULE_CLASS(parent_class)->eq(fr, cm) + && list_eq(((EMVFolderRule *)fr)->sources, ((EMVFolderRule *)cm)->sources); +} + +static xmlNodePtr +xml_encode(FilterRule *fr) +{ + EMVFolderRule *vr =(EMVFolderRule *)fr; + xmlNodePtr node, set, work; + GList *l; + + node = FILTER_RULE_CLASS(parent_class)->xml_encode(fr); + g_assert(node != NULL); + g_assert(vr->with >= 0 && vr->with < sizeof(with_names)/sizeof(with_names[0])); + set = xmlNewNode(NULL, "sources"); + xmlAddChild(node, set); + xmlSetProp(set, "with", with_names[vr->with]); + l = vr->sources; + while (l) { + work = xmlNewNode(NULL, "folder"); + xmlSetProp(work, "uri", l->data); + xmlAddChild(set, work); + l = l->next; + } + + return node; +} + +static void +set_with(EMVFolderRule *vr, const char *name) +{ + int i; + + for(i=0;i<sizeof(with_names)/sizeof(with_names[0]);i++) { + if (!strcmp(name, with_names[i])) { + vr->with = i; + return; + } + } + + vr->with = 0; +} + +static int +xml_decode(FilterRule *fr, xmlNodePtr node, struct _RuleContext *f) +{ + xmlNodePtr set, work; + int result; + EMVFolderRule *vr =(EMVFolderRule *)fr; + char *tmp; + + result = FILTER_RULE_CLASS(parent_class)->xml_decode(fr, node, f); + if (result != 0) + return result; + + /* handle old format file, vfolder source is in filterrule */ + if (strcmp(fr->source, "incoming")!= 0) { + set_with(vr, fr->source); + g_free(fr->source); + fr->source = g_strdup("incoming"); + } + + set = node->children; + while (set) { + if (!strcmp(set->name, "sources")) { + tmp = xmlGetProp(set, "with"); + if (tmp) { + set_with(vr, tmp); + xmlFree(tmp); + } + work = set->children; + while (work) { + if (!strcmp(work->name, "folder")) { + tmp = xmlGetProp(work, "uri"); + if (tmp) { + vr->sources = g_list_append(vr->sources, g_strdup(tmp)); + xmlFree(tmp); + } + } + work = work->next; + } + } + set = set->next; + } + return 0; +} + +static void +rule_copy(FilterRule *dest, FilterRule *src) +{ + EMVFolderRule *vdest, *vsrc; + GList *node; + + vdest =(EMVFolderRule *)dest; + vsrc =(EMVFolderRule *)src; + + if (vdest->sources) { + g_list_foreach(vdest->sources, (GFunc)g_free, NULL); + g_list_free(vdest->sources); + vdest->sources = NULL; + } + + node = vsrc->sources; + while (node) { + char *uri = node->data; + + vdest->sources = g_list_append(vdest->sources, g_strdup(uri)); + node = node->next; + } + + vdest->with = vsrc->with; + + FILTER_RULE_CLASS(parent_class)->copy(dest, src); +} + +enum { + BUTTON_ADD, + BUTTON_REMOVE, + BUTTON_LAST, +}; + +struct _source_data { + RuleContext *rc; + EMVFolderRule *vr; + const char *current; + GtkListStore *model; + GtkTreeView *list; + GtkButton *buttons[BUTTON_LAST]; +}; + +static void source_add(GtkWidget *widget, struct _source_data *data); +static void source_remove(GtkWidget *widget, struct _source_data *data); + +static struct { + char *name; + GtkSignalFunc func; +} edit_buttons[] = { + { "source_add", G_CALLBACK(source_add) }, + { "source_remove", G_CALLBACK(source_remove)}, +}; + +static void +set_sensitive(struct _source_data *data) +{ + gtk_widget_set_sensitive((GtkWidget *)data->buttons[BUTTON_ADD], TRUE); + gtk_widget_set_sensitive((GtkWidget *)data->buttons[BUTTON_REMOVE], data->current != NULL); +} + +static void +select_source(GtkWidget *list, struct _source_data *data) +{ + GtkTreeViewColumn *column; + GtkTreePath *path; + GtkTreeIter iter; + + gtk_tree_view_get_cursor(data->list, &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path); + gtk_tree_path_free(path); + + gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter, 0, &data->current, -1); + + set_sensitive(data); +} + +static void +select_source_with_changed(GtkWidget *widget, struct _source_data *data) +{ + em_vfolder_rule_with_t with; + + with = gtk_option_menu_get_history((GtkOptionMenu *)widget); + if (with < EM_VFOLDER_RULE_WITH_SPECIFIC || with > EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE) + with = 0; + data->vr->with = with; +} + +/* attempt to make a 'nice' folder name out of the raw uri */ +static char *format_source(const char *euri) +{ + CamelURL *url; + GString *out; + char *res, *uri; + + /* This should really probably base it on the account name? */ + uri = em_uri_to_camel(euri); + url = camel_url_new(uri, NULL); + + /* bad uri */ + if (url == NULL) + return uri; + + g_free(uri); + + out = g_string_new(url->protocol); + g_string_append_c(out, ':'); + if (url->user && url->host) { + g_string_append_printf(out, "%s@%s", url->user, url->host); + if (url->port) + g_string_append_printf(out, ":%d", url->port); + } + if (url->fragment) + g_string_append(out, url->fragment); + else if (url->path) + g_string_append(out, url->path); + + res = out->str; + g_string_free(out, FALSE); + + return res; +} + +static void +vfr_folder_response(GtkWidget *dialog, gint button, struct _source_data *data) +{ + const char *uri = em_folder_selector_get_selected_uri((EMFolderSelector *)dialog); + + if (button == GTK_RESPONSE_OK && uri != NULL) { + char *urinice, *euri; + GtkTreeSelection *selection; + GtkTreeIter iter; + + euri = em_uri_from_camel(uri); + + data->vr->sources = g_list_append(data->vr->sources, euri); + + gtk_list_store_append(data->model, &iter); + urinice = format_source(euri); + gtk_list_store_set(data->model, &iter, 0, urinice, 1, euri, -1); + g_free(urinice); + selection = gtk_tree_view_get_selection(data->list); + gtk_tree_selection_select_iter(selection, &iter); + data->current = euri; + + set_sensitive(data); + } + + gtk_widget_destroy(dialog); +} + +static void +source_add(GtkWidget *widget, struct _source_data *data) +{ + EMFolderTree *emft; + GtkWidget *dialog; + + emft =(EMFolderTree *)em_folder_tree_new_with_model(mail_component_peek_tree_model(mail_component_peek())); + + dialog = em_folder_selector_new(emft, EM_FOLDER_SELECTOR_CAN_CREATE, _("Select Folder"), NULL, _("_Add")); + gtk_window_set_transient_for((GtkWindow *)dialog, (GtkWindow *)gtk_widget_get_toplevel(widget)); + gtk_window_set_modal((GtkWindow *)dialog, TRUE); + g_signal_connect(dialog, "response", G_CALLBACK(vfr_folder_response), data); + gtk_widget_show(dialog); +} + +static void +source_remove(GtkWidget *widget, struct _source_data *data) +{ + GtkTreeSelection *selection; + const char *source; + GtkTreePath *path; + GtkTreeIter iter; + int index = 0; + int n; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->list)); + + source = NULL; + while ((source = em_vfolder_rule_next_source(data->vr, source))) { + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, index); + + if (gtk_tree_selection_path_is_selected(selection, path)) { + gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path); + + em_vfolder_rule_remove_source(data->vr, source); + gtk_list_store_remove(data->model, &iter); + gtk_tree_path_free(path); + + /* now select the next rule */ + n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(data->model), NULL); + index = index >= n ? n - 1 : index; + + if (index >= 0) { + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, index); + gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path); + gtk_tree_path_free(path); + + gtk_tree_selection_select_iter(selection, &iter); + gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter, 0, &data->current, -1); + } else { + data->current = NULL; + } + + break; + } + + index++; + gtk_tree_path_free(path); + } + + set_sensitive(data); +} + + +GtkWidget *em_vfolder_editor_sourcelist_new(char *widget_name, char *string1, char *string2, + int int1, int int2); + +GtkWidget * +em_vfolder_editor_sourcelist_new(char *widget_name, char *string1, char *string2, int int1, int int2) +{ + GtkWidget *table, *scrolled; + GtkTreeSelection *selection; + GtkCellRenderer *renderer; + GtkListStore *model; + + scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); + table = gtk_tree_view_new_with_model((GtkTreeModel *)model); + gtk_tree_view_set_headers_visible((GtkTreeView *)table, FALSE); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes((GtkTreeView *)table, -1, + _("VFolder source"), renderer, + "text", 0, NULL); + + selection = gtk_tree_view_get_selection((GtkTreeView *)table); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); + + gtk_container_add(GTK_CONTAINER(scrolled), table); + + g_object_set_data((GObject *)scrolled, "table", table); + g_object_set_data((GObject *)scrolled, "model", model); + + gtk_widget_show(scrolled); + gtk_widget_show(table); + + return scrolled; +} + +static GtkWidget * +get_widget(FilterRule *fr, RuleContext *rc) +{ + EMVFolderRule *vr =(EMVFolderRule *)fr; + GtkWidget *widget, *frame, *list; + struct _source_data *data; + GtkOptionMenu *omenu; + const char *source; + GtkTreeIter iter; + GladeXML *gui; + int i; + + widget = FILTER_RULE_CLASS(parent_class)->get_widget(fr, rc); + + data = g_malloc0(sizeof(*data)); + data->rc = rc; + data->vr = vr; + + gui = glade_xml_new(EVOLUTION_GLADEDIR "/mail-config.glade", "vfolder_source_frame", NULL); + frame = glade_xml_get_widget(gui, "vfolder_source_frame"); + + g_object_set_data_full((GObject *)frame, "data", data, g_free); + + for(i = 0; i < BUTTON_LAST; i++) { + data->buttons[i] =(GtkButton *)glade_xml_get_widget(gui, edit_buttons[i].name); + g_signal_connect(data->buttons[i], "clicked", edit_buttons[i].func, data); + } + + list = glade_xml_get_widget(gui, "source_list"); + data->list =(GtkTreeView *)g_object_get_data((GObject *)list, "table"); + data->model =(GtkListStore *)g_object_get_data((GObject *)list, "model"); + + source = NULL; + while ((source = em_vfolder_rule_next_source(vr, source))) { + char *nice = format_source(source); + + gtk_list_store_append(data->model, &iter); + gtk_list_store_set(data->model, &iter, 0, nice, 1, source, -1); + g_free(nice); + } + + g_signal_connect(data->list, "cursor-changed", G_CALLBACK(select_source), data); + + omenu =(GtkOptionMenu *)glade_xml_get_widget(gui, "source_option"); + gtk_option_menu_set_history(omenu, vr->with); + g_signal_connect(omenu, "changed", G_CALLBACK(select_source_with_changed), data); + + set_sensitive(data); + + g_object_unref(gui); + + gtk_box_pack_start(GTK_BOX(widget), frame, TRUE, TRUE, 3); + + return widget; +} |