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-account-mgr-helpers.h>
55 #include <modest-widget-memory.h>
56 #include <modest-ui-actions.h>
58 /* 'private'/'protected' functions */
59 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
60 static void modest_folder_view_init (ModestFolderView *obj);
61 static void modest_folder_view_finalize (GObject *obj);
63 static void tny_account_store_view_init (gpointer g,
66 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
67 TnyAccountStore *account_store);
69 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
71 static void on_account_update (TnyAccountStore *account_store,
75 static void on_accounts_reloaded (TnyAccountStore *store,
78 static gint cmp_rows (GtkTreeModel *tree_model,
83 static gboolean filter_row (GtkTreeModel *model,
87 static gboolean on_key_pressed (GtkWidget *self,
91 static void on_configuration_key_changed (ModestConf* conf,
93 ModestConfEvent event,
94 ModestFolderView *self);
97 static void on_drag_data_get (GtkWidget *widget,
98 GdkDragContext *context,
99 GtkSelectionData *selection_data,
104 static void on_drag_data_received (GtkWidget *widget,
105 GdkDragContext *context,
108 GtkSelectionData *selection_data,
113 static gboolean on_drag_motion (GtkWidget *widget,
114 GdkDragContext *context,
120 static gint expand_row_timeout (gpointer data);
122 static void setup_drag_and_drop (GtkTreeView *self);
125 FOLDER_SELECTION_CHANGED_SIGNAL,
126 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
130 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
131 struct _ModestFolderViewPrivate {
132 TnyAccountStore *account_store;
133 TnyFolderStore *cur_folder_store;
135 gulong account_update_signal;
136 gulong changed_signal;
137 gulong accounts_reloaded_signal;
139 TnyFolderStoreQuery *query;
140 guint timer_expander;
142 gchar *local_account_name;
143 gchar *visible_account_id;
144 ModestFolderViewStyle style;
146 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
147 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
148 MODEST_TYPE_FOLDER_VIEW, \
149 ModestFolderViewPrivate))
151 static GObjectClass *parent_class = NULL;
153 static guint signals[LAST_SIGNAL] = {0};
156 modest_folder_view_get_type (void)
158 static GType my_type = 0;
160 static const GTypeInfo my_info = {
161 sizeof(ModestFolderViewClass),
162 NULL, /* base init */
163 NULL, /* base finalize */
164 (GClassInitFunc) modest_folder_view_class_init,
165 NULL, /* class finalize */
166 NULL, /* class data */
167 sizeof(ModestFolderView),
169 (GInstanceInitFunc) modest_folder_view_init,
173 static const GInterfaceInfo tny_account_store_view_info = {
174 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
175 NULL, /* interface_finalize */
176 NULL /* interface_data */
180 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
184 g_type_add_interface_static (my_type,
185 TNY_TYPE_ACCOUNT_STORE_VIEW,
186 &tny_account_store_view_info);
192 modest_folder_view_class_init (ModestFolderViewClass *klass)
194 GObjectClass *gobject_class;
195 gobject_class = (GObjectClass*) klass;
197 parent_class = g_type_class_peek_parent (klass);
198 gobject_class->finalize = modest_folder_view_finalize;
200 g_type_class_add_private (gobject_class,
201 sizeof(ModestFolderViewPrivate));
203 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
204 g_signal_new ("folder_selection_changed",
205 G_TYPE_FROM_CLASS (gobject_class),
207 G_STRUCT_OFFSET (ModestFolderViewClass,
208 folder_selection_changed),
210 modest_marshal_VOID__POINTER_BOOLEAN,
211 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
214 * This signal is emitted whenever the currently selected
215 * folder display name is computed. Note that the name could
216 * be different to the folder name, because we could append
217 * the unread messages count to the folder name to build the
218 * folder display name
220 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
221 g_signal_new ("folder-display-name-changed",
222 G_TYPE_FROM_CLASS (gobject_class),
224 G_STRUCT_OFFSET (ModestFolderViewClass,
225 folder_display_name_changed),
227 g_cclosure_marshal_VOID__STRING,
228 G_TYPE_NONE, 1, G_TYPE_STRING);
232 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
233 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
235 ModestFolderViewPrivate *priv;
240 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
241 GObject *instance = NULL;
243 g_return_if_fail (column);
244 g_return_if_fail (tree_model);
246 gtk_tree_model_get (tree_model, iter,
247 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
248 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
249 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
250 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
251 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
253 rendobj = G_OBJECT(renderer);
264 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
266 gchar *item_name = NULL;
267 gint item_weight = 400;
269 if (type != TNY_FOLDER_TYPE_ROOT) {
272 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
273 TnyFolderType folder_type
274 = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
275 if (folder_type != TNY_FOLDER_TYPE_UNKNOWN) {
277 fname = g_strdup(modest_local_folder_info_get_type_display_name (folder_type));
281 /* Select the number to show */
282 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
287 /* Use bold font style if there are unread messages */
289 item_name = g_strdup_printf ("%s (%d)", fname, unread);
292 item_name = g_strdup (fname);
296 } else if (TNY_IS_ACCOUNT (instance)) {
297 /* If it's a server account */
298 if (modest_tny_account_is_virtual_local_folders (
299 TNY_ACCOUNT (instance))) {
300 item_name = g_strdup (priv->local_account_name);
303 item_name = g_strdup (fname);
309 item_name = g_strdup ("unknown");
311 if (item_name && item_weight) {
312 /* Set the name in the treeview cell: */
313 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
315 /* Notify display name observers */
316 if (G_OBJECT (priv->cur_folder_store) == instance) {
317 g_signal_emit (G_OBJECT(data),
318 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
326 g_object_unref (G_OBJECT (instance));
333 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
334 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
336 GObject *rendobj = NULL, *instance = NULL;
337 GdkPixbuf *pixbuf = NULL;
338 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
340 const gchar *account_id = NULL;
343 rendobj = G_OBJECT(renderer);
344 gtk_tree_model_get (tree_model, iter,
345 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
346 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
347 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
348 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
359 if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
360 type = modest_tny_folder_guess_folder_type_from_name (fname);
364 case TNY_FOLDER_TYPE_ROOT:
365 if (TNY_IS_ACCOUNT (instance)) {
367 if (modest_tny_account_is_virtual_local_folders (
368 TNY_ACCOUNT (instance))) {
369 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
372 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
374 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
375 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
377 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
381 case TNY_FOLDER_TYPE_INBOX:
382 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
384 case TNY_FOLDER_TYPE_OUTBOX:
385 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
387 case TNY_FOLDER_TYPE_JUNK:
388 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
390 case TNY_FOLDER_TYPE_SENT:
391 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
393 case TNY_FOLDER_TYPE_TRASH:
394 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
396 case TNY_FOLDER_TYPE_DRAFTS:
397 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
399 case TNY_FOLDER_TYPE_NORMAL:
401 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
405 g_object_unref (G_OBJECT (instance));
409 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
412 g_object_unref (pixbuf);
416 add_columns (GtkWidget *treeview)
418 GtkTreeViewColumn *column;
419 GtkCellRenderer *renderer;
420 GtkTreeSelection *sel;
423 column = gtk_tree_view_column_new ();
425 /* Set icon and text render function */
426 renderer = gtk_cell_renderer_pixbuf_new();
427 gtk_tree_view_column_pack_start (column, renderer, FALSE);
428 gtk_tree_view_column_set_cell_data_func(column, renderer,
429 icon_cell_data, treeview, NULL);
431 renderer = gtk_cell_renderer_text_new();
432 gtk_tree_view_column_pack_start (column, renderer, FALSE);
433 gtk_tree_view_column_set_cell_data_func(column, renderer,
434 text_cell_data, treeview, NULL);
436 /* Set selection mode */
437 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
438 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
440 /* Set treeview appearance */
441 gtk_tree_view_column_set_spacing (column, 2);
442 gtk_tree_view_column_set_resizable (column, TRUE);
443 gtk_tree_view_column_set_fixed_width (column, TRUE);
444 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
445 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
448 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
452 modest_folder_view_init (ModestFolderView *obj)
454 ModestFolderViewPrivate *priv;
457 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
459 priv->timer_expander = 0;
460 priv->account_store = NULL;
462 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
463 priv->cur_folder_store = NULL;
464 priv->visible_account_id = NULL;
466 /* Initialize the local account name */
467 conf = modest_runtime_get_conf();
468 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
471 add_columns (GTK_WIDGET (obj));
473 /* Setup drag and drop */
474 setup_drag_and_drop (GTK_TREE_VIEW(obj));
476 /* Connect signals */
477 g_signal_connect (G_OBJECT (obj),
479 G_CALLBACK (on_key_pressed), NULL);
482 * Track changes in the local account name (in the device it
483 * will be the device name)
485 g_signal_connect (G_OBJECT(conf),
487 G_CALLBACK(on_configuration_key_changed), obj);
492 tny_account_store_view_init (gpointer g, gpointer iface_data)
494 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
496 klass->set_account_store_func = modest_folder_view_set_account_store;
502 modest_folder_view_finalize (GObject *obj)
504 ModestFolderViewPrivate *priv;
505 GtkTreeSelection *sel;
507 g_return_if_fail (obj);
509 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
511 if (priv->timer_expander != 0) {
512 g_source_remove (priv->timer_expander);
513 priv->timer_expander = 0;
516 if (priv->account_store) {
517 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
518 priv->account_update_signal);
519 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
520 priv->accounts_reloaded_signal);
521 g_object_unref (G_OBJECT(priv->account_store));
522 priv->account_store = NULL;
526 g_object_unref (G_OBJECT (priv->query));
530 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
532 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
534 g_free (priv->local_account_name);
535 g_free (priv->visible_account_id);
537 G_OBJECT_CLASS(parent_class)->finalize (obj);
542 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
544 ModestFolderViewPrivate *priv;
547 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
548 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
550 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
551 device = tny_account_store_get_device (account_store);
553 if (G_UNLIKELY (priv->account_store)) {
555 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
556 priv->account_update_signal))
557 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
558 priv->account_update_signal);
559 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
560 priv->accounts_reloaded_signal))
561 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
562 priv->accounts_reloaded_signal);
564 g_object_unref (G_OBJECT (priv->account_store));
567 priv->account_store = g_object_ref (G_OBJECT (account_store));
569 priv->account_update_signal =
570 g_signal_connect (G_OBJECT(account_store), "account_update",
571 G_CALLBACK (on_account_update), self);
573 priv->accounts_reloaded_signal =
574 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
575 G_CALLBACK (on_accounts_reloaded), self);
577 on_accounts_reloaded (account_store, (gpointer ) self);
579 g_object_unref (G_OBJECT (device));
583 on_account_update (TnyAccountStore *account_store, const gchar *account,
586 if (!modest_folder_view_update_model (MODEST_FOLDER_VIEW(user_data),
588 g_printerr ("modest: failed to update model for changes in '%s'",
593 on_accounts_reloaded (TnyAccountStore *account_store,
596 ModestConf *conf = modest_runtime_get_conf ();
598 modest_widget_memory_save (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
599 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
600 modest_widget_memory_restore (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
604 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
606 GtkTreeViewColumn *col;
608 g_return_if_fail (self);
610 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
612 g_printerr ("modest: failed get column for title\n");
616 gtk_tree_view_column_set_title (col, title);
617 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
622 modest_folder_view_new (TnyFolderStoreQuery *query)
625 ModestFolderViewPrivate *priv;
626 GtkTreeSelection *sel;
628 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
629 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
632 priv->query = g_object_ref (query);
634 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
635 priv->changed_signal = g_signal_connect (sel, "changed",
636 G_CALLBACK (on_selection_changed), self);
638 return GTK_WIDGET(self);
641 /* this feels dirty; any other way to expand all the root items? */
643 expand_root_items (ModestFolderView *self)
646 path = gtk_tree_path_new_first ();
648 /* all folders should have child items, so.. */
649 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
650 gtk_tree_path_next (path);
652 gtk_tree_path_free (path);
656 * We use this function to implement the
657 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
658 * account in this case, and the local folders.
661 filter_row (GtkTreeModel *model,
665 gboolean retval = TRUE;
666 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
667 GObject *instance = NULL;
669 gtk_tree_model_get (model, iter,
670 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
671 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
674 /* Do not show if there is no instance, this could indeed
675 happen when the model is being modified while it's being
676 drawn. This could occur for example when moving folders
681 if (type == TNY_FOLDER_TYPE_ROOT) {
682 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
683 if (TNY_IS_ACCOUNT (instance)) {
684 TnyAccount *acc = TNY_ACCOUNT (instance);
685 const gchar *account_id = tny_account_get_id (acc);
687 /* If it isn't a special folder,
688 * don't show it unless it is the visible account: */
689 if (!modest_tny_account_is_virtual_local_folders (acc) &&
690 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
691 /* Show only the visible account id */
692 ModestFolderViewPrivate *priv =
693 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
694 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
700 /* The virtual local-folders folder store is also shown by default. */
702 g_object_unref (instance);
708 modest_folder_view_update_model (ModestFolderView *self,
709 TnyAccountStore *account_store)
711 ModestFolderViewPrivate *priv;
712 GtkTreeModel *model /* , *old_model */;
713 /* TnyAccount *local_account; */
714 TnyList *model_as_list;
716 g_return_val_if_fail (account_store, FALSE);
718 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
720 /* Notify that there is no folder selected */
721 g_signal_emit (G_OBJECT(self),
722 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
725 /* FIXME: the local accounts are not shown when the query
726 selects only the subscribed folders. */
727 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
728 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
730 /* Deal with the model via its TnyList Interface,
731 * filling the TnyList via a get_accounts() call: */
732 model_as_list = TNY_LIST(model);
734 /* Get the accounts: */
735 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
737 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
738 g_object_unref (model_as_list);
739 model_as_list = NULL;
741 GtkTreeModel *filter_model = NULL, *sortable = NULL;
743 sortable = gtk_tree_model_sort_new_with_model (model);
744 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
745 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
747 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
748 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
749 cmp_rows, NULL, NULL);
751 /* Create filter model */
752 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
753 filter_model = gtk_tree_model_filter_new (sortable, NULL);
754 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
761 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
762 (filter_model) ? filter_model : sortable);
763 expand_root_items (self); /* expand all account folders */
765 g_object_unref (model);
768 g_object_unref (filter_model);
770 g_object_unref (sortable);
777 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
780 TnyFolderStore *folder = NULL;
782 ModestFolderView *tree_view;
783 ModestFolderViewPrivate *priv;
785 g_return_if_fail (sel);
786 g_return_if_fail (user_data);
788 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
790 /* folder was _un_selected if true */
791 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
792 if (priv->cur_folder_store)
793 g_object_unref (priv->cur_folder_store);
794 priv->cur_folder_store = NULL;
796 /* Notify the display name observers */
797 g_signal_emit (G_OBJECT(user_data),
798 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
803 tree_view = MODEST_FOLDER_VIEW (user_data);
805 gtk_tree_model_get (model, &iter,
806 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
809 /* If the folder is the same do not notify */
810 if (priv->cur_folder_store == folder) {
811 g_object_unref (folder);
815 /* Current folder was unselected */
816 if (priv->cur_folder_store) {
817 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
818 priv->cur_folder_store, FALSE);
819 g_object_unref (priv->cur_folder_store);
822 /* New current references */
823 priv->cur_folder_store = folder;
825 /* New folder has been selected */
826 g_signal_emit (G_OBJECT(tree_view),
827 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
832 modest_folder_view_get_selected (ModestFolderView *self)
834 ModestFolderViewPrivate *priv;
836 g_return_val_if_fail (self, NULL);
838 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
839 if (priv->cur_folder_store)
840 g_object_ref (priv->cur_folder_store);
842 return priv->cur_folder_store;
846 get_cmp_rows_type_pos (GObject *folder)
848 /* Remote accounts -> Local account -> MMC account .*/
851 if (TNY_IS_ACCOUNT (folder) &&
852 modest_tny_account_is_virtual_local_folders (
853 TNY_ACCOUNT (folder))) {
855 } else if (TNY_IS_ACCOUNT (folder)) {
856 TnyAccount *account = TNY_ACCOUNT (folder);
857 const gchar *account_id = tny_account_get_id (account);
858 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
864 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
865 return -1; /* Should never happen */
870 * This function orders the mail accounts according to these rules:
871 * 1st - remote accounts
872 * 2nd - local account
876 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
882 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
883 GObject *folder1 = NULL;
884 GObject *folder2 = NULL;
886 gtk_tree_model_get (tree_model, iter1,
887 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
888 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
889 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
891 gtk_tree_model_get (tree_model, iter2,
892 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
893 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
896 if (type == TNY_FOLDER_TYPE_ROOT) {
897 /* Compare the types, so that
898 * Remote accounts -> Local account -> MMC account .*/
899 const gint pos1 = get_cmp_rows_type_pos (folder1);
900 const gint pos2 = get_cmp_rows_type_pos (folder2);
901 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
902 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
905 else if (pos1 > pos2)
908 /* Compare items of the same type: */
910 TnyAccount *account1 = NULL;
911 if (TNY_IS_ACCOUNT (folder1))
912 account1 = TNY_ACCOUNT (folder1);
914 TnyAccount *account2 = NULL;
915 if (TNY_IS_ACCOUNT (folder2))
916 account2 = TNY_ACCOUNT (folder2);
918 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
919 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
921 if (!account_id && !account_id2)
923 else if (!account_id)
925 else if (!account_id2)
927 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
930 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
933 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
937 g_object_unref(G_OBJECT(folder1));
939 g_object_unref(G_OBJECT(folder2));
947 /*****************************************************************************/
948 /* DRAG and DROP stuff */
949 /*****************************************************************************/
952 * This function fills the #GtkSelectionData with the row and the
953 * model that has been dragged. It's called when this widget is a
954 * source for dnd after the event drop happened
957 on_drag_data_get (GtkWidget *widget,
958 GdkDragContext *context,
959 GtkSelectionData *selection_data,
964 GtkTreeSelection *selection;
967 GtkTreePath *source_row;
969 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
970 gtk_tree_selection_get_selected (selection, &model, &iter);
971 source_row = gtk_tree_model_get_path (model, &iter);
973 gtk_tree_set_row_drag_data (selection_data,
977 gtk_tree_path_free (source_row);
980 typedef struct _DndHelper {
981 gboolean delete_source;
982 GtkTreePath *source_row;
983 GdkDragContext *context;
989 * This function is the callback of the
990 * modest_mail_operation_xfer_msgs () and
991 * modest_mail_operation_xfer_folder() calls. We check here if the
992 * message/folder was correctly asynchronously transferred. The reason
993 * to use the same callback is that the code is the same, it only has
994 * to check that the operation went fine and then finalize the drag
998 on_progress_changed (ModestMailOperation *mail_op,
999 ModestMailOperationState *state,
1005 helper = (DndHelper *) user_data;
1007 if (!state->finished)
1010 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1016 /* Notify the drag source. Never call delete, the monitor will
1017 do the job if needed */
1018 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1020 /* Free the helper */
1021 gtk_tree_path_free (helper->source_row);
1022 g_slice_free (DndHelper, helper);
1026 * This function is used by drag_data_received_cb to manage drag and
1027 * drop of a header, i.e, and drag from the header view to the folder
1031 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1032 GtkTreeModel *dest_model,
1033 GtkTreePath *dest_row,
1036 TnyList *headers = NULL;
1037 TnyHeader *header = NULL;
1038 TnyFolder *folder = NULL;
1039 ModestMailOperation *mail_op = NULL;
1040 GtkTreeIter source_iter, dest_iter;
1042 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1043 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1044 g_return_if_fail (dest_row);
1045 g_return_if_fail (helper);
1048 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1049 gtk_tree_model_get (source_model, &source_iter,
1050 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1052 if (!TNY_IS_HEADER(header)) {
1053 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1058 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1059 gtk_tree_model_get (dest_model, &dest_iter,
1060 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1063 if (!TNY_IS_FOLDER(folder)) {
1064 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1068 /* Transfer message */
1069 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1070 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1072 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1073 G_CALLBACK (on_progress_changed), helper);
1075 headers = tny_simple_list_new ();
1076 tny_list_append (headers, G_OBJECT (header));
1077 modest_mail_operation_xfer_msgs (mail_op,
1080 helper->delete_source,
1085 if (G_IS_OBJECT(mail_op))
1086 g_object_unref (G_OBJECT (mail_op));
1087 if (G_IS_OBJECT(header))
1088 g_object_unref (G_OBJECT (header));
1089 if (G_IS_OBJECT(folder))
1090 g_object_unref (G_OBJECT (folder));
1091 if (G_IS_OBJECT(headers))
1092 g_object_unref (headers);
1096 * This function is used by drag_data_received_cb to manage drag and
1097 * drop of a folder, i.e, and drag from the folder view to the same
1101 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1102 GtkTreeModel *dest_model,
1103 GtkTreePath *dest_row,
1104 GtkSelectionData *selection_data,
1107 ModestMailOperation *mail_op;
1108 GtkTreeIter parent_iter, iter;
1109 TnyFolderStore *parent_folder;
1112 /* Check if the drag is possible */
1113 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1114 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1116 /* selection_data)) { */
1117 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1119 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1120 gtk_tree_path_free (helper->source_row);
1121 g_slice_free (DndHelper, helper);
1126 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1127 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1128 gtk_tree_model_get (source_model, &parent_iter,
1129 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1130 &parent_folder, -1);
1131 gtk_tree_model_get (source_model, &iter,
1132 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1135 /* Do the mail operation */
1136 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1138 modest_ui_actions_move_folder_error_handler,
1140 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1142 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1143 G_CALLBACK (on_progress_changed), helper);
1145 modest_mail_operation_xfer_folder (mail_op,
1148 helper->delete_source);
1151 g_object_unref (G_OBJECT (parent_folder));
1152 g_object_unref (G_OBJECT (folder));
1153 g_object_unref (G_OBJECT (mail_op));
1157 * This function receives the data set by the "drag-data-get" signal
1158 * handler. This information comes within the #GtkSelectionData. This
1159 * function will manage both the drags of folders of the treeview and
1160 * drags of headers of the header view widget.
1163 on_drag_data_received (GtkWidget *widget,
1164 GdkDragContext *context,
1167 GtkSelectionData *selection_data,
1172 GtkWidget *source_widget;
1173 GtkTreeModel *dest_model, *source_model;
1174 GtkTreePath *source_row, *dest_row;
1175 GtkTreeViewDropPosition pos;
1176 gboolean success = FALSE, delete_source = FALSE;
1177 DndHelper *helper = NULL;
1179 /* Do not allow further process */
1180 g_signal_stop_emission_by_name (widget, "drag-data-received");
1181 source_widget = gtk_drag_get_source_widget (context);
1183 /* Get the action */
1184 if (context->action == GDK_ACTION_MOVE) {
1185 delete_source = TRUE;
1187 /* Notify that there is no folder selected. We need to
1188 do this in order to update the headers view (and
1189 its monitors, because when moving, the old folder
1190 won't longer exist. We can not wait for the end of
1191 the operation, because the operation won't start if
1192 the folder is in use */
1193 if (source_widget == widget)
1194 g_signal_emit (G_OBJECT (widget),
1195 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1198 /* Check if the get_data failed */
1199 if (selection_data == NULL || selection_data->length < 0)
1200 gtk_drag_finish (context, success, FALSE, time);
1202 /* Get the models */
1203 gtk_tree_get_row_drag_data (selection_data,
1207 /* Select the destination model */
1208 if (source_widget == widget) {
1209 dest_model = source_model;
1211 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1214 /* Get the path to the destination row. Can not call
1215 gtk_tree_view_get_drag_dest_row() because the source row
1216 is not selected anymore */
1217 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1220 /* Only allow drops IN other rows */
1221 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1222 gtk_drag_finish (context, success, FALSE, time);
1224 /* Create the helper */
1225 helper = g_slice_new0 (DndHelper);
1226 helper->delete_source = delete_source;
1227 helper->source_row = gtk_tree_path_copy (source_row);
1228 helper->context = context;
1229 helper->time = time;
1231 /* Drags from the header view */
1232 if (source_widget != widget) {
1234 drag_and_drop_from_header_view (source_model,
1241 drag_and_drop_from_folder_view (source_model,
1249 gtk_tree_path_free (source_row);
1250 gtk_tree_path_free (dest_row);
1254 * We define a "drag-drop" signal handler because we do not want to
1255 * use the default one, because the default one always calls
1256 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1257 * signal handler, because there we have all the information available
1258 * to know if the dnd was a success or not.
1261 drag_drop_cb (GtkWidget *widget,
1262 GdkDragContext *context,
1270 if (!context->targets)
1273 /* Check if we're dragging a folder row */
1274 target = gtk_drag_dest_find_target (widget, context, NULL);
1276 /* Request the data from the source. */
1277 gtk_drag_get_data(widget, context, target, time);
1283 * This function expands a node of a tree view if it's not expanded
1284 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1285 * does that, so that's why they're here.
1288 expand_row_timeout (gpointer data)
1290 GtkTreeView *tree_view = data;
1291 GtkTreePath *dest_path = NULL;
1292 GtkTreeViewDropPosition pos;
1293 gboolean result = FALSE;
1295 GDK_THREADS_ENTER ();
1297 gtk_tree_view_get_drag_dest_row (tree_view,
1302 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1303 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1304 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1305 gtk_tree_path_free (dest_path);
1309 gtk_tree_path_free (dest_path);
1314 GDK_THREADS_LEAVE ();
1320 * This function is called whenever the pointer is moved over a widget
1321 * while dragging some data. It installs a timeout that will expand a
1322 * node of the treeview if not expanded yet. This function also calls
1323 * gdk_drag_status in order to set the suggested action that will be
1324 * used by the "drag-data-received" signal handler to know if we
1325 * should do a move or just a copy of the data.
1328 on_drag_motion (GtkWidget *widget,
1329 GdkDragContext *context,
1335 GtkTreeViewDropPosition pos;
1336 GtkTreePath *dest_row;
1337 ModestFolderViewPrivate *priv;
1338 GdkDragAction suggested_action;
1339 gboolean valid_location = FALSE;
1341 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1343 if (priv->timer_expander != 0) {
1344 g_source_remove (priv->timer_expander);
1345 priv->timer_expander = 0;
1348 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1353 /* Do not allow drops between folders */
1355 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1356 pos == GTK_TREE_VIEW_DROP_AFTER) {
1357 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1358 gdk_drag_status(context, 0, time);
1359 valid_location = FALSE;
1362 valid_location = TRUE;
1365 /* Expand the selected row after 1/2 second */
1366 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1367 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1368 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1371 /* Select the desired action. By default we pick MOVE */
1372 suggested_action = GDK_ACTION_MOVE;
1374 if (context->actions == GDK_ACTION_COPY)
1375 gdk_drag_status(context, GDK_ACTION_COPY, time);
1376 else if (context->actions == GDK_ACTION_MOVE)
1377 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1378 else if (context->actions & suggested_action)
1379 gdk_drag_status(context, suggested_action, time);
1381 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1385 gtk_tree_path_free (dest_row);
1386 g_signal_stop_emission_by_name (widget, "drag-motion");
1387 return valid_location;
1391 /* Folder view drag types */
1392 const GtkTargetEntry folder_view_drag_types[] =
1394 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1395 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1399 * This function sets the treeview as a source and a target for dnd
1400 * events. It also connects all the requirede signals.
1403 setup_drag_and_drop (GtkTreeView *self)
1405 /* Set up the folder view as a dnd destination. Set only the
1406 highlight flag, otherwise gtk will have a different
1408 gtk_drag_dest_set (GTK_WIDGET (self),
1409 GTK_DEST_DEFAULT_HIGHLIGHT,
1410 folder_view_drag_types,
1411 G_N_ELEMENTS (folder_view_drag_types),
1412 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1414 g_signal_connect (G_OBJECT (self),
1415 "drag_data_received",
1416 G_CALLBACK (on_drag_data_received),
1420 /* Set up the treeview as a dnd source */
1421 gtk_drag_source_set (GTK_WIDGET (self),
1423 folder_view_drag_types,
1424 G_N_ELEMENTS (folder_view_drag_types),
1425 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1427 g_signal_connect (G_OBJECT (self),
1429 G_CALLBACK (on_drag_motion),
1432 g_signal_connect (G_OBJECT (self),
1434 G_CALLBACK (on_drag_data_get),
1437 g_signal_connect (G_OBJECT (self),
1439 G_CALLBACK (drag_drop_cb),
1444 * This function manages the navigation through the folders using the
1445 * keyboard or the hardware keys in the device
1448 on_key_pressed (GtkWidget *self,
1452 GtkTreeSelection *selection;
1454 GtkTreeModel *model;
1455 gboolean retval = FALSE;
1457 /* Up and Down are automatically managed by the treeview */
1458 if (event->keyval == GDK_Return) {
1459 /* Expand/Collapse the selected row */
1460 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1461 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1464 path = gtk_tree_model_get_path (model, &iter);
1466 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1467 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1469 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1470 gtk_tree_path_free (path);
1472 /* No further processing */
1480 * We listen to the changes in the local folder account name key,
1481 * because we want to show the right name in the view. The local
1482 * folder account name corresponds to the device name in the Maemo
1483 * version. We do this because we do not want to query gconf on each
1484 * tree view refresh. It's better to cache it and change whenever
1488 on_configuration_key_changed (ModestConf* conf,
1490 ModestConfEvent event,
1491 ModestFolderView *self)
1493 ModestFolderViewPrivate *priv;
1498 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1499 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1501 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1502 g_free (priv->local_account_name);
1504 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1505 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1507 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1508 MODEST_CONF_DEVICE_NAME, NULL);
1510 /* Force a redraw */
1511 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1512 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1513 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1514 gtk_tree_view_column_queue_resize (tree_column);
1520 modest_folder_view_set_style (ModestFolderView *self,
1521 ModestFolderViewStyle style)
1523 ModestFolderViewPrivate *priv;
1525 g_return_if_fail (self);
1527 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1529 priv->style = style;
1533 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1534 const gchar *account_id)
1536 ModestFolderViewPrivate *priv;
1537 GtkTreeModel *model;
1539 g_return_if_fail (self);
1541 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1543 /* This will be used by the filter_row callback,
1544 * to decided which rows to show: */
1545 if (priv->visible_account_id)
1546 g_free (priv->visible_account_id);
1547 priv->visible_account_id = g_strdup (account_id);
1550 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1551 if (GTK_IS_TREE_MODEL_FILTER (model))
1552 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1556 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1558 ModestFolderViewPrivate *priv;
1560 g_return_val_if_fail (self, NULL);
1562 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1564 return (const gchar *) priv->visible_account_id;
1568 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1572 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1575 gtk_tree_model_get (model, iter,
1576 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1577 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1581 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n",
1582 __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1585 gboolean result = FALSE;
1586 if (type == TNY_FOLDER_TYPE_INBOX) {
1588 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1589 /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1590 * when getting folders from the cache, before connectin, so we do
1591 * an extra check. We could fix this in tinymail, but it's easier
1594 if (strcmp (name, "Inbox") == 0)
1601 *inbox_iter = *iter;
1605 if (gtk_tree_model_iter_children (model, &child, iter)) {
1606 if (find_inbox_iter (model, &child, inbox_iter))
1610 } while (gtk_tree_model_iter_next (model, iter));
1618 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1620 GtkTreeModel *model;
1621 GtkTreeIter iter, inbox_iter;
1622 GtkTreeSelection *sel;
1624 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1628 expand_root_items (self);
1629 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1631 gtk_tree_model_get_iter_first (model, &iter);
1632 if (find_inbox_iter (model, &iter, &inbox_iter)) {
1633 gtk_tree_selection_select_iter (sel, &inbox_iter);
1636 gtk_tree_model_get_iter_first (model, &iter);
1637 gtk_tree_selection_select_iter (sel, &iter);