1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-store-tree-model.h>
36 #include <tny-gtk-header-list-model.h>
37 #include <tny-folder.h>
38 #include <tny-folder-store-observer.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <modest-tny-folder.h>
45 #include <modest-tny-local-folders-account.h>
46 #include <modest-marshal.h>
47 #include <modest-icon-names.h>
48 #include <modest-tny-account-store.h>
49 #include <modest-text-utils.h>
50 #include <modest-runtime.h>
51 #include "modest-folder-view.h"
52 #include <modest-dnd.h>
53 #include <modest-platform.h>
54 #include <modest-widget-memory.h>
55 #include <modest-ui-actions.h>
57 /* 'private'/'protected' functions */
58 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
59 static void modest_folder_view_init (ModestFolderView *obj);
60 static void modest_folder_view_finalize (GObject *obj);
62 static void tny_account_store_view_init (gpointer g,
65 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
66 TnyAccountStore *account_store);
68 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
70 static void on_account_update (TnyAccountStore *account_store,
74 static void on_account_removed (TnyAccountStore *self,
78 static void on_accounts_reloaded (TnyAccountStore *store,
81 static gint cmp_rows (GtkTreeModel *tree_model,
86 static gboolean filter_row (GtkTreeModel *model,
90 static gboolean on_key_pressed (GtkWidget *self,
94 static void on_configuration_key_changed (ModestConf* conf,
96 ModestConfEvent event,
97 ModestFolderView *self);
100 static void on_drag_data_get (GtkWidget *widget,
101 GdkDragContext *context,
102 GtkSelectionData *selection_data,
107 static void on_drag_data_received (GtkWidget *widget,
108 GdkDragContext *context,
111 GtkSelectionData *selection_data,
116 static gboolean on_drag_motion (GtkWidget *widget,
117 GdkDragContext *context,
123 static gint expand_row_timeout (gpointer data);
125 static void setup_drag_and_drop (GtkTreeView *self);
128 FOLDER_SELECTION_CHANGED_SIGNAL,
129 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
133 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
134 struct _ModestFolderViewPrivate {
135 TnyAccountStore *account_store;
136 TnyFolderStore *cur_folder_store;
138 gulong account_update_signal;
139 gulong changed_signal;
140 gulong accounts_reloaded_signal;
141 gulong account_removed_signal;
143 TnyFolderStoreQuery *query;
144 guint timer_expander;
146 gchar *local_account_name;
147 gchar *visible_account_id;
148 ModestFolderViewStyle style;
150 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
151 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
152 MODEST_TYPE_FOLDER_VIEW, \
153 ModestFolderViewPrivate))
155 static GObjectClass *parent_class = NULL;
157 static guint signals[LAST_SIGNAL] = {0};
160 modest_folder_view_get_type (void)
162 static GType my_type = 0;
164 static const GTypeInfo my_info = {
165 sizeof(ModestFolderViewClass),
166 NULL, /* base init */
167 NULL, /* base finalize */
168 (GClassInitFunc) modest_folder_view_class_init,
169 NULL, /* class finalize */
170 NULL, /* class data */
171 sizeof(ModestFolderView),
173 (GInstanceInitFunc) modest_folder_view_init,
177 static const GInterfaceInfo tny_account_store_view_info = {
178 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
179 NULL, /* interface_finalize */
180 NULL /* interface_data */
184 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
188 g_type_add_interface_static (my_type,
189 TNY_TYPE_ACCOUNT_STORE_VIEW,
190 &tny_account_store_view_info);
196 modest_folder_view_class_init (ModestFolderViewClass *klass)
198 GObjectClass *gobject_class;
199 gobject_class = (GObjectClass*) klass;
201 parent_class = g_type_class_peek_parent (klass);
202 gobject_class->finalize = modest_folder_view_finalize;
204 g_type_class_add_private (gobject_class,
205 sizeof(ModestFolderViewPrivate));
207 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
208 g_signal_new ("folder_selection_changed",
209 G_TYPE_FROM_CLASS (gobject_class),
211 G_STRUCT_OFFSET (ModestFolderViewClass,
212 folder_selection_changed),
214 modest_marshal_VOID__POINTER_BOOLEAN,
215 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
218 * This signal is emitted whenever the currently selected
219 * folder display name is computed. Note that the name could
220 * be different to the folder name, because we could append
221 * the unread messages count to the folder name to build the
222 * folder display name
224 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
225 g_signal_new ("folder-display-name-changed",
226 G_TYPE_FROM_CLASS (gobject_class),
228 G_STRUCT_OFFSET (ModestFolderViewClass,
229 folder_display_name_changed),
231 g_cclosure_marshal_VOID__STRING,
232 G_TYPE_NONE, 1, G_TYPE_STRING);
236 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
237 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
239 ModestFolderViewPrivate *priv;
244 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
245 GObject *instance = NULL;
247 g_return_if_fail (column);
248 g_return_if_fail (tree_model);
250 gtk_tree_model_get (tree_model, iter,
251 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
252 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
253 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
254 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
255 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
257 rendobj = G_OBJECT(renderer);
268 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
270 gchar *item_name = NULL;
271 gint item_weight = 400;
273 if (type != TNY_FOLDER_TYPE_ROOT) {
276 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
277 TnyFolderType folder_type
278 = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
279 if (folder_type != TNY_FOLDER_TYPE_UNKNOWN) {
281 fname = g_strdup(modest_local_folder_info_get_type_display_name (folder_type));
285 /* Select the number to show */
286 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
291 /* Use bold font style if there are unread messages */
293 item_name = g_strdup_printf ("%s (%d)", fname, unread);
296 item_name = g_strdup (fname);
300 } else if (TNY_IS_ACCOUNT (instance)) {
301 /* If it's a server account */
302 if (modest_tny_account_is_virtual_local_folders (
303 TNY_ACCOUNT (instance))) {
304 item_name = g_strdup (priv->local_account_name);
307 item_name = g_strdup (fname);
313 item_name = g_strdup ("unknown");
315 if (item_name && item_weight) {
316 /* Set the name in the treeview cell: */
317 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
319 /* Notify display name observers */
320 if (G_OBJECT (priv->cur_folder_store) == instance) {
321 g_signal_emit (G_OBJECT(data),
322 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
330 g_object_unref (G_OBJECT (instance));
337 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
338 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
340 GObject *rendobj = NULL, *instance = NULL;
341 GdkPixbuf *pixbuf = NULL;
342 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
344 const gchar *account_id = NULL;
347 rendobj = G_OBJECT(renderer);
348 gtk_tree_model_get (tree_model, iter,
349 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
350 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
351 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
352 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
363 if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
364 type = modest_tny_folder_guess_folder_type_from_name (fname);
368 case TNY_FOLDER_TYPE_ROOT:
369 if (TNY_IS_ACCOUNT (instance)) {
371 if (modest_tny_account_is_virtual_local_folders (
372 TNY_ACCOUNT (instance))) {
373 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
376 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
378 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
379 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
381 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
385 case TNY_FOLDER_TYPE_INBOX:
386 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
388 case TNY_FOLDER_TYPE_OUTBOX:
389 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
391 case TNY_FOLDER_TYPE_JUNK:
392 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
394 case TNY_FOLDER_TYPE_SENT:
395 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
397 case TNY_FOLDER_TYPE_TRASH:
398 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
400 case TNY_FOLDER_TYPE_DRAFTS:
401 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
403 case TNY_FOLDER_TYPE_NORMAL:
405 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
409 g_object_unref (G_OBJECT (instance));
413 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
416 g_object_unref (pixbuf);
420 add_columns (GtkWidget *treeview)
422 GtkTreeViewColumn *column;
423 GtkCellRenderer *renderer;
424 GtkTreeSelection *sel;
427 column = gtk_tree_view_column_new ();
429 /* Set icon and text render function */
430 renderer = gtk_cell_renderer_pixbuf_new();
431 gtk_tree_view_column_pack_start (column, renderer, FALSE);
432 gtk_tree_view_column_set_cell_data_func(column, renderer,
433 icon_cell_data, treeview, NULL);
435 renderer = gtk_cell_renderer_text_new();
436 gtk_tree_view_column_pack_start (column, renderer, FALSE);
437 gtk_tree_view_column_set_cell_data_func(column, renderer,
438 text_cell_data, treeview, NULL);
440 /* Set selection mode */
441 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
442 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
444 /* Set treeview appearance */
445 gtk_tree_view_column_set_spacing (column, 2);
446 gtk_tree_view_column_set_resizable (column, TRUE);
447 gtk_tree_view_column_set_fixed_width (column, TRUE);
448 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
449 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
452 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
456 modest_folder_view_init (ModestFolderView *obj)
458 ModestFolderViewPrivate *priv;
461 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
463 priv->timer_expander = 0;
464 priv->account_store = NULL;
466 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
467 priv->cur_folder_store = NULL;
468 priv->visible_account_id = NULL;
470 /* Initialize the local account name */
471 conf = modest_runtime_get_conf();
472 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
475 add_columns (GTK_WIDGET (obj));
477 /* Setup drag and drop */
478 setup_drag_and_drop (GTK_TREE_VIEW(obj));
480 /* Connect signals */
481 g_signal_connect (G_OBJECT (obj),
483 G_CALLBACK (on_key_pressed), NULL);
486 * Track changes in the local account name (in the device it
487 * will be the device name)
489 g_signal_connect (G_OBJECT(conf),
491 G_CALLBACK(on_configuration_key_changed), obj);
496 tny_account_store_view_init (gpointer g, gpointer iface_data)
498 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
500 klass->set_account_store_func = modest_folder_view_set_account_store;
506 modest_folder_view_finalize (GObject *obj)
508 ModestFolderViewPrivate *priv;
509 GtkTreeSelection *sel;
511 g_return_if_fail (obj);
513 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
515 if (priv->timer_expander != 0) {
516 g_source_remove (priv->timer_expander);
517 priv->timer_expander = 0;
520 if (priv->account_store) {
521 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
522 priv->account_update_signal);
523 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
524 priv->accounts_reloaded_signal);
525 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
526 priv->account_removed_signal);
527 g_object_unref (G_OBJECT(priv->account_store));
528 priv->account_store = NULL;
532 g_object_unref (G_OBJECT (priv->query));
536 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
538 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
540 g_free (priv->local_account_name);
541 g_free (priv->visible_account_id);
543 G_OBJECT_CLASS(parent_class)->finalize (obj);
548 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
550 ModestFolderViewPrivate *priv;
553 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
554 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
556 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
557 device = tny_account_store_get_device (account_store);
559 if (G_UNLIKELY (priv->account_store)) {
561 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
562 priv->account_update_signal))
563 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
564 priv->account_update_signal);
565 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
566 priv->accounts_reloaded_signal))
567 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
568 priv->accounts_reloaded_signal);
569 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
570 priv->account_removed_signal))
571 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
572 priv->account_removed_signal);
574 g_object_unref (G_OBJECT (priv->account_store));
577 priv->account_store = g_object_ref (G_OBJECT (account_store));
579 priv->account_update_signal =
580 g_signal_connect (G_OBJECT(account_store), "account_update",
581 G_CALLBACK (on_account_update), self);
583 priv->account_removed_signal =
584 g_signal_connect (G_OBJECT(account_store), "account_removed",
585 G_CALLBACK (on_account_removed), self);
587 priv->accounts_reloaded_signal =
588 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
589 G_CALLBACK (on_accounts_reloaded), self);
591 on_accounts_reloaded (account_store, (gpointer ) self);
593 g_object_unref (G_OBJECT (device));
597 on_account_removed (TnyAccountStore *account_store,
601 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
602 ModestFolderViewPrivate *priv;
604 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
606 /* If the removed account is the currently viewed one then
607 clear the configuration value. The new visible account will be the default account */
608 if (!strcmp (priv->visible_account_id, tny_account_get_id (account))) {
609 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
614 on_account_update (TnyAccountStore *account_store,
615 const gchar *account,
618 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
619 ModestFolderViewPrivate *priv;
621 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
622 if (!priv->visible_account_id)
623 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
624 MODEST_CONF_FOLDER_VIEW_KEY);
626 if (!modest_folder_view_update_model (self, account_store))
627 g_printerr ("modest: failed to update model for changes in '%s'",
632 on_accounts_reloaded (TnyAccountStore *account_store,
635 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
639 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
641 GtkTreeViewColumn *col;
643 g_return_if_fail (self);
645 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
647 g_printerr ("modest: failed get column for title\n");
651 gtk_tree_view_column_set_title (col, title);
652 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
657 modest_folder_view_new (TnyFolderStoreQuery *query)
660 ModestFolderViewPrivate *priv;
661 GtkTreeSelection *sel;
663 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
664 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
667 priv->query = g_object_ref (query);
669 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
670 priv->changed_signal = g_signal_connect (sel, "changed",
671 G_CALLBACK (on_selection_changed), self);
673 return GTK_WIDGET(self);
676 /* this feels dirty; any other way to expand all the root items? */
678 expand_root_items (ModestFolderView *self)
681 path = gtk_tree_path_new_first ();
683 /* all folders should have child items, so.. */
684 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
685 gtk_tree_path_next (path);
687 gtk_tree_path_free (path);
691 * We use this function to implement the
692 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
693 * account in this case, and the local folders.
696 filter_row (GtkTreeModel *model,
700 gboolean retval = TRUE;
701 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
702 GObject *instance = NULL;
704 gtk_tree_model_get (model, iter,
705 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
706 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
709 /* Do not show if there is no instance, this could indeed
710 happen when the model is being modified while it's being
711 drawn. This could occur for example when moving folders
716 if (type == TNY_FOLDER_TYPE_ROOT) {
717 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
718 if (TNY_IS_ACCOUNT (instance)) {
719 TnyAccount *acc = TNY_ACCOUNT (instance);
720 const gchar *account_id = tny_account_get_id (acc);
722 /* If it isn't a special folder,
723 * don't show it unless it is the visible account: */
724 if (!modest_tny_account_is_virtual_local_folders (acc) &&
725 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
726 /* Show only the visible account id */
727 ModestFolderViewPrivate *priv =
728 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
729 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
735 /* The virtual local-folders folder store is also shown by default. */
737 g_object_unref (instance);
743 modest_folder_view_update_model (ModestFolderView *self,
744 TnyAccountStore *account_store)
746 ModestFolderViewPrivate *priv;
747 GtkTreeModel *model /* , *old_model */;
748 /* TnyAccount *local_account; */
749 TnyList *model_as_list;
751 g_return_val_if_fail (account_store, FALSE);
753 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
755 /* Notify that there is no folder selected */
756 g_signal_emit (G_OBJECT(self),
757 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
760 /* FIXME: the local accounts are not shown when the query
761 selects only the subscribed folders. */
762 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
763 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
765 /* Deal with the model via its TnyList Interface,
766 * filling the TnyList via a get_accounts() call: */
767 model_as_list = TNY_LIST(model);
769 /* Get the accounts: */
770 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
772 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
773 g_object_unref (model_as_list);
774 model_as_list = NULL;
776 GtkTreeModel *filter_model = NULL, *sortable = NULL;
778 sortable = gtk_tree_model_sort_new_with_model (model);
779 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
780 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
782 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
783 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
784 cmp_rows, NULL, NULL);
786 /* Create filter model */
787 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
788 filter_model = gtk_tree_model_filter_new (sortable, NULL);
789 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
796 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
797 (filter_model) ? filter_model : sortable);
798 expand_root_items (self); /* expand all account folders */
800 g_object_unref (model);
803 g_object_unref (filter_model);
805 g_object_unref (sortable);
807 /* Select the first inbox or the local account if not found */
808 modest_folder_view_select_first_inbox_or_local (self);
815 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
818 TnyFolderStore *folder = NULL;
820 ModestFolderView *tree_view;
821 ModestFolderViewPrivate *priv;
823 g_return_if_fail (sel);
824 g_return_if_fail (user_data);
826 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
828 /* folder was _un_selected if true */
829 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
830 if (priv->cur_folder_store)
831 g_object_unref (priv->cur_folder_store);
832 priv->cur_folder_store = NULL;
834 /* Notify the display name observers */
835 g_signal_emit (G_OBJECT(user_data),
836 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
841 tree_view = MODEST_FOLDER_VIEW (user_data);
843 gtk_tree_model_get (model, &iter,
844 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
847 /* If the folder is the same do not notify */
848 if (priv->cur_folder_store == folder) {
849 g_object_unref (folder);
853 /* Current folder was unselected */
854 if (priv->cur_folder_store) {
855 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
856 priv->cur_folder_store, FALSE);
857 g_object_unref (priv->cur_folder_store);
860 /* New current references */
861 priv->cur_folder_store = folder;
863 /* New folder has been selected */
864 g_signal_emit (G_OBJECT(tree_view),
865 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
870 modest_folder_view_get_selected (ModestFolderView *self)
872 ModestFolderViewPrivate *priv;
874 g_return_val_if_fail (self, NULL);
876 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
877 if (priv->cur_folder_store)
878 g_object_ref (priv->cur_folder_store);
880 return priv->cur_folder_store;
884 get_cmp_rows_type_pos (GObject *folder)
886 /* Remote accounts -> Local account -> MMC account .*/
889 if (TNY_IS_ACCOUNT (folder) &&
890 modest_tny_account_is_virtual_local_folders (
891 TNY_ACCOUNT (folder))) {
893 } else if (TNY_IS_ACCOUNT (folder)) {
894 TnyAccount *account = TNY_ACCOUNT (folder);
895 const gchar *account_id = tny_account_get_id (account);
896 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
902 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
903 return -1; /* Should never happen */
908 * This function orders the mail accounts according to these rules:
909 * 1st - remote accounts
910 * 2nd - local account
914 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
920 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
921 GObject *folder1 = NULL;
922 GObject *folder2 = NULL;
924 gtk_tree_model_get (tree_model, iter1,
925 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
926 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
927 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
929 gtk_tree_model_get (tree_model, iter2,
930 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
931 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
934 if (type == TNY_FOLDER_TYPE_ROOT) {
935 /* Compare the types, so that
936 * Remote accounts -> Local account -> MMC account .*/
937 const gint pos1 = get_cmp_rows_type_pos (folder1);
938 const gint pos2 = get_cmp_rows_type_pos (folder2);
939 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
940 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
943 else if (pos1 > pos2)
946 /* Compare items of the same type: */
948 TnyAccount *account1 = NULL;
949 if (TNY_IS_ACCOUNT (folder1))
950 account1 = TNY_ACCOUNT (folder1);
952 TnyAccount *account2 = NULL;
953 if (TNY_IS_ACCOUNT (folder2))
954 account2 = TNY_ACCOUNT (folder2);
956 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
957 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
959 if (!account_id && !account_id2)
961 else if (!account_id)
963 else if (!account_id2)
965 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
968 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
971 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
975 g_object_unref(G_OBJECT(folder1));
977 g_object_unref(G_OBJECT(folder2));
985 /*****************************************************************************/
986 /* DRAG and DROP stuff */
987 /*****************************************************************************/
990 * This function fills the #GtkSelectionData with the row and the
991 * model that has been dragged. It's called when this widget is a
992 * source for dnd after the event drop happened
995 on_drag_data_get (GtkWidget *widget,
996 GdkDragContext *context,
997 GtkSelectionData *selection_data,
1002 GtkTreeSelection *selection;
1003 GtkTreeModel *model;
1005 GtkTreePath *source_row;
1007 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1008 gtk_tree_selection_get_selected (selection, &model, &iter);
1009 source_row = gtk_tree_model_get_path (model, &iter);
1011 gtk_tree_set_row_drag_data (selection_data,
1015 gtk_tree_path_free (source_row);
1018 typedef struct _DndHelper {
1019 gboolean delete_source;
1020 GtkTreePath *source_row;
1021 GdkDragContext *context;
1027 * This function is the callback of the
1028 * modest_mail_operation_xfer_msgs () and
1029 * modest_mail_operation_xfer_folder() calls. We check here if the
1030 * message/folder was correctly asynchronously transferred. The reason
1031 * to use the same callback is that the code is the same, it only has
1032 * to check that the operation went fine and then finalize the drag
1036 on_progress_changed (ModestMailOperation *mail_op,
1037 ModestMailOperationState *state,
1043 helper = (DndHelper *) user_data;
1045 if (!state->finished)
1048 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1054 /* Notify the drag source. Never call delete, the monitor will
1055 do the job if needed */
1056 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1058 /* Free the helper */
1059 gtk_tree_path_free (helper->source_row);
1060 g_slice_free (DndHelper, helper);
1064 * This function is used by drag_data_received_cb to manage drag and
1065 * drop of a header, i.e, and drag from the header view to the folder
1069 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1070 GtkTreeModel *dest_model,
1071 GtkTreePath *dest_row,
1074 TnyList *headers = NULL;
1075 TnyHeader *header = NULL;
1076 TnyFolder *folder = NULL;
1077 ModestMailOperation *mail_op = NULL;
1078 GtkTreeIter source_iter, dest_iter;
1080 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1081 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1082 g_return_if_fail (dest_row);
1083 g_return_if_fail (helper);
1086 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1087 gtk_tree_model_get (source_model, &source_iter,
1088 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1090 if (!TNY_IS_HEADER(header)) {
1091 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1096 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1097 gtk_tree_model_get (dest_model, &dest_iter,
1098 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1101 if (!TNY_IS_FOLDER(folder)) {
1102 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1106 /* Transfer message */
1107 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1108 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1110 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1111 G_CALLBACK (on_progress_changed), helper);
1113 headers = tny_simple_list_new ();
1114 tny_list_append (headers, G_OBJECT (header));
1115 modest_mail_operation_xfer_msgs (mail_op,
1118 helper->delete_source,
1123 if (G_IS_OBJECT(mail_op))
1124 g_object_unref (G_OBJECT (mail_op));
1125 if (G_IS_OBJECT(header))
1126 g_object_unref (G_OBJECT (header));
1127 if (G_IS_OBJECT(folder))
1128 g_object_unref (G_OBJECT (folder));
1129 if (G_IS_OBJECT(headers))
1130 g_object_unref (headers);
1134 * This function is used by drag_data_received_cb to manage drag and
1135 * drop of a folder, i.e, and drag from the folder view to the same
1139 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1140 GtkTreeModel *dest_model,
1141 GtkTreePath *dest_row,
1142 GtkSelectionData *selection_data,
1145 ModestMailOperation *mail_op;
1146 GtkTreeIter parent_iter, iter;
1147 TnyFolderStore *parent_folder;
1150 /* Check if the drag is possible */
1151 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1152 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1154 /* selection_data)) { */
1155 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1157 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1158 gtk_tree_path_free (helper->source_row);
1159 g_slice_free (DndHelper, helper);
1164 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1165 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1166 gtk_tree_model_get (source_model, &parent_iter,
1167 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1168 &parent_folder, -1);
1169 gtk_tree_model_get (source_model, &iter,
1170 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1173 /* Do the mail operation */
1174 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1176 modest_ui_actions_move_folder_error_handler,
1178 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1180 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1181 G_CALLBACK (on_progress_changed), helper);
1183 modest_mail_operation_xfer_folder (mail_op,
1186 helper->delete_source);
1189 g_object_unref (G_OBJECT (parent_folder));
1190 g_object_unref (G_OBJECT (folder));
1191 g_object_unref (G_OBJECT (mail_op));
1195 * This function receives the data set by the "drag-data-get" signal
1196 * handler. This information comes within the #GtkSelectionData. This
1197 * function will manage both the drags of folders of the treeview and
1198 * drags of headers of the header view widget.
1201 on_drag_data_received (GtkWidget *widget,
1202 GdkDragContext *context,
1205 GtkSelectionData *selection_data,
1210 GtkWidget *source_widget;
1211 GtkTreeModel *dest_model, *source_model;
1212 GtkTreePath *source_row, *dest_row;
1213 GtkTreeViewDropPosition pos;
1214 gboolean success = FALSE, delete_source = FALSE;
1215 DndHelper *helper = NULL;
1217 /* Do not allow further process */
1218 g_signal_stop_emission_by_name (widget, "drag-data-received");
1219 source_widget = gtk_drag_get_source_widget (context);
1221 /* Get the action */
1222 if (context->action == GDK_ACTION_MOVE) {
1223 delete_source = TRUE;
1225 /* Notify that there is no folder selected. We need to
1226 do this in order to update the headers view (and
1227 its monitors, because when moving, the old folder
1228 won't longer exist. We can not wait for the end of
1229 the operation, because the operation won't start if
1230 the folder is in use */
1231 if (source_widget == widget)
1232 g_signal_emit (G_OBJECT (widget),
1233 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1236 /* Check if the get_data failed */
1237 if (selection_data == NULL || selection_data->length < 0)
1238 gtk_drag_finish (context, success, FALSE, time);
1240 /* Get the models */
1241 gtk_tree_get_row_drag_data (selection_data,
1245 /* Select the destination model */
1246 if (source_widget == widget) {
1247 dest_model = source_model;
1249 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1252 /* Get the path to the destination row. Can not call
1253 gtk_tree_view_get_drag_dest_row() because the source row
1254 is not selected anymore */
1255 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1258 /* Only allow drops IN other rows */
1259 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1260 gtk_drag_finish (context, success, FALSE, time);
1262 /* Create the helper */
1263 helper = g_slice_new0 (DndHelper);
1264 helper->delete_source = delete_source;
1265 helper->source_row = gtk_tree_path_copy (source_row);
1266 helper->context = context;
1267 helper->time = time;
1269 /* Drags from the header view */
1270 if (source_widget != widget) {
1272 drag_and_drop_from_header_view (source_model,
1279 drag_and_drop_from_folder_view (source_model,
1287 gtk_tree_path_free (source_row);
1288 gtk_tree_path_free (dest_row);
1292 * We define a "drag-drop" signal handler because we do not want to
1293 * use the default one, because the default one always calls
1294 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1295 * signal handler, because there we have all the information available
1296 * to know if the dnd was a success or not.
1299 drag_drop_cb (GtkWidget *widget,
1300 GdkDragContext *context,
1308 if (!context->targets)
1311 /* Check if we're dragging a folder row */
1312 target = gtk_drag_dest_find_target (widget, context, NULL);
1314 /* Request the data from the source. */
1315 gtk_drag_get_data(widget, context, target, time);
1321 * This function expands a node of a tree view if it's not expanded
1322 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1323 * does that, so that's why they're here.
1326 expand_row_timeout (gpointer data)
1328 GtkTreeView *tree_view = data;
1329 GtkTreePath *dest_path = NULL;
1330 GtkTreeViewDropPosition pos;
1331 gboolean result = FALSE;
1333 GDK_THREADS_ENTER ();
1335 gtk_tree_view_get_drag_dest_row (tree_view,
1340 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1341 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1342 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1343 gtk_tree_path_free (dest_path);
1347 gtk_tree_path_free (dest_path);
1352 GDK_THREADS_LEAVE ();
1358 * This function is called whenever the pointer is moved over a widget
1359 * while dragging some data. It installs a timeout that will expand a
1360 * node of the treeview if not expanded yet. This function also calls
1361 * gdk_drag_status in order to set the suggested action that will be
1362 * used by the "drag-data-received" signal handler to know if we
1363 * should do a move or just a copy of the data.
1366 on_drag_motion (GtkWidget *widget,
1367 GdkDragContext *context,
1373 GtkTreeViewDropPosition pos;
1374 GtkTreePath *dest_row;
1375 ModestFolderViewPrivate *priv;
1376 GdkDragAction suggested_action;
1377 gboolean valid_location = FALSE;
1379 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1381 if (priv->timer_expander != 0) {
1382 g_source_remove (priv->timer_expander);
1383 priv->timer_expander = 0;
1386 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1391 /* Do not allow drops between folders */
1393 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1394 pos == GTK_TREE_VIEW_DROP_AFTER) {
1395 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1396 gdk_drag_status(context, 0, time);
1397 valid_location = FALSE;
1400 valid_location = TRUE;
1403 /* Expand the selected row after 1/2 second */
1404 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1405 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1406 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1409 /* Select the desired action. By default we pick MOVE */
1410 suggested_action = GDK_ACTION_MOVE;
1412 if (context->actions == GDK_ACTION_COPY)
1413 gdk_drag_status(context, GDK_ACTION_COPY, time);
1414 else if (context->actions == GDK_ACTION_MOVE)
1415 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1416 else if (context->actions & suggested_action)
1417 gdk_drag_status(context, suggested_action, time);
1419 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1423 gtk_tree_path_free (dest_row);
1424 g_signal_stop_emission_by_name (widget, "drag-motion");
1425 return valid_location;
1429 /* Folder view drag types */
1430 const GtkTargetEntry folder_view_drag_types[] =
1432 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1433 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1437 * This function sets the treeview as a source and a target for dnd
1438 * events. It also connects all the requirede signals.
1441 setup_drag_and_drop (GtkTreeView *self)
1443 /* Set up the folder view as a dnd destination. Set only the
1444 highlight flag, otherwise gtk will have a different
1446 gtk_drag_dest_set (GTK_WIDGET (self),
1447 GTK_DEST_DEFAULT_HIGHLIGHT,
1448 folder_view_drag_types,
1449 G_N_ELEMENTS (folder_view_drag_types),
1450 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1452 g_signal_connect (G_OBJECT (self),
1453 "drag_data_received",
1454 G_CALLBACK (on_drag_data_received),
1458 /* Set up the treeview as a dnd source */
1459 gtk_drag_source_set (GTK_WIDGET (self),
1461 folder_view_drag_types,
1462 G_N_ELEMENTS (folder_view_drag_types),
1463 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1465 g_signal_connect (G_OBJECT (self),
1467 G_CALLBACK (on_drag_motion),
1470 g_signal_connect (G_OBJECT (self),
1472 G_CALLBACK (on_drag_data_get),
1475 g_signal_connect (G_OBJECT (self),
1477 G_CALLBACK (drag_drop_cb),
1482 * This function manages the navigation through the folders using the
1483 * keyboard or the hardware keys in the device
1486 on_key_pressed (GtkWidget *self,
1490 GtkTreeSelection *selection;
1492 GtkTreeModel *model;
1493 gboolean retval = FALSE;
1495 /* Up and Down are automatically managed by the treeview */
1496 if (event->keyval == GDK_Return) {
1497 /* Expand/Collapse the selected row */
1498 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1499 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1502 path = gtk_tree_model_get_path (model, &iter);
1504 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1505 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1507 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1508 gtk_tree_path_free (path);
1510 /* No further processing */
1518 * We listen to the changes in the local folder account name key,
1519 * because we want to show the right name in the view. The local
1520 * folder account name corresponds to the device name in the Maemo
1521 * version. We do this because we do not want to query gconf on each
1522 * tree view refresh. It's better to cache it and change whenever
1526 on_configuration_key_changed (ModestConf* conf,
1528 ModestConfEvent event,
1529 ModestFolderView *self)
1531 ModestFolderViewPrivate *priv;
1536 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1537 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1539 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1540 g_free (priv->local_account_name);
1542 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1543 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1545 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1546 MODEST_CONF_DEVICE_NAME, NULL);
1548 /* Force a redraw */
1549 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1550 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1551 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1552 gtk_tree_view_column_queue_resize (tree_column);
1558 modest_folder_view_set_style (ModestFolderView *self,
1559 ModestFolderViewStyle style)
1561 ModestFolderViewPrivate *priv;
1563 g_return_if_fail (self);
1565 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1567 priv->style = style;
1571 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1572 const gchar *account_id)
1574 ModestFolderViewPrivate *priv;
1575 GtkTreeModel *model;
1577 g_return_if_fail (self);
1579 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1581 /* This will be used by the filter_row callback,
1582 * to decided which rows to show: */
1583 if (priv->visible_account_id) {
1584 g_free (priv->visible_account_id);
1585 priv->visible_account_id = NULL;
1588 priv->visible_account_id = g_strdup (account_id);
1591 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1592 if (GTK_IS_TREE_MODEL_FILTER (model))
1593 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1595 /* Save settings to gconf */
1596 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1597 MODEST_CONF_FOLDER_VIEW_KEY);
1601 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1603 ModestFolderViewPrivate *priv;
1605 g_return_val_if_fail (self, NULL);
1607 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1609 return (const gchar *) priv->visible_account_id;
1613 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1617 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1620 gtk_tree_model_get (model, iter,
1621 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1622 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1626 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n",
1627 __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1630 gboolean result = FALSE;
1631 if (type == TNY_FOLDER_TYPE_INBOX) {
1633 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1634 /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1635 * when getting folders from the cache, before connectin, so we do
1636 * an extra check. We could fix this in tinymail, but it's easier
1639 if (strcmp (name, "Inbox") == 0)
1646 *inbox_iter = *iter;
1650 if (gtk_tree_model_iter_children (model, &child, iter)) {
1651 if (find_inbox_iter (model, &child, inbox_iter))
1655 } while (gtk_tree_model_iter_next (model, iter));
1663 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1665 GtkTreeModel *model;
1666 GtkTreeIter iter, inbox_iter;
1667 GtkTreeSelection *sel;
1669 /* Do not set it if the folder view was not painted */
1670 if (!GTK_WIDGET_MAPPED (self))
1673 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1677 expand_root_items (self);
1678 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1680 gtk_tree_model_get_iter_first (model, &iter);
1681 if (find_inbox_iter (model, &iter, &inbox_iter)) {
1682 gtk_tree_selection_select_iter (sel, &inbox_iter);
1685 gtk_tree_model_get_iter_first (model, &iter);
1686 gtk_tree_selection_select_iter (sel, &iter);