1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-store-tree-model.h>
36 #include <tny-gtk-header-list-model.h>
37 #include <tny-folder.h>
38 #include <tny-folder-store-observer.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <modest-tny-account.h>
45 #include <modest-tny-folder.h>
46 #include <modest-tny-local-folders-account.h>
47 #include <modest-tny-outbox-account.h>
48 #include <modest-marshal.h>
49 #include <modest-icon-names.h>
50 #include <modest-tny-account-store.h>
51 #include <modest-text-utils.h>
52 #include <modest-runtime.h>
53 #include "modest-folder-view.h"
54 #include <modest-dnd.h>
55 #include <modest-platform.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 void on_selection_changed (GtkTreeSelection *sel, gpointer data);
72 static void on_account_update (TnyAccountStore *account_store,
76 static void on_account_removed (TnyAccountStore *self,
80 static void on_accounts_reloaded (TnyAccountStore *store,
83 static gint cmp_rows (GtkTreeModel *tree_model,
88 static gboolean filter_row (GtkTreeModel *model,
92 static gboolean on_key_pressed (GtkWidget *self,
96 static void on_configuration_key_changed (ModestConf* conf,
98 ModestConfEvent event,
99 ModestFolderView *self);
102 static void on_drag_data_get (GtkWidget *widget,
103 GdkDragContext *context,
104 GtkSelectionData *selection_data,
109 static void on_drag_data_received (GtkWidget *widget,
110 GdkDragContext *context,
113 GtkSelectionData *selection_data,
118 static gboolean on_drag_motion (GtkWidget *widget,
119 GdkDragContext *context,
125 static gint expand_row_timeout (gpointer data);
127 static void setup_drag_and_drop (GtkTreeView *self);
129 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view, gboolean delete);
131 static void _clear_hidding_filter (ModestFolderView *folder_view);
133 static void on_row_changed_maybe_select_folder (GtkTreeModel *tree_model,
136 ModestFolderView *self);
139 FOLDER_SELECTION_CHANGED_SIGNAL,
140 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
144 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
145 struct _ModestFolderViewPrivate {
146 TnyAccountStore *account_store;
147 TnyFolderStore *cur_folder_store;
149 TnyFolder *folder_to_select; /* folder to select after the next update */
151 gulong account_update_signal;
152 gulong changed_signal;
153 gulong accounts_reloaded_signal;
154 gulong account_removed_signal;
155 gulong conf_key_signal;
157 /* not unref this object, its a singlenton */
158 ModestEmailClipboard *clipboard;
160 /* Filter tree model */
164 TnyFolderStoreQuery *query;
165 guint timer_expander;
167 gchar *local_account_name;
168 gchar *visible_account_id;
169 ModestFolderViewStyle style;
171 gboolean reselect; /* we use this to force a reselection of the INBOX */
172 gboolean show_non_move;
174 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
175 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
176 MODEST_TYPE_FOLDER_VIEW, \
177 ModestFolderViewPrivate))
179 static GObjectClass *parent_class = NULL;
181 static guint signals[LAST_SIGNAL] = {0};
184 modest_folder_view_get_type (void)
186 static GType my_type = 0;
188 static const GTypeInfo my_info = {
189 sizeof(ModestFolderViewClass),
190 NULL, /* base init */
191 NULL, /* base finalize */
192 (GClassInitFunc) modest_folder_view_class_init,
193 NULL, /* class finalize */
194 NULL, /* class data */
195 sizeof(ModestFolderView),
197 (GInstanceInitFunc) modest_folder_view_init,
201 static const GInterfaceInfo tny_account_store_view_info = {
202 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
203 NULL, /* interface_finalize */
204 NULL /* interface_data */
208 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
212 g_type_add_interface_static (my_type,
213 TNY_TYPE_ACCOUNT_STORE_VIEW,
214 &tny_account_store_view_info);
220 modest_folder_view_class_init (ModestFolderViewClass *klass)
222 GObjectClass *gobject_class;
223 gobject_class = (GObjectClass*) klass;
225 parent_class = g_type_class_peek_parent (klass);
226 gobject_class->finalize = modest_folder_view_finalize;
228 g_type_class_add_private (gobject_class,
229 sizeof(ModestFolderViewPrivate));
231 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
232 g_signal_new ("folder_selection_changed",
233 G_TYPE_FROM_CLASS (gobject_class),
235 G_STRUCT_OFFSET (ModestFolderViewClass,
236 folder_selection_changed),
238 modest_marshal_VOID__POINTER_BOOLEAN,
239 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
242 * This signal is emitted whenever the currently selected
243 * folder display name is computed. Note that the name could
244 * be different to the folder name, because we could append
245 * the unread messages count to the folder name to build the
246 * folder display name
248 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
249 g_signal_new ("folder-display-name-changed",
250 G_TYPE_FROM_CLASS (gobject_class),
252 G_STRUCT_OFFSET (ModestFolderViewClass,
253 folder_display_name_changed),
255 g_cclosure_marshal_VOID__STRING,
256 G_TYPE_NONE, 1, G_TYPE_STRING);
259 /* Simplify checks for NULLs: */
260 static gboolean strings_are_equal (const gchar *a, const gchar *b)
266 return (strcmp (a, b) == 0);
272 static gboolean on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
274 GObject *instance = NULL;
276 gtk_tree_model_get (model, iter,
277 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
281 return FALSE; /* keep walking */
283 if (!TNY_IS_ACCOUNT (instance)) {
284 g_object_unref (instance);
285 return FALSE; /* keep walking */
288 /* Check if this is the looked-for account: */
289 TnyAccount *this_account = TNY_ACCOUNT (instance);
290 TnyAccount *account = TNY_ACCOUNT (data);
292 const gchar *this_account_id = tny_account_get_id(this_account);
293 const gchar *account_id = tny_account_get_id(account);
294 g_object_unref (instance);
297 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
298 if (strings_are_equal(this_account_id, account_id)) {
299 /* Tell the model that the data has changed, so that
300 * it calls the cell_data_func callbacks again: */
301 /* TODO: This does not seem to actually cause the new string to be shown: */
302 gtk_tree_model_row_changed (model, path, iter);
304 return TRUE; /* stop walking */
307 return FALSE; /* keep walking */
312 ModestFolderView *self;
313 gchar *previous_name;
314 } GetMmcAccountNameData;
316 static void on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
318 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
320 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
322 if (!strings_are_equal (
323 tny_account_get_name(TNY_ACCOUNT(account)),
324 data->previous_name)) {
326 /* Tell the model that the data has changed, so that
327 * it calls the cell_data_func callbacks again: */
328 ModestFolderView *self = data->self;
329 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
331 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
334 g_free (data->previous_name);
335 g_slice_free (GetMmcAccountNameData, data);
339 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
340 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
342 ModestFolderViewPrivate *priv;
347 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
348 GObject *instance = NULL;
350 g_return_if_fail (column);
351 g_return_if_fail (tree_model);
353 gtk_tree_model_get (tree_model, iter,
354 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
355 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
356 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
357 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
358 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
360 rendobj = G_OBJECT(renderer);
370 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
371 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
373 gchar *item_name = NULL;
374 gint item_weight = 400;
376 if (type != TNY_FOLDER_TYPE_ROOT) {
379 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
380 type = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
381 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
383 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
387 /* Select the number to show: the unread or unsent messages */
388 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
393 /* Use bold font style if there are unread or unset messages */
395 item_name = g_strdup_printf ("%s (%d)", fname, number);
398 item_name = g_strdup (fname);
402 } else if (TNY_IS_ACCOUNT (instance)) {
403 /* If it's a server account */
404 if (modest_tny_account_is_virtual_local_folders (
405 TNY_ACCOUNT (instance))) {
406 item_name = g_strdup (priv->local_account_name);
408 } else if (modest_tny_account_is_memory_card_account (
409 TNY_ACCOUNT (instance))) {
410 /* fname is only correct when the items are first
411 * added to the model, not when the account is
412 * changed later, so get the name from the account
414 item_name = g_strdup (tny_account_get_name (
415 TNY_ACCOUNT (instance)));
418 item_name = g_strdup (fname);
424 item_name = g_strdup ("unknown");
426 if (item_name && item_weight) {
427 /* Set the name in the treeview cell: */
428 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
430 /* Notify display name observers */
431 /* TODO: What listens for this signal, and how can it use only the new name? */
432 if (G_OBJECT (priv->cur_folder_store) == instance) {
433 g_signal_emit (G_OBJECT(self),
434 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
442 /* If it is a Memory card account, make sure that we have the correct name.
443 * This function will be trigerred again when the name has been retrieved: */
444 if (TNY_IS_STORE_ACCOUNT (instance) &&
445 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
447 /* Get the account name asynchronously: */
448 GetMmcAccountNameData *callback_data =
449 g_slice_new0(GetMmcAccountNameData);
450 callback_data->self = self;
452 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
454 callback_data->previous_name = g_strdup (name);
456 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
457 on_get_mmc_account_name, callback_data);
460 g_object_unref (G_OBJECT (instance));
467 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
468 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
470 GObject *rendobj = NULL, *instance = NULL;
471 GdkPixbuf *pixbuf = NULL;
472 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
474 const gchar *account_id = NULL;
476 gboolean has_children;
478 rendobj = G_OBJECT(renderer);
479 gtk_tree_model_get (tree_model, iter,
480 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
481 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
482 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
483 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
485 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
495 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
496 /* We include the MERGE type here because it's used to create
497 the local OUTBOX folder */
498 if (type == TNY_FOLDER_TYPE_NORMAL ||
499 type == TNY_FOLDER_TYPE_UNKNOWN) {
500 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
504 case TNY_FOLDER_TYPE_ROOT:
505 if (TNY_IS_ACCOUNT (instance)) {
507 if (modest_tny_account_is_virtual_local_folders (
508 TNY_ACCOUNT (instance))) {
509 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
512 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
514 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
515 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
517 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
521 case TNY_FOLDER_TYPE_INBOX:
522 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
524 case TNY_FOLDER_TYPE_OUTBOX:
525 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
527 case TNY_FOLDER_TYPE_JUNK:
528 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
530 case TNY_FOLDER_TYPE_SENT:
531 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
533 case TNY_FOLDER_TYPE_TRASH:
534 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
536 case TNY_FOLDER_TYPE_DRAFTS:
537 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
539 case TNY_FOLDER_TYPE_NORMAL:
541 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
545 g_object_unref (G_OBJECT (instance));
549 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
550 if (has_children && (pixbuf != NULL)) {
551 GdkPixbuf *open_pixbuf, *closed_pixbuf;
552 GdkPixbuf *open_emblem, *closed_emblem;
553 open_pixbuf = gdk_pixbuf_copy (pixbuf);
554 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
555 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
556 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
559 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
560 MIN (gdk_pixbuf_get_width (open_emblem),
561 gdk_pixbuf_get_width (open_pixbuf)),
562 MIN (gdk_pixbuf_get_height (open_emblem),
563 gdk_pixbuf_get_height (open_pixbuf)),
564 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
565 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
566 g_object_unref (open_emblem);
569 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
570 MIN (gdk_pixbuf_get_width (closed_emblem),
571 gdk_pixbuf_get_width (closed_pixbuf)),
572 MIN (gdk_pixbuf_get_height (closed_emblem),
573 gdk_pixbuf_get_height (closed_pixbuf)),
574 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
575 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
576 g_object_unref (closed_emblem);
579 g_object_unref (closed_pixbuf);
581 g_object_unref (open_pixbuf);
585 g_object_unref (pixbuf);
589 add_columns (GtkWidget *treeview)
591 GtkTreeViewColumn *column;
592 GtkCellRenderer *renderer;
593 GtkTreeSelection *sel;
596 column = gtk_tree_view_column_new ();
598 /* Set icon and text render function */
599 renderer = gtk_cell_renderer_pixbuf_new();
600 gtk_tree_view_column_pack_start (column, renderer, FALSE);
601 gtk_tree_view_column_set_cell_data_func(column, renderer,
602 icon_cell_data, treeview, NULL);
604 renderer = gtk_cell_renderer_text_new();
605 gtk_tree_view_column_pack_start (column, renderer, FALSE);
606 gtk_tree_view_column_set_cell_data_func(column, renderer,
607 text_cell_data, treeview, NULL);
609 /* Set selection mode */
610 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
611 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
613 /* Set treeview appearance */
614 gtk_tree_view_column_set_spacing (column, 2);
615 gtk_tree_view_column_set_resizable (column, TRUE);
616 gtk_tree_view_column_set_fixed_width (column, TRUE);
617 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
618 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
621 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
625 modest_folder_view_init (ModestFolderView *obj)
627 ModestFolderViewPrivate *priv;
630 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
632 priv->timer_expander = 0;
633 priv->account_store = NULL;
635 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
636 priv->cur_folder_store = NULL;
637 priv->visible_account_id = NULL;
638 priv->folder_to_select = NULL;
640 /* Initialize the local account name */
641 conf = modest_runtime_get_conf();
642 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
644 /* Init email clipboard */
645 priv->clipboard = modest_runtime_get_email_clipboard ();
646 priv->hidding_ids = NULL;
647 priv->n_selected = 0;
648 priv->reselect = FALSE;
649 priv->show_non_move = TRUE;
652 add_columns (GTK_WIDGET (obj));
654 /* Setup drag and drop */
655 setup_drag_and_drop (GTK_TREE_VIEW(obj));
657 /* Connect signals */
658 g_signal_connect (G_OBJECT (obj),
660 G_CALLBACK (on_key_pressed), NULL);
663 * Track changes in the local account name (in the device it
664 * will be the device name)
666 priv->conf_key_signal =
667 g_signal_connect (G_OBJECT(conf),
669 G_CALLBACK(on_configuration_key_changed), obj);
673 tny_account_store_view_init (gpointer g, gpointer iface_data)
675 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
677 klass->set_account_store_func = modest_folder_view_set_account_store;
683 modest_folder_view_finalize (GObject *obj)
685 ModestFolderViewPrivate *priv;
686 GtkTreeSelection *sel;
688 g_return_if_fail (obj);
690 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
692 if (priv->timer_expander != 0) {
693 g_source_remove (priv->timer_expander);
694 priv->timer_expander = 0;
697 if (priv->account_store) {
698 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
699 priv->account_update_signal);
700 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
701 priv->accounts_reloaded_signal);
702 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
703 priv->account_removed_signal);
704 g_object_unref (G_OBJECT(priv->account_store));
705 priv->account_store = NULL;
709 g_object_unref (G_OBJECT (priv->query));
713 if (priv->folder_to_select) {
714 g_object_unref (G_OBJECT(priv->folder_to_select));
715 priv->folder_to_select = NULL;
718 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
720 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
722 g_free (priv->local_account_name);
723 g_free (priv->visible_account_id);
725 if (priv->conf_key_signal) {
726 g_signal_handler_disconnect (modest_runtime_get_conf (),
727 priv->conf_key_signal);
728 priv->conf_key_signal = 0;
731 if (priv->cur_folder_store) {
732 if (TNY_IS_FOLDER(priv->cur_folder_store))
733 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
734 /* FALSE --> expunge the message */
736 g_object_unref (priv->cur_folder_store);
737 priv->cur_folder_store = NULL;
740 /* Clear hidding array created by cut operation */
741 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
743 G_OBJECT_CLASS(parent_class)->finalize (obj);
748 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
750 ModestFolderViewPrivate *priv;
753 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
754 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
756 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
757 device = tny_account_store_get_device (account_store);
759 if (G_UNLIKELY (priv->account_store)) {
761 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
762 priv->account_update_signal))
763 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
764 priv->account_update_signal);
765 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
766 priv->accounts_reloaded_signal))
767 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
768 priv->accounts_reloaded_signal);
769 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
770 priv->account_removed_signal))
771 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
772 priv->account_removed_signal);
774 g_object_unref (G_OBJECT (priv->account_store));
777 priv->account_store = g_object_ref (G_OBJECT (account_store));
779 priv->account_update_signal =
780 g_signal_connect (G_OBJECT(account_store), "account_update",
781 G_CALLBACK (on_account_update), self);
783 priv->account_removed_signal =
784 g_signal_connect (G_OBJECT(account_store), "account_removed",
785 G_CALLBACK (on_account_removed), self);
787 priv->accounts_reloaded_signal =
788 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
789 G_CALLBACK (on_accounts_reloaded), self);
791 g_signal_connect (G_OBJECT(account_store), "connecting_finished",
792 G_CALLBACK (on_accounts_reloaded), self);
794 on_accounts_reloaded (account_store, (gpointer ) self);
796 g_object_unref (G_OBJECT (device));
800 on_account_removed (TnyAccountStore *account_store,
804 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
805 ModestFolderViewPrivate *priv;
807 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
809 /* If the removed account is the currently viewed one then
810 clear the configuration value. The new visible account will be the default account */
811 if (!strcmp (priv->visible_account_id, tny_account_get_id (account))) {
812 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
817 on_account_update (TnyAccountStore *account_store,
818 const gchar *account,
821 ModestFolderView *self = NULL;
822 ModestFolderViewPrivate *priv;
824 g_return_if_fail (MODEST_IS_FOLDER_VIEW (user_data));
825 self = MODEST_FOLDER_VIEW (user_data);
826 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
828 if (!priv->visible_account_id)
829 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
830 MODEST_CONF_FOLDER_VIEW_KEY);
832 if (!modest_folder_view_update_model (self, account_store))
833 g_printerr ("modest: failed to update model for changes in '%s'",
838 on_accounts_reloaded (TnyAccountStore *account_store,
841 g_return_if_fail (MODEST_IS_FOLDER_VIEW (user_data));
842 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
846 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
848 GtkTreeViewColumn *col;
850 g_return_if_fail (self);
852 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
854 g_printerr ("modest: failed get column for title\n");
858 gtk_tree_view_column_set_title (col, title);
859 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
864 modest_folder_view_on_map (ModestFolderView *self,
865 GdkEventExpose *event,
868 ModestFolderViewPrivate *priv;
870 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
872 /* This won't happen often */
873 if (G_UNLIKELY (priv->reselect)) {
874 /* Select the first inbox or the local account if not found */
876 /* TODO: this could cause a lock at startup, so we
877 comment it for the moment. We know that this will
878 be a bug, because the INBOX is not selected, but we
879 need to rewrite some parts of Modest to avoid the
880 deathlock situation */
881 /* TODO: check if this is still the case */
882 priv->reselect = FALSE;
883 modest_folder_view_select_first_inbox_or_local (self);
889 modest_folder_view_new (TnyFolderStoreQuery *query)
892 ModestFolderViewPrivate *priv;
893 GtkTreeSelection *sel;
895 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
896 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
899 priv->query = g_object_ref (query);
901 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
902 priv->changed_signal = g_signal_connect (sel, "changed",
903 G_CALLBACK (on_selection_changed), self);
905 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
907 return GTK_WIDGET(self);
910 /* this feels dirty; any other way to expand all the root items? */
912 expand_root_items (ModestFolderView *self)
915 path = gtk_tree_path_new_first ();
917 /* all folders should have child items, so.. */
918 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
919 gtk_tree_path_next (path);
921 gtk_tree_path_free (path);
925 * We use this function to implement the
926 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
927 * account in this case, and the local folders.
930 filter_row (GtkTreeModel *model,
934 ModestFolderViewPrivate *priv;
935 gboolean retval = TRUE;
936 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
937 GObject *instance = NULL;
938 const gchar *id = NULL;
940 gboolean found = FALSE;
941 gboolean cleared = FALSE;
943 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
944 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
946 gtk_tree_model_get (model, iter,
947 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
948 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
951 /* Do not show if there is no instance, this could indeed
952 happen when the model is being modified while it's being
953 drawn. This could occur for example when moving folders
958 if (type == TNY_FOLDER_TYPE_ROOT) {
959 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
960 account instead of a folder. */
961 if (TNY_IS_ACCOUNT (instance)) {
962 TnyAccount *acc = TNY_ACCOUNT (instance);
963 const gchar *account_id = tny_account_get_id (acc);
965 /* If it isn't a special folder,
966 * don't show it unless it is the visible account: */
967 if (!modest_tny_account_is_virtual_local_folders (acc) &&
968 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
969 /* Show only the visible account id */
970 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
974 /* Never show these to the user. They are merged into one folder
975 * in the local-folders account instead: */
976 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
981 /* Check hiding (if necessary) */
982 cleared = modest_email_clipboard_cleared (priv->clipboard);
983 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
984 id = tny_folder_get_id (TNY_FOLDER(instance));
985 if (priv->hidding_ids != NULL)
986 for (i=0; i < priv->n_selected && !found; i++)
987 if (priv->hidding_ids[i] != NULL && id != NULL)
988 found = (!strcmp (priv->hidding_ids[i], id));
994 /* If this is a move to dialog, hide Sent, Outbox and Drafts
995 folder as no message can be move there according to UI specs */
996 if (!priv->show_non_move)
1000 case TNY_FOLDER_TYPE_OUTBOX:
1001 case TNY_FOLDER_TYPE_SENT:
1002 case TNY_FOLDER_TYPE_DRAFTS:
1005 case TNY_FOLDER_TYPE_UNKNOWN:
1006 case TNY_FOLDER_TYPE_NORMAL:
1007 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1008 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1009 || type == TNY_FOLDER_TYPE_DRAFTS)
1020 g_object_unref (instance);
1027 modest_folder_view_update_model (ModestFolderView *self,
1028 TnyAccountStore *account_store)
1030 ModestFolderViewPrivate *priv;
1031 GtkTreeModel *model /* , *old_model */;
1032 /* TnyAccount *local_account; */
1033 TnyList *model_as_list;
1035 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1036 g_return_val_if_fail (account_store, FALSE);
1038 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1040 /* Notify that there is no folder selected */
1041 g_signal_emit (G_OBJECT(self),
1042 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1044 if (priv->cur_folder_store) {
1045 g_object_unref (priv->cur_folder_store);
1046 priv->cur_folder_store = NULL;
1049 /* FIXME: the local accounts are not shown when the query
1050 selects only the subscribed folders. */
1051 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1052 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
1054 /* Deal with the model via its TnyList Interface,
1055 * filling the TnyList via a get_accounts() call: */
1056 model_as_list = TNY_LIST(model);
1058 /* Get the accounts: */
1059 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1061 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1062 g_object_unref (model_as_list);
1063 model_as_list = NULL;
1065 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1067 sortable = gtk_tree_model_sort_new_with_model (model);
1068 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1069 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1070 GTK_SORT_ASCENDING);
1071 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1072 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1073 cmp_rows, NULL, NULL);
1075 /* Create filter model */
1076 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1077 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1083 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1084 g_signal_connect (G_OBJECT(filter_model), "row-changed",
1085 (GCallback)on_row_changed_maybe_select_folder, self);
1086 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1087 (GCallback)on_row_changed_maybe_select_folder, self);
1090 g_object_unref (model);
1091 g_object_unref (filter_model);
1092 g_object_unref (sortable);
1094 /* Force a reselection of the INBOX next time the widget is shown */
1095 priv->reselect = TRUE;
1102 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1104 GtkTreeModel *model;
1105 TnyFolderStore *folder = NULL;
1107 ModestFolderView *tree_view;
1108 ModestFolderViewPrivate *priv;
1110 g_return_if_fail (sel);
1111 g_return_if_fail (user_data);
1113 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1115 if(!gtk_tree_selection_get_selected (sel, &model, &iter))
1118 /* Notify the display name observers */
1119 g_signal_emit (G_OBJECT(user_data),
1120 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1123 tree_view = MODEST_FOLDER_VIEW (user_data);
1124 gtk_tree_model_get (model, &iter,
1125 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1128 /* If the folder is the same do not notify */
1129 if (priv->cur_folder_store == folder && folder) {
1130 g_object_unref (folder);
1134 /* Current folder was unselected */
1135 if (priv->cur_folder_store) {
1136 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1137 priv->cur_folder_store, FALSE);
1139 if (TNY_IS_FOLDER(priv->cur_folder_store))
1140 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1141 FALSE, NULL, NULL, NULL);
1142 /* FALSE --> don't expunge the messages */
1144 g_object_unref (priv->cur_folder_store);
1145 priv->cur_folder_store = NULL;
1148 /* New current references */
1149 priv->cur_folder_store = folder;
1151 /* New folder has been selected */
1152 g_signal_emit (G_OBJECT(tree_view),
1153 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1154 0, priv->cur_folder_store, TRUE);
1158 modest_folder_view_get_selected (ModestFolderView *self)
1160 ModestFolderViewPrivate *priv;
1162 g_return_val_if_fail (self, NULL);
1164 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1165 if (priv->cur_folder_store)
1166 g_object_ref (priv->cur_folder_store);
1168 return priv->cur_folder_store;
1172 get_cmp_rows_type_pos (GObject *folder)
1174 /* Remote accounts -> Local account -> MMC account .*/
1177 if (TNY_IS_ACCOUNT (folder) &&
1178 modest_tny_account_is_virtual_local_folders (
1179 TNY_ACCOUNT (folder))) {
1181 } else if (TNY_IS_ACCOUNT (folder)) {
1182 TnyAccount *account = TNY_ACCOUNT (folder);
1183 const gchar *account_id = tny_account_get_id (account);
1184 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1190 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1191 return -1; /* Should never happen */
1196 get_cmp_subfolder_type_pos (TnyFolderType t)
1198 /* Inbox, Outbox, Drafts, Sent, User */
1202 case TNY_FOLDER_TYPE_INBOX:
1205 case TNY_FOLDER_TYPE_OUTBOX:
1208 case TNY_FOLDER_TYPE_DRAFTS:
1211 case TNY_FOLDER_TYPE_SENT:
1220 * This function orders the mail accounts according to these rules:
1221 * 1st - remote accounts
1222 * 2nd - local account
1226 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1230 gchar *name1 = NULL;
1231 gchar *name2 = NULL;
1232 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1233 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1234 GObject *folder1 = NULL;
1235 GObject *folder2 = NULL;
1237 gtk_tree_model_get (tree_model, iter1,
1238 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1239 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1240 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1242 gtk_tree_model_get (tree_model, iter2,
1243 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1244 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1245 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1248 /* Return if we get no folder. This could happen when folder
1249 operations are happening. The model is updated after the
1250 folder copy/move actually occurs, so there could be
1251 situations where the model to be drawn is not correct */
1252 if (!folder1 || !folder2)
1255 if (type == TNY_FOLDER_TYPE_ROOT) {
1256 /* Compare the types, so that
1257 * Remote accounts -> Local account -> MMC account .*/
1258 const gint pos1 = get_cmp_rows_type_pos (folder1);
1259 const gint pos2 = get_cmp_rows_type_pos (folder2);
1260 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1261 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1264 else if (pos1 > pos2)
1267 /* Compare items of the same type: */
1269 TnyAccount *account1 = NULL;
1270 if (TNY_IS_ACCOUNT (folder1))
1271 account1 = TNY_ACCOUNT (folder1);
1273 TnyAccount *account2 = NULL;
1274 if (TNY_IS_ACCOUNT (folder2))
1275 account2 = TNY_ACCOUNT (folder2);
1277 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1278 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1280 if (!account_id && !account_id2) {
1282 } else if (!account_id) {
1284 } else if (!account_id2) {
1286 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1289 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1293 gint cmp1 = 0, cmp2 = 0;
1294 /* get the parent to know if it's a local folder */
1297 gboolean has_parent;
1298 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1300 GObject *parent_folder;
1301 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1302 gtk_tree_model_get (tree_model, &parent,
1303 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1304 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1306 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1307 TNY_IS_ACCOUNT (parent_folder) &&
1308 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1309 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_folder_type (TNY_FOLDER (folder1)));
1310 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_folder_type (TNY_FOLDER (folder2)));
1312 g_object_unref (parent_folder);
1315 /* if they are not local folders */
1317 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1318 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1322 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1324 cmp = (cmp1 - cmp2);
1329 g_object_unref(G_OBJECT(folder1));
1331 g_object_unref(G_OBJECT(folder2));
1339 /*****************************************************************************/
1340 /* DRAG and DROP stuff */
1341 /*****************************************************************************/
1344 * This function fills the #GtkSelectionData with the row and the
1345 * model that has been dragged. It's called when this widget is a
1346 * source for dnd after the event drop happened
1349 on_drag_data_get (GtkWidget *widget,
1350 GdkDragContext *context,
1351 GtkSelectionData *selection_data,
1356 GtkTreeSelection *selection;
1357 GtkTreeModel *model;
1359 GtkTreePath *source_row;
1361 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1362 gtk_tree_selection_get_selected (selection, &model, &iter);
1363 source_row = gtk_tree_model_get_path (model, &iter);
1365 gtk_tree_set_row_drag_data (selection_data,
1369 gtk_tree_path_free (source_row);
1372 typedef struct _DndHelper {
1373 gboolean delete_source;
1374 GtkTreePath *source_row;
1375 GdkDragContext *context;
1381 * This function is the callback of the
1382 * modest_mail_operation_xfer_msgs () and
1383 * modest_mail_operation_xfer_folder() calls. We check here if the
1384 * message/folder was correctly asynchronously transferred. The reason
1385 * to use the same callback is that the code is the same, it only has
1386 * to check that the operation went fine and then finalize the drag
1390 on_progress_changed (ModestMailOperation *mail_op,
1391 ModestMailOperationState *state,
1397 helper = (DndHelper *) user_data;
1399 if (!state->finished)
1402 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1408 /* Notify the drag source. Never call delete, the monitor will
1409 do the job if needed */
1410 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1412 /* Free the helper */
1413 gtk_tree_path_free (helper->source_row);
1414 g_slice_free (DndHelper, helper);
1418 * This function is used by drag_data_received_cb to manage drag and
1419 * drop of a header, i.e, and drag from the header view to the folder
1423 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1424 GtkTreeModel *dest_model,
1425 GtkTreePath *dest_row,
1428 TnyList *headers = NULL;
1429 TnyHeader *header = NULL;
1430 TnyFolder *folder = NULL;
1431 ModestMailOperation *mail_op = NULL;
1432 GtkTreeIter source_iter, dest_iter;
1434 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1435 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1436 g_return_if_fail (dest_row);
1437 g_return_if_fail (helper);
1440 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1441 gtk_tree_model_get (source_model, &source_iter,
1442 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1444 if (!TNY_IS_HEADER(header)) {
1445 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1450 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1451 gtk_tree_model_get (dest_model, &dest_iter,
1452 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1455 if (!TNY_IS_FOLDER(folder)) {
1456 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1460 /* Transfer message */
1461 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1463 modest_ui_actions_move_folder_error_handler,
1465 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1467 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1468 G_CALLBACK (on_progress_changed), helper);
1470 headers = tny_simple_list_new ();
1471 tny_list_append (headers, G_OBJECT (header));
1472 modest_mail_operation_xfer_msgs (mail_op,
1475 helper->delete_source,
1480 if (G_IS_OBJECT(mail_op))
1481 g_object_unref (G_OBJECT (mail_op));
1482 if (G_IS_OBJECT(header))
1483 g_object_unref (G_OBJECT (header));
1484 if (G_IS_OBJECT(folder))
1485 g_object_unref (G_OBJECT (folder));
1486 if (G_IS_OBJECT(headers))
1487 g_object_unref (headers);
1491 * This function is used by drag_data_received_cb to manage drag and
1492 * drop of a folder, i.e, and drag from the folder view to the same
1496 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1497 GtkTreeModel *dest_model,
1498 GtkTreePath *dest_row,
1499 GtkSelectionData *selection_data,
1502 ModestMailOperation *mail_op = NULL;
1503 GtkTreeIter parent_iter, iter;
1504 TnyFolderStore *parent_folder = NULL;
1505 TnyFolder *folder = NULL;
1507 /* Check if the drag is possible */
1508 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1509 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1511 /* selection_data)) { */
1512 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1514 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1515 gtk_tree_path_free (helper->source_row);
1516 g_slice_free (DndHelper, helper);
1521 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1522 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1523 gtk_tree_model_get (source_model, &parent_iter,
1524 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1525 &parent_folder, -1);
1526 gtk_tree_model_get (source_model, &iter,
1527 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1530 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1531 if (modest_platform_connect_and_wait_if_network_folderstore (NULL, parent_folder) &&
1532 modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1533 /* Do the mail operation */
1534 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1536 modest_ui_actions_move_folder_error_handler,
1538 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1540 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1541 G_CALLBACK (on_progress_changed), helper);
1543 modest_mail_operation_xfer_folder (mail_op,
1546 helper->delete_source,
1550 g_object_unref (G_OBJECT (mail_op));
1554 g_object_unref (G_OBJECT (parent_folder));
1555 g_object_unref (G_OBJECT (folder));
1559 * This function receives the data set by the "drag-data-get" signal
1560 * handler. This information comes within the #GtkSelectionData. This
1561 * function will manage both the drags of folders of the treeview and
1562 * drags of headers of the header view widget.
1565 on_drag_data_received (GtkWidget *widget,
1566 GdkDragContext *context,
1569 GtkSelectionData *selection_data,
1574 GtkWidget *source_widget;
1575 GtkTreeModel *dest_model, *source_model;
1576 GtkTreePath *source_row, *dest_row;
1577 GtkTreeViewDropPosition pos;
1578 gboolean success = FALSE, delete_source = FALSE;
1579 DndHelper *helper = NULL;
1581 /* Do not allow further process */
1582 g_signal_stop_emission_by_name (widget, "drag-data-received");
1583 source_widget = gtk_drag_get_source_widget (context);
1585 /* Get the action */
1586 if (context->action == GDK_ACTION_MOVE) {
1587 delete_source = TRUE;
1589 /* Notify that there is no folder selected. We need to
1590 do this in order to update the headers view (and
1591 its monitors, because when moving, the old folder
1592 won't longer exist. We can not wait for the end of
1593 the operation, because the operation won't start if
1594 the folder is in use */
1595 if (source_widget == widget) {
1596 ModestFolderViewPrivate *priv;
1598 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1599 if (priv->cur_folder_store) {
1600 g_object_unref (priv->cur_folder_store);
1601 priv->cur_folder_store = NULL;
1604 g_signal_emit (G_OBJECT (widget),
1605 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1609 /* Check if the get_data failed */
1610 if (selection_data == NULL || selection_data->length < 0)
1611 gtk_drag_finish (context, success, FALSE, time);
1613 /* Get the models */
1614 gtk_tree_get_row_drag_data (selection_data,
1618 /* Select the destination model */
1619 if (source_widget == widget) {
1620 dest_model = source_model;
1622 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1625 /* Get the path to the destination row. Can not call
1626 gtk_tree_view_get_drag_dest_row() because the source row
1627 is not selected anymore */
1628 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1631 /* Only allow drops IN other rows */
1632 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1633 gtk_drag_finish (context, success, FALSE, time);
1635 /* Create the helper */
1636 helper = g_slice_new0 (DndHelper);
1637 helper->delete_source = delete_source;
1638 helper->source_row = gtk_tree_path_copy (source_row);
1639 helper->context = context;
1640 helper->time = time;
1642 /* Drags from the header view */
1643 if (source_widget != widget) {
1645 drag_and_drop_from_header_view (source_model,
1652 drag_and_drop_from_folder_view (source_model,
1660 gtk_tree_path_free (source_row);
1661 gtk_tree_path_free (dest_row);
1665 * We define a "drag-drop" signal handler because we do not want to
1666 * use the default one, because the default one always calls
1667 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1668 * signal handler, because there we have all the information available
1669 * to know if the dnd was a success or not.
1672 drag_drop_cb (GtkWidget *widget,
1673 GdkDragContext *context,
1681 if (!context->targets)
1684 /* Check if we're dragging a folder row */
1685 target = gtk_drag_dest_find_target (widget, context, NULL);
1687 /* Request the data from the source. */
1688 gtk_drag_get_data(widget, context, target, time);
1694 * This function expands a node of a tree view if it's not expanded
1695 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1696 * does that, so that's why they're here.
1699 expand_row_timeout (gpointer data)
1701 GtkTreeView *tree_view = data;
1702 GtkTreePath *dest_path = NULL;
1703 GtkTreeViewDropPosition pos;
1704 gboolean result = FALSE;
1706 GDK_THREADS_ENTER ();
1708 gtk_tree_view_get_drag_dest_row (tree_view,
1713 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1714 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1715 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1716 gtk_tree_path_free (dest_path);
1720 gtk_tree_path_free (dest_path);
1725 GDK_THREADS_LEAVE ();
1731 * This function is called whenever the pointer is moved over a widget
1732 * while dragging some data. It installs a timeout that will expand a
1733 * node of the treeview if not expanded yet. This function also calls
1734 * gdk_drag_status in order to set the suggested action that will be
1735 * used by the "drag-data-received" signal handler to know if we
1736 * should do a move or just a copy of the data.
1739 on_drag_motion (GtkWidget *widget,
1740 GdkDragContext *context,
1746 GtkTreeViewDropPosition pos;
1747 GtkTreePath *dest_row;
1748 ModestFolderViewPrivate *priv;
1749 GdkDragAction suggested_action;
1750 gboolean valid_location = FALSE;
1752 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1754 if (priv->timer_expander != 0) {
1755 g_source_remove (priv->timer_expander);
1756 priv->timer_expander = 0;
1759 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1764 /* Do not allow drops between folders */
1766 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1767 pos == GTK_TREE_VIEW_DROP_AFTER) {
1768 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1769 gdk_drag_status(context, 0, time);
1770 valid_location = FALSE;
1773 valid_location = TRUE;
1776 /* Expand the selected row after 1/2 second */
1777 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1778 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1779 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1782 /* Select the desired action. By default we pick MOVE */
1783 suggested_action = GDK_ACTION_MOVE;
1785 if (context->actions == GDK_ACTION_COPY)
1786 gdk_drag_status(context, GDK_ACTION_COPY, time);
1787 else if (context->actions == GDK_ACTION_MOVE)
1788 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1789 else if (context->actions & suggested_action)
1790 gdk_drag_status(context, suggested_action, time);
1792 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1796 gtk_tree_path_free (dest_row);
1797 g_signal_stop_emission_by_name (widget, "drag-motion");
1798 return valid_location;
1802 /* Folder view drag types */
1803 const GtkTargetEntry folder_view_drag_types[] =
1805 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1806 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1810 * This function sets the treeview as a source and a target for dnd
1811 * events. It also connects all the requirede signals.
1814 setup_drag_and_drop (GtkTreeView *self)
1816 /* Set up the folder view as a dnd destination. Set only the
1817 highlight flag, otherwise gtk will have a different
1819 gtk_drag_dest_set (GTK_WIDGET (self),
1820 GTK_DEST_DEFAULT_HIGHLIGHT,
1821 folder_view_drag_types,
1822 G_N_ELEMENTS (folder_view_drag_types),
1823 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1825 g_signal_connect (G_OBJECT (self),
1826 "drag_data_received",
1827 G_CALLBACK (on_drag_data_received),
1831 /* Set up the treeview as a dnd source */
1832 gtk_drag_source_set (GTK_WIDGET (self),
1834 folder_view_drag_types,
1835 G_N_ELEMENTS (folder_view_drag_types),
1836 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1838 g_signal_connect (G_OBJECT (self),
1840 G_CALLBACK (on_drag_motion),
1843 g_signal_connect (G_OBJECT (self),
1845 G_CALLBACK (on_drag_data_get),
1848 g_signal_connect (G_OBJECT (self),
1850 G_CALLBACK (drag_drop_cb),
1855 * This function manages the navigation through the folders using the
1856 * keyboard or the hardware keys in the device
1859 on_key_pressed (GtkWidget *self,
1863 GtkTreeSelection *selection;
1865 GtkTreeModel *model;
1866 gboolean retval = FALSE;
1868 /* Up and Down are automatically managed by the treeview */
1869 if (event->keyval == GDK_Return) {
1870 /* Expand/Collapse the selected row */
1871 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1872 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1875 path = gtk_tree_model_get_path (model, &iter);
1877 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1878 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1880 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1881 gtk_tree_path_free (path);
1883 /* No further processing */
1891 * We listen to the changes in the local folder account name key,
1892 * because we want to show the right name in the view. The local
1893 * folder account name corresponds to the device name in the Maemo
1894 * version. We do this because we do not want to query gconf on each
1895 * tree view refresh. It's better to cache it and change whenever
1899 on_configuration_key_changed (ModestConf* conf,
1901 ModestConfEvent event,
1902 ModestFolderView *self)
1904 ModestFolderViewPrivate *priv;
1909 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1910 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1912 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1913 g_free (priv->local_account_name);
1915 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1916 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1918 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1919 MODEST_CONF_DEVICE_NAME, NULL);
1921 /* Force a redraw */
1922 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1923 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1924 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1925 gtk_tree_view_column_queue_resize (tree_column);
1931 modest_folder_view_set_style (ModestFolderView *self,
1932 ModestFolderViewStyle style)
1934 ModestFolderViewPrivate *priv;
1936 g_return_if_fail (self);
1938 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1940 priv->style = style;
1944 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1945 const gchar *account_id)
1947 ModestFolderViewPrivate *priv;
1948 GtkTreeModel *model;
1950 g_return_if_fail (self);
1952 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1954 /* This will be used by the filter_row callback,
1955 * to decided which rows to show: */
1956 if (priv->visible_account_id) {
1957 g_free (priv->visible_account_id);
1958 priv->visible_account_id = NULL;
1961 priv->visible_account_id = g_strdup (account_id);
1964 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1965 if (GTK_IS_TREE_MODEL_FILTER (model))
1966 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1968 /* Save settings to gconf */
1969 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1970 MODEST_CONF_FOLDER_VIEW_KEY);
1974 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1976 ModestFolderViewPrivate *priv;
1978 g_return_val_if_fail (self, NULL);
1980 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1982 return (const gchar *) priv->visible_account_id;
1986 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1990 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1993 gtk_tree_model_get (model, iter,
1994 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1995 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1999 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n",
2000 __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
2003 gboolean result = FALSE;
2004 if (type == TNY_FOLDER_TYPE_INBOX) {
2006 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
2007 /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
2008 * when getting folders from the cache, before connectin, so we do
2009 * an extra check. We could fix this in tinymail, but it's easier
2012 if (strcmp (name, "Inbox") == 0)
2019 *inbox_iter = *iter;
2023 if (gtk_tree_model_iter_children (model, &child, iter)) {
2024 if (find_inbox_iter (model, &child, inbox_iter))
2028 } while (gtk_tree_model_iter_next (model, iter));
2037 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2039 GtkTreeModel *model;
2040 GtkTreeIter iter, inbox_iter;
2041 GtkTreeSelection *sel;
2043 /* /\* Do not set it if the folder view was not painted *\/ */
2044 /* if (!GTK_WIDGET_MAPPED (self)) */
2047 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2051 expand_root_items (self);
2052 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2054 gtk_tree_model_get_iter_first (model, &iter);
2055 gtk_tree_selection_unselect_all (sel);
2057 if (find_inbox_iter (model, &iter, &inbox_iter)) {
2058 gtk_tree_selection_select_iter (sel, &inbox_iter);
2061 gtk_tree_model_get_iter_first (model, &iter);
2062 gtk_tree_selection_select_iter (sel, &iter);
2069 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2074 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2075 TnyFolder* a_folder;
2078 gtk_tree_model_get (model, iter,
2079 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2080 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2081 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2084 g_debug ("===> %s (%p ---- %p)", name, a_folder, folder);
2087 if (folder == a_folder) {
2088 g_object_unref (a_folder);
2089 *folder_iter = *iter;
2092 g_object_unref (a_folder);
2094 if (gtk_tree_model_iter_children (model, &child, iter)) {
2095 if (find_folder_iter (model, &child, folder_iter, folder))
2099 } while (gtk_tree_model_iter_next (model, iter));
2106 on_row_changed_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2107 ModestFolderView *self)
2109 ModestFolderViewPrivate *priv = NULL;
2110 GtkTreeSelection *sel;
2112 if (!MODEST_IS_FOLDER_VIEW(self))
2115 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2117 if (priv->folder_to_select) {
2119 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2122 path = gtk_tree_model_get_path (tree_model, iter);
2123 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2125 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2127 gtk_tree_selection_select_iter (sel, iter);
2128 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2130 gtk_tree_path_free (path);
2133 g_object_unref (priv->folder_to_select);
2134 priv->folder_to_select = NULL;
2140 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2141 gboolean after_change)
2143 GtkTreeModel *model;
2144 GtkTreeIter iter, folder_iter;
2145 GtkTreeSelection *sel;
2146 ModestFolderViewPrivate *priv = NULL;
2148 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2149 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2151 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2154 if (priv->folder_to_select)
2155 g_object_unref(priv->folder_to_select);
2156 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2160 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2165 gtk_tree_model_get_iter_first (model, &iter);
2166 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2169 path = gtk_tree_model_get_path (model, &folder_iter);
2170 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2172 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2173 gtk_tree_selection_select_iter (sel, &folder_iter);
2174 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2176 gtk_tree_path_free (path);
2184 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2186 /* Copy selection */
2187 _clipboard_set_selected_data (folder_view, FALSE);
2191 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2193 ModestFolderViewPrivate *priv = NULL;
2194 GtkTreeModel *model = NULL;
2195 const gchar **hidding = NULL;
2196 guint i, n_selected;
2198 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2199 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2201 /* Copy selection */
2202 if (!_clipboard_set_selected_data (folder_view, TRUE))
2205 /* Get hidding ids */
2206 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2208 /* Clear hidding array created by previous cut operation */
2209 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2211 /* Copy hidding array */
2212 priv->n_selected = n_selected;
2213 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2214 for (i=0; i < n_selected; i++)
2215 priv->hidding_ids[i] = g_strdup(hidding[i]);
2217 /* Hide cut folders */
2218 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2219 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2223 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2226 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2227 priv->show_non_move = show;
2228 modest_folder_view_update_model(folder_view,
2229 TNY_ACCOUNT_STORE(modest_runtime_get_account_store()));
2232 /* Returns FALSE if it did not selected anything */
2234 _clipboard_set_selected_data (ModestFolderView *folder_view,
2237 ModestFolderViewPrivate *priv = NULL;
2238 TnyFolderStore *folder = NULL;
2239 gboolean retval = FALSE;
2241 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2242 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2244 /* Set selected data on clipboard */
2245 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2246 folder = modest_folder_view_get_selected (folder_view);
2248 /* Do not allow to select an account */
2249 if (TNY_IS_FOLDER (folder)) {
2250 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2255 g_object_unref (folder);
2261 _clear_hidding_filter (ModestFolderView *folder_view)
2263 ModestFolderViewPrivate *priv;
2266 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2267 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2269 if (priv->hidding_ids != NULL) {
2270 for (i=0; i < priv->n_selected; i++)
2271 g_free (priv->hidding_ids[i]);
2272 g_free(priv->hidding_ids);