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 item_name = g_strdup (fname);
309 } else if (modest_tny_folder_store_is_virtual_local_folders (
310 TNY_FOLDER_STORE(instance)))
312 /* We use ModestTnySimpleFolder store to group the outboxes and
313 * the other local folders together: */
314 item_name = g_strdup (priv->local_account_name);
319 item_name = g_strdup ("unknown");
321 if (item_name && item_weight) {
322 /* Set the name in the treeview cell: */
323 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
325 /* Notify display name observers */
326 if (G_OBJECT (priv->cur_folder_store) == instance) {
327 g_signal_emit (G_OBJECT(data),
328 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
336 g_object_unref (G_OBJECT (instance));
343 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
344 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
346 GObject *rendobj = NULL, *instance = NULL;
347 GdkPixbuf *pixbuf = NULL;
350 const gchar *account_id = NULL;
353 rendobj = G_OBJECT(renderer);
354 gtk_tree_model_get (tree_model, iter,
355 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
356 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
357 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
358 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
369 if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
370 type = modest_tny_folder_guess_folder_type_from_name (fname);
374 case TNY_FOLDER_TYPE_ROOT:
375 if (TNY_IS_ACCOUNT (instance)) {
376 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
378 if (!strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
379 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
382 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
383 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
385 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
390 else if (modest_tny_folder_store_is_virtual_local_folders (
391 TNY_FOLDER_STORE (instance))) {
392 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
395 case TNY_FOLDER_TYPE_INBOX:
396 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
398 case TNY_FOLDER_TYPE_OUTBOX:
399 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
401 case TNY_FOLDER_TYPE_JUNK:
402 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
404 case TNY_FOLDER_TYPE_SENT:
405 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
407 case TNY_FOLDER_TYPE_TRASH:
408 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
410 case TNY_FOLDER_TYPE_DRAFTS:
411 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
413 case TNY_FOLDER_TYPE_NORMAL:
415 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
419 g_object_unref (G_OBJECT (instance));
423 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
426 g_object_unref (pixbuf);
430 add_columns (GtkWidget *treeview)
432 GtkTreeViewColumn *column;
433 GtkCellRenderer *renderer;
434 GtkTreeSelection *sel;
437 column = gtk_tree_view_column_new ();
439 /* Set icon and text render function */
440 renderer = gtk_cell_renderer_pixbuf_new();
441 gtk_tree_view_column_pack_start (column, renderer, FALSE);
442 gtk_tree_view_column_set_cell_data_func(column, renderer,
443 icon_cell_data, treeview, NULL);
445 renderer = gtk_cell_renderer_text_new();
446 gtk_tree_view_column_pack_start (column, renderer, FALSE);
447 gtk_tree_view_column_set_cell_data_func(column, renderer,
448 text_cell_data, treeview, NULL);
450 /* Set selection mode */
451 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
452 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
454 /* Set treeview appearance */
455 gtk_tree_view_column_set_spacing (column, 2);
456 gtk_tree_view_column_set_resizable (column, TRUE);
457 gtk_tree_view_column_set_fixed_width (column, TRUE);
458 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
459 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
462 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
466 modest_folder_view_init (ModestFolderView *obj)
468 ModestFolderViewPrivate *priv;
471 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
473 priv->timer_expander = 0;
474 priv->account_store = NULL;
476 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
477 priv->cur_folder_store = NULL;
478 priv->visible_account_id = NULL;
480 /* Initialize the local account name */
481 conf = modest_runtime_get_conf();
482 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
485 add_columns (GTK_WIDGET (obj));
487 /* Setup drag and drop */
488 setup_drag_and_drop (GTK_TREE_VIEW(obj));
490 /* Connect signals */
491 g_signal_connect (G_OBJECT (obj),
493 G_CALLBACK (on_key_pressed), NULL);
496 * Track changes in the local account name (in the device it
497 * will be the device name)
499 g_signal_connect (G_OBJECT(conf),
501 G_CALLBACK(on_configuration_key_changed), obj);
506 tny_account_store_view_init (gpointer g, gpointer iface_data)
508 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
510 klass->set_account_store_func = modest_folder_view_set_account_store;
516 modest_folder_view_finalize (GObject *obj)
518 ModestFolderViewPrivate *priv;
519 GtkTreeSelection *sel;
521 g_return_if_fail (obj);
523 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
525 if (priv->timer_expander != 0) {
526 g_source_remove (priv->timer_expander);
527 priv->timer_expander = 0;
530 if (priv->account_store) {
531 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
532 priv->account_update_signal);
533 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
534 priv->accounts_reloaded_signal);
535 g_object_unref (G_OBJECT(priv->account_store));
536 priv->account_store = NULL;
540 g_object_unref (G_OBJECT (priv->query));
544 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
546 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
548 g_free (priv->local_account_name);
549 g_free (priv->visible_account_id);
551 G_OBJECT_CLASS(parent_class)->finalize (obj);
556 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
558 ModestFolderViewPrivate *priv;
561 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
562 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
564 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
565 device = tny_account_store_get_device (account_store);
567 if (G_UNLIKELY (priv->account_store)) {
569 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
570 priv->account_update_signal))
571 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
572 priv->account_update_signal);
573 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
574 priv->accounts_reloaded_signal))
575 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
576 priv->accounts_reloaded_signal);
578 g_object_unref (G_OBJECT (priv->account_store));
581 priv->account_store = g_object_ref (G_OBJECT (account_store));
583 priv->account_update_signal =
584 g_signal_connect (G_OBJECT(account_store), "account_update",
585 G_CALLBACK (on_account_update), self);
587 priv->accounts_reloaded_signal =
588 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
589 G_CALLBACK (on_accounts_reloaded), self);
591 g_object_unref (G_OBJECT (device));
595 on_account_update (TnyAccountStore *account_store, const gchar *account,
598 if (!update_model (MODEST_FOLDER_VIEW(user_data),
599 MODEST_TNY_ACCOUNT_STORE(account_store)))
600 g_printerr ("modest: failed to update model for changes in '%s'",
605 on_accounts_reloaded (TnyAccountStore *account_store,
608 ModestConf *conf = modest_runtime_get_conf ();
610 modest_widget_memory_save (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
611 update_model (MODEST_FOLDER_VIEW (user_data),
612 MODEST_TNY_ACCOUNT_STORE(account_store));
613 modest_widget_memory_restore (conf, G_OBJECT (user_data), MODEST_CONF_FOLDER_VIEW_KEY);
617 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
619 GtkTreeViewColumn *col;
621 g_return_if_fail (self);
623 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
625 g_printerr ("modest: failed get column for title\n");
629 gtk_tree_view_column_set_title (col, title);
630 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
635 modest_folder_view_new (TnyFolderStoreQuery *query)
638 ModestFolderViewPrivate *priv;
639 GtkTreeSelection *sel;
641 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
642 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
645 priv->query = g_object_ref (query);
647 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
648 priv->changed_signal = g_signal_connect (sel, "changed",
649 G_CALLBACK (on_selection_changed), self);
651 return GTK_WIDGET(self);
654 /* this feels dirty; any other way to expand all the root items? */
656 expand_root_items (ModestFolderView *self)
659 path = gtk_tree_path_new_first ();
661 /* all folders should have child items, so.. */
662 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
663 gtk_tree_path_next (path);
665 gtk_tree_path_free (path);
669 * We use this function to implement the
670 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
671 * account in this case, and the local folders.
674 filter_row (GtkTreeModel *model,
678 gboolean retval = TRUE;
680 GObject *instance = NULL;
682 gtk_tree_model_get (model, iter,
683 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
684 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
687 /* Do not show if there is no instance, this could indeed
688 happen when the model is being modified while it's being
689 drawn. This could occur for example when moving folders
694 if (type == TNY_FOLDER_TYPE_ROOT) {
695 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
696 if (TNY_IS_ACCOUNT (instance)) {
697 TnyAccount *acc = TNY_ACCOUNT (instance);
698 const gchar *account_id = tny_account_get_id (acc);
700 /* If it isn't a special folder,
701 * don't show it unless it is the visible account: */
702 if (strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
703 /* Show only the visible account id */
704 ModestFolderViewPrivate *priv =
705 MODEST_FOLDER_VIEW_GET_PRIVATE (data);
706 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
712 /* The virtual local-folders folder store is also shown by default. */
714 g_object_unref (instance);
720 static void on_tnylist_accounts_debug_print(gpointer data, gpointer user_data)
722 TnyAccount* account = TNY_ACCOUNT(data);
723 const gchar *prefix = (const gchar*)(user_data);
725 printf("%s account id=%s\n", prefix, tny_account_get_id (account));
730 add_account_folders_to_merged_folder (TnyAccount *account, TnyMergeFolder* merge_folder)
732 const gchar* account_id = tny_account_get_id (account);
733 const gboolean is_actual_local_folders_account = account_id &&
734 (strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID) == 0);
736 TnyList *list_outbox_folders = tny_simple_list_new ();
737 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
738 list_outbox_folders, NULL, NULL);
740 TnyIterator* iter = tny_list_create_iterator (list_outbox_folders);
741 while (!tny_iterator_is_done (iter))
743 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (iter));
747 /* TODO: Do not add outboxes that are inside local-folders/,
748 * because these are just left-over from earlier Modest versions
749 * that put the outbox there: */
750 if (is_actual_local_folders_account) {
751 const TnyFolderType type = modest_tny_folder_get_local_folder_type (folder);
752 if (type == TNY_FOLDER_TYPE_OUTBOX) {
758 tny_merge_folder_add_folder (merge_folder, folder);
760 g_object_unref (folder);
763 tny_iterator_next (iter);
766 g_object_unref (list_outbox_folders);
771 add_account_folders_to_simple_folder_store (TnyAccount *account, ModestTnySimpleFolderStore* store)
773 g_return_if_fail (account);
774 g_return_if_fail (store);
776 TnyList *list_outbox_folders = tny_simple_list_new ();
777 tny_folder_store_get_folders (TNY_FOLDER_STORE (account),
778 list_outbox_folders, NULL, NULL);
780 /* Special handling for the .modest/local-folders account,
781 * to avoid adding unwanted folders.
782 * We cannot prevent them from being in the TnyAccount without
783 * changing the libtinymail-camel. */
784 const gchar* account_id = tny_account_get_id (account);
785 const gboolean is_actual_local_folders_account = account_id &&
786 (strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID) == 0);
788 TnyIterator* iter = tny_list_create_iterator (list_outbox_folders);
789 while (!tny_iterator_is_done (iter))
791 TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (iter));
795 /* TODO: Do not add outboxes that are inside local-folders/,
796 * because these are just left-over from earlier Modest versions
797 * that put the outbox there: */
798 if (is_actual_local_folders_account) {
799 const TnyFolderType type = modest_tny_folder_get_local_folder_type (folder);
800 if (type == TNY_FOLDER_TYPE_OUTBOX) {
806 modest_tny_simple_folder_store_add_folder (store, folder);
808 g_object_unref (folder);
811 tny_iterator_next (iter);
814 g_object_unref (list_outbox_folders);
818 update_model (ModestFolderView *self, ModestTnyAccountStore *account_store)
820 ModestFolderViewPrivate *priv;
823 g_return_val_if_fail (account_store, FALSE);
825 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
827 /* Notify that there is no folder selected */
828 g_signal_emit (G_OBJECT(self),
829 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
832 /* FIXME: the local accounts are not shown when the query
833 selects only the subscribed folders. */
834 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
835 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
837 /* Deal with the model via its TnyList Interface,
838 * filling the TnyList via a get_accounts() call: */
839 TnyList *model_as_list = TNY_LIST(model);
841 /* Create a virtual local-folders folder store,
842 * containing the real local folders and the (merged) various per-account
845 ModestTnySimpleFolderStore *store = modest_tny_simple_folder_store_new ();
847 /* Get the accounts: */
848 TnyList *account_list = tny_simple_list_new ();
849 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
851 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
852 TnyIterator* iter = tny_list_create_iterator (account_list);
854 /* All per-account outbox folders are merged into one folders
855 * so that they appear as one outbox to the user: */
856 TnyMergeFolder *merged_outbox = TNY_MERGE_FOLDER (tny_merge_folder_new());
858 while (!tny_iterator_is_done (iter))
860 GObject *cur = tny_iterator_get_current (iter);
861 TnyAccount *account = TNY_ACCOUNT (cur);
863 /* Add both outbox account and local-folders account folders
864 * to our one combined account:
866 if (MODEST_IS_TNY_OUTBOX_ACCOUNT (account)) {
867 /* Add the folder to the merged folder.
868 * We will add it later to the virtual local-folders store: */
869 add_account_folders_to_merged_folder (account, merged_outbox);
871 const gchar *account_id = tny_account_get_id (account);
872 if (account_id && !strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
873 /* Add the folders to the virtual local-folders store: */
874 add_account_folders_to_simple_folder_store (account, store);
877 /* Just add the account: */
878 tny_list_append (model_as_list, G_OBJECT (account));
883 g_object_unref (cur);
884 tny_iterator_next (iter);
887 /* Add the merged outbox folder to the virtual local-folders store: */
888 modest_tny_simple_folder_store_add_folder (store, TNY_FOLDER(merged_outbox));
889 g_object_unref (merged_outbox);
890 merged_outbox = NULL;
892 /* Add the virtual local-folders store to the model: */
893 tny_list_append (model_as_list, G_OBJECT (store));
896 g_object_unref (account_list);
899 g_object_unref (model_as_list);
900 model_as_list = NULL;
902 /* tny_list_foreach (account_list, on_tnylist_accounts_debug_print, "update_model: "); */
904 GtkTreeModel *filter_model = NULL, *sortable = NULL;
906 sortable = gtk_tree_model_sort_new_with_model (model);
907 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
908 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
910 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
911 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
912 cmp_rows, NULL, NULL);
914 /* Create filter model */
915 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
916 filter_model = gtk_tree_model_filter_new (sortable, NULL);
917 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
924 gtk_tree_view_set_model (GTK_TREE_VIEW(self),
925 (filter_model) ? filter_model : sortable);
926 expand_root_items (self); /* expand all account folders */
933 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
936 TnyFolderStore *folder = NULL;
938 ModestFolderView *tree_view;
939 ModestFolderViewPrivate *priv;
942 g_return_if_fail (sel);
943 g_return_if_fail (user_data);
945 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
947 /* folder was _un_selected if true */
948 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
949 if (priv->cur_folder_store)
950 g_object_unref (priv->cur_folder_store);
951 priv->cur_folder_store = NULL;
953 /* Notify the display name observers */
954 g_signal_emit (G_OBJECT(user_data),
955 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
960 tree_view = MODEST_FOLDER_VIEW (user_data);
962 gtk_tree_model_get (model, &iter,
963 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
964 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
967 /* If the folder is the same do not notify */
968 if (priv->cur_folder_store == folder) {
969 g_object_unref (folder);
973 /* Current folder was unselected */
974 if (priv->cur_folder_store) {
975 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
976 priv->cur_folder_store, FALSE);
977 g_object_unref (priv->cur_folder_store);
980 /* New current references */
981 priv->cur_folder_store = folder;
983 /* New folder has been selected */
984 g_signal_emit (G_OBJECT(tree_view),
985 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
990 modest_folder_view_get_selected (ModestFolderView *self)
992 ModestFolderViewPrivate *priv;
994 g_return_val_if_fail (self, NULL);
996 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
997 if (priv->cur_folder_store)
998 g_object_ref (priv->cur_folder_store);
1000 return priv->cur_folder_store;
1004 get_cmp_rows_type_pos (GObject *folder)
1006 /* Remote accounts -> Local account -> MMC account .*/
1009 if (TNY_IS_FOLDER_STORE (folder) &&
1010 modest_tny_folder_store_is_virtual_local_folders (
1011 TNY_FOLDER_STORE (folder))) {
1013 } else if (TNY_IS_ACCOUNT (folder)) {
1014 TnyAccount *account = TNY_ACCOUNT (folder);
1015 const gchar *account_id = tny_account_get_id (account);
1016 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1022 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1023 return -1; /* Should never happen */
1028 * This function orders the mail accounts according to these rules:
1029 * 1st - remote accounts
1030 * 2nd - local account
1034 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1038 gchar *name1, *name2;
1040 GObject *folder1 = NULL;
1041 GObject *folder2 = NULL;
1043 gtk_tree_model_get (tree_model, iter1,
1044 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1045 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1046 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1048 gtk_tree_model_get (tree_model, iter2,
1049 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1050 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1053 if (type == TNY_FOLDER_TYPE_ROOT) {
1054 /* Compare the types, so that
1055 * Remote accounts -> Local account -> MMC account .*/
1056 const gint pos1 = get_cmp_rows_type_pos (folder1);
1057 const gint pos2 = get_cmp_rows_type_pos (folder2);
1058 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1059 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1062 else if (pos1 > pos2)
1065 /* Compare items of the same type: */
1067 TnyAccount *account1 = NULL;
1068 if (TNY_IS_ACCOUNT (folder1))
1069 account1 = TNY_ACCOUNT (folder1);
1071 TnyAccount *account2 = NULL;
1072 if (TNY_IS_ACCOUNT (folder2))
1073 account2 = TNY_ACCOUNT (folder2);
1075 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1076 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1078 if (!account_id && !account_id2)
1080 else if (!account_id)
1082 else if (!account_id2)
1084 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1087 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1090 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1094 g_object_unref(G_OBJECT(folder1));
1096 g_object_unref(G_OBJECT(folder2));
1104 /*****************************************************************************/
1105 /* DRAG and DROP stuff */
1106 /*****************************************************************************/
1109 * This function fills the #GtkSelectionData with the row and the
1110 * model that has been dragged. It's called when this widget is a
1111 * source for dnd after the event drop happened
1114 on_drag_data_get (GtkWidget *widget,
1115 GdkDragContext *context,
1116 GtkSelectionData *selection_data,
1121 GtkTreeSelection *selection;
1122 GtkTreeModel *model;
1124 GtkTreePath *source_row;
1126 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1127 gtk_tree_selection_get_selected (selection, &model, &iter);
1128 source_row = gtk_tree_model_get_path (model, &iter);
1130 gtk_tree_set_row_drag_data (selection_data,
1134 gtk_tree_path_free (source_row);
1137 typedef struct _DndHelper {
1138 gboolean delete_source;
1139 GtkTreePath *source_row;
1140 GdkDragContext *context;
1146 * This function is the callback of the
1147 * modest_mail_operation_xfer_msgs () and
1148 * modest_mail_operation_xfer_folder() calls. We check here if the
1149 * message/folder was correctly asynchronously transferred. The reason
1150 * to use the same callback is that the code is the same, it only has
1151 * to check that the operation went fine and then finalize the drag
1155 on_progress_changed (ModestMailOperation *mail_op,
1156 ModestMailOperationState *state,
1162 helper = (DndHelper *) user_data;
1164 if (!state->finished)
1167 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1173 /* Notify the drag source. Never call delete, the monitor will
1174 do the job if needed */
1175 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1177 /* Free the helper */
1178 gtk_tree_path_free (helper->source_row);
1179 g_slice_free (DndHelper, helper);
1183 * This function is used by drag_data_received_cb to manage drag and
1184 * drop of a header, i.e, and drag from the header view to the folder
1188 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1189 GtkTreeModel *dest_model,
1190 GtkTreePath *dest_row,
1196 ModestMailOperation *mail_op;
1197 GtkTreeIter source_iter, dest_iter;
1200 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1201 gtk_tree_model_get (source_model, &source_iter,
1202 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1206 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1207 gtk_tree_model_get (dest_model, &dest_iter,
1208 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1211 /* Transfer message */
1212 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1214 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1216 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1217 G_CALLBACK (on_progress_changed), helper);
1219 /* FIXME: I replaced this because the API changed, but D&D
1220 should be reviewed in order to allow multiple drags*/
1221 headers = tny_simple_list_new ();
1222 tny_list_append (headers, G_OBJECT (header));
1223 modest_mail_operation_xfer_msgs (mail_op, headers, folder, helper->delete_source, NULL, NULL);
1226 g_object_unref (G_OBJECT (mail_op));
1227 g_object_unref (G_OBJECT (header));
1228 g_object_unref (G_OBJECT (folder));
1229 g_object_unref (headers);
1233 * This function is used by drag_data_received_cb to manage drag and
1234 * drop of a folder, i.e, and drag from the folder view to the same
1238 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1239 GtkTreeModel *dest_model,
1240 GtkTreePath *dest_row,
1241 GtkSelectionData *selection_data,
1244 ModestMailOperation *mail_op;
1245 GtkTreeIter parent_iter, iter;
1246 TnyFolderStore *parent_folder;
1249 /* Check if the drag is possible */
1250 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1251 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1253 /* selection_data)) { */
1254 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1256 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1257 gtk_tree_path_free (helper->source_row);
1258 g_slice_free (DndHelper, helper);
1263 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1264 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1265 gtk_tree_model_get (source_model, &parent_iter,
1266 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1267 &parent_folder, -1);
1268 gtk_tree_model_get (source_model, &iter,
1269 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1272 /* Do the mail operation */
1273 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1275 modest_ui_actions_move_folder_error_handler,
1277 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1279 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1280 G_CALLBACK (on_progress_changed), helper);
1282 modest_mail_operation_xfer_folder (mail_op,
1285 helper->delete_source);
1288 g_object_unref (G_OBJECT (parent_folder));
1289 g_object_unref (G_OBJECT (folder));
1290 g_object_unref (G_OBJECT (mail_op));
1294 * This function receives the data set by the "drag-data-get" signal
1295 * handler. This information comes within the #GtkSelectionData. This
1296 * function will manage both the drags of folders of the treeview and
1297 * drags of headers of the header view widget.
1300 on_drag_data_received (GtkWidget *widget,
1301 GdkDragContext *context,
1304 GtkSelectionData *selection_data,
1309 GtkWidget *source_widget;
1310 GtkTreeModel *dest_model, *source_model;
1311 GtkTreePath *source_row, *dest_row;
1312 GtkTreeViewDropPosition pos;
1313 gboolean success = FALSE, delete_source = FALSE;
1314 DndHelper *helper = NULL;
1316 /* Do not allow further process */
1317 g_signal_stop_emission_by_name (widget, "drag-data-received");
1318 source_widget = gtk_drag_get_source_widget (context);
1320 /* Get the action */
1321 if (context->action == GDK_ACTION_MOVE) {
1322 delete_source = TRUE;
1324 /* Notify that there is no folder selected. We need to
1325 do this in order to update the headers view (and
1326 its monitors, because when moving, the old folder
1327 won't longer exist. We can not wait for the end of
1328 the operation, because the operation won't start if
1329 the folder is in use */
1330 if (source_widget == widget)
1331 g_signal_emit (G_OBJECT (widget),
1332 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1335 /* Check if the get_data failed */
1336 if (selection_data == NULL || selection_data->length < 0)
1337 gtk_drag_finish (context, success, FALSE, time);
1339 /* Get the models */
1340 gtk_tree_get_row_drag_data (selection_data,
1344 /* Select the destination model */
1345 if (source_widget == widget) {
1346 dest_model = source_model;
1348 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1351 /* Get the path to the destination row. Can not call
1352 gtk_tree_view_get_drag_dest_row() because the source row
1353 is not selected anymore */
1354 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1357 /* Only allow drops IN other rows */
1358 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1359 gtk_drag_finish (context, success, FALSE, time);
1361 /* Create the helper */
1362 helper = g_slice_new0 (DndHelper);
1363 helper->delete_source = delete_source;
1364 helper->source_row = gtk_tree_path_copy (source_row);
1365 helper->context = context;
1366 helper->time = time;
1368 /* Drags from the header view */
1369 if (source_widget != widget) {
1371 drag_and_drop_from_header_view (source_model,
1378 drag_and_drop_from_folder_view (source_model,
1386 gtk_tree_path_free (source_row);
1387 gtk_tree_path_free (dest_row);
1391 * We define a "drag-drop" signal handler because we do not want to
1392 * use the default one, because the default one always calls
1393 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1394 * signal handler, because there we have all the information available
1395 * to know if the dnd was a success or not.
1398 drag_drop_cb (GtkWidget *widget,
1399 GdkDragContext *context,
1407 if (!context->targets)
1410 /* Check if we're dragging a folder row */
1411 target = gtk_drag_dest_find_target (widget, context, NULL);
1413 /* Request the data from the source. */
1414 gtk_drag_get_data(widget, context, target, time);
1420 * This function expands a node of a tree view if it's not expanded
1421 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1422 * does that, so that's why they're here.
1425 expand_row_timeout (gpointer data)
1427 GtkTreeView *tree_view = data;
1428 GtkTreePath *dest_path = NULL;
1429 GtkTreeViewDropPosition pos;
1430 gboolean result = FALSE;
1432 GDK_THREADS_ENTER ();
1434 gtk_tree_view_get_drag_dest_row (tree_view,
1439 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1440 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1441 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1442 gtk_tree_path_free (dest_path);
1446 gtk_tree_path_free (dest_path);
1451 GDK_THREADS_LEAVE ();
1457 * This function is called whenever the pointer is moved over a widget
1458 * while dragging some data. It installs a timeout that will expand a
1459 * node of the treeview if not expanded yet. This function also calls
1460 * gdk_drag_status in order to set the suggested action that will be
1461 * used by the "drag-data-received" signal handler to know if we
1462 * should do a move or just a copy of the data.
1465 on_drag_motion (GtkWidget *widget,
1466 GdkDragContext *context,
1472 GtkTreeViewDropPosition pos;
1473 GtkTreePath *dest_row;
1474 ModestFolderViewPrivate *priv;
1475 GdkDragAction suggested_action;
1476 gboolean valid_location = FALSE;
1478 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1480 if (priv->timer_expander != 0) {
1481 g_source_remove (priv->timer_expander);
1482 priv->timer_expander = 0;
1485 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1490 /* Do not allow drops between folders */
1492 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1493 pos == GTK_TREE_VIEW_DROP_AFTER) {
1494 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1495 gdk_drag_status(context, 0, time);
1496 valid_location = FALSE;
1499 valid_location = TRUE;
1502 /* Expand the selected row after 1/2 second */
1503 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1504 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1505 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1508 /* Select the desired action. By default we pick MOVE */
1509 suggested_action = GDK_ACTION_MOVE;
1511 if (context->actions == GDK_ACTION_COPY)
1512 gdk_drag_status(context, GDK_ACTION_COPY, time);
1513 else if (context->actions == GDK_ACTION_MOVE)
1514 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1515 else if (context->actions & suggested_action)
1516 gdk_drag_status(context, suggested_action, time);
1518 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1522 gtk_tree_path_free (dest_row);
1523 g_signal_stop_emission_by_name (widget, "drag-motion");
1524 return valid_location;
1528 /* Folder view drag types */
1529 const GtkTargetEntry folder_view_drag_types[] =
1531 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1532 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1536 * This function sets the treeview as a source and a target for dnd
1537 * events. It also connects all the requirede signals.
1540 setup_drag_and_drop (GtkTreeView *self)
1542 /* Set up the folder view as a dnd destination. Set only the
1543 highlight flag, otherwise gtk will have a different
1545 gtk_drag_dest_set (GTK_WIDGET (self),
1546 GTK_DEST_DEFAULT_HIGHLIGHT,
1547 folder_view_drag_types,
1548 G_N_ELEMENTS (folder_view_drag_types),
1549 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1551 g_signal_connect (G_OBJECT (self),
1552 "drag_data_received",
1553 G_CALLBACK (on_drag_data_received),
1557 /* Set up the treeview as a dnd source */
1558 gtk_drag_source_set (GTK_WIDGET (self),
1560 folder_view_drag_types,
1561 G_N_ELEMENTS (folder_view_drag_types),
1562 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1564 g_signal_connect (G_OBJECT (self),
1566 G_CALLBACK (on_drag_motion),
1569 g_signal_connect (G_OBJECT (self),
1571 G_CALLBACK (on_drag_data_get),
1574 g_signal_connect (G_OBJECT (self),
1576 G_CALLBACK (drag_drop_cb),
1581 * This function manages the navigation through the folders using the
1582 * keyboard or the hardware keys in the device
1585 on_key_pressed (GtkWidget *self,
1589 GtkTreeSelection *selection;
1591 GtkTreeModel *model;
1592 gboolean retval = FALSE;
1594 /* Up and Down are automatically managed by the treeview */
1595 if (event->keyval == GDK_Return) {
1596 /* Expand/Collapse the selected row */
1597 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1598 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1601 path = gtk_tree_model_get_path (model, &iter);
1603 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1604 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1606 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1607 gtk_tree_path_free (path);
1609 /* No further processing */
1617 * We listen to the changes in the local folder account name key,
1618 * because we want to show the right name in the view. The local
1619 * folder account name corresponds to the device name in the Maemo
1620 * version. We do this because we do not want to query gconf on each
1621 * tree view refresh. It's better to cache it and change whenever
1625 on_configuration_key_changed (ModestConf* conf,
1627 ModestConfEvent event,
1628 ModestFolderView *self)
1630 ModestFolderViewPrivate *priv;
1635 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1636 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1638 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1639 g_free (priv->local_account_name);
1641 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1642 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1644 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1645 MODEST_CONF_DEVICE_NAME, NULL);
1647 /* Force a redraw */
1648 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1649 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1650 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1651 gtk_tree_view_column_queue_resize (tree_column);
1657 modest_folder_view_set_style (ModestFolderView *self,
1658 ModestFolderViewStyle style)
1660 ModestFolderViewPrivate *priv;
1662 g_return_if_fail (self);
1664 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1666 priv->style = style;
1670 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1671 const gchar *account_id)
1673 ModestFolderViewPrivate *priv;
1674 GtkTreeModel *model;
1676 g_return_if_fail (self);
1678 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1680 /* This will be used by the filter_row callback,
1681 * to decided which rows to show: */
1682 if (priv->visible_account_id)
1683 g_free (priv->visible_account_id);
1684 priv->visible_account_id = g_strdup (account_id);
1687 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1688 if (GTK_IS_TREE_MODEL_FILTER (model))
1689 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1693 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1695 ModestFolderViewPrivate *priv;
1697 g_return_val_if_fail (self, NULL);
1699 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1701 return (const gchar *) priv->visible_account_id;