/* * $Id: $ */ /* vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: */ /* * Virtual folder plugin for claws-mail * * Claws Mail is Copyright (C) 1999-2012 by the Claws Mail Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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, see . * */ #ifdef HAVE_CONFIG_H # include #endif #include "gtk/gtk.h" #include "glib.h" #include "gettext.h" #include "gtkutils.h" #include "mainwindow.h" #include "foldersel.h" #include "alertpanel.h" #include "vfolder_gtk.h" #include "vfolder.h" #include "vfolder_prop.h" #define HEADERS N_("Message _Headers") #define BODY N_("_Message body") #define BOTH N_("_Both") typedef struct { GtkWidget* filter; GtkWidget* frozen; GtkWidget* deep_copy; GtkWidget* source; GtkWidget* label_btn; GtkWidget* message_btn; GtkWidget* both_btn; } PropsDialog; static void add_current_config(VFolderItem* item, PropsDialog* props) { if (item->filter) gtk_entry_set_text(GTK_ENTRY(props->filter), item->filter); if (item->source) { gchar* id = folder_item_get_identifier(item->source); if (id) { gtk_entry_set_text(GTK_ENTRY(props->source), id); g_free(id); } } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props->frozen), item->frozen); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props->deep_copy), item->deep_copy); switch (item->search) { case SEARCH_BODY: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props->message_btn), TRUE); case SEARCH_BOTH: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props->both_btn), TRUE); case SEARCH_HEADERS: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(props->label_btn), TRUE); } } static gboolean is_source_widget(GtkWidget* widget) { const gchar* name = gtk_widget_get_name(widget); return (name && strcmp("source", name) == 0); } static void foldersel_cb(GtkWidget *widget, gpointer data) { FolderItem *item; gchar *item_id; gint newpos = 0; GtkWidget* entry = GTK_WIDGET(data); item = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE); if (item && IS_VFOLDER_FOLDER_ITEM(item)) { /* Cannot create virtual folder from virtual folder */ alertpanel_error(_("%s: Cannot create virtual folder from virtual folder"), item->name); return; } else { if (item && (item_id = folder_item_get_identifier(item)) != NULL) { gtk_editable_delete_text(GTK_EDITABLE(entry), 0, -1); gtk_editable_insert_text(GTK_EDITABLE(entry), item_id, strlen(item_id), &newpos); g_free(item_id); } debug_print("Source Folder: %s\n", gtk_entry_get_text(GTK_ENTRY(entry))); } } static GtkWidget* vfolder_prop_row(GtkWidget* widget, const gchar* label_markup, gint width, gboolean center) { GtkWidget* row = gtk_hbox_new(FALSE, 5); GtkWidget* label = gtk_label_new(NULL); gtk_widget_set_size_request(label, width, -1); gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), label_markup); gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget); gtk_box_pack_start(GTK_BOX(row), label, FALSE, FALSE, 5); gtk_box_pack_start(GTK_BOX(row), widget, TRUE, FALSE, 5); if (is_source_widget(widget)) { GtkWidget* btn = gtk_button_new_from_stock(GTK_STOCK_OPEN); g_signal_connect(G_OBJECT(btn), "clicked", G_CALLBACK(foldersel_cb), widget); gtk_box_pack_start(GTK_BOX(row), btn, FALSE, FALSE, 5); } return row; } static gboolean vfolder_set_search_type(VFolderItem* item, GtkWidget* list) { GSList *btn_list, *btns; gboolean active = FALSE; GtkToggleButton* btn = NULL; btn_list = gtk_radio_button_get_group(GTK_RADIO_BUTTON(list)); for (btns = btn_list; btns && !active; btns = g_slist_next(btns)) { btn = GTK_TOGGLE_BUTTON(btns->data); active = gtk_toggle_button_get_active(btn); } if (active) { const gchar* label = gtk_button_get_label(GTK_BUTTON(btn)); if (label) { if (strcmp(BOTH, label) == 0) { if (item->search != SEARCH_BOTH) { item->search = SEARCH_BOTH; return TRUE; } } else if (strcmp(BODY, label) == 0) { if (item->search != SEARCH_BODY) { item->search = SEARCH_BODY; return TRUE; } } else { if (item->search != SEARCH_HEADERS) { item->search = SEARCH_HEADERS; return TRUE; } } } } return FALSE; } /* static void vfolder_copy_msginfo_list(gpointer data, gpointer user_data) { MsgInfo* msg = (MsgInfo *) data; MsgInfo* new_msg; VFolderItem* item = (VFolderItem *) user_data; g_return_if_fail(msg != NULL); g_return_if_fail(item != NULL); new_msg = procmsg_msginfo_copy(msg); item->msginfos = g_slist_prepend(item->msginfos, new_msg); } */ static gboolean vfolder_search_headers(MsgInfo* msg, GPatternSpec* pattern) { return ((msg->cc && g_pattern_match_string(pattern, msg->cc)) || (msg->from && g_pattern_match_string(pattern, msg->from)) || (msg->fromname && g_pattern_match_string(pattern, msg->fromname)) || (msg->inreplyto && g_pattern_match_string(pattern, msg->inreplyto)) || (msg->subject && g_pattern_match_string(pattern, msg->subject)) || (msg->to && g_pattern_match_string(pattern, msg->to))); } static gboolean vfolder_search_body(MsgInfo* msg, GPatternSpec* pattern) { gchar* body; gboolean found = FALSE; body = procmsg_get_message_file(msg); if (body) { found = g_pattern_match_string(pattern, body); g_free(body); } return found; } static MsgInfoList* vfolder_filter_msgs_list(MsgInfoList* msgs, VFolderItem* item) { MsgInfoList *list = NULL, *tmp; GPatternSpec* pattern; MsgInfo* msg; if (!item || item->filter == NULL) return list; pattern = g_pattern_spec_new(item->filter); for (tmp = msgs; tmp; tmp = g_slist_next(tmp)) { msg = (MsgInfo *) tmp->data; switch (item->search) { case SEARCH_HEADERS: if (vfolder_search_headers(msg, pattern)) list = g_slist_prepend(list, msg); break; case SEARCH_BODY: if (vfolder_search_body(msg, pattern)) list = g_slist_prepend(list, msg); break; case SEARCH_BOTH: if (vfolder_search_headers(msg, pattern)) { list = g_slist_prepend(list, msg); continue; } if (vfolder_search_body(msg, pattern)) list = g_slist_prepend(list, msg); break; } } g_pattern_spec_free(pattern); return list; } static gboolean vfolder_create_msgs_list(VFolderItem* item, gboolean copy) { MsgInfoList *msgs = NULL, *filtered = NULL; gboolean ok = FALSE; GSList* filelist = NULL; if (item->filter && item->msg_filter_func) { item->deep_copy = copy; ok = TRUE; msgs = folder_item_get_msg_list(item->source); filtered = item->msg_filter_func(msgs, item); if (filtered) { filelist = procmsg_get_message_file_list(filtered); if (filelist) { gint n = folder_item_add_msgs(FOLDER_ITEM(item), filelist, FALSE); FOLDER_ITEM(item)->last_num = n; procmsg_message_file_list_free(filelist); } g_slist_free(filtered); } g_slist_free(msgs); } return ok; } void vfolder_set_msgs_filter(VFolderItem* vfolder_item) { g_return_if_fail(vfolder_item != NULL); vfolder_item->msg_filter_func = vfolder_filter_msgs_list; } gboolean vfolder_create_item_dialog(FolderItem* folder_item) { gboolean created = FALSE; VFolderItem* item = NULL; g_return_val_if_fail(folder_item != NULL, created); g_return_val_if_fail(IS_VFOLDER_FOLDER_ITEM(folder_item), created); item = VFOLDER_ITEM(folder_item); item->msg_filter_func = vfolder_filter_msgs_list; if (vfolder_edit_item_dialog(item)) { /* save properties */ if (FOLDER_ITEM_PROPS_OK != vfolder_folder_item_props_write(item)) created = FALSE; else created = TRUE; } return created; } gboolean vfolder_edit_item_dialog(VFolderItem* vfolder_item) { gboolean ok = FALSE; PropsDialog* props_dialog; GtkWidget* dialog; GtkWidget* content; GtkWidget* row; GtkWidget* box; gint response; gchar* name; const gchar* str; gboolean frozen, deep_copy; FolderItem* source; gchar* old_filter = NULL; g_return_val_if_fail(vfolder_item != NULL, ok); MainWindow *mainwin = mainwindow_get_mainwindow(); props_dialog = g_new0(PropsDialog, 1); props_dialog->filter = gtk_entry_new(); props_dialog->frozen = gtk_check_button_new(); props_dialog->deep_copy = gtk_check_button_new(); props_dialog->source = gtk_entry_new(); props_dialog->label_btn = gtk_radio_button_new_with_mnemonic(NULL, HEADERS); props_dialog->message_btn = gtk_radio_button_new_with_mnemonic_from_widget( GTK_RADIO_BUTTON(props_dialog->label_btn), BODY); props_dialog->both_btn = gtk_radio_button_new_with_mnemonic_from_widget( GTK_RADIO_BUTTON(props_dialog->label_btn), BOTH); gtk_widget_set_name(props_dialog->source, "source"); add_current_config(vfolder_item, props_dialog); dialog = gtk_dialog_new_with_buttons( N_("Edit VFolder Properties"), GTK_WINDOW(mainwin->window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_REJECT); content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); GtkWidget* vbox = gtk_vbox_new(FALSE, 5); row = vfolder_prop_row(props_dialog->source, N_("_Source folder"), 110, FALSE); gtk_box_pack_start(GTK_BOX(vbox), row, FALSE, FALSE, 5); GtkWidget* frame1 = gtk_frame_new(_("Message filter")); GtkWidget* vbox1 = gtk_vbox_new(TRUE, 2); gtk_container_add(GTK_CONTAINER(frame1), vbox1); row = vfolder_prop_row(props_dialog->filter, N_("_Filter"), 110, FALSE); gtk_box_pack_start(GTK_BOX(vbox1), row, FALSE, FALSE, 5); box = gtk_hbox_new(TRUE, 2); gtk_box_pack_start(GTK_BOX(box), props_dialog->label_btn, TRUE, TRUE, 2); gtk_box_pack_start(GTK_BOX(box), props_dialog->message_btn, TRUE, TRUE, 2); gtk_box_pack_start(GTK_BOX(box), props_dialog->both_btn, TRUE, TRUE, 2); gtk_box_pack_start(GTK_BOX(vbox1), box, FALSE, FALSE, 5); gtk_box_pack_start(GTK_BOX(vbox), frame1, FALSE, FALSE, 5); row = vfolder_prop_row(props_dialog->frozen, N_("F_reeze content"), 110, TRUE); gtk_box_pack_start(GTK_BOX(vbox), row, FALSE, FALSE, 5); row = vfolder_prop_row(props_dialog->deep_copy, N_("Co_py messages"), 110, TRUE); gtk_box_pack_start(GTK_BOX(vbox), row, FALSE, FALSE, 5); name = g_strconcat(FOLDER_ITEM(vfolder_item)->name, N_(": settings"), NULL); GtkWidget* frame = gtk_frame_new(name); g_free(name); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_widget_show_all(frame); gtk_container_add(GTK_CONTAINER(content), frame); response = gtk_dialog_run(GTK_DIALOG(dialog)); if (response == GTK_RESPONSE_ACCEPT) { frozen = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(props_dialog->frozen)); deep_copy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(props_dialog->deep_copy)); str = gtk_entry_get_text(GTK_ENTRY(props_dialog->filter)); if (str) { old_filter = g_strdup(vfolder_item->filter); if (strlen(str) == 0) { if (vfolder_item->filter) { g_free(vfolder_item->filter); vfolder_item->filter = NULL; ok = TRUE; } } else { if (!vfolder_item->filter || strcmp(vfolder_item->filter, str) != 0) { g_free(vfolder_item->filter); vfolder_item->filter = g_strdup(str); ok = TRUE; } } } if (vfolder_set_search_type(vfolder_item, props_dialog->label_btn)) ok = TRUE; str = gtk_entry_get_text(GTK_ENTRY(props_dialog->source)); if (str) { source = folder_find_item_from_identifier(str); if (source && (source->stype != F_NORMAL && source->stype != F_INBOX)) { alertpanel_error(_("%s: Not suitable for virtual folders\n" "Use only folder type: Normal or Inbox\n"), str); g_free(vfolder_item->filter); vfolder_item->filter = g_strdup(old_filter); ok = FALSE; goto error; } if (strlen(str) == 0) { if (vfolder_item->source) { vfolder_item->source = NULL; folder_item_remove_all_msg(FOLDER_ITEM(vfolder_item)); ok = TRUE; } } else { folder_item_remove_all_msg(FOLDER_ITEM(vfolder_item)); gchar* id = (vfolder_item->source) ? folder_item_get_identifier(vfolder_item->source) : NULL; if (!id || strcmp(id, str) != 0) vfolder_item->source = source; if (vfolder_item->source) { ok = vfolder_create_msgs_list(vfolder_item, deep_copy); if (ok == FALSE) { g_free(vfolder_item->filter); vfolder_item->filter = g_strdup(old_filter); goto error; } } else { g_free(vfolder_item->filter); vfolder_item->filter = g_strdup(old_filter); ok = FALSE; goto error; } } } if (vfolder_item->frozen != frozen) { vfolder_item->frozen = frozen; ok = TRUE; } if (vfolder_item->deep_copy != deep_copy) { vfolder_item->deep_copy = deep_copy; ok = TRUE; } } error: gtk_widget_destroy(dialog); g_free(props_dialog); g_free(old_filter); return ok; }