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 GObject *instance = NULL;
242 g_return_if_fail (column);
243 g_return_if_fail (tree_model);
245 gtk_tree_model_get (tree_model, iter,
246 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
247 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
248 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
249 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
250 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
252 rendobj = G_OBJECT(renderer);
263 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
265 gchar *item_name = NULL;
266 gint item_weight = 400;
268 if (type != TNY_FOLDER_TYPE_ROOT) {
271 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
273 type = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
274 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
276 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
280 /* Select the number to show */
281 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
286 /* Use bold font style if there are unread messages */
288 item_name = g_strdup_printf ("%s (%d)", fname, unread);
291 item_name = g_strdup (fname);
295 } else if (TNY_IS_ACCOUNT (instance)) {
296 /* If it's a server account */
297 if (modest_tny_account_is_virtual_local_folders (
298 TNY_ACCOUNT (instance))) {
299 item_name = g_strdup (priv->local_account_name);
302 item_name = g_strdup (fname);
308 item_name = g_strdup ("unknown");
310 if (item_name && item_weight) {
311 /* Set the name in the treeview cell: */
312 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
314 /* Notify display name observers */
315 if (G_OBJECT (priv->cur_folder_store) == instance) {
316 g_signal_emit (G_OBJECT(data),
317 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
325 g_object_unref (G_OBJECT (instance));
332 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
333 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
335 GObject *rendobj = NULL, *instance = NULL;
336 GdkPixbuf *pixbuf = NULL;
339 const gchar *account_id = NULL;
342 rendobj = G_OBJECT(renderer);
343 gtk_tree_model_get (tree_model, iter,
344 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
345 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
346 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
347 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
358 if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
359 type = modest_tny_folder_guess_folder_type_from_name (fname);
363 case TNY_FOLDER_TYPE_ROOT:
364 if (TNY_IS_ACCOUNT (instance)) {
366 if (modest_tny_account_is_virtual_local_folders (
367 TNY_ACCOUNT (instance))) {
368 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
371 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
373 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
374 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
376 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
380 case TNY_FOLDER_TYPE_INBOX:
381 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
383 case TNY_FOLDER_TYPE_OUTBOX:
384 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
386 case TNY_FOLDER_TYPE_JUNK:
387 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
389 case TNY_FOLDER_TYPE_SENT:
390 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
392 case TNY_FOLDER_TYPE_TRASH:
393 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
395 case TNY_FOLDER_TYPE_DRAFTS:
396 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
398 case TNY_FOLDER_TYPE_NORMAL:
400 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
404 g_object_unref (G_OBJECT (instance));
408 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
411 g_object_unref (pixbuf);
415 add_columns (GtkWidget *treeview)
417 GtkTreeViewColumn *column;
418 GtkCellRenderer *renderer;
419 GtkTreeSelection *sel;
422 column = gtk_tree_view_column_new ();
424 /* Set icon and text render function */
425 renderer = gtk_cell_renderer_pixbuf_new();
426 gtk_tree_view_column_pack_start (column, renderer, FALSE);
427 gtk_tree_view_column_set_cell_data_func(column, renderer,
428 icon_cell_data, treeview, NULL);
430 renderer = gtk_cell_renderer_text_new();
431 gtk_tree_view_column_pack_start (column, renderer, FALSE);
432 gtk_tree_view_column_set_cell_data_func(column, renderer,
433 text_cell_data, treeview, NULL);
435 /* Set selection mode */
436 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
437 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
439 /* Set treeview appearance */
440 gtk_tree_view_column_set_spacing (column, 2);
441 gtk_tree_view_column_set_resizable (column, TRUE);
442 gtk_tree_view_column_set_fixed_width (column, TRUE);
443 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
444 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
447 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
451 modest_folder_view_init (ModestFolderView *obj)
453 ModestFolderViewPrivate *priv;
456 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
458 priv->timer_expander = 0;
459 priv->account_store = NULL;
461 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
462 priv->cur_folder_store = NULL;
463 priv->visible_account_id = NULL;
465 /* Initialize the local account name */
466 conf = modest_runtime_get_conf();
467 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
470 add_columns (GTK_WIDGET (obj));
472 /* Setup drag and drop */
473 setup_drag_and_drop (GTK_TREE_VIEW(obj));
475 /* Connect signals */
476 g_signal_connect (G_OBJECT (obj),
478 G_CALLBACK (on_key_pressed), NULL);
481 * Track changes in the local account name (in the device it
482 * will be the device name)
484 g_signal_connect (G_OBJECT(conf),
486 G_CALLBACK(on_configuration_key_changed), obj);
491 tny_account_store_view_init (gpointer g, gpointer iface_data)
493 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
495 klass->set_account_store_func = modest_folder_view_set_account_store;
501 modest_folder_view_finalize (GObject *obj)
503 ModestFolderViewPrivate *priv;
504 GtkTreeSelection *sel;
506 g_return_if_fail (obj);
508 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
510 if (priv->timer_expander != 0) {
511 g_source_remove (priv->timer_expander);
512 priv->timer_expander = 0;
515 if (priv->account_store) {
516 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
517 priv->account_update_signal);
518 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
519 priv->accounts_reloaded_signal);
520 g_object_unref (G_OBJECT(priv->account_store));
521 priv->account_store = NULL;
525 g_object_unref (G_OBJECT (priv->query));
529 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
531 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
533 g_free (priv->local_account_name);
534 g_free (priv->visible_account_id);
536 G_OBJECT_CLASS(parent_class)->finalize (obj);
541 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
543 ModestFolderViewPrivate *priv;
546 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
547 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
549 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
550 device = tny_account_store_get_device (account_store);
552 if (G_UNLIKELY (priv->account_store)) {
554 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
555 priv->account_update_signal))
556 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
557 priv->account_update_signal);
558 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
559 priv->accounts_reloaded_signal))
560 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
561 priv->accounts_reloaded_signal);
563 g_object_unref (G_OBJECT (priv->account_store));
566 priv->account_store = g_object_ref (G_OBJECT (account_store));
568 priv->account_update_signal =
569 g_signal_connect (G_OBJECT(account_store), "account_update",
570 G_CALLBACK (on_account_update), self);
572 priv->accounts_reloaded_signal =
573 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
574 G_CALLBACK (on_accounts_reloaded), self);
576 g_object_unref (G_OBJECT (device));
580 on_account_update (TnyAccountStore *account_store, const gchar *account,
583 if (!modest_folder_view_update_model (MODEST_FOLDER_VIEW(user_data),
585 g_printerr ("modest: failed to update model for changes in '%s'",
590 on_accounts_reloaded (TnyAccountStore *account_store,
593 ModestConf *conf = modest_runtime_get_conf ();
595 modest_widget_memory_save (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
596 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
597 modest_widget_memory_restore (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
601 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
603 GtkTreeViewColumn *col;
605 g_return_if_fail (self);
607 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
609 g_printerr ("modest: failed get column for title\n");
613 gtk_tree_view_column_set_title (col, title);
614 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
619 modest_folder_view_new (TnyFolderStoreQuery *query)
622 ModestFolderViewPrivate *priv;
623 GtkTreeSelection *sel;
625 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
626 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
629 priv->query = g_object_ref (query);
631 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
632 priv->changed_signal = g_signal_connect (sel, "changed",
633 G_CALLBACK (on_selection_changed), self);
635 return GTK_WIDGET(self);
638 /* this feels dirty; any other way to expand all the root items? */
640 expand_root_items (ModestFolderView *self)
643 path = gtk_tree_path_new_first ();
645 /* all folders should have child items, so.. */
646 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
647 gtk_tree_path_next (path);
649 gtk_tree_path_free (path);
653 * We use this function to implement the
654 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
655 * account in this case, and the local folders.
658 filter_row (GtkTreeModel *model,
662 gboolean retval = TRUE;
664 GObject *instance = NULL;
666 gtk_tree_model_get (model, iter,
667 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
668 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
671 /* Do not show if there is no instance, this could indeed
672 happen when the model is being modified while it's being
673 drawn. This could occur for example when moving folders
678 if (type == TNY_FOLDER_TYPE_ROOT) {
679 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
680 if (TNY_IS_ACCOUNT (instance)) {
681 TnyAccount *acc = TNY_ACCOUNT (instance);
682 const gchar *account_id = tny_account_get_id (acc);
684 /* If it isn't a special folder,
685 * don't show it unless it is the visible account: */
686 if (!modest_tny_account_is_virtual_local_folders (acc) &&
687 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
688 /* Show only the visible account id */
689 ModestFolderViewPrivate *priv =
690 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
691 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
697 /* The virtual local-folders folder store is also shown by default. */
699 g_object_unref (instance);
705 modest_folder_view_update_model (ModestFolderView *self,
706 TnyAccountStore *account_store)
708 ModestFolderViewPrivate *priv;
709 GtkTreeModel *model /* , *old_model */;
710 /* TnyAccount *local_account; */
711 TnyList *model_as_list;
713 g_return_val_if_fail (account_store, FALSE);
715 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
717 /* Notify that there is no folder selected */
718 g_signal_emit (G_OBJECT(self),
719 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
722 /* FIXME: the local accounts are not shown when the query
723 selects only the subscribed folders. */
724 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
725 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
727 /* Deal with the model via its TnyList Interface,
728 * filling the TnyList via a get_accounts() call: */
729 model_as_list = TNY_LIST(model);
731 /* Get the accounts: */
732 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
734 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
735 g_object_unref (model_as_list);
736 model_as_list = NULL;
738 GtkTreeModel *filter_model = NULL, *sortable = NULL;
740 sortable = gtk_tree_model_sort_new_with_model (model);
741 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
742 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
744 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
745 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
746 cmp_rows, NULL, NULL);
748 /* Create filter model */
749 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
750 filter_model = gtk_tree_model_filter_new (sortable, NULL);
751 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
758 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
759 (filter_model) ? filter_model : sortable);
760 expand_root_items (self); /* expand all account folders */
762 g_object_unref (model);
765 g_object_unref (filter_model);
767 g_object_unref (sortable);
774 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
777 TnyFolderStore *folder = NULL;
779 ModestFolderView *tree_view;
780 ModestFolderViewPrivate *priv;
783 g_return_if_fail (sel);
784 g_return_if_fail (user_data);
786 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
788 /* folder was _un_selected if true */
789 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
790 if (priv->cur_folder_store)
791 g_object_unref (priv->cur_folder_store);
792 priv->cur_folder_store = NULL;
794 /* Notify the display name observers */
795 g_signal_emit (G_OBJECT(user_data),
796 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
801 tree_view = MODEST_FOLDER_VIEW (user_data);
803 gtk_tree_model_get (model, &iter,
804 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
805 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
808 /* If the folder is the same do not notify */
809 if (priv->cur_folder_store == folder) {
810 g_object_unref (folder);
814 /* Current folder was unselected */
815 if (priv->cur_folder_store) {
816 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
817 priv->cur_folder_store, FALSE);
818 g_object_unref (priv->cur_folder_store);
821 /* New current references */
822 priv->cur_folder_store = folder;
824 /* New folder has been selected */
825 g_signal_emit (G_OBJECT(tree_view),
826 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
831 modest_folder_view_get_selected (ModestFolderView *self)
833 ModestFolderViewPrivate *priv;
835 g_return_val_if_fail (self, NULL);
837 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
838 if (priv->cur_folder_store)
839 g_object_ref (priv->cur_folder_store);
841 return priv->cur_folder_store;
845 get_cmp_rows_type_pos (GObject *folder)
847 /* Remote accounts -> Local account -> MMC account .*/
850 if (TNY_IS_ACCOUNT (folder) &&
851 modest_tny_account_is_virtual_local_folders (
852 TNY_ACCOUNT (folder))) {
854 } else if (TNY_IS_ACCOUNT (folder)) {
855 TnyAccount *account = TNY_ACCOUNT (folder);
856 const gchar *account_id = tny_account_get_id (account);
857 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
863 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
864 return -1; /* Should never happen */
869 * This function orders the mail accounts according to these rules:
870 * 1st - remote accounts
871 * 2nd - local account
875 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
879 gchar *name1, *name2;
881 GObject *folder1 = NULL;
882 GObject *folder2 = NULL;
884 gtk_tree_model_get (tree_model, iter1,
885 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
886 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
887 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
889 gtk_tree_model_get (tree_model, iter2,
890 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
891 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
894 if (type == TNY_FOLDER_TYPE_ROOT) {
895 /* Compare the types, so that
896 * Remote accounts -> Local account -> MMC account .*/
897 const gint pos1 = get_cmp_rows_type_pos (folder1);
898 const gint pos2 = get_cmp_rows_type_pos (folder2);
899 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
900 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
903 else if (pos1 > pos2)
906 /* Compare items of the same type: */
908 TnyAccount *account1 = NULL;
909 if (TNY_IS_ACCOUNT (folder1))
910 account1 = TNY_ACCOUNT (folder1);
912 TnyAccount *account2 = NULL;
913 if (TNY_IS_ACCOUNT (folder2))
914 account2 = TNY_ACCOUNT (folder2);
916 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
917 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
919 if (!account_id && !account_id2)
921 else if (!account_id)
923 else if (!account_id2)
925 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
928 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
931 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
935 g_object_unref(G_OBJECT(folder1));
937 g_object_unref(G_OBJECT(folder2));
945 /*****************************************************************************/
946 /* DRAG and DROP stuff */
947 /*****************************************************************************/
950 * This function fills the #GtkSelectionData with the row and the
951 * model that has been dragged. It's called when this widget is a
952 * source for dnd after the event drop happened
955 on_drag_data_get (GtkWidget *widget,
956 GdkDragContext *context,
957 GtkSelectionData *selection_data,
962 GtkTreeSelection *selection;
965 GtkTreePath *source_row;
967 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
968 gtk_tree_selection_get_selected (selection, &model, &iter);
969 source_row = gtk_tree_model_get_path (model, &iter);
971 gtk_tree_set_row_drag_data (selection_data,
975 gtk_tree_path_free (source_row);
978 typedef struct _DndHelper {
979 gboolean delete_source;
980 GtkTreePath *source_row;
981 GdkDragContext *context;
987 * This function is the callback of the
988 * modest_mail_operation_xfer_msgs () and
989 * modest_mail_operation_xfer_folder() calls. We check here if the
990 * message/folder was correctly asynchronously transferred. The reason
991 * to use the same callback is that the code is the same, it only has
992 * to check that the operation went fine and then finalize the drag
996 on_progress_changed (ModestMailOperation *mail_op,
997 ModestMailOperationState *state,
1003 helper = (DndHelper *) user_data;
1005 if (!state->finished)
1008 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1014 /* Notify the drag source. Never call delete, the monitor will
1015 do the job if needed */
1016 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1018 /* Free the helper */
1019 gtk_tree_path_free (helper->source_row);
1020 g_slice_free (DndHelper, helper);
1024 * This function is used by drag_data_received_cb to manage drag and
1025 * drop of a header, i.e, and drag from the header view to the folder
1029 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1030 GtkTreeModel *dest_model,
1031 GtkTreePath *dest_row,
1034 TnyList *headers = NULL;
1035 TnyHeader *header = NULL;
1036 TnyFolder *folder = NULL;
1037 ModestMailOperation *mail_op = NULL;
1038 GtkTreeIter source_iter, dest_iter;
1040 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1041 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1042 g_return_if_fail (dest_row);
1043 g_return_if_fail (helper);
1046 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1047 gtk_tree_model_get (source_model, &source_iter,
1048 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1050 if (!TNY_IS_HEADER(header)) {
1051 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1056 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1057 gtk_tree_model_get (dest_model, &dest_iter,
1058 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1061 if (!TNY_IS_FOLDER(folder)) {
1062 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1066 /* Transfer message */
1067 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1068 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1070 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1071 G_CALLBACK (on_progress_changed), helper);
1073 headers = tny_simple_list_new ();
1074 tny_list_append (headers, G_OBJECT (header));
1075 modest_mail_operation_xfer_msgs (mail_op,
1078 helper->delete_source,
1083 if (G_IS_OBJECT(mail_op))
1084 g_object_unref (G_OBJECT (mail_op));
1085 if (G_IS_OBJECT(header))
1086 g_object_unref (G_OBJECT (header));
1087 if (G_IS_OBJECT(folder))
1088 g_object_unref (G_OBJECT (folder));
1089 if (G_IS_OBJECT(headers))
1090 g_object_unref (headers);
1094 * This function is used by drag_data_received_cb to manage drag and
1095 * drop of a folder, i.e, and drag from the folder view to the same
1099 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1100 GtkTreeModel *dest_model,
1101 GtkTreePath *dest_row,
1102 GtkSelectionData *selection_data,
1105 ModestMailOperation *mail_op;
1106 GtkTreeIter parent_iter, iter;
1107 TnyFolderStore *parent_folder;
1110 /* Check if the drag is possible */
1111 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1112 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1114 /* selection_data)) { */
1115 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1117 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1118 gtk_tree_path_free (helper->source_row);
1119 g_slice_free (DndHelper, helper);
1124 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1125 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1126 gtk_tree_model_get (source_model, &parent_iter,
1127 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1128 &parent_folder, -1);
1129 gtk_tree_model_get (source_model, &iter,
1130 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1133 /* Do the mail operation */
1134 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1136 modest_ui_actions_move_folder_error_handler,
1138 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1140 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1141 G_CALLBACK (on_progress_changed), helper);
1143 modest_mail_operation_xfer_folder (mail_op,
1146 helper->delete_source);
1149 g_object_unref (G_OBJECT (parent_folder));
1150 g_object_unref (G_OBJECT (folder));
1151 g_object_unref (G_OBJECT (mail_op));
1155 * This function receives the data set by the "drag-data-get" signal
1156 * handler. This information comes within the #GtkSelectionData. This
1157 * function will manage both the drags of folders of the treeview and
1158 * drags of headers of the header view widget.
1161 on_drag_data_received (GtkWidget *widget,
1162 GdkDragContext *context,
1165 GtkSelectionData *selection_data,
1170 GtkWidget *source_widget;
1171 GtkTreeModel *dest_model, *source_model;
1172 GtkTreePath *source_row, *dest_row;
1173 GtkTreeViewDropPosition pos;
1174 gboolean success = FALSE, delete_source = FALSE;
1175 DndHelper *helper = NULL;
1177 /* Do not allow further process */
1178 g_signal_stop_emission_by_name (widget, "drag-data-received");
1179 source_widget = gtk_drag_get_source_widget (context);
1181 /* Get the action */
1182 if (context->action == GDK_ACTION_MOVE) {
1183 delete_source = TRUE;
1185 /* Notify that there is no folder selected. We need to
1186 do this in order to update the headers view (and
1187 its monitors, because when moving, the old folder
1188 won't longer exist. We can not wait for the end of
1189 the operation, because the operation won't start if
1190 the folder is in use */
1191 if (source_widget == widget)
1192 g_signal_emit (G_OBJECT (widget),
1193 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1196 /* Check if the get_data failed */
1197 if (selection_data == NULL || selection_data->length < 0)
1198 gtk_drag_finish (context, success, FALSE, time);
1200 /* Get the models */
1201 gtk_tree_get_row_drag_data (selection_data,
1205 /* Select the destination model */
1206 if (source_widget == widget) {
1207 dest_model = source_model;
1209 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1212 /* Get the path to the destination row. Can not call
1213 gtk_tree_view_get_drag_dest_row() because the source row
1214 is not selected anymore */
1215 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1218 /* Only allow drops IN other rows */
1219 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1220 gtk_drag_finish (context, success, FALSE, time);
1222 /* Create the helper */
1223 helper = g_slice_new0 (DndHelper);
1224 helper->delete_source = delete_source;
1225 helper->source_row = gtk_tree_path_copy (source_row);
1226 helper->context = context;
1227 helper->time = time;
1229 /* Drags from the header view */
1230 if (source_widget != widget) {
1232 drag_and_drop_from_header_view (source_model,
1239 drag_and_drop_from_folder_view (source_model,
1247 gtk_tree_path_free (source_row);
1248 gtk_tree_path_free (dest_row);
1252 * We define a "drag-drop" signal handler because we do not want to
1253 * use the default one, because the default one always calls
1254 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1255 * signal handler, because there we have all the information available
1256 * to know if the dnd was a success or not.
1259 drag_drop_cb (GtkWidget *widget,
1260 GdkDragContext *context,
1268 if (!context->targets)
1271 /* Check if we're dragging a folder row */
1272 target = gtk_drag_dest_find_target (widget, context, NULL);
1274 /* Request the data from the source. */
1275 gtk_drag_get_data(widget, context, target, time);
1281 * This function expands a node of a tree view if it's not expanded
1282 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1283 * does that, so that's why they're here.
1286 expand_row_timeout (gpointer data)
1288 GtkTreeView *tree_view = data;
1289 GtkTreePath *dest_path = NULL;
1290 GtkTreeViewDropPosition pos;
1291 gboolean result = FALSE;
1293 GDK_THREADS_ENTER ();
1295 gtk_tree_view_get_drag_dest_row (tree_view,
1300 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1301 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1302 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1303 gtk_tree_path_free (dest_path);
1307 gtk_tree_path_free (dest_path);
1312 GDK_THREADS_LEAVE ();
1318 * This function is called whenever the pointer is moved over a widget
1319 * while dragging some data. It installs a timeout that will expand a
1320 * node of the treeview if not expanded yet. This function also calls
1321 * gdk_drag_status in order to set the suggested action that will be
1322 * used by the "drag-data-received" signal handler to know if we
1323 * should do a move or just a copy of the data.
1326 on_drag_motion (GtkWidget *widget,
1327 GdkDragContext *context,
1333 GtkTreeViewDropPosition pos;
1334 GtkTreePath *dest_row;
1335 ModestFolderViewPrivate *priv;
1336 GdkDragAction suggested_action;
1337 gboolean valid_location = FALSE;
1339 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1341 if (priv->timer_expander != 0) {
1342 g_source_remove (priv->timer_expander);
1343 priv->timer_expander = 0;
1346 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1351 /* Do not allow drops between folders */
1353 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1354 pos == GTK_TREE_VIEW_DROP_AFTER) {
1355 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1356 gdk_drag_status(context, 0, time);
1357 valid_location = FALSE;
1360 valid_location = TRUE;
1363 /* Expand the selected row after 1/2 second */
1364 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1365 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1366 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1369 /* Select the desired action. By default we pick MOVE */
1370 suggested_action = GDK_ACTION_MOVE;
1372 if (context->actions == GDK_ACTION_COPY)
1373 gdk_drag_status(context, GDK_ACTION_COPY, time);
1374 else if (context->actions == GDK_ACTION_MOVE)
1375 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1376 else if (context->actions & suggested_action)
1377 gdk_drag_status(context, suggested_action, time);
1379 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1383 gtk_tree_path_free (dest_row);
1384 g_signal_stop_emission_by_name (widget, "drag-motion");
1385 return valid_location;
1389 /* Folder view drag types */
1390 const GtkTargetEntry folder_view_drag_types[] =
1392 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1393 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1397 * This function sets the treeview as a source and a target for dnd
1398 * events. It also connects all the requirede signals.
1401 setup_drag_and_drop (GtkTreeView *self)
1403 /* Set up the folder view as a dnd destination. Set only the
1404 highlight flag, otherwise gtk will have a different
1406 gtk_drag_dest_set (GTK_WIDGET (self),
1407 GTK_DEST_DEFAULT_HIGHLIGHT,
1408 folder_view_drag_types,
1409 G_N_ELEMENTS (folder_view_drag_types),
1410 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1412 g_signal_connect (G_OBJECT (self),
1413 "drag_data_received",
1414 G_CALLBACK (on_drag_data_received),
1418 /* Set up the treeview as a dnd source */
1419 gtk_drag_source_set (GTK_WIDGET (self),
1421 folder_view_drag_types,
1422 G_N_ELEMENTS (folder_view_drag_types),
1423 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1425 g_signal_connect (G_OBJECT (self),
1427 G_CALLBACK (on_drag_motion),
1430 g_signal_connect (G_OBJECT (self),
1432 G_CALLBACK (on_drag_data_get),
1435 g_signal_connect (G_OBJECT (self),
1437 G_CALLBACK (drag_drop_cb),
1442 * This function manages the navigation through the folders using the
1443 * keyboard or the hardware keys in the device
1446 on_key_pressed (GtkWidget *self,
1450 GtkTreeSelection *selection;
1452 GtkTreeModel *model;
1453 gboolean retval = FALSE;
1455 /* Up and Down are automatically managed by the treeview */
1456 if (event->keyval == GDK_Return) {
1457 /* Expand/Collapse the selected row */
1458 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1459 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1462 path = gtk_tree_model_get_path (model, &iter);
1464 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1465 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1467 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1468 gtk_tree_path_free (path);
1470 /* No further processing */
1478 * We listen to the changes in the local folder account name key,
1479 * because we want to show the right name in the view. The local
1480 * folder account name corresponds to the device name in the Maemo
1481 * version. We do this because we do not want to query gconf on each
1482 * tree view refresh. It's better to cache it and change whenever
1486 on_configuration_key_changed (ModestConf* conf,
1488 ModestConfEvent event,
1489 ModestFolderView *self)
1491 ModestFolderViewPrivate *priv;
1496 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1497 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1499 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1500 g_free (priv->local_account_name);
1502 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1503 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1505 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1506 MODEST_CONF_DEVICE_NAME, NULL);
1508 /* Force a redraw */
1509 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1510 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1511 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1512 gtk_tree_view_column_queue_resize (tree_column);
1518 modest_folder_view_set_style (ModestFolderView *self,
1519 ModestFolderViewStyle style)
1521 ModestFolderViewPrivate *priv;
1523 g_return_if_fail (self);
1525 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1527 priv->style = style;
1531 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1532 const gchar *account_id)
1534 ModestFolderViewPrivate *priv;
1535 GtkTreeModel *model;
1537 g_return_if_fail (self);
1539 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1541 /* This will be used by the filter_row callback,
1542 * to decided which rows to show: */
1543 if (priv->visible_account_id)
1544 g_free (priv->visible_account_id);
1545 priv->visible_account_id = g_strdup (account_id);
1548 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1549 if (GTK_IS_TREE_MODEL_FILTER (model))
1550 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1554 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1556 ModestFolderViewPrivate *priv;
1558 g_return_val_if_fail (self, NULL);
1560 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1562 return (const gchar *) priv->visible_account_id;
1566 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1572 gtk_tree_model_get (model, iter,
1573 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1576 if (type == TNY_FOLDER_TYPE_INBOX) {
1577 *inbox_iter = *iter;
1581 if (gtk_tree_model_iter_children (model, &child, iter)) {
1582 if (find_inbox_iter (model, &child, inbox_iter))
1586 } while (gtk_tree_model_iter_next (model, iter));
1594 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1596 GtkTreeModel *model;
1597 GtkTreeIter iter, inbox_iter;
1598 GtkTreeSelection *sel;
1600 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1604 expand_root_items (self);
1605 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1607 gtk_tree_model_get_iter_first (model, &iter);
1608 if (find_inbox_iter (model, &iter, &inbox_iter))
1609 gtk_tree_selection_select_iter (sel, &inbox_iter);
1611 gtk_tree_model_get_iter_first (model, &iter);
1612 gtk_tree_selection_select_iter (sel, &iter);