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>
57 #include <modest-ui-actions.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 TnyFolderStoreQuery *query;
144 guint timer_expander;
146 gchar *local_account_name;
147 gchar *visible_account_id;
148 ModestFolderViewStyle style;
150 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
151 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
152 MODEST_TYPE_FOLDER_VIEW, \
153 ModestFolderViewPrivate))
155 static GObjectClass *parent_class = NULL;
157 static guint signals[LAST_SIGNAL] = {0};
160 modest_folder_view_get_type (void)
162 static GType my_type = 0;
164 static const GTypeInfo my_info = {
165 sizeof(ModestFolderViewClass),
166 NULL, /* base init */
167 NULL, /* base finalize */
168 (GClassInitFunc) modest_folder_view_class_init,
169 NULL, /* class finalize */
170 NULL, /* class data */
171 sizeof(ModestFolderView),
173 (GInstanceInitFunc) modest_folder_view_init,
177 static const GInterfaceInfo tny_account_store_view_info = {
178 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
179 NULL, /* interface_finalize */
180 NULL /* interface_data */
184 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
188 g_type_add_interface_static (my_type,
189 TNY_TYPE_ACCOUNT_STORE_VIEW,
190 &tny_account_store_view_info);
196 modest_folder_view_class_init (ModestFolderViewClass *klass)
198 GObjectClass *gobject_class;
199 gobject_class = (GObjectClass*) klass;
201 parent_class = g_type_class_peek_parent (klass);
202 gobject_class->finalize = modest_folder_view_finalize;
204 g_type_class_add_private (gobject_class,
205 sizeof(ModestFolderViewPrivate));
207 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
208 g_signal_new ("folder_selection_changed",
209 G_TYPE_FROM_CLASS (gobject_class),
211 G_STRUCT_OFFSET (ModestFolderViewClass,
212 folder_selection_changed),
214 modest_marshal_VOID__POINTER_BOOLEAN,
215 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
218 * This signal is emitted whenever the currently selected
219 * folder display name is computed. Note that the name could
220 * be different to the folder name, because we could append
221 * the unread messages count to the folder name to build the
222 * folder display name
224 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
225 g_signal_new ("folder-display-name-changed",
226 G_TYPE_FROM_CLASS (gobject_class),
228 G_STRUCT_OFFSET (ModestFolderViewClass,
229 folder_display_name_changed),
231 g_cclosure_marshal_VOID__STRING,
232 G_TYPE_NONE, 1, G_TYPE_STRING);
236 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
237 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
239 ModestFolderViewPrivate *priv;
244 GObject *instance = NULL;
246 g_return_if_fail (column);
247 g_return_if_fail (tree_model);
249 gtk_tree_model_get (tree_model, iter,
250 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
251 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
252 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
253 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
254 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
256 rendobj = G_OBJECT(renderer);
267 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
269 gchar *item_name = NULL;
270 gint item_weight = 400;
272 if (type != TNY_FOLDER_TYPE_ROOT) {
275 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
277 type = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
278 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
280 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
284 /* Select the number to show */
285 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
290 /* Use bold font style if there are unread messages */
292 item_name = g_strdup_printf ("%s (%d)", fname, unread);
295 item_name = g_strdup (fname);
299 } else if (TNY_IS_ACCOUNT (instance)) {
300 /* If it's a server account */
301 const gchar * account_id = tny_account_get_id (TNY_ACCOUNT (instance));
302 if (!strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
303 item_name = g_strdup (priv->local_account_name);
305 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
306 /* TODO: get MMC card name */
307 item_name = g_strdup (_("MMC"));
309 item_name = g_strdup (fname);
314 } else if (modest_tny_folder_store_is_virtual_local_folders (
315 TNY_FOLDER_STORE(instance)))
317 /* We use ModestTnySimpleFolder store to group the outboxes and
318 * the other local folders together: */
319 item_name = g_strdup (_("Local Folders"));
324 item_name = g_strdup ("unknown");
326 if (item_name && item_weight) {
327 /* Set the name in the treeview cell: */
328 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
330 /* Notify display name observers */
331 if (G_OBJECT (priv->cur_folder_store) == instance) {
332 g_signal_emit (G_OBJECT(data),
333 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
341 g_object_unref (G_OBJECT (instance));
348 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
349 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
351 GObject *rendobj = NULL, *instance = NULL;
352 GdkPixbuf *pixbuf = NULL;
355 const gchar *account_id = NULL;
358 rendobj = G_OBJECT(renderer);
359 gtk_tree_model_get (tree_model, iter,
360 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
361 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
362 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
363 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
374 if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
375 type = modest_tny_folder_guess_folder_type_from_name (fname);
379 case TNY_FOLDER_TYPE_ROOT:
380 if (TNY_IS_ACCOUNT (instance)) {
381 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
383 if (!strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
384 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
387 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
388 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
390 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
395 else if (modest_tny_folder_store_is_virtual_local_folders (
396 TNY_FOLDER_STORE (instance))) {
397 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
400 case TNY_FOLDER_TYPE_INBOX:
401 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
403 case TNY_FOLDER_TYPE_OUTBOX:
404 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
406 case TNY_FOLDER_TYPE_JUNK:
407 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
409 case TNY_FOLDER_TYPE_SENT:
410 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
412 case TNY_FOLDER_TYPE_TRASH:
413 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
415 case TNY_FOLDER_TYPE_DRAFTS:
416 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
418 case TNY_FOLDER_TYPE_NORMAL:
420 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
424 g_object_unref (G_OBJECT (instance));
428 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
431 g_object_unref (pixbuf);
435 add_columns (GtkWidget *treeview)
437 GtkTreeViewColumn *column;
438 GtkCellRenderer *renderer;
439 GtkTreeSelection *sel;
442 column = gtk_tree_view_column_new ();
444 /* Set icon and text render function */
445 renderer = gtk_cell_renderer_pixbuf_new();
446 gtk_tree_view_column_pack_start (column, renderer, FALSE);
447 gtk_tree_view_column_set_cell_data_func(column, renderer,
448 icon_cell_data, treeview, NULL);
450 renderer = gtk_cell_renderer_text_new();
451 gtk_tree_view_column_pack_start (column, renderer, FALSE);
452 gtk_tree_view_column_set_cell_data_func(column, renderer,
453 text_cell_data, treeview, NULL);
455 /* Set selection mode */
456 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
457 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
459 /* Set treeview appearance */
460 gtk_tree_view_column_set_spacing (column, 2);
461 gtk_tree_view_column_set_resizable (column, TRUE);
462 gtk_tree_view_column_set_fixed_width (column, TRUE);
463 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
464 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
467 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
471 modest_folder_view_init (ModestFolderView *obj)
473 ModestFolderViewPrivate *priv;
476 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
478 priv->timer_expander = 0;
479 priv->account_store = NULL;
481 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
482 priv->cur_folder_store = NULL;
483 priv->visible_account_id = NULL;
485 /* Initialize the local account name */
486 conf = modest_runtime_get_conf();
487 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
490 add_columns (GTK_WIDGET (obj));
492 /* Setup drag and drop */
493 setup_drag_and_drop (GTK_TREE_VIEW(obj));
495 /* Connect signals */
496 g_signal_connect (G_OBJECT (obj),
498 G_CALLBACK (on_key_pressed), NULL);
501 * Track changes in the local account name (in the device it
502 * will be the device name)
504 g_signal_connect (G_OBJECT(conf),
506 G_CALLBACK(on_configuration_key_changed), obj);
511 tny_account_store_view_init (gpointer g, gpointer iface_data)
513 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
515 klass->set_account_store_func = modest_folder_view_set_account_store;
521 modest_folder_view_finalize (GObject *obj)
523 ModestFolderViewPrivate *priv;
524 GtkTreeSelection *sel;
526 g_return_if_fail (obj);
528 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
530 if (priv->timer_expander != 0) {
531 g_source_remove (priv->timer_expander);
532 priv->timer_expander = 0;
535 if (priv->account_store) {
536 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
537 priv->account_update_signal);
538 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
539 priv->accounts_reloaded_signal);
540 g_object_unref (G_OBJECT(priv->account_store));
541 priv->account_store = NULL;
545 g_object_unref (G_OBJECT (priv->query));
549 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
551 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
553 g_free (priv->local_account_name);
554 g_free (priv->visible_account_id);
556 G_OBJECT_CLASS(parent_class)->finalize (obj);
561 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
563 ModestFolderViewPrivate *priv;
566 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
567 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
569 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
570 device = tny_account_store_get_device (account_store);
572 if (G_UNLIKELY (priv->account_store)) {
574 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
575 priv->account_update_signal))
576 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
577 priv->account_update_signal);
578 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
579 priv->accounts_reloaded_signal))
580 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
581 priv->accounts_reloaded_signal);
583 g_object_unref (G_OBJECT (priv->account_store));
586 priv->account_store = g_object_ref (G_OBJECT (account_store));
588 priv->account_update_signal =
589 g_signal_connect (G_OBJECT(account_store), "account_update",
590 G_CALLBACK (on_account_update), self);
592 priv->accounts_reloaded_signal =
593 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
594 G_CALLBACK (on_accounts_reloaded), self);
596 g_object_unref (G_OBJECT (device));
600 on_account_update (TnyAccountStore *account_store, const gchar *account,
603 if (!update_model (MODEST_FOLDER_VIEW(user_data),
604 MODEST_TNY_ACCOUNT_STORE(account_store)))
605 g_printerr ("modest: failed to update model for changes in '%s'",
610 on_accounts_reloaded (TnyAccountStore *account_store,
613 ModestConf *conf = modest_runtime_get_conf ();
615 modest_widget_memory_save (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
616 update_model (MODEST_FOLDER_VIEW (user_data),
617 MODEST_TNY_ACCOUNT_STORE(account_store));
618 modest_widget_memory_restore (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
622 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
624 GtkTreeViewColumn *col;
626 g_return_if_fail (self);
628 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
630 g_printerr ("modest: failed get column for title\n");
634 gtk_tree_view_column_set_title (col, title);
635 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
640 modest_folder_view_new (TnyFolderStoreQuery *query)
643 ModestFolderViewPrivate *priv;
644 GtkTreeSelection *sel;
646 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
647 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
650 priv->query = g_object_ref (query);
652 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
653 priv->changed_signal = g_signal_connect (sel, "changed",
654 G_CALLBACK (on_selection_changed), self);
656 return GTK_WIDGET(self);
659 /* this feels dirty; any other way to expand all the root items? */
661 expand_root_items (ModestFolderView *self)
664 path = gtk_tree_path_new_first ();
666 /* all folders should have child items, so.. */
667 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
668 gtk_tree_path_next (path);
670 gtk_tree_path_free (path);
674 * We use this function to implement the
675 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
676 * account in this case, and the local folders.
679 filter_row (GtkTreeModel *model,
683 gboolean retval = TRUE;
685 GObject *instance = NULL;
687 gtk_tree_model_get (model, iter,
688 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
689 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
692 /* Do not show if there is no instance, this could indeed
693 happen when the model is being modified while it's being
694 drawn. This could occur for example when moving folders
699 if (type == TNY_FOLDER_TYPE_ROOT) {
700 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
701 if (TNY_IS_ACCOUNT (instance)) {
702 TnyAccount *acc = TNY_ACCOUNT (instance);
703 const gchar *account_id = tny_account_get_id (acc);
705 /* If it isn't a special folder,
706 * don't show it unless it is the visible account: */
707 if (strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
708 /* Show only the visible account id */
709 ModestFolderViewPrivate *priv =
710 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
711 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
717 /* The virtual local-folders folder store is also shown by default. */
719 g_object_unref (instance);
725 static void on_tnylist_accounts_debug_print(gpointer data, gpointer user_data)
727 TnyAccount* account = TNY_ACCOUNT(data);
728 const gchar *prefix = (const gchar*)(user_data);
730 printf("%s account id=%s\n", prefix, tny_account_get_id (account));
735 add_account_folders_to_merged_folder (TnyAccount *account, TnyMergeFolder* merge_folder)
737 const gchar* account_id = tny_account_get_id (account);
738 const gboolean is_actual_local_folders_account = account_id &&
739 (strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID) == 0);
741 TnyList *list_outbox_folders = tny_simple_list_new ();
742 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
743 list_outbox_folders, NULL, NULL);
745 TnyIterator* iter = tny_list_create_iterator (list_outbox_folders);
746 while (!tny_iterator_is_done (iter))
748 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (iter));
752 /* TODO: Do not add outboxes that are inside local-folders/,
753 * because these are just left-over from earlier Modest versions
754 * that put the outbox there: */
755 if (is_actual_local_folders_account) {
756 const TnyFolderType type = modest_tny_folder_get_local_folder_type (folder);
757 if (type == TNY_FOLDER_TYPE_OUTBOX) {
763 tny_merge_folder_add_folder (merge_folder, folder);
765 g_object_unref (folder);
768 tny_iterator_next (iter);
771 g_object_unref (list_outbox_folders);
776 add_account_folders_to_simple_folder_store (TnyAccount *account, ModestTnySimpleFolderStore* store)
778 g_return_if_fail (account);
779 g_return_if_fail (store);
781 TnyList *list_outbox_folders = tny_simple_list_new ();
782 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
783 list_outbox_folders, NULL, NULL);
785 /* Special handling for the .modest/local-folders account,
786 * to avoid adding unwanted folders.
787 * We cannot prevent them from being in the TnyAccount without
788 * changing the libtinymail-camel. */
789 const gchar* account_id = tny_account_get_id (account);
790 const gboolean is_actual_local_folders_account = account_id &&
791 (strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID) == 0);
793 TnyIterator* iter = tny_list_create_iterator (list_outbox_folders);
794 while (!tny_iterator_is_done (iter))
796 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (iter));
800 /* TODO: Do not add outboxes that are inside local-folders/,
801 * because these are just left-over from earlier Modest versions
802 * that put the outbox there: */
803 if (is_actual_local_folders_account) {
804 const TnyFolderType type = modest_tny_folder_get_local_folder_type (folder);
805 if (type == TNY_FOLDER_TYPE_OUTBOX) {
811 modest_tny_simple_folder_store_add_folder (store, folder);
813 g_object_unref (folder);
816 tny_iterator_next (iter);
819 g_object_unref (list_outbox_folders);
823 update_model (ModestFolderView *self, ModestTnyAccountStore *account_store)
825 ModestFolderViewPrivate *priv;
828 g_return_val_if_fail (account_store, FALSE);
830 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
832 /* Notify that there is no folder selected */
833 g_signal_emit (G_OBJECT(self),
834 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
837 /* FIXME: the local accounts are not shown when the query
838 selects only the subscribed folders. */
839 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
840 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
842 /* Deal with the model via its TnyList Interface,
843 * filling the TnyList via a get_accounts() call: */
844 TnyList *model_as_list = TNY_LIST(model);
846 /* Create a virtual local-folders folder store,
847 * containing the real local folders and the (merged) various per-account
850 ModestTnySimpleFolderStore *store = modest_tny_simple_folder_store_new ();
852 /* Get the accounts: */
853 TnyList *account_list = tny_simple_list_new ();
854 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
856 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
857 TnyIterator* iter = tny_list_create_iterator (account_list);
859 /* All per-account outbox folders are merged into one folders
860 * so that they appear as one outbox to the user: */
861 TnyMergeFolder *merged_outbox = TNY_MERGE_FOLDER (tny_merge_folder_new());
863 while (!tny_iterator_is_done (iter))
865 GObject *cur = tny_iterator_get_current (iter);
866 TnyAccount *account = TNY_ACCOUNT (cur);
868 /* Add both outbox account and local-folders account folders
869 * to our one combined account:
871 if (MODEST_IS_TNY_OUTBOX_ACCOUNT (account)) {
872 /* Add the folder to the merged folder.
873 * We will add it later to the virtual local-folders store: */
874 add_account_folders_to_merged_folder (account, merged_outbox);
876 const gchar *account_id = tny_account_get_id (account);
877 if (account_id && !strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
878 /* Add the folders to the virtual local-folders store: */
879 add_account_folders_to_simple_folder_store (account, store);
882 /* Just add the account: */
883 tny_list_append (model_as_list, G_OBJECT (account));
888 g_object_unref (cur);
889 tny_iterator_next (iter);
892 /* Add the merged outbox folder to the virtual local-folders store: */
893 modest_tny_simple_folder_store_add_folder (store, TNY_FOLDER(merged_outbox));
894 g_object_unref (merged_outbox);
895 merged_outbox = NULL;
897 /* Add the virtual local-folders store to the model: */
898 tny_list_append (model_as_list, G_OBJECT (store));
901 g_object_unref (account_list);
904 g_object_unref (model_as_list);
905 model_as_list = NULL;
907 /* tny_list_foreach (account_list, on_tnylist_accounts_debug_print, "update_model: "); */
909 GtkTreeModel *filter_model = NULL, *sortable = NULL;
911 sortable = gtk_tree_model_sort_new_with_model (model);
912 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
913 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
915 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
916 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
917 cmp_rows, NULL, NULL);
919 /* Create filter model */
920 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
921 filter_model = gtk_tree_model_filter_new (sortable, NULL);
922 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
929 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
930 (filter_model) ? filter_model : sortable);
931 expand_root_items (self); /* expand all account folders */
938 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
941 TnyFolderStore *folder = NULL;
943 ModestFolderView *tree_view;
944 ModestFolderViewPrivate *priv;
947 g_return_if_fail (sel);
948 g_return_if_fail (user_data);
950 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
952 /* folder was _un_selected if true */
953 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
954 if (priv->cur_folder_store)
955 g_object_unref (priv->cur_folder_store);
956 priv->cur_folder_store = NULL;
958 /* Notify the display name observers */
959 g_signal_emit (G_OBJECT(user_data),
960 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
965 tree_view = MODEST_FOLDER_VIEW (user_data);
967 gtk_tree_model_get (model, &iter,
968 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
969 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
972 /* If the folder is the same do not notify */
973 if (priv->cur_folder_store == folder) {
974 g_object_unref (folder);
978 /* Current folder was unselected */
979 if (priv->cur_folder_store) {
980 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
981 priv->cur_folder_store, FALSE);
982 g_object_unref (priv->cur_folder_store);
985 /* New current references */
986 priv->cur_folder_store = folder;
988 /* New folder has been selected */
989 g_signal_emit (G_OBJECT(tree_view),
990 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
995 modest_folder_view_get_selected (ModestFolderView *self)
997 ModestFolderViewPrivate *priv;
999 g_return_val_if_fail (self, NULL);
1001 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1002 if (priv->cur_folder_store)
1003 g_object_ref (priv->cur_folder_store);
1005 return priv->cur_folder_store;
1009 get_cmp_rows_type_pos (GObject *folder)
1011 /* Remote accounts -> Local account -> MMC account .*/
1014 if (TNY_IS_FOLDER_STORE (folder) &&
1015 modest_tny_folder_store_is_virtual_local_folders (
1016 TNY_FOLDER_STORE (folder))) {
1018 } else if (TNY_IS_ACCOUNT (folder)) {
1019 TnyAccount *account = TNY_ACCOUNT (folder);
1020 const gchar *account_id = tny_account_get_id (account);
1021 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1027 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1028 return -1; /* Should never happen */
1033 * This function orders the mail accounts according to these rules:
1034 * 1st - remote accounts
1035 * 2nd - local account
1039 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1043 gchar *name1, *name2;
1045 GObject *folder1 = NULL;
1046 GObject *folder2 = NULL;
1048 gtk_tree_model_get (tree_model, iter1,
1049 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1050 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1051 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1053 gtk_tree_model_get (tree_model, iter2,
1054 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1055 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1058 if (type == TNY_FOLDER_TYPE_ROOT) {
1059 /* Compare the types, so that
1060 * Remote accounts -> Local account -> MMC account .*/
1061 const gint pos1 = get_cmp_rows_type_pos (folder1);
1062 const gint pos2 = get_cmp_rows_type_pos (folder2);
1063 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1064 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1067 else if (pos1 > pos2)
1070 /* Compare items of the same type: */
1072 TnyAccount *account1 = NULL;
1073 if (TNY_IS_ACCOUNT (folder1))
1074 account1 = TNY_ACCOUNT (folder1);
1076 TnyAccount *account2 = NULL;
1077 if (TNY_IS_ACCOUNT (folder2))
1078 account2 = TNY_ACCOUNT (folder2);
1080 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1081 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1083 if (!account_id && !account_id2)
1085 else if (!account_id)
1087 else if (!account_id2)
1089 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1092 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1095 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1099 g_object_unref(G_OBJECT(folder1));
1101 g_object_unref(G_OBJECT(folder2));
1109 /*****************************************************************************/
1110 /* DRAG and DROP stuff */
1111 /*****************************************************************************/
1114 * This function fills the #GtkSelectionData with the row and the
1115 * model that has been dragged. It's called when this widget is a
1116 * source for dnd after the event drop happened
1119 on_drag_data_get (GtkWidget *widget,
1120 GdkDragContext *context,
1121 GtkSelectionData *selection_data,
1126 GtkTreeSelection *selection;
1127 GtkTreeModel *model;
1129 GtkTreePath *source_row;
1131 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1132 gtk_tree_selection_get_selected (selection, &model, &iter);
1133 source_row = gtk_tree_model_get_path (model, &iter);
1135 gtk_tree_set_row_drag_data (selection_data,
1139 gtk_tree_path_free (source_row);
1142 typedef struct _DndHelper {
1143 gboolean delete_source;
1144 GtkTreePath *source_row;
1145 GdkDragContext *context;
1151 * This function is the callback of the
1152 * modest_mail_operation_xfer_msgs () and
1153 * modest_mail_operation_xfer_folder() calls. We check here if the
1154 * message/folder was correctly asynchronously transferred. The reason
1155 * to use the same callback is that the code is the same, it only has
1156 * to check that the operation went fine and then finalize the drag
1160 on_progress_changed (ModestMailOperation *mail_op,
1161 ModestMailOperationState *state,
1167 helper = (DndHelper *) user_data;
1169 if (!state->finished)
1172 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1178 /* Notify the drag source. Never call delete, the monitor will
1179 do the job if needed */
1180 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1182 /* Free the helper */
1183 gtk_tree_path_free (helper->source_row);
1184 g_slice_free (DndHelper, helper);
1188 * This function is used by drag_data_received_cb to manage drag and
1189 * drop of a header, i.e, and drag from the header view to the folder
1193 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1194 GtkTreeModel *dest_model,
1195 GtkTreePath *dest_row,
1201 ModestMailOperation *mail_op;
1202 GtkTreeIter source_iter, dest_iter;
1205 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1206 gtk_tree_model_get (source_model, &source_iter,
1207 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1211 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1212 gtk_tree_model_get (dest_model, &dest_iter,
1213 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1216 /* Transfer message */
1217 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1219 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1221 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1222 G_CALLBACK (on_progress_changed), helper);
1224 /* FIXME: I replaced this because the API changed, but D&D
1225 should be reviewed in order to allow multiple drags*/
1226 headers = tny_simple_list_new ();
1227 tny_list_append (headers, G_OBJECT (header));
1228 modest_mail_operation_xfer_msgs (mail_op, headers, folder, helper->delete_source, NULL, NULL);
1231 g_object_unref (G_OBJECT (mail_op));
1232 g_object_unref (G_OBJECT (header));
1233 g_object_unref (G_OBJECT (folder));
1234 g_object_unref (headers);
1238 * This function is used by drag_data_received_cb to manage drag and
1239 * drop of a folder, i.e, and drag from the folder view to the same
1243 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1244 GtkTreeModel *dest_model,
1245 GtkTreePath *dest_row,
1246 GtkSelectionData *selection_data,
1249 ModestMailOperation *mail_op;
1250 GtkTreeIter parent_iter, iter;
1251 TnyFolderStore *parent_folder;
1254 /* Check if the drag is possible */
1255 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1256 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1258 /* selection_data)) { */
1259 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1261 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1262 gtk_tree_path_free (helper->source_row);
1263 g_slice_free (DndHelper, helper);
1268 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1269 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1270 gtk_tree_model_get (source_model, &parent_iter,
1271 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1272 &parent_folder, -1);
1273 gtk_tree_model_get (source_model, &iter,
1274 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1277 /* Do the mail operation */
1278 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1280 modest_ui_actions_move_folder_error_handler,
1282 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1284 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1285 G_CALLBACK (on_progress_changed), helper);
1287 modest_mail_operation_xfer_folder (mail_op,
1290 helper->delete_source);
1293 g_object_unref (G_OBJECT (parent_folder));
1294 g_object_unref (G_OBJECT (folder));
1295 g_object_unref (G_OBJECT (mail_op));
1299 * This function receives the data set by the "drag-data-get" signal
1300 * handler. This information comes within the #GtkSelectionData. This
1301 * function will manage both the drags of folders of the treeview and
1302 * drags of headers of the header view widget.
1305 on_drag_data_received (GtkWidget *widget,
1306 GdkDragContext *context,
1309 GtkSelectionData *selection_data,
1314 GtkWidget *source_widget;
1315 GtkTreeModel *dest_model, *source_model;
1316 GtkTreePath *source_row, *dest_row;
1317 GtkTreeViewDropPosition pos;
1318 gboolean success = FALSE, delete_source = FALSE;
1319 DndHelper *helper = NULL;
1321 /* Do not allow further process */
1322 g_signal_stop_emission_by_name (widget, "drag-data-received");
1323 source_widget = gtk_drag_get_source_widget (context);
1325 /* Get the action */
1326 if (context->action == GDK_ACTION_MOVE) {
1327 delete_source = TRUE;
1329 /* Notify that there is no folder selected. We need to
1330 do this in order to update the headers view (and
1331 its monitors, because when moving, the old folder
1332 won't longer exist. We can not wait for the end of
1333 the operation, because the operation won't start if
1334 the folder is in use */
1335 if (source_widget == widget)
1336 g_signal_emit (G_OBJECT (widget),
1337 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1340 /* Check if the get_data failed */
1341 if (selection_data == NULL || selection_data->length < 0)
1342 gtk_drag_finish (context, success, FALSE, time);
1344 /* Get the models */
1345 gtk_tree_get_row_drag_data (selection_data,
1349 /* Select the destination model */
1350 if (source_widget == widget) {
1351 dest_model = source_model;
1353 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1356 /* Get the path to the destination row. Can not call
1357 gtk_tree_view_get_drag_dest_row() because the source row
1358 is not selected anymore */
1359 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1362 /* Only allow drops IN other rows */
1363 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1364 gtk_drag_finish (context, success, FALSE, time);
1366 /* Create the helper */
1367 helper = g_slice_new0 (DndHelper);
1368 helper->delete_source = delete_source;
1369 helper->source_row = gtk_tree_path_copy (source_row);
1370 helper->context = context;
1371 helper->time = time;
1373 /* Drags from the header view */
1374 if (source_widget != widget) {
1376 drag_and_drop_from_header_view (source_model,
1383 drag_and_drop_from_folder_view (source_model,
1391 gtk_tree_path_free (source_row);
1392 gtk_tree_path_free (dest_row);
1396 * We define a "drag-drop" signal handler because we do not want to
1397 * use the default one, because the default one always calls
1398 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1399 * signal handler, because there we have all the information available
1400 * to know if the dnd was a success or not.
1403 drag_drop_cb (GtkWidget *widget,
1404 GdkDragContext *context,
1412 if (!context->targets)
1415 /* Check if we're dragging a folder row */
1416 target = gtk_drag_dest_find_target (widget, context, NULL);
1418 /* Request the data from the source. */
1419 gtk_drag_get_data(widget, context, target, time);
1425 * This function expands a node of a tree view if it's not expanded
1426 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1427 * does that, so that's why they're here.
1430 expand_row_timeout (gpointer data)
1432 GtkTreeView *tree_view = data;
1433 GtkTreePath *dest_path = NULL;
1434 GtkTreeViewDropPosition pos;
1435 gboolean result = FALSE;
1437 GDK_THREADS_ENTER ();
1439 gtk_tree_view_get_drag_dest_row (tree_view,
1444 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1445 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1446 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1447 gtk_tree_path_free (dest_path);
1451 gtk_tree_path_free (dest_path);
1456 GDK_THREADS_LEAVE ();
1462 * This function is called whenever the pointer is moved over a widget
1463 * while dragging some data. It installs a timeout that will expand a
1464 * node of the treeview if not expanded yet. This function also calls
1465 * gdk_drag_status in order to set the suggested action that will be
1466 * used by the "drag-data-received" signal handler to know if we
1467 * should do a move or just a copy of the data.
1470 on_drag_motion (GtkWidget *widget,
1471 GdkDragContext *context,
1477 GtkTreeViewDropPosition pos;
1478 GtkTreePath *dest_row;
1479 ModestFolderViewPrivate *priv;
1480 GdkDragAction suggested_action;
1481 gboolean valid_location = FALSE;
1483 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1485 if (priv->timer_expander != 0) {
1486 g_source_remove (priv->timer_expander);
1487 priv->timer_expander = 0;
1490 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1495 /* Do not allow drops between folders */
1497 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1498 pos == GTK_TREE_VIEW_DROP_AFTER) {
1499 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1500 gdk_drag_status(context, 0, time);
1501 valid_location = FALSE;
1504 valid_location = TRUE;
1507 /* Expand the selected row after 1/2 second */
1508 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1509 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1510 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1513 /* Select the desired action. By default we pick MOVE */
1514 suggested_action = GDK_ACTION_MOVE;
1516 if (context->actions == GDK_ACTION_COPY)
1517 gdk_drag_status(context, GDK_ACTION_COPY, time);
1518 else if (context->actions == GDK_ACTION_MOVE)
1519 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1520 else if (context->actions & suggested_action)
1521 gdk_drag_status(context, suggested_action, time);
1523 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1527 gtk_tree_path_free (dest_row);
1528 g_signal_stop_emission_by_name (widget, "drag-motion");
1529 return valid_location;
1533 /* Folder view drag types */
1534 const GtkTargetEntry folder_view_drag_types[] =
1536 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1537 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1541 * This function sets the treeview as a source and a target for dnd
1542 * events. It also connects all the requirede signals.
1545 setup_drag_and_drop (GtkTreeView *self)
1547 /* Set up the folder view as a dnd destination. Set only the
1548 highlight flag, otherwise gtk will have a different
1550 gtk_drag_dest_set (GTK_WIDGET (self),
1551 GTK_DEST_DEFAULT_HIGHLIGHT,
1552 folder_view_drag_types,
1553 G_N_ELEMENTS (folder_view_drag_types),
1554 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1556 g_signal_connect (G_OBJECT (self),
1557 "drag_data_received",
1558 G_CALLBACK (on_drag_data_received),
1562 /* Set up the treeview as a dnd source */
1563 gtk_drag_source_set (GTK_WIDGET (self),
1565 folder_view_drag_types,
1566 G_N_ELEMENTS (folder_view_drag_types),
1567 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1569 g_signal_connect (G_OBJECT (self),
1571 G_CALLBACK (on_drag_motion),
1574 g_signal_connect (G_OBJECT (self),
1576 G_CALLBACK (on_drag_data_get),
1579 g_signal_connect (G_OBJECT (self),
1581 G_CALLBACK (drag_drop_cb),
1586 * This function manages the navigation through the folders using the
1587 * keyboard or the hardware keys in the device
1590 on_key_pressed (GtkWidget *self,
1594 GtkTreeSelection *selection;
1596 GtkTreeModel *model;
1597 gboolean retval = FALSE;
1599 /* Up and Down are automatically managed by the treeview */
1600 if (event->keyval == GDK_Return) {
1601 /* Expand/Collapse the selected row */
1602 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1603 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1606 path = gtk_tree_model_get_path (model, &iter);
1608 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1609 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1611 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1612 gtk_tree_path_free (path);
1614 /* No further processing */
1622 * We listen to the changes in the local folder account name key,
1623 * because we want to show the right name in the view. The local
1624 * folder account name corresponds to the device name in the Maemo
1625 * version. We do this because we do not want to query gconf on each
1626 * tree view refresh. It's better to cache it and change whenever
1630 on_configuration_key_changed (ModestConf* conf,
1632 ModestConfEvent event,
1633 ModestFolderView *self)
1635 ModestFolderViewPrivate *priv;
1640 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1641 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1643 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1644 g_free (priv->local_account_name);
1646 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1647 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1649 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1650 MODEST_CONF_DEVICE_NAME, NULL);
1652 /* Force a redraw */
1653 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1654 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1655 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1656 gtk_tree_view_column_queue_resize (tree_column);
1662 modest_folder_view_set_style (ModestFolderView *self,
1663 ModestFolderViewStyle style)
1665 ModestFolderViewPrivate *priv;
1667 g_return_if_fail (self);
1669 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1671 priv->style = style;
1675 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1676 const gchar *account_id)
1678 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);
1692 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1693 if (GTK_IS_TREE_MODEL_FILTER (model))
1694 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1698 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1700 ModestFolderViewPrivate *priv;
1702 g_return_val_if_fail (self, NULL);
1704 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1706 return (const gchar *) priv->visible_account_id;