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 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 priv->conf_key_signal =
491 g_signal_connect (G_OBJECT(conf),
493 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 if (priv->conf_key_signal) {
545 g_signal_handler_disconnect (modest_runtime_get_conf (),
546 priv->conf_key_signal);
547 priv->conf_key_signal = 0;
550 G_OBJECT_CLASS(parent_class)->finalize (obj);
555 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
557 ModestFolderViewPrivate *priv;
560 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
561 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
563 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
564 device = tny_account_store_get_device (account_store);
566 if (G_UNLIKELY (priv->account_store)) {
568 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
569 priv->account_update_signal))
570 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
571 priv->account_update_signal);
572 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
573 priv->accounts_reloaded_signal))
574 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
575 priv->accounts_reloaded_signal);
576 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
577 priv->account_removed_signal))
578 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
579 priv->account_removed_signal);
581 g_object_unref (G_OBJECT (priv->account_store));
584 priv->account_store = g_object_ref (G_OBJECT (account_store));
586 priv->account_update_signal =
587 g_signal_connect (G_OBJECT(account_store), "account_update",
588 G_CALLBACK (on_account_update), self);
590 priv->account_removed_signal =
591 g_signal_connect (G_OBJECT(account_store), "account_removed",
592 G_CALLBACK (on_account_removed), self);
594 priv->accounts_reloaded_signal =
595 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
596 G_CALLBACK (on_accounts_reloaded), self);
598 on_accounts_reloaded (account_store, (gpointer ) self);
600 g_object_unref (G_OBJECT (device));
604 on_account_removed (TnyAccountStore *account_store,
608 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
609 ModestFolderViewPrivate *priv;
611 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
613 /* If the removed account is the currently viewed one then
614 clear the configuration value. The new visible account will be the default account */
615 if (!strcmp (priv->visible_account_id, tny_account_get_id (account))) {
616 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
621 on_account_update (TnyAccountStore *account_store,
622 const gchar *account,
625 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
626 ModestFolderViewPrivate *priv;
628 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
629 if (!priv->visible_account_id)
630 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
631 MODEST_CONF_FOLDER_VIEW_KEY);
633 if (!modest_folder_view_update_model (self, account_store))
634 g_printerr ("modest: failed to update model for changes in '%s'",
639 on_accounts_reloaded (TnyAccountStore *account_store,
642 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
646 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
648 GtkTreeViewColumn *col;
650 g_return_if_fail (self);
652 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
654 g_printerr ("modest: failed get column for title\n");
658 gtk_tree_view_column_set_title (col, title);
659 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
664 modest_folder_view_new (TnyFolderStoreQuery *query)
667 ModestFolderViewPrivate *priv;
668 GtkTreeSelection *sel;
670 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
671 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
674 priv->query = g_object_ref (query);
676 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
677 priv->changed_signal = g_signal_connect (sel, "changed",
678 G_CALLBACK (on_selection_changed), self);
680 return GTK_WIDGET(self);
683 /* this feels dirty; any other way to expand all the root items? */
685 expand_root_items (ModestFolderView *self)
688 path = gtk_tree_path_new_first ();
690 /* all folders should have child items, so.. */
691 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
692 gtk_tree_path_next (path);
694 gtk_tree_path_free (path);
698 * We use this function to implement the
699 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
700 * account in this case, and the local folders.
703 filter_row (GtkTreeModel *model,
707 gboolean retval = TRUE;
708 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
709 GObject *instance = NULL;
711 gtk_tree_model_get (model, iter,
712 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
713 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
716 /* Do not show if there is no instance, this could indeed
717 happen when the model is being modified while it's being
718 drawn. This could occur for example when moving folders
723 if (type == TNY_FOLDER_TYPE_ROOT) {
724 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
725 if (TNY_IS_ACCOUNT (instance)) {
726 TnyAccount *acc = TNY_ACCOUNT (instance);
727 const gchar *account_id = tny_account_get_id (acc);
729 /* If it isn't a special folder,
730 * don't show it unless it is the visible account: */
731 if (!modest_tny_account_is_virtual_local_folders (acc) &&
732 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
733 /* Show only the visible account id */
734 ModestFolderViewPrivate *priv =
735 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
736 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
740 /* Never show these to the user. They are merged into one folder
741 * in the local-folders account instead: */
742 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
747 /* The virtual local-folders folder store is also shown by default. */
749 g_object_unref (instance);
755 modest_folder_view_update_model (ModestFolderView *self,
756 TnyAccountStore *account_store)
758 ModestFolderViewPrivate *priv;
759 GtkTreeModel *model /* , *old_model */;
760 /* TnyAccount *local_account; */
761 TnyList *model_as_list;
763 g_return_val_if_fail (account_store, FALSE);
765 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
767 /* Notify that there is no folder selected */
768 g_signal_emit (G_OBJECT(self),
769 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
772 /* FIXME: the local accounts are not shown when the query
773 selects only the subscribed folders. */
774 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
775 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
777 /* Deal with the model via its TnyList Interface,
778 * filling the TnyList via a get_accounts() call: */
779 model_as_list = TNY_LIST(model);
781 /* Get the accounts: */
782 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
784 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
785 g_object_unref (model_as_list);
786 model_as_list = NULL;
788 GtkTreeModel *filter_model = NULL, *sortable = NULL;
790 sortable = gtk_tree_model_sort_new_with_model (model);
791 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
792 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
794 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
795 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
796 cmp_rows, NULL, NULL);
798 /* Create filter model */
799 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
800 filter_model = gtk_tree_model_filter_new (sortable, NULL);
801 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
808 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
809 (filter_model) ? filter_model : sortable);
810 expand_root_items (self); /* expand all account folders */
812 g_object_unref (model);
815 g_object_unref (filter_model);
817 g_object_unref (sortable);
819 /* Select the first inbox or the local account if not found */
820 modest_folder_view_select_first_inbox_or_local (self);
827 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
830 TnyFolderStore *folder = NULL;
832 ModestFolderView *tree_view;
833 ModestFolderViewPrivate *priv;
835 g_return_if_fail (sel);
836 g_return_if_fail (user_data);
838 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
840 /* folder was _un_selected if true */
841 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
842 if (priv->cur_folder_store)
843 g_object_unref (priv->cur_folder_store);
844 priv->cur_folder_store = NULL;
846 /* Notify the display name observers */
847 g_signal_emit (G_OBJECT(user_data),
848 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
853 tree_view = MODEST_FOLDER_VIEW (user_data);
855 gtk_tree_model_get (model, &iter,
856 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
859 /* If the folder is the same do not notify */
860 if (priv->cur_folder_store == folder) {
861 g_object_unref (folder);
865 /* Current folder was unselected */
866 if (priv->cur_folder_store) {
867 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
868 priv->cur_folder_store, FALSE);
869 g_object_unref (priv->cur_folder_store);
872 /* New current references */
873 priv->cur_folder_store = folder;
875 /* New folder has been selected */
876 g_signal_emit (G_OBJECT(tree_view),
877 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
882 modest_folder_view_get_selected (ModestFolderView *self)
884 ModestFolderViewPrivate *priv;
886 g_return_val_if_fail (self, NULL);
888 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
889 if (priv->cur_folder_store)
890 g_object_ref (priv->cur_folder_store);
892 return priv->cur_folder_store;
896 get_cmp_rows_type_pos (GObject *folder)
898 /* Remote accounts -> Local account -> MMC account .*/
901 if (TNY_IS_ACCOUNT (folder) &&
902 modest_tny_account_is_virtual_local_folders (
903 TNY_ACCOUNT (folder))) {
905 } else if (TNY_IS_ACCOUNT (folder)) {
906 TnyAccount *account = TNY_ACCOUNT (folder);
907 const gchar *account_id = tny_account_get_id (account);
908 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
914 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
915 return -1; /* Should never happen */
920 * This function orders the mail accounts according to these rules:
921 * 1st - remote accounts
922 * 2nd - local account
926 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
932 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
933 GObject *folder1 = NULL;
934 GObject *folder2 = NULL;
936 gtk_tree_model_get (tree_model, iter1,
937 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
938 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
939 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
941 gtk_tree_model_get (tree_model, iter2,
942 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
943 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
946 if (type == TNY_FOLDER_TYPE_ROOT) {
947 /* Compare the types, so that
948 * Remote accounts -> Local account -> MMC account .*/
949 const gint pos1 = get_cmp_rows_type_pos (folder1);
950 const gint pos2 = get_cmp_rows_type_pos (folder2);
951 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
952 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
955 else if (pos1 > pos2)
958 /* Compare items of the same type: */
960 TnyAccount *account1 = NULL;
961 if (TNY_IS_ACCOUNT (folder1))
962 account1 = TNY_ACCOUNT (folder1);
964 TnyAccount *account2 = NULL;
965 if (TNY_IS_ACCOUNT (folder2))
966 account2 = TNY_ACCOUNT (folder2);
968 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
969 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
971 if (!account_id && !account_id2)
973 else if (!account_id)
975 else if (!account_id2)
977 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
980 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
983 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
987 g_object_unref(G_OBJECT(folder1));
989 g_object_unref(G_OBJECT(folder2));
997 /*****************************************************************************/
998 /* DRAG and DROP stuff */
999 /*****************************************************************************/
1002 * This function fills the #GtkSelectionData with the row and the
1003 * model that has been dragged. It's called when this widget is a
1004 * source for dnd after the event drop happened
1007 on_drag_data_get (GtkWidget *widget,
1008 GdkDragContext *context,
1009 GtkSelectionData *selection_data,
1014 GtkTreeSelection *selection;
1015 GtkTreeModel *model;
1017 GtkTreePath *source_row;
1019 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1020 gtk_tree_selection_get_selected (selection, &model, &iter);
1021 source_row = gtk_tree_model_get_path (model, &iter);
1023 gtk_tree_set_row_drag_data (selection_data,
1027 gtk_tree_path_free (source_row);
1030 typedef struct _DndHelper {
1031 gboolean delete_source;
1032 GtkTreePath *source_row;
1033 GdkDragContext *context;
1039 * This function is the callback of the
1040 * modest_mail_operation_xfer_msgs () and
1041 * modest_mail_operation_xfer_folder() calls. We check here if the
1042 * message/folder was correctly asynchronously transferred. The reason
1043 * to use the same callback is that the code is the same, it only has
1044 * to check that the operation went fine and then finalize the drag
1048 on_progress_changed (ModestMailOperation *mail_op,
1049 ModestMailOperationState *state,
1055 helper = (DndHelper *) user_data;
1057 if (!state->finished)
1060 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1066 /* Notify the drag source. Never call delete, the monitor will
1067 do the job if needed */
1068 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1070 /* Free the helper */
1071 gtk_tree_path_free (helper->source_row);
1072 g_slice_free (DndHelper, helper);
1076 * This function is used by drag_data_received_cb to manage drag and
1077 * drop of a header, i.e, and drag from the header view to the folder
1081 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1082 GtkTreeModel *dest_model,
1083 GtkTreePath *dest_row,
1086 TnyList *headers = NULL;
1087 TnyHeader *header = NULL;
1088 TnyFolder *folder = NULL;
1089 ModestMailOperation *mail_op = NULL;
1090 GtkTreeIter source_iter, dest_iter;
1092 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1093 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1094 g_return_if_fail (dest_row);
1095 g_return_if_fail (helper);
1098 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1099 gtk_tree_model_get (source_model, &source_iter,
1100 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1102 if (!TNY_IS_HEADER(header)) {
1103 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1108 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1109 gtk_tree_model_get (dest_model, &dest_iter,
1110 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1113 if (!TNY_IS_FOLDER(folder)) {
1114 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1118 /* Transfer message */
1119 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1120 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1122 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1123 G_CALLBACK (on_progress_changed), helper);
1125 headers = tny_simple_list_new ();
1126 tny_list_append (headers, G_OBJECT (header));
1127 modest_mail_operation_xfer_msgs (mail_op,
1130 helper->delete_source,
1135 if (G_IS_OBJECT(mail_op))
1136 g_object_unref (G_OBJECT (mail_op));
1137 if (G_IS_OBJECT(header))
1138 g_object_unref (G_OBJECT (header));
1139 if (G_IS_OBJECT(folder))
1140 g_object_unref (G_OBJECT (folder));
1141 if (G_IS_OBJECT(headers))
1142 g_object_unref (headers);
1146 * This function is used by drag_data_received_cb to manage drag and
1147 * drop of a folder, i.e, and drag from the folder view to the same
1151 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1152 GtkTreeModel *dest_model,
1153 GtkTreePath *dest_row,
1154 GtkSelectionData *selection_data,
1157 ModestMailOperation *mail_op;
1158 GtkTreeIter parent_iter, iter;
1159 TnyFolderStore *parent_folder;
1162 /* Check if the drag is possible */
1163 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1164 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1166 /* selection_data)) { */
1167 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1169 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1170 gtk_tree_path_free (helper->source_row);
1171 g_slice_free (DndHelper, helper);
1176 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1177 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1178 gtk_tree_model_get (source_model, &parent_iter,
1179 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1180 &parent_folder, -1);
1181 gtk_tree_model_get (source_model, &iter,
1182 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1185 /* Do the mail operation */
1186 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1188 modest_ui_actions_move_folder_error_handler,
1190 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1192 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1193 G_CALLBACK (on_progress_changed), helper);
1195 modest_mail_operation_xfer_folder (mail_op,
1198 helper->delete_source);
1201 g_object_unref (G_OBJECT (parent_folder));
1202 g_object_unref (G_OBJECT (folder));
1203 g_object_unref (G_OBJECT (mail_op));
1207 * This function receives the data set by the "drag-data-get" signal
1208 * handler. This information comes within the #GtkSelectionData. This
1209 * function will manage both the drags of folders of the treeview and
1210 * drags of headers of the header view widget.
1213 on_drag_data_received (GtkWidget *widget,
1214 GdkDragContext *context,
1217 GtkSelectionData *selection_data,
1222 GtkWidget *source_widget;
1223 GtkTreeModel *dest_model, *source_model;
1224 GtkTreePath *source_row, *dest_row;
1225 GtkTreeViewDropPosition pos;
1226 gboolean success = FALSE, delete_source = FALSE;
1227 DndHelper *helper = NULL;
1229 /* Do not allow further process */
1230 g_signal_stop_emission_by_name (widget, "drag-data-received");
1231 source_widget = gtk_drag_get_source_widget (context);
1233 /* Get the action */
1234 if (context->action == GDK_ACTION_MOVE) {
1235 delete_source = TRUE;
1237 /* Notify that there is no folder selected. We need to
1238 do this in order to update the headers view (and
1239 its monitors, because when moving, the old folder
1240 won't longer exist. We can not wait for the end of
1241 the operation, because the operation won't start if
1242 the folder is in use */
1243 if (source_widget == widget)
1244 g_signal_emit (G_OBJECT (widget),
1245 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1248 /* Check if the get_data failed */
1249 if (selection_data == NULL || selection_data->length < 0)
1250 gtk_drag_finish (context, success, FALSE, time);
1252 /* Get the models */
1253 gtk_tree_get_row_drag_data (selection_data,
1257 /* Select the destination model */
1258 if (source_widget == widget) {
1259 dest_model = source_model;
1261 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1264 /* Get the path to the destination row. Can not call
1265 gtk_tree_view_get_drag_dest_row() because the source row
1266 is not selected anymore */
1267 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1270 /* Only allow drops IN other rows */
1271 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1272 gtk_drag_finish (context, success, FALSE, time);
1274 /* Create the helper */
1275 helper = g_slice_new0 (DndHelper);
1276 helper->delete_source = delete_source;
1277 helper->source_row = gtk_tree_path_copy (source_row);
1278 helper->context = context;
1279 helper->time = time;
1281 /* Drags from the header view */
1282 if (source_widget != widget) {
1284 drag_and_drop_from_header_view (source_model,
1291 drag_and_drop_from_folder_view (source_model,
1299 gtk_tree_path_free (source_row);
1300 gtk_tree_path_free (dest_row);
1304 * We define a "drag-drop" signal handler because we do not want to
1305 * use the default one, because the default one always calls
1306 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1307 * signal handler, because there we have all the information available
1308 * to know if the dnd was a success or not.
1311 drag_drop_cb (GtkWidget *widget,
1312 GdkDragContext *context,
1320 if (!context->targets)
1323 /* Check if we're dragging a folder row */
1324 target = gtk_drag_dest_find_target (widget, context, NULL);
1326 /* Request the data from the source. */
1327 gtk_drag_get_data(widget, context, target, time);
1333 * This function expands a node of a tree view if it's not expanded
1334 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1335 * does that, so that's why they're here.
1338 expand_row_timeout (gpointer data)
1340 GtkTreeView *tree_view = data;
1341 GtkTreePath *dest_path = NULL;
1342 GtkTreeViewDropPosition pos;
1343 gboolean result = FALSE;
1345 GDK_THREADS_ENTER ();
1347 gtk_tree_view_get_drag_dest_row (tree_view,
1352 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1353 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1354 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1355 gtk_tree_path_free (dest_path);
1359 gtk_tree_path_free (dest_path);
1364 GDK_THREADS_LEAVE ();
1370 * This function is called whenever the pointer is moved over a widget
1371 * while dragging some data. It installs a timeout that will expand a
1372 * node of the treeview if not expanded yet. This function also calls
1373 * gdk_drag_status in order to set the suggested action that will be
1374 * used by the "drag-data-received" signal handler to know if we
1375 * should do a move or just a copy of the data.
1378 on_drag_motion (GtkWidget *widget,
1379 GdkDragContext *context,
1385 GtkTreeViewDropPosition pos;
1386 GtkTreePath *dest_row;
1387 ModestFolderViewPrivate *priv;
1388 GdkDragAction suggested_action;
1389 gboolean valid_location = FALSE;
1391 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1393 if (priv->timer_expander != 0) {
1394 g_source_remove (priv->timer_expander);
1395 priv->timer_expander = 0;
1398 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1403 /* Do not allow drops between folders */
1405 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1406 pos == GTK_TREE_VIEW_DROP_AFTER) {
1407 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1408 gdk_drag_status(context, 0, time);
1409 valid_location = FALSE;
1412 valid_location = TRUE;
1415 /* Expand the selected row after 1/2 second */
1416 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1417 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1418 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1421 /* Select the desired action. By default we pick MOVE */
1422 suggested_action = GDK_ACTION_MOVE;
1424 if (context->actions == GDK_ACTION_COPY)
1425 gdk_drag_status(context, GDK_ACTION_COPY, time);
1426 else if (context->actions == GDK_ACTION_MOVE)
1427 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1428 else if (context->actions & suggested_action)
1429 gdk_drag_status(context, suggested_action, time);
1431 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1435 gtk_tree_path_free (dest_row);
1436 g_signal_stop_emission_by_name (widget, "drag-motion");
1437 return valid_location;
1441 /* Folder view drag types */
1442 const GtkTargetEntry folder_view_drag_types[] =
1444 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1445 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1449 * This function sets the treeview as a source and a target for dnd
1450 * events. It also connects all the requirede signals.
1453 setup_drag_and_drop (GtkTreeView *self)
1455 /* Set up the folder view as a dnd destination. Set only the
1456 highlight flag, otherwise gtk will have a different
1458 gtk_drag_dest_set (GTK_WIDGET (self),
1459 GTK_DEST_DEFAULT_HIGHLIGHT,
1460 folder_view_drag_types,
1461 G_N_ELEMENTS (folder_view_drag_types),
1462 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1464 g_signal_connect (G_OBJECT (self),
1465 "drag_data_received",
1466 G_CALLBACK (on_drag_data_received),
1470 /* Set up the treeview as a dnd source */
1471 gtk_drag_source_set (GTK_WIDGET (self),
1473 folder_view_drag_types,
1474 G_N_ELEMENTS (folder_view_drag_types),
1475 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1477 g_signal_connect (G_OBJECT (self),
1479 G_CALLBACK (on_drag_motion),
1482 g_signal_connect (G_OBJECT (self),
1484 G_CALLBACK (on_drag_data_get),
1487 g_signal_connect (G_OBJECT (self),
1489 G_CALLBACK (drag_drop_cb),
1494 * This function manages the navigation through the folders using the
1495 * keyboard or the hardware keys in the device
1498 on_key_pressed (GtkWidget *self,
1502 GtkTreeSelection *selection;
1504 GtkTreeModel *model;
1505 gboolean retval = FALSE;
1507 /* Up and Down are automatically managed by the treeview */
1508 if (event->keyval == GDK_Return) {
1509 /* Expand/Collapse the selected row */
1510 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1511 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1514 path = gtk_tree_model_get_path (model, &iter);
1516 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1517 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1519 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1520 gtk_tree_path_free (path);
1522 /* No further processing */
1530 * We listen to the changes in the local folder account name key,
1531 * because we want to show the right name in the view. The local
1532 * folder account name corresponds to the device name in the Maemo
1533 * version. We do this because we do not want to query gconf on each
1534 * tree view refresh. It's better to cache it and change whenever
1538 on_configuration_key_changed (ModestConf* conf,
1540 ModestConfEvent event,
1541 ModestFolderView *self)
1543 ModestFolderViewPrivate *priv;
1548 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1549 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1551 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1552 g_free (priv->local_account_name);
1554 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1555 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1557 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1558 MODEST_CONF_DEVICE_NAME, NULL);
1560 /* Force a redraw */
1561 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1562 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1563 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1564 gtk_tree_view_column_queue_resize (tree_column);
1570 modest_folder_view_set_style (ModestFolderView *self,
1571 ModestFolderViewStyle style)
1573 ModestFolderViewPrivate *priv;
1575 g_return_if_fail (self);
1577 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1579 priv->style = style;
1583 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1584 const gchar *account_id)
1586 ModestFolderViewPrivate *priv;
1587 GtkTreeModel *model;
1589 g_return_if_fail (self);
1591 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1593 /* This will be used by the filter_row callback,
1594 * to decided which rows to show: */
1595 if (priv->visible_account_id) {
1596 g_free (priv->visible_account_id);
1597 priv->visible_account_id = NULL;
1600 priv->visible_account_id = g_strdup (account_id);
1603 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1604 if (GTK_IS_TREE_MODEL_FILTER (model))
1605 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1607 /* Save settings to gconf */
1608 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1609 MODEST_CONF_FOLDER_VIEW_KEY);
1613 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1615 ModestFolderViewPrivate *priv;
1617 g_return_val_if_fail (self, NULL);
1619 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1621 return (const gchar *) priv->visible_account_id;
1625 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1629 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1632 gtk_tree_model_get (model, iter,
1633 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1634 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1638 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n",
1639 __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1642 gboolean result = FALSE;
1643 if (type == TNY_FOLDER_TYPE_INBOX) {
1645 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1646 /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1647 * when getting folders from the cache, before connectin, so we do
1648 * an extra check. We could fix this in tinymail, but it's easier
1651 if (strcmp (name, "Inbox") == 0)
1658 *inbox_iter = *iter;
1662 if (gtk_tree_model_iter_children (model, &child, iter)) {
1663 if (find_inbox_iter (model, &child, inbox_iter))
1667 } while (gtk_tree_model_iter_next (model, iter));
1673 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1675 GtkTreeModel *model;
1676 GtkTreeIter iter, inbox_iter;
1677 GtkTreeSelection *sel;
1679 /* Do not set it if the folder view was not painted */
1680 if (!GTK_WIDGET_MAPPED (self))
1683 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1687 expand_root_items (self);
1688 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1690 gtk_tree_model_get_iter_first (model, &iter);
1691 if (find_inbox_iter (model, &iter, &inbox_iter)) {
1692 gtk_tree_selection_select_iter (sel, &inbox_iter);
1695 gtk_tree_model_get_iter_first (model, &iter);
1696 gtk_tree_selection_select_iter (sel, &iter);