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-account-store.h>
39 #include <tny-account.h>
40 #include <tny-folder.h>
41 #include <tny-camel-folder.h>
42 #include <tny-simple-list.h>
43 #include <tny-merge-folder.h>
44 #include <modest-tny-folder.h>
45 #include <modest-tny-simple-folder-store.h>
46 #include <modest-marshal.h>
47 #include <modest-icon-names.h>
48 #include <modest-tny-account-store.h>
49 #include <modest-tny-outbox-account.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-account-mgr-helpers.h>
56 #include <modest-widget-memory.h>
59 /* 'private'/'protected' functions */
60 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
61 static void modest_folder_view_init (ModestFolderView *obj);
62 static void modest_folder_view_finalize (GObject *obj);
64 static void tny_account_store_view_init (gpointer g,
67 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
68 TnyAccountStore *account_store);
70 static gboolean update_model (ModestFolderView *self,
71 ModestTnyAccountStore *account_store);
73 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
75 static void on_account_update (TnyAccountStore *account_store,
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;
143 GtkTreeSelection *cur_selection;
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 GObject *instance = NULL;
247 g_return_if_fail (column);
248 g_return_if_fail (tree_model);
250 gtk_tree_model_get (tree_model, iter,
251 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
252 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
253 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
254 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
255 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
257 rendobj = G_OBJECT(renderer);
268 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
270 gchar *item_name = NULL;
271 gint item_weight = 400;
273 if (type != TNY_FOLDER_TYPE_ROOT) {
276 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
278 type = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
279 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
281 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
285 /* Select the number to show */
286 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
291 /* Use bold font style if there are unread messages */
293 item_name = g_strdup_printf ("%s (%d)", fname, unread);
296 item_name = g_strdup (fname);
300 } else if (TNY_IS_ACCOUNT (instance)) {
301 /* If it's a server account */
302 const gchar * account_id = tny_account_get_id (TNY_ACCOUNT (instance));
303 if (!strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
304 item_name = g_strdup (priv->local_account_name);
306 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
307 /* TODO: get MMC card name */
308 item_name = g_strdup (_("MMC"));
310 item_name = g_strdup (fname);
315 } else if (modest_tny_folder_store_is_virtual_local_folders (
316 TNY_FOLDER_STORE(instance)))
318 /* We use ModestTnySimpleFolder store to group the outboxes and
319 * the other local folders together: */
320 item_name = g_strdup (_("Local Folders"));
325 item_name = g_strdup ("unknown");
327 if (item_name && item_weight) {
328 /* Set the name in the treeview cell: */
329 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
331 /* Notify display name observers */
332 if (G_OBJECT (priv->cur_folder_store) == instance) {
333 g_signal_emit (G_OBJECT(data),
334 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
342 g_object_unref (G_OBJECT (instance));
349 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
350 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
352 GObject *rendobj = NULL, *instance = NULL;
353 GdkPixbuf *pixbuf = NULL;
356 const gchar *account_id = NULL;
359 rendobj = G_OBJECT(renderer);
360 gtk_tree_model_get (tree_model, iter,
361 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
362 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
363 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
364 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
375 if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
376 type = modest_tny_folder_guess_folder_type_from_name (fname);
380 case TNY_FOLDER_TYPE_ROOT:
381 if (TNY_IS_ACCOUNT (instance)) {
382 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
384 if (!strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
385 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
388 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
389 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
391 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
396 else if (modest_tny_folder_store_is_virtual_local_folders (
397 TNY_FOLDER_STORE (instance))) {
398 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
401 case TNY_FOLDER_TYPE_INBOX:
402 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
404 case TNY_FOLDER_TYPE_OUTBOX:
405 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
407 case TNY_FOLDER_TYPE_JUNK:
408 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
410 case TNY_FOLDER_TYPE_SENT:
411 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
413 case TNY_FOLDER_TYPE_TRASH:
414 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
416 case TNY_FOLDER_TYPE_DRAFTS:
417 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
419 case TNY_FOLDER_TYPE_NORMAL:
421 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
425 g_object_unref (G_OBJECT (instance));
429 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
432 g_object_unref (pixbuf);
436 add_columns (GtkWidget *treeview)
438 GtkTreeViewColumn *column;
439 GtkCellRenderer *renderer;
440 GtkTreeSelection *sel;
443 column = gtk_tree_view_column_new ();
445 /* Set icon and text render function */
446 renderer = gtk_cell_renderer_pixbuf_new();
447 gtk_tree_view_column_pack_start (column, renderer, FALSE);
448 gtk_tree_view_column_set_cell_data_func(column, renderer,
449 icon_cell_data, treeview, NULL);
451 renderer = gtk_cell_renderer_text_new();
452 gtk_tree_view_column_pack_start (column, renderer, FALSE);
453 gtk_tree_view_column_set_cell_data_func(column, renderer,
454 text_cell_data, treeview, NULL);
456 /* Set selection mode */
457 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
458 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
460 /* Set treeview appearance */
461 gtk_tree_view_column_set_spacing (column, 2);
462 gtk_tree_view_column_set_resizable (column, TRUE);
463 gtk_tree_view_column_set_fixed_width (column, TRUE);
464 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
465 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
468 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
472 modest_folder_view_init (ModestFolderView *obj)
474 ModestFolderViewPrivate *priv;
477 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
479 priv->timer_expander = 0;
480 priv->account_store = NULL;
482 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
483 priv->cur_folder_store = NULL;
484 priv->visible_account_id = NULL;
486 /* Initialize the local account name */
487 conf = modest_runtime_get_conf();
488 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
491 add_columns (GTK_WIDGET (obj));
493 /* Setup drag and drop */
494 setup_drag_and_drop (GTK_TREE_VIEW(obj));
497 modest_widget_memory_restore (conf, G_OBJECT (obj), MODEST_CONF_FOLDER_VIEW_KEY);
499 /* Connect signals */
500 g_signal_connect (G_OBJECT (obj),
502 G_CALLBACK (on_key_pressed), NULL);
505 * Track changes in the local account name (in the device it
506 * will be the device name)
508 g_signal_connect (G_OBJECT(conf),
510 G_CALLBACK(on_configuration_key_changed), obj);
515 tny_account_store_view_init (gpointer g, gpointer iface_data)
517 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
519 klass->set_account_store_func = modest_folder_view_set_account_store;
525 modest_folder_view_finalize (GObject *obj)
527 ModestFolderViewPrivate *priv;
528 GtkTreeSelection *sel;
530 g_return_if_fail (obj);
532 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
534 if (priv->timer_expander != 0) {
535 g_source_remove (priv->timer_expander);
536 priv->timer_expander = 0;
539 if (priv->account_store) {
540 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
541 priv->account_update_signal);
542 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
543 priv->accounts_reloaded_signal);
544 g_object_unref (G_OBJECT(priv->account_store));
545 priv->account_store = NULL;
549 g_object_unref (G_OBJECT (priv->query));
553 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
555 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
557 g_free (priv->local_account_name);
558 g_free (priv->visible_account_id);
560 G_OBJECT_CLASS(parent_class)->finalize (obj);
565 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
567 ModestFolderViewPrivate *priv;
570 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
571 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
573 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
574 device = tny_account_store_get_device (account_store);
576 if (G_UNLIKELY (priv->account_store)) {
578 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
579 priv->account_update_signal))
580 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
581 priv->account_update_signal);
582 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
583 priv->accounts_reloaded_signal))
584 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
585 priv->accounts_reloaded_signal);
587 g_object_unref (G_OBJECT (priv->account_store));
590 priv->account_store = g_object_ref (G_OBJECT (account_store));
592 priv->account_update_signal =
593 g_signal_connect (G_OBJECT(account_store), "account_update",
594 G_CALLBACK (on_account_update), self);
596 priv->accounts_reloaded_signal =
597 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
598 G_CALLBACK (on_accounts_reloaded), self);
600 if (!update_model (MODEST_FOLDER_VIEW (self),
601 MODEST_TNY_ACCOUNT_STORE (priv->account_store)))
602 g_printerr ("modest: failed to update model\n");
604 g_object_unref (G_OBJECT (device));
608 on_account_update (TnyAccountStore *account_store, const gchar *account,
611 if (!update_model (MODEST_FOLDER_VIEW(user_data),
612 MODEST_TNY_ACCOUNT_STORE(account_store)))
613 g_printerr ("modest: failed to update model for changes in '%s'",
618 on_accounts_reloaded (TnyAccountStore *account_store,
621 update_model (MODEST_FOLDER_VIEW (user_data),
622 MODEST_TNY_ACCOUNT_STORE(account_store));
626 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
628 GtkTreeViewColumn *col;
630 g_return_if_fail (self);
632 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
634 g_printerr ("modest: failed get column for title\n");
638 gtk_tree_view_column_set_title (col, title);
639 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
644 modest_folder_view_new (TnyFolderStoreQuery *query)
647 ModestFolderViewPrivate *priv;
648 GtkTreeSelection *sel;
650 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
651 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
654 priv->query = g_object_ref (query);
656 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
657 priv->changed_signal = g_signal_connect (sel, "changed",
658 G_CALLBACK (on_selection_changed), self);
660 return GTK_WIDGET(self);
663 /* this feels dirty; any other way to expand all the root items? */
665 expand_root_items (ModestFolderView *self)
668 path = gtk_tree_path_new_first ();
670 /* all folders should have child items, so.. */
671 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
672 gtk_tree_path_next (path);
674 gtk_tree_path_free (path);
678 * We use this function to implement the
679 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
680 * account in this case, and the local folders.
683 filter_row (GtkTreeModel *model,
687 gboolean retval = TRUE;
689 GObject *instance = NULL;
691 gtk_tree_model_get (model, iter,
692 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
693 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
696 if (type == TNY_FOLDER_TYPE_ROOT) {
697 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
698 if (TNY_IS_ACCOUNT (instance)) {
699 TnyAccount *acc = TNY_ACCOUNT (instance);
700 const gchar *account_id = tny_account_get_id (acc);
702 /* If it isn't a special folder,
703 * don't show it unless it is the visible account: */
704 if (strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID) &&
705 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
707 /* TODO: Merge the folders before we get to this point? */
709 /* Show only the visible account id */
711 ModestFolderViewPrivate *priv =
712 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
713 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
720 g_object_unref (instance);
726 static void on_tnylist_accounts_debug_print(gpointer data, gpointer user_data)
728 TnyAccount* account = TNY_ACCOUNT(data);
729 const gchar *prefix = (const gchar*)(user_data);
731 printf("%s account id=%s\n", prefix, tny_account_get_id (account));
736 add_account_folders_to_merged_folder (TnyAccount *account, TnyMergeFolder* merge_folder)
738 const gchar* account_id = tny_account_get_id (account);
739 const gboolean is_actual_local_folders_account = account_id &&
740 (strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID) == 0);
742 TnyList *list_outbox_folders = tny_simple_list_new ();
743 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
744 list_outbox_folders, NULL, NULL);
746 TnyIterator* iter = tny_list_create_iterator (list_outbox_folders);
747 while (!tny_iterator_is_done (iter))
749 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (iter));
753 /* TODO: Do not add outboxes that are inside local-folders/,
754 * because these are just left-over from earlier Modest versions
755 * that put the outbox there: */
756 if (is_actual_local_folders_account) {
757 const TnyFolderType type = modest_tny_folder_get_local_folder_type (folder);
758 if (type == TNY_FOLDER_TYPE_OUTBOX) {
764 tny_merge_folder_add_folder (merge_folder, folder);
766 g_object_unref (folder);
769 tny_iterator_next (iter);
772 g_object_unref (list_outbox_folders);
777 add_account_folders_to_simple_folder_store (TnyAccount *account, ModestTnySimpleFolderStore* store)
779 g_return_if_fail (account);
780 g_return_if_fail (store);
782 TnyList *list_outbox_folders = tny_simple_list_new ();
783 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
784 list_outbox_folders, NULL, NULL);
786 /* Special handling for the .modest/local-folders account,
787 * to avoid adding unwanted folders.
788 * We cannot prevent them from being in the TnyAccount without
789 * changing the libtinymail-camel. */
790 const gchar* account_id = tny_account_get_id (account);
791 const gboolean is_actual_local_folders_account = account_id &&
792 (strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID) == 0);
794 TnyIterator* iter = tny_list_create_iterator (list_outbox_folders);
795 while (!tny_iterator_is_done (iter))
797 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (iter));
801 /* TODO: Do not add outboxes that are inside local-folders/,
802 * because these are just left-over from earlier Modest versions
803 * that put the outbox there: */
804 if (is_actual_local_folders_account) {
805 const TnyFolderType type = modest_tny_folder_get_local_folder_type (folder);
806 if (type == TNY_FOLDER_TYPE_OUTBOX) {
812 modest_tny_simple_folder_store_add_folder (store, folder);
814 g_object_unref (folder);
817 tny_iterator_next (iter);
820 g_object_unref (list_outbox_folders);
824 update_model (ModestFolderView *self, ModestTnyAccountStore *account_store)
826 ModestFolderViewPrivate *priv;
830 g_return_val_if_fail (account_store, FALSE);
832 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
834 /* Notify that there is no folder selected */
835 g_signal_emit (G_OBJECT(self),
836 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
839 /* FIXME: the local accounts are not shown when the query
840 selects only the subscribed folders. */
841 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
842 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
844 /* Deal with the model via its TnyList Interface,
845 * filling the TnyList via a get_accounts() call: */
846 TnyList *model_as_list = TNY_LIST(model);
848 /* Create a virtual local-folders folder store,
849 * containing the real local folders and the (merged) various per-account
852 ModestTnySimpleFolderStore *store = modest_tny_simple_folder_store_new ();
854 /* Get the accounts: */
855 TnyList *account_list = tny_simple_list_new ();
856 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
858 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
859 TnyIterator* iter = tny_list_create_iterator (account_list);
861 /* All per-account outbox folders are merged into one folders
862 * so that they appear as one outbox to the user: */
863 TnyMergeFolder *merged_outbox = TNY_MERGE_FOLDER (tny_merge_folder_new());
865 while (!tny_iterator_is_done (iter))
867 GObject *cur = tny_iterator_get_current (iter);
868 TnyAccount *account = TNY_ACCOUNT (cur);
870 /* Add both outbox account and local-folders account folders
871 * to our one combined account:
873 if (MODEST_IS_TNY_OUTBOX_ACCOUNT (account)) {
874 /* Add the folder to the merged folder.
875 * We will add it later to the virtual local-folders store: */
876 add_account_folders_to_merged_folder (account, merged_outbox);
878 const gchar *account_id = tny_account_get_id (account);
879 if (account_id && !strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
880 /* Add the folders to the virtual local-folders store: */
881 add_account_folders_to_simple_folder_store (account, store);
884 /* Just add the account: */
885 tny_list_append (model_as_list, G_OBJECT (account));
890 g_object_unref (cur);
891 tny_iterator_next (iter);
894 /* Add the merged outbox folder to the virtual local-folders store: */
895 modest_tny_simple_folder_store_add_folder (store, TNY_FOLDER(merged_outbox));
896 g_object_unref (merged_outbox);
897 merged_outbox = NULL;
899 /* Add the virtual local-folders store to the model: */
900 tny_list_append (model_as_list, G_OBJECT (store));
903 g_object_unref (account_list);
906 g_object_unref (model_as_list);
907 model_as_list = NULL;
909 /* tny_list_foreach (account_list, on_tnylist_accounts_debug_print, "update_model: "); */
911 GtkTreeModel *filter_model = NULL, *sortable = NULL;
913 sortable = gtk_tree_model_sort_new_with_model (model);
914 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
915 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
917 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
918 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
919 cmp_rows, NULL, NULL);
921 /* Create filter model */
922 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
923 filter_model = gtk_tree_model_filter_new (sortable, NULL);
924 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
931 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
932 (filter_model) ? filter_model : sortable);
933 expand_root_items (self); /* expand all account folders */
940 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
943 TnyFolderStore *folder = NULL;
945 ModestFolderView *tree_view;
946 ModestFolderViewPrivate *priv;
949 g_return_if_fail (sel);
950 g_return_if_fail (user_data);
952 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
953 priv->cur_selection = sel;
955 /* folder was _un_selected if true */
956 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
957 if (priv->cur_folder_store)
958 g_object_unref (priv->cur_folder_store);
959 priv->cur_folder_store = NULL;
961 /* Notify the display name observers */
962 g_signal_emit (G_OBJECT(user_data),
963 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
968 tree_view = MODEST_FOLDER_VIEW (user_data);
970 gtk_tree_model_get (model, &iter,
971 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
972 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
975 /* If the folder is the same do not notify */
976 if (priv->cur_folder_store == folder) {
977 g_object_unref (folder);
981 /* Current folder was unselected */
982 if (priv->cur_folder_store) {
983 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
984 priv->cur_folder_store, FALSE);
985 g_object_unref (priv->cur_folder_store);
988 /* New current references */
989 priv->cur_folder_store = folder;
991 /* New folder has been selected */
992 g_signal_emit (G_OBJECT(tree_view),
993 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
998 modest_folder_view_get_selected (ModestFolderView *self)
1000 ModestFolderViewPrivate *priv;
1002 g_return_val_if_fail (self, NULL);
1004 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1005 if (priv->cur_folder_store)
1006 g_object_ref (priv->cur_folder_store);
1008 return priv->cur_folder_store;
1012 get_cmp_rows_type_pos (GObject *folder)
1014 /* Remote accounts -> Local account -> MMC account .*/
1017 if (TNY_IS_FOLDER_STORE (folder) &&
1018 modest_tny_folder_store_is_virtual_local_folders (
1019 TNY_FOLDER_STORE (folder))) {
1021 } else if (TNY_IS_ACCOUNT (folder)) {
1022 TnyAccount *account = TNY_ACCOUNT (folder);
1023 const gchar *account_id = tny_account_get_id (account);
1024 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1030 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1031 return -1; /* Should never happen */
1036 * This function orders the mail accounts according to these rules:
1037 * 1st - remote accounts
1038 * 2nd - local account
1042 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1046 gchar *name1, *name2;
1048 GObject *folder1 = NULL;
1049 GObject *folder2 = NULL;
1051 gtk_tree_model_get (tree_model, iter1,
1052 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1053 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1054 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1056 gtk_tree_model_get (tree_model, iter2,
1057 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1058 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1061 if (type == TNY_FOLDER_TYPE_ROOT) {
1062 /* Compare the types, so that
1063 * Remote accounts -> Local account -> MMC account .*/
1064 const gint pos1 = get_cmp_rows_type_pos (folder1);
1065 const gint pos2 = get_cmp_rows_type_pos (folder2);
1066 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1067 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1070 else if (pos1 > pos2)
1073 /* Compare items of the same type: */
1075 TnyAccount *account1 = NULL;
1076 if (TNY_IS_ACCOUNT (folder1))
1077 account1 = TNY_ACCOUNT (folder1);
1079 TnyAccount *account2 = NULL;
1080 if (TNY_IS_ACCOUNT (folder2))
1081 account2 = TNY_ACCOUNT (folder2);
1083 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1084 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1086 if (!account_id && !account_id2)
1088 else if (!account_id)
1090 else if (!account_id2)
1092 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1095 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1098 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1102 g_object_unref(G_OBJECT(folder1));
1104 g_object_unref(G_OBJECT(folder2));
1112 /*****************************************************************************/
1113 /* DRAG and DROP stuff */
1114 /*****************************************************************************/
1117 * This function fills the #GtkSelectionData with the row and the
1118 * model that has been dragged. It's called when this widget is a
1119 * source for dnd after the event drop happened
1122 on_drag_data_get (GtkWidget *widget,
1123 GdkDragContext *context,
1124 GtkSelectionData *selection_data,
1129 GtkTreeSelection *selection;
1130 GtkTreeModel *model;
1132 GtkTreePath *source_row;
1134 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1135 gtk_tree_selection_get_selected (selection, &model, &iter);
1136 source_row = gtk_tree_model_get_path (model, &iter);
1138 gtk_tree_set_row_drag_data (selection_data,
1142 gtk_tree_path_free (source_row);
1145 typedef struct _DndHelper {
1146 gboolean delete_source;
1147 GtkTreePath *source_row;
1148 GdkDragContext *context;
1154 * This function is the callback of the
1155 * modest_mail_operation_xfer_msgs () and
1156 * modest_mail_operation_xfer_folder() calls. We check here if the
1157 * message/folder was correctly asynchronously transferred. The reason
1158 * to use the same callback is that the code is the same, it only has
1159 * to check that the operation went fine and then finalize the drag
1163 on_progress_changed (ModestMailOperation *mail_op, gpointer user_data)
1168 helper = (DndHelper *) user_data;
1170 if (!modest_mail_operation_is_finished (mail_op))
1173 if (modest_mail_operation_get_status (mail_op) ==
1174 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1180 /* Notify the drag source. Never call delete, the monitor will
1181 do the job if needed */
1182 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1184 /* Free the helper */
1185 gtk_tree_path_free (helper->source_row);
1186 g_slice_free (DndHelper, helper);
1190 * This function is used by drag_data_received_cb to manage drag and
1191 * drop of a header, i.e, and drag from the header view to the folder
1195 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1196 GtkTreeModel *dest_model,
1197 GtkTreePath *dest_row,
1203 ModestMailOperation *mail_op;
1204 GtkTreeIter source_iter, dest_iter;
1207 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1208 gtk_tree_model_get (source_model, &source_iter,
1209 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1213 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1214 gtk_tree_model_get (dest_model, &dest_iter,
1215 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1218 /* Transfer message */
1219 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_ID_RECEIVE, NULL);
1221 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1223 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1224 G_CALLBACK (on_progress_changed), helper);
1226 /* FIXME: I replaced this because the API changed, but D&D
1227 should be reviewed in order to allow multiple drags*/
1228 headers = tny_simple_list_new ();
1229 tny_list_append (headers, G_OBJECT (header));
1230 modest_mail_operation_xfer_msgs (mail_op, headers, folder, helper->delete_source, NULL, NULL);
1233 g_object_unref (G_OBJECT (mail_op));
1234 g_object_unref (G_OBJECT (header));
1235 g_object_unref (G_OBJECT (folder));
1236 g_object_unref (headers);
1240 * This function is used by drag_data_received_cb to manage drag and
1241 * drop of a folder, i.e, and drag from the folder view to the same
1245 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1246 GtkTreeModel *dest_model,
1247 GtkTreePath *dest_row,
1248 GtkSelectionData *selection_data,
1251 ModestMailOperation *mail_op;
1252 GtkTreeIter parent_iter, iter;
1253 TnyFolderStore *parent_folder;
1256 /* Check if the drag is possible */
1257 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1258 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1260 /* selection_data)) { */
1261 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1263 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1264 gtk_tree_path_free (helper->source_row);
1265 g_slice_free (DndHelper, helper);
1270 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1271 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1272 gtk_tree_model_get (source_model, &parent_iter,
1273 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1274 &parent_folder, -1);
1275 gtk_tree_model_get (source_model, &iter,
1276 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1279 /* Do the mail operation */
1280 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_ID_RECEIVE, NULL);
1281 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1283 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1284 G_CALLBACK (on_progress_changed), helper);
1286 modest_mail_operation_xfer_folder (mail_op,
1289 helper->delete_source);
1292 g_object_unref (G_OBJECT (parent_folder));
1293 g_object_unref (G_OBJECT (folder));
1294 g_object_unref (G_OBJECT (mail_op));
1298 * This function receives the data set by the "drag-data-get" signal
1299 * handler. This information comes within the #GtkSelectionData. This
1300 * function will manage both the drags of folders of the treeview and
1301 * drags of headers of the header view widget.
1304 on_drag_data_received (GtkWidget *widget,
1305 GdkDragContext *context,
1308 GtkSelectionData *selection_data,
1313 GtkWidget *source_widget;
1314 GtkTreeModel *dest_model, *source_model;
1315 GtkTreePath *source_row, *dest_row;
1316 GtkTreeViewDropPosition pos;
1317 gboolean success = FALSE, delete_source = FALSE;
1318 DndHelper *helper = NULL;
1320 /* Do not allow further process */
1321 g_signal_stop_emission_by_name (widget, "drag-data-received");
1322 source_widget = gtk_drag_get_source_widget (context);
1324 /* Get the action */
1325 if (context->action == GDK_ACTION_MOVE) {
1326 delete_source = TRUE;
1328 /* Notify that there is no folder selected. We need to
1329 do this in order to update the headers view (and
1330 its monitors, because when moving, the old folder
1331 won't longer exist. We can not wait for the end of
1332 the operation, because the operation won't start if
1333 the folder is in use */
1334 if (source_widget == widget)
1335 g_signal_emit (G_OBJECT (widget),
1336 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1339 /* Check if the get_data failed */
1340 if (selection_data == NULL || selection_data->length < 0)
1341 gtk_drag_finish (context, success, FALSE, time);
1343 /* Get the models */
1344 gtk_tree_get_row_drag_data (selection_data,
1348 /* Select the destination model */
1349 if (source_widget == widget) {
1350 dest_model = source_model;
1352 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1355 /* Get the path to the destination row. Can not call
1356 gtk_tree_view_get_drag_dest_row() because the source row
1357 is not selected anymore */
1358 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1361 /* Only allow drops IN other rows */
1362 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1363 gtk_drag_finish (context, success, FALSE, time);
1365 /* Create the helper */
1366 helper = g_slice_new0 (DndHelper);
1367 helper->delete_source = delete_source;
1368 helper->source_row = gtk_tree_path_copy (source_row);
1369 helper->context = context;
1370 helper->time = time;
1372 /* Drags from the header view */
1373 if (source_widget != widget) {
1375 drag_and_drop_from_header_view (source_model,
1382 drag_and_drop_from_folder_view (source_model,
1390 gtk_tree_path_free (source_row);
1391 gtk_tree_path_free (dest_row);
1395 * We define a "drag-drop" signal handler because we do not want to
1396 * use the default one, because the default one always calls
1397 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1398 * signal handler, because there we have all the information available
1399 * to know if the dnd was a success or not.
1402 drag_drop_cb (GtkWidget *widget,
1403 GdkDragContext *context,
1411 if (!context->targets)
1414 /* Check if we're dragging a folder row */
1415 target = gtk_drag_dest_find_target (widget, context, NULL);
1417 /* Request the data from the source. */
1418 gtk_drag_get_data(widget, context, target, time);
1424 * This function expands a node of a tree view if it's not expanded
1425 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1426 * does that, so that's why they're here.
1429 expand_row_timeout (gpointer data)
1431 GtkTreeView *tree_view = data;
1432 GtkTreePath *dest_path = NULL;
1433 GtkTreeViewDropPosition pos;
1434 gboolean result = FALSE;
1436 GDK_THREADS_ENTER ();
1438 gtk_tree_view_get_drag_dest_row (tree_view,
1443 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1444 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1445 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1446 gtk_tree_path_free (dest_path);
1450 gtk_tree_path_free (dest_path);
1455 GDK_THREADS_LEAVE ();
1461 * This function is called whenever the pointer is moved over a widget
1462 * while dragging some data. It installs a timeout that will expand a
1463 * node of the treeview if not expanded yet. This function also calls
1464 * gdk_drag_status in order to set the suggested action that will be
1465 * used by the "drag-data-received" signal handler to know if we
1466 * should do a move or just a copy of the data.
1469 on_drag_motion (GtkWidget *widget,
1470 GdkDragContext *context,
1476 GtkTreeViewDropPosition pos;
1477 GtkTreePath *dest_row;
1478 ModestFolderViewPrivate *priv;
1479 GdkDragAction suggested_action;
1480 gboolean valid_location = FALSE;
1482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1484 if (priv->timer_expander != 0) {
1485 g_source_remove (priv->timer_expander);
1486 priv->timer_expander = 0;
1489 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1494 /* Do not allow drops between folders */
1496 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1497 pos == GTK_TREE_VIEW_DROP_AFTER) {
1498 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1499 gdk_drag_status(context, 0, time);
1500 valid_location = FALSE;
1503 valid_location = TRUE;
1506 /* Expand the selected row after 1/2 second */
1507 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1508 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1509 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1512 /* Select the desired action. By default we pick MOVE */
1513 suggested_action = GDK_ACTION_MOVE;
1515 if (context->actions == GDK_ACTION_COPY)
1516 gdk_drag_status(context, GDK_ACTION_COPY, time);
1517 else if (context->actions == GDK_ACTION_MOVE)
1518 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1519 else if (context->actions & suggested_action)
1520 gdk_drag_status(context, suggested_action, time);
1522 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1526 gtk_tree_path_free (dest_row);
1527 g_signal_stop_emission_by_name (widget, "drag-motion");
1528 return valid_location;
1532 /* Folder view drag types */
1533 const GtkTargetEntry folder_view_drag_types[] =
1535 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1536 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1540 * This function sets the treeview as a source and a target for dnd
1541 * events. It also connects all the requirede signals.
1544 setup_drag_and_drop (GtkTreeView *self)
1546 /* Set up the folder view as a dnd destination. Set only the
1547 highlight flag, otherwise gtk will have a different
1549 gtk_drag_dest_set (GTK_WIDGET (self),
1550 GTK_DEST_DEFAULT_HIGHLIGHT,
1551 folder_view_drag_types,
1552 G_N_ELEMENTS (folder_view_drag_types),
1553 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1555 g_signal_connect (G_OBJECT (self),
1556 "drag_data_received",
1557 G_CALLBACK (on_drag_data_received),
1561 /* Set up the treeview as a dnd source */
1562 gtk_drag_source_set (GTK_WIDGET (self),
1564 folder_view_drag_types,
1565 G_N_ELEMENTS (folder_view_drag_types),
1566 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1568 g_signal_connect (G_OBJECT (self),
1570 G_CALLBACK (on_drag_motion),
1573 g_signal_connect (G_OBJECT (self),
1575 G_CALLBACK (on_drag_data_get),
1578 g_signal_connect (G_OBJECT (self),
1580 G_CALLBACK (drag_drop_cb),
1585 * This function manages the navigation through the folders using the
1586 * keyboard or the hardware keys in the device
1589 on_key_pressed (GtkWidget *self,
1593 GtkTreeSelection *selection;
1595 GtkTreeModel *model;
1596 gboolean retval = FALSE;
1598 /* Up and Down are automatically managed by the treeview */
1599 if (event->keyval == GDK_Return) {
1600 /* Expand/Collapse the selected row */
1601 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1602 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1605 path = gtk_tree_model_get_path (model, &iter);
1607 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1608 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1610 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1611 gtk_tree_path_free (path);
1613 /* No further processing */
1621 * We listen to the changes in the local folder account name key,
1622 * because we want to show the right name in the view. The local
1623 * folder account name corresponds to the device name in the Maemo
1624 * version. We do this because we do not want to query gconf on each
1625 * tree view refresh. It's better to cache it and change whenever
1629 on_configuration_key_changed (ModestConf* conf,
1631 ModestConfEvent event,
1632 ModestFolderView *self)
1634 ModestFolderViewPrivate *priv;
1639 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1640 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1642 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1643 g_free (priv->local_account_name);
1645 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1646 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1648 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1649 MODEST_CONF_DEVICE_NAME, NULL);
1651 /* Force a redraw */
1652 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1653 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1654 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1655 gtk_tree_view_column_queue_resize (tree_column);
1661 modest_folder_view_set_style (ModestFolderView *self,
1662 ModestFolderViewStyle style)
1664 ModestFolderViewPrivate *priv;
1666 g_return_if_fail (self);
1668 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1670 priv->style = style;
1674 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1675 const gchar *account_id)
1677 ModestFolderViewPrivate *priv;
1679 GtkTreeModel *model;
1681 g_return_if_fail (self);
1683 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1685 /* This will be used by the filter_row callback,
1686 * to decided which rows to show: */
1687 if (priv->visible_account_id)
1688 g_free (priv->visible_account_id);
1689 priv->visible_account_id = g_strdup (account_id);
1691 /* Save preferences */
1692 conf = modest_runtime_get_conf ();
1693 modest_widget_memory_save (conf, G_OBJECT (self), MODEST_CONF_FOLDER_VIEW_KEY);
1696 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1697 if (GTK_IS_TREE_MODEL_FILTER (model))
1698 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1702 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1704 ModestFolderViewPrivate *priv;
1706 g_return_val_if_fail (self, NULL);
1708 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1710 return (const gchar *) priv->visible_account_id;