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_removed (TnyAccountStore *self,
76 static void on_account_inserted (TnyAccountStore *self,
80 static gint cmp_rows (GtkTreeModel *tree_model,
85 static gboolean filter_row (GtkTreeModel *model,
89 static gboolean on_key_pressed (GtkWidget *self,
93 static void on_configuration_key_changed (ModestConf* conf,
95 ModestConfEvent event,
96 ModestConfNotificationId notification_id,
97 ModestFolderView *self);
100 static void on_drag_data_get (GtkWidget *widget,
101 GdkDragContext *context,
102 GtkSelectionData *selection_data,
107 static void on_drag_data_received (GtkWidget *widget,
108 GdkDragContext *context,
111 GtkSelectionData *selection_data,
116 static gboolean on_drag_motion (GtkWidget *widget,
117 GdkDragContext *context,
123 static gint expand_row_timeout (gpointer data);
125 static void setup_drag_and_drop (GtkTreeView *self);
127 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
130 static void _clear_hidding_filter (ModestFolderView *folder_view);
132 static void on_row_changed_maybe_select_folder (GtkTreeModel *tree_model,
135 ModestFolderView *self);
138 FOLDER_SELECTION_CHANGED_SIGNAL,
139 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
143 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
144 struct _ModestFolderViewPrivate {
145 TnyAccountStore *account_store;
146 TnyFolderStore *cur_folder_store;
148 TnyFolder *folder_to_select; /* folder to select after the next update */
150 ModestConfNotificationId notification_id;
152 gulong changed_signal;
153 gulong account_inserted_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 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
381 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
382 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
384 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
388 /* Select the number to show: the unread or unsent messages */
389 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
394 /* Use bold font style if there are unread or unset messages */
396 item_name = g_strdup_printf ("%s (%d)", fname, number);
399 item_name = g_strdup (fname);
403 } else if (TNY_IS_ACCOUNT (instance)) {
404 /* If it's a server account */
405 if (modest_tny_account_is_virtual_local_folders (
406 TNY_ACCOUNT (instance))) {
407 item_name = g_strdup (priv->local_account_name);
409 } else if (modest_tny_account_is_memory_card_account (
410 TNY_ACCOUNT (instance))) {
411 /* fname is only correct when the items are first
412 * added to the model, not when the account is
413 * changed later, so get the name from the account
415 item_name = g_strdup (tny_account_get_name (
416 TNY_ACCOUNT (instance)));
419 item_name = g_strdup (fname);
425 item_name = g_strdup ("unknown");
427 if (item_name && item_weight) {
428 /* Set the name in the treeview cell: */
429 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
431 /* Notify display name observers */
432 /* TODO: What listens for this signal, and how can it use only the new name? */
433 if (G_OBJECT (priv->cur_folder_store) == instance) {
434 g_signal_emit (G_OBJECT(self),
435 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));
465 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
466 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
468 GObject *rendobj = NULL, *instance = NULL;
469 GdkPixbuf *pixbuf = NULL;
470 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
471 const gchar *account_id = NULL;
472 gboolean has_children;
474 rendobj = G_OBJECT(renderer);
475 gtk_tree_model_get (tree_model, iter,
476 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
477 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
479 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
484 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
485 /* We include the MERGE type here because it's used to create
486 the local OUTBOX folder */
487 if (type == TNY_FOLDER_TYPE_NORMAL ||
488 type == TNY_FOLDER_TYPE_UNKNOWN) {
489 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
493 case TNY_FOLDER_TYPE_ROOT:
494 if (TNY_IS_ACCOUNT (instance)) {
496 if (modest_tny_account_is_virtual_local_folders (
497 TNY_ACCOUNT (instance))) {
498 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
501 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
503 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
504 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
506 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
510 case TNY_FOLDER_TYPE_INBOX:
511 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
513 case TNY_FOLDER_TYPE_OUTBOX:
514 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
516 case TNY_FOLDER_TYPE_JUNK:
517 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
519 case TNY_FOLDER_TYPE_SENT:
520 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
522 case TNY_FOLDER_TYPE_TRASH:
523 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
525 case TNY_FOLDER_TYPE_DRAFTS:
526 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
528 case TNY_FOLDER_TYPE_NORMAL:
530 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
534 g_object_unref (G_OBJECT (instance));
537 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
538 if (has_children && (pixbuf != NULL)) {
539 GdkPixbuf *open_pixbuf, *closed_pixbuf;
540 GdkPixbuf *open_emblem, *closed_emblem;
541 open_pixbuf = gdk_pixbuf_copy (pixbuf);
542 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
543 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
544 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
547 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
548 MIN (gdk_pixbuf_get_width (open_emblem),
549 gdk_pixbuf_get_width (open_pixbuf)),
550 MIN (gdk_pixbuf_get_height (open_emblem),
551 gdk_pixbuf_get_height (open_pixbuf)),
552 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
553 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
554 g_object_unref (open_emblem);
557 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
558 MIN (gdk_pixbuf_get_width (closed_emblem),
559 gdk_pixbuf_get_width (closed_pixbuf)),
560 MIN (gdk_pixbuf_get_height (closed_emblem),
561 gdk_pixbuf_get_height (closed_pixbuf)),
562 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
563 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
564 g_object_unref (closed_emblem);
567 g_object_unref (closed_pixbuf);
569 g_object_unref (open_pixbuf);
573 g_object_unref (pixbuf);
577 add_columns (GtkWidget *treeview)
579 GtkTreeViewColumn *column;
580 GtkCellRenderer *renderer;
581 GtkTreeSelection *sel;
584 column = gtk_tree_view_column_new ();
586 /* Set icon and text render function */
587 renderer = gtk_cell_renderer_pixbuf_new();
588 gtk_tree_view_column_pack_start (column, renderer, FALSE);
589 gtk_tree_view_column_set_cell_data_func(column, renderer,
590 icon_cell_data, treeview, NULL);
592 renderer = gtk_cell_renderer_text_new();
593 gtk_tree_view_column_pack_start (column, renderer, FALSE);
594 gtk_tree_view_column_set_cell_data_func(column, renderer,
595 text_cell_data, treeview, NULL);
597 /* Set selection mode */
598 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
599 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
601 /* Set treeview appearance */
602 gtk_tree_view_column_set_spacing (column, 2);
603 gtk_tree_view_column_set_resizable (column, TRUE);
604 gtk_tree_view_column_set_fixed_width (column, TRUE);
605 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
606 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
609 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
613 modest_folder_view_init (ModestFolderView *obj)
615 ModestFolderViewPrivate *priv;
618 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
620 priv->timer_expander = 0;
621 priv->account_store = NULL;
623 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
624 priv->cur_folder_store = NULL;
625 priv->visible_account_id = NULL;
626 priv->folder_to_select = NULL;
628 /* Initialize the local account name */
629 conf = modest_runtime_get_conf();
630 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
632 /* Init email clipboard */
633 priv->clipboard = modest_runtime_get_email_clipboard ();
634 priv->hidding_ids = NULL;
635 priv->n_selected = 0;
636 priv->reselect = FALSE;
637 priv->show_non_move = TRUE;
640 add_columns (GTK_WIDGET (obj));
642 /* Setup drag and drop */
643 setup_drag_and_drop (GTK_TREE_VIEW(obj));
645 /* Connect signals */
646 g_signal_connect (G_OBJECT (obj),
648 G_CALLBACK (on_key_pressed), NULL);
651 * Track changes in the local account name (in the device it
652 * will be the device name)
654 priv->notification_id = modest_conf_listen_to_namespace (conf,
655 MODEST_CONF_NAMESPACE);
656 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
658 G_CALLBACK(on_configuration_key_changed),
663 tny_account_store_view_init (gpointer g, gpointer iface_data)
665 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
667 klass->set_account_store_func = modest_folder_view_set_account_store;
673 modest_folder_view_finalize (GObject *obj)
675 ModestFolderViewPrivate *priv;
676 GtkTreeSelection *sel;
678 g_return_if_fail (obj);
680 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
682 if (priv->notification_id) {
683 modest_conf_forget_namespace (modest_runtime_get_conf (),
684 MODEST_CONF_NAMESPACE,
685 priv->notification_id);
688 if (priv->timer_expander != 0) {
689 g_source_remove (priv->timer_expander);
690 priv->timer_expander = 0;
693 if (priv->account_store) {
694 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
695 priv->account_inserted_signal);
696 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
697 priv->account_removed_signal);
698 g_object_unref (G_OBJECT(priv->account_store));
699 priv->account_store = NULL;
703 g_object_unref (G_OBJECT (priv->query));
707 if (priv->folder_to_select) {
708 g_object_unref (G_OBJECT(priv->folder_to_select));
709 priv->folder_to_select = NULL;
712 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
714 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
716 g_free (priv->local_account_name);
717 g_free (priv->visible_account_id);
719 if (priv->conf_key_signal) {
720 g_signal_handler_disconnect (modest_runtime_get_conf (),
721 priv->conf_key_signal);
722 priv->conf_key_signal = 0;
725 if (priv->cur_folder_store) {
726 if (TNY_IS_FOLDER(priv->cur_folder_store))
727 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
728 /* FALSE --> expunge the message */
730 g_object_unref (priv->cur_folder_store);
731 priv->cur_folder_store = NULL;
734 /* Clear hidding array created by cut operation */
735 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
737 G_OBJECT_CLASS(parent_class)->finalize (obj);
742 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
744 ModestFolderViewPrivate *priv;
747 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
748 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
750 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
751 device = tny_account_store_get_device (account_store);
753 if (G_UNLIKELY (priv->account_store)) {
755 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
756 priv->account_inserted_signal))
757 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
758 priv->account_inserted_signal);
759 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
760 priv->account_removed_signal))
761 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
762 priv->account_removed_signal);
764 g_object_unref (G_OBJECT (priv->account_store));
767 priv->account_store = g_object_ref (G_OBJECT (account_store));
769 priv->account_removed_signal =
770 g_signal_connect (G_OBJECT(account_store), "account_removed",
771 G_CALLBACK (on_account_removed), self);
773 priv->account_inserted_signal =
774 g_signal_connect (G_OBJECT(account_store), "account_inserted",
775 G_CALLBACK (on_account_inserted), self);
778 /* g_signal_connect (G_OBJECT(account_store), "connecting_finished", */
779 /* G_CALLBACK (on_accounts_reloaded), self); */
781 /* on_accounts_reloaded (account_store, (gpointer ) self); */
783 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
785 g_object_unref (G_OBJECT (device));
789 on_account_inserted (TnyAccountStore *account_store,
793 ModestFolderViewPrivate *priv;
794 GtkTreeModel *sort_model, *filter_model;
796 /* Ignore transport account insertions, we're not showing them
797 in the folder view */
798 if (TNY_IS_TRANSPORT_ACCOUNT (account))
801 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
803 /* If we're adding a new account, and there is no previous
804 one, we need to select the visible server account */
805 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
806 !priv->visible_account_id)
807 modest_widget_memory_restore (modest_runtime_get_conf(),
808 G_OBJECT (user_data),
809 MODEST_CONF_FOLDER_VIEW_KEY);
811 /* Get the inner model */
812 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
813 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
815 /* Insert the account in the model */
816 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
821 on_account_removed (TnyAccountStore *account_store,
825 ModestFolderView *self = NULL;
826 ModestFolderViewPrivate *priv;
827 GtkTreeModel *sort_model, *filter_model;
829 /* Ignore transport account removals, we're not showing them
830 in the folder view */
831 if (TNY_IS_TRANSPORT_ACCOUNT (account))
834 g_print ("--------------------- FOLDER ---------------\n");
836 self = MODEST_FOLDER_VIEW (user_data);
837 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
839 /* TODO: invalidate the cur_folder_* and folder_to_select things */
841 /* Remove the account from the model */
842 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
843 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
844 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
847 /* If the removed account is the currently viewed one then
848 clear the configuration value. The new visible account will be the default account */
849 if (priv->visible_account_id &&
850 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
852 /* Clear the current visible account_id */
853 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
855 /* Call the restore method, this will set the new visible account */
856 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
857 MODEST_CONF_FOLDER_VIEW_KEY);
860 /* Select the INBOX */
861 modest_folder_view_select_first_inbox_or_local (self);
865 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
867 GtkTreeViewColumn *col;
869 g_return_if_fail (self);
871 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
873 g_printerr ("modest: failed get column for title\n");
877 gtk_tree_view_column_set_title (col, title);
878 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
883 modest_folder_view_on_map (ModestFolderView *self,
884 GdkEventExpose *event,
887 ModestFolderViewPrivate *priv;
889 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
891 /* This won't happen often */
892 if (G_UNLIKELY (priv->reselect)) {
893 /* Select the first inbox or the local account if not found */
895 /* TODO: this could cause a lock at startup, so we
896 comment it for the moment. We know that this will
897 be a bug, because the INBOX is not selected, but we
898 need to rewrite some parts of Modest to avoid the
899 deathlock situation */
900 /* TODO: check if this is still the case */
901 priv->reselect = FALSE;
902 modest_folder_view_select_first_inbox_or_local (self);
903 /* Notify the display name observers */
904 g_signal_emit (G_OBJECT(self),
905 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
912 modest_folder_view_new (TnyFolderStoreQuery *query)
915 ModestFolderViewPrivate *priv;
916 GtkTreeSelection *sel;
918 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
919 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
922 priv->query = g_object_ref (query);
924 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
925 priv->changed_signal = g_signal_connect (sel, "changed",
926 G_CALLBACK (on_selection_changed), self);
928 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
930 return GTK_WIDGET(self);
933 /* this feels dirty; any other way to expand all the root items? */
935 expand_root_items (ModestFolderView *self)
938 path = gtk_tree_path_new_first ();
940 /* all folders should have child items, so.. */
941 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
942 gtk_tree_path_next (path);
944 gtk_tree_path_free (path);
948 * We use this function to implement the
949 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
950 * account in this case, and the local folders.
953 filter_row (GtkTreeModel *model,
957 ModestFolderViewPrivate *priv;
958 gboolean retval = TRUE;
959 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
960 GObject *instance = NULL;
961 const gchar *id = NULL;
963 gboolean found = FALSE;
964 gboolean cleared = FALSE;
966 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
967 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
969 gtk_tree_model_get (model, iter,
970 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
971 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
974 /* Do not show if there is no instance, this could indeed
975 happen when the model is being modified while it's being
976 drawn. This could occur for example when moving folders
981 if (type == TNY_FOLDER_TYPE_ROOT) {
982 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
983 account instead of a folder. */
984 if (TNY_IS_ACCOUNT (instance)) {
985 TnyAccount *acc = TNY_ACCOUNT (instance);
986 const gchar *account_id = tny_account_get_id (acc);
988 /* If it isn't a special folder,
989 * don't show it unless it is the visible account: */
990 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
991 !modest_tny_account_is_virtual_local_folders (acc) &&
992 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
994 /* Show only the visible account id */
995 if (priv->visible_account_id) {
996 if (strcmp (account_id, priv->visible_account_id))
1003 /* Never show these to the user. They are merged into one folder
1004 * in the local-folders account instead: */
1005 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1010 /* Check hiding (if necessary) */
1011 cleared = modest_email_clipboard_cleared (priv->clipboard);
1012 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1013 id = tny_folder_get_id (TNY_FOLDER(instance));
1014 if (priv->hidding_ids != NULL)
1015 for (i=0; i < priv->n_selected && !found; i++)
1016 if (priv->hidding_ids[i] != NULL && id != NULL)
1017 found = (!strcmp (priv->hidding_ids[i], id));
1023 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1024 folder as no message can be move there according to UI specs */
1025 if (!priv->show_non_move)
1029 case TNY_FOLDER_TYPE_OUTBOX:
1030 case TNY_FOLDER_TYPE_SENT:
1031 case TNY_FOLDER_TYPE_DRAFTS:
1034 case TNY_FOLDER_TYPE_UNKNOWN:
1035 case TNY_FOLDER_TYPE_NORMAL:
1036 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1037 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1038 || type == TNY_FOLDER_TYPE_DRAFTS)
1049 g_object_unref (instance);
1056 modest_folder_view_update_model (ModestFolderView *self,
1057 TnyAccountStore *account_store)
1059 ModestFolderViewPrivate *priv;
1060 GtkTreeModel *model /* , *old_model */;
1061 /* TnyAccount *local_account; */
1062 TnyList *model_as_list;
1064 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1065 g_return_val_if_fail (account_store, FALSE);
1067 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1069 /* Notify that there is no folder selected */
1070 g_signal_emit (G_OBJECT(self),
1071 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1073 if (priv->cur_folder_store) {
1074 g_object_unref (priv->cur_folder_store);
1075 priv->cur_folder_store = NULL;
1078 /* FIXME: the local accounts are not shown when the query
1079 selects only the subscribed folders. */
1080 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1081 model = tny_gtk_folder_store_tree_model_new (NULL);
1083 /* Deal with the model via its TnyList Interface,
1084 * filling the TnyList via a get_accounts() call: */
1085 model_as_list = TNY_LIST(model);
1087 /* Get the accounts: */
1088 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1090 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1091 g_object_unref (model_as_list);
1092 model_as_list = NULL;
1094 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1096 sortable = gtk_tree_model_sort_new_with_model (model);
1097 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1098 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1099 GTK_SORT_ASCENDING);
1100 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1101 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1102 cmp_rows, NULL, NULL);
1104 /* Create filter model */
1105 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1106 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1112 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1113 g_signal_connect (G_OBJECT(filter_model), "row-changed",
1114 (GCallback)on_row_changed_maybe_select_folder, self);
1115 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1116 (GCallback)on_row_changed_maybe_select_folder, self);
1119 g_object_unref (model);
1120 g_object_unref (filter_model);
1121 g_object_unref (sortable);
1123 /* Force a reselection of the INBOX next time the widget is shown */
1124 priv->reselect = TRUE;
1131 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1133 GtkTreeModel *model;
1134 TnyFolderStore *folder = NULL;
1136 ModestFolderView *tree_view;
1137 ModestFolderViewPrivate *priv;
1139 g_return_if_fail (sel);
1140 g_return_if_fail (user_data);
1142 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1144 if(!gtk_tree_selection_get_selected (sel, &model, &iter))
1147 /* Notify the display name observers */
1148 g_signal_emit (G_OBJECT(user_data),
1149 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1152 tree_view = MODEST_FOLDER_VIEW (user_data);
1153 gtk_tree_model_get (model, &iter,
1154 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1157 /* If the folder is the same do not notify */
1158 if (priv->cur_folder_store == folder && folder) {
1159 g_object_unref (folder);
1163 /* Current folder was unselected */
1164 if (priv->cur_folder_store) {
1165 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1166 priv->cur_folder_store, FALSE);
1168 if (TNY_IS_FOLDER(priv->cur_folder_store))
1169 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1170 FALSE, NULL, NULL, NULL);
1171 /* FALSE --> don't expunge the messages */
1173 g_object_unref (priv->cur_folder_store);
1174 priv->cur_folder_store = NULL;
1177 /* New current references */
1178 priv->cur_folder_store = folder;
1180 /* New folder has been selected */
1181 g_signal_emit (G_OBJECT(tree_view),
1182 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1183 0, priv->cur_folder_store, TRUE);
1187 modest_folder_view_get_selected (ModestFolderView *self)
1189 ModestFolderViewPrivate *priv;
1191 g_return_val_if_fail (self, NULL);
1193 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1194 if (priv->cur_folder_store)
1195 g_object_ref (priv->cur_folder_store);
1197 return priv->cur_folder_store;
1201 get_cmp_rows_type_pos (GObject *folder)
1203 /* Remote accounts -> Local account -> MMC account .*/
1206 if (TNY_IS_ACCOUNT (folder) &&
1207 modest_tny_account_is_virtual_local_folders (
1208 TNY_ACCOUNT (folder))) {
1210 } else if (TNY_IS_ACCOUNT (folder)) {
1211 TnyAccount *account = TNY_ACCOUNT (folder);
1212 const gchar *account_id = tny_account_get_id (account);
1213 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1219 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1220 return -1; /* Should never happen */
1225 get_cmp_subfolder_type_pos (TnyFolderType t)
1227 /* Inbox, Outbox, Drafts, Sent, User */
1231 case TNY_FOLDER_TYPE_INBOX:
1234 case TNY_FOLDER_TYPE_OUTBOX:
1237 case TNY_FOLDER_TYPE_DRAFTS:
1240 case TNY_FOLDER_TYPE_SENT:
1249 * This function orders the mail accounts according to these rules:
1250 * 1st - remote accounts
1251 * 2nd - local account
1255 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1259 gchar *name1 = NULL;
1260 gchar *name2 = NULL;
1261 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1262 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1263 GObject *folder1 = NULL;
1264 GObject *folder2 = NULL;
1266 gtk_tree_model_get (tree_model, iter1,
1267 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1268 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1269 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1271 gtk_tree_model_get (tree_model, iter2,
1272 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1273 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1274 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1277 /* Return if we get no folder. This could happen when folder
1278 operations are happening. The model is updated after the
1279 folder copy/move actually occurs, so there could be
1280 situations where the model to be drawn is not correct */
1281 if (!folder1 || !folder2)
1284 if (type == TNY_FOLDER_TYPE_ROOT) {
1285 /* Compare the types, so that
1286 * Remote accounts -> Local account -> MMC account .*/
1287 const gint pos1 = get_cmp_rows_type_pos (folder1);
1288 const gint pos2 = get_cmp_rows_type_pos (folder2);
1289 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1290 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1293 else if (pos1 > pos2)
1296 /* Compare items of the same type: */
1298 TnyAccount *account1 = NULL;
1299 if (TNY_IS_ACCOUNT (folder1))
1300 account1 = TNY_ACCOUNT (folder1);
1302 TnyAccount *account2 = NULL;
1303 if (TNY_IS_ACCOUNT (folder2))
1304 account2 = TNY_ACCOUNT (folder2);
1306 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1307 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1309 if (!account_id && !account_id2) {
1311 } else if (!account_id) {
1313 } else if (!account_id2) {
1315 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1318 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1322 gint cmp1 = 0, cmp2 = 0;
1323 /* get the parent to know if it's a local folder */
1326 gboolean has_parent;
1327 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1329 GObject *parent_folder;
1330 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1331 gtk_tree_model_get (tree_model, &parent,
1332 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1333 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1335 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1336 TNY_IS_ACCOUNT (parent_folder) &&
1337 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1338 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1339 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1341 g_object_unref (parent_folder);
1344 /* if they are not local folders */
1346 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1347 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1351 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1353 cmp = (cmp1 - cmp2);
1358 g_object_unref(G_OBJECT(folder1));
1360 g_object_unref(G_OBJECT(folder2));
1368 /*****************************************************************************/
1369 /* DRAG and DROP stuff */
1370 /*****************************************************************************/
1373 * This function fills the #GtkSelectionData with the row and the
1374 * model that has been dragged. It's called when this widget is a
1375 * source for dnd after the event drop happened
1378 on_drag_data_get (GtkWidget *widget,
1379 GdkDragContext *context,
1380 GtkSelectionData *selection_data,
1385 GtkTreeSelection *selection;
1386 GtkTreeModel *model;
1388 GtkTreePath *source_row;
1390 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1391 gtk_tree_selection_get_selected (selection, &model, &iter);
1392 source_row = gtk_tree_model_get_path (model, &iter);
1394 gtk_tree_set_row_drag_data (selection_data,
1398 gtk_tree_path_free (source_row);
1401 typedef struct _DndHelper {
1402 gboolean delete_source;
1403 GtkTreePath *source_row;
1404 GdkDragContext *context;
1410 * This function is the callback of the
1411 * modest_mail_operation_xfer_msgs () and
1412 * modest_mail_operation_xfer_folder() calls. We check here if the
1413 * message/folder was correctly asynchronously transferred. The reason
1414 * to use the same callback is that the code is the same, it only has
1415 * to check that the operation went fine and then finalize the drag
1419 on_progress_changed (ModestMailOperation *mail_op,
1420 ModestMailOperationState *state,
1426 helper = (DndHelper *) user_data;
1428 if (!state->finished)
1431 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1437 /* Notify the drag source. Never call delete, the monitor will
1438 do the job if needed */
1439 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1441 /* Free the helper */
1442 gtk_tree_path_free (helper->source_row);
1443 g_slice_free (DndHelper, helper);
1447 /* get the folder for the row the treepath refers to. */
1448 /* folder must be unref'd */
1450 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1453 TnyFolder *folder = NULL;
1455 if (gtk_tree_model_get_iter (model,&iter, path))
1456 gtk_tree_model_get (model, &iter,
1457 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1463 * This function is used by drag_data_received_cb to manage drag and
1464 * drop of a header, i.e, and drag from the header view to the folder
1468 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1469 GtkTreeModel *dest_model,
1470 GtkTreePath *dest_row,
1473 TnyList *headers = NULL;
1474 TnyHeader *header = NULL;
1475 TnyFolder *folder = NULL;
1476 ModestMailOperation *mail_op = NULL;
1477 GtkTreeIter source_iter;
1479 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1480 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1481 g_return_if_fail (dest_row);
1482 g_return_if_fail (helper);
1485 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1486 gtk_tree_model_get (source_model, &source_iter,
1487 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1489 if (!TNY_IS_HEADER(header)) {
1490 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1495 folder = tree_path_to_folder (dest_model, dest_row);
1496 if (!TNY_IS_FOLDER(folder)) {
1497 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1500 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1501 g_debug ("folder rules: cannot write to that folder");
1506 /* Transfer message */
1507 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1509 modest_ui_actions_move_folder_error_handler,
1511 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1513 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1514 G_CALLBACK (on_progress_changed), helper);
1516 headers = tny_simple_list_new ();
1517 tny_list_append (headers, G_OBJECT (header));
1518 modest_mail_operation_xfer_msgs (mail_op,
1521 helper->delete_source,
1526 if (G_IS_OBJECT(mail_op))
1527 g_object_unref (G_OBJECT (mail_op));
1528 if (G_IS_OBJECT(header))
1529 g_object_unref (G_OBJECT (header));
1530 if (G_IS_OBJECT(folder))
1531 g_object_unref (G_OBJECT (folder));
1532 if (G_IS_OBJECT(headers))
1533 g_object_unref (headers);
1537 * This function is used by drag_data_received_cb to manage drag and
1538 * drop of a folder, i.e, and drag from the folder view to the same
1542 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1543 GtkTreeModel *dest_model,
1544 GtkTreePath *dest_row,
1545 GtkSelectionData *selection_data,
1548 ModestMailOperation *mail_op = NULL;
1549 GtkTreeIter parent_iter, iter;
1550 TnyFolderStore *parent_folder = NULL;
1551 TnyFolder *folder = NULL;
1552 gboolean forbidden = TRUE;
1554 /* check the folder rules for the destination */
1555 folder = tree_path_to_folder (dest_model, dest_row);
1557 ModestTnyFolderRules rules =
1558 modest_tny_folder_get_rules (folder);
1559 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1561 g_debug ("folder rules: cannot write to that folder");
1562 g_object_unref (folder);
1566 /* check the folder rules for the source */
1567 folder = tree_path_to_folder (source_model, helper->source_row);
1569 ModestTnyFolderRules rules =
1570 modest_tny_folder_get_rules (folder);
1571 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1573 g_debug ("folder rules: cannot move that folder");
1574 g_object_unref (folder);
1579 /* Check if the drag is possible */
1580 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1582 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1583 gtk_tree_path_free (helper->source_row);
1584 g_slice_free (DndHelper, helper);
1589 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1590 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1591 gtk_tree_model_get (source_model, &parent_iter,
1592 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1593 &parent_folder, -1);
1594 gtk_tree_model_get (source_model, &iter,
1595 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1598 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1599 if (modest_platform_connect_and_wait_if_network_folderstore (NULL, parent_folder) &&
1600 modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1601 /* Do the mail operation */
1602 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1604 modest_ui_actions_move_folder_error_handler,
1606 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1608 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1609 G_CALLBACK (on_progress_changed), helper);
1611 modest_mail_operation_xfer_folder (mail_op,
1614 helper->delete_source,
1618 g_object_unref (G_OBJECT (mail_op));
1622 g_object_unref (G_OBJECT (parent_folder));
1623 g_object_unref (G_OBJECT (folder));
1627 * This function receives the data set by the "drag-data-get" signal
1628 * handler. This information comes within the #GtkSelectionData. This
1629 * function will manage both the drags of folders of the treeview and
1630 * drags of headers of the header view widget.
1633 on_drag_data_received (GtkWidget *widget,
1634 GdkDragContext *context,
1637 GtkSelectionData *selection_data,
1642 GtkWidget *source_widget;
1643 GtkTreeModel *dest_model, *source_model;
1644 GtkTreePath *source_row, *dest_row;
1645 GtkTreeViewDropPosition pos;
1646 gboolean success = FALSE, delete_source = FALSE;
1647 DndHelper *helper = NULL;
1649 /* Do not allow further process */
1650 g_signal_stop_emission_by_name (widget, "drag-data-received");
1651 source_widget = gtk_drag_get_source_widget (context);
1653 /* Get the action */
1654 if (context->action == GDK_ACTION_MOVE) {
1655 delete_source = TRUE;
1657 /* Notify that there is no folder selected. We need to
1658 do this in order to update the headers view (and
1659 its monitors, because when moving, the old folder
1660 won't longer exist. We can not wait for the end of
1661 the operation, because the operation won't start if
1662 the folder is in use */
1663 if (source_widget == widget) {
1664 ModestFolderViewPrivate *priv;
1666 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1667 if (priv->cur_folder_store) {
1668 g_object_unref (priv->cur_folder_store);
1669 priv->cur_folder_store = NULL;
1672 g_signal_emit (G_OBJECT (widget),
1673 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1677 /* Check if the get_data failed */
1678 if (selection_data == NULL || selection_data->length < 0)
1679 gtk_drag_finish (context, success, FALSE, time);
1681 /* Get the models */
1682 gtk_tree_get_row_drag_data (selection_data,
1686 /* Select the destination model */
1687 if (source_widget == widget) {
1688 dest_model = source_model;
1690 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1693 /* Get the path to the destination row. Can not call
1694 gtk_tree_view_get_drag_dest_row() because the source row
1695 is not selected anymore */
1696 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1699 /* Only allow drops IN other rows */
1700 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1701 gtk_drag_finish (context, success, FALSE, time);
1703 /* Create the helper */
1704 helper = g_slice_new0 (DndHelper);
1705 helper->delete_source = delete_source;
1706 helper->source_row = gtk_tree_path_copy (source_row);
1707 helper->context = context;
1708 helper->time = time;
1710 /* Drags from the header view */
1711 if (source_widget != widget) {
1713 drag_and_drop_from_header_view (source_model,
1720 drag_and_drop_from_folder_view (source_model,
1728 gtk_tree_path_free (source_row);
1729 gtk_tree_path_free (dest_row);
1733 * We define a "drag-drop" signal handler because we do not want to
1734 * use the default one, because the default one always calls
1735 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1736 * signal handler, because there we have all the information available
1737 * to know if the dnd was a success or not.
1740 drag_drop_cb (GtkWidget *widget,
1741 GdkDragContext *context,
1749 if (!context->targets)
1752 /* Check if we're dragging a folder row */
1753 target = gtk_drag_dest_find_target (widget, context, NULL);
1755 /* Request the data from the source. */
1756 gtk_drag_get_data(widget, context, target, time);
1762 * This function expands a node of a tree view if it's not expanded
1763 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1764 * does that, so that's why they're here.
1767 expand_row_timeout (gpointer data)
1769 GtkTreeView *tree_view = data;
1770 GtkTreePath *dest_path = NULL;
1771 GtkTreeViewDropPosition pos;
1772 gboolean result = FALSE;
1774 GDK_THREADS_ENTER ();
1776 gtk_tree_view_get_drag_dest_row (tree_view,
1781 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1782 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1783 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1784 gtk_tree_path_free (dest_path);
1788 gtk_tree_path_free (dest_path);
1793 GDK_THREADS_LEAVE ();
1799 * This function is called whenever the pointer is moved over a widget
1800 * while dragging some data. It installs a timeout that will expand a
1801 * node of the treeview if not expanded yet. This function also calls
1802 * gdk_drag_status in order to set the suggested action that will be
1803 * used by the "drag-data-received" signal handler to know if we
1804 * should do a move or just a copy of the data.
1807 on_drag_motion (GtkWidget *widget,
1808 GdkDragContext *context,
1814 GtkTreeViewDropPosition pos;
1815 GtkTreePath *dest_row;
1816 ModestFolderViewPrivate *priv;
1817 GdkDragAction suggested_action;
1818 gboolean valid_location = FALSE;
1820 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1822 if (priv->timer_expander != 0) {
1823 g_source_remove (priv->timer_expander);
1824 priv->timer_expander = 0;
1827 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1832 /* Do not allow drops between folders */
1834 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1835 pos == GTK_TREE_VIEW_DROP_AFTER) {
1836 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1837 gdk_drag_status(context, 0, time);
1838 valid_location = FALSE;
1841 valid_location = TRUE;
1844 /* Expand the selected row after 1/2 second */
1845 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1846 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1847 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1850 /* Select the desired action. By default we pick MOVE */
1851 suggested_action = GDK_ACTION_MOVE;
1853 if (context->actions == GDK_ACTION_COPY)
1854 gdk_drag_status(context, GDK_ACTION_COPY, time);
1855 else if (context->actions == GDK_ACTION_MOVE)
1856 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1857 else if (context->actions & suggested_action)
1858 gdk_drag_status(context, suggested_action, time);
1860 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1864 gtk_tree_path_free (dest_row);
1865 g_signal_stop_emission_by_name (widget, "drag-motion");
1866 return valid_location;
1870 /* Folder view drag types */
1871 const GtkTargetEntry folder_view_drag_types[] =
1873 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1874 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1878 * This function sets the treeview as a source and a target for dnd
1879 * events. It also connects all the requirede signals.
1882 setup_drag_and_drop (GtkTreeView *self)
1884 /* Set up the folder view as a dnd destination. Set only the
1885 highlight flag, otherwise gtk will have a different
1887 gtk_drag_dest_set (GTK_WIDGET (self),
1888 GTK_DEST_DEFAULT_HIGHLIGHT,
1889 folder_view_drag_types,
1890 G_N_ELEMENTS (folder_view_drag_types),
1891 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1893 g_signal_connect (G_OBJECT (self),
1894 "drag_data_received",
1895 G_CALLBACK (on_drag_data_received),
1899 /* Set up the treeview as a dnd source */
1900 gtk_drag_source_set (GTK_WIDGET (self),
1902 folder_view_drag_types,
1903 G_N_ELEMENTS (folder_view_drag_types),
1904 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1906 g_signal_connect (G_OBJECT (self),
1908 G_CALLBACK (on_drag_motion),
1911 g_signal_connect (G_OBJECT (self),
1913 G_CALLBACK (on_drag_data_get),
1916 g_signal_connect (G_OBJECT (self),
1918 G_CALLBACK (drag_drop_cb),
1923 * This function manages the navigation through the folders using the
1924 * keyboard or the hardware keys in the device
1927 on_key_pressed (GtkWidget *self,
1931 GtkTreeSelection *selection;
1933 GtkTreeModel *model;
1934 gboolean retval = FALSE;
1936 /* Up and Down are automatically managed by the treeview */
1937 if (event->keyval == GDK_Return) {
1938 /* Expand/Collapse the selected row */
1939 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1940 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1943 path = gtk_tree_model_get_path (model, &iter);
1945 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1946 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1948 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1949 gtk_tree_path_free (path);
1951 /* No further processing */
1959 * We listen to the changes in the local folder account name key,
1960 * because we want to show the right name in the view. The local
1961 * folder account name corresponds to the device name in the Maemo
1962 * version. We do this because we do not want to query gconf on each
1963 * tree view refresh. It's better to cache it and change whenever
1967 on_configuration_key_changed (ModestConf* conf,
1969 ModestConfEvent event,
1970 ModestConfNotificationId id,
1971 ModestFolderView *self)
1973 ModestFolderViewPrivate *priv;
1976 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1977 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1979 /* Do not listen for changes in other namespaces */
1980 if (priv->notification_id != id)
1983 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1984 g_free (priv->local_account_name);
1986 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1987 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1989 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1990 MODEST_CONF_DEVICE_NAME, NULL);
1992 /* Force a redraw */
1993 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1994 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1995 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1996 gtk_tree_view_column_queue_resize (tree_column);
2002 modest_folder_view_set_style (ModestFolderView *self,
2003 ModestFolderViewStyle style)
2005 ModestFolderViewPrivate *priv;
2007 g_return_if_fail (self);
2009 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2011 priv->style = style;
2015 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2016 const gchar *account_id)
2018 ModestFolderViewPrivate *priv;
2019 GtkTreeModel *model;
2021 g_return_if_fail (self);
2023 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2025 /* This will be used by the filter_row callback,
2026 * to decided which rows to show: */
2027 if (priv->visible_account_id) {
2028 g_free (priv->visible_account_id);
2029 priv->visible_account_id = NULL;
2032 priv->visible_account_id = g_strdup (account_id);
2035 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2036 if (GTK_IS_TREE_MODEL_FILTER (model))
2037 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2039 /* Save settings to gconf */
2040 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2041 MODEST_CONF_FOLDER_VIEW_KEY);
2045 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2047 ModestFolderViewPrivate *priv;
2049 g_return_val_if_fail (self, NULL);
2051 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2053 return (const gchar *) priv->visible_account_id;
2057 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2061 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2063 gtk_tree_model_get (model, iter,
2064 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2067 gboolean result = FALSE;
2068 if (type == TNY_FOLDER_TYPE_INBOX) {
2072 *inbox_iter = *iter;
2076 if (gtk_tree_model_iter_children (model, &child, iter)) {
2077 if (find_inbox_iter (model, &child, inbox_iter))
2081 } while (gtk_tree_model_iter_next (model, iter));
2090 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2092 GtkTreeModel *model;
2093 GtkTreeIter iter, inbox_iter;
2094 GtkTreeSelection *sel;
2095 GtkTreePath *path = NULL;
2097 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2101 expand_root_items (self);
2102 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2104 gtk_tree_model_get_iter_first (model, &iter);
2106 if (find_inbox_iter (model, &iter, &inbox_iter))
2107 path = gtk_tree_model_get_path (model, &inbox_iter);
2109 path = gtk_tree_path_new_first ();
2111 /* Select the row and free */
2112 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2113 gtk_tree_path_free (path);
2119 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2124 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2125 TnyFolder* a_folder;
2128 gtk_tree_model_get (model, iter,
2129 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2130 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2131 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2134 g_debug ("===> %s (%p ---- %p)", name, a_folder, folder);
2137 if (folder == a_folder) {
2138 g_object_unref (a_folder);
2139 *folder_iter = *iter;
2142 g_object_unref (a_folder);
2144 if (gtk_tree_model_iter_children (model, &child, iter)) {
2145 if (find_folder_iter (model, &child, folder_iter, folder))
2149 } while (gtk_tree_model_iter_next (model, iter));
2156 on_row_changed_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2157 ModestFolderView *self)
2159 ModestFolderViewPrivate *priv = NULL;
2160 GtkTreeSelection *sel;
2162 if (!MODEST_IS_FOLDER_VIEW(self))
2165 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2167 if (priv->folder_to_select) {
2169 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2172 path = gtk_tree_model_get_path (tree_model, iter);
2173 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2175 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2177 gtk_tree_selection_select_iter (sel, iter);
2178 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2180 gtk_tree_path_free (path);
2183 g_object_unref (priv->folder_to_select);
2184 priv->folder_to_select = NULL;
2190 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2191 gboolean after_change)
2193 GtkTreeModel *model;
2194 GtkTreeIter iter, folder_iter;
2195 GtkTreeSelection *sel;
2196 ModestFolderViewPrivate *priv = NULL;
2198 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2199 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2201 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2205 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2206 gtk_tree_selection_unselect_all (sel);
2208 if (priv->folder_to_select)
2209 g_object_unref(priv->folder_to_select);
2210 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2214 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2219 gtk_tree_model_get_iter_first (model, &iter);
2220 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2223 path = gtk_tree_model_get_path (model, &folder_iter);
2224 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2226 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2227 gtk_tree_selection_select_iter (sel, &folder_iter);
2228 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2230 gtk_tree_path_free (path);
2238 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2240 /* Copy selection */
2241 _clipboard_set_selected_data (folder_view, FALSE);
2245 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2247 ModestFolderViewPrivate *priv = NULL;
2248 GtkTreeModel *model = NULL;
2249 const gchar **hidding = NULL;
2250 guint i, n_selected;
2252 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2253 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2255 /* Copy selection */
2256 if (!_clipboard_set_selected_data (folder_view, TRUE))
2259 /* Get hidding ids */
2260 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2262 /* Clear hidding array created by previous cut operation */
2263 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2265 /* Copy hidding array */
2266 priv->n_selected = n_selected;
2267 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2268 for (i=0; i < n_selected; i++)
2269 priv->hidding_ids[i] = g_strdup(hidding[i]);
2271 /* Hide cut folders */
2272 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2273 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2277 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2280 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2281 priv->show_non_move = show;
2282 modest_folder_view_update_model(folder_view,
2283 TNY_ACCOUNT_STORE(modest_runtime_get_account_store()));
2286 /* Returns FALSE if it did not selected anything */
2288 _clipboard_set_selected_data (ModestFolderView *folder_view,
2291 ModestFolderViewPrivate *priv = NULL;
2292 TnyFolderStore *folder = NULL;
2293 gboolean retval = FALSE;
2295 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2296 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2298 /* Set selected data on clipboard */
2299 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2300 folder = modest_folder_view_get_selected (folder_view);
2302 /* Do not allow to select an account */
2303 if (TNY_IS_FOLDER (folder)) {
2304 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2309 g_object_unref (folder);
2315 _clear_hidding_filter (ModestFolderView *folder_view)
2317 ModestFolderViewPrivate *priv;
2320 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2321 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2323 if (priv->hidding_ids != NULL) {
2324 for (i=0; i < priv->n_selected; i++)
2325 g_free (priv->hidding_ids[i]);
2326 g_free(priv->hidding_ids);