diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2009-06-10 11:15:20 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2009-06-13 22:49:05 +0800 |
commit | be8ee5393471a83b24aed4de1669afd723cb3168 (patch) | |
tree | f8a8e50d3830ec32f83fd2fa7e8815ebfc8316dc | |
parent | ed0cdbd79042a962ec229f739b4c3284b00a4dc0 (diff) | |
download | gsoc2013-evolution-be8ee5393471a83b24aed4de1669afd723cb3168.tar.gz gsoc2013-evolution-be8ee5393471a83b24aed4de1669afd723cb3168.tar.zst gsoc2013-evolution-be8ee5393471a83b24aed4de1669afd723cb3168.zip |
Use key files for tracking widget states.
Each EShellView now maintains a GKeyFile for recording disposable widget
state such as tree view path expansion, scroll bar positions, combo box
selections, etc. The EShellView records changes to the key file to
~/.evolution/<shell-backend>/config/state, and automatically restores
the GKeyFile at startup.
Currently only the mailer uses the key file, but it's intended to serve
all shell views. It replaces the use of Camel "cmeta" files, as well as
"et-expanded-*" and "folder-tree-expand-state.xml" files.
Also, the mailer's folder tree model now includes a column for tracking
which sidebar folders are expanded. Folder tree widgets appearing in
dialog windows can copy the sidebar's expanded state using
em_folder_tree_clone_expanded().
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, |