talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

commit 69e7131dc87ed1dbec2f9140a03b38feaf192cdf
parent 74059cdc111cdc7ada818eb3a57df6f441acdbe5
Author: wwp <subscript@free.fr>
Date:   Sat, 11 Sep 2021 22:31:26 +0200

Fix CID 1491382: malloc size too short by one because, worst case, qp_decode_const
will dump the exact same input string.

Diffstat:
Msrc/account.c | 64+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/addrbook.c | 27+++++++++++++++++++++++++--
Msrc/addrbook.h | 2++
Msrc/addrcache.c | 1+
Msrc/addrcache.h | 1+
Msrc/addressbook.c | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/addrindex.c | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/addrindex.h | 6++++++
Msrc/addritem.c | 24++++++++++++++++++++++++
Msrc/addritem.h | 3+++
Msrc/common/smtp.c | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/common/smtp.h | 3+++
Msrc/compose.c | 58+++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/compose.h | 1+
Msrc/folder.c | 1+
Msrc/folder_item_prefs.c | 4++++
Msrc/folder_item_prefs.h | 1+
Msrc/gtk/manage_window.c | 50++++++++++++++++++++++++++++++++++++++------------
Msrc/gtk/quicksearch.c | 9+++++++--
Msrc/gtk/quicksearch.h | 2++
Msrc/html.c | 35+++++++++++++++++++++++++++++++++--
Msrc/html.h | 1+
Msrc/jpilot.c | 14++++++++++++++
Msrc/jpilot.h | 2++
Msrc/mainwindow.c | 29+++++++++++++++++++++++++----
Msrc/matcher.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/matcher.h | 3+++
Msrc/matcher_parser_parse.y | 5++---
Msrc/messageview.c | 62++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/mimeview.c | 25+++++++++++++++++--------
Msrc/plugins/Makefile.am | 3++-
Msrc/plugins/fancy/fancy_viewer.c | 21++++++++++++++++++++-
Msrc/plugins/notification/notification_trayicon.c | 4++++
Msrc/plugins/pgpmime/pgpmime.c | 53+++++++++++++++++++++++++++++++++++++++++------------
Msrc/plugins/python/composewindowtype.c | 2+-
Msrc/plugins/rssyl/rssyl.c | 4++--
Msrc/plugins/rssyl/rssyl_parse_feed.c | 4++--
Msrc/plugins/rssyl/rssyl_prefs.c | 13+++++++++++++
Msrc/plugins/rssyl/rssyl_prefs.h | 2++
Msrc/plugins/rssyl/rssyl_subscribe.c | 4++--
Msrc/plugins/rssyl/rssyl_update_feed.c | 9+++++++--
Msrc/plugins/vcalendar/vcal_meeting_gtk.c | 3+++
Msrc/prefs_account.c | 21+++++++++++++++++++++
Msrc/prefs_account.h | 1+
Msrc/prefs_common.c | 23+++++++++++++++++++++++
Msrc/prefs_common.h | 14++++++++++++++
Msrc/prefs_ext_prog.c | 34++++++++++++++++++++++++++++++++++
Msrc/prefs_folder_item.c | 295+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/prefs_matcher.c | 215++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/prefs_message.c | 12++++++++++++
Msrc/prefs_summaries.c | 18+++++++++++++++++-
Msrc/procmime.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/procmime.h | 2++
Msrc/procmsg.c | 12++++++++++--
Msrc/send_message.c | 10++++++----
Msrc/send_message.h | 2++
Msrc/summaryview.c | 23+++++++++++++++++++----
Msrc/textview.c | 26++++++++++++++++++++++++++
Msrc/toolbar.c | 18+++++++++++++++---
Msrc/vcard.c | 20++++++++++++++++++--
Msrc/vcard.h | 2++
61 files changed, 1630 insertions(+), 234 deletions(-)

diff --git a/src/account.c b/src/account.c @@ -60,6 +60,7 @@ enum { ACCOUNT_IS_DEFAULT, ACCOUNT_ENABLE_GET_ALL, + ACCOUNT_INCLUDE_IN_LIST, ACCOUNT_NAME, ACCOUNT_PROTOCOL, ACCOUNT_SERVER, @@ -157,6 +158,10 @@ static void account_get_all_toggled (GtkCellRendererToggle *widget, gchar *path, GtkWidget *list_view); +static void account_selectable_as_current_account_toggled (GtkCellRendererToggle *widget, + gchar *path, + GtkWidget *list_view); + static void account_double_clicked (GtkTreeView *list_view, GtkTreePath *path, GtkTreeViewColumn *column, @@ -208,6 +213,9 @@ void account_read_config_all(void) memmove(buf, buf + 1, sizeof(buf) - 1); buf[strlen(buf) - 1] = '\0'; debug_print("Found label: %s\n", buf); + if (ac_label_list && g_slist_find_custom(ac_label_list, buf, (GCompareFunc)strcmp)) { + g_warning("duplicate account found (%s)", buf); + } ac_label_list = g_slist_append(ac_label_list, g_strdup(buf)); } @@ -232,11 +240,8 @@ void account_read_config_all(void) account_set_menu(); main_window_reflect_prefs_all_now(); - while (ac_label_list) { - g_free(ac_label_list->data); - ac_label_list = g_slist_remove(ac_label_list, - ac_label_list->data); - } + g_slist_foreach(ac_label_list, (GFunc)g_free, NULL); + g_slist_free(ac_label_list); } void account_write_config_all(void) @@ -1491,6 +1496,7 @@ static GtkListStore* account_create_data_store(void) return gtk_list_store_new(N_ACCOUNT_COLUMNS, G_TYPE_INT, /* ACCOUNT_IS_DEFAULT */ G_TYPE_BOOLEAN, /* ACCOUNT_ENABLE_GET_ALL */ + G_TYPE_BOOLEAN, /* ACCOUNT_INCLUDE_IN_LIST */ G_TYPE_STRING, /* ACCOUNT_NAME */ G_TYPE_STRING, /* ACCOUNT_PROTOCOL */ G_TYPE_STRING, /* ACCOUNT_SERVER */ @@ -1513,6 +1519,7 @@ static void account_list_store_insert_account_item(GtkListStore *list_store, ac_prefs->protocol == A_NNTP || ac_prefs->protocol == A_LOCAL) && ac_prefs->recv_at_getall; + gboolean is_selectable_as_current_account = ac_prefs->selectable_as_current_account; gchar *protocol, *server; #ifdef USE_GNUTLS @@ -1548,6 +1555,7 @@ static void account_list_store_insert_account_item(GtkListStore *list_store, gtk_list_store_set(list_store, &iter, ACCOUNT_IS_DEFAULT, ac_prefs->is_default ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, ACCOUNT_ENABLE_GET_ALL, is_get_all, + ACCOUNT_INCLUDE_IN_LIST, is_selectable_as_current_account, ACCOUNT_NAME, ac_prefs->account_name, ACCOUNT_PROTOCOL, protocol, ACCOUNT_SERVER, server, @@ -1622,6 +1630,23 @@ static void account_create_list_view_columns(GtkWidget *list_view) G_CALLBACK(account_get_all_toggled), list_view); + renderer = gtk_cell_renderer_toggle_new(); + g_object_set(renderer, + "radio", FALSE, + "activatable", TRUE, + NULL); + column = gtk_tree_view_column_new_with_attributes + (C_("Accounts List Selectable Column Name", "S"), renderer, + "active", ACCOUNT_INCLUDE_IN_LIST, + NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column); + gtk_tree_view_column_set_alignment (column, 0.5); + CLAWS_SET_TIP(gtk_tree_view_column_get_widget(column), + _("Selectable as current account")); + g_signal_connect(G_OBJECT(renderer), "toggled", + G_CALLBACK(account_selectable_as_current_account_toggled), + list_view); + renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes (_("Name"), renderer, @@ -1817,6 +1842,35 @@ static void account_get_all_toggled(GtkCellRendererToggle *widget, ac->recv_at_getall ^= TRUE; } +/*! + *\brief Triggered when "include in list" column is activated or de-activated + */ +static void account_selectable_as_current_account_toggled(GtkCellRendererToggle *widget, + gchar *path, + GtkWidget *list_view) +{ + GtkTreeIter iter; + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list_view)); + PrefsAccount *ac = NULL; + gboolean selectable_as_current_account; + + if (!gtk_tree_model_get_iter_from_string(model, &iter, path)) + return; + + gtk_tree_model_get(model, &iter, + ACCOUNT_DATA, &ac, + ACCOUNT_INCLUDE_IN_LIST, &selectable_as_current_account, + -1); + + /* set value in store */ + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + ACCOUNT_INCLUDE_IN_LIST, !selectable_as_current_account, + -1); + + /* set value in account */ + ac->selectable_as_current_account ^= TRUE; +} + static void account_double_clicked(GtkTreeView *list_view, GtkTreePath *path, GtkTreeViewColumn *column, diff --git a/src/addrbook.c b/src/addrbook.c @@ -72,6 +72,7 @@ AddressBookFile *addrbook_create_book() book->tempList = NULL; book->tempHash = NULL; book->addressCache->modified = TRUE; + book->addressCache->collapsedFlag = TRUE; return book; } @@ -351,11 +352,14 @@ ItemEMail *addrbook_person_remove_email(AddressBookFile *book, #define AB_ATTAG_EMAIL "email" #define AB_ATTAG_EID "eid" #define AB_ATTAG_PID "pid" +#define AB_ATTAG_COLLAPSED "collapsed" /* Attribute values */ #define AB_ATTAG_VAL_PERSON "person" #define AB_ATTAG_VAL_GROUP "group" #define AB_ATTAG_VAL_FOLDER "folder" +#define AB_ATTAG_VAL_YES "yes" +#define AB_ATTAG_VAL_NO "no" /** * Parse address item for person from XML file. @@ -710,6 +714,9 @@ static void addrbook_parse_folder(AddressBookFile *book, XMLFile *file) ADDRITEM_NAME(folder) = g_strdup(value); else if (strcmp(name, AB_ATTAG_REMARKS) == 0) folder->remarks = g_strdup(value); + else if (strcmp(name, AB_ATTAG_COLLAPSED) == 0) + folder->isCollapsed = + (strcmp(value, AB_ATTAG_VAL_YES) == 0) ? TRUE : FALSE; attr = g_list_next(attr); } if (xml_parse_next_tag(file)) { /* Consume closing tag */ @@ -1192,6 +1199,9 @@ static void addrbook_write_item_folder_vis(gpointer key, gpointer value, data->error = TRUE; if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, folder->remarks) < 0) data->error = TRUE; + if (addrbook_write_attr(fp, AB_ATTAG_COLLAPSED, + folder->isCollapsed ? AB_ATTAG_VAL_YES : AB_ATTAG_VAL_NO ) < 0) + data->error = TRUE; if (claws_fputs(" >\n", fp) == EOF) data->error = TRUE; if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ITEM_LIST) < 0) @@ -2228,6 +2238,21 @@ gchar *addrbook_guess_next_file(AddressBookFile *book) return newFile; } +gboolean addrbook_get_collapsed( AddressBookFile *book ) +{ + g_return_val_if_fail(book != NULL, FALSE); +fprintf(stderr, "==> addrbook_get_collapsed: %d\n", book->addressCache->collapsedFlag); + return book->addressCache->collapsedFlag; +} + +void addrbook_set_collapsed( AddressBookFile *book, const gboolean value ) +{ + g_return_if_fail(book != NULL); +fprintf(stderr, "==> addrbook_set_collapsed: %d\n", value); + book->addressCache->collapsedFlag = value; + addrcache_set_dirty(book->addressCache, TRUE); +} + void addrbook_delete_book_file(AddressBookFile *book) { gchar *book_path; @@ -2244,5 +2269,3 @@ void addrbook_delete_book_file(AddressBookFile *book) /* * End of Source. */ - - diff --git a/src/addrbook.h b/src/addrbook.h @@ -58,7 +58,9 @@ void addrbook_set_path ( AddressBookFile *book, const gchar *value ); void addrbook_set_file ( AddressBookFile *book, const gchar *value ); gboolean addrbook_get_modified ( AddressBookFile *book ); gboolean addrbook_get_accessed ( AddressBookFile *book ); +gboolean addrbook_get_collapsed ( AddressBookFile *book ); void addrbook_set_accessed ( AddressBookFile *book, const gboolean value ); +void addrbook_set_collapsed ( AddressBookFile *book, const gboolean value ); gboolean addrbook_get_read_flag ( AddressBookFile *book ); gint addrbook_get_status ( AddressBookFile *book ); ItemFolder *addrbook_get_root_folder ( AddressBookFile *book ); diff --git a/src/addrcache.c b/src/addrcache.c @@ -65,6 +65,7 @@ AddressCache *addrcache_create() { cache->accessFlag = FALSE; cache->name = NULL; cache->modifyTime = 0; + cache->collapsedFlag = FALSE; /* Generate the next ID using system time */ cache->nextID = 1; diff --git a/src/addrcache.h b/src/addrcache.h @@ -44,6 +44,7 @@ struct _AddressCache { gboolean dirtyFlag; gboolean accessFlag; gchar *name; + gboolean collapsedFlag; }; /* Function prototypes */ diff --git a/src/addressbook.c b/src/addressbook.c @@ -198,6 +198,12 @@ static void addressbook_select_row_tree (GtkCMCTree *ctree, GtkCMCTreeNode *node, gint column, gpointer data); +static void addressbook_tree_expand_node (GtkCMCTree *ctree, + GtkCMCTreeNode *node, + gpointer *data ); +static void addressbook_tree_collapse_node (GtkCMCTree *ctree, + GtkCMCTreeNode *node, + gpointer *data ); static void addressbook_list_row_selected (GtkCMCTree *clist, GtkCMCTreeNode *node, gint column, @@ -233,6 +239,18 @@ static void addressbook_new_folder_cb (GtkAction *action, gpointer data); static void addressbook_new_group_cb (GtkAction *action, gpointer data); +static void addressbook_treenode_expand_all_cb(gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_treenode_expand_sub_cb(gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_treenode_collapse_all_cb(gpointer data, + guint action, + GtkWidget *widget); +static void addressbook_treenode_collapse_sub_cb(gpointer data, + guint action, + GtkWidget *widget); static void addressbook_treenode_edit_cb (GtkAction *action, gpointer data); static void addressbook_treenode_delete_cb (GtkAction *action, @@ -423,10 +441,13 @@ static GtkActionEntry addressbook_entries[] = {"Book/EditBook", NULL, N_("_Edit book"), NULL, NULL, G_CALLBACK(addressbook_treenode_edit_cb) }, {"Book/DeleteBook", NULL, N_("_Delete book"), NULL, NULL, G_CALLBACK(addressbook_treenode_delete_cb) }, /* {"Book/---", NULL, "---", NULL, NULL, NULL }, */ + {"Book/ExpandAll", NULL, N_("_Expand all"), NULL, NULL, G_CALLBACK(addressbook_treenode_expand_all_cb) }, + {"Book/CollapseAll", NULL, N_("_Collapse all"), NULL, NULL, G_CALLBACK(addressbook_treenode_collapse_all_cb) }, + /* {"Book/---", NULL, "---", NULL, NULL, NULL }, */ {"Book/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(addressbook_file_save_cb) }, {"Book/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(close_cb) }, -/* Adress menu */ +/* Edit menu */ {"Address/SelectAll", NULL, N_("_Select all"), "<control>A", NULL, G_CALLBACK(addressbook_select_all_cb) }, {"Address/---", NULL, "---", NULL, NULL, NULL }, {"Address/Cut", NULL, N_("C_ut"), "<control>X", NULL, G_CALLBACK(addressbook_clip_cut_cb) }, @@ -957,6 +978,9 @@ static void addressbook_create(void) MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "EditBook", "Book/EditBook", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "DeleteBook", "Book/DeleteBook", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Separator2", "Book/---", GTK_UI_MANAGER_SEPARATOR) + MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "ExpandAll", "Book/ExpandAll", GTK_UI_MANAGER_MENUITEM) + MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "CollapseAll", "Book/CollapseAll", GTK_UI_MANAGER_MENUITEM) + MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Separator3", "Book/---", GTK_UI_MANAGER_SEPARATOR) MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Save", "Book/Save", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Close", "Book/Close", GTK_UI_MANAGER_MENUITEM) @@ -1025,6 +1049,10 @@ static void addressbook_create(void) g_signal_connect(G_OBJECT(ctree), "button_release_event", G_CALLBACK(addressbook_tree_button_released), NULL); + g_signal_connect(G_OBJECT(ctree), "tree_collapse", + G_CALLBACK(addressbook_tree_collapse_node), NULL); + g_signal_connect(G_OBJECT(ctree), "tree_expand", + G_CALLBACK(addressbook_tree_expand_node), NULL); /* TEMPORARY */ g_signal_connect(G_OBJECT(ctree), "select_row", G_CALLBACK(addressbook_select_row_tree), NULL); @@ -1271,6 +1299,7 @@ static void addressbook_create(void) MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Copy", "ABListPopup/Copy", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Paste", "ABListPopup/Paste", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator4", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR) + MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator5", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR) MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Mailto", "ABListPopup/Mailto", GTK_UI_MANAGER_MENUITEM) #ifdef USE_LDAP MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "BrowseEntry", "ABListPopup/BrowseEntry", GTK_UI_MANAGER_MENUITEM) @@ -1331,6 +1360,10 @@ static void addressbook_create(void) gtk_paned_set_position(GTK_PANED(hpaned), prefs_common.addressbook_hpaned_pos); + /* update nodes status */ +fprintf(stderr, "==> FORCING addressbook_tree_selected\n"); + addressbook_tree_selected(GTK_CMCTREE(ctree), + gtk_cmctree_node_nth(GTK_CMCTREE(ctree), 0), 0, NULL); gtk_widget_show_all(window); } @@ -1896,6 +1929,7 @@ static void addressbook_tree_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node, ItemFolder *rootFolder = NULL; AddressObjectType aot; +fprintf(stderr, "==> addressbook_tree_selected:\n"); addrbook.treeSelected = node; addrbook.listSelected = NULL; addressbook_status_show( "" ); @@ -1904,6 +1938,7 @@ static void addressbook_tree_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node, if( node ) obj = gtk_cmctree_node_get_row_data( ctree, node ); if( obj == NULL ) { addressbook_set_clist(NULL, TRUE); +fprintf(stderr, "==> addressbook_tree_selected: return #1\n"); return; } addrbook.opened = node; @@ -1912,6 +1947,7 @@ static void addressbook_tree_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node, /* Read from file */ static gboolean tVal = TRUE; +fprintf(stderr, "==> addressbook_tree_selected: ADDR_DATASOURCE\n"); ads = ADAPTER_DSOURCE(obj); ds = ads->dataSource; @@ -1945,7 +1981,8 @@ static void addressbook_tree_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node, } addressbook_node_add_folder( node, ds, rootFolder, aot ); addrindex_ds_set_access_flag( ds, &tVal ); - gtk_cmctree_expand( ctree, node ); +/*TODO*/ +/* gtk_cmctree_expand( ctree, node );*/ } } else { addressbook_set_clist(NULL, TRUE); @@ -1963,10 +2000,12 @@ static void addressbook_tree_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node, addressbook_edit_person_invalidate(NULL, NULL, NULL); /* Setup main menu selections */ +fprintf(stderr, "==> addressbook_tree_selected: set sens\n"); addressbook_menubar_set_sensitive( FALSE ); addressbook_menuitem_set_sensitive( obj, node ); addressbook_list_select_clear(); addressbook_list_menu_setup(); +fprintf(stderr, "==> addressbook_tree_selected: return #2\n"); return; } @@ -2125,6 +2164,85 @@ static void addressbook_select_row_tree (GtkCMCTree *ctree, gint column, gpointer data) { +fprintf(stderr, "==> addressbook_select_row_tree\n"); +} + +static void addressbook_treenode_collapse (GtkCMCTree *ctree, + GtkCMCTreeNode *node, + gboolean collapse) +{ + AddressObject *obj = NULL; + + if( node && GTK_CMCTREE_ROW(node)->children ) + obj = gtk_cmctree_node_get_row_data( ctree, node ); + if( !obj ) + return; + +fprintf(stderr, "==> addressbook_treenode_collapse: %d\n", collapse); + if( obj->type == ADDR_INTERFACE ) { + AdapterInterface *adapter = NULL; + AddressInterface *iface = NULL; + adapter = ADAPTER_INTERFACE(obj); + if (!adapter) + return; + iface = adapter->interface; + if( !iface ) + return; + +fprintf(stderr, "==> addressbook_tree_expand_node: if %d %s\n", obj->type, obj->name); + iface->isCollapsed = collapse; + + } else if ( obj->type == ADDR_DATASOURCE ) { + AdapterDSource *ads = NULL; + AddressDataSource *ds = NULL; + gboolean val = collapse; + +fprintf(stderr, "==> addressbook_tree_expand_node: ds %d %s\n", obj->type, obj->name); + ads = ADAPTER_DSOURCE(obj); + if( !ads ) + return; + ds = ads->dataSource; + if( !ds ) + return; + addrindex_ds_set_collapsed_flag( ds, &val ); + + } else if( obj->type == ADDR_ITEM_FOLDER ) { + AddressDataSource *ds = NULL; + AddrBookBase *adbase = NULL; + ItemFolder *folder = NULL; + AddressCache *cache = NULL; + +fprintf(stderr, "==> addressbook_tree_expand_node: folder %d %s\n", obj->type, obj->name); + folder = ADAPTER_FOLDER(obj)->itemFolder; + if( !folder ) + return; + addritem_folder_set_collapsed( folder, collapse); + ds = addressbook_find_datasource( node ); + if( !ds ) + return; + adbase = ( AddrBookBase * ) ds->rawDataSource; + if( !adbase ) + return; + cache = adbase->addressCache; + if( cache) + addrcache_set_dirty( cache, TRUE ); + } +} + +static void addressbook_tree_expand_node (GtkCMCTree *ctree, + GtkCMCTreeNode *node, + gpointer *data ) +{ +fprintf(stderr, "==> addressbook_tree_expand_node\n"); + addressbook_treenode_collapse(ctree, node, FALSE); +} + +static void addressbook_tree_collapse_node (GtkCMCTree *ctree, + GtkCMCTreeNode *node, + gpointer *data ) +{ +fprintf(stderr, "==> addressbook_tree_collapse_node\n"); + addressbook_treenode_collapse(ctree, node, TRUE); } /** @@ -2179,6 +2297,7 @@ static void addressbook_clip_cut_cb( GtkAction *action, gpointer data ) { addrclip_clear( _clipBoard_ ); addrclip_add( _clipBoard_, _addressSelect_ ); /* addrclip_list_show( _clipBoard_, stdout ); */ + addressbook_list_menu_setup(); } /** @@ -2189,12 +2308,13 @@ static void addressbook_clip_copy_cb(GtkAction *action, gpointer data) { addrclip_clear( _clipBoard_ ); addrclip_add( _clipBoard_, _addressSelect_ ); /* addrclip_list_show( _clipBoard_, stdout ); */ + addressbook_list_menu_setup(); } /** * Paste clipboard into address list widget. */ -static void addressbook_clip_paste_cb( GtkAction *action, gpointer data ) { +static void addressbook_clip_paste( AddressClipboard *clipBoard, GtkAction *action, gpointer data ) { GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree ); AddressObject *pobj = NULL; AddressDataSource *ds = NULL; @@ -2224,25 +2344,25 @@ static void addressbook_clip_paste_cb( GtkAction *action, gpointer data ) { abf = addressbook_get_book_file(); if( abf == NULL ) return; - if( _clipBoard_->cutFlag ) { + if( clipBoard->cutFlag ) { /* Paste/Cut */ - folderGroup = addrclip_paste_cut( _clipBoard_, abf, folder ); + folderGroup = addrclip_paste_cut( clipBoard, abf, folder ); /* Remove all groups and folders in clipboard from tree node */ addressbook_treenode_remove_item(); /* Remove all "cut" items */ - addrclip_delete_item( _clipBoard_ ); + addrclip_delete_item( clipBoard ); /* Clear clipboard - cut items??? */ - addrclip_clear( _clipBoard_ ); + addrclip_clear( clipBoard ); } else { /* Paste/Copy */ - folderGroup = addrclip_paste_copy( _clipBoard_, abf, folder ); + folderGroup = addrclip_paste_copy( clipBoard, abf, folder ); } - /* addrclip_list_show( _clipBoard_, stdout ); */ + /* addrclip_list_show( clipBoard, stdout ); */ if( folderGroup ) { /* Update tree by inserting node for each folder or group */ addressbook_treenode_add_list( @@ -2258,8 +2378,13 @@ static void addressbook_clip_paste_cb( GtkAction *action, gpointer data ) { gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), addrbook.opened), TRUE); - +} +/** + * Paste clipboard into address list widget. + */ +static void addressbook_clip_paste_cb( GtkAction *action, gpointer data ) { + addressbook_clip_paste( _clipBoard_, action, data); } /** @@ -2322,6 +2447,7 @@ static void addressbook_treenode_cut_cb( GtkAction *action, gpointer data ) { addrclip_clear( _clipBoard_ ); addrclip_add( _clipBoard_, _addressSelect_ ); /* addrclip_list_show( _clipBoard_, stdout ); */ + addressbook_list_menu_setup(); } /** @@ -2333,13 +2459,14 @@ static void addressbook_treenode_copy_cb( GtkAction *action, gpointer data ) { addrclip_clear( _clipBoard_ ); addrclip_add( _clipBoard_, _addressSelect_ ); /* addrclip_list_show( _clipBoard_, stdout ); */ + addressbook_list_menu_setup(); } /** * Paste clipboard into address tree widget. */ static void addressbook_treenode_paste_cb( GtkAction *action, gpointer data ) { - addressbook_clip_paste_cb(NULL,NULL); + addressbook_clip_paste( _clipBoard_, NULL, NULL); } /** @@ -2530,6 +2657,7 @@ static gboolean addressbook_tree_button_pressed(GtkWidget *ctree, gboolean canLookup = FALSE; GtkCMCTreeNode *node = NULL; +fprintf(stderr, "==> addressbook_tree_button_pressed\n"); if( ! event ) return FALSE; /* if( ! event || event->type != GDK_BUTTON_PRESS) return FALSE;*/ @@ -2550,7 +2678,7 @@ static gboolean addressbook_tree_button_pressed(GtkWidget *ctree, /* edit group */ addressbook_treenode_edit_cb(NULL, NULL); } else { - /* expand pr collapse */ + /* expand or collapse */ node = gtk_cmctree_node_nth(GTK_CMCTREE(ctree), row); gtk_cmctree_toggle_expansion(GTK_CMCTREE(ctree), node); } @@ -2667,6 +2795,8 @@ just_set_sens: cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook", canEdit ); cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook", canDelete ); + cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/ExpandAll", TRUE ); + cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/CollapseAll", TRUE ); cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut", canCut ); cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy", canCopy ); cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste", canPaste ); @@ -2837,6 +2967,60 @@ static gchar *addressbook_edit_datasource( AddressObject *obj, GtkCMCTreeNode *n } /* +* Expand all sub-items of the address book +*/ +static void addressbook_treenode_expand_all_cb(gpointer data, guint action, + GtkWidget *widget) +{ + GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree); + gint index = 0; + GtkCMCTreeNode* node = gtk_cmctree_node_nth(ctree, index); + + while (node) { + gtk_cmctree_expand_recursive(ctree, node); + node = gtk_cmctree_node_nth(ctree, ++index); + } +} + +/* +* Expand all sub-items of the current node +*/ +static void addressbook_treenode_expand_sub_cb(gpointer data, guint action, + GtkWidget *widget) +{ + if ( ! addrbook.treeSelected ) + return; + gtk_cmctree_expand_recursive(GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected); +} + +/* +* Collapse all sub-items of the address book +*/ +static void addressbook_treenode_collapse_all_cb(gpointer data, guint action, + GtkWidget *widget) +{ + GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree); + gint index = 0; + GtkCMCTreeNode* node = gtk_cmctree_node_nth(ctree, index); + + while (node) { + gtk_cmctree_collapse_recursive(ctree, node); + node = gtk_cmctree_node_nth(ctree, ++index); + } +} + +/* +* Collapse all sub-items of the current node +*/ +static void addressbook_treenode_collapse_sub_cb(gpointer data, guint action, + GtkWidget *widget) +{ + if ( ! addrbook.treeSelected ) + return; + gtk_cmctree_collapse_recursive(GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected); +} + +/* * Edit an object that is in the address tree area. */ static void addressbook_treenode_edit_cb(GtkAction *action, gpointer data) @@ -3184,6 +3368,7 @@ static ItemFolder * addressbook_setup_subf( /* Now let's see the folder */ nNode = addressbook_node_add_folder( pNode, ds, folder, aoType ); +fprintf(stderr, "==> EXPAND %s: addressbook_setup_subf\n", folder->obj.name); gtk_cmctree_expand( ctree, pNode ); if( nNode ) { gtk_sctree_select( GTK_SCTREE(ctree), nNode ); @@ -4406,12 +4591,11 @@ static GtkCMCTreeNode *addressbook_node_add_folder( { GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree); GtkCMCTreeNode *newNode = NULL; - AdapterFolder *adapter; AddressTypeControlItem *atci = NULL; GList *listItems = NULL; - gchar *name; ItemFolder *rootFolder; +fprintf(stderr, "==> addressbook_node_add_folder\n"); /* Only visible folders */ if( itemFolder == NULL || itemFolder->isHidden ) return NULL; @@ -4431,6 +4615,9 @@ static GtkCMCTreeNode *addressbook_node_add_folder( newNode = node; } else { + AdapterFolder *adapter; + gchar *name = NULL; + adapter = g_new0( AdapterFolder, 1 ); ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_FOLDER; ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemFolder) ); @@ -4444,6 +4631,7 @@ static GtkCMCTreeNode *addressbook_node_add_folder( gtk_cmctree_node_set_row_data_full( ctree, newNode, adapter, addressbook_free_treenode ); } +fprintf(stderr, "==> addressbook_node_add_folder adding FOLDER: %s\n", name); } listItems = itemFolder->listFolder; @@ -4459,6 +4647,25 @@ static GtkCMCTreeNode *addressbook_node_add_folder( listItems = g_list_next( listItems ); } gtk_sctree_sort_node( ctree, node ); + + /* restore expanded/collapsed status of non-root node */ + if( !itemFolder->isRoot ) { +fprintf(stderr, "--- addressbook_node_add_folder RESTORE STATUS: %s -> %d\n", itemFolder->obj.name, addritem_folder_get_collapsed( itemFolder )); + if( addritem_folder_get_collapsed( itemFolder ) ) { + g_signal_handlers_block_by_func(G_OBJECT(ctree), + G_CALLBACK(addressbook_tree_collapse_node), NULL); + gtk_cmctree_collapse( ctree, newNode ); + g_signal_handlers_unblock_by_func(G_OBJECT(ctree), + G_CALLBACK(addressbook_tree_collapse_node), NULL); + } else { + g_signal_handlers_block_by_func(G_OBJECT(ctree), + G_CALLBACK(addressbook_tree_expand_node), NULL); + gtk_cmctree_expand( ctree, newNode ); + g_signal_handlers_unblock_by_func(G_OBJECT(ctree), + G_CALLBACK(addressbook_tree_expand_node), NULL); + } + } + return newNode; } @@ -5844,6 +6051,7 @@ static void addressbook_drag_received_cb(GtkWidget *widget, gint row, column; GtkCMCTreeNode *node; GtkCMCTreeNode *lastopened = addrbook.opened; + AddressClipboard *clipBoard = NULL; if (!strncmp(gtk_selection_data_get_data(data), "Dummy_addr", 10)) { if (gtk_cmclist_get_selection_info @@ -5856,17 +6064,23 @@ static void addressbook_drag_received_cb(GtkWidget *widget, return; gtk_cmclist_freeze(GTK_CMCLIST(addrbook.clist)); + clipBoard = addrclip_create(); + addrclip_set_index( clipBoard, _addressIndex_ ); /* use the master clipBoard index */ if (gdk_drag_context_get_selected_action(drag_context) == GDK_ACTION_COPY || - !strcmp(gtk_selection_data_get_data(data), "Dummy_addr_copy")) - addressbook_clip_copy_cb(NULL, NULL); - else - addressbook_clip_cut_cb(NULL, NULL); + !strcmp(gtk_selection_data_get_data(data), "Dummy_addr_copy")) { + clipBoard->cutFlag = FALSE; + addrclip_add( clipBoard, _addressSelect_ ); + } else { + clipBoard->cutFlag = TRUE; + addrclip_add( clipBoard, _addressSelect_ ); + } gtk_sctree_select( GTK_SCTREE(addrbook.ctree), node); - addressbook_clip_paste_cb(NULL,NULL); + addressbook_clip_paste( clipBoard, NULL, NULL); gtk_sctree_select( GTK_SCTREE(addrbook.ctree), lastopened); gtk_cmclist_thaw(GTK_CMCLIST(addrbook.clist)); gtk_drag_finish(drag_context, TRUE, TRUE, time); } + addrclip_free( clipBoard ); } /* diff --git a/src/addrindex.c b/src/addrindex.c @@ -83,12 +83,15 @@ /* XML Attribute names */ #define ATTAG_BOOK_NAME "name" #define ATTAG_BOOK_FILE "file" +#define ATTAG_BOOK_COLLAPSED "collapsed" #define ATTAG_VCARD_NAME "name" #define ATTAG_VCARD_FILE "file" +#define ATTAG_VCARD_COLLAPSED "collapsed" #define ATTAG_JPILOT_NAME "name" #define ATTAG_JPILOT_FILE "file" +#define ATTAG_JPILOT_COLLAPSED "collapsed" #define ATTAG_JPILOT_CUSTOM_1 "custom-1" #define ATTAG_JPILOT_CUSTOM_2 "custom-2" #define ATTAG_JPILOT_CUSTOM_3 "custom-3" @@ -96,6 +99,7 @@ #define ATTAG_JPILOT_CUSTOM "custom-" #define ATTAG_LDAP_NAME "name" +#define ATTAG_LDAP_COLLAPSED "collapsed" #define ATTAG_LDAP_HOST "host" #define ATTAG_LDAP_PORT "port" #define ATTAG_LDAP_BASE_DN "base-dn" @@ -210,6 +214,7 @@ static AddressInterface *addrindex_create_interface( iface->getAllGroups = NULL; iface->getName = NULL; iface->listSource = NULL; + iface->setCollapsedFlag = NULL; /* Search stuff */ iface->externalQuery = FALSE; @@ -234,6 +239,7 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) { iface->readOnly = FALSE; iface->getModifyFlag = ( void * ) addrbook_get_modified; iface->getAccessFlag = ( void * ) addrbook_get_accessed; + iface->getCollapsedFlag = ( void * ) addrbook_get_collapsed; iface->getReadFlag = ( void * ) addrbook_get_read_flag; iface->getStatusCode = ( void * ) addrbook_get_status; iface->getReadData = ( void * ) addrbook_read_data; @@ -244,6 +250,7 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) { iface->getAllGroups = ( void * ) addrbook_get_all_groups; iface->getName = ( void * ) addrbook_get_name; iface->setAccessFlag = ( void * ) addrbook_set_accessed; + iface->setCollapsedFlag = ( void * ) addrbook_set_collapsed; iface->searchOrder = 0; /* Add to list of interfaces in address book */ @@ -256,6 +263,7 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) { ADDR_IF_VCARD, "vCard", TAG_IF_VCARD, TAG_DS_VCARD ); iface->getModifyFlag = ( void * ) vcard_get_modified; iface->getAccessFlag = ( void * ) vcard_get_accessed; + iface->getCollapsedFlag = ( void * ) vcard_get_collapsed; iface->getReadFlag = ( void * ) vcard_get_read_flag; iface->getStatusCode = ( void * ) vcard_get_status; iface->getReadData = ( void * ) vcard_read_data; @@ -265,6 +273,7 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) { iface->getAllPersons = ( void * ) vcard_get_all_persons; iface->getName = ( void * ) vcard_get_name; iface->setAccessFlag = ( void * ) vcard_set_accessed; + iface->setCollapsedFlag = ( void * ) vcard_set_collapsed; iface->searchOrder = 0; addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface ); @@ -279,6 +288,7 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) { iface->useInterface = iface->haveLibrary; iface->getModifyFlag = ( void * ) jpilot_get_modified; iface->getAccessFlag = ( void * ) jpilot_get_accessed; + iface->getCollapsedFlag = ( void * ) jpilot_get_collapsed; iface->getReadFlag = ( void * ) jpilot_get_read_flag; iface->getStatusCode = ( void * ) jpilot_get_status; iface->getReadData = ( void * ) jpilot_read_data; @@ -288,6 +298,7 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) { iface->getAllPersons = ( void * ) jpilot_get_all_persons; iface->getName = ( void * ) jpilot_get_name; iface->setAccessFlag = ( void * ) jpilot_set_accessed; + iface->setCollapsedFlag = ( void * ) jpilot_set_collapsed; iface->searchOrder = 0; #else iface->useInterface = FALSE; @@ -307,6 +318,7 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) { iface->useInterface = iface->haveLibrary; iface->getModifyFlag = ( void * ) ldapsvr_get_modified; iface->getAccessFlag = ( void * ) ldapsvr_get_accessed; + iface->getCollapsedFlag = NULL; iface->getReadFlag = ( void * ) ldapsvr_get_read_flag; iface->getStatusCode = ( void * ) ldapsvr_get_status; iface->getReadData = ( void * ) ldapsvr_read_data; @@ -315,6 +327,7 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) { iface->getListPerson = ( void * ) ldapsvr_get_list_person; iface->getName = ( void * ) ldapsvr_get_name; iface->setAccessFlag = ( void * ) ldapsvr_set_accessed; + iface->setCollapsedFlag = NULL; iface->externalQuery = TRUE; iface->searchOrder = 1; #else @@ -1123,6 +1136,10 @@ static AddressDataSource *addrindex_parse_book( XMLFile *file ) { else if( strcmp( name, ATTAG_BOOK_FILE ) == 0) { addrbook_set_file( abf, value ); } + else if( strcmp( name, ATTAG_BOOK_COLLAPSED ) == 0) { + addrbook_set_collapsed( abf, + (strcmp( value, ATVAL_BOOLEAN_YES ) == 0) ? TRUE : FALSE ); + } attr = g_list_next( attr ); } ds->rawDataSource = abf; @@ -1138,6 +1155,9 @@ static int addrindex_write_book( FILE *fp, AddressDataSource *ds, gint lvl ) { return -1; if (addrindex_write_attr( fp, ATTAG_BOOK_FILE, abf->fileName ) < 0) return -1; + if (addrindex_write_attr( fp, ATTAG_BOOK_COLLAPSED, + abf->addressCache->collapsedFlag ? ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO ) < 0) + return -1; if (claws_fputs( " />\n", fp ) == EOF) return -1; } @@ -1161,6 +1181,10 @@ static AddressDataSource *addrindex_parse_vcard( XMLFile *file ) { else if( strcmp( name, ATTAG_VCARD_FILE ) == 0) { vcard_set_file( vcf, value ); } + else if( strcmp( name, ATTAG_VCARD_COLLAPSED ) == 0) { + vcard_set_collapsed( vcf, + (strcmp( value, ATVAL_BOOLEAN_YES ) == 0) ? TRUE : FALSE ); + } attr = g_list_next( attr ); } ds->rawDataSource = vcf; @@ -1176,6 +1200,9 @@ static int addrindex_write_vcard( FILE *fp, AddressDataSource *ds, gint lvl ) { return -1; if (addrindex_write_attr( fp, ATTAG_VCARD_FILE, vcf->path ) < 0) return -1; + if (addrindex_write_attr( fp, ATTAG_VCARD_COLLAPSED, + vcf->addressCache->collapsedFlag ? ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO ) < 0) + return -1; if (claws_fputs( " />\n", fp ) == EOF) return -1; } @@ -1212,6 +1239,10 @@ static AddressDataSource *addrindex_parse_jpilot( XMLFile *file ) { else if( strcmp( name, ATTAG_JPILOT_CUSTOM_4 ) == 0 ) { jpilot_add_custom_label( jpf, value ); } + else if( strcmp( name, ATTAG_JPILOT_COLLAPSED ) == 0 ) { + jpilot_set_collapsed( jpf, + (strcmp( value, ATVAL_BOOLEAN_YES ) == 0) ? TRUE : FALSE ); + } attr = g_list_next( attr ); } ds->rawDataSource = jpf; @@ -1230,6 +1261,9 @@ static int addrindex_write_jpilot( FILE *fp,AddressDataSource *ds, gint lvl ) { return -1; if (addrindex_write_attr( fp, ATTAG_JPILOT_FILE, jpf->path ) < 0) return -1; + if (addrindex_write_attr( fp, ATTAG_JPILOT_COLLAPSED, + jpf->addressCache->collapsedFlag ? ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO ) < 0) + return -1; node = customLbl; ind = 1; while( node ) { @@ -1357,7 +1391,7 @@ static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) { gchar *serverName = NULL; gchar *criteria = NULL; gboolean bDynSearch; - gboolean bTLS, bSSL; + gboolean bTLS, bSSL, bCollapsed; gint iMatch; gchar *password = NULL; @@ -1433,6 +1467,12 @@ static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) { bSSL = TRUE; } } + else if( strcmp( name, ATTAG_LDAP_COLLAPSED ) == 0 ) { + bCollapsed = FALSE; + if( strcmp( value, ATVAL_BOOLEAN_YES ) == 0 ) { + bCollapsed = TRUE; + } + } attr = g_list_next( attr ); } @@ -1712,6 +1752,9 @@ static int addrindex_write_index( AddressIndex *addrIndex, FILE *fp ) { nodeDS = iface->listSource; if (addrindex_write_elem_s( fp, lvlList, iface->listTag ) < 0) return -1; + if (addrindex_write_attr( fp, ATTAG_BOOK_COLLAPSED, + iface->isCollapsed ? ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO ) < 0) + return -1; if (claws_fputs( ">\n", fp ) == EOF) return -1; while( nodeDS ) { @@ -2401,6 +2444,24 @@ gboolean addrindex_ds_get_read_flag( AddressDataSource *ds ) { } /* + * Return collapsed flag for specified data source. + */ +gboolean addrindex_ds_get_collapsed_flag( AddressDataSource *ds ) { + gboolean retVal = FALSE; + AddressInterface *iface; + +fprintf(stderr, "==> addrindex_ds_get_collapsed_flag:\n"); + if( ds == NULL ) return retVal; + iface = ds->interface; + if( iface == NULL ) return retVal; + if( iface->getCollapsedFlag ) { +fprintf(stderr, "==> addrindex_ds_get_collapsed_flag: invoking iface's getCollapsedFlag\n"); + retVal = ( iface->getCollapsedFlag ) ( ds->rawDataSource ); + } + return retVal; +} + +/* * Return status code for specified data source. */ gint addrindex_ds_get_status_code( AddressDataSource *ds ) { @@ -2483,6 +2544,22 @@ void addrindex_ds_set_access_flag( AddressDataSource *ds, gboolean *value ) { } /* + * Set the collapsed flag inside the data source. + */ +void addrindex_ds_set_collapsed_flag( AddressDataSource *ds, gboolean *value ) { + AddressInterface *iface; + +fprintf(stderr, "==> addrindex_ds_set_collapsed_flag:\n"); + if( ds == NULL ) return; + iface = ds->interface; + if( iface == NULL ) return; + if( iface->setCollapsedFlag) { +fprintf(stderr, "==> addrindex_ds_set_collapsed_flag: invoking iface's setCollapsedFlag\n"); + ( iface->setCollapsedFlag ) ( ds->rawDataSource, *value ); + } +} + +/* * Return read only flag for specified data source. */ gboolean addrindex_ds_get_readonly( AddressDataSource *ds ) { diff --git a/src/addrindex.h b/src/addrindex.h @@ -73,8 +73,10 @@ struct _AddressInterface { gboolean haveLibrary; gboolean readOnly; GList *listSource; + gboolean isCollapsed; gboolean (*getModifyFlag)( void * ); gboolean (*getAccessFlag)( void * ); + gboolean (*getCollapsedFlag)( void * ); gboolean (*getReadFlag)( void * ); gint (*getStatusCode)( void * ); gint (*getReadData)( void * ); @@ -85,6 +87,7 @@ struct _AddressInterface { GList *(*getAllGroups)( void * ); gchar *(*getName)( void * ); void (*setAccessFlag)( void *, void * ); + void (*setCollapsedFlag)( void *, const gboolean ); gboolean externalQuery; gint searchOrder; void (*startSearch)( void * ); @@ -138,12 +141,15 @@ gint addrindex_save_all_books ( AddressIndex *addrIndex ); gboolean addrindex_ds_get_modify_flag ( AddressDataSource *ds ); gboolean addrindex_ds_get_access_flag ( AddressDataSource *ds ); gboolean addrindex_ds_get_read_flag ( AddressDataSource *ds ); +gboolean addrindex_ds_get_collapsed_flag ( AddressDataSource *ds ); gint addrindex_ds_get_status_code ( AddressDataSource *ds ); gint addrindex_ds_read_data ( AddressDataSource *ds ); ItemFolder *addrindex_ds_get_root_folder( AddressDataSource *ds ); gchar *addrindex_ds_get_name ( AddressDataSource *ds ); void addrindex_ds_set_access_flag ( AddressDataSource *ds, gboolean *value ); +void addrindex_ds_set_collapsed_flag ( AddressDataSource *ds, + gboolean *value ); gboolean addrindex_ds_get_readonly ( AddressDataSource *ds ); /* Search support */ diff --git a/src/addritem.c b/src/addritem.c @@ -802,6 +802,7 @@ ItemFolder *addritem_create_item_folder( void ) { folder->folderType = ADDRFOLDER_NONE; folder->folderData = NULL; folder->isHidden = FALSE; + folder->isCollapsed = TRUE; return folder; } @@ -851,6 +852,27 @@ void addritem_folder_set_hidden( ItemFolder *folder, const gboolean value ) { } /** + * Get expanded/collapsed status of folder. + * \param folder Folder. + */ +gboolean addritem_folder_get_collapsed ( ItemFolder *folder ) +{ +fprintf(stderr, "==> addritem_folder_get_collapsed: %d\n", folder->isCollapsed); + return folder->isCollapsed; +} + +/** + * Specify expanded/collapsed status of folder. + * \param folder Folder. + * \param value Set to <code>TRUE</code> if the folder is collapsed. + */ +void addritem_folder_set_collapsed ( ItemFolder *folder, const gboolean value) +{ +fprintf(stderr, "==> addritem_folder_set_collapsed: %d\n", value); + folder->isCollapsed = value; +} + +/** * Free address folder. Note: this does not free up the lists of children * (folders, groups and person). This should be done prior to calling this * function. @@ -879,6 +901,7 @@ void addritem_free_item_folder( ItemFolder *folder ) { folder->folderType = ADDRFOLDER_NONE; folder->folderData = NULL; folder->isHidden = FALSE; + folder->isCollapsed = TRUE; g_free( folder ); } @@ -947,6 +970,7 @@ void addritem_print_item_folder( ItemFolder *folder, FILE *stream ) { fprintf( stream, "\trem: '%s'\n", folder->remarks ); fprintf( stream, "\ttyp: %d\n", folder->folderType ); fprintf( stream, "\thid: %s\n", folder->isHidden ? "hidden" : "visible" ); + fprintf( stream, "\texp: %s\n", folder->isCollapsed ? "collapsed" : "expanded" ); fprintf( stream, "\t---\n" ); parent = ( ItemFolder * ) ADDRITEM_PARENT(folder); if( parent ) { diff --git a/src/addritem.h b/src/addritem.h @@ -108,6 +108,7 @@ struct _ItemFolder { AddressFolderType folderType; /* Folder type */ gpointer *folderData; /* Pointer to folder's data */ gboolean isHidden; /* TRUE if folder is hidden */ + gboolean isCollapsed; /* TRUE if folder is collapsed */ }; typedef struct _ItemGroup ItemGroup; @@ -180,6 +181,8 @@ gboolean addritem_folder_add_folder ( ItemFolder *folder, ItemFolder *item ); gboolean addritem_folder_add_group ( ItemFolder *folder, ItemGroup *item ); GList *addritem_folder_get_person_list ( ItemFolder *folder ); GList *addritem_folder_get_group_list ( ItemFolder *folder ); +gboolean addritem_folder_get_collapsed ( ItemFolder *folder ); +void addritem_folder_set_collapsed ( ItemFolder *folder, const gboolean value); void addritem_parse_first_last ( ItemPerson *person ); GList *addritem_folder_path ( const ItemFolder *folder, diff --git a/src/common/smtp.c b/src/common/smtp.c @@ -106,6 +106,9 @@ Session *smtp_session_new(void *prefs_account) session->error_val = SM_OK; session->error_msg = NULL; + session->is_dsn_requested = FALSE; + session->is_dsn_supported = FALSE; + return SESSION(session); } @@ -127,6 +130,7 @@ gint smtp_from(SMTPSession *session) { gchar buf[MESSAGEBUFSIZE]; gchar *mail_size = NULL; + gchar *dsn_parm = NULL; cm_return_val_if_fail(session->from != NULL, SM_ERROR); @@ -137,15 +141,20 @@ gint smtp_from(SMTPSession *session) else mail_size = g_strdup(""); + if (session->is_dsn_requested && session->is_dsn_supported) + dsn_parm = g_strdup(" RET=HDRS"); + else + dsn_parm = g_strdup(""); if (strchr(session->from, '<')) - g_snprintf(buf, sizeof(buf), "MAIL FROM:%s%s", session->from, - mail_size); + g_snprintf(buf, sizeof(buf), "MAIL FROM:%s%s%s", session->from, + mail_size, dsn_parm); else - g_snprintf(buf, sizeof(buf), "MAIL FROM:<%s>%s", session->from, - mail_size); + g_snprintf(buf, sizeof(buf), "MAIL FROM:<%s>%s%s", session->from, + mail_size, dsn_parm); g_free(mail_size); + g_free(dsn_parm); if (session_send_msg(SESSION(session), buf) < 0) return SM_ERROR; @@ -335,6 +344,10 @@ static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg) p += 9; session->avail_auth_type |= SMTPAUTH_TLS_AVAILABLE; } + if (g_ascii_strncasecmp(p, "DSN", 3) == 0) { + p += 4; + session->is_dsn_supported = TRUE; + } return SM_OK; } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') && (msg[3] == ' ' || msg[3] == '\0')) @@ -460,17 +473,52 @@ static gint smtp_rcpt(SMTPSession *session) { gchar buf[MESSAGEBUFSIZE]; gchar *to; + gchar *dsn_parm = NULL; + gchar *open_bracket = NULL; + gchar *close_bracket = NULL; + gchar *to822 = NULL; cm_return_val_if_fail(session->cur_to != NULL, SM_ERROR); session->state = SMTP_RCPT; to = (gchar *)session->cur_to->data; + open_bracket = strchr(to, '<'); + + if (session->is_dsn_requested && session->is_dsn_supported) { + to822 = to; + if (open_bracket) { + /* make it RFC 822 compliant */ + to822 = g_strdup(open_bracket + 1); + close_bracket = strchr(to822, '>'); + if (close_bracket) { /* should always be true */ + *close_bracket = '\0'; + } + } + g_snprintf(buf, sizeof(buf), /* borrow the buf for a second */ + " NOTIFY=SUCCESS,DELAY,FAILURE ORCPT=rfc822;%s", to822); + dsn_parm = g_strdup(buf); /* make buf available again */ + if (open_bracket) { + g_free(to822); /* won't need it anymore */ + } + } + else { + if (session->is_dsn_requested + && session->is_dsn_supported == FALSE) { + log_warning(LOG_PROTOCOL, + _("DSN requested but server does not support it\n")); + } + dsn_parm = g_strdup(""); + } - if (strchr(to, '<')) - g_snprintf(buf, sizeof(buf), "RCPT TO:%s", to); + + if (open_bracket) + g_snprintf(buf, sizeof(buf), "RCPT TO:%s%s", to, dsn_parm); else - g_snprintf(buf, sizeof(buf), "RCPT TO:<%s>", to); + g_snprintf(buf, sizeof(buf), "RCPT TO:<%s>%s", to, dsn_parm); + + g_free(dsn_parm); /* won't need it anymore */ + if (session_send_msg(SESSION(session), buf) < 0) return SM_ERROR; log_print(LOG_PROTOCOL, "SMTP> %s\n", buf); diff --git a/src/common/smtp.h b/src/common/smtp.h @@ -113,6 +113,9 @@ struct _SMTPSession gchar *error_msg; gboolean is_esmtp; ESMTPFlag esmtp_flags; + + gboolean is_dsn_requested; + gboolean is_dsn_supported; void *dialog; diff --git a/src/compose.c b/src/compose.c @@ -499,6 +499,8 @@ static void compose_activate_privacy_system (Compose *compose, PrefsAccount *account, gboolean warn); static void compose_apply_folder_privacy_settings(Compose *compose, FolderItem *folder_item); +static void compose_toggle_dsn_requested_cb(GtkToggleAction *action, + gpointer data); static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data); static void compose_toggle_remove_refs_cb(GtkToggleAction *action, @@ -722,7 +724,8 @@ static GtkToggleActionEntry compose_toggle_entries[] = {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */ {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */ {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */ - {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */ + {"Options/RequestDSN", NULL, N_("Request _Notification of Successful Delivery"), NULL, NULL, G_CALLBACK(compose_toggle_dsn_requested_cb), FALSE }, /* Toggle */ + {"Options/RequestRetRcpt", NULL, N_("Request _Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */ {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */ {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */ }; @@ -1064,8 +1067,13 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI } else { compose_set_folder_prefs(compose, item, TRUE); } - if (item && item->ret_rcpt) { - cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE); + if (item) { + if (item->prefs && item->prefs->request_dsn) { + cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestDSN", TRUE); + } + if (item->ret_rcpt) { + cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE); + } } } else { if (mailto && *mailto != '\0') { @@ -1566,8 +1574,14 @@ static Compose *compose_generic_reply(MsgInfo *msginfo, compose_extract_original_charset(compose); - if (msginfo->folder && msginfo->folder->ret_rcpt) - cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE); + if (msginfo->folder) { + if (msginfo->folder->prefs && msginfo->folder->prefs->request_dsn) { + cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestDSN", TRUE); + } + if (msginfo->folder->ret_rcpt) { + cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE); + } + } /* Set save folder */ if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) { @@ -1753,6 +1767,11 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo, if (!compose->fwdinfo) compose->fwdinfo = procmsg_msginfo_copy(msginfo); + if (compose->deferred_destroy) { + compose_destroy(compose); + return NULL; + } + compose_extract_original_charset(compose); if (msginfo->subject && *msginfo->subject) { @@ -1956,7 +1975,7 @@ static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_ for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) { if (msginfo->data) { - MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED); +/* MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);*/ MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED); } } @@ -2404,6 +2423,13 @@ Compose *compose_reedit(MsgInfo *msginfo, gboolean batch) compose_set_save_to(compose, &queueheader_buf[4]); g_free(queueheader_buf); } + if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "DSN:")) { + gint dsn = atoi(&queueheader_buf[strlen("DSN:")]); + if (dsn) { + cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestDSN", TRUE); + } + g_free(queueheader_buf); + } if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) { gint active = atoi(&queueheader_buf[strlen("RRCPT:")]); if (active) { @@ -5596,6 +5622,7 @@ static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest) "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt", "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:", "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:", + "DSN:", NULL }; gint ret = 0; @@ -6262,6 +6289,9 @@ static ComposeQueueResult compose_queue_sub(Compose *compose, gint *msgnum, Fold if (compose->return_receipt) { err |= (fprintf(fp, "RRCPT:1\n") < 0); } + if (compose->dsn_requested) { + err |= (fprintf(fp, "DSN:1\n") < 0); + } /* Message-ID of message replying to */ if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) { gchar *folderid = NULL; @@ -7922,6 +7952,7 @@ static Compose *compose_create(PrefsAccount *account, MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR) + MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestDSN", "Options/RequestDSN", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR) MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM) @@ -9221,7 +9252,7 @@ static void compose_attach_update_label(Compose *compose) total_size += ainfo->size; i++; } - text = g_strdup_printf(" (%d/%s)", i, to_human_readable(total_size)); + text = g_strdup_printf(" (%d / %s)", i, to_human_readable(total_size)); gtk_label_set_text(GTK_LABEL(compose->attach_label), text); g_free(text); } @@ -10304,6 +10335,9 @@ gboolean compose_draft (gpointer data, guint action) if (compose->return_receipt) { err |= (fprintf(fp, "RRCPT:1\n") < 0); } + if (compose->dsn_requested) { + err |= (fprintf(fp, "DSN:1\n") < 0); + } if (compose->privacy_system) { err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0); err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0); @@ -11748,6 +11782,16 @@ static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer d compose->return_receipt = FALSE; } +static void compose_toggle_dsn_requested_cb(GtkToggleAction *action, gpointer data) +{ + Compose *compose = (Compose *)data; + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) + compose->dsn_requested = TRUE; + else + compose->dsn_requested = FALSE; +} + static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data) { Compose *compose = (Compose *)data; diff --git a/src/compose.h b/src/compose.h @@ -206,6 +206,7 @@ struct _Compose gboolean sending; gboolean return_receipt; + gboolean dsn_requested; gboolean batch; diff --git a/src/folder.c b/src/folder.c @@ -4840,6 +4840,7 @@ gint folder_item_search_msgs_local (Folder *folder, procmsg_msginfo_free(&msg); + gtk_main_iteration_do (FALSE); if (progress_cb != NULL && !progress_cb(progress_data, FALSE, processed_count, matched_count, msgcount)) diff --git a/src/folder_item_prefs.c b/src/folder_item_prefs.c @@ -127,6 +127,8 @@ static PrefParam param[] = { NULL, NULL, NULL}, {"forward_body_format", NULL, &tmp_prefs.forward_body_format, P_STRING, NULL, NULL, NULL}, + {"request_dsn", "FALSE", &tmp_prefs.request_dsn, P_BOOL, + NULL, NULL, NULL}, {"config_version", "-1", &tmp_prefs.config_version, P_INT, NULL, NULL, NULL}, {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL} @@ -212,6 +214,7 @@ static FolderItemPrefs *folder_item_prefs_clear(FolderItemPrefs *prefs) prefs->always_sign = SIGN_OR_ENCRYPT_DEFAULT; prefs->always_encrypt = SIGN_OR_ENCRYPT_DEFAULT; prefs->save_copy_to_folder = FALSE; + prefs->request_dsn = FALSE; prefs->enable_processing = FALSE; prefs->enable_processing_when_opening = FALSE; @@ -327,6 +330,7 @@ void folder_item_prefs_copy_prefs(FolderItem * src, FolderItem * dest) tmp_prefs.always_encrypt = src->prefs->always_encrypt; tmp_prefs.save_copy_to_folder = src->prefs->save_copy_to_folder; tmp_prefs.color = src->prefs->color; + tmp_prefs.request_dsn = src->prefs->request_dsn; tmp_prefs.compose_with_format = src->prefs->compose_with_format; tmp_prefs.compose_subject_format = g_strdup(src->prefs->compose_subject_format); diff --git a/src/folder_item_prefs.h b/src/folder_item_prefs.h @@ -54,6 +54,7 @@ struct _FolderItemPrefs { gboolean skip_on_goto_unread_or_new; gboolean request_return_receipt; + gboolean request_dsn; gboolean enable_default_to; gchar *default_to; gboolean enable_default_reply_to; diff --git a/src/gtk/manage_window.c b/src/gtk/manage_window.c @@ -23,64 +23,90 @@ #include "manage_window.h" #include "utils.h" +#define DEBUG 0 + GtkWidget *focus_window; gint manage_window_focus_in(GtkWidget *widget, GdkEventFocus *event, gpointer data) { -/* const gchar *title = NULL; */ +#if DEBUG + const gchar *title = NULL; +#endif if (!GTK_IS_WINDOW(widget)) return FALSE; - -/* title = gtk_window_get_title(GTK_WINDOW(widget)); + +#if DEBUG + title = gtk_window_get_title(GTK_WINDOW(widget)); debug_print("Focus in event: window: %p - %s\n", widget, - title ? title : "no title"); */ + title ? title : "no title"); +#endif focus_window = widget; +#if DEBUG + debug_print("focus_window = %p\n", focus_window); +#endif return FALSE; } gint manage_window_focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer data) { -/* const gchar *title = NULL; */ +#if DEBUG + const gchar *title = NULL; +#endif if (!GTK_IS_WINDOW(widget)) return FALSE; -/* title = gtk_window_get_title(GTK_WINDOW(widget)); +#if DEBUG + title = gtk_window_get_title(GTK_WINDOW(widget)); debug_print("Focus out event: window: %p - %s\n", widget, - title ? title : "no title"); */ + title ? title : "no title"); +#endif if (focus_window == widget) focus_window = NULL; +#if DEBUG + debug_print("focus_window = %p\n", focus_window); +#endif return FALSE; } gint manage_window_unmap(GtkWidget *widget, GdkEventAny *event, gpointer data) { -/* const gchar *title = gtk_window_get_title(GTK_WINDOW(widget)); +#if DEBUG + const gchar *title = gtk_window_get_title(GTK_WINDOW(widget)); debug_print("Unmap event: window: %p - %s\n", widget, - title ? title : "no title"); */ + title ? title : "no title"); +#endif if (focus_window == widget) focus_window = NULL; +#if DEBUG + debug_print("focus_window = %p\n", focus_window); +#endif return FALSE; } void manage_window_destroy(GtkWidget *widget, gpointer data) { -/* const gchar *title = gtk_window_get_title(GTK_WINDOW(widget)); +#if DEBUG + const gchar *title = gtk_window_get_title(GTK_WINDOW(widget)); debug_print("Destroy event: window: %p - %s\n", widget, - title ? title : "no title"); */ - + title ? title : "no title"); +#endif if (focus_window == widget) focus_window = NULL; + +#if DEBUG + debug_print("focus_window = %p\n", focus_window); +#endif } void manage_window_set_transient(GtkWindow *window) diff --git a/src/gtk/quicksearch.c b/src/gtk/quicksearch.c @@ -356,6 +356,11 @@ static gboolean searchbar_pressed(GtkWidget *widget, GdkEventKey *event, if (event != NULL && (event->keyval == GDK_KEY_Escape)) { gchar *str; + if (quicksearch->running) { + advsearch_abort(quicksearch->asearch); + return TRUE; + } + quicksearch->in_typing = FALSE; str = quicksearch_get_text(quicksearch); @@ -403,7 +408,7 @@ static gboolean searchbar_pressed(GtkWidget *widget, GdkEventKey *event, * * When adding new lines, remember to put 2 strings for each line */ -static gchar *search_descr_strings[] = { +gchar *extended_search_descr_strings[] = { "a", N_("all messages"), "ag #", N_("messages whose age is greater than # days"), "al #", N_("messages whose age is less than # days"), @@ -471,7 +476,7 @@ static DescriptionWindow search_descr = { N_("Extended Search allows the user to define criteria that messages must " "have in order to match and be displayed in the message list.\n" "The following symbols can be used:"), - search_descr_strings + extended_search_descr_strings }; static void search_description_cb(GtkWidget *widget) diff --git a/src/gtk/quicksearch.h b/src/gtk/quicksearch.h @@ -26,6 +26,8 @@ typedef struct _QuickSearch QuickSearch; typedef void (*QuickSearchExecuteCallback) (QuickSearch *quicksearch, gpointer data); +extern gchar *extended_search_descr_strings[]; + #include "procmsg.h" QuickSearch *quicksearch_new(); diff --git a/src/html.c b/src/html.c @@ -68,6 +68,7 @@ SC_HTMLParser *sc_html_parser_new(FILE *fp, CodeConverter *conv) parser->empty_line = TRUE; parser->space = FALSE; parser->pre = FALSE; + parser->msword = FALSE; parser->indent = 0; return parser; @@ -379,10 +380,23 @@ static SC_HTMLState sc_html_parse_tag(SC_HTMLParser *parser) parser->state = SC_HTML_HREF; } else if (!strcmp(tag->name, "p")) { parser->space = FALSE; + GList *cur; + gboolean realp = TRUE; /* a proper paragraph */ + if (parser->msword) { + /* Look for paragraphs that are just lines without spacing. */ + for (cur = tag->attr; cur != NULL; cur = cur->next) { + if (cur->data && + !strcmp(((SC_HTMLAttr *)cur->data)->name, "class") && + !strcmp(((SC_HTMLAttr *)cur->data)->value, "MsoPlainText")) { + realp = FALSE; /* used to be a text/plain line */ + break; + } + } + } if (!parser->empty_line) { parser->space = FALSE; if (!parser->newline) sc_html_append_char(parser, '\n'); - sc_html_append_char(parser, '\n'); + if (realp) sc_html_append_char(parser, '\n'); } parser->state = SC_HTML_PAR; } else if (!strcmp(tag->name, "pre")) { @@ -432,7 +446,7 @@ static SC_HTMLState sc_html_parse_tag(SC_HTMLParser *parser) if (!parser->empty_line) { parser->space = FALSE; if (!parser->newline) sc_html_append_char(parser, '\n'); - sc_html_append_char(parser, '\n'); +// sc_html_append_char(parser, '\n'); } parser->state = SC_HTML_NORMAL; } else if (!strcmp(tag->name, "/div") || @@ -443,7 +457,24 @@ static SC_HTMLState sc_html_parse_tag(SC_HTMLParser *parser) sc_html_append_char(parser, '\n'); } parser->state = SC_HTML_NORMAL; + } else if (!strcmp(tag->name, "meta")) { + /* Look for HTML from MS Outlook, which lists a version of Microsoft Word as Generator */ + GList *cur; + int msword = 0; + for (cur = tag->attr; cur != NULL; cur = cur->next) { + if (cur->data && + !strcmp(((SC_HTMLAttr *)cur->data)->name, "name") && + !strcmp(((SC_HTMLAttr *)cur->data)->value, "Generator") ) { + ++msword; + } + if (cur->data && + !strcmp(((SC_HTMLAttr *)cur->data)->name, "content") && + !strncmp(((SC_HTMLAttr *)cur->data)->value, "Microsoft Word ", 15) ) { + ++msword; } + } + if (msword == 2) parser->msword = TRUE; + } sc_html_free_tag(tag); diff --git a/src/html.h b/src/html.h @@ -63,6 +63,7 @@ struct _SC_HTMLParser gboolean empty_line; gboolean space; gboolean pre; + gboolean msword; gint indent; }; diff --git a/src/jpilot.c b/src/jpilot.c @@ -190,6 +190,7 @@ JPilotFile *jpilot_create() { pilotFile->labelInd = NULL; pilotFile->havePC3 = FALSE; pilotFile->pc3ModifyTime = 0; + pilotFile->addressCache->collapsedFlag = TRUE; return pilotFile; } @@ -1661,6 +1662,19 @@ gboolean jpilot_test_pilot_lib( void ) { return TRUE; } +gboolean jpilot_get_collapsed( JPilotFile *pilotFile ) { + g_return_val_if_fail( pilotFile != NULL, FALSE ); +fprintf(stderr, "==> jpilot_get_collapsed: %d\n", pilotFile->addressCache->collapsedFlag); + return pilotFile->addressCache->collapsedFlag; +} + +void jpilot_set_collapsed( JPilotFile *pilotFile, const gboolean value ) { + g_return_if_fail( pilotFile != NULL ); +fprintf(stderr, "==> jpilot_set_collapsed: %d\n", value); + pilotFile->addressCache->collapsedFlag = value; + addrcache_set_dirty(pilotFile->addressCache, TRUE); +} + #endif /* USE_JPILOT */ /* diff --git a/src/jpilot.h b/src/jpilot.h @@ -79,7 +79,9 @@ void jpilot_free ( JPilotFile *pilotFile ); gint jpilot_get_status ( JPilotFile *pilotFile ); gboolean jpilot_get_modified ( JPilotFile *pilotFile ); gboolean jpilot_get_accessed ( JPilotFile *pilotFile ); +gboolean jpilot_get_collapsed ( JPilotFile *pilotFile ); void jpilot_set_accessed ( JPilotFile *pilotFile, const gboolean value ); +void jpilot_set_collapsed ( JPilotFile *pilotFile, const gboolean value ); gboolean jpilot_get_read_flag ( JPilotFile *pilotFile ); ItemFolder *jpilot_get_root_folder ( JPilotFile *pilotFile ); gchar *jpilot_get_name ( JPilotFile *pilotFile ); diff --git a/src/mainwindow.c b/src/mainwindow.c @@ -2457,6 +2457,9 @@ static void main_window_set_account_selector_menu(MainWindow *mainwin, for (cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) { ac_prefs = (PrefsAccount *)cur_ac->data; + if (!ac_prefs->selectable_as_current_account) { + continue; + } menuitem = gtk_menu_item_new_with_label (ac_prefs->account_name ? ac_prefs->account_name : _("Untitled")); @@ -2504,6 +2507,8 @@ static void main_window_set_account_receive_menu(MainWindow *mainwin, for (child = account_list; child != NULL; child = child->next) { ac_prefs = (PrefsAccount *)child->data; + if (!ac_prefs->selectable_as_current_account) + continue; if (ac_prefs->protocol == A_NONE) continue; @@ -2537,6 +2542,8 @@ static void main_window_set_toolbar_combo_receive_menu(MainWindow *mainwin, for (cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) { ac_prefs = (PrefsAccount *)cur_ac->data; + if (!ac_prefs->selectable_as_current_account) + continue; if (ac_prefs->protocol == A_NONE) continue; @@ -2572,6 +2579,9 @@ static void main_window_set_toolbar_combo_compose_menu(MainWindow *mainwin, for (cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) { ac_prefs = (PrefsAccount *)cur_ac->data; + if (!ac_prefs->selectable_as_current_account) { + continue; + } menuitem = gtk_menu_item_new_with_label (ac_prefs->account_name ? ac_prefs->account_name : _("Untitled")); @@ -2947,6 +2957,7 @@ SensitiveCondMask main_window_get_current_state(MainWindow *mainwin) FolderItem *item = mainwin->summaryview->folder_item; GList *account_list = account_get_list(); GSList *tmp; + guint acc; selection = summary_get_selection_type(mainwin->summaryview); @@ -3028,18 +3039,28 @@ SensitiveCondMask main_window_get_current_state(MainWindow *mainwin) if (item && item->prefs->processing && selection != SUMMARY_NONE) UPDATE_STATE(M_HAVE_PROCESSING); - if (g_list_length(account_list) > 1) - UPDATE_STATE(M_HAVE_MULTI_ACCOUNT); + acc = 0; + for (account_list = account_get_list(); account_list != NULL; account_list = account_list->next) { + if (((PrefsAccount*)account_list->data)->selectable_as_current_account) { + if (acc >= 1) { + UPDATE_STATE(M_HAVE_MULTI_ACCOUNT); + break; + } + acc++; + } + } for (account_list = account_get_list(); account_list != NULL; account_list = account_list->next) { - if (((PrefsAccount*)account_list->data)->protocol != A_NONE) { + if ((((PrefsAccount*)account_list->data)->protocol != A_NONE) && + ((PrefsAccount*)account_list->data)->selectable_as_current_account) { UPDATE_STATE(M_HAVE_ANY_RETRIEVABLE_ACCOUNT); break; } } for (account_list = account_get_list(); account_list != NULL; account_list = account_list->next) { - if (((PrefsAccount*)account_list->data)->protocol == A_NNTP) { + if ((((PrefsAccount*)account_list->data)->protocol == A_NNTP) && + ((PrefsAccount*)account_list->data)->selectable_as_current_account) { UPDATE_STATE(M_HAVE_NEWS_ACCOUNT); break; } diff --git a/src/matcher.c b/src/matcher.c @@ -777,6 +777,67 @@ static gboolean matcherprop_match_test(const MatcherProp *prop, return (retval == 0); } +gint matcherprop_parse_date_time(gint criteria, const gchar *s, struct tm *tm_out) +{ + struct tm tm; + + memset(&tm, 0, sizeof(struct tm)); + tm.tm_isdst = -1; + + if (sscanf(s, "%04d-%02d-%02d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday) == 3) + { + --tm.tm_mon; + tm.tm_year -= 1900; + } + + if (criteria == MATCHCRITERIA_DATE_AFTER) + { + tm.tm_hour = 23; + tm.tm_min = tm.tm_sec = 59; + } + + s = strchr(s, ' '); + if (s) + { + int *v[] = { &tm.tm_hour, &tm.tm_min, &tm.tm_sec }; + int i; + + tm.tm_hour = tm.tm_min = tm.tm_sec = -1; + for (i = 0, ++s; i < 3; ++s) + { + if (*s >= '0' && *s <= '9') + { + if (*v[i] < 0) + *v[i] = 0; + else + *v[i] *= 10; + *v[i] += *s - '0'; + } + else if (*s == ':') + ++i; + else + break; + } + + for (i = 0; i < 3; ++i) + { + if (*v[i] < 0) + *v[i] = (criteria == MATCHCRITERIA_DATE_AFTER) ? 59 : 0; + } + if (tm.tm_hour > 23) + tm.tm_hour = 23; + if (tm.tm_min > 59) + tm.tm_min = 59; + if (tm.tm_sec > 59) + tm.tm_sec = 59; + } + + if (tm_out) + *tm_out = tm; + + return mktime(&tm); +} + /*! *\brief Check if a message matches the condition in a matcher * structure. diff --git a/src/matcher.h b/src/matcher.h @@ -23,6 +23,7 @@ #include <sys/types.h> #include <regex.h> #include <glib.h> +#include <time.h> #include "proctypes.h" #include "matchertypes.h" @@ -193,4 +194,6 @@ void prefs_matcher_write_config (void); gchar * matcher_quote_str(const gchar * src); +gint matcherprop_parse_date_time(gint criteria, const gchar *s, struct tm *tm_out); + #endif diff --git a/src/matcher_parser_parse.y b/src/matcher_parser_parse.y @@ -25,7 +25,6 @@ #include "utils.h" #include "filtering.h" -#include "procheader.h" #include "matcher.h" #include "matcher_parser.h" #include "matcher_parser_lex.h" @@ -999,7 +998,7 @@ MATCHER_ALL criteria = MATCHCRITERIA_DATE_AFTER; expr = $2; - value = procheader_date_parse(NULL, expr, 0); + value = matcherprop_parse_date_time(criteria, expr, NULL); prop = matcherprop_new(criteria, NULL, 0, expr, value); } | MATCHER_DATE_BEFORE MATCHER_STRING @@ -1010,7 +1009,7 @@ MATCHER_ALL criteria = MATCHCRITERIA_DATE_BEFORE; expr = $2; - value = procheader_date_parse(NULL, expr, 0); + value = matcherprop_parse_date_time(criteria, expr, NULL); prop = matcherprop_new(criteria, NULL, 0, expr, value); } | MATCHER_NEWSGROUPS match_type MATCHER_STRING diff --git a/src/messageview.c b/src/messageview.c @@ -1320,8 +1320,18 @@ static void messageview_find_part_depth_first(MimeInfoSearch *context, MimeMedia } } -gint messageview_show(MessageView *messageview, MsgInfo *msginfo, - gboolean all_headers) +static gint real_messageview_show(MessageView *messageview, MsgInfo *msginfo, + gboolean all_headers, gboolean decrypt); + +static void decrypt_message_clicked(NoticeView *noticeview, MessageView *messageview) +{ + noticeview_hide(noticeview); + real_messageview_show(messageview, messageview->msginfo, + messageview->all_headers, TRUE); +} + +static gint real_messageview_show(MessageView *messageview, MsgInfo *msginfo, + gboolean all_headers, gboolean decrypt) { gchar *text = NULL; gchar *file; @@ -1400,22 +1410,24 @@ gint messageview_show(MessageView *messageview, MsgInfo *msginfo, return -1; } - while ((encinfo = find_encrypted_part(mimeinfo)) != NULL) { - debug_print("decrypting message part\n"); - if (privacy_mimeinfo_decrypt(encinfo) < 0) { - text = g_strdup_printf(_("Couldn't decrypt: %s"), - privacy_get_error()); - noticeview_show(messageview->noticeview); - noticeview_set_icon(messageview->noticeview, - STOCK_PIXMAP_NOTICE_WARN); - noticeview_set_text(messageview->noticeview, text); - gtk_widget_hide(messageview->noticeview->button); - gtk_widget_hide(messageview->noticeview->button2); - g_free(text); - break; + if (decrypt) { + while ((encinfo = find_encrypted_part(mimeinfo)) != NULL) { + debug_print("decrypting message part\n"); + if (privacy_mimeinfo_decrypt(encinfo) < 0) { + text = g_strdup_printf(_("Couldn't decrypt: %s"), + privacy_get_error()); + noticeview_show(messageview->noticeview); + noticeview_set_icon(messageview->noticeview, + STOCK_PIXMAP_NOTICE_WARN); + noticeview_set_text(messageview->noticeview, text); + gtk_widget_hide(messageview->noticeview->button); + gtk_widget_hide(messageview->noticeview->button2); + g_free(text); + break; + } } } - + if (messageview->msginfo != msginfo) { procmsg_msginfo_free(&(messageview->msginfo)); messageview->msginfo = NULL; @@ -1432,6 +1444,17 @@ gint messageview_show(MessageView *messageview, MsgInfo *msginfo, if (prefs_common.display_header_pane) headerview_show(messageview->headerview, messageview->msginfo); + if (!decrypt && find_encrypted_part(mimeinfo) != NULL) { + noticeview_set_text(messageview->noticeview, _("This message is encrypted.")); + noticeview_set_icon(messageview->noticeview, + STOCK_PIXMAP_DOC_INFO); + noticeview_set_button_text(messageview->noticeview, _("Decrypt message")); + noticeview_set_button_press_callback(messageview->noticeview, + G_CALLBACK(decrypt_message_clicked), + (gpointer) messageview); + noticeview_show(messageview->noticeview); + } + messageview_register_nav(messageview); messageview_set_position(messageview, 0); @@ -1566,6 +1589,13 @@ done: return 0; } +gint messageview_show(MessageView *messageview, MsgInfo *msginfo, + gboolean all_headers) +{ + return real_messageview_show(messageview, msginfo, all_headers, + prefs_common.decrypt_messages); +} + void messageview_reflect_prefs_pixmap_theme(void) { GList *cur; diff --git a/src/mimeview.c b/src/mimeview.c @@ -1889,7 +1889,7 @@ static gboolean mimeview_write_part(const gchar *filename, gchar *dir; gint err; - dir= g_path_get_dirname(filename); + dir = g_path_get_dirname(filename); if (!is_dir_exist(dir)) make_dir_hier(dir); g_free(dir); @@ -1898,14 +1898,18 @@ static gboolean mimeview_write_part(const gchar *filename, AlertValue aval; gchar *res; gchar *tmp; - - if (!g_utf8_validate(filename, -1, NULL)) + gint sz; + + sz = get_file_size(filename); + if (!g_utf8_validate(filename, -1, NULL)) { tmp = conv_filename_to_utf8(filename); - else + if (sz == -1) + sz = get_file_size(filename); + } else tmp = g_strdup(filename); - - res = g_strdup_printf(_("Overwrite existing file '%s'?"), - tmp); + + res = g_strdup_printf(_("Overwrite existing file '%s' (%ld bytes)\nwith this one (%ld bytes)?"), + tmp, sz, partinfo->length); g_free(tmp); aval = alertpanel(_("Overwrite"), res, _("_Cancel"), _("_OK"), NULL, ALERTFOCUS_FIRST); @@ -1913,7 +1917,12 @@ static gboolean mimeview_write_part(const gchar *filename, if (G_ALERTALTERNATE != aval) return FALSE; } - if ((err = procmime_get_part(filename, partinfo)) < 0) { + if ((partinfo->type == MIMETYPE_TEXT) && partinfo->subtype + && (strcasecmp(partinfo->subtype, "plain") == 0)) + err = procmime_get_part_with_bom(filename, partinfo, TRUE); + else + err = procmime_get_part(filename, partinfo); + if (err < 0) { debug_print("error saving MIME part: %d\n", err); if (handle_error) alertpanel_error diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am @@ -36,6 +36,7 @@ SUBDIRS = \ spamassassin \ spam_report \ tnef_parse \ - vcalendar + vcalendar \ + vfolder .PHONY: test diff --git a/src/plugins/fancy/fancy_viewer.c b/src/plugins/fancy/fancy_viewer.c @@ -62,6 +62,7 @@ static void zoom_out_cb(GtkWidget *widget, GdkEvent *ev, FancyViewer *viewer); static gboolean fancy_prefs_cb(GtkWidget *widget, GdkEventButton *ev, FancyViewer *viewer); static void zoom_100_cb(GtkWidget *widget, GdkEvent *ev, FancyViewer *viewer); static void open_in_browser_cb(GtkWidget *widget, FancyViewer *viewer); +static void open_in_alt_browser_cb(GtkWidget *widget, FancyViewer *viewer); static void fancy_create_popup_prefs_menu(FancyViewer *viewer); static void fancy_show_notice(FancyViewer *viewer, const gchar *message); static size_t download_file_curl_write_cb(void *buffer, size_t size, @@ -743,10 +744,17 @@ static void search_the_web_cb(GtkWidget *widget, FancyViewer *viewer) static void open_in_browser_cb(GtkWidget *widget, FancyViewer *viewer) { debug_print("open outer: %s\n", viewer->cur_link); - if(viewer->cur_link) + if (viewer->cur_link) open_uri(viewer->cur_link, prefs_common_get_uri_cmd()); } +static void open_in_alt_browser_cb(GtkWidget *widget, FancyViewer *viewer) +{ + debug_print("open outer: %s\n", viewer->cur_link); + if (viewer->cur_link) + open_uri(viewer->cur_link, prefs_common_get_uri_alt_cmd()); +} + static size_t download_file_curl_write_cb(void *buffer, size_t size, size_t nmemb, void *data) { @@ -953,6 +961,17 @@ static gboolean context_menu_cb (WebKitWebView *view, WebKitContextMenu *menu, (GtkCallback)viewer_menu_handler, viewer); + if (prefs_common_get_uri_alt_cmd()) { + GtkWidget *openaltbrowser = gtk_image_menu_item_new_with_label(_("Open in Alternate Browser")); + GtkWidget *img = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(openaltbrowser), img); + gtk_widget_show(GTK_WIDGET(openaltbrowser)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), openaltbrowser); + g_signal_connect(G_OBJECT(openaltbrowser), "activate", + G_CALLBACK(open_in_alt_browser_cb), + (gpointer *) viewer); + } + if (plugin) { GtkWidget *rssyl = gtk_menu_item_new_with_label(_("Import feed")); gtk_widget_show(GTK_WIDGET(rssyl)); diff --git a/src/plugins/notification/notification_trayicon.c b/src/plugins/notification/notification_trayicon.c @@ -367,6 +367,10 @@ static void notification_trayicon_account_list_reset(const gchar *menuname, if (receive && ac_prefs->protocol == A_NONE) continue; + if (!ac_prefs->selectable_as_current_account) { + continue; + } + menuitem = gtk_menu_item_new_with_label (ac_prefs->account_name ? ac_prefs->account_name : _("Untitled")); diff --git a/src/plugins/pgpmime/pgpmime.c b/src/plugins/pgpmime/pgpmime.c @@ -270,24 +270,53 @@ static gboolean pgpmime_is_encrypted(MimeInfo *mimeinfo) const gchar *begin_indicator = "-----BEGIN PGP MESSAGE-----"; const gchar *end_indicator = "-----END PGP MESSAGE-----"; gchar *textdata; + guint firstChild = 0; if (mimeinfo->type != MIMETYPE_MULTIPART) return FALSE; - if (g_ascii_strcasecmp(mimeinfo->subtype, "encrypted")) - return FALSE; - tmpstr = procmime_mimeinfo_get_parameter(mimeinfo, "protocol"); - if ((tmpstr == NULL) || g_ascii_strcasecmp(tmpstr, "application/pgp-encrypted")) - return FALSE; - if (g_node_n_children(mimeinfo->node) != 2) - return FALSE; - - tmpinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, 0)->data; + + /* Workaround for mail + gpgmail + exchange + * MS Exchange modifies the message structure and + * inserts an empty plain text attachment in the beginning, + * so the resulting message structure looks like this: + * + * message/rfc822 (offset:0 length:4534 encoding: 6) + * multipart/mixed (offset:1542 length:2992 encoding: 6) + * text/plain (offset:1680 length:0 encoding: 3) + * application/pgp-encrypted (offset:2142 length:21 encoding: 4) + * application/octet-stream (offset:2586 length:1897 encoding: 4) + */ + if (g_node_n_children(mimeinfo->node) == 3) + { + if (g_ascii_strcasecmp(mimeinfo->subtype, "mixed")) + return FALSE; + tmpinfo = (MimeInfo*) g_node_nth_child(mimeinfo->node, 0)->data; + if (tmpinfo->length != 0) + return FALSE; + if (tmpinfo->type != MIMETYPE_TEXT) + return FALSE; + if (g_ascii_strcasecmp(tmpinfo->subtype, "plain")) + return FALSE; + + firstChild = 1; + } + else /* standard PGP MIME email (two children) */ + { + if (g_ascii_strcasecmp(mimeinfo->subtype, "encrypted")) + return FALSE; + tmpstr = procmime_mimeinfo_get_parameter(mimeinfo, "protocol"); + if ((tmpstr == NULL) || g_ascii_strcasecmp(tmpstr, "application/pgp-encrypted")) + return FALSE; + if (g_node_n_children(mimeinfo->node) != 2) + return FALSE; + } + + tmpinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, firstChild)->data; if (tmpinfo->type != MIMETYPE_APPLICATION) return FALSE; if (g_ascii_strcasecmp(tmpinfo->subtype, "pgp-encrypted")) return FALSE; - - tmpinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, 1)->data; + tmpinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, firstChild + 1)->data; if (tmpinfo->type != MIMETYPE_APPLICATION) return FALSE; if (g_ascii_strcasecmp(tmpinfo->subtype, "octet-stream")) @@ -333,7 +362,7 @@ static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo) cm_return_val_if_fail(pgpmime_is_encrypted(mimeinfo), NULL); - encinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, 1)->data; + encinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, g_node_n_children(mimeinfo->node) - 1)->data; cipher = sgpgme_data_from_mimeinfo(encinfo); plain = sgpgme_decrypt_verify(cipher, &sigstat, ctx); diff --git a/src/plugins/python/composewindowtype.c b/src/plugins/python/composewindowtype.c @@ -139,7 +139,7 @@ static int ComposeWindow_init(clawsmail_ComposeWindowObject *self, PyObject *arg list = account_get_list(); for (cur = list ; cur != NULL ; cur = g_list_next(cur)) { ac = (PrefsAccount *) cur->data; - if (ac->protocol != A_NNTP) { + if (ac->protocol != A_NNTP && !ac->selectable_as_current_account) { compose = compose_new_with_folderitem(ac, item, NULL); did_find_compose = TRUE; } diff --git a/src/plugins/rssyl/rssyl.c b/src/plugins/rssyl/rssyl.c @@ -444,9 +444,9 @@ static FolderItem *rssyl_item_new(Folder *folder) ritem->fetch_comments_max_age = -1; ritem->write_heading = TRUE; ritem->fetching_comments = FALSE; - ritem->silent_update = 0; + ritem->silent_update = 1; ritem->last_update = 0; - ritem->ignore_title_rename = FALSE; + ritem->ignore_title_rename = TRUE; ritem->ssl_verify_peer = TRUE; ritem->feedprop = NULL; ritem->refresh_id = 0; diff --git a/src/plugins/rssyl/rssyl_parse_feed.c b/src/plugins/rssyl/rssyl_parse_feed.c @@ -197,7 +197,7 @@ gboolean rssyl_parse_feed(RFolderItem *ritem, Feed *feed) /* FIXME: store feed properties */ } - folder_item_update_freeze(); +// folder_item_update_freeze(); /* Read contents of folder, so we can check for duplicates/updates */ rssyl_folder_read_existing(ritem); @@ -220,7 +220,7 @@ gboolean rssyl_parse_feed(RFolderItem *ritem, Feed *feed) } folder_item_scan(&ritem->item); - folder_item_update_thaw(); +// folder_item_update_thaw(); if( !ritem->fetching_comments ) log_print(LOG_PROTOCOL, RSSYL_LOG_UPDATED, ritem->url); diff --git a/src/plugins/rssyl/rssyl_prefs.c b/src/plugins/rssyl/rssyl_prefs.c @@ -55,6 +55,8 @@ static PrefParam param[] = { P_BOOL, NULL, NULL, NULL }, { "refresh_enabled", "TRUE", &rssyl_prefs.refresh_enabled, P_BOOL, NULL, NULL, NULL }, + { "refresh_all_skips", "FALSE", &rssyl_prefs.refresh_all_skips, + P_BOOL, NULL, NULL, NULL }, { "cookies_path", "", &rssyl_prefs.cookies_path, P_STRING, NULL, NULL, NULL }, { "ssl_verify_peer", "TRUE", &rssyl_prefs.ssl_verify_peer, @@ -129,6 +131,7 @@ static void create_rssyl_prefs_page(PrefsPage *page, GtkWidget *refresh, *refresh_enabled, *refresh_hbox; GtkWidget *label; GtkWidget *refresh_on_startup; + GtkWidget *refresh_all_skips; GtkAdjustment *refresh_adj; GtkWidget *cookies_path, *cookies_btn, *cookies_hbox; GtkWidget *ssl_verify_peer; @@ -163,6 +166,13 @@ static void create_rssyl_prefs_page(PrefsPage *page, rssyl_prefs.refresh_on_startup); gtk_box_pack_start(GTK_BOX(vbox1), refresh_on_startup, FALSE, FALSE, 0); + /* Whether refresh-all will skip feeds that are not programmed to be periodically refreshed or not */ + refresh_all_skips = gtk_check_button_new_with_label( + _("Refresh all skips feeds programmed to be refreshed manually")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(refresh_all_skips), + rssyl_prefs.refresh_all_skips); + gtk_box_pack_start(GTK_BOX(vbox1), refresh_all_skips, FALSE, FALSE, 0); + vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); /* Whether to verify SSL peer certificate */ @@ -207,6 +217,7 @@ static void create_rssyl_prefs_page(PrefsPage *page, prefs_page->refresh_enabled = refresh_enabled; prefs_page->refresh = refresh; prefs_page->refresh_on_startup = refresh_on_startup; + prefs_page->refresh_all_skips = refresh_all_skips; prefs_page->cookies_path = cookies_path; prefs_page->ssl_verify_peer = ssl_verify_peer; } @@ -230,6 +241,8 @@ static void save_rssyl_prefs(PrefsPage *page) GTK_SPIN_BUTTON(prefs_page->refresh)); rssyl_prefs.refresh_on_startup = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(prefs_page->refresh_on_startup)); + rssyl_prefs.refresh_all_skips = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(prefs_page->refresh_all_skips)); g_free(rssyl_prefs.cookies_path); rssyl_prefs.cookies_path = g_strdup(gtk_entry_get_text( GTK_ENTRY(prefs_page->cookies_path))); diff --git a/src/plugins/rssyl/rssyl_prefs.h b/src/plugins/rssyl/rssyl_prefs.h @@ -33,6 +33,7 @@ struct _RPrefs { gboolean refresh_enabled; gint refresh; gboolean refresh_on_startup; + gboolean refresh_all_skips; gchar *cookies_path; gboolean ssl_verify_peer; }; @@ -44,6 +45,7 @@ struct _RPrefsPage { GtkWidget *refresh_enabled; GtkWidget *refresh; GtkWidget *refresh_on_startup; + GtkWidget *refresh_all_skips; GtkWidget *cookies_path; GtkWidget *ssl_verify_peer; }; diff --git a/src/plugins/rssyl/rssyl_subscribe.c b/src/plugins/rssyl/rssyl_subscribe.c @@ -147,7 +147,7 @@ FolderItem *rssyl_subscribe(FolderItem *parent, const gchar *url, } /* TODO: handle cases where i reaches 20 */ - folder_item_update_freeze(); +// folder_item_update_freeze(); new_item = folder_create_folder(parent, tmpname2); g_free(tmpname); @@ -182,7 +182,7 @@ FolderItem *rssyl_subscribe(FolderItem *parent, const gchar *url, if (edit_properties) rssyl_gtk_prop(ritem); - folder_item_update_thaw(); +// folder_item_update_thaw(); return new_item; } diff --git a/src/plugins/rssyl/rssyl_update_feed.c b/src/plugins/rssyl/rssyl_update_feed.c @@ -317,8 +317,13 @@ static gboolean rssyl_update_recursively_func(GNode *node, gpointer data) ritem = (RFolderItem *)item; if( ritem->url != NULL ) { - debug_print("RSSyl: Updating feed '%s'\n", item->name); - rssyl_update_feed(ritem, 0); + if(rssyl_prefs_get()->refresh_all_skips && + (ritem->default_refresh_interval == FALSE) && (ritem->refresh_interval == 0)) { + debug_print("RSSyl: Skipping feed '%s'\n", item->name); + } else { + debug_print("RSSyl: Updating feed '%s'\n", item->name); + rssyl_update_feed(ritem, 0); + } } else debug_print("RSSyl: Updating in folder '%s'\n", item->name); diff --git a/src/plugins/vcalendar/vcal_meeting_gtk.c b/src/plugins/vcalendar/vcal_meeting_gtk.c @@ -1561,6 +1561,9 @@ static VCalMeeting *vcal_meeting_create_real(VCalEvent *event, gboolean visible) for (i = 0; accounts != NULL; accounts = accounts->next) { PrefsAccount *ac = (PrefsAccount *)accounts->data; + if (!ac->selectable_as_current_account) { + continue; + } if (ac->protocol == A_NNTP) { continue; } diff --git a/src/prefs_account.c b/src/prefs_account.c @@ -103,6 +103,7 @@ typedef struct BasicPage GtkWidget *acname_entry; GtkWidget *default_checkbtn; + GtkWidget *selectableascurrentaccount_checkbtn; GtkWidget *name_entry; GtkWidget *addr_entry; @@ -463,6 +464,10 @@ static PrefParam basic_param[] = { &basic_page.default_checkbtn, prefs_set_data_from_toggle, prefs_set_toggle}, + {"selectable_as_current_account", "TRUE", &tmp_ac_prefs.selectable_as_current_account, P_BOOL, + &basic_page.selectableascurrentaccount_checkbtn, + prefs_set_data_from_toggle, prefs_set_toggle}, + {"name", NULL, &tmp_ac_prefs.name, P_STRING, &basic_page.name_entry, prefs_set_data_from_entry, prefs_set_entry}, @@ -1154,6 +1159,7 @@ static void basic_create_widget_func(PrefsPage * _page, GtkWidget *name_entry; GtkWidget *addr_entry; GtkWidget *org_entry; + GtkWidget *selectableascurrentaccount_checkbtn; GtkWidget *serv_frame; GtkWidget *vbox2; @@ -1216,6 +1222,10 @@ static void basic_create_widget_func(PrefsPage * _page, gtk_box_pack_start (GTK_BOX (vbox1), default_checkbtn, FALSE, FALSE, 0); #endif + PACK_CHECK_BUTTON + (vbox1, selectableascurrentaccount_checkbtn, + _("Selectable as current account")); + PACK_FRAME (vbox1, frame1, _("Personal information")); table1 = gtk_grid_new(); @@ -1457,6 +1467,7 @@ static void basic_create_widget_func(PrefsPage * _page, page->acname_entry = acname_entry; page->default_checkbtn = default_checkbtn; + page->selectableascurrentaccount_checkbtn = selectableascurrentaccount_checkbtn; page->name_entry = name_entry; page->addr_entry = addr_entry; @@ -4467,6 +4478,7 @@ static void create_privacy_prefs(gpointer key, gpointer _value, gpointer user_da void prefs_account_write_config_all(GList *account_list) { + GSList *ac_label_list = NULL; GList *cur; gchar *rcpath; PrefFile *pfile; @@ -4482,6 +4494,12 @@ void prefs_account_write_config_all(GList *account_list) GString *str; tmp_ac_prefs = *(PrefsAccount *)cur->data; + gchar *buf = g_strdup_printf("%d", tmp_ac_prefs.account_id); + if (ac_label_list && g_slist_find_custom(ac_label_list, buf, (GCompareFunc)strcmp)) { + g_warning("duplicate account found (%s)", buf); + } + ac_label_list = g_slist_append(ac_label_list, + g_strdup(buf)); if (fprintf(pfile->fp, "[Account: %d]\n", tmp_ac_prefs.account_id) <= 0) return; @@ -4514,6 +4532,9 @@ void prefs_account_write_config_all(GList *account_list) } } + g_slist_foreach(ac_label_list, (GFunc)g_free, NULL); + g_slist_free(ac_label_list); + if (prefs_file_close(pfile) < 0) g_warning("failed to write configuration to file"); diff --git a/src/prefs_account.h b/src/prefs_account.h @@ -60,6 +60,7 @@ struct _Folder; struct _PrefsAccount { gchar *account_name; + gboolean selectable_as_current_account; /* Personal info */ gchar *name; diff --git a/src/prefs_common.c b/src/prefs_common.c @@ -107,6 +107,7 @@ static PrefParam param_os_specific[] = { &prefs_common.normalfont, P_STRING, NULL, NULL, NULL}, {"bold_font_gtk2", "Sans 9 Bold", &prefs_common.boldfont, P_STRING, NULL, NULL, NULL}, + /* Message */ {"attach_save_directory", NULL, &prefs_common.attach_save_dir, P_STRING, NULL, NULL, NULL}, @@ -712,6 +713,7 @@ static PrefParam param[] = { NULL, NULL, NULL}, {"compose_y", "0", &prefs_common.compose_y, P_INT, NULL, NULL, NULL}, + /* Message */ {"enable_color", "TRUE", &prefs_common.enable_color, P_BOOL, NULL, NULL, NULL}, @@ -802,6 +804,9 @@ static PrefParam param[] = { {"attach_load_directory", NULL, &SPECIFIC_PREFS.attach_load_dir, P_STRING, NULL, NULL, NULL}, + {"decrypt_messages", "TRUE", &prefs_common.decrypt_messages, P_BOOL, + NULL, NULL, NULL}, + /* MIME viewer */ {"mime_textviewer", NULL, &SPECIFIC_PREFS.mime_textviewer, P_STRING, NULL, NULL, NULL}, @@ -810,6 +815,10 @@ static PrefParam param[] = { {"show_inline_attachments", "TRUE", &prefs_common.show_inline_attachments, P_BOOL, NULL, NULL, NULL}, + {"save_attachment_handle_bom", "0", + &prefs_common.save_attachment_handle_bom, P_INT, + NULL, NULL, NULL}, + /* Interface */ #ifndef GENERIC_UMPC {"layout_mode", "0", &prefs_common.layout_mode, P_INT, @@ -865,6 +874,9 @@ static PrefParam param[] = { {"summary_select_prio10", "0", &prefs_common.summary_select_prio[9], P_ENUM, NULL, NULL, NULL}, + {"mark_as_read_on_never", "FALSE", + &prefs_common.mark_as_read_on_never, + P_BOOL, NULL, NULL, NULL}, {"mark_as_read_on_new_window", "FALSE", &prefs_common.mark_as_read_on_new_window, P_BOOL, NULL, NULL, NULL}, @@ -920,6 +932,8 @@ static PrefParam param[] = { #ifndef G_OS_WIN32 {"uri_open_command", DEFAULT_BROWSER_CMD, &SPECIFIC_PREFS.uri_cmd, P_STRING, NULL, NULL, NULL}, + {"uri_open_command_alt", DEFAULT_BROWSER_CMD, + &SPECIFIC_PREFS.uri_alt_cmd, P_STRING, NULL, NULL, NULL}, #else {"gtk_theme", DEFAULT_W32_GTK_THEME, &SPECIFIC_PREFS.gtk_theme, P_STRING, NULL, NULL, NULL}, @@ -1716,6 +1730,15 @@ const gchar *prefs_common_get_uri_cmd(void) #endif } +const gchar *prefs_common_get_uri_alt_cmd(void) +{ +#ifdef G_OS_WIN32 + return NULL; +#else + return prefs_common.uri_alt_cmd; +#endif +} + const gchar *prefs_common_get_ext_editor_cmd(void) { return prefs_common.ext_editor_cmd; diff --git a/src/prefs_common.h b/src/prefs_common.h @@ -144,6 +144,13 @@ typedef enum COL_LAST_COLOR_INDEX } ColorIndex; +typedef enum +{ + HANDLE_BOM_WRITE = 0, + HANDLE_BOM_DO_NOT_WRITE, + HANDLE_BOM_ASK_USER +} HandleBom; + struct _PrefsCommon { gint config_version; @@ -372,6 +379,8 @@ struct _PrefsCommon gboolean attach_desc; + gboolean decrypt_messages; + /* MIME viewer */ gchar *mime_textviewer; gchar *mime_open_cmd; @@ -381,6 +390,8 @@ struct _PrefsCommon GList *mime_open_cmd_history; gboolean show_inline_attachments; + HandleBom save_attachment_handle_bom; + /* Addressbook */ gboolean addressbook_use_editaddress_dialog; gint addressbook_hpaned_pos; @@ -400,6 +411,7 @@ struct _PrefsCommon gboolean open_selected_on_directional; gboolean always_show_msg; + gboolean mark_as_read_on_never; gboolean mark_as_read_on_new_window; gboolean mark_as_read_delay; gboolean immediate_exec; @@ -428,6 +440,7 @@ struct _PrefsCommon /* Other */ #ifndef G_OS_WIN32 gchar *uri_cmd; + gchar *uri_alt_cmd; #else gchar *gtk_theme; #endif @@ -606,6 +619,7 @@ gchar *pref_get_pref_from_textview(GtkTextView *textview); gchar *pref_get_pref_from_entry(GtkEntry *entry); const gchar *prefs_common_translated_header_name(const gchar *header_name); const gchar *prefs_common_get_uri_cmd(void); +const gchar *prefs_common_get_uri_alt_cmd(void); const gchar *prefs_common_get_ext_editor_cmd(void); #define OPEN_SELECTED(when) (prefs_common.always_show_msg || prefs_common.when) diff --git a/src/prefs_ext_prog.c b/src/prefs_ext_prog.c @@ -52,6 +52,9 @@ typedef struct _ExtProgPage GtkWidget *uri_label; GtkWidget *uri_combo; GtkWidget *uri_entry; + GtkWidget *uri_alt_label; + GtkWidget *uri_alt_combo; + GtkWidget *uri_alt_entry; #endif /* !G_OS_WIN32 */ GtkWidget *exteditor_label; @@ -76,6 +79,9 @@ static void prefs_ext_prog_create_widget(PrefsPage *_page, GtkWindow *window, GtkWidget *uri_label; GtkWidget *uri_combo; GtkWidget *uri_entry; + GtkWidget *uri_alt_label; + GtkWidget *uri_alt_combo; + GtkWidget *uri_alt_entry; #endif /* !G_OS_WIN32 */ GtkWidget *exteditor_label; GtkWidget *exteditor_combo; @@ -167,6 +173,31 @@ static void prefs_ext_prog_create_widget(PrefsPage *_page, GtkWindow *window, uri_entry = gtk_bin_get_child(GTK_BIN((uri_combo))); gtk_entry_set_text(GTK_ENTRY(uri_entry), prefs_common.uri_cmd ? prefs_common.uri_cmd : ""); + + uri_alt_label = gtk_label_new (_("Alternate web browser")); + gtk_widget_show(uri_alt_label); + i++; + gtk_label_set_justify(GTK_LABEL (uri_alt_label), GTK_JUSTIFY_RIGHT); + gtk_label_set_xalign(GTK_LABEL (uri_alt_label), 1.0); + gtk_grid_attach(GTK_GRID(table2), uri_alt_label, 0, i, 1, 1); + + uri_alt_combo = combobox_text_new(TRUE, + DEFAULT_BROWSER_CMD, + "galeon --new-tab '%s'", + "galeon '%s'", + "mozilla -remote 'openurl(%s,new-window)'", + "netscape -remote 'openURL(%s, new-window)'", + "netscape '%s'", + "gnome-moz-remote --newwin '%s'", + "kfmclient openURL '%s'", + "opera -newwindow '%s'", + "rxvt -e w3m '%s'", + "rxvt -e lynx '%s'", + NULL); + gtk_grid_attach(GTK_GRID(table2), uri_alt_combo, 1, i, 1, 1); + + uri_alt_entry = gtk_bin_get_child(GTK_BIN((uri_alt_combo))); + gtk_entry_set_text(GTK_ENTRY(uri_alt_entry), prefs_common.uri_alt_cmd ? prefs_common.uri_alt_cmd : ""); #endif /* !G_OS_WIN32 */ exteditor_label = gtk_label_new (_("Text editor")); @@ -234,6 +265,7 @@ static void prefs_ext_prog_create_widget(PrefsPage *_page, GtkWindow *window, prefs_ext_prog->window = GTK_WIDGET(window); #ifndef G_OS_WIN32 prefs_ext_prog->uri_entry = uri_entry; + prefs_ext_prog->uri_alt_entry = uri_alt_entry; #endif prefs_ext_prog->exteditor_entry = exteditor_entry; prefs_ext_prog->astextviewer_entry = astextviewer_entry; @@ -248,6 +280,8 @@ static void prefs_ext_prog_save(PrefsPage *_page) #ifndef G_OS_WIN32 prefs_common.uri_cmd = gtk_editable_get_chars (GTK_EDITABLE(ext_prog->uri_entry), 0, -1); + prefs_common.uri_alt_cmd = gtk_editable_get_chars + (GTK_EDITABLE(ext_prog->uri_alt_entry), 0, -1); #endif /* !G_OS_WIN32 */ prefs_common.ext_editor_cmd = gtk_editable_get_chars (GTK_EDITABLE(ext_prog->exteditor_entry), 0, -1); diff --git a/src/prefs_folder_item.c b/src/prefs_folder_item.c @@ -61,6 +61,7 @@ typedef struct _FolderItemGeneralPage FolderItemGeneralPage; typedef struct _FolderItemComposePage FolderItemComposePage; +typedef struct _FolderItemSendPage FolderItemSendPage; typedef struct _FolderItemTemplatesPage FolderItemTemplatesPage; static gboolean can_save = TRUE; @@ -114,8 +115,6 @@ struct _FolderItemComposePage GtkWidget *window; GtkWidget *table; GtkWidget *no_save_warning; - GtkWidget *checkbtn_request_return_receipt; - GtkWidget *checkbtn_save_copy_to_folder; GtkWidget *checkbtn_default_to; GtkWidget *entry_default_to; GtkWidget *checkbtn_default_reply_to; @@ -138,8 +137,6 @@ struct _FolderItemComposePage GtkWidget *always_encrypt; /* apply to sub folders */ - GtkWidget *request_return_receipt_rec_checkbtn; - GtkWidget *save_copy_to_folder_rec_checkbtn; GtkWidget *default_to_rec_checkbtn; GtkWidget *default_reply_to_rec_checkbtn; GtkWidget *default_cc_rec_checkbtn; @@ -154,6 +151,26 @@ struct _FolderItemComposePage GtkWidget *always_encrypt_rec_checkbtn; }; +struct _FolderItemSendPage +{ + PrefsPage page; + + FolderItem *item; + + GtkWidget *window; + GtkWidget *table; + GtkWidget *no_save_warning; + + GtkWidget *checkbtn_request_return_receipt; + GtkWidget *checkbtn_request_dsn; + GtkWidget *checkbtn_save_copy_to_folder; + + /* apply to sub folders */ + GtkWidget *request_return_receipt_rec_checkbtn; + GtkWidget *request_dsn_rec_checkbtn; + GtkWidget *save_copy_to_folder_rec_checkbtn; +}; + struct _FolderItemTemplatesPage { PrefsPage page; @@ -184,10 +201,12 @@ struct _FolderItemTemplatesPage static void general_save_folder_prefs(FolderItem *folder, FolderItemGeneralPage *page); static void compose_save_folder_prefs(FolderItem *folder, FolderItemComposePage *page); +static void send_save_folder_prefs(FolderItem *folder, FolderItemSendPage *page); static void templates_save_folder_prefs(FolderItem *folder, FolderItemTemplatesPage *page); static gboolean general_save_recurse_func(GNode *node, gpointer data); static gboolean compose_save_recurse_func(GNode *node, gpointer data); +static gboolean send_save_recurse_func(GNode *node, gpointer data); static gboolean templates_save_recurse_func(GNode *node, gpointer data); static gint prefs_folder_item_chmod_mode (gchar *folder_chmod); @@ -221,6 +240,7 @@ static GtkWidget *prefs_folder_no_save_warning_create_widget() { return hbox; } +/* "General" page functions */ static void prefs_folder_item_general_create_widget_func(PrefsPage * page_, GtkWindow * window, gpointer data) @@ -846,6 +866,7 @@ static RecvProtocol item_protocol(FolderItem *item) return item->folder->account->protocol; } +/* "Compose" page functions */ static void prefs_folder_item_compose_create_widget_func(PrefsPage * page_, GtkWindow * window, gpointer data) @@ -861,8 +882,6 @@ static void prefs_folder_item_compose_create_widget_func(PrefsPage * page_, GtkWidget *label; GtkWidget *no_save_warning = NULL; - GtkWidget *checkbtn_request_return_receipt = NULL; - GtkWidget *checkbtn_save_copy_to_folder = NULL; GtkWidget *checkbtn_default_to = NULL; GtkWidget *entry_default_to = NULL; GtkWidget *checkbtn_default_reply_to = NULL; @@ -890,8 +909,6 @@ static void prefs_folder_item_compose_create_widget_func(PrefsPage * page_, GtkListStore *always_sign_menu; GtkWidget *always_encrypt; GtkListStore *always_encrypt_menu; - GtkWidget *request_return_receipt_rec_checkbtn = NULL; - GtkWidget *save_copy_to_folder_rec_checkbtn = NULL; GtkWidget *default_to_rec_checkbtn = NULL; GtkWidget *default_reply_to_rec_checkbtn = NULL; GtkWidget *default_cc_rec_checkbtn = NULL; @@ -932,28 +949,6 @@ static void prefs_folder_item_compose_create_widget_func(PrefsPage * page_, rowcount++; if (item_protocol(item) != A_NNTP) { - /* Request Return Receipt */ - checkbtn_request_return_receipt = gtk_check_button_new_with_label - (_("Request Return Receipt")); - gtk_grid_attach(GTK_GRID(table), checkbtn_request_return_receipt, 0, rowcount, 1, 1); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_request_return_receipt), - item->ret_rcpt ? TRUE : FALSE); - - request_return_receipt_rec_checkbtn = gtk_check_button_new(); - gtk_grid_attach(GTK_GRID(table), request_return_receipt_rec_checkbtn, 2, rowcount, 1, 1); - rowcount++; - - /* Save Copy to Folder */ - checkbtn_save_copy_to_folder = gtk_check_button_new_with_label - (_("Save copy of outgoing messages to this folder instead of Sent")); - gtk_grid_attach(GTK_GRID(table), checkbtn_save_copy_to_folder, 0, rowcount, 2, 1); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_save_copy_to_folder), - item->prefs->save_copy_to_folder ? TRUE : FALSE); - - save_copy_to_folder_rec_checkbtn = gtk_check_button_new(); - gtk_grid_attach(GTK_GRID(table), save_copy_to_folder_rec_checkbtn, 2, rowcount, 1, 1); - rowcount++; - /* Default To */ tr = g_strdup(C_("folder properties: %s stands for a header name", "Default %s")); @@ -1093,6 +1088,9 @@ static void prefs_folder_item_compose_create_widget_func(PrefsPage * page_, account_list = account_get_list(); for (cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) { ac_prefs = (PrefsAccount *)cur_ac->data; + if (!ac_prefs->selectable_as_current_account) { + continue; + } if (item->folder->account && ( (item_protocol(item) == A_NNTP && ac_prefs->protocol != A_NNTP) ||(item_protocol(item) != A_NNTP && ac_prefs->protocol == A_NNTP))) @@ -1252,13 +1250,12 @@ static void prefs_folder_item_compose_create_widget_func(PrefsPage * page_, rowcount++; + /* Finished ! */ gtk_widget_show_all(table); page->window = GTK_WIDGET(window); page->table = table; page->no_save_warning = no_save_warning; - page->checkbtn_request_return_receipt = checkbtn_request_return_receipt; - page->checkbtn_save_copy_to_folder = checkbtn_save_copy_to_folder; page->checkbtn_default_to = checkbtn_default_to; page->entry_default_to = entry_default_to; page->checkbtn_default_reply_to = checkbtn_default_reply_to; @@ -1280,8 +1277,6 @@ static void prefs_folder_item_compose_create_widget_func(PrefsPage * page_, page->always_sign = always_sign; page->always_encrypt = always_encrypt; - page->request_return_receipt_rec_checkbtn = request_return_receipt_rec_checkbtn; - page->save_copy_to_folder_rec_checkbtn = save_copy_to_folder_rec_checkbtn; page->default_to_rec_checkbtn = default_to_rec_checkbtn; page->default_reply_to_rec_checkbtn = default_reply_to_rec_checkbtn; page->default_cc_rec_checkbtn = default_cc_rec_checkbtn; @@ -1334,18 +1329,6 @@ static void compose_save_folder_prefs(FolderItem *folder, FolderItemComposePage cm_return_if_fail(prefs != NULL); if (item_protocol(folder) != A_NNTP) { - if (all || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->request_return_receipt_rec_checkbtn))) { - prefs->request_return_receipt = - gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->checkbtn_request_return_receipt)); - /* MIGRATION */ - folder->ret_rcpt = prefs->request_return_receipt; - } - - if (all || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->save_copy_to_folder_rec_checkbtn))) { - prefs->save_copy_to_folder = - gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->checkbtn_save_copy_to_folder)); - } - if (all || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->default_to_rec_checkbtn))) { prefs->enable_default_to = @@ -1386,8 +1369,6 @@ static void compose_save_folder_prefs(FolderItem *folder, FolderItemComposePage } } else { - prefs->request_return_receipt = FALSE; - prefs->save_copy_to_folder = FALSE; prefs->enable_default_to = FALSE; prefs->enable_default_reply_to = FALSE; prefs->enable_default_cc = FALSE; @@ -1442,13 +1423,12 @@ static gboolean compose_save_recurse_func(GNode *node, gpointer data) compose_save_folder_prefs(item, page); + /* optimise by not continuing if none of the 'apply to sub folders' check boxes are selected - and optimise the checking by only doing it once */ if ((node == page->item->node) && item_protocol(item) != A_NNTP && - !(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->request_return_receipt_rec_checkbtn)) || - gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->save_copy_to_folder_rec_checkbtn)) || - gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->default_to_rec_checkbtn)) || + !(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->default_to_rec_checkbtn)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->default_account_rec_checkbtn)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->default_cc_rec_checkbtn)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->default_bcc_rec_checkbtn)) || @@ -1485,6 +1465,195 @@ static void prefs_folder_item_compose_save_func(PrefsPage *page_) } +/* "Send" page functions */ +static void prefs_folder_item_send_create_widget_func(PrefsPage * page_, + GtkWindow * window, + gpointer data) +{ + FolderItemSendPage *page = (FolderItemSendPage *) page_; + FolderItem *item = (FolderItem *) data; + guint rowcount; + + GtkWidget *table; + GtkWidget *label; + + GtkWidget *no_save_warning = NULL; + GtkWidget *checkbtn_request_return_receipt = NULL; + GtkWidget *checkbtn_request_dsn = NULL; + GtkWidget *checkbtn_save_copy_to_folder = NULL; + + GtkWidget *request_return_receipt_rec_checkbtn = NULL; + GtkWidget *request_dsn_rec_checkbtn = NULL; + GtkWidget *save_copy_to_folder_rec_checkbtn = NULL; + + page->item = item; + + /* Table */ + table = gtk_table_new(3, 3, FALSE); + gtk_container_set_border_width (GTK_CONTAINER (table), VBOX_BORDER); + gtk_table_set_row_spacings(GTK_TABLE(table), 4); + gtk_table_set_col_spacings(GTK_TABLE(table), 4); + rowcount = 0; + + if (!can_save) { + no_save_warning = prefs_folder_no_save_warning_create_widget(); + gtk_table_attach(GTK_TABLE(table), no_save_warning, 0, 3, + rowcount, rowcount + 1, GTK_FILL, 0, 0, 0); + rowcount++; + } + + /* Apply to subfolders */ + label = gtk_label_new(_("Apply to\nsubfolders")); + gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, + rowcount, rowcount + 1, GTK_SHRINK, GTK_SHRINK, 0, 0); + rowcount++; + /* NOTE: the test for NNTP is not strictly needed, because register_send_page() + * will test it also and will not create the page in case of A_NNTP. + * But it might help if more widgets are created in the future */ + if (item_protocol(item) != A_NNTP) { + /* Request DSN */ + checkbtn_request_dsn = gtk_check_button_new_with_label + (_("Always Request Notification of successful Delivery")); + gtk_table_attach(GTK_TABLE(table), checkbtn_request_dsn, + 0, 2, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, + GTK_FILL, 0, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_request_dsn), + item->prefs->request_dsn ? TRUE : FALSE); + + request_dsn_rec_checkbtn = gtk_check_button_new(); + gtk_table_attach(GTK_TABLE(table), request_dsn_rec_checkbtn, 2, 3, + rowcount, rowcount + 1, GTK_SHRINK, GTK_SHRINK, 0, 0); + + rowcount++; + + /* Request Return Receipt */ + checkbtn_request_return_receipt = gtk_check_button_new_with_label + (_("Always Request Return Receipt")); + gtk_table_attach(GTK_TABLE(table), checkbtn_request_return_receipt, + 0, 2, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, + GTK_FILL, 0, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_request_return_receipt), + item->ret_rcpt ? TRUE : FALSE); + + request_return_receipt_rec_checkbtn = gtk_check_button_new(); + gtk_table_attach(GTK_TABLE(table), request_return_receipt_rec_checkbtn, 2, 3, + rowcount, rowcount + 1, GTK_SHRINK, GTK_SHRINK, 0, 0); + + rowcount++; + + /* Save Copy to Folder */ + checkbtn_save_copy_to_folder = gtk_check_button_new_with_label + (_("Save copy of outgoing messages to this folder instead of Sent")); + gtk_table_attach(GTK_TABLE(table), checkbtn_save_copy_to_folder, 0, 2, + rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_save_copy_to_folder), + item->prefs->save_copy_to_folder ? TRUE : FALSE); + + save_copy_to_folder_rec_checkbtn = gtk_check_button_new(); + gtk_table_attach(GTK_TABLE(table), save_copy_to_folder_rec_checkbtn, 2, 3, + rowcount, rowcount + 1, GTK_SHRINK, GTK_SHRINK, 0, 0); + + rowcount++; + } + /* Finished ! */ + gtk_widget_show_all(table); + + page->window = GTK_WIDGET(window); + page->table = table; + page->no_save_warning = no_save_warning; + + page->request_return_receipt_rec_checkbtn = request_return_receipt_rec_checkbtn; + page->request_dsn_rec_checkbtn = request_dsn_rec_checkbtn; + page->checkbtn_save_copy_to_folder = checkbtn_save_copy_to_folder; + + page->checkbtn_request_return_receipt = checkbtn_request_return_receipt; + page->checkbtn_request_dsn = checkbtn_request_dsn; + page->save_copy_to_folder_rec_checkbtn = save_copy_to_folder_rec_checkbtn; + + page->page.widget = table; +} + +static void prefs_folder_item_send_destroy_widget_func(PrefsPage *page_) +{ +/* FolderItemSendPage *page = (FolderItemSendPage *) page_; */ +} + +static void send_save_folder_prefs(FolderItem *folder, FolderItemSendPage *page) +{ + FolderItemPrefs *prefs = folder->prefs; + + gboolean all = FALSE; + + if (folder->path == NULL) + return; + + if (page->item == folder) + all = TRUE; + + cm_return_if_fail(prefs != NULL); + + if (item_protocol(folder) != A_NNTP) { + if (all || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->request_dsn_rec_checkbtn))) { + prefs->request_dsn = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->checkbtn_request_dsn)); + } + + if (all || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->request_return_receipt_rec_checkbtn))) { + prefs->request_return_receipt = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->checkbtn_request_return_receipt)); + /* MIGRATION */ + folder->ret_rcpt = prefs->request_return_receipt; + } + + if (all || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->save_copy_to_folder_rec_checkbtn))) { + prefs->save_copy_to_folder = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->checkbtn_save_copy_to_folder)); + } + + } + else + { + prefs->request_dsn = FALSE; + prefs->request_return_receipt = FALSE; + prefs->save_copy_to_folder = FALSE; + } +} + +static gboolean send_save_recurse_func(GNode *node, gpointer data) +{ + FolderItem *item = (FolderItem *) node->data; + FolderItemSendPage *page = (FolderItemSendPage *) data; + + cm_return_val_if_fail(item != NULL, TRUE); + cm_return_val_if_fail(page != NULL, TRUE); + + send_save_folder_prefs(item, page); + + /* optimise by not continuing if none of the 'apply to sub folders' + check boxes are selected - and optimise the checking by only doing + it once */ + if ((node == page->item->node) && item_protocol(item) != A_NNTP && + !(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->request_dsn_rec_checkbtn)) || + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->request_return_receipt_rec_checkbtn)) || + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->save_copy_to_folder_rec_checkbtn)) + )) { + return TRUE; + } + return FALSE; +} + +static void prefs_folder_item_send_save_func(PrefsPage *page_) +{ + FolderItemSendPage *page = (FolderItemSendPage *) page_; + + g_node_traverse(page->item->node, G_PRE_ORDER, G_TRAVERSE_ALL, + -1, send_save_recurse_func, page); + +} + + +/* "Templates" page functions */ static void prefs_folder_item_templates_create_widget_func(PrefsPage * page_, GtkWindow * window, gpointer data) @@ -1755,6 +1924,7 @@ static void prefs_folder_item_templates_save_func(PrefsPage *page_) } +/* other functions */ static gint prefs_folder_item_chmod_mode(gchar *folder_chmod) { gint newmode = 0; @@ -1921,6 +2091,22 @@ static void register_compose_page(void) prefs_folder_item_register_page((PrefsPage *) &folder_item_compose_page, NULL); } +static void register_send_page(void) +{ + static gchar *pfi_send_path[2]; + static FolderItemSendPage folder_item_send_page; + + pfi_send_path[0] = _("Send"); + pfi_send_path[1] = NULL; + + folder_item_send_page.page.path = pfi_send_path; + folder_item_send_page.page.create_widget = prefs_folder_item_send_create_widget_func; + folder_item_send_page.page.destroy_widget = prefs_folder_item_send_destroy_widget_func; + folder_item_send_page.page.save_page = prefs_folder_item_send_save_func; + + prefs_folder_item_register_page((PrefsPage *) &folder_item_send_page, NULL); +} + static void register_templates_page(void) { static gchar *pfi_templates_path[2]; @@ -1956,6 +2142,13 @@ void prefs_folder_item_open(FolderItem *item) if (prefs_pages == NULL) { register_general_page(); + /* create a "Send" page only for non-NNTP folders. + * The page would currently be empty anyway. + * Don't forget to remove the test if more widgets + * are added in the future that apply to NTTP as well. */ + if (item_protocol(item) != A_NNTP) { + register_send_page(); + } register_compose_page(); register_templates_page(); } diff --git a/src/prefs_matcher.c b/src/prefs_matcher.c @@ -36,7 +36,6 @@ #include "prefs_gtk.h" #include "prefs_matcher.h" #include "prefs_common.h" -#include "procheader.h" #include "mainwindow.h" #include "foldersel.h" #include "manage_window.h" @@ -97,9 +96,10 @@ static struct Matcher { GtkWidget *case_checkbtn; GtkWidget *regexp_checkbtn; GtkWidget *color_optmenu; - GtkWidget *calendar; - GtkWidget *time_label; - GtkWidget *time_entry; + GtkWidget *date_btn; + GtkWidget *hour_entry; + GtkWidget *min_entry; + GtkWidget *sec_entry; GtkWidget *test_btn; GtkWidget *addressbook_select_btn; @@ -484,6 +484,106 @@ static void prefs_matcher_size_allocate_cb(GtkWidget *widget, &prefs_common.matcherwin_width, &prefs_common.matcherwin_height); } +static void set_date_btn_to(struct tm *tm) +{ + GtkLabel *lbl; + gchar buf[11]; + + lbl = GTK_LABEL(gtk_bin_get_child(GTK_BIN(matcher.date_btn))); + snprintf(buf, 11, "%04d-%02d-%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + + gtk_label_set_text(lbl, buf); +} + +static void on_date_selected(GtkCalendar *cal) +{ + struct tm tm; + time_t ts; + + ts = time(NULL); + if (!localtime_r(&ts, &tm)) + goto done; + + gtk_calendar_get_date(cal, &tm.tm_year, &tm.tm_mon, &tm.tm_mday); + tm.tm_year -= 1900; + set_date_btn_to(&tm); + +done: + gtk_widget_destroy(gtk_widget_get_toplevel(GTK_WIDGET(cal))); +} + +static gboolean destroy_on_esc(GtkWidget *w, GdkEventKey *event) +{ + if (event->keyval == GDK_KEY_Escape) + { + gtk_widget_destroy(w); + return TRUE; + } + + return FALSE; +} + +static void date_btn_clicked(GtkWidget *btn) +{ + GtkWidget *window; + GtkWidget *frame; + GtkWidget *cal; + GtkCalendarDisplayOptions display_options; + const gchar *date; + gint year, month, day; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_decorated(GTK_WINDOW(window), FALSE); + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), TRUE); + gtk_window_set_skip_pager_hint(GTK_WINDOW(window), TRUE); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + g_signal_connect(G_OBJECT(window), "focus-out-event", + G_CALLBACK(gtk_widget_destroy), NULL); + g_signal_connect(G_OBJECT(window), "key-press-event", + G_CALLBACK(destroy_on_esc), NULL); + + frame = gtk_frame_new(NULL); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); + gtk_container_add(GTK_CONTAINER(window), frame); + + cal = gtk_calendar_new(); + display_options = GTK_CALENDAR_SHOW_HEADING | + GTK_CALENDAR_SHOW_WEEK_NUMBERS | + GTK_CALENDAR_SHOW_DAY_NAMES; + gtk_calendar_set_display_options(GTK_CALENDAR(cal), display_options); + + date = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(matcher.date_btn)))); + if (sscanf(date, "%04d-%02d-%02d", &year, &month, &day) == 3) + { + gtk_calendar_select_month(GTK_CALENDAR(cal), month - 1, year); + gtk_calendar_select_day(GTK_CALENDAR(cal), day); + } + + gtk_container_add(GTK_CONTAINER(frame), cal); + g_signal_connect(G_OBJECT(cal), "day-selected-double-click", + G_CALLBACK (on_date_selected), + NULL); + + /* move window so it looks like a popup */ + { + GtkAllocation alloc; + gint wx, wy; + gint x, y; + + gtk_window_get_position(GTK_WINDOW(gtk_widget_get_toplevel(btn)), &wx, &wy); + if (gtk_widget_translate_coordinates(btn,gtk_widget_get_toplevel(btn), 0, 0, &x, &y)) + { + wx += x; + wy += y; + } + gtk_widget_get_allocation(btn, &alloc); + wy += alloc.height; + gtk_window_move(GTK_WINDOW(window), wx, wy); + } + + gtk_widget_show_all(window); +} + /*! *\brief Create the matcher dialog */ @@ -548,11 +648,11 @@ static void prefs_matcher_create(void) GtkWidget *test_btn; GtkWidget *addressbook_select_btn; GtkWidget *color_optmenu; - GtkWidget *calendar; - GtkWidget *time_label; - GtkWidget *time_entry; GtkWidget *date_hbox; - GtkWidget *date_vbox; + GtkWidget *date_btn; + GtkWidget *hour_entry; + GtkWidget *min_entry; + GtkWidget *sec_entry; static GdkGeometry geometry; GtkSizeGroup *size_group; @@ -755,21 +855,26 @@ static void prefs_matcher_create(void) gtk_widget_set_halign(hbox, GTK_ALIGN_FILL); /* Date widgets */ - date_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, VSPACING_NARROW); - calendar = gtk_calendar_new(); - gtk_box_pack_start(GTK_BOX(hbox), calendar, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(lower_hbox), date_vbox, FALSE, FALSE, 0); date_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, HSPACING_NARROW); - gtk_box_pack_start(GTK_BOX(date_vbox), date_hbox, FALSE, FALSE, 0); - - time_entry = gtkut_time_select_combo_new(); - gtk_box_pack_start(GTK_BOX(date_hbox), time_entry, FALSE, FALSE, 0); - time_label = gtk_label_new(_("on:")); - gtk_label_set_xalign(GTK_LABEL(time_label),0); - gtk_label_set_yalign(GTK_LABEL(time_label),0.5); - gtk_box_pack_start(GTK_BOX(date_hbox), time_label, FALSE, FALSE, 0); - + gtk_box_pack_start(GTK_BOX(lower_hbox), date_hbox, FALSE, FALSE, 0); + + date_btn = gtk_button_new_with_label("YYYY-MM-DD"); + g_signal_connect(date_btn, "clicked", G_CALLBACK(date_btn_clicked), NULL); + gtk_box_pack_start(GTK_BOX(date_hbox), date_btn, TRUE, TRUE, 0); + + hour_entry = gtk_spin_button_new_with_range(0, 23, 1); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(hour_entry), 0); + gtk_box_pack_start(GTK_BOX(date_hbox), hour_entry, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(date_hbox), gtk_label_new(":"), FALSE, FALSE, 0); + min_entry = gtk_spin_button_new_with_range(0, 59, 1); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(min_entry), 0); + gtk_box_pack_start(GTK_BOX(date_hbox), min_entry, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(date_hbox), gtk_label_new(":"), FALSE, FALSE, 0); + sec_entry = gtk_spin_button_new_with_range(0, 59, 1); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(sec_entry), 0); + gtk_box_pack_start(GTK_BOX(date_hbox), sec_entry, FALSE, FALSE, 0); + /* test info button */ test_btn = gtkut_stock_button("dialog-information", _("_Information")); gtk_box_pack_start(GTK_BOX(lower_hbox), test_btn, FALSE, FALSE, 0); @@ -880,9 +985,10 @@ static void prefs_matcher_create(void) matcher.regexp_checkbtn = regexp_checkbtn; matcher.bool_op_combo = bool_op_combo; matcher.test_btn = test_btn; - matcher.calendar = calendar; - matcher.time_label = time_label; - matcher.time_entry = time_entry; + matcher.date_btn = date_btn; + matcher.hour_entry = hour_entry; + matcher.min_entry = min_entry; + matcher.sec_entry = sec_entry; #ifndef USE_ALT_ADDRBOOK matcher.addressbook_select_btn = addressbook_select_btn; #endif @@ -965,6 +1071,18 @@ static gboolean match_combo_model_set(void) return FALSE; } +static void reset_date_btn(void) +{ + struct tm tm; + time_t ts; + + ts = time(NULL); + if (!localtime_r(&ts, &tm)) + return; + + set_date_btn_to(&tm); +} + /*! *\brief Clears a condition in the list widget */ @@ -985,8 +1103,10 @@ static void prefs_matcher_reset_condition(void) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn), FALSE); - gtk_calendar_select_today(GTK_CALENDAR(matcher.calendar)); - gtkut_time_select_select_by_time(GTK_COMBO_BOX(matcher.time_entry), 0, 0); + reset_date_btn(); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.hour_entry), 0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.min_entry), 0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.sec_entry), 0); } /*! @@ -1497,7 +1617,6 @@ static MatcherProp *prefs_matcher_dialog_to_matcher(void) const gchar *header; const gchar *expr; gint value, sel; - gint year, month, day, hour, minute; if (value_criteria == -1) return NULL; @@ -1564,16 +1683,11 @@ static MatcherProp *prefs_matcher_dialog_to_matcher(void) case CRITERIA_DATE_AFTER: case CRITERIA_DATE_BEFORE: - expr = NULL; - gtk_calendar_get_date(GTK_CALENDAR(matcher.calendar), &year, &month, &day); - if (gtkut_time_select_get_time(GTK_COMBO_BOX(matcher.time_entry), &hour, &minute)) - expr = g_strdup_printf("%4d-%02d-%02d %02d:%02d:00", - year, month + 1, day, hour, minute); - - if (expr == NULL) { - alertpanel_error(_("Invalid hour.")); - return NULL; - } + expr = g_strdup_printf("%s %02d:%02d:%02d", + gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(matcher.date_btn)))), + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(matcher.hour_entry)), + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(matcher.min_entry)), + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(matcher.sec_entry))); break; case CRITERIA_TEST: @@ -1968,11 +2082,7 @@ static void prefs_matcher_criteria_select(GtkWidget *widget, prefs_matcher_enable_widget(matcher.string_entry, (MATCH_CASE_REGEXP(value) || value == MATCH_TEST)); - prefs_matcher_enable_widget(matcher.calendar, - (value == MATCH_DATE)); - prefs_matcher_enable_widget(matcher.time_label, - (value == MATCH_DATE)); - prefs_matcher_enable_widget(matcher.time_entry, + prefs_matcher_enable_widget(gtk_widget_get_parent(matcher.date_btn), (value == MATCH_DATE)); prefs_matcher_enable_widget(matcher.numeric_entry, MATCH_NUMERIC(value)); @@ -2012,8 +2122,10 @@ static void prefs_matcher_criteria_select(GtkWidget *widget, case MATCH_DATE: prefs_matcher_set_model(matcher.match_combo, matcher.model_date); gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Date is")); - gtk_calendar_select_today(GTK_CALENDAR(matcher.calendar)); - gtkut_time_select_select_by_time(GTK_COMBO_BOX(matcher.time_entry), 0, 0); + reset_date_btn(); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.hour_entry), 0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.min_entry), 0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.sec_entry), 0); break; case MATCH_AGE: prefs_matcher_set_model(matcher.match_combo, matcher.model_age); @@ -2450,8 +2562,6 @@ static gboolean prefs_matcher_selected(GtkTreeSelection *selector, gint criteria; GtkTreeIter iter; gboolean is_valid; - struct tm lt; - char zone[6]; if (currently_selected) return TRUE; @@ -2557,13 +2667,16 @@ static gboolean prefs_matcher_selected(GtkTreeSelection *selector, case MATCHCRITERIA_DATE_AFTER: case MATCHCRITERIA_DATE_BEFORE: - zone[0] = '\0'; - procheader_date_parse_to_tm(prop->expr, &lt, zone); - gtk_calendar_select_day(GTK_CALENDAR(matcher.calendar), lt.tm_mday); - gtk_calendar_select_month(GTK_CALENDAR(matcher.calendar), lt.tm_mon, lt.tm_year + 1900); - gtkut_time_select_select_by_time(GTK_COMBO_BOX(matcher.time_entry), lt.tm_hour, lt.tm_min); + { + struct tm tm; + matcherprop_parse_date_time(prop->criteria, prop->expr, &tm); + set_date_btn_to(&tm); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.hour_entry), tm.tm_hour); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.min_entry), tm.tm_min); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.sec_entry), tm.tm_sec); break; + } case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK: case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK: diff --git a/src/prefs_message.c b/src/prefs_message.c @@ -63,6 +63,8 @@ typedef struct _MessagePage GtkWidget *checkbtn_attach_desc; GtkWidget *entry_quote_chars; + + GtkWidget *checkbtn_decrypt; } MessagePage; static void disphdr_pane_toggled(GtkToggleButton *toggle_btn, GtkWidget *widget) @@ -114,6 +116,8 @@ static void prefs_message_create_widget(PrefsPage *_page, GtkWindow *window, GtkWidget *entry_quote_chars; GtkWidget *label_quote_chars; + GtkWidget *checkbtn_decrypt; + vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, VSPACING); gtk_widget_show (vbox1); gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER); @@ -259,6 +263,9 @@ static void prefs_message_create_widget(PrefsPage *_page, GtkWindow *window, FALSE, FALSE, 0); gtk_widget_set_size_request (entry_quote_chars, 64, -1); + PACK_CHECK_BUTTON(vbox1, checkbtn_decrypt, + _("Automatically decrypt messages")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_disphdrpane), prefs_common.display_header_pane); @@ -289,6 +296,8 @@ static void prefs_message_create_widget(PrefsPage *_page, GtkWindow *window, prefs_common.scroll_step); gtk_entry_set_text(GTK_ENTRY(entry_quote_chars), prefs_common.quote_chars?prefs_common.quote_chars:""); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_decrypt), + prefs_common.decrypt_messages); prefs_message->window = GTK_WIDGET(window); prefs_message->checkbtn_disphdrpane = checkbtn_disphdrpane; @@ -305,6 +314,7 @@ static void prefs_message_create_widget(PrefsPage *_page, GtkWindow *window, prefs_message->checkbtn_halfpage = checkbtn_halfpage; prefs_message->checkbtn_attach_desc = checkbtn_attach_desc; prefs_message->entry_quote_chars = entry_quote_chars; + prefs_message->checkbtn_decrypt = checkbtn_decrypt; prefs_message->page.widget = vbox1; } @@ -339,6 +349,8 @@ static void prefs_message_save(PrefsPage *_page) GTK_SPIN_BUTTON(page->spinbtn_linespc)); prefs_common.scroll_step = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(page->spinbtn_scrollstep)); + prefs_common.decrypt_messages = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(page->checkbtn_decrypt)); g_free(prefs_common.quote_chars); prefs_common.quote_chars = gtk_editable_get_chars( diff --git a/src/prefs_summaries.c b/src/prefs_summaries.c @@ -69,6 +69,7 @@ typedef struct _SummariesPage GtkWidget *checkbtn_show_on_prevnext; GtkWidget *checkbtn_show_on_deletemove; GtkWidget *checkbtn_show_on_directional; + GtkWidget *checkbtn_mark_as_read_on_never; GtkWidget *checkbtn_mark_as_read_on_newwin; GtkWidget *spinbtn_mark_as_read_delay; GtkWidget *checkbtn_immedexec; @@ -361,6 +362,7 @@ static void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window, GtkWidget *optmenu_summaryfromshow; GtkWidget *optmenu_nextunreadmsgdialog; GtkWidget *button_edit_actions; + GtkWidget *radio_mark_as_read_on_never; GtkWidget *radio_mark_as_read_on_select; GtkWidget *radio_mark_as_read_on_new_win; GtkWidget *optmenu_sort_key; @@ -540,7 +542,16 @@ static void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window, vbox3 = gtkut_get_options_frame(vbox1, NULL, _("Mark message as read")); - radio_mark_as_read_on_select = gtk_radio_button_new_with_label(NULL, + radio_mark_as_read_on_never = gtk_radio_button_new_with_label( + NULL, + _("never")); + gtk_widget_set_tooltip_text (radio_mark_as_read_on_never, + _("Never automatically mark a message as read.")); + gtk_box_pack_start (GTK_BOX (vbox3), radio_mark_as_read_on_never, + FALSE, FALSE, 0); + + radio_mark_as_read_on_select = gtk_radio_button_new_with_label_from_widget( + GTK_RADIO_BUTTON(radio_mark_as_read_on_never), _("when selected, after")); hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); @@ -693,6 +704,7 @@ static void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window, prefs_summaries->checkbtn_show_on_deletemove = checkbtn_show_on_deletemove; prefs_summaries->checkbtn_show_on_directional = checkbtn_show_on_directional; + prefs_summaries->checkbtn_mark_as_read_on_never = radio_mark_as_read_on_never; prefs_summaries->checkbtn_mark_as_read_on_newwin = radio_mark_as_read_on_new_win; prefs_summaries->spinbtn_mark_as_read_delay = spinbtn_mark_as_read_delay; prefs_summaries->checkbtn_immedexec = checkbtn_immedexec; @@ -749,6 +761,8 @@ static void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window, gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_show_on_directional), prefs_common.open_selected_on_directional); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_mark_as_read_on_never), + prefs_common.mark_as_read_on_never); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_mark_as_read_on_new_win), prefs_common.mark_as_read_on_new_window); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbtn_mark_as_read_delay), @@ -827,6 +841,8 @@ static void prefs_summaries_save(PrefsPage *_page) prefs_common.open_selected_on_directional = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(page->checkbtn_show_on_directional)); + prefs_common.mark_as_read_on_never = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(page->checkbtn_mark_as_read_on_never)); prefs_common.mark_as_read_on_new_window = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(page->checkbtn_mark_as_read_on_newwin)); prefs_common.immediate_exec = gtk_toggle_button_get_active( diff --git a/src/procmime.c b/src/procmime.c @@ -659,7 +659,8 @@ gboolean procmime_encode_content(MimeInfo *mimeinfo, EncodingType encoding) return TRUE; } -static gint procmime_get_part_to_stream(FILE *outfp, MimeInfo *mimeinfo) +static gint procmime_get_part_to_stream(FILE *outfp, MimeInfo *mimeinfo, + const gboolean handle_bom, const gchar *outfile) { FILE *infp; gchar buf[BUFFSIZE]; @@ -686,6 +687,96 @@ static gint procmime_get_part_to_stream(FILE *outfp, MimeInfo *mimeinfo) restlength = mimeinfo->length; +/* handle BOM bytes according to hidden option: write them when saving the attachment (default), + skip them or ask the user about skipping or writing them */ +/* see: http://en.wikipedia.org/wiki/Byte_order_mark */ + +/* debug_print("procmime_get_part_with_bom: handle bom: %d, option: %d\n", handle_bom, prefs_common.save_attachment_handle_bom);*/ + if (handle_bom && (restlength >= 2) && (prefs_common.save_attachment_handle_bom != HANDLE_BOM_WRITE)) { + gint bom_length = 0; + +#define COMP_4_BYTES(A, B, C, D) \ + ( ((buf[0]&0xff) == (guchar)(A)) && ((buf[1]&0xff) == (guchar)(B)) && ((buf[2]&0xff) == (guchar)(C)) && ((buf[3]&0xff) == (guchar)(D)) ) +#define COMP_3_BYTES(A, B, C) \ + ( ((buf[0]&0xff) == (guchar)(A)) && ((buf[1]&0xff) == (guchar)(B)) && ((buf[2]&0xff) == (guchar)(C)) ) +#define COMP_2_BYTES(A, B) \ + ( ((buf[0]&0xff) == (guchar)(A)) && ((buf[1]&0xff) == (guchar)(B)) ) + + debug_print("looking for BOM..\n"); + if ((readlength = fread(buf, 1, 4, infp)) > 0) { + gint i; + + debug_print("looking for BOM: read %d bytes\n", readlength); + for (i = 0; i < readlength; i++) + debug_print("[%d] = %c %03d %02x\n", i, buf[i]&0xff, buf[i]&0xff, buf[i]&0xff); + + if (readlength == 4) { + if (COMP_4_BYTES(0x00, 0x00, 0xfe, 0xff) || /* UTF-32 (Big Endian) */ + COMP_4_BYTES(0xff, 0xfe, 0x00, 0x00) || /* UTF-32 (Little Endian) */ + COMP_4_BYTES(0x2b, 0x2f, 0x76, 0x38) || /* UTF-7 */ + COMP_4_BYTES(0x2b, 0x2f, 0x76, 0x39) || /* UTF-7 */ + COMP_4_BYTES(0x2b, 0x2f, 0x76, 0x2b) || /* UTF-7 */ + COMP_4_BYTES(0x2b, 0x2f, 0x76, 0x2f) || /* UTF-7 */ + COMP_4_BYTES(0xdd, 0x73, 0x66, 0x73) || /* UTF-EBCDIC */ + COMP_4_BYTES(0xfb, 0xee, 0x28, 0xff) || /* BOCU-1 */ + COMP_4_BYTES(0x84, 0x31, 0x95, 0x33)) { /* GB-18030 */ + bom_length = 4; + } + } + if ((bom_length == 0) && (readlength >= 3)) { + if (COMP_3_BYTES(0xef, 0xbb, 0xbf) || /* UTF-8 */ + COMP_3_BYTES(0xf7, 0x64, 0x4c) || /* UTF-1 */ + COMP_3_BYTES(0x0e, 0xfe, 0xff) || /* SCSU */ + COMP_3_BYTES(0xfb, 0xee, 0x28)) { /* BOCU-1 */ + bom_length = 3; + } + } + if ((bom_length == 0) && (readlength >= 2)) { + if (COMP_2_BYTES(0xfe, 0xff) || /* UTF-16 (Big Endian) */ + COMP_2_BYTES(0xff, 0xfe)) { /* UTF-16 (Little Endian) */ + bom_length = 2; + } + } + debug_print("BOM matched: %d bytes\n", bom_length); + if ((bom_length > 0) && (prefs_common.save_attachment_handle_bom == HANDLE_BOM_ASK_USER)) { + AlertValue val; + gchar *msg; + gchar *filename; + + filename = g_path_get_basename(outfile); + msg = g_strdup_printf( + _("Claws Mail has detected that the attachment '%s' " + "starts with a %d-byte Byte Order Mask (BOM).\n\n" + "Do you want to save the file exactly as it has been received (with " + "the BOM bytes) or skip the BOM bytes when saving the file?"), + filename, bom_length); + g_free(filename); + val = alertpanel_full(_("Write BOM bytes?"), msg, + _("Save untouched"), _("Save without BOM"), NULL, + ALERTFOCUS_FIRST, FALSE, NULL, ALERT_QUESTION); + g_free(msg); + if (val != G_ALERTALTERNATE) { + /* save untouched attachment: do not skip BOM bytes */ + bom_length = 0; + } + } + + debug_print("writing %s BOM bytes: writing %d bytes\n", + (bom_length > 0)?"without":"with", readlength - bom_length); + if (fwrite(buf + bom_length, 1, readlength - bom_length, outfp) != (readlength - bom_length)) { + saved_errno = errno; + fclose(infp); + fclose(outfp); + return -(saved_errno); + } + restlength -= readlength; + } + +#undef COMP_2_BYTES +#undef COMP_3_BYTES +#undef COMP_4_BYTES + } + while ((restlength > 0) && ((readlength = claws_fread(buf, 1, restlength > BUFFSIZE ? BUFFSIZE : restlength, infp)) > 0)) { if (claws_fwrite(buf, 1, readlength, outfp) != readlength) { saved_errno = errno; @@ -701,7 +792,8 @@ static gint procmime_get_part_to_stream(FILE *outfp, MimeInfo *mimeinfo) return 0; } -gint procmime_get_part(const gchar *outfile, MimeInfo *mimeinfo) +gint procmime_get_part_with_bom(const gchar *outfile, MimeInfo *mimeinfo, + const gboolean handle_bom) { FILE *outfp; gint result; @@ -720,7 +812,7 @@ gint procmime_get_part(const gchar *outfile, MimeInfo *mimeinfo) g_warning("can't change file mode: %s", outfile); } - result = procmime_get_part_to_stream(outfp, mimeinfo); + result = procmime_get_part_to_stream(outfp, mimeinfo, handle_bom, outfile); if (claws_fclose(outfp) == EOF) { saved_errno = errno; @@ -733,6 +825,11 @@ gint procmime_get_part(const gchar *outfile, MimeInfo *mimeinfo) return result; } +gint procmime_get_part(const gchar *outfile, MimeInfo *mimeinfo) +{ + return procmime_get_part_with_bom(outfile, mimeinfo, FALSE); +} + gboolean procmime_scan_text_content(MimeInfo *mimeinfo, gboolean (*scan_callback)(const gchar *str, gpointer cb_data), gpointer cb_data) @@ -770,7 +867,7 @@ gboolean procmime_scan_text_content(MimeInfo *mimeinfo, return TRUE; } - if ((r = procmime_get_part_to_stream(tmpfp, mimeinfo)) < 0) { + if ((r = procmime_get_part_to_stream(tmpfp, mimeinfo, FALSE, "")) < 0) { g_warning("procmime_get_part_to_stream error %d", r); g_free(tmpfile); return TRUE; @@ -905,7 +1002,7 @@ FILE *procmime_get_binary_content(MimeInfo *mimeinfo) } #endif - if (procmime_get_part_to_stream(outfp, mimeinfo) < 0) { + if (procmime_get_part_to_stream(outfp, mimeinfo, FALSE, "") < 0) { return NULL; } ftruncate(fileno(outfp), ftell(outfp)); diff --git a/src/procmime.h b/src/procmime.h @@ -197,6 +197,8 @@ MimeInfo *procmime_scan_mime_header (FILE *fp); gboolean procmime_decode_content (MimeInfo *mimeinfo); gboolean procmime_encode_content (MimeInfo *mimeinfo, EncodingType encoding); +gint procmime_get_part_with_bom (const gchar *outfile, + MimeInfo *mimeinfo, const gboolean handle_bom); gint procmime_get_part (const gchar *outfile, MimeInfo *mimeinfo); FILE *procmime_get_first_text_content (MsgInfo *msginfo); diff --git a/src/procmsg.c b/src/procmsg.c @@ -76,6 +76,7 @@ enum Q_ENCRYPT_OLD = 14, Q_ENCRYPT_DATA_OLD = 15, Q_CLAWS_HDRS_OLD = 16, + Q_DSN = 17, }; void procmsg_msg_list_free(GSList *mlist) @@ -906,6 +907,7 @@ parse_again: if (orig && g_slist_length(orig)) { if (!last_account && nothing_to_sort) { /* can't find an account for the rest of the list */ +fprintf(stderr, "==> 1220858 logically dead code\n"); cur = orig; while (cur) { result = g_slist_append(result, cur->data); @@ -1559,6 +1561,7 @@ static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_ses {"X-Sylpheed-Encrypt:", NULL, FALSE}, {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */ {"X-Sylpheed-End-Special-Headers:", NULL, FALSE}, + {"DSN:", NULL, FALSE}, {NULL, NULL, FALSE}}; FILE *fp; gint filepos; @@ -1575,6 +1578,7 @@ static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_ses PrefsAccount *mailac = NULL, *newsac = NULL; gboolean encrypt = FALSE; FolderItem *outbox; + gboolean dsn_requested = FALSE; cm_return_val_if_fail(file != NULL, -1); @@ -1628,6 +1632,10 @@ static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_ses if (p[0] == '1') encrypt = TRUE; break; + case Q_DSN: + if (p[0] == '1') + dsn_requested = TRUE; + break; case Q_CLAWS_HDRS: case Q_CLAWS_HDRS_OLD: /* end of special headers reached */ @@ -1670,7 +1678,7 @@ send_mail: } if (mailac) { - mailval = send_message_smtp_full(mailac, to_list, fp, keep_session); + mailval = send_message_smtp_full(mailac, to_list, dsn_requested, fp, keep_session); if (mailval == -1 && errstr) { if (*errstr) g_free(*errstr); *errstr = g_strdup_printf(_("An error happened during SMTP session.")); @@ -1684,7 +1692,7 @@ send_mail: tmp_ac.address = from; tmp_ac.smtp_server = smtpserver; tmp_ac.smtpport = SMTP_PORT; - mailval = send_message_smtp(&tmp_ac, to_list, fp); + mailval = send_message_smtp(&tmp_ac, to_list, dsn_requested, fp); if (mailval == -1 && errstr) { if (*errstr) g_free(*errstr); *errstr = g_strdup_printf(_("No specific account has been found to " diff --git a/src/send_message.c b/src/send_message.c @@ -125,7 +125,7 @@ gint send_message(const gchar *file, PrefsAccount *ac_prefs, GSList *to_list) inc_unlock(); return val; } else { - val = send_message_smtp(ac_prefs, to_list, fp); + val = send_message_smtp(ac_prefs, to_list, FALSE, fp); claws_fclose(fp); inc_unlock(); @@ -212,7 +212,7 @@ gint send_message_local(const gchar *command, FILE *fp) return 0; } -gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, gboolean keep_session) +gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, gboolean dsn_requested, FILE *fp, gboolean keep_session) { Session *session; SMTPSession *smtp_session; @@ -385,6 +385,7 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g ac_prefs->session = NULL; smtp_session = SMTP_SESSION(session); smtp_session->state = SMTP_HELO; + smtp_session->is_dsn_supported = FALSE; send_dialog = (SendProgressDialog *)smtp_session->dialog; was_inited = TRUE; } @@ -395,6 +396,7 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g smtp_session->cur_to = to_list; smtp_session->send_data = (guchar *)get_outgoing_rfc2822_str(fp); smtp_session->send_data_len = strlen((gchar *)smtp_session->send_data); + smtp_session->is_dsn_requested = dsn_requested; if (ac_prefs->use_proxy && ac_prefs->use_proxy_for_send) { if (ac_prefs->use_default_proxy) { @@ -488,9 +490,9 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g return ret; } -gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp) +gint send_message_smtp(PrefsAccount *ac_prefs, GSList *to_list, gboolean dsn_requested, FILE *fp) { - return send_message_smtp_full(ac_prefs, to_list, fp, FALSE); + return send_message_smtp_full(ac_prefs, to_list, dsn_requested, fp, FALSE); } static gint send_recv_message(Session *session, const gchar *msg, gpointer data) diff --git a/src/send_message.h b/src/send_message.h @@ -36,9 +36,11 @@ gint send_message_local (const gchar *command, FILE *fp); gint send_message_smtp (PrefsAccount *ac_prefs, GSList *to_list, + gboolean dsn_requested, FILE *fp); gint send_message_smtp_full (PrefsAccount *ac_prefs, GSList *to_list, + gboolean dsn_requested, FILE *fp, gboolean keep_session); void send_cancel (void); diff --git a/src/summaryview.c b/src/summaryview.c @@ -3784,7 +3784,8 @@ static void summary_display_msg_full(SummaryView *summaryview, } } - if (val == 0 && MSG_IS_UNREAD(msginfo->flags)) { + if (val == 0 && MSG_IS_UNREAD(msginfo->flags) + && prefs_common.mark_as_read_on_never == 0) { if (!prefs_common.mark_as_read_on_new_window && prefs_common.mark_as_read_delay) { MarkAsReadData *data = g_new0(MarkAsReadData, 1); @@ -5007,10 +5008,24 @@ void summary_save_as(SummaryView *summaryview) if (!dest) return; if (is_file_exist(dest)) { + gchar *res; + gint sz; + + sz = get_file_size(dest); + if (!g_utf8_validate(dest, -1, NULL)) { + tmp = conv_filename_to_utf8(dest); + if (sz == -1) + sz = get_file_size(dest); + } else + tmp = g_strdup(dest); + + res = g_strdup_printf(_("Append to existing file or overwrite it ('%s', %ld bytes)\nwith this one (%ld bytes)?"), + dest, sz, msginfo->size); + g_free(tmp); aval = alertpanel(_("Append or Overwrite"), - _("Append or overwrite existing file?"), - _("_Append"), _("_Overwrite"), _("_Cancel"), - ALERTFOCUS_FIRST); + res, _("_Append"), _("_Overwrite"), + _("_Cancel"), ALERTFOCUS_FIRST); + g_free(res); if (aval != 0 && aval != 1) return; } diff --git a/src/textview.c b/src/textview.c @@ -223,6 +223,8 @@ static void textview_toggle_quote (TextView *textview, static void open_uri_cb (GtkAction *action, TextView *textview); +static void open_uri_alt_cb (GtkAction *action, + TextView *textview); static void copy_uri_cb (GtkAction *action, TextView *textview); static void add_uri_to_addrbook_cb (GtkAction *action, @@ -239,6 +241,7 @@ static GtkActionEntry textview_link_popup_entries[] = { {"TextviewPopupLink", NULL, "TextviewPopupLink", NULL, NULL, NULL }, {"TextviewPopupLink/Open", NULL, N_("_Open in web browser"), NULL, NULL, G_CALLBACK(open_uri_cb) }, + {"TextviewPopupLink/OpenAlt", NULL, N_("_Open in alternate web browser"), NULL, NULL, G_CALLBACK(open_uri_alt_cb) }, {"TextviewPopupLink/Copy", NULL, N_("Copy this _link"), NULL, NULL, G_CALLBACK(copy_uri_cb) }, }; @@ -381,6 +384,8 @@ TextView *textview_create(void) MENUITEM_ADDUI_MANAGER(textview->ui_manager, "/Menus/TextviewPopupLink", "Open", "TextviewPopupLink/Open", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(textview->ui_manager, + "/Menus/TextviewPopupLink", "OpenAlt", "TextviewPopupLink/OpenAlt", GTK_UI_MANAGER_MENUITEM) + MENUITEM_ADDUI_MANAGER(textview->ui_manager, "/Menus/TextviewPopupLink", "Copy", "TextviewPopupLink/Copy", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(textview->ui_manager, "/Menus/TextviewPopupMail", "Compose", "TextviewPopupMail/Compose", GTK_UI_MANAGER_MENUITEM) @@ -3151,6 +3156,27 @@ static void open_uri_cb (GtkAction *action, TextView *textview) } } +static void open_uri_alt_cb (GtkAction *action, TextView *textview) +{ + ClickableText *uri = g_object_get_data(G_OBJECT(textview->link_popup_menu), + "menu_button"); + const gchar *raw_url = g_object_get_data(G_OBJECT(textview->link_popup_menu), + "raw_url"); + + if (uri) { + if (textview_uri_security_check(textview, uri, FALSE) == TRUE) + open_uri(uri->uri, + prefs_common_get_uri_alt_cmd()); + g_object_set_data(G_OBJECT(textview->link_popup_menu), "menu_button", + NULL); + } + if (raw_url) { + open_uri(raw_url, prefs_common_get_uri_alt_cmd()); + g_object_set_data(G_OBJECT(textview->link_popup_menu), "raw_url", + NULL); + } +} + static void copy_uri_cb (GtkAction *action, TextView *textview) { ClickableText *uri = g_object_get_data(G_OBJECT(textview->link_popup_menu), diff --git a/src/toolbar.c b/src/toolbar.c @@ -1492,19 +1492,29 @@ static void toolbar_next_unread_cb(GtkWidget *widget, gpointer data) ToolbarItem *toolbar_item = (ToolbarItem*)data; MainWindow *mainwin; MessageView *msgview; + gboolean save_option_value; cm_return_if_fail(toolbar_item != NULL); switch (toolbar_item->type) { case TOOLBAR_MAIN: + mainwin = (MainWindow*)toolbar_item->parent; +/* (workaround) force marking opened message as read when entering new folders */ +save_option_value = prefs_common.open_selected_on_folder_open; +prefs_common.open_selected_on_folder_open = TRUE; summary_select_next_unread(mainwin->summaryview); +prefs_common.open_selected_on_folder_open = save_option_value; break; case TOOLBAR_MSGVIEW: msgview = (MessageView*)toolbar_item->parent; msgview->updating = TRUE; +/* (workaround) force marking opened message as read when entering new folders */ +save_option_value = prefs_common.open_selected_on_folder_open; +prefs_common.open_selected_on_folder_open = TRUE; summary_select_next_unread(msgview->mainwin->summaryview); +prefs_common.open_selected_on_folder_open = save_option_value; msgview->updating = FALSE; if (msgview->deferred_destroy) { @@ -2841,7 +2851,7 @@ do { \ for (cur = toolbar->action_list; cur != NULL; cur = cur->next) { ToolbarClawsActions *act = (ToolbarClawsActions*)cur->data; - SET_WIDGET_COND(act->widget, M_TARGET_EXIST, M_UNLOCKED); + SET_WIDGET_COND(act->widget, M_TARGET_EXIST); } state = main_window_get_current_state(mainwin); @@ -3141,7 +3151,8 @@ void compose_mail_cb(gpointer data, guint action, GtkWidget *widget) list = account_get_list(); for (cur = list ; cur != NULL ; cur = g_list_next(cur)) { ac = (PrefsAccount *) cur->data; - if (ac->protocol != A_NNTP) { + if ((ac->protocol != A_NNTP) && + (ac->selectable_as_current_account)) { compose_new_with_folderitem(ac, item, NULL); return; } @@ -3167,7 +3178,8 @@ void compose_news_cb(gpointer data, guint action, GtkWidget *widget) list = account_get_list(); for(cur = list ; cur != NULL ; cur = g_list_next(cur)) { ac = (PrefsAccount *) cur->data; - if (ac->protocol == A_NNTP) { + if ((ac->protocol == A_NNTP) && + (ac->selectable_as_current_account)) { compose_new_with_folderitem(ac, mainwin->summaryview->folder_item, NULL); return; diff --git a/src/vcard.c b/src/vcard.c @@ -62,6 +62,7 @@ VCardFile *vcard_create() { cardFile->file = NULL; cardFile->path = NULL; cardFile->bufptr = cardFile->buffer; + cardFile->addressCache->collapsedFlag = TRUE; return cardFile; } @@ -390,8 +391,8 @@ static gchar *vcard_unescape_qp( gchar *value ) { return NULL; len = strlen(value); - res = g_malloc(len); - qp_decode_const(res, len-1, value); + res = g_malloc(len + 1); + qp_decode_const(res, len, value); if (!g_utf8_validate(res, -1, NULL)) { gchar *mybuf = g_malloc(strlen(res)*2 +1); conv_localetodisp(mybuf, strlen(res)*2 +1, res); @@ -699,6 +700,21 @@ gint vcard_test_read_file( const gchar *fileSpec ) { return retVal; } +gboolean vcard_get_collapsed( VCardFile *cardFile ) +{ + g_return_val_if_fail( cardFile != NULL, FALSE ); +fprintf(stderr, "==> vcard_get_collapsed: %d\n", cardFile->addressCache->collapsedFlag); + return cardFile->addressCache->collapsedFlag; +} + +void vcard_set_collapsed( VCardFile *cardFile, const gboolean value ) +{ + g_return_if_fail( cardFile != NULL ); +fprintf(stderr, "==> vcard_set_collapsed: %d\n", value); + cardFile->addressCache->collapsedFlag = value; + addrcache_set_dirty(cardFile->addressCache, TRUE); +} + /* * End of Source. */ diff --git a/src/vcard.h b/src/vcard.h @@ -87,8 +87,10 @@ void vcard_set_name ( VCardFile* cardFile, const gchar *value ); void vcard_set_file ( VCardFile* cardFile, const gchar *value ); void vcard_set_modified ( VCardFile *cardFile, const gboolean value ); void vcard_set_accessed ( VCardFile *cardFile, const gboolean value ); +void vcard_set_collapsed ( VCardFile *cardFile, const gboolean value ); gboolean vcard_get_modified ( VCardFile *cardFile ); gboolean vcard_get_accessed ( VCardFile *cardFile ); +gboolean vcard_get_collapsed ( VCardFile *cardFile ); gboolean vcard_get_read_flag ( VCardFile *cardFile ); gint vcard_get_status ( VCardFile *cardFile ); ItemFolder *vcard_get_root_folder ( VCardFile *cardFile );