diff options
29 files changed, 1022 insertions, 773 deletions
diff --git a/addressbook/gui/component/e-book-shell-view-actions.c b/addressbook/gui/component/e-book-shell-view-actions.c index 73766c7df3..773eb3fa7a 100644 --- a/addressbook/gui/component/e-book-shell-view-actions.c +++ b/addressbook/gui/component/e-book-shell-view-actions.c @@ -562,7 +562,13 @@ action_search_filter_cb (GtkRadioAction *action, GtkRadioAction *current, EBookShellView *book_shell_view) { - e_book_shell_view_execute_search (book_shell_view); + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); } static GtkActionEntry contact_entries[] = { diff --git a/calendar/module/e-cal-shell-view-actions.c b/calendar/module/e-cal-shell-view-actions.c index 5f5380d919..02b7af609e 100644 --- a/calendar/module/e-cal-shell-view-actions.c +++ b/calendar/module/e-cal-shell-view-actions.c @@ -599,7 +599,13 @@ action_search_filter_cb (GtkRadioAction *action, GtkRadioAction *current, ECalShellView *cal_shell_view) { - e_cal_shell_view_execute_search (cal_shell_view); + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); } static GtkActionEntry calendar_entries[] = { diff --git a/calendar/module/e-memo-shell-view-actions.c b/calendar/module/e-memo-shell-view-actions.c index 855a7b18b4..1736606abe 100644 --- a/calendar/module/e-memo-shell-view-actions.c +++ b/calendar/module/e-memo-shell-view-actions.c @@ -548,7 +548,13 @@ action_search_filter_cb (GtkRadioAction *action, GtkRadioAction *current, EMemoShellView *memo_shell_view) { - e_memo_shell_view_execute_search (memo_shell_view); + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); } static GtkActionEntry memo_entries[] = { diff --git a/calendar/module/e-task-shell-view-actions.c b/calendar/module/e-task-shell-view-actions.c index 8431b64624..4deed9fdf0 100644 --- a/calendar/module/e-task-shell-view-actions.c +++ b/calendar/module/e-task-shell-view-actions.c @@ -62,7 +62,13 @@ action_search_filter_cb (GtkRadioAction *action, GtkRadioAction *current, ETaskShellView *task_shell_view) { - e_task_shell_view_execute_search (task_shell_view); + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); } static void diff --git a/doc/reference/shell/tmpl/eshell-unused.sgml b/doc/reference/shell/tmpl/eshell-unused.sgml index 72168ee8e2..dcfb2afdde 100644 --- a/doc/reference/shell/tmpl/eshell-unused.sgml +++ b/doc/reference/shell/tmpl/eshell-unused.sgml @@ -525,6 +525,22 @@ intelligent @shutdown: @migrate: +<!-- ##### SIGNAL EShellView::capture-state ##### --> +<para> + +</para> + +@eshellview: the object which received the signal. +@arg1: + +<!-- ##### SIGNAL EShellView::restore-state ##### --> +<para> + +</para> + +@eshellview: the object which received the signal. +@arg1: + <!-- ##### ARG EShellView:shell-module ##### --> <para> diff --git a/e-util/e-util.c b/e-util/e-util.c index 0eaf8c39a4..55eb6eba6b 100644 --- a/e-util/e-util.c +++ b/e-util/e-util.c @@ -372,6 +372,41 @@ e_action_group_remove_all_actions (GtkActionGroup *action_group) } /** + * e_radio_action_get_current_action: + * @radio_action: a #GtkRadioAction + * + * Returns the currently active member of the group to which @radio_action + * belongs. + * + * Returns: the currently active group member + **/ +GtkRadioAction * +e_radio_action_get_current_action (GtkRadioAction *radio_action) +{ + GSList *group; + gint current_value; + + g_return_val_if_fail (GTK_IS_RADIO_ACTION (radio_action), NULL); + + group = gtk_radio_action_get_group (radio_action); + current_value = gtk_radio_action_get_current_value (radio_action); + + while (group != NULL) { + gint value; + + radio_action = GTK_RADIO_ACTION (group->data); + g_object_get (radio_action, "value", &value, NULL); + + if (value == current_value) + return radio_action; + + group = g_slist_next (group); + } + + return NULL; +} + +/** * e_str_without_underscores: * @s: the string to strip underscores from. * diff --git a/e-util/e-util.h b/e-util/e-util.h index abc76359a6..33bc940df7 100644 --- a/e-util/e-util.h +++ b/e-util/e-util.h @@ -57,6 +57,8 @@ gint e_action_compare_by_label (GtkAction *action1, GtkAction *action2); void e_action_group_remove_all_actions (GtkActionGroup *action_group); +GtkRadioAction *e_radio_action_get_current_action + (GtkRadioAction *radio_action); gchar * e_str_without_underscores (const gchar *s); gint e_str_compare (gconstpointer x, diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c index edea7cfa6f..94855ee68b 100644 --- a/mail/e-mail-reader.c +++ b/mail/e-mail-reader.c @@ -167,6 +167,8 @@ action_mail_copy_cb (GtkAction *action, folder = message_list->folder; + em_folder_tree_clone_expanded (folder_tree); + em_folder_tree_set_excluded ( EM_FOLDER_TREE (folder_tree), EMFT_EXCLUDE_NOSELECT | EMFT_EXCLUDE_VIRTUAL | @@ -577,6 +579,8 @@ action_mail_move_cb (GtkAction *action, folder = message_list->folder; + em_folder_tree_clone_expanded (folder_tree); + em_folder_tree_set_excluded ( EM_FOLDER_TREE (folder_tree), EMFT_EXCLUDE_NOSELECT | EMFT_EXCLUDE_VIRTUAL | diff --git a/mail/e-mail-shell-content.c b/mail/e-mail-shell-content.c index 203876fcc3..abb7e8fa26 100644 --- a/mail/e-mail-shell-content.c +++ b/mail/e-mail-shell-content.c @@ -44,6 +44,9 @@ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContentPrivate)) +#define STATE_KEY_SCROLLBAR_POSITION "ScrollbarPosition" +#define STATE_KEY_SELECTED_MESSAGE "SelectedMessage" + struct _EMailShellContentPrivate { GtkWidget *paned; GtkWidget *message_list; @@ -52,8 +55,6 @@ struct _EMailShellContentPrivate { EMFormatHTMLDisplay *html_display; GalViewInstance *view_instance; - gchar *selected_uid; - /* ETable scrolling hack */ gdouble default_scrollbar_position; @@ -97,52 +98,78 @@ static void mail_shell_content_message_list_scrolled_cb (EMailShellContent *mail_shell_content, MessageList *message_list) { + EShellContent *shell_content; + EShellView *shell_view; + GKeyFile *key_file; + const gchar *folder_uri; const gchar *key; + gchar *group_name; gdouble position; - gchar *value; /* Save the scrollbar position for the current folder. */ - if (message_list->folder == NULL) + folder_uri = message_list->folder_uri; + + if (folder_uri == NULL) return; - key = "evolution:list_scroll_position"; + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + key_file = e_shell_view_get_state_key_file (shell_view); + + key = STATE_KEY_SCROLLBAR_POSITION; + group_name = g_strdup_printf ("Folder %s", folder_uri); position = message_list_get_scrollbar_position (message_list); - value = g_strdup_printf ("%f", position); - if (camel_object_meta_set (message_list->folder, key, value)) - camel_object_state_write (message_list->folder); + g_key_file_set_double (key_file, group_name, key, position); + e_shell_view_set_state_dirty (shell_view); - g_free (value); + g_free (group_name); } static gboolean mail_shell_content_scroll_timeout_cb (EMailShellContent *mail_shell_content) { EMailShellContentPrivate *priv = mail_shell_content->priv; + EShellContent *shell_content; + EShellView *shell_view; MessageList *message_list; EMailReader *reader; + GKeyFile *key_file; + const gchar *folder_uri; const gchar *key; - gdouble position; - gchar *value; + gchar *group_name; /* Initialize the scrollbar position for the current folder * and setup a callback to handle scrollbar position changes. */ + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + key_file = e_shell_view_get_state_key_file (shell_view); + reader = E_MAIL_READER (mail_shell_content); message_list = e_mail_reader_get_message_list (reader); - position = priv->default_scrollbar_position; + folder_uri = message_list->folder_uri; + + if (folder_uri == NULL) + goto skip; + + /* Restore the message list scrollbar position. */ - key = "evolution:list_scroll_position"; - value = camel_object_meta_get (message_list->folder, key); + key = STATE_KEY_SCROLLBAR_POSITION; + group_name = g_strdup_printf ("Folder %s", folder_uri); - if (value != NULL) { - position = strtod (value, NULL); - g_free (value); + if (g_key_file_has_key (key_file, group_name, key, NULL)) { + gdouble position; + + position = g_key_file_get_double ( + key_file, group_name, key, NULL); + message_list_set_scrollbar_position (message_list, position); } - message_list_set_scrollbar_position (message_list, position); + g_free (group_name); +skip: priv->message_list_scrolled_id = g_signal_connect_swapped ( message_list, "message-list-scrolled", G_CALLBACK (mail_shell_content_message_list_scrolled_cb), @@ -158,37 +185,59 @@ mail_shell_content_message_list_built_cb (EMailShellContent *mail_shell_content, MessageList *message_list) { EMailShellContentPrivate *priv = mail_shell_content->priv; + EShellContent *shell_content; + EShellView *shell_view; GtkScrolledWindow *scrolled_window; GtkWidget *vscrollbar; - gdouble position = 0.0; + GKeyFile *key_file; + gchar *uid; g_signal_handler_disconnect ( message_list, priv->message_list_built_id); priv->message_list_built_id = 0; - if (message_list->cursor_uid == NULL && priv->selected_uid != NULL) { + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + key_file = e_shell_view_get_state_key_file (shell_view); + + if (message_list->cursor_uid != NULL) + uid = NULL; + + else if (message_list->folder_uri == NULL) + uid = NULL; + + else if (mail_shell_content->priv->suppress_message_selection) + uid = NULL; + + else { + const gchar *folder_uri; + const gchar *key; + gchar *group_name; + + key = STATE_KEY_SELECTED_MESSAGE; + folder_uri = message_list->folder_uri; + group_name = g_strdup_printf ("Folder %s", folder_uri); + uid = g_key_file_get_string (key_file, group_name, key, NULL); + g_free (group_name); + } + + if (uid != NULL) { + CamelFolder *folder; CamelMessageInfo *info; - /* If the message isn't in the folder yet, keep selected_uid - * around, as it could be caught by a set_folder() at some - * later date. */ - info = camel_folder_get_message_info ( - message_list->folder, priv->selected_uid); + folder = message_list->folder; + info = camel_folder_get_message_info (folder, uid); if (info != NULL) { - camel_folder_free_message_info ( - message_list->folder, info); - e_mail_reader_set_message ( - E_MAIL_READER (mail_shell_content), - priv->selected_uid, TRUE); - g_free (priv->selected_uid); - priv->selected_uid = NULL; + EMailReader *reader; + + reader = E_MAIL_READER (mail_shell_content); + e_mail_reader_set_message (reader, uid, TRUE); + camel_folder_free_message_info (folder, info); } - position = message_list_get_scrollbar_position (message_list); + g_free (uid); } - priv->default_scrollbar_position = position; - /* FIXME This is a gross workaround for an ETable bug that I can't * fix (Ximian bug #55303). * @@ -227,25 +276,38 @@ mail_shell_content_display_view_cb (EMailShellContent *mail_shell_content, static void mail_shell_content_message_selected_cb (EMailShellContent *mail_shell_content, - const gchar *selected_uid, + const gchar *message_uid, MessageList *message_list) { - const gchar *key = "evolution:selected_uid"; - CamelFolder *folder; + EShellContent *shell_content; + EShellView *shell_view; + GKeyFile *key_file; + const gchar *folder_uri; + const gchar *key; + gchar *group_name; - folder = message_list->folder; + folder_uri = message_list->folder_uri; /* This also gets triggered when selecting a store name on * the sidebar such as "On This Computer", in which case - * 'folder' will be NULL. */ - if (folder == NULL) + * 'folder_uri' will be NULL. */ + if (folder_uri == NULL) return; - if (camel_object_meta_set (folder, key, selected_uid)) - camel_object_state_write (folder); + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + key_file = e_shell_view_get_state_key_file (shell_view); + + key = STATE_KEY_SELECTED_MESSAGE; + group_name = g_strdup_printf ("Folder %s", folder_uri); + + if (message_uid != NULL) + g_key_file_set_string (key_file, group_name, key, message_uid); + else + g_key_file_remove_key (key_file, group_name, key, NULL); + e_shell_view_set_state_dirty (shell_view); - g_free (mail_shell_content->priv->selected_uid); - mail_shell_content->priv->selected_uid = NULL; + g_free (group_name); } static void @@ -345,19 +407,6 @@ mail_shell_content_dispose (GObject *object) } static void -mail_shell_content_finalize (GObject *object) -{ - EMailShellContentPrivate *priv; - - priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (object); - - g_free (priv->selected_uid); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void mail_shell_content_constructed (GObject *object) { EMailShellContentPrivate *priv; @@ -543,7 +592,6 @@ mail_shell_content_set_folder (EMailReader *reader, EMailReaderIface *default_iface; MessageList *message_list; gboolean different_folder; - gchar *meta_data; priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader); @@ -570,15 +618,6 @@ mail_shell_content_set_folder (EMailReader *reader, if (different_folder) priv->suppress_message_selection = FALSE; - if (!priv->suppress_message_selection) - meta_data = camel_object_meta_get ( - folder, "evolution:selected_uid"); - else - meta_data = NULL; - - g_free (priv->selected_uid); - priv->selected_uid = meta_data; - /* This is a one-time-only callback. */ if (message_list->cursor_uid == NULL && priv->message_list_built_id == 0) priv->message_list_built_id = g_signal_connect_swapped ( @@ -613,7 +652,6 @@ mail_shell_content_class_init (EMailShellContentClass *class) object_class->set_property = mail_shell_content_set_property; object_class->get_property = mail_shell_content_get_property; object_class->dispose = mail_shell_content_dispose; - object_class->finalize = mail_shell_content_finalize; object_class->constructed = mail_shell_content_constructed; shell_content_class = E_SHELL_CONTENT_CLASS (class); diff --git a/mail/e-mail-shell-sidebar.c b/mail/e-mail-shell-sidebar.c index 1b090fa703..10f0062f26 100644 --- a/mail/e-mail-shell-sidebar.c +++ b/mail/e-mail-shell-sidebar.c @@ -33,6 +33,8 @@ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebarPrivate)) +#define STATE_KEY_EXPANDED "Expanded" + struct _EMailShellSidebarPrivate { GtkWidget *folder_tree; }; @@ -46,6 +48,301 @@ static gpointer parent_class; static GType mail_shell_sidebar_type; static void +mail_shell_sidebar_restore_state (EMailShellSidebar *mail_shell_sidebar) +{ + EShellView *shell_view; + EShellSidebar *shell_sidebar; + EMFolderTreeModel *folder_tree_model; + EMFolderTree *folder_tree; + GtkTreeModel *tree_model; + GtkTreeView *tree_view; + GtkTreeIter iter; + GHashTable *hash_table; + GKeyFile *key_file; + gboolean valid; + gchar *selected; + gchar **groups; + gint ii; + + shell_sidebar = E_SHELL_SIDEBAR (mail_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + key_file = e_shell_view_get_state_key_file (shell_view); + + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + folder_tree_model = em_folder_tree_get_model (folder_tree); + hash_table = folder_tree_model->uri_hash; + + tree_view = GTK_TREE_VIEW (folder_tree); + tree_model = GTK_TREE_MODEL (folder_tree_model); + + /* Restore selected folder. */ + + selected = g_key_file_get_string ( + key_file, "Folder Tree", "Selected", NULL); + if (selected != NULL) { + em_folder_tree_set_selected (folder_tree, selected, FALSE); + g_free (selected); + } + + /* Set the initial folder tree expanded state in two stages: + * + * 1) Iterate over the "Store" and "Folder" state file groups + * and apply the "Expanded" keys where possible. + * + * 2) Iterate over the top-level nodes in the folder tree + * (these are all stores) and expand those that have no + * corresponding "Expanded" key in the state file. This + * ensures that new stores are expanded by default. + * + * The expanded state is recorded and maintained in the tree model + * so that folder trees in other contexts can duplicate it using + * em_folder_tree_clone_expanded(). + */ + + /* Stage 1 */ + + groups = g_key_file_get_groups (key_file, NULL); + + for (ii = 0; groups[ii] != NULL; ii++) { + GtkTreeRowReference *reference; + GtkTreePath *path; + GtkTreeIter iter; + const gchar *group_name = groups[ii]; + const gchar *key = STATE_KEY_EXPANDED; + const gchar *uri; + gboolean expanded; + + if (g_str_has_prefix (group_name, "Store ")) { + uri = group_name + 6; + expanded = TRUE; + } else if (g_str_has_prefix (group_name, "Folder ")) { + uri = group_name + 7; + expanded = FALSE; + } else + continue; + + if (g_key_file_has_key (key_file, group_name, key, NULL)) + expanded = g_key_file_get_boolean ( + key_file, group_name, key, NULL); + + if (!expanded) + continue; + + reference = g_hash_table_lookup (hash_table, uri); + if (reference == NULL) + continue; + + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (tree_model, &iter, path); + gtk_tree_view_expand_row (tree_view, path, FALSE); + gtk_tree_path_free (path); + + /* The expanded column is used to clone the sidebar's + * expanded state in other EMFolderTree instances. */ + gtk_tree_store_set ( + GTK_TREE_STORE (tree_model), &iter, + COL_BOOL_EXPANDED, TRUE, -1); + } + + g_strfreev (groups); + + /* Stage 2 */ + + valid = gtk_tree_model_get_iter_first (tree_model, &iter); + + while (valid) { + const gchar *key = STATE_KEY_EXPANDED; + gchar *group_name; + gchar *uri; + + gtk_tree_model_get ( + tree_model, &iter, COL_STRING_URI, &uri, -1); + + if (uri == NULL) + goto next; + + group_name = g_strdup_printf ("Store %s", uri); + + if (!g_key_file_has_key (key_file, group_name, key, NULL)) { + GtkTreePath *path; + + path = gtk_tree_model_get_path (tree_model, &iter); + gtk_tree_view_expand_row (tree_view, path, FALSE); + gtk_tree_path_free (path); + + /* The expanded column is used to clone the sidebar's + * expanded state in other EMFolderTree instances. */ + gtk_tree_store_set ( + GTK_TREE_STORE (tree_model), &iter, + COL_BOOL_EXPANDED, TRUE, -1); + } + + g_free (group_name); + g_free (uri); + + next: + valid = gtk_tree_model_iter_next (tree_model, &iter); + } +} + +static void +mail_shell_sidebar_row_collapsed_cb (EShellSidebar *shell_sidebar, + GtkTreeIter *iter, + GtkTreePath *path, + GtkTreeView *tree_view) +{ + EShellView *shell_view; + GtkTreeModel *model; + GKeyFile *key_file; + const gchar *key; + gboolean is_folder; + gboolean is_store; + gchar *group_name; + gchar *uri; + + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + key_file = e_shell_view_get_state_key_file (shell_view); + + model = gtk_tree_view_get_model (tree_view); + + gtk_tree_model_get ( + model, iter, + COL_STRING_URI, &uri, + COL_BOOL_IS_STORE, &is_store, + COL_BOOL_IS_FOLDER, &is_folder, -1); + + g_return_if_fail (is_store || is_folder); + + /* The expanded column is used to clone the sidebar's + * expanded state in other EMFolderTree instances. */ + gtk_tree_store_set ( + GTK_TREE_STORE (model), iter, + COL_BOOL_EXPANDED, FALSE, -1); + + key = STATE_KEY_EXPANDED; + if (is_store) + group_name = g_strdup_printf ("Store %s", uri); + else + group_name = g_strdup_printf ("Folder %s", uri); + + g_key_file_set_boolean (key_file, group_name, key, FALSE); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + g_free (uri); +} + +static void +mail_shell_sidebar_row_expanded_cb (EShellSidebar *shell_sidebar, + GtkTreeIter *unused, + GtkTreePath *path, + GtkTreeView *tree_view) +{ + EShellView *shell_view; + GtkTreeModel *model; + GKeyFile *key_file; + const gchar *key; + gboolean is_folder; + gboolean is_store; + gchar *group_name; + gchar *uri; + + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + key_file = e_shell_view_get_state_key_file (shell_view); + + path = gtk_tree_path_copy (path); + model = gtk_tree_view_get_model (tree_view); + + /* Expand the node and all ancestors. */ + while (gtk_tree_path_get_depth (path) > 0) { + GtkTreeIter iter; + + gtk_tree_model_get_iter (model, &iter, path); + + gtk_tree_model_get ( + model, &iter, + COL_STRING_URI, &uri, + COL_BOOL_IS_STORE, &is_store, + COL_BOOL_IS_FOLDER, &is_folder, -1); + + g_return_if_fail (is_store || is_folder); + + /* The expanded column is used to clone the sidebar's + * expanded state in other EMFolderTree instances. */ + gtk_tree_store_set ( + GTK_TREE_STORE (model), &iter, + COL_BOOL_EXPANDED, TRUE, -1); + + key = STATE_KEY_EXPANDED; + if (is_store) + group_name = g_strdup_printf ("Store %s", uri); + else + group_name = g_strdup_printf ("Folder %s", uri); + + g_key_file_set_boolean (key_file, group_name, key, TRUE); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + g_free (uri); + + gtk_tree_path_up (path); + } + + gtk_tree_path_free (path); +} + +static void +mail_shell_sidebar_model_loaded_row_cb (EMailShellSidebar *mail_shell_sidebar, + GtkTreePath *path, + GtkTreeIter *iter, + GtkTreeModel *model) +{ + EShellSidebar *shell_sidebar; + EShellView *shell_view; + GtkTreeView *tree_view; + GKeyFile *key_file; + gboolean is_folder; + gboolean is_store; + const gchar *key; + gchar *group_name; + gchar *uri; + gboolean expanded; + + shell_sidebar = E_SHELL_SIDEBAR (mail_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + key_file = e_shell_view_get_state_key_file (shell_view); + + tree_view = GTK_TREE_VIEW (mail_shell_sidebar->priv->folder_tree); + + gtk_tree_model_get ( + model, iter, + COL_STRING_URI, &uri, + COL_BOOL_IS_STORE, &is_store, + COL_BOOL_IS_FOLDER, &is_folder, -1); + + g_return_if_fail (is_store || is_folder); + + key = STATE_KEY_EXPANDED; + if (is_store) { + group_name = g_strdup_printf ("Store %s", uri); + expanded = TRUE; + } else { + group_name = g_strdup_printf ("Folder %s", uri); + expanded = FALSE; + } + + if (g_key_file_has_key (key_file, group_name, key, NULL)) + expanded = g_key_file_get_boolean ( + key_file, group_name, key, NULL); + + if (expanded) + gtk_tree_view_expand_row (tree_view, path, FALSE); + + g_free (group_name); + g_free (uri); +} + +static void mail_shell_sidebar_selection_changed_cb (EShellSidebar *shell_sidebar, GtkTreeSelection *selection) { @@ -53,21 +350,34 @@ mail_shell_sidebar_selection_changed_cb (EShellSidebar *shell_sidebar, EShellViewClass *shell_view_class; GtkTreeModel *model; GtkTreeIter iter; + GKeyFile *key_file; const gchar *icon_name; gchar *display_name = NULL; + gchar *uri = NULL; gboolean is_folder = FALSE; guint flags = 0; shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get ( model, &iter, COL_STRING_DISPLAY_NAME, &display_name, + COL_STRING_URI, &uri, COL_BOOL_IS_FOLDER, &is_folder, COL_UINT_FLAGS, &flags, -1); + if (uri != NULL) + g_key_file_set_string ( + key_file, "Folder Tree", "Selected", uri); + else + g_key_file_remove_key ( + key_file, "Folder Tree", "Selected", NULL); + + e_shell_view_set_state_dirty (shell_view); + if (is_folder) icon_name = em_folder_utils_get_icon_name (flags); else { @@ -79,6 +389,7 @@ mail_shell_sidebar_selection_changed_cb (EShellSidebar *shell_sidebar, e_shell_sidebar_set_primary_text (shell_sidebar, display_name); g_free (display_name); + g_free (uri); } static void @@ -128,8 +439,9 @@ mail_shell_sidebar_finalize (GObject *object) static void mail_shell_sidebar_constructed (GObject *object) { - EMailShellSidebarPrivate *priv; EMailShellBackend *mail_shell_backend; + EMailShellSidebar *mail_shell_sidebar; + EMFolderTreeModel *folder_tree_model; EShellSidebar *shell_sidebar; EShellBackend *shell_backend; EShellView *shell_view; @@ -138,15 +450,18 @@ mail_shell_sidebar_constructed (GObject *object) GtkWidget *container; GtkWidget *widget; - priv = E_MAIL_SHELL_SIDEBAR_GET_PRIVATE (object); - /* Chain up to parent's constructed method. */ G_OBJECT_CLASS (parent_class)->constructed (object); shell_sidebar = E_SHELL_SIDEBAR (object); shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); shell_backend = e_shell_view_get_shell_backend (shell_view); + mail_shell_backend = E_MAIL_SHELL_BACKEND (shell_backend); + mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (object); + + folder_tree_model = e_mail_shell_backend_get_folder_tree_model ( + mail_shell_backend); /* Build sidebar widgets. */ @@ -163,16 +478,33 @@ mail_shell_sidebar_constructed (GObject *object) container = widget; - widget = em_folder_tree_new (mail_shell_backend); + widget = em_folder_tree_new_with_model (folder_tree_model); em_folder_tree_set_excluded (EM_FOLDER_TREE (widget), 0); em_folder_tree_enable_drag_and_drop (EM_FOLDER_TREE (widget)); gtk_container_add (GTK_CONTAINER (container), widget); - priv->folder_tree = g_object_ref (widget); + mail_shell_sidebar->priv->folder_tree = g_object_ref (widget); gtk_widget_show (widget); - tree_view = GTK_TREE_VIEW (priv->folder_tree); + tree_view = GTK_TREE_VIEW (mail_shell_sidebar->priv->folder_tree); selection = gtk_tree_view_get_selection (tree_view); + mail_shell_sidebar_restore_state (mail_shell_sidebar); + + g_signal_connect_swapped ( + tree_view, "row-collapsed", + G_CALLBACK (mail_shell_sidebar_row_collapsed_cb), + shell_sidebar); + + g_signal_connect_swapped ( + tree_view, "row-expanded", + G_CALLBACK (mail_shell_sidebar_row_expanded_cb), + shell_sidebar); + + g_signal_connect_swapped ( + folder_tree_model, "loaded-row", + G_CALLBACK (mail_shell_sidebar_model_loaded_row_cb), + shell_sidebar); + g_signal_connect_swapped ( selection, "changed", G_CALLBACK (mail_shell_sidebar_selection_changed_cb), diff --git a/mail/e-mail-shell-view-actions.c b/mail/e-mail-shell-view-actions.c index 6e4e7fdc8c..e9cba33117 100644 --- a/mail/e-mail-shell-view-actions.c +++ b/mail/e-mail-shell-view-actions.c @@ -21,6 +21,10 @@ #include "e-mail-shell-view-private.h" +#define STATE_KEY_SEARCH_FILTER "SearchFilter" +#define STATE_KEY_SEARCH_SCOPE "SearchScope" +#define STATE_KEY_SEARCH_TEXT "SearchText" + static void action_gal_save_custom_view_cb (GtkAction *action, EMailShellView *mail_shell_view) @@ -135,6 +139,7 @@ action_mail_folder_copy_cb (GtkAction *action, EMailShellSidebar *mail_shell_sidebar; CamelFolderInfo *folder_info; EMFolderTree *folder_tree; + EMFolderTreeModel *model; mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); @@ -142,7 +147,8 @@ action_mail_folder_copy_cb (GtkAction *action, g_return_if_fail (folder_info != NULL); /* XXX Leaking folder_info? */ - em_folder_utils_copy_folder (folder_info, FALSE); + model = em_folder_tree_get_model (folder_tree); + em_folder_utils_copy_folder (model, folder_info, FALSE); } static void @@ -230,6 +236,7 @@ action_mail_folder_move_cb (GtkAction *action, EMailShellSidebar *mail_shell_sidebar; CamelFolderInfo *folder_info; EMFolderTree *folder_tree; + EMFolderTreeModel *model; mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); @@ -237,7 +244,8 @@ action_mail_folder_move_cb (GtkAction *action, g_return_if_fail (folder_info != NULL); /* XXX Leaking folder_info? */ - em_folder_utils_copy_folder (folder_info, TRUE); + model = em_folder_tree_get_model (folder_tree); + em_folder_utils_copy_folder (model, folder_info, TRUE); } static void @@ -600,19 +608,6 @@ action_mail_label_none_cb (GtkAction *action, } static void -action_mail_preview_cb (GtkToggleAction *action, - EMailShellView *mail_shell_view) -{ - EMailShellContent *mail_shell_content; - gboolean active; - - mail_shell_content = mail_shell_view->priv->mail_shell_content; - active = gtk_toggle_action_get_active (action); - - e_mail_shell_content_set_preview_visible (mail_shell_content, active); -} - -static void action_mail_search_cb (GtkRadioAction *action, GtkRadioAction *current, EMailShellView *mail_shell_view) @@ -883,14 +878,46 @@ action_search_execute_cb (GtkAction *action, EMailShellView *mail_shell_view) { EShellView *shell_view; + EShellContent *shell_content; + EMailReader *reader; + MessageList *message_list; + GKeyFile *key_file; + const gchar *folder_uri; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); /* All shell views respond to the activation of this action, * which is defined by EShellWindow. But only the currently * active shell view proceeds with executing the search. */ - shell_view = E_SHELL_VIEW (mail_shell_view); if (!e_shell_view_is_active (shell_view)) return; + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + + if (folder_uri != NULL) { + const gchar *key; + const gchar *string; + gchar *group_name; + + key = STATE_KEY_SEARCH_TEXT; + string = e_shell_content_get_search_text (shell_content); + group_name = g_strdup_printf ("Folder %s", folder_uri); + + if (string != NULL && *string != '\0') + g_key_file_set_string ( + key_file, group_name, key, string); + else + g_key_file_remove_key ( + key_file, group_name, key, NULL); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + } + e_mail_shell_view_execute_search (mail_shell_view); } @@ -899,7 +926,37 @@ action_search_filter_cb (GtkRadioAction *action, GtkRadioAction *current, EMailShellView *mail_shell_view) { - e_mail_shell_view_execute_search (mail_shell_view); + EShellView *shell_view; + EShellWindow *shell_window; + EMailReader *reader; + MessageList *message_list; + GKeyFile *key_file; + const gchar *folder_uri; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + + if (folder_uri != NULL) { + const gchar *key; + const gchar *string; + gchar *group_name; + + key = STATE_KEY_SEARCH_FILTER; + string = gtk_action_get_name (GTK_ACTION (current)); + group_name = g_strdup_printf ("Folder %s", folder_uri); + + g_key_file_set_string (key_file, group_name, key, string); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + } + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); } static void @@ -907,7 +964,37 @@ action_search_scope_cb (GtkRadioAction *action, GtkRadioAction *current, EMailShellView *mail_shell_view) { - e_mail_shell_view_execute_search (mail_shell_view); + EShellView *shell_view; + EShellWindow *shell_window; + EMailReader *reader; + MessageList *message_list; + GKeyFile *key_file; + const gchar *folder_uri; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + + if (folder_uri != NULL) { + const gchar *key; + const gchar *string; + gchar *group_name; + + key = STATE_KEY_SEARCH_SCOPE; + string = gtk_action_get_name (GTK_ACTION (current)); + group_name = g_strdup_printf ("Folder %s", folder_uri); + + g_key_file_set_string (key_file, group_name, key, string); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + } + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); } static GtkActionEntry mail_entries[] = { @@ -1205,7 +1292,7 @@ static GtkToggleActionEntry mail_toggle_entries[] = { N_("Show Message _Preview"), "<Control>m", N_("Show message preview pane"), - G_CALLBACK (action_mail_preview_cb), + NULL, /* Handled by property bindings */ TRUE }, { "mail-threads-group-by", @@ -1468,6 +1555,10 @@ e_mail_shell_view_actions_init (EMailShellView *mail_shell_view) dst_object = G_OBJECT (ACTION (MAIL_THREADS_EXPAND_ALL)); e_binding_new (src_object, "active", dst_object, "sensitive"); + e_mutual_binding_new ( + G_OBJECT (ACTION (MAIL_PREVIEW)), "active", + G_OBJECT (shell_content), "preview-visible"); + /* XXX The boolean sense of the GConf key is the inverse of * the menu item, so we have to maintain two properties. */ e_mutual_binding_new_with_negation ( diff --git a/mail/e-mail-shell-view-private.c b/mail/e-mail-shell-view-private.c index 9026c8ed60..d4fd3c7564 100644 --- a/mail/e-mail-shell-view-private.c +++ b/mail/e-mail-shell-view-private.c @@ -30,27 +30,23 @@ mail_shell_view_folder_tree_selected_cb (EMailShellView *mail_shell_view, guint32 flags, EMFolderTree *folder_tree) { + EShellView *shell_view; EMailReader *reader; gboolean folder_selected; + shell_view = E_SHELL_VIEW (mail_shell_view); reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); folder_selected = !(flags & CAMEL_FOLDER_NOSELECT) && full_name != NULL; - if (folder_selected) { - EMFolderTreeModel *model; - - model = em_folder_tree_get_model (folder_tree); - em_folder_tree_model_set_selected (model, uri); - em_folder_tree_model_save_state (model); - + if (folder_selected) e_mail_reader_set_folder_uri (reader, uri); - } else + else e_mail_reader_set_folder (reader, NULL, NULL); - e_shell_view_update_actions (E_SHELL_VIEW (mail_shell_view)); + e_shell_view_update_actions (shell_view); } static void @@ -122,7 +118,8 @@ mail_shell_view_message_list_right_click_cb (EShellView *shell_view, } static void -mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view) +mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view, + EMailReader *reader) { EMailShellContent *mail_shell_content; @@ -218,7 +215,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view) EShellSidebar *shell_sidebar; EShellWindow *shell_window; EMFormatHTMLDisplay *html_display; - EMFolderTreeModel *folder_tree_model; EMFolderTree *folder_tree; RuleContext *context; FilterRule *rule = NULL; @@ -229,7 +225,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view) GtkHTML *html; const gchar *source; guint merge_id; - gchar *uri; gint ii = 0; shell_view = E_SHELL_VIEW (mail_shell_view); @@ -298,6 +293,11 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view) mail_shell_view); g_signal_connect_swapped ( + reader, "folder-loaded", + G_CALLBACK (e_mail_shell_view_restore_state), + mail_shell_view); + + g_signal_connect_swapped ( tree_model, "row-changed", G_CALLBACK (e_mail_shell_view_update_search_filter), mail_shell_view); @@ -336,24 +336,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view) priv->search_rules[ii++] = g_object_ref (rule); } g_assert (ii == MAIL_NUM_SEARCH_RULES); - - /* Restore the previously selected folder. */ - folder_tree_model = em_folder_tree_get_model (folder_tree); - uri = em_folder_tree_model_get_selected (folder_tree_model); - if (uri != NULL) { - gboolean expanded; - - expanded = em_folder_tree_model_get_expanded_uri ( - folder_tree_model, uri); - em_folder_tree_set_selected (folder_tree, uri, FALSE); - e_mail_reader_set_folder_uri (reader, uri); - - if (!expanded) - em_folder_tree_model_set_expanded_uri ( - folder_tree_model, uri, expanded); - - g_free (uri); - } } void @@ -377,6 +359,33 @@ e_mail_shell_view_private_finalize (EMailShellView *mail_shell_view) } void +e_mail_shell_view_restore_state (EMailShellView *mail_shell_view) +{ + EShellView *shell_view; + EShellContent *shell_content; + EMailReader *reader; + MessageList *message_list; + const gchar *folder_uri; + gchar *group_name; + + /* XXX Move this to EMailShellContent. */ + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + + reader = E_MAIL_READER (shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + g_return_if_fail (folder_uri != NULL); + + group_name = g_strdup_printf ("Folder %s", folder_uri); + e_shell_content_restore_state (shell_content, group_name); + g_free (group_name); +} + +void e_mail_shell_view_execute_search (EMailShellView *mail_shell_view) { EShell *shell; @@ -538,7 +547,7 @@ filter: temp = g_strdup_printf ( "(and %s (match-all " "(> (get-received-date) " - "(- (get_current_date) 86400))))", + "(- (get-current-date) 86400))))", query); g_free (query); query = temp; @@ -555,7 +564,7 @@ filter: temp = g_strdup_printf ( "(and %s (match-all " "(> (get-received-date) " - "(- (get_current_date) 432000))))", + "(- (get-current-date) 432000))))", query); g_free (query); query = temp; diff --git a/mail/e-mail-shell-view-private.h b/mail/e-mail-shell-view-private.h index 13c76c7cfe..f6646cabc2 100644 --- a/mail/e-mail-shell-view-private.h +++ b/mail/e-mail-shell-view-private.h @@ -149,6 +149,8 @@ void e_mail_shell_view_private_finalize void e_mail_shell_view_actions_init (EMailShellView *mail_shell_view); +void e_mail_shell_view_restore_state + (EMailShellView *mail_shell_view); void e_mail_shell_view_execute_search (EMailShellView *mail_shell_view); void e_mail_shell_view_create_filter_from_selected diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c index 74438b2043..6c35a4e7e8 100644 --- a/mail/em-composer-utils.c +++ b/mail/em-composer-utils.c @@ -117,8 +117,6 @@ emcs_set_drafts_info (struct emcs_t *emcs, camel_object_ref (drafts_folder); emcs->drafts_folder = drafts_folder; emcs->drafts_uid = g_strdup (drafts_uid); - - g_debug ("%s", G_STRFUNC); } static void @@ -141,8 +139,6 @@ emcs_set_folder_info (struct emcs_t *emcs, emcs->uid = g_strdup (uid); emcs->flags = flags; emcs->set = set; - - g_debug ("%s", G_STRFUNC); } static void @@ -2412,8 +2408,9 @@ post_header_clicked_cb (EComposerPostHeader *header, model = e_mail_shell_backend_get_folder_tree_model (mail_shell_backend); folder_tree = em_folder_tree_new_with_model (model); - em_folder_tree_set_multiselect ( - EM_FOLDER_TREE (folder_tree), TRUE); + em_folder_tree_clone_expanded (EM_FOLDER_TREE (folder_tree)); + em_folder_tree_set_multiselect (EM_FOLDER_TREE (folder_tree), TRUE); + em_folder_tree_set_excluded ( EM_FOLDER_TREE (folder_tree), EMFT_EXCLUDE_NOSELECT | diff --git a/mail/em-folder-selection-button.c b/mail/em-folder-selection-button.c index 933393ae80..ecdccc0af7 100644 --- a/mail/em-folder-selection-button.c +++ b/mail/em-folder-selection-button.c @@ -256,6 +256,7 @@ folder_selection_button_clicked (GtkButton *button) emft = (EMFolderTree *) em_folder_tree_new_with_model (priv->model); + em_folder_tree_clone_expanded (emft); em_folder_tree_set_multiselect (emft, priv->multiple_select); em_folder_tree_set_excluded ( emft, EMFT_EXCLUDE_NOSELECT | diff --git a/mail/em-folder-selection.c b/mail/em-folder-selection.c index a0528554d6..56c11bcfdf 100644 --- a/mail/em-folder-selection.c +++ b/mail/em-folder-selection.c @@ -33,53 +33,48 @@ #include "em-folder-selector.h" #include "em-folder-selection.h" -#include "e-mail-shell-backend.h" - /* TODO: rmeove this file, it could just go on em-folder-selection or em-utils */ -struct _select_folder_data { - void (*done) (const gchar *uri, gpointer data); - gpointer data; -}; - -static void -emfs_selector_response(EMFolderSelector *emfs, gint response, struct _select_folder_data *d) -{ - if (response == GTK_RESPONSE_OK) { - const gchar *uri = em_folder_selector_get_selected_uri(emfs); - - d->done(uri, d->data); - } - - gtk_widget_destroy((GtkWidget *)emfs); -} - void -em_select_folder (GtkWindow *parent_window, const gchar *title, const gchar *oklabel, const gchar *default_uri, - EMFTExcludeFunc exclude, - void (*done) (const gchar *uri, gpointer user_data), gpointer user_data) +em_select_folder (EMFolderTreeModel *model, + const gchar *title, + const gchar *oklabel, + const gchar *default_uri, + EMFTExcludeFunc exclude, + void (*done) (const gchar *uri, gpointer user_data), + gpointer user_data) { - struct _select_folder_data *d; - EMFolderTreeModel *model; GtkWidget *dialog; EMFolderTree *emft; - model = e_mail_shell_backend_get_folder_tree_model (global_mail_shell_backend); + g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); + g_return_if_fail (done != NULL); + + /* XXX Do we leak this reference? */ emft = (EMFolderTree *) em_folder_tree_new_with_model (model); + em_folder_tree_clone_expanded (emft); + if (exclude) - em_folder_tree_set_excluded_func(emft, exclude, user_data); + em_folder_tree_set_excluded_func (emft, exclude, user_data); else - em_folder_tree_set_excluded (emft, EMFT_EXCLUDE_NOSELECT|EMFT_EXCLUDE_VIRTUAL|EMFT_EXCLUDE_VTRASH); + em_folder_tree_set_excluded ( + emft, EMFT_EXCLUDE_NOSELECT | + EMFT_EXCLUDE_VIRTUAL | EMFT_EXCLUDE_VTRASH); - dialog = em_folder_selector_new(emft, EM_FOLDER_SELECTOR_CAN_CREATE, title, NULL, oklabel); + dialog = em_folder_selector_new ( + emft, EM_FOLDER_SELECTOR_CAN_CREATE, title, NULL, oklabel); - d = g_malloc0(sizeof(*d)); - d->data = user_data; - d->done = done; - g_signal_connect(dialog, "response", G_CALLBACK (emfs_selector_response), d); - g_object_set_data_full((GObject *)dialog, "e-select-data", d, (GDestroyNotify)g_free); - gtk_widget_show(dialog); + if (default_uri != NULL) + em_folder_selector_set_selected ( + EM_FOLDER_SELECTOR (dialog), default_uri); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + const gchar *uri; + + uri = em_folder_selector_get_selected_uri ( + EM_FOLDER_SELECTOR (dialog)); + done (uri, user_data); + } - if (default_uri) - em_folder_selector_set_selected((EMFolderSelector *)dialog, default_uri); + gtk_widget_destroy (dialog); } diff --git a/mail/em-folder-selection.h b/mail/em-folder-selection.h index 098fa2b227..96d14cbc19 100644 --- a/mail/em-folder-selection.h +++ b/mail/em-folder-selection.h @@ -24,22 +24,18 @@ #ifndef EM_FOLDER_SELECTION_H #define EM_FOLDER_SELECTION_H -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus */ +#include <mail/em-folder-tree-model.h> -#include "em-folder-tree.h" +G_BEGIN_DECLS -struct _GtkWindow; +void em_select_folder (EMFolderTreeModel *model, + const gchar *title, + const gchar *oklabel, + const gchar *default_uri, + EMFTExcludeFunc exclude, + void (*done)(const gchar *uri, gpointer data), + gpointer data); -void em_select_folder (struct _GtkWindow *parent_window, const gchar *title, const gchar *oklabel, const gchar *default_uri, - EMFTExcludeFunc exclude, - void (*done)(const gchar *uri, gpointer data), - gpointer data); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ +G_END_DECLS #endif /* EM_FOLDER_SELECTION_H */ diff --git a/mail/em-folder-tree-model.c b/mail/em-folder-tree-model.c index f78405a7c7..f1a667b9f4 100644 --- a/mail/em-folder-tree-model.c +++ b/mail/em-folder-tree-model.c @@ -107,38 +107,6 @@ store_info_free (EMFolderTreeModelStoreInfo *si) g_free (si); } -static void -folder_tree_model_load_state (EMFolderTreeModel *model, - const gchar *filename) -{ - xmlNodePtr root, node; - - if (model->state) - xmlFreeDoc (model->state); - - if ((model->state = e_xml_parse_file (filename)) != NULL) { - node = xmlDocGetRootElement (model->state); - if (!node || strcmp ((gchar *)node->name, "tree-state") != 0) { - /* it is not expected XML file, thus free it and use the default */ - xmlFreeDoc (model->state); - } else - return; - } - - /* setup some defaults - expand "Local Folders" and "Search Folders" */ - model->state = xmlNewDoc ((const guchar *)"1.0"); - root = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL); - xmlDocSetRootElement (model->state, root); - - node = xmlNewChild (root, NULL, (const guchar *)"node", NULL); - xmlSetProp (node, (const guchar *)"name", (const guchar *)"local"); - xmlSetProp (node, (const guchar *)"expand", (const guchar *)"true"); - - node = xmlNewChild (root, NULL, (const guchar *)"node", NULL); - xmlSetProp (node, (const guchar *)"name", (const guchar *)"vfolder"); - xmlSetProp (node, (const guchar *)"expand", (const guchar *)"true"); -} - static gint folder_tree_model_sort (GtkTreeModel *model, GtkTreeIter *a, @@ -256,10 +224,6 @@ folder_tree_model_finalize (GObject *object) { EMFolderTreeModel *model = (EMFolderTreeModel *) object; - g_free (model->filename); - if (model->state) - xmlFreeDoc (model->state); - g_hash_table_destroy (model->store_hash); g_hash_table_destroy (model->uri_hash); @@ -272,23 +236,6 @@ folder_tree_model_finalize (GObject *object) } static void -folder_tree_model_constructed (GObject *object) -{ - EMFolderTreeModel *model = EM_FOLDER_TREE_MODEL (object); - EShellBackend *shell_backend; - const gchar *config_dir; - gchar *filename; - - shell_backend = model->priv->shell_backend; - config_dir = e_shell_backend_get_config_dir (shell_backend); - - filename = g_build_filename ( - config_dir, "folder-tree-expand-state.xml", NULL); - folder_tree_model_load_state (model, filename); - model->filename = filename; -} - -static void folder_tree_model_class_init (EMFolderTreeModelClass *class) { GObjectClass *object_class; @@ -300,7 +247,6 @@ folder_tree_model_class_init (EMFolderTreeModelClass *class) object_class->set_property = folder_tree_model_set_property; object_class->get_property = folder_tree_model_get_property; object_class->finalize = folder_tree_model_finalize; - object_class->constructed = folder_tree_model_constructed; g_object_class_install_property ( object_class, @@ -361,6 +307,7 @@ folder_tree_model_init (EMFolderTreeModel *model) G_TYPE_STRING, /* uri */ G_TYPE_UINT, /* unread count */ G_TYPE_UINT, /* flags */ + G_TYPE_BOOLEAN, /* is expanded in sidebar */ G_TYPE_BOOLEAN, /* is a store node */ G_TYPE_BOOLEAN, /* is a folder node */ G_TYPE_BOOLEAN, /* has not-yet-loaded subfolders */ @@ -399,19 +346,6 @@ folder_tree_model_init (EMFolderTreeModel *model) model->account_removed_id = g_signal_connect ( model->accounts, "account-removed", G_CALLBACK (account_removed), model); - //g_signal_connect ( - // model, "row-changed", - // G_CALLBACK (emft_model_unread_count_changed), NULL); -} - -static void -tree_model_iface_init (GtkTreeModelIface *iface) -{ -} - -static void -tree_sortable_iface_init (GtkTreeSortableIface *iface) -{ } GType @@ -433,41 +367,20 @@ em_folder_tree_model_get_type (void) NULL /* value_table */ }; - static const GInterfaceInfo tree_model_info = { - (GInterfaceInitFunc) tree_model_iface_init, - NULL, - NULL - }; - - static const GInterfaceInfo sortable_info = { - (GInterfaceInitFunc) tree_sortable_iface_init, - NULL, - NULL - }; - type = g_type_register_static ( GTK_TYPE_TREE_STORE, "EMFolderTreeModel", &type_info, 0); - - g_type_add_interface_static ( - type, GTK_TYPE_TREE_MODEL, &tree_model_info); - g_type_add_interface_static ( - type, GTK_TYPE_TREE_SORTABLE, &sortable_info); } return type; } - static void emft_model_unread_count_changed (GtkTreeModel *model, GtkTreeIter *iter) { GtkTreeIter parent_iter; GtkTreeIter child_iter = *iter; - g_signal_handlers_block_by_func ( - model, emft_model_unread_count_changed, NULL); - /* Folders are displayed with a bold weight to indicate that they contain unread messages. We signal that parent rows have changed here to update them. */ @@ -480,14 +393,8 @@ emft_model_unread_count_changed (GtkTreeModel *model, GtkTreeIter *iter) gtk_tree_path_free (parent_path); child_iter = parent_iter; } - - g_signal_handlers_unblock_by_func ( - model, emft_model_unread_count_changed, NULL); } - - - EMFolderTreeModel * em_folder_tree_model_new (EMailShellBackend *mail_shell_backend) { @@ -1012,7 +919,7 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store, con camel_object_ref (store); si->store = store; si->account = account; - si->row = row; + si->row = gtk_tree_row_reference_copy (row); si->full_hash = g_hash_table_new_full ( g_str_hash, g_str_equal, (GDestroyNotify) g_free, @@ -1020,6 +927,9 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store, con g_hash_table_insert (model->store_hash, store, si); g_hash_table_insert (model->account_hash, account, si); + /* Transfer ownership of the URI and GtkTreeRowReference. */ + g_hash_table_insert (model->uri_hash, uri, row); + /* each store has folders... but we don't load them until the user demands them */ root = iter; gtk_tree_store_append ((GtkTreeStore *) model, &iter, &root); @@ -1034,8 +944,6 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store, con COL_UINT_UNREAD_LAST_SEL, 0, -1); - g_free (uri); - /* listen to store events */ #define CAMEL_CALLBACK(func) ((CamelObjectEventHookFunc) func) si->created_id = camel_object_hook_event (store, "folder_created", CAMEL_CALLBACK (folder_created_cb), model); @@ -1133,275 +1041,6 @@ em_folder_tree_model_remove_store (EMFolderTreeModel *model, CamelStore *store) em_folder_tree_model_remove_folders (model, si, &iter); } - -static xmlNodePtr -find_xml_node (xmlNodePtr root, const gchar *name) -{ - xmlNodePtr node; - gchar *nname; - - node = root->children; - while (node != NULL) { - if (!strcmp ((gchar *)node->name, "node")) { - nname = (gchar *)xmlGetProp (node, (const guchar *)"name"); - if (nname && !strcmp (nname, name)) { - xmlFree (nname); - return node; - } - - xmlFree (nname); - } - - node = node->next; - } - - return node; -} - -gboolean -em_folder_tree_model_get_expanded (EMFolderTreeModel *model, const gchar *key) -{ - xmlNodePtr node; - const gchar *name; - gchar *buf, *p; - - /* This code needs to be rewritten. - First it doesn't belong on the model - Second, it shouldn't use an xml tree to store a bit table in memory! */ - - node = model->state ? model->state->children : NULL; - if (!node || strcmp ((gchar *)node->name, "tree-state") != 0) - return FALSE; - - name = buf = g_alloca (strlen (key) + 1); - p = g_stpcpy (buf, key); - if (p[-1] == '/') - p[-1] = '\0'; - p = NULL; - - do { - if ((p = strchr (name, '/'))) - *p = '\0'; - - if ((node = find_xml_node (node, name))) { - gboolean expanded; - - buf = (gchar *)xmlGetProp (node, (const guchar *)"expand"); - expanded = buf && !strcmp ((gchar *)buf, "true"); - xmlFree (buf); - - if (!expanded || p == NULL) - return expanded; - } - - name = p ? p + 1 : NULL; - } while (name && node); - - return FALSE; -} - - -void -em_folder_tree_model_set_expanded (EMFolderTreeModel *model, const gchar *key, gboolean expanded) -{ - xmlNodePtr node, parent; - const gchar *name; - gchar *buf, *p; - - if (model->state == NULL) - model->state = xmlNewDoc ((const guchar *)"1.0"); - - if (!model->state->children) { - node = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL); - xmlDocSetRootElement (model->state, node); - } else { - node = model->state->children; - } - - name = buf = g_alloca (strlen (key) + 1); - p = g_stpcpy (buf, key); - if (p[-1] == '/') - p[-1] = '\0'; - p = NULL; - - do { - parent = node; - if ((p = strchr (name, '/'))) - *p = '\0'; - - if (!(node = find_xml_node (node, name))) { - if (!expanded) { - /* node doesn't exist, so we don't need to set expanded to FALSE */ - return; - } - - /* node (or parent node) doesn't exist, need to add it */ - node = xmlNewChild (parent, NULL, (const guchar *)"node", NULL); - xmlSetProp (node, (const guchar *)"name", (guchar *)name); - } - - xmlSetProp (node, (const guchar *)"expand", (const guchar *)(expanded || p ? "true" : "false")); - - name = p ? p + 1 : NULL; - } while (name); -} - -/** - * emftm_uri_to_key - * Converts uri to key used in functions like em_folder_tree_model_[s/g]et_expanded. - * @param uri Uri to be converted. - * @return Key of the uri or NULL, if failed. Returned value should be clear by g_free. - **/ -static gchar * -emftm_uri_to_key (const gchar *uri) -{ - CamelException ex = { 0 }; - CamelStore *store; - CamelURL *url; - gchar *key; - - if (!uri) - return NULL; - - store = (CamelStore *)camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex); - camel_exception_clear(&ex); - - url = camel_url_new (uri, NULL); - - if (store == NULL || url == NULL) { - key = NULL; - } else { - const gchar *path; - EAccount *account; - - if (((CamelService *)store)->provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH) - path = url->fragment; - else - path = url->path && url->path[0]=='/' ? url->path+1:url->path; - - if (path == NULL) - path = ""; - - if ( (account = mail_config_get_account_by_source_url (uri)) ) - key = g_strdup_printf ("%s/%s", account->uid, path); - else if (CAMEL_IS_VEE_STORE (store)) - key = g_strdup_printf ("vfolder/%s", path); - else - key = g_strdup_printf ("local/%s", path); - } - - if (url) - camel_url_free (url); - - if (store) - camel_object_unref (store); - - return key; -} - -/** - * em_folder_tree_model_get_expanded_uri - * Same as @ref em_folder_tree_model_get_expanded, but here we use uri, not key for node. - **/ -gboolean -em_folder_tree_model_get_expanded_uri (EMFolderTreeModel *model, const gchar *uri) -{ - gchar *key; - gboolean expanded; - - g_return_val_if_fail (model != NULL, FALSE); - g_return_val_if_fail (uri != NULL, FALSE); - - key = emftm_uri_to_key (uri); - expanded = key && em_folder_tree_model_get_expanded (model, key); - - g_free (key); - - return expanded; -} - -/** - * em_folder_tree_model_set_expanded_uri - * Same as @ref em_folder_tree_model_set_expanded, but here we use uri, not key for node. - **/ -void -em_folder_tree_model_set_expanded_uri (EMFolderTreeModel *model, const gchar *uri, gboolean expanded) -{ - gchar *key; - - g_return_if_fail (model != NULL); - g_return_if_fail (uri != NULL); - - key = emftm_uri_to_key (uri); - if (key) - em_folder_tree_model_set_expanded (model, key, expanded); - - g_free (key); -} - -void -em_folder_tree_model_save_state (EMFolderTreeModel *model) -{ - gchar *dirname; - - if (model->state == NULL) - return; - - dirname = g_path_get_dirname (model->filename); - if (g_mkdir_with_parents (dirname, 0777) == -1 && errno != EEXIST) { - g_free (dirname); - return; - } - - g_free (dirname); - - e_xml_save_file (model->filename, model->state); -} - - -static void -expand_foreach_r (EMFolderTreeModel *model, xmlNodePtr parent, const gchar *dirname, EMFTModelExpandFunc func, gpointer user_data) -{ - xmlNodePtr node = parent->children; - gchar *path, *name, *expand; - - while (node != NULL) { - if (!strcmp ((gchar *)node->name, "node")) { - name = (gchar *)xmlGetProp (node, (const guchar *)"name"); - expand = (gchar *)xmlGetProp (node, (const guchar *)"expand"); - - if (expand && name && !strcmp ((gchar *)expand, "true")) { - if (dirname) - path = g_strdup_printf ("%s/%s", dirname, name); - else - path = g_strdup (name); - - func (model, path, user_data); - if (node->children) - expand_foreach_r (model, node, path, func, user_data); - g_free (path); - } - - xmlFree (expand); - xmlFree (name); - } - - node = node->next; - } -} - -void -em_folder_tree_model_expand_foreach (EMFolderTreeModel *model, EMFTModelExpandFunc func, gpointer user_data) -{ - xmlNodePtr root; - - root = model->state ? model->state->children : NULL; - if (!root || !root->children || strcmp ((gchar *)root->name, "tree-state") != 0) - return; - - expand_foreach_r (model, root, NULL, func, user_data); -} - gboolean em_folder_tree_model_is_type_inbox (EMFolderTreeModel *model, CamelStore *store, const gchar *full) { @@ -1487,7 +1126,6 @@ em_folder_tree_model_set_unread_count (EMFolderTreeModel *model, CamelStore *sto GtkTreePath *tree_path; GtkTreeIter iter; guint old_unread = 0; - gchar *uri, *sel_uri; g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); g_return_if_fail (CAMEL_IS_STORE (store)); @@ -1516,82 +1154,15 @@ em_folder_tree_model_set_unread_count (EMFolderTreeModel *model, CamelStore *sto gtk_tree_path_free (tree_path); - sel_uri = em_folder_tree_model_get_selected (model); gtk_tree_model_get ( GTK_TREE_MODEL (model), &iter, - COL_UINT_UNREAD_LAST_SEL, &old_unread, - COL_STRING_URI, &uri, -1); - if (!(g_strcmp0 (sel_uri, uri) != 0 && unread > old_unread)) - old_unread = unread; + COL_UINT_UNREAD_LAST_SEL, &old_unread, -1); + gtk_tree_store_set ( GTK_TREE_STORE (model), &iter, COL_UINT_UNREAD, unread, - COL_UINT_UNREAD_LAST_SEL, old_unread, -1); - - g_free (uri); - g_free (sel_uri); + COL_UINT_UNREAD_LAST_SEL, MIN (old_unread, unread), -1); /* May be this is from where we should propagate unread count to parents etc. */ emft_model_unread_count_changed (GTK_TREE_MODEL (model), &iter); } - - -gchar * -em_folder_tree_model_get_selected (EMFolderTreeModel *model) -{ - xmlNodePtr node; - gchar *buf, *uri; - - node = model->state ? model->state->children : NULL; - if (!node || strcmp ((gchar *)node->name, "tree-state") != 0) - return NULL; - - node = node->children; - while (node != NULL) { - if (!strcmp ((gchar *)node->name, "selected")) - break; - node = node->next; - } - - if (node == NULL) - return NULL; - - buf = (gchar *)xmlGetProp (node, (guchar *)"uri"); - uri = g_strdup (buf); - xmlFree (buf); - - if (uri && !*uri) { - g_free (uri); - return NULL; - } - return uri; -} - - -void -em_folder_tree_model_set_selected (EMFolderTreeModel *model, const gchar *uri) -{ - xmlNodePtr root, node; - - if (model->state == NULL) - model->state = xmlNewDoc ((guchar *)"1.0"); - - if (!model->state->children) { - root = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL); - xmlDocSetRootElement (model->state, root); - } else { - root = model->state->children; - } - - node = root->children; - while (node != NULL) { - if (!strcmp ((gchar *)node->name, "selected")) - break; - node = node->next; - } - - if (node == NULL) - node = xmlNewChild (root, NULL, (const guchar *)"selected", NULL); - - xmlSetProp (node, (const guchar *)"uri", (guchar *)uri); -} diff --git a/mail/em-folder-tree-model.h b/mail/em-folder-tree-model.h index 418248acd6..f8952d0982 100644 --- a/mail/em-folder-tree-model.h +++ b/mail/em-folder-tree-model.h @@ -24,7 +24,6 @@ #define EM_FOLDER_TREE_MODEL_H #include <gtk/gtk.h> -#include <libxml/tree.h> #include <camel/camel-store.h> #include <libedataserver/e-account-list.h> #include <mail/e-mail-shell-backend.h> @@ -64,6 +63,7 @@ enum { COL_UINT_UNREAD, /* unread count */ COL_UINT_FLAGS, /* FolderInfo.flags */ + COL_BOOL_EXPANDED, /* node is expanded in sidebar */ COL_BOOL_IS_STORE, /* toplevel store node? */ COL_BOOL_IS_FOLDER, /* folder (not a store) */ COL_BOOL_LOAD_SUBDIRS, /* %TRUE only if the store/folder @@ -93,9 +93,6 @@ struct _EMFolderTreeModel { GtkTreeStore parent; EMFolderTreeModelPrivate *priv; - gchar *filename; /* state filename */ - xmlDocPtr state; /* saved expanded state from previous session */ - GHashTable *store_hash; /* maps CamelStore's to store-info's */ GHashTable *uri_hash; /* maps URI's to GtkTreeRowReferences */ @@ -120,9 +117,6 @@ struct _EMFolderTreeModelClass { void (* folder_added) (EMFolderTreeModel *model, const gchar *path, const gchar *uri); - - void (* store_added) (EMFolderTreeModel *model, - const gchar *uri); }; @@ -142,20 +136,6 @@ void em_folder_tree_model_remove_store (EMFolderTreeModel *model, CamelStore *st void em_folder_tree_model_remove_folders (EMFolderTreeModel *model, struct _EMFolderTreeModelStoreInfo *si, GtkTreeIter *toplevel); -gchar *em_folder_tree_model_get_selected (EMFolderTreeModel *model); -void em_folder_tree_model_set_selected (EMFolderTreeModel *model, const gchar *uri); - -gboolean em_folder_tree_model_get_expanded (EMFolderTreeModel *model, const gchar *key); -void em_folder_tree_model_set_expanded (EMFolderTreeModel *model, const gchar *key, gboolean expanded); - -gboolean em_folder_tree_model_get_expanded_uri (EMFolderTreeModel *model, const gchar *uri); -void em_folder_tree_model_set_expanded_uri (EMFolderTreeModel *model, const gchar *uri, gboolean expanded); - -void em_folder_tree_model_save_state (EMFolderTreeModel *model); - -typedef void (* EMFTModelExpandFunc) (EMFolderTreeModel *model, const gchar *path, gpointer user_data); -void em_folder_tree_model_expand_foreach (EMFolderTreeModel *model, EMFTModelExpandFunc func, gpointer user_data); - void em_folder_tree_model_set_unread_count (EMFolderTreeModel *model, CamelStore *store, const gchar *path, gint unread); gboolean em_folder_tree_model_is_type_inbox (EMFolderTreeModel *model, CamelStore *store, const gchar *full); gchar * em_folder_tree_model_get_folder_name (EMFolderTreeModel *model, CamelStore *store, const gchar *full); diff --git a/mail/em-folder-tree.c b/mail/em-folder-tree.c index e6abb53c9a..de488c7258 100644 --- a/mail/em-folder-tree.c +++ b/mail/em-folder-tree.c @@ -104,8 +104,6 @@ struct _EMFolderTreePrivate { * we need to set it when we set the * selection */ - guint save_state_id; - guint autoscroll_id; guint autoexpand_id; GtkTreeRowReference *autoexpand_row; @@ -159,11 +157,6 @@ static guint signals[LAST_SIGNAL] = { 0 }; extern CamelSession *session; extern CamelStore *vfolder_store; -static gboolean emft_save_state (EMFolderTree *emft); -static void emft_queue_save_state (EMFolderTree *emft); - -static void emft_update_model_expanded_state (struct _EMFolderTreePrivate *priv, GtkTreeIter *iter, gboolean expanded); - static void emft_tree_row_activated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, EMFolderTree *emft); static gboolean emft_tree_test_collapse_row (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *path, EMFolderTree *emft); static void emft_tree_row_expanded (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *path, EMFolderTree *emft); @@ -230,11 +223,6 @@ em_folder_tree_destroy (GtkObject *object) priv->loaded_row_id = 0; } - if (priv->save_state_id != 0) { - g_source_remove (priv->save_state_id); - emft_save_state (EM_FOLDER_TREE (object)); - } - if (priv->autoscroll_id != 0) { g_source_remove (priv->autoscroll_id); priv->autoscroll_id = 0; @@ -418,10 +406,13 @@ render_icon (GtkTreeViewColumn *column, GtkTreeModel *model, GtkTreeIter *iter) { + GtkTreeSelection *selection; + GtkWidget *tree_view; GIcon *icon; guint unread; guint old_unread; gchar *icon_name; + gboolean row_selected; gtk_tree_model_get ( model, iter, @@ -434,8 +425,12 @@ render_icon (GtkTreeViewColumn *column, icon = g_themed_icon_new (icon_name); + tree_view = gtk_tree_view_column_get_tree_view (column); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); + row_selected = gtk_tree_selection_iter_is_selected (selection, iter); + /* Show an emblem if there's new mail. */ - if (unread > old_unread) { + if (!row_selected && unread > old_unread) { GIcon *temp_icon; GEmblem *emblem; @@ -547,22 +542,6 @@ em_folder_tree_construct (EMFolderTree *emft, EMFolderTreeModel *model) g_signal_connect (selection, "changed", G_CALLBACK (emft_tree_selection_changed), emft); } -GtkWidget * -em_folder_tree_new (EMailShellBackend *mail_shell_backend) -{ - EMFolderTreeModel *model; - EMFolderTree *emft; - - g_return_val_if_fail ( - E_IS_MAIL_SHELL_BACKEND (mail_shell_backend), NULL); - - model = e_mail_shell_backend_get_folder_tree_model (mail_shell_backend); - emft = (EMFolderTree *) em_folder_tree_new_with_model (model); - g_object_unref (model); - - return (GtkWidget *) emft; -} - /* NOTE: Removes and frees the selected uri structure */ static void emft_select_uri(EMFolderTree *emft, GtkTreePath *path, struct _selected_uri *u) @@ -670,7 +649,6 @@ emft_maybe_expand_row (EMFolderTreeModel *model, GtkTreePath *tree_path, GtkTree gchar *full_name; gchar *key; struct _selected_uri *u; - gboolean is_expanded; tree_view = GTK_TREE_VIEW (emft); @@ -691,21 +669,14 @@ emft_maybe_expand_row (EMFolderTreeModel *model, GtkTreePath *tree_path, GtkTree key = g_strdup_printf ("local/%s", full_name ? full_name : ""); } - is_expanded = em_folder_tree_model_get_expanded (model, key); u = g_hash_table_lookup(priv->select_uris_table, key); - if (is_expanded || u) { - if (is_expanded) { - gtk_tree_view_expand_to_path (tree_view, tree_path); - gtk_tree_view_expand_row (tree_view, tree_path, FALSE); - } else { - gchar *c = strrchr (key, '/'); + if (u) { + gchar *c = strrchr (key, '/'); - *c = '\0'; - emft_expand_node (model, key, emft); - } + *c = '\0'; + emft_expand_node (model, key, emft); - if (u) - emft_select_uri(emft, tree_path, u); + emft_select_uri(emft, tree_path, u); } g_free (full_name); @@ -722,8 +693,6 @@ em_folder_tree_new_with_model (EMFolderTreeModel *model) em_folder_tree_construct (emft, model); g_object_ref (model); - em_folder_tree_model_expand_foreach (model, (EMFTModelExpandFunc)emft_expand_node, emft); - emft->priv->loading_row_id = g_signal_connect (model, "loading-row", G_CALLBACK (emft_maybe_expand_row), emft); emft->priv->loaded_row_id = g_signal_connect (model, "loaded-row", G_CALLBACK (emft_maybe_expand_row), emft); @@ -733,6 +702,38 @@ em_folder_tree_new_with_model (EMFolderTreeModel *model) return (GtkWidget *) emft; } +static gboolean +folder_tree_clone_expanded (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + GtkTreeView *tree_view) +{ + gboolean expanded; + + gtk_tree_model_get (model, iter, COL_BOOL_EXPANDED, &expanded, -1); + + if (expanded) + gtk_tree_view_expand_row (tree_view, path, FALSE); + else + gtk_tree_view_collapse_row (tree_view, path); + + return FALSE; +} + +void +em_folder_tree_clone_expanded (EMFolderTree *emft) +{ + GtkTreeModel *model; + + g_return_if_fail (EM_IS_FOLDER_TREE (emft)); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (emft)); + + gtk_tree_model_foreach ( + model, (GtkTreeModelForeachFunc) + folder_tree_clone_expanded, emft); +} + static void tree_drag_begin (GtkWidget *widget, GdkDragContext *context, EMFolderTree *emft) { @@ -1532,11 +1533,14 @@ em_folder_tree_enable_drag_and_drop (EMFolderTree *emft) } void -em_folder_tree_set_multiselect (EMFolderTree *tree, gboolean mode) +em_folder_tree_set_multiselect (EMFolderTree *tree, + gboolean mode) { GtkTreeSelection *sel; GtkTreeView *tree_view; + g_return_if_fail (EM_IS_FOLDER_TREE (tree)); + tree_view = GTK_TREE_VIEW (tree); sel = gtk_tree_view_get_selection (tree_view); @@ -1544,13 +1548,22 @@ em_folder_tree_set_multiselect (EMFolderTree *tree, gboolean mode) gtk_tree_selection_set_mode (sel, mode ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE); } -void em_folder_tree_set_excluded(EMFolderTree *emft, guint32 flags) +void +em_folder_tree_set_excluded (EMFolderTree *emft, guint32 flags) { + g_return_if_fail (EM_IS_FOLDER_TREE (emft)); + emft->priv->excluded = flags; } -void em_folder_tree_set_excluded_func(EMFolderTree *emft, EMFTExcludeFunc exclude, gpointer data) +void +em_folder_tree_set_excluded_func (EMFolderTree *emft, + EMFTExcludeFunc exclude, + gpointer data) { + g_return_if_fail (EM_IS_FOLDER_TREE (emft)); + g_return_if_fail (exclude != NULL); + emft->priv->excluded_func = exclude; emft->priv->excluded_data = data; } @@ -1684,7 +1697,6 @@ em_folder_tree_set_selected_list (EMFolderTree *emft, GList *list, gboolean expa end = strrchr(expand_key, '/'); do { emft_expand_node(priv->model, expand_key, emft); - em_folder_tree_model_set_expanded(priv->model, expand_key, TRUE); *end = 0; end = strrchr(expand_key, '/'); } while (end); @@ -1816,14 +1828,11 @@ emft_get_folder_info__done (struct _EMFolderTreeGetFolderInfo *m) if (fi == NULL) { /* no children afterall... remove the "Loading..." placeholder node */ - emft_update_model_expanded_state (priv, &root, FALSE); - gtk_tree_store_remove (model, &iter); if (is_store) { path = gtk_tree_model_get_path ((GtkTreeModel *) model, &root); gtk_tree_view_collapse_row (tree_view, path); - emft_queue_save_state (m->emft); gtk_tree_path_free (path); return; } @@ -1842,7 +1851,6 @@ emft_get_folder_info__done (struct _EMFolderTreeGetFolderInfo *m) } gtk_tree_store_set (model, &root, COL_BOOL_LOAD_SUBDIRS, FALSE, -1); - emft_queue_save_state (m->emft); } static void @@ -1865,41 +1873,8 @@ static MailMsgInfo get_folder_info_info = { }; static void -emft_update_model_expanded_state (struct _EMFolderTreePrivate *priv, GtkTreeIter *iter, gboolean expanded) -{ - struct _EMFolderTreeModelStoreInfo *si; - gboolean is_store; - CamelStore *store; - EAccount *account; - gchar *full_name; - gchar *key; - - gtk_tree_model_get ((GtkTreeModel *) priv->model, iter, - COL_STRING_FULL_NAME, &full_name, - COL_POINTER_CAMEL_STORE, &store, - COL_BOOL_IS_STORE, &is_store, - -1); - - si = g_hash_table_lookup (priv->model->store_hash, store); - if ((account = e_get_account_by_name (si->display_name))) { - key = g_strdup_printf ("%s/%s", account->uid, full_name ? full_name : ""); - } else if (CAMEL_IS_VEE_STORE (store)) { - /* vfolder store */ - key = g_strdup_printf ("vfolder/%s", full_name ? full_name : ""); - } else { - /* local store */ - key = g_strdup_printf ("local/%s", full_name ? full_name : ""); - } - - em_folder_tree_model_set_expanded (priv->model, key, expanded); - g_free (full_name); - g_free (key); -} - -static void emft_tree_row_expanded (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *tree_path, EMFolderTree *emft) { - struct _EMFolderTreePrivate *priv = emft->priv; struct _EMFolderTreeGetFolderInfo *m; GtkTreeModel *model; CamelStore *store; @@ -1914,10 +1889,7 @@ emft_tree_row_expanded (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePath *t COL_BOOL_LOAD_SUBDIRS, &load, -1); - emft_update_model_expanded_state (priv, root, TRUE); - if (!load) { - emft_queue_save_state (emft); g_free (full_name); return; } @@ -1950,9 +1922,6 @@ emft_tree_test_collapse_row (GtkTreeView *treeview, GtkTreeIter *root, GtkTreePa gtk_tree_view_set_cursor (treeview, tree_path, NULL, FALSE); } - emft_update_model_expanded_state (emft->priv, root, FALSE); - emft_queue_save_state (emft); - return FALSE; } @@ -2081,9 +2050,7 @@ emft_tree_selection_changed (GtkTreeSelection *selection, EMFolderTree *emft) guint old_unread = 0; if (!emft_selection_get_selected (selection, &model, &iter)) { - em_folder_tree_model_set_selected (emft->priv->model, NULL); g_signal_emit (emft, signals[FOLDER_SELECTED], 0, NULL, NULL, 0); - emft_queue_save_state (emft); return; } @@ -2401,29 +2368,6 @@ em_folder_tree_get_model_storeinfo (EMFolderTree *emft, CamelStore *store) return si; } -static gboolean -emft_save_state (EMFolderTree *emft) -{ - struct _EMFolderTreePrivate *priv = emft->priv; - - em_folder_tree_model_save_state (priv->model); - priv->save_state_id = 0; - - return FALSE; -} - - -static void -emft_queue_save_state (EMFolderTree *emft) -{ - struct _EMFolderTreePrivate *priv = emft->priv; - - if (priv->save_state_id != 0) - return; - - priv->save_state_id = g_timeout_add_seconds (1, (GSourceFunc) emft_save_state, emft); -} - void em_folder_tree_set_skip_double_click (EMFolderTree *emft, gboolean skip) { diff --git a/mail/em-folder-tree.h b/mail/em-folder-tree.h index bcbac54b8d..4d32f00787 100644 --- a/mail/em-folder-tree.h +++ b/mail/em-folder-tree.h @@ -83,9 +83,8 @@ struct _EMFolderTreeClass { }; GType em_folder_tree_get_type (void); - -GtkWidget *em_folder_tree_new (EMailShellBackend *mail_shell_backend); GtkWidget *em_folder_tree_new_with_model (EMFolderTreeModel *model); +void em_folder_tree_clone_expanded (EMFolderTree *emft); void em_folder_tree_enable_drag_and_drop (EMFolderTree *emft); diff --git a/mail/em-folder-utils.c b/mail/em-folder-utils.c index f92e8e3115..4ab00e237e 100644 --- a/mail/em-folder-utils.c +++ b/mail/em-folder-utils.c @@ -355,17 +355,24 @@ emfu_copy_folder_exclude(EMFolderTree *tree, GtkTreeModel *model, GtkTreeIter *i /* FIXME: this interface references the folderinfo without copying it */ /* FIXME: these functions must be documented */ void -em_folder_utils_copy_folder(CamelFolderInfo *folderinfo, gint delete) +em_folder_utils_copy_folder (EMFolderTreeModel *model, + CamelFolderInfo *folderinfo, + gint delete) { struct _copy_folder_data *cfd; + g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); + g_return_if_fail (folderinfo != NULL); + cfd = g_malloc (sizeof (*cfd)); cfd->fi = folderinfo; cfd->delete = delete; - em_select_folder (NULL, _("Select folder"), delete?_("_Move"):_("C_opy"), - NULL, emfu_copy_folder_exclude, - emfu_copy_folder_selected, cfd); + em_select_folder ( + model, _("Select folder"), + delete ? _("_Move") : _("C_opy"), + NULL, emfu_copy_folder_exclude, + emfu_copy_folder_selected, cfd); } static void @@ -696,6 +703,7 @@ em_folder_utils_create_folder (CamelFolderInfo *folderinfo, EMFolderTree *emft, model = e_mail_shell_backend_get_folder_tree_model (global_mail_shell_backend); folder_tree = (EMFolderTree *) em_folder_tree_new_with_model (model); + em_folder_tree_clone_expanded (folder_tree); dialog = em_folder_selector_create_new (folder_tree, 0, _("Create Folder"), _("Specify where to create the folder:")); if (folderinfo != NULL) diff --git a/mail/em-folder-utils.h b/mail/em-folder-utils.h index db7ddb82fd..5d1333e9d0 100644 --- a/mail/em-folder-utils.h +++ b/mail/em-folder-utils.h @@ -40,14 +40,15 @@ gint em_folder_utils_copy_folders (CamelStore *fromstore, /* FIXME These API's are really busted. There is no consistency and * most rely on the wrong data. */ -void em_folder_utils_copy_folder (CamelFolderInfo *folderinfo, - gint delete); +void em_folder_utils_copy_folder (EMFolderTreeModel *model, + CamelFolderInfo *folderinfo, + gboolean delete); void em_folder_utils_delete_folder (CamelFolder *folder); void em_folder_utils_rename_folder (CamelFolder *folder); void em_folder_utils_create_folder (CamelFolderInfo *folderinfo, - EMFolderTree * emft, + EMFolderTree *emft, GtkWindow *parent); const gchar * em_folder_utils_get_icon_name (guint32 flags); diff --git a/plugins/groupwise-features/install-shared.c b/plugins/groupwise-features/install-shared.c index 67b1b5bdee..e9fd927894 100644 --- a/plugins/groupwise-features/install-shared.c +++ b/plugins/groupwise-features/install-shared.c @@ -166,6 +166,8 @@ accept_clicked(GnomeDruidPage *page, GtkWidget *druid, CamelMimeMessage *msg) accept_data = g_new0(struct AcceptData, 1); model = mail_component_peek_tree_model (NULL); folder_tree = (EMFolderTree *) em_folder_tree_new_with_model (model); + em_folder_tree_clone_expanded (folder_tree); + dialog = em_folder_selector_create_new (folder_tree, 0, _("Create folder"), _("Specify where to create the folder:")); uri = em_folder_tree_get_selected_uri(folder_tree); em_folder_selector_set_selected ((EMFolderSelector *) dialog, uri); diff --git a/plugins/groupwise-features/share-folder-common.c b/plugins/groupwise-features/share-folder-common.c index 50bb6fccf3..900d949251 100644 --- a/plugins/groupwise-features/share-folder-common.c +++ b/plugins/groupwise-features/share-folder-common.c @@ -383,6 +383,8 @@ create_shared_folder(EPopup *ep, EPopupItem *p, gpointer data) model = mail_component_peek_tree_model (mail_component_peek ()); folder_tree = (EMFolderTree *) em_folder_tree_new_with_model (model); + em_folder_tree_clone_expanded (folder_tree); + dialog = em_folder_selector_create_new (folder_tree, 0, _("Create folder"), _("Specify where to create the folder:")); uri = em_folder_tree_get_selected_uri(folder_tree); em_folder_selector_set_selected ((EMFolderSelector *) dialog, uri); diff --git a/shell/e-shell-content.c b/shell/e-shell-content.c index 216520fbb4..2fc9569bd2 100644 --- a/shell/e-shell-content.c +++ b/shell/e-shell-content.c @@ -24,6 +24,7 @@ #include <glib/gi18n.h> #include "e-util/e-binding.h" +#include "e-util/e-util.h" #include "filter/rule-editor.h" #include "widgets/misc/e-action-combo-box.h" #include "widgets/misc/e-hinted-entry.h" @@ -36,6 +37,10 @@ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_SHELL_CONTENT, EShellContentPrivate)) +#define STATE_KEY_SEARCH_FILTER "SearchFilter" +#define STATE_KEY_SEARCH_SCOPE "SearchScope" +#define STATE_KEY_SEARCH_TEXT "SearchText" + struct _EShellContentPrivate { gpointer shell_view; /* weak pointer */ @@ -55,8 +60,6 @@ struct _EShellContentPrivate { GtkWidget *search_entry; GtkWidget *scope_label; GtkWidget *scope_combo_box; - - GtkStateType search_state; }; enum { @@ -138,7 +141,10 @@ action_search_execute_cb (GtkAction *action, /* Direct the focus away from the search entry, so that a * focus-in event is required before the text can be changed. * This will reset the entry to the appropriate visual state. */ - gtk_widget_grab_focus (gtk_bin_get_child (GTK_BIN (shell_content))); + widget = gtk_bin_get_child (GTK_BIN (shell_content)); + if (GTK_IS_PANED (widget)) + widget = gtk_paned_get_child1 (GTK_PANED (widget)); + gtk_widget_grab_focus (widget); } static void @@ -897,7 +903,6 @@ shell_content_init (EShellContent *shell_content) gtk_label_set_mnemonic_widget (label, widget); gtk_box_pack_start (box, widget, TRUE, TRUE, 0); shell_content->priv->search_entry = g_object_ref (widget); - shell_content->priv->search_state = GTK_STATE_NORMAL; gtk_widget_show (widget); g_signal_connect_swapped ( @@ -1486,3 +1491,69 @@ exit: g_object_unref (rule); gtk_widget_destroy (dialog); } + +void +e_shell_content_restore_state (EShellContent *shell_content, + const gchar *group_name) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GKeyFile *key_file; + GtkAction *action; + GtkWidget *widget; + const gchar *key; + gchar *string; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + g_return_if_fail (group_name != NULL); + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); + + /* Changing the combo boxes triggers searches, so block + * the search action until the state is fully restored. */ + action = e_shell_window_get_action (shell_window, "search-execute"); + gtk_action_block_activate (action); + + key = STATE_KEY_SEARCH_FILTER; + string = g_key_file_get_string (key_file, group_name, key, NULL); + if (string != NULL && *string != '\0') + action = e_shell_window_get_action (shell_window, string); + else + action = NULL; + if (action != NULL) + gtk_action_activate (action); + else { + /* Pick the first combo box item. */ + widget = shell_content->priv->filter_combo_box; + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); + } + g_free (string); + + key = STATE_KEY_SEARCH_SCOPE; + string = g_key_file_get_string (key_file, group_name, key, NULL); + if (string != NULL && *string != '\0') + action = e_shell_window_get_action (shell_window, string); + else + action = NULL; + if (action != NULL) + gtk_action_activate (action); + else { + /* Pick the first combo box item. */ + widget = shell_content->priv->scope_combo_box; + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); + } + g_free (string); + + key = STATE_KEY_SEARCH_TEXT; + string = g_key_file_get_string (key_file, group_name, key, NULL); + e_shell_content_set_search_text (shell_content, string); + g_free (string); + + action = e_shell_window_get_action (shell_window, "search-execute"); + gtk_action_unblock_activate (action); + + /* Now execute the search. */ + gtk_action_activate (action); +} diff --git a/shell/e-shell-content.h b/shell/e-shell-content.h index af2799c7d4..8e754fff26 100644 --- a/shell/e-shell-content.h +++ b/shell/e-shell-content.h @@ -140,6 +140,8 @@ void e_shell_content_run_edit_searches_dialog (EShellContent *shell_content); void e_shell_content_run_save_search_dialog (EShellContent *shell_content); +void e_shell_content_restore_state (EShellContent *shell_content, + const gchar *group_name); G_END_DECLS diff --git a/shell/e-shell-view.c b/shell/e-shell-view.c index cf89442c7c..d1ab1af4a4 100644 --- a/shell/e-shell-view.c +++ b/shell/e-shell-view.c @@ -33,10 +33,15 @@ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_SHELL_VIEW, EShellViewPrivate)) +#define STATE_SAVE_TIMEOUT_SECONDS 3 + struct _EShellViewPrivate { gpointer shell_window; /* weak pointer */ + GKeyFile *state_key_file; + guint state_save_source_id; + gchar *title; gchar *view_id; gint page_num; @@ -118,6 +123,72 @@ shell_view_update_view_id (EShellView *shell_view, } static void +shell_view_load_state (EShellView *shell_view) +{ + EShellBackend *shell_backend; + GKeyFile *key_file; + const gchar *config_dir; + gchar *filename; + GError *error = NULL; + + shell_backend = e_shell_view_get_shell_backend (shell_view); + config_dir = e_shell_backend_get_config_dir (shell_backend); + filename = g_build_filename (config_dir, "state", NULL); + + /* XXX Should do this asynchronously. */ + key_file = shell_view->priv->state_key_file; + g_key_file_load_from_file (key_file, filename, 0, &error); + + if (error == NULL) + goto exit; + + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + g_warning ("%s", error->message); + + g_error_free (error); + +exit: + g_free (filename); +} + +static void +shell_view_save_state (EShellView *shell_view) +{ + EShellBackend *shell_backend; + GKeyFile *key_file; + const gchar *config_dir; + gchar *contents; + gchar *filename; + GError *error = NULL; + + shell_backend = e_shell_view_get_shell_backend (shell_view); + config_dir = e_shell_backend_get_config_dir (shell_backend); + filename = g_build_filename (config_dir, "state", NULL); + + /* XXX Should do this asynchronously. */ + key_file = shell_view->priv->state_key_file; + contents = g_key_file_to_data (key_file, NULL, NULL); + g_file_set_contents (filename, contents, -1, &error); + g_free (contents); + + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_free (filename); +} + +static gboolean +shell_view_state_timeout_cb (EShellView *shell_view) +{ + shell_view_save_state (shell_view); + shell_view->priv->state_save_source_id = 0; + + return FALSE; +} + +static void shell_view_emit_toggled (EShellView *shell_view) { g_signal_emit (shell_view, signals[TOGGLED], 0); @@ -267,6 +338,13 @@ shell_view_dispose (GObject *object) priv = E_SHELL_VIEW_GET_PRIVATE (object); + /* Expedite any pending state saves. */ + if (priv->state_save_source_id > 0) { + g_source_remove (priv->state_save_source_id); + priv->state_save_source_id = 0; + shell_view_save_state (E_SHELL_VIEW (object)); + } + if (priv->shell_window != NULL) { g_object_remove_weak_pointer ( G_OBJECT (priv->shell_window), &priv->shell_window); @@ -304,6 +382,8 @@ shell_view_finalize (GObject *object) priv = E_SHELL_VIEW_GET_PRIVATE (object); + g_key_file_free (priv->state_key_file); + g_free (priv->title); g_free (priv->view_id); @@ -330,6 +410,8 @@ shell_view_constructed (GObject *object) e_plugin_ui_register_manager (ui_manager, id, shell_view); + shell_view_load_state (shell_view); + /* Invoke factory methods. */ widget = class->new_shell_content (shell_view); @@ -598,6 +680,7 @@ shell_view_init (EShellView *shell_view, size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); shell_view->priv = E_SHELL_VIEW_GET_PRIVATE (shell_view); + shell_view->priv->state_key_file = g_key_file_new (); shell_view->priv->size_group = size_group; } @@ -947,6 +1030,48 @@ e_shell_view_get_shell_taskbar (EShellView *shell_view) } /** + * e_shell_view_get_state_key_file: + * @shell_view: an #EShellView + * + * Returns the #GKeyFile holding widget state data for @shell_view. + * + * Returns: the #GKeyFile for @shell_view + **/ +GKeyFile * +e_shell_view_get_state_key_file (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return shell_view->priv->state_key_file; +} + +/** + * e_shell_view_set_state_dirty: + * @shell_view: an #EShellView + * + * Marks the widget state data as modified (or "dirty") and schedules it + * to be saved to disk after a short delay. The delay caps the frequency + * of saving to disk. + **/ +void +e_shell_view_set_state_dirty (EShellView *shell_view) +{ + guint source_id; + + g_return_if_fail (E_IS_SHELL_VIEW (shell_view)); + + /* If a timeout is already scheduled, do nothing. */ + if (shell_view->priv->state_save_source_id > 0) + return; + + source_id = g_timeout_add_seconds ( + STATE_SAVE_TIMEOUT_SECONDS, (GSourceFunc) + shell_view_state_timeout_cb, shell_view); + + shell_view->priv->state_save_source_id = source_id; +} + +/** * e_shell_view_update_actions: * @shell_view: an #EShellView * diff --git a/shell/e-shell-view.h b/shell/e-shell-view.h index a3ca595fba..b323839478 100644 --- a/shell/e-shell-view.h +++ b/shell/e-shell-view.h @@ -171,6 +171,8 @@ EShellContent * e_shell_view_get_shell_content (EShellView *shell_view); EShellSidebar * e_shell_view_get_shell_sidebar (EShellView *shell_view); EShellTaskbar * e_shell_view_get_shell_taskbar (EShellView *shell_view); EShellWindow * e_shell_view_get_shell_window (EShellView *shell_view); +GKeyFile * e_shell_view_get_state_key_file (EShellView *shell_view); +void e_shell_view_set_state_dirty (EShellView *shell_view); void e_shell_view_update_actions (EShellView *shell_view); void e_shell_view_show_popup_menu (EShellView *shell_view, const gchar *widget_path, |