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;
143 gulong conf_key_signal;
145 TnyFolderStoreQuery *query;
146 guint timer_expander;
148 gchar *local_account_name;
149 gchar *visible_account_id;
150 ModestFolderViewStyle style;
152 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
153 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
154 MODEST_TYPE_FOLDER_VIEW, \
155 ModestFolderViewPrivate))
157 static GObjectClass *parent_class = NULL;
159 static guint signals[LAST_SIGNAL] = {0};
162 modest_folder_view_get_type (void)
164 static GType my_type = 0;
166 static const GTypeInfo my_info = {
167 sizeof(ModestFolderViewClass),
168 NULL, /* base init */
169 NULL, /* base finalize */
170 (GClassInitFunc) modest_folder_view_class_init,
171 NULL, /* class finalize */
172 NULL, /* class data */
173 sizeof(ModestFolderView),
175 (GInstanceInitFunc) modest_folder_view_init,
179 static const GInterfaceInfo tny_account_store_view_info = {
180 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
181 NULL, /* interface_finalize */
182 NULL /* interface_data */
186 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
190 g_type_add_interface_static (my_type,
191 TNY_TYPE_ACCOUNT_STORE_VIEW,
192 &tny_account_store_view_info);
198 modest_folder_view_class_init (ModestFolderViewClass *klass)
200 GObjectClass *gobject_class;
201 gobject_class = (GObjectClass*) klass;
203 parent_class = g_type_class_peek_parent (klass);
204 gobject_class->finalize = modest_folder_view_finalize;
206 g_type_class_add_private (gobject_class,
207 sizeof(ModestFolderViewPrivate));
209 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
210 g_signal_new ("folder_selection_changed",
211 G_TYPE_FROM_CLASS (gobject_class),
213 G_STRUCT_OFFSET (ModestFolderViewClass,
214 folder_selection_changed),
216 modest_marshal_VOID__POINTER_BOOLEAN,
217 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
220 * This signal is emitted whenever the currently selected
221 * folder display name is computed. Note that the name could
222 * be different to the folder name, because we could append
223 * the unread messages count to the folder name to build the
224 * folder display name
226 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
227 g_signal_new ("folder-display-name-changed",
228 G_TYPE_FROM_CLASS (gobject_class),
230 G_STRUCT_OFFSET (ModestFolderViewClass,
231 folder_display_name_changed),
233 g_cclosure_marshal_VOID__STRING,
234 G_TYPE_NONE, 1, G_TYPE_STRING);
238 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
239 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
241 ModestFolderViewPrivate *priv;
246 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
247 GObject *instance = NULL;
249 g_return_if_fail (column);
250 g_return_if_fail (tree_model);
252 gtk_tree_model_get (tree_model, iter,
253 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
254 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
255 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
256 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
257 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
259 rendobj = G_OBJECT(renderer);
270 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
272 gchar *item_name = NULL;
273 gint item_weight = 400;
275 if (type != TNY_FOLDER_TYPE_ROOT) {
278 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
279 type = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
280 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
282 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
286 /* Select the number to show: the unread or unsent messages */
287 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
292 /* Use bold font style if there are unread or unset messages */
294 item_name = g_strdup_printf ("%s (%d)", fname, number);
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 /* We include the MERGE type here because it's used to create
365 the local OUTBOX folder */
366 if (type == TNY_FOLDER_TYPE_NORMAL ||
367 type == TNY_FOLDER_TYPE_UNKNOWN ||
368 type == TNY_FOLDER_TYPE_MERGE) {
369 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
373 case TNY_FOLDER_TYPE_ROOT:
374 if (TNY_IS_ACCOUNT (instance)) {
376 if (modest_tny_account_is_virtual_local_folders (
377 TNY_ACCOUNT (instance))) {
378 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
381 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
383 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
384 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
386 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
390 case TNY_FOLDER_TYPE_INBOX:
391 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
393 case TNY_FOLDER_TYPE_OUTBOX:
394 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
396 case TNY_FOLDER_TYPE_JUNK:
397 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
399 case TNY_FOLDER_TYPE_SENT:
400 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
402 case TNY_FOLDER_TYPE_TRASH:
403 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
405 case TNY_FOLDER_TYPE_DRAFTS:
406 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
408 case TNY_FOLDER_TYPE_NORMAL:
410 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
414 g_object_unref (G_OBJECT (instance));
418 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
421 g_object_unref (pixbuf);
425 add_columns (GtkWidget *treeview)
427 GtkTreeViewColumn *column;
428 GtkCellRenderer *renderer;
429 GtkTreeSelection *sel;
432 column = gtk_tree_view_column_new ();
434 /* Set icon and text render function */
435 renderer = gtk_cell_renderer_pixbuf_new();
436 gtk_tree_view_column_pack_start (column, renderer, FALSE);
437 gtk_tree_view_column_set_cell_data_func(column, renderer,
438 icon_cell_data, treeview, NULL);
440 renderer = gtk_cell_renderer_text_new();
441 gtk_tree_view_column_pack_start (column, renderer, FALSE);
442 gtk_tree_view_column_set_cell_data_func(column, renderer,
443 text_cell_data, treeview, NULL);
445 /* Set selection mode */
446 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
447 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
449 /* Set treeview appearance */
450 gtk_tree_view_column_set_spacing (column, 2);
451 gtk_tree_view_column_set_resizable (column, TRUE);
452 gtk_tree_view_column_set_fixed_width (column, TRUE);
453 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
454 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
457 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
461 modest_folder_view_init (ModestFolderView *obj)
463 ModestFolderViewPrivate *priv;
466 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
468 priv->timer_expander = 0;
469 priv->account_store = NULL;
471 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
472 priv->cur_folder_store = NULL;
473 priv->visible_account_id = NULL;
475 /* Initialize the local account name */
476 conf = modest_runtime_get_conf();
477 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
480 add_columns (GTK_WIDGET (obj));
482 /* Setup drag and drop */
483 setup_drag_and_drop (GTK_TREE_VIEW(obj));
485 /* Connect signals */
486 g_signal_connect (G_OBJECT (obj),
488 G_CALLBACK (on_key_pressed), NULL);
491 * Track changes in the local account name (in the device it
492 * will be the device name)
494 priv->conf_key_signal =
495 g_signal_connect (G_OBJECT(conf),
497 G_CALLBACK(on_configuration_key_changed), obj);
501 tny_account_store_view_init (gpointer g, gpointer iface_data)
503 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
505 klass->set_account_store_func = modest_folder_view_set_account_store;
511 modest_folder_view_finalize (GObject *obj)
513 ModestFolderViewPrivate *priv;
514 GtkTreeSelection *sel;
516 g_return_if_fail (obj);
518 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
520 if (priv->timer_expander != 0) {
521 g_source_remove (priv->timer_expander);
522 priv->timer_expander = 0;
525 if (priv->account_store) {
526 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
527 priv->account_update_signal);
528 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
529 priv->accounts_reloaded_signal);
530 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
531 priv->account_removed_signal);
532 g_object_unref (G_OBJECT(priv->account_store));
533 priv->account_store = NULL;
537 g_object_unref (G_OBJECT (priv->query));
541 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
543 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
545 g_free (priv->local_account_name);
546 g_free (priv->visible_account_id);
548 if (priv->conf_key_signal) {
549 g_signal_handler_disconnect (modest_runtime_get_conf (),
550 priv->conf_key_signal);
551 priv->conf_key_signal = 0;
554 G_OBJECT_CLASS(parent_class)->finalize (obj);
559 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
561 ModestFolderViewPrivate *priv;
564 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
565 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
567 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
568 device = tny_account_store_get_device (account_store);
570 if (G_UNLIKELY (priv->account_store)) {
572 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
573 priv->account_update_signal))
574 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
575 priv->account_update_signal);
576 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
577 priv->accounts_reloaded_signal))
578 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
579 priv->accounts_reloaded_signal);
580 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
581 priv->account_removed_signal))
582 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
583 priv->account_removed_signal);
585 g_object_unref (G_OBJECT (priv->account_store));
588 priv->account_store = g_object_ref (G_OBJECT (account_store));
590 priv->account_update_signal =
591 g_signal_connect (G_OBJECT(account_store), "account_update",
592 G_CALLBACK (on_account_update), self);
594 priv->account_removed_signal =
595 g_signal_connect (G_OBJECT(account_store), "account_removed",
596 G_CALLBACK (on_account_removed), self);
598 priv->accounts_reloaded_signal =
599 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
600 G_CALLBACK (on_accounts_reloaded), self);
602 on_accounts_reloaded (account_store, (gpointer ) self);
604 g_object_unref (G_OBJECT (device));
608 on_account_removed (TnyAccountStore *account_store,
612 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
613 ModestFolderViewPrivate *priv;
615 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
617 /* If the removed account is the currently viewed one then
618 clear the configuration value. The new visible account will be the default account */
619 if (!strcmp (priv->visible_account_id, tny_account_get_id (account))) {
620 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
625 on_account_update (TnyAccountStore *account_store,
626 const gchar *account,
629 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
630 ModestFolderViewPrivate *priv;
632 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
633 if (!priv->visible_account_id)
634 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
635 MODEST_CONF_FOLDER_VIEW_KEY);
637 if (!modest_folder_view_update_model (self, account_store))
638 g_printerr ("modest: failed to update model for changes in '%s'",
643 on_accounts_reloaded (TnyAccountStore *account_store,
646 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
650 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
652 GtkTreeViewColumn *col;
654 g_return_if_fail (self);
656 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
658 g_printerr ("modest: failed get column for title\n");
662 gtk_tree_view_column_set_title (col, title);
663 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
668 modest_folder_view_new (TnyFolderStoreQuery *query)
671 ModestFolderViewPrivate *priv;
672 GtkTreeSelection *sel;
674 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
675 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
678 priv->query = g_object_ref (query);
680 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
681 priv->changed_signal = g_signal_connect (sel, "changed",
682 G_CALLBACK (on_selection_changed), self);
684 return GTK_WIDGET(self);
687 /* this feels dirty; any other way to expand all the root items? */
689 expand_root_items (ModestFolderView *self)
692 path = gtk_tree_path_new_first ();
694 /* all folders should have child items, so.. */
695 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
696 gtk_tree_path_next (path);
698 gtk_tree_path_free (path);
702 * We use this function to implement the
703 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
704 * account in this case, and the local folders.
707 filter_row (GtkTreeModel *model,
711 gboolean retval = TRUE;
712 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
713 GObject *instance = NULL;
715 gtk_tree_model_get (model, iter,
716 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
717 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
720 /* Do not show if there is no instance, this could indeed
721 happen when the model is being modified while it's being
722 drawn. This could occur for example when moving folders
727 if (type == TNY_FOLDER_TYPE_ROOT) {
728 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
729 if (TNY_IS_ACCOUNT (instance)) {
730 TnyAccount *acc = TNY_ACCOUNT (instance);
731 const gchar *account_id = tny_account_get_id (acc);
733 /* If it isn't a special folder,
734 * don't show it unless it is the visible account: */
735 if (!modest_tny_account_is_virtual_local_folders (acc) &&
736 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
737 /* Show only the visible account id */
738 ModestFolderViewPrivate *priv =
739 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
740 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
744 /* Never show these to the user. They are merged into one folder
745 * in the local-folders account instead: */
746 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
751 /* The virtual local-folders folder store is also shown by default. */
753 g_object_unref (instance);
759 modest_folder_view_update_model (ModestFolderView *self,
760 TnyAccountStore *account_store)
762 ModestFolderViewPrivate *priv;
763 GtkTreeModel *model /* , *old_model */;
764 /* TnyAccount *local_account; */
765 TnyList *model_as_list;
767 g_return_val_if_fail (account_store, FALSE);
769 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
771 /* Notify that there is no folder selected */
772 g_signal_emit (G_OBJECT(self),
773 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
776 /* FIXME: the local accounts are not shown when the query
777 selects only the subscribed folders. */
778 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
779 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
781 /* Deal with the model via its TnyList Interface,
782 * filling the TnyList via a get_accounts() call: */
783 model_as_list = TNY_LIST(model);
785 /* Get the accounts: */
786 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
788 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
789 g_object_unref (model_as_list);
790 model_as_list = NULL;
792 GtkTreeModel *filter_model = NULL, *sortable = NULL;
794 sortable = gtk_tree_model_sort_new_with_model (model);
795 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
796 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
798 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
799 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
800 cmp_rows, NULL, NULL);
802 /* Create filter model */
803 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
804 filter_model = gtk_tree_model_filter_new (sortable, NULL);
805 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
812 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
813 (filter_model) ? filter_model : sortable);
814 expand_root_items (self); /* expand all account folders */
816 g_object_unref (model);
819 g_object_unref (filter_model);
821 g_object_unref (sortable);
823 /* Select the first inbox or the local account if not found */
824 modest_folder_view_select_first_inbox_or_local (self);
831 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
834 TnyFolderStore *folder = NULL;
836 ModestFolderView *tree_view;
837 ModestFolderViewPrivate *priv;
839 g_return_if_fail (sel);
840 g_return_if_fail (user_data);
842 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
844 /* folder was _un_selected if true */
845 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
846 if (priv->cur_folder_store)
847 g_object_unref (priv->cur_folder_store);
848 priv->cur_folder_store = NULL;
850 /* Notify the display name observers */
851 g_signal_emit (G_OBJECT(user_data),
852 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
857 tree_view = MODEST_FOLDER_VIEW (user_data);
859 gtk_tree_model_get (model, &iter,
860 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
863 /* If the folder is the same do not notify */
864 if (priv->cur_folder_store == folder) {
865 g_object_unref (folder);
869 /* Current folder was unselected */
870 if (priv->cur_folder_store) {
871 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
872 priv->cur_folder_store, FALSE);
873 g_object_unref (priv->cur_folder_store);
876 /* New current references */
877 priv->cur_folder_store = folder;
879 /* New folder has been selected */
880 g_signal_emit (G_OBJECT(tree_view),
881 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
886 modest_folder_view_get_selected (ModestFolderView *self)
888 ModestFolderViewPrivate *priv;
890 g_return_val_if_fail (self, NULL);
892 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
893 if (priv->cur_folder_store)
894 g_object_ref (priv->cur_folder_store);
896 return priv->cur_folder_store;
900 get_cmp_rows_type_pos (GObject *folder)
902 /* Remote accounts -> Local account -> MMC account .*/
905 if (TNY_IS_ACCOUNT (folder) &&
906 modest_tny_account_is_virtual_local_folders (
907 TNY_ACCOUNT (folder))) {
909 } else if (TNY_IS_ACCOUNT (folder)) {
910 TnyAccount *account = TNY_ACCOUNT (folder);
911 const gchar *account_id = tny_account_get_id (account);
912 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
918 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
919 return -1; /* Should never happen */
924 * This function orders the mail accounts according to these rules:
925 * 1st - remote accounts
926 * 2nd - local account
930 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
936 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
937 GObject *folder1 = NULL;
938 GObject *folder2 = NULL;
940 gtk_tree_model_get (tree_model, iter1,
941 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
942 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
943 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
945 gtk_tree_model_get (tree_model, iter2,
946 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
947 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
950 if (type == TNY_FOLDER_TYPE_ROOT) {
951 /* Compare the types, so that
952 * Remote accounts -> Local account -> MMC account .*/
953 const gint pos1 = get_cmp_rows_type_pos (folder1);
954 const gint pos2 = get_cmp_rows_type_pos (folder2);
955 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
956 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
959 else if (pos1 > pos2)
962 /* Compare items of the same type: */
964 TnyAccount *account1 = NULL;
965 if (TNY_IS_ACCOUNT (folder1))
966 account1 = TNY_ACCOUNT (folder1);
968 TnyAccount *account2 = NULL;
969 if (TNY_IS_ACCOUNT (folder2))
970 account2 = TNY_ACCOUNT (folder2);
972 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
973 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
975 if (!account_id && !account_id2)
977 else if (!account_id)
979 else if (!account_id2)
981 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
984 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
987 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
991 g_object_unref(G_OBJECT(folder1));
993 g_object_unref(G_OBJECT(folder2));
1001 /*****************************************************************************/
1002 /* DRAG and DROP stuff */
1003 /*****************************************************************************/
1006 * This function fills the #GtkSelectionData with the row and the
1007 * model that has been dragged. It's called when this widget is a
1008 * source for dnd after the event drop happened
1011 on_drag_data_get (GtkWidget *widget,
1012 GdkDragContext *context,
1013 GtkSelectionData *selection_data,
1018 GtkTreeSelection *selection;
1019 GtkTreeModel *model;
1021 GtkTreePath *source_row;
1023 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1024 gtk_tree_selection_get_selected (selection, &model, &iter);
1025 source_row = gtk_tree_model_get_path (model, &iter);
1027 gtk_tree_set_row_drag_data (selection_data,
1031 gtk_tree_path_free (source_row);
1034 typedef struct _DndHelper {
1035 gboolean delete_source;
1036 GtkTreePath *source_row;
1037 GdkDragContext *context;
1043 * This function is the callback of the
1044 * modest_mail_operation_xfer_msgs () and
1045 * modest_mail_operation_xfer_folder() calls. We check here if the
1046 * message/folder was correctly asynchronously transferred. The reason
1047 * to use the same callback is that the code is the same, it only has
1048 * to check that the operation went fine and then finalize the drag
1052 on_progress_changed (ModestMailOperation *mail_op,
1053 ModestMailOperationState *state,
1059 helper = (DndHelper *) user_data;
1061 if (!state->finished)
1064 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1070 /* Notify the drag source. Never call delete, the monitor will
1071 do the job if needed */
1072 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1074 /* Free the helper */
1075 gtk_tree_path_free (helper->source_row);
1076 g_slice_free (DndHelper, helper);
1080 * This function is used by drag_data_received_cb to manage drag and
1081 * drop of a header, i.e, and drag from the header view to the folder
1085 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1086 GtkTreeModel *dest_model,
1087 GtkTreePath *dest_row,
1090 TnyList *headers = NULL;
1091 TnyHeader *header = NULL;
1092 TnyFolder *folder = NULL;
1093 ModestMailOperation *mail_op = NULL;
1094 GtkTreeIter source_iter, dest_iter;
1096 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1097 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1098 g_return_if_fail (dest_row);
1099 g_return_if_fail (helper);
1102 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1103 gtk_tree_model_get (source_model, &source_iter,
1104 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1106 if (!TNY_IS_HEADER(header)) {
1107 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1112 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1113 gtk_tree_model_get (dest_model, &dest_iter,
1114 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1117 if (!TNY_IS_FOLDER(folder)) {
1118 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1122 /* Transfer message */
1123 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1124 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1126 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1127 G_CALLBACK (on_progress_changed), helper);
1129 headers = tny_simple_list_new ();
1130 tny_list_append (headers, G_OBJECT (header));
1131 modest_mail_operation_xfer_msgs (mail_op,
1134 helper->delete_source,
1139 if (G_IS_OBJECT(mail_op))
1140 g_object_unref (G_OBJECT (mail_op));
1141 if (G_IS_OBJECT(header))
1142 g_object_unref (G_OBJECT (header));
1143 if (G_IS_OBJECT(folder))
1144 g_object_unref (G_OBJECT (folder));
1145 if (G_IS_OBJECT(headers))
1146 g_object_unref (headers);
1150 * This function is used by drag_data_received_cb to manage drag and
1151 * drop of a folder, i.e, and drag from the folder view to the same
1155 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1156 GtkTreeModel *dest_model,
1157 GtkTreePath *dest_row,
1158 GtkSelectionData *selection_data,
1161 ModestMailOperation *mail_op;
1162 GtkTreeIter parent_iter, iter;
1163 TnyFolderStore *parent_folder;
1166 /* Check if the drag is possible */
1167 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1168 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1170 /* selection_data)) { */
1171 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1173 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1174 gtk_tree_path_free (helper->source_row);
1175 g_slice_free (DndHelper, helper);
1180 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1181 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1182 gtk_tree_model_get (source_model, &parent_iter,
1183 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1184 &parent_folder, -1);
1185 gtk_tree_model_get (source_model, &iter,
1186 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1189 /* Do the mail operation */
1190 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1192 modest_ui_actions_move_folder_error_handler,
1194 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1196 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1197 G_CALLBACK (on_progress_changed), helper);
1199 modest_mail_operation_xfer_folder (mail_op,
1202 helper->delete_source);
1205 g_object_unref (G_OBJECT (parent_folder));
1206 g_object_unref (G_OBJECT (folder));
1207 g_object_unref (G_OBJECT (mail_op));
1211 * This function receives the data set by the "drag-data-get" signal
1212 * handler. This information comes within the #GtkSelectionData. This
1213 * function will manage both the drags of folders of the treeview and
1214 * drags of headers of the header view widget.
1217 on_drag_data_received (GtkWidget *widget,
1218 GdkDragContext *context,
1221 GtkSelectionData *selection_data,
1226 GtkWidget *source_widget;
1227 GtkTreeModel *dest_model, *source_model;
1228 GtkTreePath *source_row, *dest_row;
1229 GtkTreeViewDropPosition pos;
1230 gboolean success = FALSE, delete_source = FALSE;
1231 DndHelper *helper = NULL;
1233 /* Do not allow further process */
1234 g_signal_stop_emission_by_name (widget, "drag-data-received");
1235 source_widget = gtk_drag_get_source_widget (context);
1237 /* Get the action */
1238 if (context->action == GDK_ACTION_MOVE) {
1239 delete_source = TRUE;
1241 /* Notify that there is no folder selected. We need to
1242 do this in order to update the headers view (and
1243 its monitors, because when moving, the old folder
1244 won't longer exist. We can not wait for the end of
1245 the operation, because the operation won't start if
1246 the folder is in use */
1247 if (source_widget == widget)
1248 g_signal_emit (G_OBJECT (widget),
1249 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1252 /* Check if the get_data failed */
1253 if (selection_data == NULL || selection_data->length < 0)
1254 gtk_drag_finish (context, success, FALSE, time);
1256 /* Get the models */
1257 gtk_tree_get_row_drag_data (selection_data,
1261 /* Select the destination model */
1262 if (source_widget == widget) {
1263 dest_model = source_model;
1265 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1268 /* Get the path to the destination row. Can not call
1269 gtk_tree_view_get_drag_dest_row() because the source row
1270 is not selected anymore */
1271 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1274 /* Only allow drops IN other rows */
1275 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1276 gtk_drag_finish (context, success, FALSE, time);
1278 /* Create the helper */
1279 helper = g_slice_new0 (DndHelper);
1280 helper->delete_source = delete_source;
1281 helper->source_row = gtk_tree_path_copy (source_row);
1282 helper->context = context;
1283 helper->time = time;
1285 /* Drags from the header view */
1286 if (source_widget != widget) {
1288 drag_and_drop_from_header_view (source_model,
1295 drag_and_drop_from_folder_view (source_model,
1303 gtk_tree_path_free (source_row);
1304 gtk_tree_path_free (dest_row);
1308 * We define a "drag-drop" signal handler because we do not want to
1309 * use the default one, because the default one always calls
1310 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1311 * signal handler, because there we have all the information available
1312 * to know if the dnd was a success or not.
1315 drag_drop_cb (GtkWidget *widget,
1316 GdkDragContext *context,
1324 if (!context->targets)
1327 /* Check if we're dragging a folder row */
1328 target = gtk_drag_dest_find_target (widget, context, NULL);
1330 /* Request the data from the source. */
1331 gtk_drag_get_data(widget, context, target, time);
1337 * This function expands a node of a tree view if it's not expanded
1338 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1339 * does that, so that's why they're here.
1342 expand_row_timeout (gpointer data)
1344 GtkTreeView *tree_view = data;
1345 GtkTreePath *dest_path = NULL;
1346 GtkTreeViewDropPosition pos;
1347 gboolean result = FALSE;
1349 GDK_THREADS_ENTER ();
1351 gtk_tree_view_get_drag_dest_row (tree_view,
1356 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1357 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1358 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1359 gtk_tree_path_free (dest_path);
1363 gtk_tree_path_free (dest_path);
1368 GDK_THREADS_LEAVE ();
1374 * This function is called whenever the pointer is moved over a widget
1375 * while dragging some data. It installs a timeout that will expand a
1376 * node of the treeview if not expanded yet. This function also calls
1377 * gdk_drag_status in order to set the suggested action that will be
1378 * used by the "drag-data-received" signal handler to know if we
1379 * should do a move or just a copy of the data.
1382 on_drag_motion (GtkWidget *widget,
1383 GdkDragContext *context,
1389 GtkTreeViewDropPosition pos;
1390 GtkTreePath *dest_row;
1391 ModestFolderViewPrivate *priv;
1392 GdkDragAction suggested_action;
1393 gboolean valid_location = FALSE;
1395 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1397 if (priv->timer_expander != 0) {
1398 g_source_remove (priv->timer_expander);
1399 priv->timer_expander = 0;
1402 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1407 /* Do not allow drops between folders */
1409 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1410 pos == GTK_TREE_VIEW_DROP_AFTER) {
1411 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1412 gdk_drag_status(context, 0, time);
1413 valid_location = FALSE;
1416 valid_location = TRUE;
1419 /* Expand the selected row after 1/2 second */
1420 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1421 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1422 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1425 /* Select the desired action. By default we pick MOVE */
1426 suggested_action = GDK_ACTION_MOVE;
1428 if (context->actions == GDK_ACTION_COPY)
1429 gdk_drag_status(context, GDK_ACTION_COPY, time);
1430 else if (context->actions == GDK_ACTION_MOVE)
1431 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1432 else if (context->actions & suggested_action)
1433 gdk_drag_status(context, suggested_action, time);
1435 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1439 gtk_tree_path_free (dest_row);
1440 g_signal_stop_emission_by_name (widget, "drag-motion");
1441 return valid_location;
1445 /* Folder view drag types */
1446 const GtkTargetEntry folder_view_drag_types[] =
1448 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1449 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1453 * This function sets the treeview as a source and a target for dnd
1454 * events. It also connects all the requirede signals.
1457 setup_drag_and_drop (GtkTreeView *self)
1459 /* Set up the folder view as a dnd destination. Set only the
1460 highlight flag, otherwise gtk will have a different
1462 gtk_drag_dest_set (GTK_WIDGET (self),
1463 GTK_DEST_DEFAULT_HIGHLIGHT,
1464 folder_view_drag_types,
1465 G_N_ELEMENTS (folder_view_drag_types),
1466 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1468 g_signal_connect (G_OBJECT (self),
1469 "drag_data_received",
1470 G_CALLBACK (on_drag_data_received),
1474 /* Set up the treeview as a dnd source */
1475 gtk_drag_source_set (GTK_WIDGET (self),
1477 folder_view_drag_types,
1478 G_N_ELEMENTS (folder_view_drag_types),
1479 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1481 g_signal_connect (G_OBJECT (self),
1483 G_CALLBACK (on_drag_motion),
1486 g_signal_connect (G_OBJECT (self),
1488 G_CALLBACK (on_drag_data_get),
1491 g_signal_connect (G_OBJECT (self),
1493 G_CALLBACK (drag_drop_cb),
1498 * This function manages the navigation through the folders using the
1499 * keyboard or the hardware keys in the device
1502 on_key_pressed (GtkWidget *self,
1506 GtkTreeSelection *selection;
1508 GtkTreeModel *model;
1509 gboolean retval = FALSE;
1511 /* Up and Down are automatically managed by the treeview */
1512 if (event->keyval == GDK_Return) {
1513 /* Expand/Collapse the selected row */
1514 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1515 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1518 path = gtk_tree_model_get_path (model, &iter);
1520 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1521 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1523 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1524 gtk_tree_path_free (path);
1526 /* No further processing */
1534 * We listen to the changes in the local folder account name key,
1535 * because we want to show the right name in the view. The local
1536 * folder account name corresponds to the device name in the Maemo
1537 * version. We do this because we do not want to query gconf on each
1538 * tree view refresh. It's better to cache it and change whenever
1542 on_configuration_key_changed (ModestConf* conf,
1544 ModestConfEvent event,
1545 ModestFolderView *self)
1547 ModestFolderViewPrivate *priv;
1552 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1553 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1555 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1556 g_free (priv->local_account_name);
1558 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1559 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1561 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1562 MODEST_CONF_DEVICE_NAME, NULL);
1564 /* Force a redraw */
1565 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1566 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1567 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1568 gtk_tree_view_column_queue_resize (tree_column);
1574 modest_folder_view_set_style (ModestFolderView *self,
1575 ModestFolderViewStyle style)
1577 ModestFolderViewPrivate *priv;
1579 g_return_if_fail (self);
1581 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1583 priv->style = style;
1587 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1588 const gchar *account_id)
1590 ModestFolderViewPrivate *priv;
1591 GtkTreeModel *model;
1593 g_return_if_fail (self);
1595 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1597 /* This will be used by the filter_row callback,
1598 * to decided which rows to show: */
1599 if (priv->visible_account_id) {
1600 g_free (priv->visible_account_id);
1601 priv->visible_account_id = NULL;
1604 priv->visible_account_id = g_strdup (account_id);
1607 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1608 if (GTK_IS_TREE_MODEL_FILTER (model))
1609 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1611 /* Save settings to gconf */
1612 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1613 MODEST_CONF_FOLDER_VIEW_KEY);
1617 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1619 ModestFolderViewPrivate *priv;
1621 g_return_val_if_fail (self, NULL);
1623 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1625 return (const gchar *) priv->visible_account_id;
1629 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1633 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1636 gtk_tree_model_get (model, iter,
1637 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1638 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1642 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n",
1643 __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1646 gboolean result = FALSE;
1647 if (type == TNY_FOLDER_TYPE_INBOX) {
1649 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1650 /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1651 * when getting folders from the cache, before connectin, so we do
1652 * an extra check. We could fix this in tinymail, but it's easier
1655 if (strcmp (name, "Inbox") == 0)
1662 *inbox_iter = *iter;
1666 if (gtk_tree_model_iter_children (model, &child, iter)) {
1667 if (find_inbox_iter (model, &child, inbox_iter))
1671 } while (gtk_tree_model_iter_next (model, iter));
1677 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1679 GtkTreeModel *model;
1680 GtkTreeIter iter, inbox_iter;
1681 GtkTreeSelection *sel;
1683 /* Do not set it if the folder view was not painted */
1684 if (!GTK_WIDGET_MAPPED (self))
1687 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1691 expand_root_items (self);
1692 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1694 gtk_tree_model_get_iter_first (model, &iter);
1695 if (find_inbox_iter (model, &iter, &inbox_iter)) {
1696 gtk_tree_selection_select_iter (sel, &inbox_iter);
1699 gtk_tree_model_get_iter_first (model, &iter);
1700 gtk_tree_selection_select_iter (sel, &iter);