1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-store-tree-model.h>
36 #include <tny-gtk-header-list-model.h>
37 #include <tny-folder.h>
38 #include <tny-folder-store-observer.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <tny-camel-account.h>
45 #include <modest-tny-account.h>
46 #include <modest-tny-folder.h>
47 #include <modest-tny-local-folders-account.h>
48 #include <modest-tny-outbox-account.h>
49 #include <modest-marshal.h>
50 #include <modest-icon-names.h>
51 #include <modest-tny-account-store.h>
52 #include <modest-text-utils.h>
53 #include <modest-runtime.h>
54 #include "modest-folder-view.h"
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
58 #include "modest-dnd.h"
60 /* Folder view drag types */
61 const GtkTargetEntry folder_view_drag_types[] =
63 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
64 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
67 /* 'private'/'protected' functions */
68 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
69 static void modest_folder_view_init (ModestFolderView *obj);
70 static void modest_folder_view_finalize (GObject *obj);
72 static void tny_account_store_view_init (gpointer g,
75 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
76 TnyAccountStore *account_store);
78 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
80 static void on_account_removed (TnyAccountStore *self,
84 static void on_account_inserted (TnyAccountStore *self,
88 static void on_account_changed (TnyAccountStore *self,
92 static gint cmp_rows (GtkTreeModel *tree_model,
97 static gboolean filter_row (GtkTreeModel *model,
101 static gboolean on_key_pressed (GtkWidget *self,
105 static void on_configuration_key_changed (ModestConf* conf,
107 ModestConfEvent event,
108 ModestConfNotificationId notification_id,
109 ModestFolderView *self);
112 static void on_drag_data_get (GtkWidget *widget,
113 GdkDragContext *context,
114 GtkSelectionData *selection_data,
119 static void on_drag_data_received (GtkWidget *widget,
120 GdkDragContext *context,
123 GtkSelectionData *selection_data,
128 static gboolean on_drag_motion (GtkWidget *widget,
129 GdkDragContext *context,
135 static void expand_root_items (ModestFolderView *self);
137 static gint expand_row_timeout (gpointer data);
139 static void setup_drag_and_drop (GtkTreeView *self);
141 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
144 static void _clear_hidding_filter (ModestFolderView *folder_view);
146 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
149 ModestFolderView *self);
152 FOLDER_SELECTION_CHANGED_SIGNAL,
153 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
157 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
158 struct _ModestFolderViewPrivate {
159 TnyAccountStore *account_store;
160 TnyFolderStore *cur_folder_store;
162 TnyFolder *folder_to_select; /* folder to select after the next update */
164 ModestConfNotificationId notification_id;
166 gulong changed_signal;
167 gulong account_inserted_signal;
168 gulong account_removed_signal;
169 gulong account_changed_signal;
170 gulong conf_key_signal;
172 /* not unref this object, its a singlenton */
173 ModestEmailClipboard *clipboard;
175 /* Filter tree model */
179 TnyFolderStoreQuery *query;
180 guint timer_expander;
182 gchar *local_account_name;
183 gchar *visible_account_id;
184 ModestFolderViewStyle style;
186 gboolean reselect; /* we use this to force a reselection of the INBOX */
187 gboolean show_non_move;
188 gboolean reexpand; /* next time we expose, we'll expand all root folders */
190 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
191 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
192 MODEST_TYPE_FOLDER_VIEW, \
193 ModestFolderViewPrivate))
195 static GObjectClass *parent_class = NULL;
197 static guint signals[LAST_SIGNAL] = {0};
200 modest_folder_view_get_type (void)
202 static GType my_type = 0;
204 static const GTypeInfo my_info = {
205 sizeof(ModestFolderViewClass),
206 NULL, /* base init */
207 NULL, /* base finalize */
208 (GClassInitFunc) modest_folder_view_class_init,
209 NULL, /* class finalize */
210 NULL, /* class data */
211 sizeof(ModestFolderView),
213 (GInstanceInitFunc) modest_folder_view_init,
217 static const GInterfaceInfo tny_account_store_view_info = {
218 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
219 NULL, /* interface_finalize */
220 NULL /* interface_data */
224 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
228 g_type_add_interface_static (my_type,
229 TNY_TYPE_ACCOUNT_STORE_VIEW,
230 &tny_account_store_view_info);
236 modest_folder_view_class_init (ModestFolderViewClass *klass)
238 GObjectClass *gobject_class;
239 gobject_class = (GObjectClass*) klass;
241 parent_class = g_type_class_peek_parent (klass);
242 gobject_class->finalize = modest_folder_view_finalize;
244 g_type_class_add_private (gobject_class,
245 sizeof(ModestFolderViewPrivate));
247 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
248 g_signal_new ("folder_selection_changed",
249 G_TYPE_FROM_CLASS (gobject_class),
251 G_STRUCT_OFFSET (ModestFolderViewClass,
252 folder_selection_changed),
254 modest_marshal_VOID__POINTER_BOOLEAN,
255 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
258 * This signal is emitted whenever the currently selected
259 * folder display name is computed. Note that the name could
260 * be different to the folder name, because we could append
261 * the unread messages count to the folder name to build the
262 * folder display name
264 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
265 g_signal_new ("folder-display-name-changed",
266 G_TYPE_FROM_CLASS (gobject_class),
268 G_STRUCT_OFFSET (ModestFolderViewClass,
269 folder_display_name_changed),
271 g_cclosure_marshal_VOID__STRING,
272 G_TYPE_NONE, 1, G_TYPE_STRING);
275 /* Simplify checks for NULLs: */
277 strings_are_equal (const gchar *a, const gchar *b)
283 return (strcmp (a, b) == 0);
290 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
292 GObject *instance = NULL;
294 gtk_tree_model_get (model, iter,
295 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
299 return FALSE; /* keep walking */
301 if (!TNY_IS_ACCOUNT (instance)) {
302 g_object_unref (instance);
303 return FALSE; /* keep walking */
306 /* Check if this is the looked-for account: */
307 TnyAccount *this_account = TNY_ACCOUNT (instance);
308 TnyAccount *account = TNY_ACCOUNT (data);
310 const gchar *this_account_id = tny_account_get_id(this_account);
311 const gchar *account_id = tny_account_get_id(account);
312 g_object_unref (instance);
315 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
316 if (strings_are_equal(this_account_id, account_id)) {
317 /* Tell the model that the data has changed, so that
318 * it calls the cell_data_func callbacks again: */
319 /* TODO: This does not seem to actually cause the new string to be shown: */
320 gtk_tree_model_row_changed (model, path, iter);
322 return TRUE; /* stop walking */
325 return FALSE; /* keep walking */
330 ModestFolderView *self;
331 gchar *previous_name;
332 } GetMmcAccountNameData;
335 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
337 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
339 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
341 if (!strings_are_equal (
342 tny_account_get_name(TNY_ACCOUNT(account)),
343 data->previous_name)) {
345 /* Tell the model that the data has changed, so that
346 * it calls the cell_data_func callbacks again: */
347 ModestFolderView *self = data->self;
348 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
350 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
353 g_free (data->previous_name);
354 g_slice_free (GetMmcAccountNameData, data);
358 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
359 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
361 ModestFolderViewPrivate *priv;
364 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
365 GObject *instance = NULL;
367 g_return_if_fail (column);
368 g_return_if_fail (tree_model);
369 g_return_if_fail (iter != NULL);
371 gtk_tree_model_get (tree_model, iter,
372 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
373 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
374 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
376 rendobj = G_OBJECT(renderer);
386 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
387 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
389 gchar *item_name = NULL;
390 gint item_weight = 400;
392 if (type != TNY_FOLDER_TYPE_ROOT) {
395 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
396 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
397 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
398 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
400 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
404 /* note: we cannot reliably get the counts from the tree model, we need
405 * to use explicit calls on tny_folder for some reason.
407 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
408 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
409 (type == TNY_FOLDER_TYPE_OUTBOX) ||
410 (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
411 number = tny_folder_get_all_count (TNY_FOLDER(instance));
413 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
415 /* Use bold font style if there are unread or unset messages */
417 item_name = g_strdup_printf ("%s (%d)", fname, number);
420 item_name = g_strdup (fname);
424 } else if (TNY_IS_ACCOUNT (instance)) {
425 /* If it's a server account */
426 if (modest_tny_account_is_virtual_local_folders (
427 TNY_ACCOUNT (instance))) {
428 item_name = g_strdup (priv->local_account_name);
430 } else if (modest_tny_account_is_memory_card_account (
431 TNY_ACCOUNT (instance))) {
432 /* fname is only correct when the items are first
433 * added to the model, not when the account is
434 * changed later, so get the name from the account
436 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
439 item_name = g_strdup (fname);
445 item_name = g_strdup ("unknown");
447 if (item_name && item_weight) {
448 /* Set the name in the treeview cell: */
449 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
451 /* Notify display name observers */
452 /* TODO: What listens for this signal, and how can it use only the new name? */
453 if (G_OBJECT (priv->cur_folder_store) == instance) {
454 g_signal_emit (G_OBJECT(self),
455 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
462 /* If it is a Memory card account, make sure that we have the correct name.
463 * This function will be trigerred again when the name has been retrieved: */
464 if (TNY_IS_STORE_ACCOUNT (instance) &&
465 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
467 /* Get the account name asynchronously: */
468 GetMmcAccountNameData *callback_data =
469 g_slice_new0(GetMmcAccountNameData);
470 callback_data->self = self;
472 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
474 callback_data->previous_name = g_strdup (name);
476 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
477 on_get_mmc_account_name, callback_data);
480 g_object_unref (G_OBJECT (instance));
485 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
486 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
488 GObject *rendobj = NULL, *instance = NULL;
489 GdkPixbuf *pixbuf = NULL;
490 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
491 const gchar *account_id = NULL;
492 gboolean has_children;
494 rendobj = G_OBJECT(renderer);
495 gtk_tree_model_get (tree_model, iter,
496 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
497 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
499 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
504 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
505 /* We include the MERGE type here because it's used to create
506 the local OUTBOX folder */
507 if (type == TNY_FOLDER_TYPE_NORMAL ||
508 type == TNY_FOLDER_TYPE_UNKNOWN) {
509 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
513 case TNY_FOLDER_TYPE_ROOT:
514 if (TNY_IS_ACCOUNT (instance)) {
516 if (modest_tny_account_is_virtual_local_folders (
517 TNY_ACCOUNT (instance))) {
518 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
521 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
523 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
524 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
526 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
530 case TNY_FOLDER_TYPE_INBOX:
531 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
533 case TNY_FOLDER_TYPE_OUTBOX:
534 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
536 case TNY_FOLDER_TYPE_JUNK:
537 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
539 case TNY_FOLDER_TYPE_SENT:
540 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
542 case TNY_FOLDER_TYPE_TRASH:
543 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
545 case TNY_FOLDER_TYPE_DRAFTS:
546 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
548 case TNY_FOLDER_TYPE_NORMAL:
550 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
554 g_object_unref (G_OBJECT (instance));
557 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
558 if (has_children && (pixbuf != NULL)) {
559 GdkPixbuf *open_pixbuf, *closed_pixbuf;
560 GdkPixbuf *open_emblem, *closed_emblem;
561 open_pixbuf = gdk_pixbuf_copy (pixbuf);
562 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
563 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
564 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
567 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
568 MIN (gdk_pixbuf_get_width (open_emblem),
569 gdk_pixbuf_get_width (open_pixbuf)),
570 MIN (gdk_pixbuf_get_height (open_emblem),
571 gdk_pixbuf_get_height (open_pixbuf)),
572 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
573 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
574 g_object_unref (open_emblem);
577 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
578 MIN (gdk_pixbuf_get_width (closed_emblem),
579 gdk_pixbuf_get_width (closed_pixbuf)),
580 MIN (gdk_pixbuf_get_height (closed_emblem),
581 gdk_pixbuf_get_height (closed_pixbuf)),
582 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
583 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
584 g_object_unref (closed_emblem);
587 g_object_unref (closed_pixbuf);
589 g_object_unref (open_pixbuf);
593 g_object_unref (pixbuf);
597 add_columns (GtkWidget *treeview)
599 GtkTreeViewColumn *column;
600 GtkCellRenderer *renderer;
601 GtkTreeSelection *sel;
604 column = gtk_tree_view_column_new ();
606 /* Set icon and text render function */
607 renderer = gtk_cell_renderer_pixbuf_new();
608 gtk_tree_view_column_pack_start (column, renderer, FALSE);
609 gtk_tree_view_column_set_cell_data_func(column, renderer,
610 icon_cell_data, treeview, NULL);
612 renderer = gtk_cell_renderer_text_new();
613 gtk_tree_view_column_pack_start (column, renderer, FALSE);
614 gtk_tree_view_column_set_cell_data_func(column, renderer,
615 text_cell_data, treeview, NULL);
617 /* Set selection mode */
618 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
619 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
621 /* Set treeview appearance */
622 gtk_tree_view_column_set_spacing (column, 2);
623 gtk_tree_view_column_set_resizable (column, TRUE);
624 gtk_tree_view_column_set_fixed_width (column, TRUE);
625 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
626 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
629 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
633 modest_folder_view_init (ModestFolderView *obj)
635 ModestFolderViewPrivate *priv;
638 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
640 priv->timer_expander = 0;
641 priv->account_store = NULL;
643 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
644 priv->cur_folder_store = NULL;
645 priv->visible_account_id = NULL;
646 priv->folder_to_select = NULL;
648 priv->reexpand = TRUE;
650 /* Initialize the local account name */
651 conf = modest_runtime_get_conf();
652 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
654 /* Init email clipboard */
655 priv->clipboard = modest_runtime_get_email_clipboard ();
656 priv->hidding_ids = NULL;
657 priv->n_selected = 0;
658 priv->reselect = FALSE;
659 priv->show_non_move = TRUE;
662 add_columns (GTK_WIDGET (obj));
664 /* Setup drag and drop */
665 setup_drag_and_drop (GTK_TREE_VIEW(obj));
667 /* Connect signals */
668 g_signal_connect (G_OBJECT (obj),
670 G_CALLBACK (on_key_pressed), NULL);
673 * Track changes in the local account name (in the device it
674 * will be the device name)
676 priv->notification_id = modest_conf_listen_to_namespace (conf,
677 MODEST_CONF_NAMESPACE);
679 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
681 G_CALLBACK(on_configuration_key_changed),
686 tny_account_store_view_init (gpointer g, gpointer iface_data)
688 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
690 klass->set_account_store_func = modest_folder_view_set_account_store;
696 modest_folder_view_finalize (GObject *obj)
698 ModestFolderViewPrivate *priv;
699 GtkTreeSelection *sel;
701 g_return_if_fail (obj);
703 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
705 if (priv->notification_id) {
706 modest_conf_forget_namespace (modest_runtime_get_conf (),
707 MODEST_CONF_NAMESPACE,
708 priv->notification_id);
711 if (priv->timer_expander != 0) {
712 g_source_remove (priv->timer_expander);
713 priv->timer_expander = 0;
716 if (priv->account_store) {
717 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
718 priv->account_inserted_signal);
719 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
720 priv->account_removed_signal);
721 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
722 priv->account_changed_signal);
723 g_object_unref (G_OBJECT(priv->account_store));
724 priv->account_store = NULL;
728 g_object_unref (G_OBJECT (priv->query));
732 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
733 if (priv->folder_to_select) {
734 g_object_unref (G_OBJECT(priv->folder_to_select));
735 priv->folder_to_select = NULL;
738 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
740 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
742 g_free (priv->local_account_name);
743 g_free (priv->visible_account_id);
745 if (priv->conf_key_signal) {
746 g_signal_handler_disconnect (modest_runtime_get_conf (),
747 priv->conf_key_signal);
748 priv->conf_key_signal = 0;
751 if (priv->cur_folder_store) {
752 if (TNY_IS_FOLDER(priv->cur_folder_store))
753 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
754 /* FALSE --> expunge the message */
756 g_object_unref (priv->cur_folder_store);
757 priv->cur_folder_store = NULL;
760 /* Clear hidding array created by cut operation */
761 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
763 G_OBJECT_CLASS(parent_class)->finalize (obj);
768 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
770 ModestFolderViewPrivate *priv;
773 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
774 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
776 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
777 device = tny_account_store_get_device (account_store);
779 if (G_UNLIKELY (priv->account_store)) {
781 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
782 priv->account_inserted_signal))
783 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
784 priv->account_inserted_signal);
785 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
786 priv->account_removed_signal))
787 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
788 priv->account_removed_signal);
789 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
790 priv->account_changed_signal))
791 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
792 priv->account_changed_signal);
793 g_object_unref (G_OBJECT (priv->account_store));
796 priv->account_store = g_object_ref (G_OBJECT (account_store));
798 priv->account_removed_signal =
799 g_signal_connect (G_OBJECT(account_store), "account_removed",
800 G_CALLBACK (on_account_removed), self);
802 priv->account_inserted_signal =
803 g_signal_connect (G_OBJECT(account_store), "account_inserted",
804 G_CALLBACK (on_account_inserted), self);
806 priv->account_changed_signal =
807 g_signal_connect (G_OBJECT(account_store), "account_changed",
808 G_CALLBACK (on_account_changed), self);
810 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
812 g_object_unref (G_OBJECT (device));
816 on_account_inserted (TnyAccountStore *account_store,
820 ModestFolderViewPrivate *priv;
821 GtkTreeModel *sort_model, *filter_model;
823 /* Ignore transport account insertions, we're not showing them
824 in the folder view */
825 if (TNY_IS_TRANSPORT_ACCOUNT (account))
828 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
830 /* If we're adding a new account, and there is no previous
831 one, we need to select the visible server account */
832 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
833 !priv->visible_account_id)
834 modest_widget_memory_restore (modest_runtime_get_conf(),
835 G_OBJECT (user_data),
836 MODEST_CONF_FOLDER_VIEW_KEY);
838 /* Get the inner model */
839 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
840 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
842 /* Insert the account in the model */
843 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
850 on_account_changed (TnyAccountStore *account_store,
851 TnyAccount *tny_account,
855 ModestFolderViewPrivate *priv;
856 GtkTreeModel *sort_model, *filter_model;
858 /* Ignore transport account insertions, we're not showing them
859 in the folder view */
860 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
863 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
865 /* Get the inner model */
866 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
867 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
869 /* Remove the account from the model */
870 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
871 G_OBJECT (tny_account));
873 /* Insert the account in the model */
874 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
875 G_OBJECT (tny_account));
881 on_account_removed (TnyAccountStore *account_store,
885 ModestFolderView *self = NULL;
886 ModestFolderViewPrivate *priv;
887 GtkTreeModel *sort_model, *filter_model;
888 GtkTreeSelection *sel = NULL;
890 /* Ignore transport account removals, we're not showing them
891 in the folder view */
892 if (TNY_IS_TRANSPORT_ACCOUNT (account))
895 self = MODEST_FOLDER_VIEW (user_data);
896 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
898 /* Invalidate the cur_folder_store only if the selected folder
899 belongs to the account that is being removed */
900 if (priv->cur_folder_store) {
901 TnyAccount *selected_folder_account = NULL;
903 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
904 selected_folder_account =
905 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
907 selected_folder_account =
908 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
911 if (selected_folder_account == account) {
912 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
913 gtk_tree_selection_unselect_all (sel);
915 g_object_unref (selected_folder_account);
918 /* Invalidate row to select only if the folder to select
919 belongs to the account that is being removed*/
920 if (priv->folder_to_select) {
921 TnyAccount *folder_to_select_account = NULL;
923 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
924 if (folder_to_select_account == account) {
925 /* modest_folder_view_disable_next_folder_selection (self); */
926 g_object_unref (priv->folder_to_select);
927 priv->folder_to_select = NULL;
929 g_object_unref (folder_to_select_account);
932 /* Remove the account from the model */
933 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
934 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
935 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
938 /* If the removed account is the currently viewed one then
939 clear the configuration value. The new visible account will be the default account */
940 if (priv->visible_account_id &&
941 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
943 /* Clear the current visible account_id */
944 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
946 /* Call the restore method, this will set the new visible account */
947 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
948 MODEST_CONF_FOLDER_VIEW_KEY);
951 /* Select the INBOX */
952 modest_folder_view_select_first_inbox_or_local (self);
956 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
958 GtkTreeViewColumn *col;
960 g_return_if_fail (self);
962 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
964 g_printerr ("modest: failed get column for title\n");
968 gtk_tree_view_column_set_title (col, title);
969 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
974 modest_folder_view_on_map (ModestFolderView *self,
975 GdkEventExpose *event,
978 ModestFolderViewPrivate *priv;
980 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
982 /* This won't happen often */
983 if (G_UNLIKELY (priv->reselect)) {
984 /* Select the first inbox or the local account if not found */
986 /* TODO: this could cause a lock at startup, so we
987 comment it for the moment. We know that this will
988 be a bug, because the INBOX is not selected, but we
989 need to rewrite some parts of Modest to avoid the
990 deathlock situation */
991 /* TODO: check if this is still the case */
992 priv->reselect = FALSE;
993 modest_folder_view_select_first_inbox_or_local (self);
994 /* Notify the display name observers */
995 g_signal_emit (G_OBJECT(self),
996 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1000 if (priv->reexpand) {
1001 expand_root_items (self);
1002 priv->reexpand = FALSE;
1009 modest_folder_view_new (TnyFolderStoreQuery *query)
1012 ModestFolderViewPrivate *priv;
1013 GtkTreeSelection *sel;
1015 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1016 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1019 priv->query = g_object_ref (query);
1021 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1022 priv->changed_signal = g_signal_connect (sel, "changed",
1023 G_CALLBACK (on_selection_changed), self);
1025 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1027 return GTK_WIDGET(self);
1030 /* this feels dirty; any other way to expand all the root items? */
1032 expand_root_items (ModestFolderView *self)
1035 GtkTreeModel *model;
1038 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1039 path = gtk_tree_path_new_first ();
1041 /* all folders should have child items, so.. */
1043 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1044 gtk_tree_path_next (path);
1045 } while (gtk_tree_model_get_iter (model, &iter, path));
1047 gtk_tree_path_free (path);
1051 * We use this function to implement the
1052 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1053 * account in this case, and the local folders.
1056 filter_row (GtkTreeModel *model,
1060 ModestFolderViewPrivate *priv;
1061 gboolean retval = TRUE;
1062 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1063 GObject *instance = NULL;
1064 const gchar *id = NULL;
1066 gboolean found = FALSE;
1067 gboolean cleared = FALSE;
1069 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1070 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1072 gtk_tree_model_get (model, iter,
1073 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1074 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1077 /* Do not show if there is no instance, this could indeed
1078 happen when the model is being modified while it's being
1079 drawn. This could occur for example when moving folders
1084 if (type == TNY_FOLDER_TYPE_ROOT) {
1085 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1086 account instead of a folder. */
1087 if (TNY_IS_ACCOUNT (instance)) {
1088 TnyAccount *acc = TNY_ACCOUNT (instance);
1089 const gchar *account_id = tny_account_get_id (acc);
1091 /* If it isn't a special folder,
1092 * don't show it unless it is the visible account: */
1093 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1094 !modest_tny_account_is_virtual_local_folders (acc) &&
1095 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1097 /* Show only the visible account id */
1098 if (priv->visible_account_id) {
1099 if (strcmp (account_id, priv->visible_account_id))
1106 /* Never show these to the user. They are merged into one folder
1107 * in the local-folders account instead: */
1108 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1113 /* Check hiding (if necessary) */
1114 cleared = modest_email_clipboard_cleared (priv->clipboard);
1115 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1116 id = tny_folder_get_id (TNY_FOLDER(instance));
1117 if (priv->hidding_ids != NULL)
1118 for (i=0; i < priv->n_selected && !found; i++)
1119 if (priv->hidding_ids[i] != NULL && id != NULL)
1120 found = (!strcmp (priv->hidding_ids[i], id));
1126 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1127 folder as no message can be move there according to UI specs */
1128 if (!priv->show_non_move) {
1130 case TNY_FOLDER_TYPE_OUTBOX:
1131 case TNY_FOLDER_TYPE_SENT:
1132 case TNY_FOLDER_TYPE_DRAFTS:
1135 case TNY_FOLDER_TYPE_UNKNOWN:
1136 case TNY_FOLDER_TYPE_NORMAL:
1137 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1138 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1139 type == TNY_FOLDER_TYPE_SENT
1140 || type == TNY_FOLDER_TYPE_DRAFTS)
1149 g_object_unref (instance);
1156 modest_folder_view_update_model (ModestFolderView *self,
1157 TnyAccountStore *account_store)
1159 ModestFolderViewPrivate *priv;
1160 GtkTreeModel *model /* , *old_model */;
1161 /* TnyAccount *local_account; */
1162 TnyList *model_as_list;
1164 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1165 g_return_val_if_fail (account_store, FALSE);
1167 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1169 /* Notify that there is no folder selected */
1170 g_signal_emit (G_OBJECT(self),
1171 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1173 if (priv->cur_folder_store) {
1174 g_object_unref (priv->cur_folder_store);
1175 priv->cur_folder_store = NULL;
1178 /* FIXME: the local accounts are not shown when the query
1179 selects only the subscribed folders. */
1180 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1181 model = tny_gtk_folder_store_tree_model_new (NULL);
1183 /* Deal with the model via its TnyList Interface,
1184 * filling the TnyList via a get_accounts() call: */
1185 model_as_list = TNY_LIST(model);
1187 /* Get the accounts: */
1188 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1190 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1191 g_object_unref (model_as_list);
1192 model_as_list = NULL;
1194 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1196 sortable = gtk_tree_model_sort_new_with_model (model);
1197 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1198 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1199 GTK_SORT_ASCENDING);
1200 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1201 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1202 cmp_rows, NULL, NULL);
1204 /* Create filter model */
1205 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1206 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1212 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1213 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1214 (GCallback) on_row_inserted_maybe_select_folder, self);
1217 g_object_unref (model);
1218 g_object_unref (filter_model);
1219 g_object_unref (sortable);
1221 /* Force a reselection of the INBOX next time the widget is shown */
1222 priv->reselect = TRUE;
1229 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1231 GtkTreeModel *model = NULL;
1232 TnyFolderStore *folder = NULL;
1234 ModestFolderView *tree_view = NULL;
1235 ModestFolderViewPrivate *priv = NULL;
1236 gboolean selected = FALSE;
1238 g_return_if_fail (sel);
1239 g_return_if_fail (user_data);
1241 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1243 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1245 /* Notify the display name observers */
1246 g_signal_emit (G_OBJECT(user_data),
1247 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1250 tree_view = MODEST_FOLDER_VIEW (user_data);
1253 gtk_tree_model_get (model, &iter,
1254 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1257 /* If the folder is the same do not notify */
1258 if (folder && priv->cur_folder_store == folder) {
1259 g_object_unref (folder);
1264 /* Current folder was unselected */
1265 if (priv->cur_folder_store) {
1266 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1267 priv->cur_folder_store, FALSE);
1269 if (TNY_IS_FOLDER(priv->cur_folder_store))
1270 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1271 FALSE, NULL, NULL, NULL);
1273 /* FALSE --> don't expunge the messages */
1275 g_object_unref (priv->cur_folder_store);
1276 priv->cur_folder_store = NULL;
1279 /* New current references */
1280 priv->cur_folder_store = folder;
1282 /* New folder has been selected */
1283 g_signal_emit (G_OBJECT(tree_view),
1284 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1285 0, priv->cur_folder_store, TRUE);
1289 modest_folder_view_get_selected (ModestFolderView *self)
1291 ModestFolderViewPrivate *priv;
1293 g_return_val_if_fail (self, NULL);
1295 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1296 if (priv->cur_folder_store)
1297 g_object_ref (priv->cur_folder_store);
1299 return priv->cur_folder_store;
1303 get_cmp_rows_type_pos (GObject *folder)
1305 /* Remote accounts -> Local account -> MMC account .*/
1308 if (TNY_IS_ACCOUNT (folder) &&
1309 modest_tny_account_is_virtual_local_folders (
1310 TNY_ACCOUNT (folder))) {
1312 } else if (TNY_IS_ACCOUNT (folder)) {
1313 TnyAccount *account = TNY_ACCOUNT (folder);
1314 const gchar *account_id = tny_account_get_id (account);
1315 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1321 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1322 return -1; /* Should never happen */
1327 get_cmp_subfolder_type_pos (TnyFolderType t)
1329 /* Inbox, Outbox, Drafts, Sent, User */
1333 case TNY_FOLDER_TYPE_INBOX:
1336 case TNY_FOLDER_TYPE_OUTBOX:
1339 case TNY_FOLDER_TYPE_DRAFTS:
1342 case TNY_FOLDER_TYPE_SENT:
1351 * This function orders the mail accounts according to these rules:
1352 * 1st - remote accounts
1353 * 2nd - local account
1357 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1361 gchar *name1 = NULL;
1362 gchar *name2 = NULL;
1363 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1364 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1365 GObject *folder1 = NULL;
1366 GObject *folder2 = NULL;
1368 gtk_tree_model_get (tree_model, iter1,
1369 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1370 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1371 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1373 gtk_tree_model_get (tree_model, iter2,
1374 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1375 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1376 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1379 /* Return if we get no folder. This could happen when folder
1380 operations are happening. The model is updated after the
1381 folder copy/move actually occurs, so there could be
1382 situations where the model to be drawn is not correct */
1383 if (!folder1 || !folder2)
1386 if (type == TNY_FOLDER_TYPE_ROOT) {
1387 /* Compare the types, so that
1388 * Remote accounts -> Local account -> MMC account .*/
1389 const gint pos1 = get_cmp_rows_type_pos (folder1);
1390 const gint pos2 = get_cmp_rows_type_pos (folder2);
1391 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1392 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1395 else if (pos1 > pos2)
1398 /* Compare items of the same type: */
1400 TnyAccount *account1 = NULL;
1401 if (TNY_IS_ACCOUNT (folder1))
1402 account1 = TNY_ACCOUNT (folder1);
1404 TnyAccount *account2 = NULL;
1405 if (TNY_IS_ACCOUNT (folder2))
1406 account2 = TNY_ACCOUNT (folder2);
1408 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1409 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1411 if (!account_id && !account_id2) {
1413 } else if (!account_id) {
1415 } else if (!account_id2) {
1417 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1420 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1424 gint cmp1 = 0, cmp2 = 0;
1425 /* get the parent to know if it's a local folder */
1428 gboolean has_parent;
1429 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1431 GObject *parent_folder;
1432 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1433 gtk_tree_model_get (tree_model, &parent,
1434 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1435 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1437 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1438 TNY_IS_ACCOUNT (parent_folder) &&
1439 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1440 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1441 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1443 g_object_unref (parent_folder);
1446 /* if they are not local folders */
1448 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1449 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1453 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1455 cmp = (cmp1 - cmp2);
1460 g_object_unref(G_OBJECT(folder1));
1462 g_object_unref(G_OBJECT(folder2));
1470 /*****************************************************************************/
1471 /* DRAG and DROP stuff */
1472 /*****************************************************************************/
1474 * This function fills the #GtkSelectionData with the row and the
1475 * model that has been dragged. It's called when this widget is a
1476 * source for dnd after the event drop happened
1479 on_drag_data_get (GtkWidget *widget,
1480 GdkDragContext *context,
1481 GtkSelectionData *selection_data,
1486 GtkTreeSelection *selection;
1487 GtkTreeModel *model;
1489 GtkTreePath *source_row;
1491 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1492 gtk_tree_selection_get_selected (selection, &model, &iter);
1493 source_row = gtk_tree_model_get_path (model, &iter);
1495 gtk_tree_set_row_drag_data (selection_data,
1499 gtk_tree_path_free (source_row);
1502 typedef struct _DndHelper {
1503 gboolean delete_source;
1504 GtkTreePath *source_row;
1505 GdkDragContext *context;
1511 * This function is the callback of the
1512 * modest_mail_operation_xfer_msgs () and
1513 * modest_mail_operation_xfer_folder() calls. We check here if the
1514 * message/folder was correctly asynchronously transferred. The reason
1515 * to use the same callback is that the code is the same, it only has
1516 * to check that the operation went fine and then finalize the drag
1520 xfer_cb (ModestMailOperation *mail_op,
1526 helper = (DndHelper *) user_data;
1528 if (modest_mail_operation_get_status (mail_op) ==
1529 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1535 /* Notify the drag source. Never call delete, the monitor will
1536 do the job if needed */
1537 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1539 /* Free the helper */
1540 gtk_tree_path_free (helper->source_row);
1541 g_slice_free (DndHelper, helper);
1544 /* get the folder for the row the treepath refers to. */
1545 /* folder must be unref'd */
1547 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1550 TnyFolder *folder = NULL;
1552 if (gtk_tree_model_get_iter (model,&iter, path))
1553 gtk_tree_model_get (model, &iter,
1554 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1560 * This function is used by drag_data_received_cb to manage drag and
1561 * drop of a header, i.e, and drag from the header view to the folder
1565 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1566 GtkTreeModel *dest_model,
1567 GtkTreePath *dest_row,
1568 GtkSelectionData *selection_data,
1571 TnyList *headers = NULL;
1572 TnyFolder *folder = NULL;
1573 ModestMailOperation *mail_op = NULL;
1574 GtkTreeIter source_iter, dest_iter;
1575 ModestWindowMgr *mgr = NULL;
1576 ModestWindow *main_win = NULL;
1577 gchar **uris, **tmp;
1580 /* Build the list of headers */
1581 mgr = modest_runtime_get_window_mgr ();
1582 headers = tny_simple_list_new ();
1583 uris = modest_dnd_selection_data_get_paths (selection_data);
1586 while (*tmp != NULL) {
1591 path = gtk_tree_path_new_from_string (*tmp);
1592 gtk_tree_model_get_iter (source_model, &source_iter, path);
1593 gtk_tree_model_get (source_model, &source_iter,
1594 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1597 /* Do not enable d&d of headers already opened */
1598 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
1599 tny_list_append (headers, G_OBJECT (header));
1601 /* Free and go on */
1602 gtk_tree_path_free (path);
1603 g_object_unref (header);
1608 /* Get the target folder */
1609 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1610 gtk_tree_model_get (dest_model, &dest_iter,
1611 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1614 /* Ask for confirmation to move */
1615 main_win = modest_window_mgr_get_main_window(mgr);
1616 response = modest_ui_actions_msgs_move_to_confirmation (GTK_WINDOW(main_win), folder,
1618 if (response == GTK_RESPONSE_CANCEL)
1621 /* Transfer messages */
1622 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1624 modest_ui_actions_move_folder_error_handler,
1627 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1630 modest_mail_operation_xfer_msgs (mail_op,
1633 helper->delete_source,
1638 if (G_IS_OBJECT(mail_op))
1639 g_object_unref (G_OBJECT (mail_op));
1640 if (G_IS_OBJECT(folder))
1641 g_object_unref (G_OBJECT (folder));
1642 if (G_IS_OBJECT(headers))
1643 g_object_unref (headers);
1647 * This function is used by drag_data_received_cb to manage drag and
1648 * drop of a folder, i.e, and drag from the folder view to the same
1652 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1653 GtkTreeModel *dest_model,
1654 GtkTreePath *dest_row,
1655 GtkSelectionData *selection_data,
1658 ModestMailOperation *mail_op = NULL;
1659 GtkTreeIter dest_iter, iter;
1660 TnyFolderStore *dest_folder = NULL;
1661 TnyFolder *folder = NULL;
1662 gboolean forbidden = FALSE;
1665 /* check the folder rules for the destination */
1666 folder = tree_path_to_folder (dest_model, dest_row);
1667 if (TNY_IS_FOLDER(folder)) {
1668 ModestTnyFolderRules rules =
1669 modest_tny_folder_get_rules (folder);
1670 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1671 } else if (TNY_IS_FOLDER_STORE(folder)) {
1672 /* enable local root as destination for folders */
1673 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1674 && TNY_IS_ACCOUNT (folder))
1677 g_object_unref (folder);
1680 /* check the folder rules for the source */
1681 folder = tree_path_to_folder (source_model, helper->source_row);
1682 if (TNY_IS_FOLDER(folder)) {
1683 ModestTnyFolderRules rules =
1684 modest_tny_folder_get_rules (folder);
1685 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1688 g_object_unref (folder);
1692 /* Check if the drag is possible */
1693 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1694 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1695 gtk_tree_path_free (helper->source_row);
1696 g_slice_free (DndHelper, helper);
1701 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1702 gtk_tree_model_get (dest_model, &dest_iter,
1703 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1705 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1706 gtk_tree_model_get (source_model, &iter,
1707 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1710 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1711 if (modest_platform_connect_and_wait_if_network_folderstore (NULL, dest_folder) &&
1712 modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1713 ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1715 /* Do the mail operation */
1717 modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1718 G_OBJECT (modest_window_mgr_get_main_window (mgr)),
1719 modest_ui_actions_move_folder_error_handler,
1722 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1725 modest_mail_operation_xfer_folder (mail_op,
1728 helper->delete_source,
1732 g_object_unref (G_OBJECT (mail_op));
1736 g_object_unref (G_OBJECT (dest_folder));
1737 g_object_unref (G_OBJECT (folder));
1741 * This function receives the data set by the "drag-data-get" signal
1742 * handler. This information comes within the #GtkSelectionData. This
1743 * function will manage both the drags of folders of the treeview and
1744 * drags of headers of the header view widget.
1747 on_drag_data_received (GtkWidget *widget,
1748 GdkDragContext *context,
1751 GtkSelectionData *selection_data,
1756 GtkWidget *source_widget;
1757 GtkTreeModel *dest_model, *source_model;
1758 GtkTreePath *source_row, *dest_row;
1759 GtkTreeViewDropPosition pos;
1760 gboolean success = FALSE, delete_source = FALSE;
1761 DndHelper *helper = NULL;
1763 /* Do not allow further process */
1764 g_signal_stop_emission_by_name (widget, "drag-data-received");
1765 source_widget = gtk_drag_get_source_widget (context);
1767 /* Get the action */
1768 if (context->action == GDK_ACTION_MOVE) {
1769 delete_source = TRUE;
1771 /* Notify that there is no folder selected. We need to
1772 do this in order to update the headers view (and
1773 its monitors, because when moving, the old folder
1774 won't longer exist. We can not wait for the end of
1775 the operation, because the operation won't start if
1776 the folder is in use */
1777 if (source_widget == widget) {
1778 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1779 gtk_tree_selection_unselect_all (sel);
1783 /* Check if the get_data failed */
1784 if (selection_data == NULL || selection_data->length < 0)
1785 gtk_drag_finish (context, success, FALSE, time);
1787 /* Select the destination model */
1788 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1790 /* Get the path to the destination row. Can not call
1791 gtk_tree_view_get_drag_dest_row() because the source row
1792 is not selected anymore */
1793 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1796 /* Only allow drops IN other rows */
1798 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1799 pos == GTK_TREE_VIEW_DROP_AFTER)
1800 gtk_drag_finish (context, success, FALSE, time);
1802 /* Create the helper */
1803 helper = g_slice_new0 (DndHelper);
1804 helper->delete_source = delete_source;
1805 helper->context = context;
1806 helper->time = time;
1808 /* Drags from the header view */
1809 if (source_widget != widget) {
1810 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
1812 drag_and_drop_from_header_view (source_model,
1818 /* Get the source model and row */
1819 gtk_tree_get_row_drag_data (selection_data,
1822 helper->source_row = gtk_tree_path_copy (source_row);
1824 drag_and_drop_from_folder_view (source_model,
1830 gtk_tree_path_free (source_row);
1834 gtk_tree_path_free (dest_row);
1838 * We define a "drag-drop" signal handler because we do not want to
1839 * use the default one, because the default one always calls
1840 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1841 * signal handler, because there we have all the information available
1842 * to know if the dnd was a success or not.
1845 drag_drop_cb (GtkWidget *widget,
1846 GdkDragContext *context,
1854 if (!context->targets)
1857 /* Check if we're dragging a folder row */
1858 target = gtk_drag_dest_find_target (widget, context, NULL);
1860 /* Request the data from the source. */
1861 gtk_drag_get_data(widget, context, target, time);
1867 * This function expands a node of a tree view if it's not expanded
1868 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1869 * does that, so that's why they're here.
1872 expand_row_timeout (gpointer data)
1874 GtkTreeView *tree_view = data;
1875 GtkTreePath *dest_path = NULL;
1876 GtkTreeViewDropPosition pos;
1877 gboolean result = FALSE;
1879 GDK_THREADS_ENTER ();
1881 gtk_tree_view_get_drag_dest_row (tree_view,
1886 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1887 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1888 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1889 gtk_tree_path_free (dest_path);
1893 gtk_tree_path_free (dest_path);
1898 GDK_THREADS_LEAVE ();
1904 * This function is called whenever the pointer is moved over a widget
1905 * while dragging some data. It installs a timeout that will expand a
1906 * node of the treeview if not expanded yet. This function also calls
1907 * gdk_drag_status in order to set the suggested action that will be
1908 * used by the "drag-data-received" signal handler to know if we
1909 * should do a move or just a copy of the data.
1912 on_drag_motion (GtkWidget *widget,
1913 GdkDragContext *context,
1919 GtkTreeViewDropPosition pos;
1920 GtkTreePath *dest_row;
1921 GtkTreeModel *dest_model;
1922 ModestFolderViewPrivate *priv;
1923 GdkDragAction suggested_action;
1924 gboolean valid_location = FALSE;
1927 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1929 if (priv->timer_expander != 0) {
1930 g_source_remove (priv->timer_expander);
1931 priv->timer_expander = 0;
1934 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1939 /* Do not allow drops between folders */
1941 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1942 pos == GTK_TREE_VIEW_DROP_AFTER) {
1943 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1944 gdk_drag_status(context, 0, time);
1945 valid_location = FALSE;
1948 valid_location = TRUE;
1951 /* Check that the destination folder is writable */
1952 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1953 folder = tree_path_to_folder (dest_model, dest_row);
1955 ModestTnyFolderRules rules = modest_tny_folder_get_rules(folder);
1957 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1958 valid_location = FALSE;
1961 g_object_unref (folder);
1964 /* Expand the selected row after 1/2 second */
1965 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1966 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1967 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1970 /* Select the desired action. By default we pick MOVE */
1971 suggested_action = GDK_ACTION_MOVE;
1973 if (context->actions == GDK_ACTION_COPY)
1974 gdk_drag_status(context, GDK_ACTION_COPY, time);
1975 else if (context->actions == GDK_ACTION_MOVE)
1976 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1977 else if (context->actions & suggested_action)
1978 gdk_drag_status(context, suggested_action, time);
1980 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1984 gtk_tree_path_free (dest_row);
1985 g_signal_stop_emission_by_name (widget, "drag-motion");
1987 return valid_location;
1991 * This function sets the treeview as a source and a target for dnd
1992 * events. It also connects all the requirede signals.
1995 setup_drag_and_drop (GtkTreeView *self)
1997 /* Set up the folder view as a dnd destination. Set only the
1998 highlight flag, otherwise gtk will have a different
2000 gtk_drag_dest_set (GTK_WIDGET (self),
2001 GTK_DEST_DEFAULT_HIGHLIGHT,
2002 folder_view_drag_types,
2003 G_N_ELEMENTS (folder_view_drag_types),
2004 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2006 g_signal_connect (G_OBJECT (self),
2007 "drag_data_received",
2008 G_CALLBACK (on_drag_data_received),
2012 /* Set up the treeview as a dnd source */
2013 gtk_drag_source_set (GTK_WIDGET (self),
2015 folder_view_drag_types,
2016 G_N_ELEMENTS (folder_view_drag_types),
2017 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2019 g_signal_connect (G_OBJECT (self),
2021 G_CALLBACK (on_drag_motion),
2024 g_signal_connect (G_OBJECT (self),
2026 G_CALLBACK (on_drag_data_get),
2029 g_signal_connect (G_OBJECT (self),
2031 G_CALLBACK (drag_drop_cb),
2036 * This function manages the navigation through the folders using the
2037 * keyboard or the hardware keys in the device
2040 on_key_pressed (GtkWidget *self,
2044 GtkTreeSelection *selection;
2046 GtkTreeModel *model;
2047 gboolean retval = FALSE;
2049 /* Up and Down are automatically managed by the treeview */
2050 if (event->keyval == GDK_Return) {
2051 /* Expand/Collapse the selected row */
2052 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2053 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2056 path = gtk_tree_model_get_path (model, &iter);
2058 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2059 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2061 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2062 gtk_tree_path_free (path);
2064 /* No further processing */
2072 * We listen to the changes in the local folder account name key,
2073 * because we want to show the right name in the view. The local
2074 * folder account name corresponds to the device name in the Maemo
2075 * version. We do this because we do not want to query gconf on each
2076 * tree view refresh. It's better to cache it and change whenever
2080 on_configuration_key_changed (ModestConf* conf,
2082 ModestConfEvent event,
2083 ModestConfNotificationId id,
2084 ModestFolderView *self)
2086 ModestFolderViewPrivate *priv;
2089 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2090 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2092 /* Do not listen for changes in other namespaces */
2093 if (priv->notification_id != id)
2096 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2097 g_free (priv->local_account_name);
2099 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2100 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2102 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2103 MODEST_CONF_DEVICE_NAME, NULL);
2105 /* Force a redraw */
2106 #if GTK_CHECK_VERSION(2, 8, 0)
2107 GtkTreeViewColumn * tree_column;
2109 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2110 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2111 gtk_tree_view_column_queue_resize (tree_column);
2113 gtk_widget_queue_draw (GTK_WIDGET (self));
2119 modest_folder_view_set_style (ModestFolderView *self,
2120 ModestFolderViewStyle style)
2122 ModestFolderViewPrivate *priv;
2124 g_return_if_fail (self);
2126 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2128 priv->style = style;
2132 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2133 const gchar *account_id)
2135 ModestFolderViewPrivate *priv;
2136 GtkTreeModel *model;
2138 g_return_if_fail (self);
2140 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2142 /* This will be used by the filter_row callback,
2143 * to decided which rows to show: */
2144 if (priv->visible_account_id) {
2145 g_free (priv->visible_account_id);
2146 priv->visible_account_id = NULL;
2149 priv->visible_account_id = g_strdup (account_id);
2152 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2153 if (GTK_IS_TREE_MODEL_FILTER (model))
2154 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2156 /* Save settings to gconf */
2157 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2158 MODEST_CONF_FOLDER_VIEW_KEY);
2162 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2164 ModestFolderViewPrivate *priv;
2166 g_return_val_if_fail (self, NULL);
2168 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2170 return (const gchar *) priv->visible_account_id;
2174 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2178 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2180 gtk_tree_model_get (model, iter,
2181 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2184 gboolean result = FALSE;
2185 if (type == TNY_FOLDER_TYPE_INBOX) {
2189 *inbox_iter = *iter;
2193 if (gtk_tree_model_iter_children (model, &child, iter)) {
2194 if (find_inbox_iter (model, &child, inbox_iter))
2198 } while (gtk_tree_model_iter_next (model, iter));
2207 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2209 GtkTreeModel *model;
2210 GtkTreeIter iter, inbox_iter;
2211 GtkTreeSelection *sel;
2212 GtkTreePath *path = NULL;
2214 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2218 expand_root_items (self);
2219 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2221 gtk_tree_model_get_iter_first (model, &iter);
2223 if (find_inbox_iter (model, &iter, &inbox_iter))
2224 path = gtk_tree_model_get_path (model, &inbox_iter);
2226 path = gtk_tree_path_new_first ();
2228 /* Select the row and free */
2229 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2230 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2231 gtk_tree_path_free (path);
2234 gtk_widget_grab_focus (GTK_WIDGET(self));
2240 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2245 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2246 TnyFolder* a_folder;
2249 gtk_tree_model_get (model, iter,
2250 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2251 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2252 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2256 if (folder == a_folder) {
2257 g_object_unref (a_folder);
2258 *folder_iter = *iter;
2261 g_object_unref (a_folder);
2263 if (gtk_tree_model_iter_children (model, &child, iter)) {
2264 if (find_folder_iter (model, &child, folder_iter, folder))
2268 } while (gtk_tree_model_iter_next (model, iter));
2275 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2276 ModestFolderView *self)
2278 ModestFolderViewPrivate *priv = NULL;
2279 GtkTreeSelection *sel;
2280 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2281 GObject *instance = NULL;
2283 if (!MODEST_IS_FOLDER_VIEW(self))
2286 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2288 priv->reexpand = TRUE;
2290 gtk_tree_model_get (tree_model, iter,
2291 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2292 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2294 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2295 priv->folder_to_select = g_object_ref (instance);
2297 g_object_unref (instance);
2300 if (priv->folder_to_select) {
2302 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2305 path = gtk_tree_model_get_path (tree_model, iter);
2306 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2308 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2310 gtk_tree_selection_select_iter (sel, iter);
2311 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2313 gtk_tree_path_free (path);
2318 modest_folder_view_disable_next_folder_selection (self);
2319 /* g_object_unref (priv->folder_to_select); */
2320 /* priv->folder_to_select = NULL; */
2326 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2328 ModestFolderViewPrivate *priv = NULL;
2330 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2331 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2333 if (priv->folder_to_select)
2334 g_object_unref(priv->folder_to_select);
2336 priv->folder_to_select = NULL;
2340 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2341 gboolean after_change)
2343 GtkTreeModel *model;
2344 GtkTreeIter iter, folder_iter;
2345 GtkTreeSelection *sel;
2346 ModestFolderViewPrivate *priv = NULL;
2348 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2349 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2351 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2355 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2356 gtk_tree_selection_unselect_all (sel);
2358 if (priv->folder_to_select)
2359 g_object_unref(priv->folder_to_select);
2360 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2364 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2369 gtk_tree_model_get_iter_first (model, &iter);
2370 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2373 path = gtk_tree_model_get_path (model, &folder_iter);
2374 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2376 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2377 gtk_tree_selection_select_iter (sel, &folder_iter);
2378 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2380 gtk_tree_path_free (path);
2388 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2390 /* Copy selection */
2391 _clipboard_set_selected_data (folder_view, FALSE);
2395 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2397 ModestFolderViewPrivate *priv = NULL;
2398 GtkTreeModel *model = NULL;
2399 const gchar **hidding = NULL;
2400 guint i, n_selected;
2402 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2403 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2405 /* Copy selection */
2406 if (!_clipboard_set_selected_data (folder_view, TRUE))
2409 /* Get hidding ids */
2410 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2412 /* Clear hidding array created by previous cut operation */
2413 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2415 /* Copy hidding array */
2416 priv->n_selected = n_selected;
2417 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2418 for (i=0; i < n_selected; i++)
2419 priv->hidding_ids[i] = g_strdup(hidding[i]);
2421 /* Hide cut folders */
2422 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2423 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2427 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2428 ModestFolderView *folder_view_dst)
2430 GtkTreeModel *filter_model = NULL;
2431 GtkTreeModel *model = NULL;
2432 GtkTreeModel *new_filter_model = NULL;
2434 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2435 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2438 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2439 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2441 /* Build new filter model */
2442 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2443 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2447 /* Set copied model */
2448 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2449 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
2450 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
2453 g_object_unref (new_filter_model);
2457 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2460 GtkTreeModel *model = NULL;
2461 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2462 priv->show_non_move = show;
2463 /* modest_folder_view_update_model(folder_view, */
2464 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2466 /* Hide special folders */
2467 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2468 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2469 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2473 /* Returns FALSE if it did not selected anything */
2475 _clipboard_set_selected_data (ModestFolderView *folder_view,
2478 ModestFolderViewPrivate *priv = NULL;
2479 TnyFolderStore *folder = NULL;
2480 gboolean retval = FALSE;
2482 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2483 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2485 /* Set selected data on clipboard */
2486 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2487 folder = modest_folder_view_get_selected (folder_view);
2489 /* Do not allow to select an account */
2490 if (TNY_IS_FOLDER (folder)) {
2491 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2496 g_object_unref (folder);
2502 _clear_hidding_filter (ModestFolderView *folder_view)
2504 ModestFolderViewPrivate *priv;
2507 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2508 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2510 if (priv->hidding_ids != NULL) {
2511 for (i=0; i < priv->n_selected; i++)
2512 g_free (priv->hidding_ids[i]);
2513 g_free(priv->hidding_ids);