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-tny-outbox-account.h>
47 #include <modest-marshal.h>
48 #include <modest-icon-names.h>
49 #include <modest-tny-account-store.h>
50 #include <modest-text-utils.h>
51 #include <modest-runtime.h>
52 #include "modest-folder-view.h"
53 #include <modest-dnd.h>
54 #include <modest-platform.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_account_removed (TnyAccountStore *self,
79 static void on_accounts_reloaded (TnyAccountStore *store,
82 static gint cmp_rows (GtkTreeModel *tree_model,
87 static gboolean filter_row (GtkTreeModel *model,
91 static gboolean on_key_pressed (GtkWidget *self,
95 static void on_configuration_key_changed (ModestConf* conf,
97 ModestConfEvent event,
98 ModestFolderView *self);
101 static void on_drag_data_get (GtkWidget *widget,
102 GdkDragContext *context,
103 GtkSelectionData *selection_data,
108 static void on_drag_data_received (GtkWidget *widget,
109 GdkDragContext *context,
112 GtkSelectionData *selection_data,
117 static gboolean on_drag_motion (GtkWidget *widget,
118 GdkDragContext *context,
124 static gint expand_row_timeout (gpointer data);
126 static void setup_drag_and_drop (GtkTreeView *self);
129 FOLDER_SELECTION_CHANGED_SIGNAL,
130 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
134 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
135 struct _ModestFolderViewPrivate {
136 TnyAccountStore *account_store;
137 TnyFolderStore *cur_folder_store;
139 gulong account_update_signal;
140 gulong changed_signal;
141 gulong accounts_reloaded_signal;
142 gulong account_removed_signal;
144 TnyFolderStoreQuery *query;
145 guint timer_expander;
147 gchar *local_account_name;
148 gchar *visible_account_id;
149 ModestFolderViewStyle style;
151 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
152 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
153 MODEST_TYPE_FOLDER_VIEW, \
154 ModestFolderViewPrivate))
156 static GObjectClass *parent_class = NULL;
158 static guint signals[LAST_SIGNAL] = {0};
161 modest_folder_view_get_type (void)
163 static GType my_type = 0;
165 static const GTypeInfo my_info = {
166 sizeof(ModestFolderViewClass),
167 NULL, /* base init */
168 NULL, /* base finalize */
169 (GClassInitFunc) modest_folder_view_class_init,
170 NULL, /* class finalize */
171 NULL, /* class data */
172 sizeof(ModestFolderView),
174 (GInstanceInitFunc) modest_folder_view_init,
178 static const GInterfaceInfo tny_account_store_view_info = {
179 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
180 NULL, /* interface_finalize */
181 NULL /* interface_data */
185 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
189 g_type_add_interface_static (my_type,
190 TNY_TYPE_ACCOUNT_STORE_VIEW,
191 &tny_account_store_view_info);
197 modest_folder_view_class_init (ModestFolderViewClass *klass)
199 GObjectClass *gobject_class;
200 gobject_class = (GObjectClass*) klass;
202 parent_class = g_type_class_peek_parent (klass);
203 gobject_class->finalize = modest_folder_view_finalize;
205 g_type_class_add_private (gobject_class,
206 sizeof(ModestFolderViewPrivate));
208 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
209 g_signal_new ("folder_selection_changed",
210 G_TYPE_FROM_CLASS (gobject_class),
212 G_STRUCT_OFFSET (ModestFolderViewClass,
213 folder_selection_changed),
215 modest_marshal_VOID__POINTER_BOOLEAN,
216 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
219 * This signal is emitted whenever the currently selected
220 * folder display name is computed. Note that the name could
221 * be different to the folder name, because we could append
222 * the unread messages count to the folder name to build the
223 * folder display name
225 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
226 g_signal_new ("folder-display-name-changed",
227 G_TYPE_FROM_CLASS (gobject_class),
229 G_STRUCT_OFFSET (ModestFolderViewClass,
230 folder_display_name_changed),
232 g_cclosure_marshal_VOID__STRING,
233 G_TYPE_NONE, 1, G_TYPE_STRING);
237 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
238 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
240 ModestFolderViewPrivate *priv;
245 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
246 GObject *instance = NULL;
248 g_return_if_fail (column);
249 g_return_if_fail (tree_model);
251 gtk_tree_model_get (tree_model, iter,
252 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
253 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
254 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
255 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
256 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
258 rendobj = G_OBJECT(renderer);
269 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
271 gchar *item_name = NULL;
272 gint item_weight = 400;
274 if (type != TNY_FOLDER_TYPE_ROOT) {
277 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
278 TnyFolderType folder_type
279 = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
280 if (folder_type != TNY_FOLDER_TYPE_UNKNOWN) {
282 fname = g_strdup(modest_local_folder_info_get_type_display_name (folder_type));
286 /* Select the number to show */
287 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
292 /* Use bold font style if there are unread messages */
294 item_name = g_strdup_printf ("%s (%d)", fname, unread);
297 item_name = g_strdup (fname);
301 } else if (TNY_IS_ACCOUNT (instance)) {
302 /* If it's a server account */
303 if (modest_tny_account_is_virtual_local_folders (
304 TNY_ACCOUNT (instance))) {
305 item_name = g_strdup (priv->local_account_name);
308 item_name = g_strdup (fname);
314 item_name = g_strdup ("unknown");
316 if (item_name && item_weight) {
317 /* Set the name in the treeview cell: */
318 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
320 /* Notify display name observers */
321 if (G_OBJECT (priv->cur_folder_store) == instance) {
322 g_signal_emit (G_OBJECT(data),
323 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
331 g_object_unref (G_OBJECT (instance));
338 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
339 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
341 GObject *rendobj = NULL, *instance = NULL;
342 GdkPixbuf *pixbuf = NULL;
343 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
345 const gchar *account_id = NULL;
348 rendobj = G_OBJECT(renderer);
349 gtk_tree_model_get (tree_model, iter,
350 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
351 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
352 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
353 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
364 if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
365 type = modest_tny_folder_guess_folder_type_from_name (fname);
369 case TNY_FOLDER_TYPE_ROOT:
370 if (TNY_IS_ACCOUNT (instance)) {
372 if (modest_tny_account_is_virtual_local_folders (
373 TNY_ACCOUNT (instance))) {
374 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
377 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
379 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
380 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
382 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
386 case TNY_FOLDER_TYPE_INBOX:
387 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
389 case TNY_FOLDER_TYPE_OUTBOX:
390 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
392 case TNY_FOLDER_TYPE_JUNK:
393 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
395 case TNY_FOLDER_TYPE_SENT:
396 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
398 case TNY_FOLDER_TYPE_TRASH:
399 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
401 case TNY_FOLDER_TYPE_DRAFTS:
402 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
404 case TNY_FOLDER_TYPE_NORMAL:
406 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
410 g_object_unref (G_OBJECT (instance));
414 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
417 g_object_unref (pixbuf);
421 add_columns (GtkWidget *treeview)
423 GtkTreeViewColumn *column;
424 GtkCellRenderer *renderer;
425 GtkTreeSelection *sel;
428 column = gtk_tree_view_column_new ();
430 /* Set icon and text render function */
431 renderer = gtk_cell_renderer_pixbuf_new();
432 gtk_tree_view_column_pack_start (column, renderer, FALSE);
433 gtk_tree_view_column_set_cell_data_func(column, renderer,
434 icon_cell_data, treeview, NULL);
436 renderer = gtk_cell_renderer_text_new();
437 gtk_tree_view_column_pack_start (column, renderer, FALSE);
438 gtk_tree_view_column_set_cell_data_func(column, renderer,
439 text_cell_data, treeview, NULL);
441 /* Set selection mode */
442 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
443 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
445 /* Set treeview appearance */
446 gtk_tree_view_column_set_spacing (column, 2);
447 gtk_tree_view_column_set_resizable (column, TRUE);
448 gtk_tree_view_column_set_fixed_width (column, TRUE);
449 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
450 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
453 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
457 modest_folder_view_init (ModestFolderView *obj)
459 ModestFolderViewPrivate *priv;
462 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
464 priv->timer_expander = 0;
465 priv->account_store = NULL;
467 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
468 priv->cur_folder_store = NULL;
469 priv->visible_account_id = NULL;
471 /* Initialize the local account name */
472 conf = modest_runtime_get_conf();
473 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
476 add_columns (GTK_WIDGET (obj));
478 /* Setup drag and drop */
479 setup_drag_and_drop (GTK_TREE_VIEW(obj));
481 /* Connect signals */
482 g_signal_connect (G_OBJECT (obj),
484 G_CALLBACK (on_key_pressed), NULL);
487 * Track changes in the local account name (in the device it
488 * will be the device name)
490 g_signal_connect (G_OBJECT(conf),
492 G_CALLBACK(on_configuration_key_changed), obj);
497 tny_account_store_view_init (gpointer g, gpointer iface_data)
499 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
501 klass->set_account_store_func = modest_folder_view_set_account_store;
507 modest_folder_view_finalize (GObject *obj)
509 ModestFolderViewPrivate *priv;
510 GtkTreeSelection *sel;
512 g_return_if_fail (obj);
514 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
516 if (priv->timer_expander != 0) {
517 g_source_remove (priv->timer_expander);
518 priv->timer_expander = 0;
521 if (priv->account_store) {
522 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
523 priv->account_update_signal);
524 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
525 priv->accounts_reloaded_signal);
526 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
527 priv->account_removed_signal);
528 g_object_unref (G_OBJECT(priv->account_store));
529 priv->account_store = NULL;
533 g_object_unref (G_OBJECT (priv->query));
537 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
539 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
541 g_free (priv->local_account_name);
542 g_free (priv->visible_account_id);
544 G_OBJECT_CLASS(parent_class)->finalize (obj);
549 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
551 ModestFolderViewPrivate *priv;
554 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
555 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
557 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
558 device = tny_account_store_get_device (account_store);
560 if (G_UNLIKELY (priv->account_store)) {
562 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
563 priv->account_update_signal))
564 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
565 priv->account_update_signal);
566 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
567 priv->accounts_reloaded_signal))
568 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
569 priv->accounts_reloaded_signal);
570 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
571 priv->account_removed_signal))
572 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
573 priv->account_removed_signal);
575 g_object_unref (G_OBJECT (priv->account_store));
578 priv->account_store = g_object_ref (G_OBJECT (account_store));
580 priv->account_update_signal =
581 g_signal_connect (G_OBJECT(account_store), "account_update",
582 G_CALLBACK (on_account_update), self);
584 priv->account_removed_signal =
585 g_signal_connect (G_OBJECT(account_store), "account_removed",
586 G_CALLBACK (on_account_removed), self);
588 priv->accounts_reloaded_signal =
589 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
590 G_CALLBACK (on_accounts_reloaded), self);
592 on_accounts_reloaded (account_store, (gpointer ) self);
594 g_object_unref (G_OBJECT (device));
598 on_account_removed (TnyAccountStore *account_store,
602 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
603 ModestFolderViewPrivate *priv;
605 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
607 /* If the removed account is the currently viewed one then
608 clear the configuration value. The new visible account will be the default account */
609 if (!strcmp (priv->visible_account_id, tny_account_get_id (account))) {
610 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
615 on_account_update (TnyAccountStore *account_store,
616 const gchar *account,
619 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
620 ModestFolderViewPrivate *priv;
622 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
623 if (!priv->visible_account_id)
624 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
625 MODEST_CONF_FOLDER_VIEW_KEY);
627 if (!modest_folder_view_update_model (self, account_store))
628 g_printerr ("modest: failed to update model for changes in '%s'",
633 on_accounts_reloaded (TnyAccountStore *account_store,
636 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
640 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
642 GtkTreeViewColumn *col;
644 g_return_if_fail (self);
646 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
648 g_printerr ("modest: failed get column for title\n");
652 gtk_tree_view_column_set_title (col, title);
653 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
658 modest_folder_view_new (TnyFolderStoreQuery *query)
661 ModestFolderViewPrivate *priv;
662 GtkTreeSelection *sel;
664 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
665 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
668 priv->query = g_object_ref (query);
670 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
671 priv->changed_signal = g_signal_connect (sel, "changed",
672 G_CALLBACK (on_selection_changed), self);
674 return GTK_WIDGET(self);
677 /* this feels dirty; any other way to expand all the root items? */
679 expand_root_items (ModestFolderView *self)
682 path = gtk_tree_path_new_first ();
684 /* all folders should have child items, so.. */
685 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
686 gtk_tree_path_next (path);
688 gtk_tree_path_free (path);
692 * We use this function to implement the
693 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
694 * account in this case, and the local folders.
697 filter_row (GtkTreeModel *model,
701 gboolean retval = TRUE;
702 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
703 GObject *instance = NULL;
705 gtk_tree_model_get (model, iter,
706 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
707 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
710 /* Do not show if there is no instance, this could indeed
711 happen when the model is being modified while it's being
712 drawn. This could occur for example when moving folders
717 if (type == TNY_FOLDER_TYPE_ROOT) {
718 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
719 if (TNY_IS_ACCOUNT (instance)) {
720 TnyAccount *acc = TNY_ACCOUNT (instance);
721 const gchar *account_id = tny_account_get_id (acc);
723 /* If it isn't a special folder,
724 * don't show it unless it is the visible account: */
725 if (!modest_tny_account_is_virtual_local_folders (acc) &&
726 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
727 /* Show only the visible account id */
728 ModestFolderViewPrivate *priv =
729 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
730 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
734 /* Never show these to the user. They are merged into one folder
735 * in the local-folders account instead: */
736 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
741 /* The virtual local-folders folder store is also shown by default. */
743 g_object_unref (instance);
749 modest_folder_view_update_model (ModestFolderView *self,
750 TnyAccountStore *account_store)
752 ModestFolderViewPrivate *priv;
753 GtkTreeModel *model /* , *old_model */;
754 /* TnyAccount *local_account; */
755 TnyList *model_as_list;
757 g_return_val_if_fail (account_store, FALSE);
759 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
761 /* Notify that there is no folder selected */
762 g_signal_emit (G_OBJECT(self),
763 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
766 /* FIXME: the local accounts are not shown when the query
767 selects only the subscribed folders. */
768 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
769 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
771 /* Deal with the model via its TnyList Interface,
772 * filling the TnyList via a get_accounts() call: */
773 model_as_list = TNY_LIST(model);
775 /* Get the accounts: */
776 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
778 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
779 g_object_unref (model_as_list);
780 model_as_list = NULL;
782 GtkTreeModel *filter_model = NULL, *sortable = NULL;
784 sortable = gtk_tree_model_sort_new_with_model (model);
785 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
786 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
788 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
789 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
790 cmp_rows, NULL, NULL);
792 /* Create filter model */
793 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
794 filter_model = gtk_tree_model_filter_new (sortable, NULL);
795 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
802 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
803 (filter_model) ? filter_model : sortable);
804 expand_root_items (self); /* expand all account folders */
806 g_object_unref (model);
809 g_object_unref (filter_model);
811 g_object_unref (sortable);
813 /* Select the first inbox or the local account if not found */
814 modest_folder_view_select_first_inbox_or_local (self);
821 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
824 TnyFolderStore *folder = NULL;
826 ModestFolderView *tree_view;
827 ModestFolderViewPrivate *priv;
829 g_return_if_fail (sel);
830 g_return_if_fail (user_data);
832 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
834 /* folder was _un_selected if true */
835 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
836 if (priv->cur_folder_store)
837 g_object_unref (priv->cur_folder_store);
838 priv->cur_folder_store = NULL;
840 /* Notify the display name observers */
841 g_signal_emit (G_OBJECT(user_data),
842 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
847 tree_view = MODEST_FOLDER_VIEW (user_data);
849 gtk_tree_model_get (model, &iter,
850 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
853 /* If the folder is the same do not notify */
854 if (priv->cur_folder_store == folder) {
855 g_object_unref (folder);
859 /* Current folder was unselected */
860 if (priv->cur_folder_store) {
861 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
862 priv->cur_folder_store, FALSE);
863 g_object_unref (priv->cur_folder_store);
866 /* New current references */
867 priv->cur_folder_store = folder;
869 /* New folder has been selected */
870 g_signal_emit (G_OBJECT(tree_view),
871 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
876 modest_folder_view_get_selected (ModestFolderView *self)
878 ModestFolderViewPrivate *priv;
880 g_return_val_if_fail (self, NULL);
882 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
883 if (priv->cur_folder_store)
884 g_object_ref (priv->cur_folder_store);
886 return priv->cur_folder_store;
890 get_cmp_rows_type_pos (GObject *folder)
892 /* Remote accounts -> Local account -> MMC account .*/
895 if (TNY_IS_ACCOUNT (folder) &&
896 modest_tny_account_is_virtual_local_folders (
897 TNY_ACCOUNT (folder))) {
899 } else if (TNY_IS_ACCOUNT (folder)) {
900 TnyAccount *account = TNY_ACCOUNT (folder);
901 const gchar *account_id = tny_account_get_id (account);
902 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
908 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
909 return -1; /* Should never happen */
914 * This function orders the mail accounts according to these rules:
915 * 1st - remote accounts
916 * 2nd - local account
920 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
926 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
927 GObject *folder1 = NULL;
928 GObject *folder2 = NULL;
930 gtk_tree_model_get (tree_model, iter1,
931 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
932 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
933 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
935 gtk_tree_model_get (tree_model, iter2,
936 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
937 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
940 if (type == TNY_FOLDER_TYPE_ROOT) {
941 /* Compare the types, so that
942 * Remote accounts -> Local account -> MMC account .*/
943 const gint pos1 = get_cmp_rows_type_pos (folder1);
944 const gint pos2 = get_cmp_rows_type_pos (folder2);
945 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
946 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
949 else if (pos1 > pos2)
952 /* Compare items of the same type: */
954 TnyAccount *account1 = NULL;
955 if (TNY_IS_ACCOUNT (folder1))
956 account1 = TNY_ACCOUNT (folder1);
958 TnyAccount *account2 = NULL;
959 if (TNY_IS_ACCOUNT (folder2))
960 account2 = TNY_ACCOUNT (folder2);
962 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
963 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
965 if (!account_id && !account_id2)
967 else if (!account_id)
969 else if (!account_id2)
971 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
974 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
977 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
981 g_object_unref(G_OBJECT(folder1));
983 g_object_unref(G_OBJECT(folder2));
991 /*****************************************************************************/
992 /* DRAG and DROP stuff */
993 /*****************************************************************************/
996 * This function fills the #GtkSelectionData with the row and the
997 * model that has been dragged. It's called when this widget is a
998 * source for dnd after the event drop happened
1001 on_drag_data_get (GtkWidget *widget,
1002 GdkDragContext *context,
1003 GtkSelectionData *selection_data,
1008 GtkTreeSelection *selection;
1009 GtkTreeModel *model;
1011 GtkTreePath *source_row;
1013 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1014 gtk_tree_selection_get_selected (selection, &model, &iter);
1015 source_row = gtk_tree_model_get_path (model, &iter);
1017 gtk_tree_set_row_drag_data (selection_data,
1021 gtk_tree_path_free (source_row);
1024 typedef struct _DndHelper {
1025 gboolean delete_source;
1026 GtkTreePath *source_row;
1027 GdkDragContext *context;
1033 * This function is the callback of the
1034 * modest_mail_operation_xfer_msgs () and
1035 * modest_mail_operation_xfer_folder() calls. We check here if the
1036 * message/folder was correctly asynchronously transferred. The reason
1037 * to use the same callback is that the code is the same, it only has
1038 * to check that the operation went fine and then finalize the drag
1042 on_progress_changed (ModestMailOperation *mail_op,
1043 ModestMailOperationState *state,
1049 helper = (DndHelper *) user_data;
1051 if (!state->finished)
1054 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1060 /* Notify the drag source. Never call delete, the monitor will
1061 do the job if needed */
1062 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1064 /* Free the helper */
1065 gtk_tree_path_free (helper->source_row);
1066 g_slice_free (DndHelper, helper);
1070 * This function is used by drag_data_received_cb to manage drag and
1071 * drop of a header, i.e, and drag from the header view to the folder
1075 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1076 GtkTreeModel *dest_model,
1077 GtkTreePath *dest_row,
1080 TnyList *headers = NULL;
1081 TnyHeader *header = NULL;
1082 TnyFolder *folder = NULL;
1083 ModestMailOperation *mail_op = NULL;
1084 GtkTreeIter source_iter, dest_iter;
1086 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1087 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1088 g_return_if_fail (dest_row);
1089 g_return_if_fail (helper);
1092 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1093 gtk_tree_model_get (source_model, &source_iter,
1094 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1096 if (!TNY_IS_HEADER(header)) {
1097 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1102 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1103 gtk_tree_model_get (dest_model, &dest_iter,
1104 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1107 if (!TNY_IS_FOLDER(folder)) {
1108 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1112 /* Transfer message */
1113 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1114 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1116 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1117 G_CALLBACK (on_progress_changed), helper);
1119 headers = tny_simple_list_new ();
1120 tny_list_append (headers, G_OBJECT (header));
1121 modest_mail_operation_xfer_msgs (mail_op,
1124 helper->delete_source,
1129 if (G_IS_OBJECT(mail_op))
1130 g_object_unref (G_OBJECT (mail_op));
1131 if (G_IS_OBJECT(header))
1132 g_object_unref (G_OBJECT (header));
1133 if (G_IS_OBJECT(folder))
1134 g_object_unref (G_OBJECT (folder));
1135 if (G_IS_OBJECT(headers))
1136 g_object_unref (headers);
1140 * This function is used by drag_data_received_cb to manage drag and
1141 * drop of a folder, i.e, and drag from the folder view to the same
1145 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1146 GtkTreeModel *dest_model,
1147 GtkTreePath *dest_row,
1148 GtkSelectionData *selection_data,
1151 ModestMailOperation *mail_op;
1152 GtkTreeIter parent_iter, iter;
1153 TnyFolderStore *parent_folder;
1156 /* Check if the drag is possible */
1157 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1158 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1160 /* selection_data)) { */
1161 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1163 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1164 gtk_tree_path_free (helper->source_row);
1165 g_slice_free (DndHelper, helper);
1170 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1171 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1172 gtk_tree_model_get (source_model, &parent_iter,
1173 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1174 &parent_folder, -1);
1175 gtk_tree_model_get (source_model, &iter,
1176 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1179 /* Do the mail operation */
1180 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1182 modest_ui_actions_move_folder_error_handler,
1184 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1186 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1187 G_CALLBACK (on_progress_changed), helper);
1189 modest_mail_operation_xfer_folder (mail_op,
1192 helper->delete_source);
1195 g_object_unref (G_OBJECT (parent_folder));
1196 g_object_unref (G_OBJECT (folder));
1197 g_object_unref (G_OBJECT (mail_op));
1201 * This function receives the data set by the "drag-data-get" signal
1202 * handler. This information comes within the #GtkSelectionData. This
1203 * function will manage both the drags of folders of the treeview and
1204 * drags of headers of the header view widget.
1207 on_drag_data_received (GtkWidget *widget,
1208 GdkDragContext *context,
1211 GtkSelectionData *selection_data,
1216 GtkWidget *source_widget;
1217 GtkTreeModel *dest_model, *source_model;
1218 GtkTreePath *source_row, *dest_row;
1219 GtkTreeViewDropPosition pos;
1220 gboolean success = FALSE, delete_source = FALSE;
1221 DndHelper *helper = NULL;
1223 /* Do not allow further process */
1224 g_signal_stop_emission_by_name (widget, "drag-data-received");
1225 source_widget = gtk_drag_get_source_widget (context);
1227 /* Get the action */
1228 if (context->action == GDK_ACTION_MOVE) {
1229 delete_source = TRUE;
1231 /* Notify that there is no folder selected. We need to
1232 do this in order to update the headers view (and
1233 its monitors, because when moving, the old folder
1234 won't longer exist. We can not wait for the end of
1235 the operation, because the operation won't start if
1236 the folder is in use */
1237 if (source_widget == widget)
1238 g_signal_emit (G_OBJECT (widget),
1239 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1242 /* Check if the get_data failed */
1243 if (selection_data == NULL || selection_data->length < 0)
1244 gtk_drag_finish (context, success, FALSE, time);
1246 /* Get the models */
1247 gtk_tree_get_row_drag_data (selection_data,
1251 /* Select the destination model */
1252 if (source_widget == widget) {
1253 dest_model = source_model;
1255 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1258 /* Get the path to the destination row. Can not call
1259 gtk_tree_view_get_drag_dest_row() because the source row
1260 is not selected anymore */
1261 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1264 /* Only allow drops IN other rows */
1265 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1266 gtk_drag_finish (context, success, FALSE, time);
1268 /* Create the helper */
1269 helper = g_slice_new0 (DndHelper);
1270 helper->delete_source = delete_source;
1271 helper->source_row = gtk_tree_path_copy (source_row);
1272 helper->context = context;
1273 helper->time = time;
1275 /* Drags from the header view */
1276 if (source_widget != widget) {
1278 drag_and_drop_from_header_view (source_model,
1285 drag_and_drop_from_folder_view (source_model,
1293 gtk_tree_path_free (source_row);
1294 gtk_tree_path_free (dest_row);
1298 * We define a "drag-drop" signal handler because we do not want to
1299 * use the default one, because the default one always calls
1300 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1301 * signal handler, because there we have all the information available
1302 * to know if the dnd was a success or not.
1305 drag_drop_cb (GtkWidget *widget,
1306 GdkDragContext *context,
1314 if (!context->targets)
1317 /* Check if we're dragging a folder row */
1318 target = gtk_drag_dest_find_target (widget, context, NULL);
1320 /* Request the data from the source. */
1321 gtk_drag_get_data(widget, context, target, time);
1327 * This function expands a node of a tree view if it's not expanded
1328 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1329 * does that, so that's why they're here.
1332 expand_row_timeout (gpointer data)
1334 GtkTreeView *tree_view = data;
1335 GtkTreePath *dest_path = NULL;
1336 GtkTreeViewDropPosition pos;
1337 gboolean result = FALSE;
1339 GDK_THREADS_ENTER ();
1341 gtk_tree_view_get_drag_dest_row (tree_view,
1346 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1347 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1348 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1349 gtk_tree_path_free (dest_path);
1353 gtk_tree_path_free (dest_path);
1358 GDK_THREADS_LEAVE ();
1364 * This function is called whenever the pointer is moved over a widget
1365 * while dragging some data. It installs a timeout that will expand a
1366 * node of the treeview if not expanded yet. This function also calls
1367 * gdk_drag_status in order to set the suggested action that will be
1368 * used by the "drag-data-received" signal handler to know if we
1369 * should do a move or just a copy of the data.
1372 on_drag_motion (GtkWidget *widget,
1373 GdkDragContext *context,
1379 GtkTreeViewDropPosition pos;
1380 GtkTreePath *dest_row;
1381 ModestFolderViewPrivate *priv;
1382 GdkDragAction suggested_action;
1383 gboolean valid_location = FALSE;
1385 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1387 if (priv->timer_expander != 0) {
1388 g_source_remove (priv->timer_expander);
1389 priv->timer_expander = 0;
1392 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1397 /* Do not allow drops between folders */
1399 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1400 pos == GTK_TREE_VIEW_DROP_AFTER) {
1401 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1402 gdk_drag_status(context, 0, time);
1403 valid_location = FALSE;
1406 valid_location = TRUE;
1409 /* Expand the selected row after 1/2 second */
1410 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1411 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1412 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1415 /* Select the desired action. By default we pick MOVE */
1416 suggested_action = GDK_ACTION_MOVE;
1418 if (context->actions == GDK_ACTION_COPY)
1419 gdk_drag_status(context, GDK_ACTION_COPY, time);
1420 else if (context->actions == GDK_ACTION_MOVE)
1421 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1422 else if (context->actions & suggested_action)
1423 gdk_drag_status(context, suggested_action, time);
1425 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1429 gtk_tree_path_free (dest_row);
1430 g_signal_stop_emission_by_name (widget, "drag-motion");
1431 return valid_location;
1435 /* Folder view drag types */
1436 const GtkTargetEntry folder_view_drag_types[] =
1438 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1439 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1443 * This function sets the treeview as a source and a target for dnd
1444 * events. It also connects all the requirede signals.
1447 setup_drag_and_drop (GtkTreeView *self)
1449 /* Set up the folder view as a dnd destination. Set only the
1450 highlight flag, otherwise gtk will have a different
1452 gtk_drag_dest_set (GTK_WIDGET (self),
1453 GTK_DEST_DEFAULT_HIGHLIGHT,
1454 folder_view_drag_types,
1455 G_N_ELEMENTS (folder_view_drag_types),
1456 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1458 g_signal_connect (G_OBJECT (self),
1459 "drag_data_received",
1460 G_CALLBACK (on_drag_data_received),
1464 /* Set up the treeview as a dnd source */
1465 gtk_drag_source_set (GTK_WIDGET (self),
1467 folder_view_drag_types,
1468 G_N_ELEMENTS (folder_view_drag_types),
1469 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1471 g_signal_connect (G_OBJECT (self),
1473 G_CALLBACK (on_drag_motion),
1476 g_signal_connect (G_OBJECT (self),
1478 G_CALLBACK (on_drag_data_get),
1481 g_signal_connect (G_OBJECT (self),
1483 G_CALLBACK (drag_drop_cb),
1488 * This function manages the navigation through the folders using the
1489 * keyboard or the hardware keys in the device
1492 on_key_pressed (GtkWidget *self,
1496 GtkTreeSelection *selection;
1498 GtkTreeModel *model;
1499 gboolean retval = FALSE;
1501 /* Up and Down are automatically managed by the treeview */
1502 if (event->keyval == GDK_Return) {
1503 /* Expand/Collapse the selected row */
1504 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1505 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1508 path = gtk_tree_model_get_path (model, &iter);
1510 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1511 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1513 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1514 gtk_tree_path_free (path);
1516 /* No further processing */
1524 * We listen to the changes in the local folder account name key,
1525 * because we want to show the right name in the view. The local
1526 * folder account name corresponds to the device name in the Maemo
1527 * version. We do this because we do not want to query gconf on each
1528 * tree view refresh. It's better to cache it and change whenever
1532 on_configuration_key_changed (ModestConf* conf,
1534 ModestConfEvent event,
1535 ModestFolderView *self)
1537 ModestFolderViewPrivate *priv;
1542 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1543 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1545 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1546 g_free (priv->local_account_name);
1548 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1549 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1551 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1552 MODEST_CONF_DEVICE_NAME, NULL);
1554 /* Force a redraw */
1555 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1556 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1557 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1558 gtk_tree_view_column_queue_resize (tree_column);
1564 modest_folder_view_set_style (ModestFolderView *self,
1565 ModestFolderViewStyle style)
1567 ModestFolderViewPrivate *priv;
1569 g_return_if_fail (self);
1571 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1573 priv->style = style;
1577 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1578 const gchar *account_id)
1580 ModestFolderViewPrivate *priv;
1581 GtkTreeModel *model;
1583 g_return_if_fail (self);
1585 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1587 /* This will be used by the filter_row callback,
1588 * to decided which rows to show: */
1589 if (priv->visible_account_id) {
1590 g_free (priv->visible_account_id);
1591 priv->visible_account_id = NULL;
1594 priv->visible_account_id = g_strdup (account_id);
1597 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1598 if (GTK_IS_TREE_MODEL_FILTER (model))
1599 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1601 /* Save settings to gconf */
1602 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1603 MODEST_CONF_FOLDER_VIEW_KEY);
1607 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1609 ModestFolderViewPrivate *priv;
1611 g_return_val_if_fail (self, NULL);
1613 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1615 return (const gchar *) priv->visible_account_id;
1619 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1623 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1626 gtk_tree_model_get (model, iter,
1627 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1628 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1632 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n",
1633 __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1636 gboolean result = FALSE;
1637 if (type == TNY_FOLDER_TYPE_INBOX) {
1639 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1640 /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1641 * when getting folders from the cache, before connectin, so we do
1642 * an extra check. We could fix this in tinymail, but it's easier
1645 if (strcmp (name, "Inbox") == 0)
1652 *inbox_iter = *iter;
1656 if (gtk_tree_model_iter_children (model, &child, iter)) {
1657 if (find_inbox_iter (model, &child, inbox_iter))
1661 } while (gtk_tree_model_iter_next (model, iter));
1669 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1671 GtkTreeModel *model;
1672 GtkTreeIter iter, inbox_iter;
1673 GtkTreeSelection *sel;
1675 /* Do not set it if the folder view was not painted */
1676 if (!GTK_WIDGET_MAPPED (self))
1679 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1683 expand_root_items (self);
1684 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1686 gtk_tree_model_get_iter_first (model, &iter);
1687 if (find_inbox_iter (model, &iter, &inbox_iter)) {
1688 gtk_tree_selection_select_iter (sel, &inbox_iter);
1691 gtk_tree_model_get_iter_first (model, &iter);
1692 gtk_tree_selection_select_iter (sel, &iter);