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 g_object_unref (G_OBJECT (device));
581 on_account_update (TnyAccountStore *account_store, const gchar *account,
584 if (!modest_folder_view_update_model (MODEST_FOLDER_VIEW(user_data),
586 g_printerr ("modest: failed to update model for changes in '%s'",
591 on_accounts_reloaded (TnyAccountStore *account_store,
594 ModestConf *conf = modest_runtime_get_conf ();
596 modest_widget_memory_save (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
597 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
598 modest_widget_memory_restore (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
602 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
604 GtkTreeViewColumn *col;
606 g_return_if_fail (self);
608 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
610 g_printerr ("modest: failed get column for title\n");
614 gtk_tree_view_column_set_title (col, title);
615 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
620 modest_folder_view_new (TnyFolderStoreQuery *query)
623 ModestFolderViewPrivate *priv;
624 GtkTreeSelection *sel;
626 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
627 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
630 priv->query = g_object_ref (query);
632 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
633 priv->changed_signal = g_signal_connect (sel, "changed",
634 G_CALLBACK (on_selection_changed), self);
636 return GTK_WIDGET(self);
639 /* this feels dirty; any other way to expand all the root items? */
641 expand_root_items (ModestFolderView *self)
644 path = gtk_tree_path_new_first ();
646 /* all folders should have child items, so.. */
647 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
648 gtk_tree_path_next (path);
650 gtk_tree_path_free (path);
654 * We use this function to implement the
655 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
656 * account in this case, and the local folders.
659 filter_row (GtkTreeModel *model,
663 gboolean retval = TRUE;
664 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
665 GObject *instance = NULL;
667 gtk_tree_model_get (model, iter,
668 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
669 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
672 /* Do not show if there is no instance, this could indeed
673 happen when the model is being modified while it's being
674 drawn. This could occur for example when moving folders
679 if (type == TNY_FOLDER_TYPE_ROOT) {
680 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
681 if (TNY_IS_ACCOUNT (instance)) {
682 TnyAccount *acc = TNY_ACCOUNT (instance);
683 const gchar *account_id = tny_account_get_id (acc);
685 /* If it isn't a special folder,
686 * don't show it unless it is the visible account: */
687 if (!modest_tny_account_is_virtual_local_folders (acc) &&
688 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
689 /* Show only the visible account id */
690 ModestFolderViewPrivate *priv =
691 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
692 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
698 /* The virtual local-folders folder store is also shown by default. */
700 g_object_unref (instance);
706 modest_folder_view_update_model (ModestFolderView *self,
707 TnyAccountStore *account_store)
709 ModestFolderViewPrivate *priv;
710 GtkTreeModel *model /* , *old_model */;
711 /* TnyAccount *local_account; */
712 TnyList *model_as_list;
714 g_return_val_if_fail (account_store, FALSE);
716 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
718 /* Notify that there is no folder selected */
719 g_signal_emit (G_OBJECT(self),
720 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
723 /* FIXME: the local accounts are not shown when the query
724 selects only the subscribed folders. */
725 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
726 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
728 /* Deal with the model via its TnyList Interface,
729 * filling the TnyList via a get_accounts() call: */
730 model_as_list = TNY_LIST(model);
732 /* Get the accounts: */
733 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
735 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
736 g_object_unref (model_as_list);
737 model_as_list = NULL;
739 GtkTreeModel *filter_model = NULL, *sortable = NULL;
741 sortable = gtk_tree_model_sort_new_with_model (model);
742 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
743 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
745 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
746 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
747 cmp_rows, NULL, NULL);
749 /* Create filter model */
750 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
751 filter_model = gtk_tree_model_filter_new (sortable, NULL);
752 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
759 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
760 (filter_model) ? filter_model : sortable);
761 expand_root_items (self); /* expand all account folders */
763 g_object_unref (model);
766 g_object_unref (filter_model);
768 g_object_unref (sortable);
775 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
778 TnyFolderStore *folder = NULL;
780 ModestFolderView *tree_view;
781 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_INSTANCE_COLUMN, &folder,
807 /* If the folder is the same do not notify */
808 if (priv->cur_folder_store == folder) {
809 g_object_unref (folder);
813 /* Current folder was unselected */
814 if (priv->cur_folder_store) {
815 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
816 priv->cur_folder_store, FALSE);
817 g_object_unref (priv->cur_folder_store);
820 /* New current references */
821 priv->cur_folder_store = folder;
823 /* New folder has been selected */
824 g_signal_emit (G_OBJECT(tree_view),
825 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
830 modest_folder_view_get_selected (ModestFolderView *self)
832 ModestFolderViewPrivate *priv;
834 g_return_val_if_fail (self, NULL);
836 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
837 if (priv->cur_folder_store)
838 g_object_ref (priv->cur_folder_store);
840 return priv->cur_folder_store;
844 get_cmp_rows_type_pos (GObject *folder)
846 /* Remote accounts -> Local account -> MMC account .*/
849 if (TNY_IS_ACCOUNT (folder) &&
850 modest_tny_account_is_virtual_local_folders (
851 TNY_ACCOUNT (folder))) {
853 } else if (TNY_IS_ACCOUNT (folder)) {
854 TnyAccount *account = TNY_ACCOUNT (folder);
855 const gchar *account_id = tny_account_get_id (account);
856 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
862 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
863 return -1; /* Should never happen */
868 * This function orders the mail accounts according to these rules:
869 * 1st - remote accounts
870 * 2nd - local account
874 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
880 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
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)
1570 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1573 gtk_tree_model_get (model, iter,
1574 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1575 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1579 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n",
1580 __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1583 gboolean result = FALSE;
1584 if (type == TNY_FOLDER_TYPE_INBOX) {
1586 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1587 /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1588 * when getting folders from the cache, before connectin, so we do
1589 * an extra check. We could fix this in tinymail, but it's easier
1592 if (strcmp (name, "Inbox") == 0)
1599 *inbox_iter = *iter;
1603 if (gtk_tree_model_iter_children (model, &child, iter)) {
1604 if (find_inbox_iter (model, &child, inbox_iter))
1608 } while (gtk_tree_model_iter_next (model, iter));
1616 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1618 GtkTreeModel *model;
1619 GtkTreeIter iter, inbox_iter;
1620 GtkTreeSelection *sel;
1622 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1626 expand_root_items (self);
1627 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1629 gtk_tree_model_get_iter_first (model, &iter);
1630 if (find_inbox_iter (model, &iter, &inbox_iter)) {
1631 gtk_tree_selection_select_iter (sel, &inbox_iter);
1634 gtk_tree_model_get_iter_first (model, &iter);
1635 gtk_tree_selection_select_iter (sel, &iter);