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 void on_account_changed (TnyAccountStore *self,
84 static gint cmp_rows (GtkTreeModel *tree_model,
89 static gboolean filter_row (GtkTreeModel *model,
93 static gboolean on_key_pressed (GtkWidget *self,
97 static void on_configuration_key_changed (ModestConf* conf,
99 ModestConfEvent event,
100 ModestConfNotificationId notification_id,
101 ModestFolderView *self);
104 static void on_drag_data_get (GtkWidget *widget,
105 GdkDragContext *context,
106 GtkSelectionData *selection_data,
111 static void on_drag_data_received (GtkWidget *widget,
112 GdkDragContext *context,
115 GtkSelectionData *selection_data,
120 static gboolean on_drag_motion (GtkWidget *widget,
121 GdkDragContext *context,
127 static gint expand_row_timeout (gpointer data);
129 static void setup_drag_and_drop (GtkTreeView *self);
131 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
134 static void _clear_hidding_filter (ModestFolderView *folder_view);
136 static void on_row_changed_maybe_select_folder (GtkTreeModel *tree_model,
139 ModestFolderView *self);
142 FOLDER_SELECTION_CHANGED_SIGNAL,
143 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
147 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
148 struct _ModestFolderViewPrivate {
149 TnyAccountStore *account_store;
150 TnyFolderStore *cur_folder_store;
152 TnyFolder *folder_to_select; /* folder to select after the next update */
154 ModestConfNotificationId notification_id;
156 gulong changed_signal;
157 gulong account_inserted_signal;
158 gulong account_removed_signal;
159 gulong account_changed_signal;
160 gulong conf_key_signal;
162 /* not unref this object, its a singlenton */
163 ModestEmailClipboard *clipboard;
165 /* Filter tree model */
169 TnyFolderStoreQuery *query;
170 guint timer_expander;
172 gchar *local_account_name;
173 gchar *visible_account_id;
174 ModestFolderViewStyle style;
176 gboolean reselect; /* we use this to force a reselection of the INBOX */
177 gboolean show_non_move;
179 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
180 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
181 MODEST_TYPE_FOLDER_VIEW, \
182 ModestFolderViewPrivate))
184 static GObjectClass *parent_class = NULL;
186 static guint signals[LAST_SIGNAL] = {0};
189 modest_folder_view_get_type (void)
191 static GType my_type = 0;
193 static const GTypeInfo my_info = {
194 sizeof(ModestFolderViewClass),
195 NULL, /* base init */
196 NULL, /* base finalize */
197 (GClassInitFunc) modest_folder_view_class_init,
198 NULL, /* class finalize */
199 NULL, /* class data */
200 sizeof(ModestFolderView),
202 (GInstanceInitFunc) modest_folder_view_init,
206 static const GInterfaceInfo tny_account_store_view_info = {
207 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
208 NULL, /* interface_finalize */
209 NULL /* interface_data */
213 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
217 g_type_add_interface_static (my_type,
218 TNY_TYPE_ACCOUNT_STORE_VIEW,
219 &tny_account_store_view_info);
225 modest_folder_view_class_init (ModestFolderViewClass *klass)
227 GObjectClass *gobject_class;
228 gobject_class = (GObjectClass*) klass;
230 parent_class = g_type_class_peek_parent (klass);
231 gobject_class->finalize = modest_folder_view_finalize;
233 g_type_class_add_private (gobject_class,
234 sizeof(ModestFolderViewPrivate));
236 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
237 g_signal_new ("folder_selection_changed",
238 G_TYPE_FROM_CLASS (gobject_class),
240 G_STRUCT_OFFSET (ModestFolderViewClass,
241 folder_selection_changed),
243 modest_marshal_VOID__POINTER_BOOLEAN,
244 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
247 * This signal is emitted whenever the currently selected
248 * folder display name is computed. Note that the name could
249 * be different to the folder name, because we could append
250 * the unread messages count to the folder name to build the
251 * folder display name
253 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
254 g_signal_new ("folder-display-name-changed",
255 G_TYPE_FROM_CLASS (gobject_class),
257 G_STRUCT_OFFSET (ModestFolderViewClass,
258 folder_display_name_changed),
260 g_cclosure_marshal_VOID__STRING,
261 G_TYPE_NONE, 1, G_TYPE_STRING);
264 /* Simplify checks for NULLs: */
266 strings_are_equal (const gchar *a, const gchar *b)
272 return (strcmp (a, b) == 0);
279 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
281 GObject *instance = NULL;
283 gtk_tree_model_get (model, iter,
284 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
288 return FALSE; /* keep walking */
290 if (!TNY_IS_ACCOUNT (instance)) {
291 g_object_unref (instance);
292 return FALSE; /* keep walking */
295 /* Check if this is the looked-for account: */
296 TnyAccount *this_account = TNY_ACCOUNT (instance);
297 TnyAccount *account = TNY_ACCOUNT (data);
299 const gchar *this_account_id = tny_account_get_id(this_account);
300 const gchar *account_id = tny_account_get_id(account);
301 g_object_unref (instance);
304 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
305 if (strings_are_equal(this_account_id, account_id)) {
306 /* Tell the model that the data has changed, so that
307 * it calls the cell_data_func callbacks again: */
308 /* TODO: This does not seem to actually cause the new string to be shown: */
309 gtk_tree_model_row_changed (model, path, iter);
311 return TRUE; /* stop walking */
314 return FALSE; /* keep walking */
319 ModestFolderView *self;
320 gchar *previous_name;
321 } GetMmcAccountNameData;
324 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
326 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
328 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
330 if (!strings_are_equal (
331 tny_account_get_name(TNY_ACCOUNT(account)),
332 data->previous_name)) {
334 /* Tell the model that the data has changed, so that
335 * it calls the cell_data_func callbacks again: */
336 ModestFolderView *self = data->self;
337 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
339 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
342 g_free (data->previous_name);
343 g_slice_free (GetMmcAccountNameData, data);
347 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
348 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
350 ModestFolderViewPrivate *priv;
355 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
356 GObject *instance = NULL;
358 g_return_if_fail (column);
359 g_return_if_fail (tree_model);
361 gtk_tree_model_get (tree_model, iter,
362 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
363 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
364 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
365 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
366 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
368 rendobj = G_OBJECT(renderer);
378 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
379 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
381 gchar *item_name = NULL;
382 gint item_weight = 400;
384 if (type != TNY_FOLDER_TYPE_ROOT) {
387 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
388 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
389 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
390 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
392 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
396 /* Select the number to show: the unread or unsent messages */
397 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
402 /* Use bold font style if there are unread or unset messages */
404 item_name = g_strdup_printf ("%s (%d)", fname, number);
407 item_name = g_strdup (fname);
411 } else if (TNY_IS_ACCOUNT (instance)) {
412 /* If it's a server account */
413 if (modest_tny_account_is_virtual_local_folders (
414 TNY_ACCOUNT (instance))) {
415 item_name = g_strdup (priv->local_account_name);
417 } else if (modest_tny_account_is_memory_card_account (
418 TNY_ACCOUNT (instance))) {
419 /* fname is only correct when the items are first
420 * added to the model, not when the account is
421 * changed later, so get the name from the account
423 item_name = g_strdup (tny_account_get_name (
424 TNY_ACCOUNT (instance)));
427 item_name = g_strdup (fname);
433 item_name = g_strdup ("unknown");
435 if (item_name && item_weight) {
436 /* Set the name in the treeview cell: */
437 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
439 /* Notify display name observers */
440 /* TODO: What listens for this signal, and how can it use only the new name? */
441 if (G_OBJECT (priv->cur_folder_store) == instance) {
442 g_signal_emit (G_OBJECT(self),
443 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
450 /* If it is a Memory card account, make sure that we have the correct name.
451 * This function will be trigerred again when the name has been retrieved: */
452 if (TNY_IS_STORE_ACCOUNT (instance) &&
453 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
455 /* Get the account name asynchronously: */
456 GetMmcAccountNameData *callback_data =
457 g_slice_new0(GetMmcAccountNameData);
458 callback_data->self = self;
460 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
462 callback_data->previous_name = g_strdup (name);
464 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
465 on_get_mmc_account_name, callback_data);
468 g_object_unref (G_OBJECT (instance));
473 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
474 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
476 GObject *rendobj = NULL, *instance = NULL;
477 GdkPixbuf *pixbuf = NULL;
478 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
479 const gchar *account_id = NULL;
480 gboolean has_children;
482 rendobj = G_OBJECT(renderer);
483 gtk_tree_model_get (tree_model, iter,
484 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
485 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
487 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
492 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
493 /* We include the MERGE type here because it's used to create
494 the local OUTBOX folder */
495 if (type == TNY_FOLDER_TYPE_NORMAL ||
496 type == TNY_FOLDER_TYPE_UNKNOWN) {
497 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
501 case TNY_FOLDER_TYPE_ROOT:
502 if (TNY_IS_ACCOUNT (instance)) {
504 if (modest_tny_account_is_virtual_local_folders (
505 TNY_ACCOUNT (instance))) {
506 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
509 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
511 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
512 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
514 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
518 case TNY_FOLDER_TYPE_INBOX:
519 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
521 case TNY_FOLDER_TYPE_OUTBOX:
522 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
524 case TNY_FOLDER_TYPE_JUNK:
525 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
527 case TNY_FOLDER_TYPE_SENT:
528 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
530 case TNY_FOLDER_TYPE_TRASH:
531 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
533 case TNY_FOLDER_TYPE_DRAFTS:
534 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
536 case TNY_FOLDER_TYPE_NORMAL:
538 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
542 g_object_unref (G_OBJECT (instance));
545 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
546 if (has_children && (pixbuf != NULL)) {
547 GdkPixbuf *open_pixbuf, *closed_pixbuf;
548 GdkPixbuf *open_emblem, *closed_emblem;
549 open_pixbuf = gdk_pixbuf_copy (pixbuf);
550 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
551 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
552 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
555 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
556 MIN (gdk_pixbuf_get_width (open_emblem),
557 gdk_pixbuf_get_width (open_pixbuf)),
558 MIN (gdk_pixbuf_get_height (open_emblem),
559 gdk_pixbuf_get_height (open_pixbuf)),
560 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
561 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
562 g_object_unref (open_emblem);
565 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
566 MIN (gdk_pixbuf_get_width (closed_emblem),
567 gdk_pixbuf_get_width (closed_pixbuf)),
568 MIN (gdk_pixbuf_get_height (closed_emblem),
569 gdk_pixbuf_get_height (closed_pixbuf)),
570 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
571 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
572 g_object_unref (closed_emblem);
575 g_object_unref (closed_pixbuf);
577 g_object_unref (open_pixbuf);
581 g_object_unref (pixbuf);
585 add_columns (GtkWidget *treeview)
587 GtkTreeViewColumn *column;
588 GtkCellRenderer *renderer;
589 GtkTreeSelection *sel;
592 column = gtk_tree_view_column_new ();
594 /* Set icon and text render function */
595 renderer = gtk_cell_renderer_pixbuf_new();
596 gtk_tree_view_column_pack_start (column, renderer, FALSE);
597 gtk_tree_view_column_set_cell_data_func(column, renderer,
598 icon_cell_data, treeview, NULL);
600 renderer = gtk_cell_renderer_text_new();
601 gtk_tree_view_column_pack_start (column, renderer, FALSE);
602 gtk_tree_view_column_set_cell_data_func(column, renderer,
603 text_cell_data, treeview, NULL);
605 /* Set selection mode */
606 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
607 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
609 /* Set treeview appearance */
610 gtk_tree_view_column_set_spacing (column, 2);
611 gtk_tree_view_column_set_resizable (column, TRUE);
612 gtk_tree_view_column_set_fixed_width (column, TRUE);
613 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
614 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
617 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
621 modest_folder_view_init (ModestFolderView *obj)
623 ModestFolderViewPrivate *priv;
626 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
628 priv->timer_expander = 0;
629 priv->account_store = NULL;
631 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
632 priv->cur_folder_store = NULL;
633 priv->visible_account_id = NULL;
634 priv->folder_to_select = NULL;
636 /* Initialize the local account name */
637 conf = modest_runtime_get_conf();
638 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
640 /* Init email clipboard */
641 priv->clipboard = modest_runtime_get_email_clipboard ();
642 priv->hidding_ids = NULL;
643 priv->n_selected = 0;
644 priv->reselect = FALSE;
645 priv->show_non_move = TRUE;
648 add_columns (GTK_WIDGET (obj));
650 /* Setup drag and drop */
651 setup_drag_and_drop (GTK_TREE_VIEW(obj));
653 /* Connect signals */
654 g_signal_connect (G_OBJECT (obj),
656 G_CALLBACK (on_key_pressed), NULL);
659 * Track changes in the local account name (in the device it
660 * will be the device name)
662 priv->notification_id = modest_conf_listen_to_namespace (conf,
663 MODEST_CONF_NAMESPACE);
664 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
666 G_CALLBACK(on_configuration_key_changed),
671 tny_account_store_view_init (gpointer g, gpointer iface_data)
673 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
675 klass->set_account_store_func = modest_folder_view_set_account_store;
681 modest_folder_view_finalize (GObject *obj)
683 ModestFolderViewPrivate *priv;
684 GtkTreeSelection *sel;
686 g_return_if_fail (obj);
688 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
690 if (priv->notification_id) {
691 modest_conf_forget_namespace (modest_runtime_get_conf (),
692 MODEST_CONF_NAMESPACE,
693 priv->notification_id);
696 if (priv->timer_expander != 0) {
697 g_source_remove (priv->timer_expander);
698 priv->timer_expander = 0;
701 if (priv->account_store) {
702 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
703 priv->account_inserted_signal);
704 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
705 priv->account_removed_signal);
706 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
707 priv->account_changed_signal);
708 g_object_unref (G_OBJECT(priv->account_store));
709 priv->account_store = NULL;
713 g_object_unref (G_OBJECT (priv->query));
717 if (priv->folder_to_select) {
718 g_object_unref (G_OBJECT(priv->folder_to_select));
719 priv->folder_to_select = NULL;
722 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
724 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
726 g_free (priv->local_account_name);
727 g_free (priv->visible_account_id);
729 if (priv->conf_key_signal) {
730 g_signal_handler_disconnect (modest_runtime_get_conf (),
731 priv->conf_key_signal);
732 priv->conf_key_signal = 0;
735 if (priv->cur_folder_store) {
736 if (TNY_IS_FOLDER(priv->cur_folder_store))
737 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
738 /* FALSE --> expunge the message */
740 g_object_unref (priv->cur_folder_store);
741 priv->cur_folder_store = NULL;
744 /* Clear hidding array created by cut operation */
745 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
747 G_OBJECT_CLASS(parent_class)->finalize (obj);
752 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
754 ModestFolderViewPrivate *priv;
757 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
758 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
760 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
761 device = tny_account_store_get_device (account_store);
763 if (G_UNLIKELY (priv->account_store)) {
765 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
766 priv->account_inserted_signal))
767 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
768 priv->account_inserted_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);
773 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
774 priv->account_changed_signal))
775 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
776 priv->account_changed_signal);
777 g_object_unref (G_OBJECT (priv->account_store));
780 priv->account_store = g_object_ref (G_OBJECT (account_store));
782 priv->account_removed_signal =
783 g_signal_connect (G_OBJECT(account_store), "account_removed",
784 G_CALLBACK (on_account_removed), self);
786 priv->account_inserted_signal =
787 g_signal_connect (G_OBJECT(account_store), "account_inserted",
788 G_CALLBACK (on_account_inserted), self);
790 priv->account_changed_signal =
791 g_signal_connect (G_OBJECT(account_store), "account_changed",
792 G_CALLBACK (on_account_changed), self);
794 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
796 g_object_unref (G_OBJECT (device));
800 on_account_inserted (TnyAccountStore *account_store,
804 ModestFolderViewPrivate *priv;
805 GtkTreeModel *sort_model, *filter_model;
807 /* Ignore transport account insertions, we're not showing them
808 in the folder view */
809 if (TNY_IS_TRANSPORT_ACCOUNT (account))
812 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
814 /* If we're adding a new account, and there is no previous
815 one, we need to select the visible server account */
816 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
817 !priv->visible_account_id)
818 modest_widget_memory_restore (modest_runtime_get_conf(),
819 G_OBJECT (user_data),
820 MODEST_CONF_FOLDER_VIEW_KEY);
822 /* Get the inner model */
823 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
824 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
826 /* Insert the account in the model */
827 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
833 on_account_changed (TnyAccountStore *account_store, TnyAccount *tny_account,
836 g_warning ("%s: account_id = %s", __FUNCTION__, tny_account_get_id(tny_account));
842 on_account_removed (TnyAccountStore *account_store,
846 ModestFolderView *self = NULL;
847 ModestFolderViewPrivate *priv;
848 GtkTreeModel *sort_model, *filter_model;
850 /* Ignore transport account removals, we're not showing them
851 in the folder view */
852 if (TNY_IS_TRANSPORT_ACCOUNT (account))
855 g_print ("--------------------- FOLDER ---------------\n");
857 self = MODEST_FOLDER_VIEW (user_data);
858 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
860 /* TODO: invalidate the cur_folder_* and folder_to_select things */
862 /* Remove the account from the model */
863 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
864 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
865 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
868 /* If the removed account is the currently viewed one then
869 clear the configuration value. The new visible account will be the default account */
870 if (priv->visible_account_id &&
871 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
873 /* Clear the current visible account_id */
874 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
876 /* Call the restore method, this will set the new visible account */
877 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
878 MODEST_CONF_FOLDER_VIEW_KEY);
881 /* Select the INBOX */
882 modest_folder_view_select_first_inbox_or_local (self);
886 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
888 GtkTreeViewColumn *col;
890 g_return_if_fail (self);
892 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
894 g_printerr ("modest: failed get column for title\n");
898 gtk_tree_view_column_set_title (col, title);
899 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
904 modest_folder_view_on_map (ModestFolderView *self,
905 GdkEventExpose *event,
908 ModestFolderViewPrivate *priv;
910 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
912 /* This won't happen often */
913 if (G_UNLIKELY (priv->reselect)) {
914 /* Select the first inbox or the local account if not found */
916 /* TODO: this could cause a lock at startup, so we
917 comment it for the moment. We know that this will
918 be a bug, because the INBOX is not selected, but we
919 need to rewrite some parts of Modest to avoid the
920 deathlock situation */
921 /* TODO: check if this is still the case */
922 priv->reselect = FALSE;
923 modest_folder_view_select_first_inbox_or_local (self);
924 /* Notify the display name observers */
925 g_signal_emit (G_OBJECT(self),
926 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
933 modest_folder_view_new (TnyFolderStoreQuery *query)
936 ModestFolderViewPrivate *priv;
937 GtkTreeSelection *sel;
939 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
940 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
943 priv->query = g_object_ref (query);
945 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
946 priv->changed_signal = g_signal_connect (sel, "changed",
947 G_CALLBACK (on_selection_changed), self);
949 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
951 return GTK_WIDGET(self);
954 /* this feels dirty; any other way to expand all the root items? */
956 expand_root_items (ModestFolderView *self)
959 path = gtk_tree_path_new_first ();
961 /* all folders should have child items, so.. */
962 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
963 gtk_tree_path_next (path);
965 gtk_tree_path_free (path);
969 * We use this function to implement the
970 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
971 * account in this case, and the local folders.
974 filter_row (GtkTreeModel *model,
978 ModestFolderViewPrivate *priv;
979 gboolean retval = TRUE;
980 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
981 GObject *instance = NULL;
982 const gchar *id = NULL;
984 gboolean found = FALSE;
985 gboolean cleared = FALSE;
987 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
988 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
990 gtk_tree_model_get (model, iter,
991 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
992 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
995 /* Do not show if there is no instance, this could indeed
996 happen when the model is being modified while it's being
997 drawn. This could occur for example when moving folders
1002 if (type == TNY_FOLDER_TYPE_ROOT) {
1003 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1004 account instead of a folder. */
1005 if (TNY_IS_ACCOUNT (instance)) {
1006 TnyAccount *acc = TNY_ACCOUNT (instance);
1007 const gchar *account_id = tny_account_get_id (acc);
1009 /* If it isn't a special folder,
1010 * don't show it unless it is the visible account: */
1011 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1012 !modest_tny_account_is_virtual_local_folders (acc) &&
1013 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1015 /* Show only the visible account id */
1016 if (priv->visible_account_id) {
1017 if (strcmp (account_id, priv->visible_account_id))
1024 /* Never show these to the user. They are merged into one folder
1025 * in the local-folders account instead: */
1026 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1031 /* Check hiding (if necessary) */
1032 cleared = modest_email_clipboard_cleared (priv->clipboard);
1033 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1034 id = tny_folder_get_id (TNY_FOLDER(instance));
1035 if (priv->hidding_ids != NULL)
1036 for (i=0; i < priv->n_selected && !found; i++)
1037 if (priv->hidding_ids[i] != NULL && id != NULL)
1038 found = (!strcmp (priv->hidding_ids[i], id));
1044 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1045 folder as no message can be move there according to UI specs */
1046 if (!priv->show_non_move)
1050 case TNY_FOLDER_TYPE_OUTBOX:
1051 case TNY_FOLDER_TYPE_SENT:
1052 case TNY_FOLDER_TYPE_DRAFTS:
1055 case TNY_FOLDER_TYPE_UNKNOWN:
1056 case TNY_FOLDER_TYPE_NORMAL:
1057 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1058 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1059 || type == TNY_FOLDER_TYPE_DRAFTS)
1070 g_object_unref (instance);
1077 modest_folder_view_update_model (ModestFolderView *self,
1078 TnyAccountStore *account_store)
1080 ModestFolderViewPrivate *priv;
1081 GtkTreeModel *model /* , *old_model */;
1082 /* TnyAccount *local_account; */
1083 TnyList *model_as_list;
1085 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1086 g_return_val_if_fail (account_store, FALSE);
1088 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1090 /* Notify that there is no folder selected */
1091 g_signal_emit (G_OBJECT(self),
1092 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1094 if (priv->cur_folder_store) {
1095 g_object_unref (priv->cur_folder_store);
1096 priv->cur_folder_store = NULL;
1099 /* FIXME: the local accounts are not shown when the query
1100 selects only the subscribed folders. */
1101 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1102 model = tny_gtk_folder_store_tree_model_new (NULL);
1104 /* Deal with the model via its TnyList Interface,
1105 * filling the TnyList via a get_accounts() call: */
1106 model_as_list = TNY_LIST(model);
1108 /* Get the accounts: */
1109 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1111 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1112 g_object_unref (model_as_list);
1113 model_as_list = NULL;
1115 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1117 sortable = gtk_tree_model_sort_new_with_model (model);
1118 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1119 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1120 GTK_SORT_ASCENDING);
1121 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1122 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1123 cmp_rows, NULL, NULL);
1125 /* Create filter model */
1126 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1127 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1133 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1134 g_signal_connect (G_OBJECT(filter_model), "row-changed",
1135 (GCallback)on_row_changed_maybe_select_folder, self);
1136 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1137 (GCallback)on_row_changed_maybe_select_folder, self);
1140 g_object_unref (model);
1141 g_object_unref (filter_model);
1142 g_object_unref (sortable);
1144 /* Force a reselection of the INBOX next time the widget is shown */
1145 priv->reselect = TRUE;
1152 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1154 GtkTreeModel *model;
1155 TnyFolderStore *folder = NULL;
1157 ModestFolderView *tree_view;
1158 ModestFolderViewPrivate *priv;
1160 g_return_if_fail (sel);
1161 g_return_if_fail (user_data);
1163 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1165 if(!gtk_tree_selection_get_selected (sel, &model, &iter))
1168 /* Notify the display name observers */
1169 g_signal_emit (G_OBJECT(user_data),
1170 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1173 tree_view = MODEST_FOLDER_VIEW (user_data);
1174 gtk_tree_model_get (model, &iter,
1175 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1178 /* If the folder is the same do not notify */
1179 if (priv->cur_folder_store == folder && folder) {
1180 g_object_unref (folder);
1184 /* Current folder was unselected */
1185 if (priv->cur_folder_store) {
1186 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1187 priv->cur_folder_store, FALSE);
1189 if (TNY_IS_FOLDER(priv->cur_folder_store))
1190 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1191 FALSE, NULL, NULL, NULL);
1192 /* FALSE --> don't expunge the messages */
1194 g_object_unref (priv->cur_folder_store);
1195 priv->cur_folder_store = NULL;
1198 /* New current references */
1199 priv->cur_folder_store = folder;
1201 /* New folder has been selected */
1202 g_signal_emit (G_OBJECT(tree_view),
1203 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1204 0, priv->cur_folder_store, TRUE);
1208 modest_folder_view_get_selected (ModestFolderView *self)
1210 ModestFolderViewPrivate *priv;
1212 g_return_val_if_fail (self, NULL);
1214 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1215 if (priv->cur_folder_store)
1216 g_object_ref (priv->cur_folder_store);
1218 return priv->cur_folder_store;
1222 get_cmp_rows_type_pos (GObject *folder)
1224 /* Remote accounts -> Local account -> MMC account .*/
1227 if (TNY_IS_ACCOUNT (folder) &&
1228 modest_tny_account_is_virtual_local_folders (
1229 TNY_ACCOUNT (folder))) {
1231 } else if (TNY_IS_ACCOUNT (folder)) {
1232 TnyAccount *account = TNY_ACCOUNT (folder);
1233 const gchar *account_id = tny_account_get_id (account);
1234 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1240 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1241 return -1; /* Should never happen */
1246 get_cmp_subfolder_type_pos (TnyFolderType t)
1248 /* Inbox, Outbox, Drafts, Sent, User */
1252 case TNY_FOLDER_TYPE_INBOX:
1255 case TNY_FOLDER_TYPE_OUTBOX:
1258 case TNY_FOLDER_TYPE_DRAFTS:
1261 case TNY_FOLDER_TYPE_SENT:
1270 * This function orders the mail accounts according to these rules:
1271 * 1st - remote accounts
1272 * 2nd - local account
1276 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1280 gchar *name1 = NULL;
1281 gchar *name2 = NULL;
1282 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1283 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1284 GObject *folder1 = NULL;
1285 GObject *folder2 = NULL;
1287 gtk_tree_model_get (tree_model, iter1,
1288 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1289 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1290 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1292 gtk_tree_model_get (tree_model, iter2,
1293 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1294 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1295 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1298 /* Return if we get no folder. This could happen when folder
1299 operations are happening. The model is updated after the
1300 folder copy/move actually occurs, so there could be
1301 situations where the model to be drawn is not correct */
1302 if (!folder1 || !folder2)
1305 if (type == TNY_FOLDER_TYPE_ROOT) {
1306 /* Compare the types, so that
1307 * Remote accounts -> Local account -> MMC account .*/
1308 const gint pos1 = get_cmp_rows_type_pos (folder1);
1309 const gint pos2 = get_cmp_rows_type_pos (folder2);
1310 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1311 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1314 else if (pos1 > pos2)
1317 /* Compare items of the same type: */
1319 TnyAccount *account1 = NULL;
1320 if (TNY_IS_ACCOUNT (folder1))
1321 account1 = TNY_ACCOUNT (folder1);
1323 TnyAccount *account2 = NULL;
1324 if (TNY_IS_ACCOUNT (folder2))
1325 account2 = TNY_ACCOUNT (folder2);
1327 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1328 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1330 if (!account_id && !account_id2) {
1332 } else if (!account_id) {
1334 } else if (!account_id2) {
1336 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1339 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1343 gint cmp1 = 0, cmp2 = 0;
1344 /* get the parent to know if it's a local folder */
1347 gboolean has_parent;
1348 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1350 GObject *parent_folder;
1351 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1352 gtk_tree_model_get (tree_model, &parent,
1353 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1354 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1356 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1357 TNY_IS_ACCOUNT (parent_folder) &&
1358 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1359 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1360 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1362 g_object_unref (parent_folder);
1365 /* if they are not local folders */
1367 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1368 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1372 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1374 cmp = (cmp1 - cmp2);
1379 g_object_unref(G_OBJECT(folder1));
1381 g_object_unref(G_OBJECT(folder2));
1389 /*****************************************************************************/
1390 /* DRAG and DROP stuff */
1391 /*****************************************************************************/
1394 * This function fills the #GtkSelectionData with the row and the
1395 * model that has been dragged. It's called when this widget is a
1396 * source for dnd after the event drop happened
1399 on_drag_data_get (GtkWidget *widget,
1400 GdkDragContext *context,
1401 GtkSelectionData *selection_data,
1406 GtkTreeSelection *selection;
1407 GtkTreeModel *model;
1409 GtkTreePath *source_row;
1411 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1412 gtk_tree_selection_get_selected (selection, &model, &iter);
1413 source_row = gtk_tree_model_get_path (model, &iter);
1415 gtk_tree_set_row_drag_data (selection_data,
1419 gtk_tree_path_free (source_row);
1422 typedef struct _DndHelper {
1423 gboolean delete_source;
1424 GtkTreePath *source_row;
1425 GdkDragContext *context;
1431 * This function is the callback of the
1432 * modest_mail_operation_xfer_msgs () and
1433 * modest_mail_operation_xfer_folder() calls. We check here if the
1434 * message/folder was correctly asynchronously transferred. The reason
1435 * to use the same callback is that the code is the same, it only has
1436 * to check that the operation went fine and then finalize the drag
1440 on_progress_changed (ModestMailOperation *mail_op,
1441 ModestMailOperationState *state,
1447 helper = (DndHelper *) user_data;
1449 if (!state->finished)
1452 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1458 /* Notify the drag source. Never call delete, the monitor will
1459 do the job if needed */
1460 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1462 /* Free the helper */
1463 gtk_tree_path_free (helper->source_row);
1464 g_slice_free (DndHelper, helper);
1468 /* get the folder for the row the treepath refers to. */
1469 /* folder must be unref'd */
1471 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1474 TnyFolder *folder = NULL;
1476 if (gtk_tree_model_get_iter (model,&iter, path))
1477 gtk_tree_model_get (model, &iter,
1478 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1484 * This function is used by drag_data_received_cb to manage drag and
1485 * drop of a header, i.e, and drag from the header view to the folder
1489 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1490 GtkTreeModel *dest_model,
1491 GtkTreePath *dest_row,
1494 TnyList *headers = NULL;
1495 TnyHeader *header = NULL;
1496 TnyFolder *folder = NULL;
1497 ModestMailOperation *mail_op = NULL;
1498 GtkTreeIter source_iter;
1500 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1501 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1502 g_return_if_fail (dest_row);
1503 g_return_if_fail (helper);
1506 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1507 gtk_tree_model_get (source_model, &source_iter,
1508 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1510 if (!TNY_IS_HEADER(header)) {
1511 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1516 folder = tree_path_to_folder (dest_model, dest_row);
1517 if (!TNY_IS_FOLDER(folder)) {
1518 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1521 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1522 g_debug ("folder rules: cannot write to that folder");
1527 /* Transfer message */
1528 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1530 modest_ui_actions_move_folder_error_handler,
1532 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1534 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1535 G_CALLBACK (on_progress_changed), helper);
1537 headers = tny_simple_list_new ();
1538 tny_list_append (headers, G_OBJECT (header));
1539 modest_mail_operation_xfer_msgs (mail_op,
1542 helper->delete_source,
1547 if (G_IS_OBJECT(mail_op))
1548 g_object_unref (G_OBJECT (mail_op));
1549 if (G_IS_OBJECT(header))
1550 g_object_unref (G_OBJECT (header));
1551 if (G_IS_OBJECT(folder))
1552 g_object_unref (G_OBJECT (folder));
1553 if (G_IS_OBJECT(headers))
1554 g_object_unref (headers);
1558 * This function is used by drag_data_received_cb to manage drag and
1559 * drop of a folder, i.e, and drag from the folder view to the same
1563 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1564 GtkTreeModel *dest_model,
1565 GtkTreePath *dest_row,
1566 GtkSelectionData *selection_data,
1569 ModestMailOperation *mail_op = NULL;
1570 GtkTreeIter parent_iter, iter;
1571 TnyFolderStore *parent_folder = NULL;
1572 TnyFolder *folder = NULL;
1573 gboolean forbidden = TRUE;
1575 /* check the folder rules for the destination */
1576 folder = tree_path_to_folder (dest_model, dest_row);
1578 ModestTnyFolderRules rules =
1579 modest_tny_folder_get_rules (folder);
1580 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1582 g_debug ("folder rules: cannot write to that folder");
1583 g_object_unref (folder);
1587 /* check the folder rules for the source */
1588 folder = tree_path_to_folder (source_model, helper->source_row);
1590 ModestTnyFolderRules rules =
1591 modest_tny_folder_get_rules (folder);
1592 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1594 g_debug ("folder rules: cannot move that folder");
1595 g_object_unref (folder);
1600 /* Check if the drag is possible */
1601 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1603 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1604 gtk_tree_path_free (helper->source_row);
1605 g_slice_free (DndHelper, helper);
1610 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1611 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1612 gtk_tree_model_get (source_model, &parent_iter,
1613 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1614 &parent_folder, -1);
1615 gtk_tree_model_get (source_model, &iter,
1616 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1619 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1620 if (modest_platform_connect_and_wait_if_network_folderstore (NULL, parent_folder) &&
1621 modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1622 /* Do the mail operation */
1623 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1625 modest_ui_actions_move_folder_error_handler,
1627 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1629 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1630 G_CALLBACK (on_progress_changed), helper);
1632 modest_mail_operation_xfer_folder (mail_op,
1635 helper->delete_source,
1639 g_object_unref (G_OBJECT (mail_op));
1643 g_object_unref (G_OBJECT (parent_folder));
1644 g_object_unref (G_OBJECT (folder));
1648 * This function receives the data set by the "drag-data-get" signal
1649 * handler. This information comes within the #GtkSelectionData. This
1650 * function will manage both the drags of folders of the treeview and
1651 * drags of headers of the header view widget.
1654 on_drag_data_received (GtkWidget *widget,
1655 GdkDragContext *context,
1658 GtkSelectionData *selection_data,
1663 GtkWidget *source_widget;
1664 GtkTreeModel *dest_model, *source_model;
1665 GtkTreePath *source_row, *dest_row;
1666 GtkTreeViewDropPosition pos;
1667 gboolean success = FALSE, delete_source = FALSE;
1668 DndHelper *helper = NULL;
1670 /* Do not allow further process */
1671 g_signal_stop_emission_by_name (widget, "drag-data-received");
1672 source_widget = gtk_drag_get_source_widget (context);
1674 /* Get the action */
1675 if (context->action == GDK_ACTION_MOVE) {
1676 delete_source = TRUE;
1678 /* Notify that there is no folder selected. We need to
1679 do this in order to update the headers view (and
1680 its monitors, because when moving, the old folder
1681 won't longer exist. We can not wait for the end of
1682 the operation, because the operation won't start if
1683 the folder is in use */
1684 if (source_widget == widget) {
1685 ModestFolderViewPrivate *priv;
1687 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1688 if (priv->cur_folder_store) {
1689 g_object_unref (priv->cur_folder_store);
1690 priv->cur_folder_store = NULL;
1693 g_signal_emit (G_OBJECT (widget),
1694 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1698 /* Check if the get_data failed */
1699 if (selection_data == NULL || selection_data->length < 0)
1700 gtk_drag_finish (context, success, FALSE, time);
1702 /* Get the models */
1703 gtk_tree_get_row_drag_data (selection_data,
1707 /* Select the destination model */
1708 if (source_widget == widget) {
1709 dest_model = source_model;
1711 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1714 /* Get the path to the destination row. Can not call
1715 gtk_tree_view_get_drag_dest_row() because the source row
1716 is not selected anymore */
1717 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1720 /* Only allow drops IN other rows */
1721 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1722 gtk_drag_finish (context, success, FALSE, time);
1724 /* Create the helper */
1725 helper = g_slice_new0 (DndHelper);
1726 helper->delete_source = delete_source;
1727 helper->source_row = gtk_tree_path_copy (source_row);
1728 helper->context = context;
1729 helper->time = time;
1731 /* Drags from the header view */
1732 if (source_widget != widget) {
1734 drag_and_drop_from_header_view (source_model,
1741 drag_and_drop_from_folder_view (source_model,
1749 gtk_tree_path_free (source_row);
1750 gtk_tree_path_free (dest_row);
1754 * We define a "drag-drop" signal handler because we do not want to
1755 * use the default one, because the default one always calls
1756 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1757 * signal handler, because there we have all the information available
1758 * to know if the dnd was a success or not.
1761 drag_drop_cb (GtkWidget *widget,
1762 GdkDragContext *context,
1770 if (!context->targets)
1773 /* Check if we're dragging a folder row */
1774 target = gtk_drag_dest_find_target (widget, context, NULL);
1776 /* Request the data from the source. */
1777 gtk_drag_get_data(widget, context, target, time);
1783 * This function expands a node of a tree view if it's not expanded
1784 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1785 * does that, so that's why they're here.
1788 expand_row_timeout (gpointer data)
1790 GtkTreeView *tree_view = data;
1791 GtkTreePath *dest_path = NULL;
1792 GtkTreeViewDropPosition pos;
1793 gboolean result = FALSE;
1795 GDK_THREADS_ENTER ();
1797 gtk_tree_view_get_drag_dest_row (tree_view,
1802 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1803 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1804 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1805 gtk_tree_path_free (dest_path);
1809 gtk_tree_path_free (dest_path);
1814 GDK_THREADS_LEAVE ();
1820 * This function is called whenever the pointer is moved over a widget
1821 * while dragging some data. It installs a timeout that will expand a
1822 * node of the treeview if not expanded yet. This function also calls
1823 * gdk_drag_status in order to set the suggested action that will be
1824 * used by the "drag-data-received" signal handler to know if we
1825 * should do a move or just a copy of the data.
1828 on_drag_motion (GtkWidget *widget,
1829 GdkDragContext *context,
1835 GtkTreeViewDropPosition pos;
1836 GtkTreePath *dest_row;
1837 ModestFolderViewPrivate *priv;
1838 GdkDragAction suggested_action;
1839 gboolean valid_location = FALSE;
1841 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1843 if (priv->timer_expander != 0) {
1844 g_source_remove (priv->timer_expander);
1845 priv->timer_expander = 0;
1848 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1853 /* Do not allow drops between folders */
1855 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1856 pos == GTK_TREE_VIEW_DROP_AFTER) {
1857 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1858 gdk_drag_status(context, 0, time);
1859 valid_location = FALSE;
1862 valid_location = TRUE;
1865 /* Expand the selected row after 1/2 second */
1866 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1867 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1868 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1871 /* Select the desired action. By default we pick MOVE */
1872 suggested_action = GDK_ACTION_MOVE;
1874 if (context->actions == GDK_ACTION_COPY)
1875 gdk_drag_status(context, GDK_ACTION_COPY, time);
1876 else if (context->actions == GDK_ACTION_MOVE)
1877 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1878 else if (context->actions & suggested_action)
1879 gdk_drag_status(context, suggested_action, time);
1881 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1885 gtk_tree_path_free (dest_row);
1886 g_signal_stop_emission_by_name (widget, "drag-motion");
1887 return valid_location;
1891 /* Folder view drag types */
1892 const GtkTargetEntry folder_view_drag_types[] =
1894 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1895 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1899 * This function sets the treeview as a source and a target for dnd
1900 * events. It also connects all the requirede signals.
1903 setup_drag_and_drop (GtkTreeView *self)
1905 /* Set up the folder view as a dnd destination. Set only the
1906 highlight flag, otherwise gtk will have a different
1908 gtk_drag_dest_set (GTK_WIDGET (self),
1909 GTK_DEST_DEFAULT_HIGHLIGHT,
1910 folder_view_drag_types,
1911 G_N_ELEMENTS (folder_view_drag_types),
1912 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1914 g_signal_connect (G_OBJECT (self),
1915 "drag_data_received",
1916 G_CALLBACK (on_drag_data_received),
1920 /* Set up the treeview as a dnd source */
1921 gtk_drag_source_set (GTK_WIDGET (self),
1923 folder_view_drag_types,
1924 G_N_ELEMENTS (folder_view_drag_types),
1925 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1927 g_signal_connect (G_OBJECT (self),
1929 G_CALLBACK (on_drag_motion),
1932 g_signal_connect (G_OBJECT (self),
1934 G_CALLBACK (on_drag_data_get),
1937 g_signal_connect (G_OBJECT (self),
1939 G_CALLBACK (drag_drop_cb),
1944 * This function manages the navigation through the folders using the
1945 * keyboard or the hardware keys in the device
1948 on_key_pressed (GtkWidget *self,
1952 GtkTreeSelection *selection;
1954 GtkTreeModel *model;
1955 gboolean retval = FALSE;
1957 /* Up and Down are automatically managed by the treeview */
1958 if (event->keyval == GDK_Return) {
1959 /* Expand/Collapse the selected row */
1960 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1961 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1964 path = gtk_tree_model_get_path (model, &iter);
1966 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1967 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1969 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1970 gtk_tree_path_free (path);
1972 /* No further processing */
1980 * We listen to the changes in the local folder account name key,
1981 * because we want to show the right name in the view. The local
1982 * folder account name corresponds to the device name in the Maemo
1983 * version. We do this because we do not want to query gconf on each
1984 * tree view refresh. It's better to cache it and change whenever
1988 on_configuration_key_changed (ModestConf* conf,
1990 ModestConfEvent event,
1991 ModestConfNotificationId id,
1992 ModestFolderView *self)
1994 ModestFolderViewPrivate *priv;
1997 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1998 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2000 /* Do not listen for changes in other namespaces */
2001 if (priv->notification_id != id)
2004 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2005 g_free (priv->local_account_name);
2007 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2008 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2010 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2011 MODEST_CONF_DEVICE_NAME, NULL);
2013 /* Force a redraw */
2014 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2015 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2016 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2017 gtk_tree_view_column_queue_resize (tree_column);
2023 modest_folder_view_set_style (ModestFolderView *self,
2024 ModestFolderViewStyle style)
2026 ModestFolderViewPrivate *priv;
2028 g_return_if_fail (self);
2030 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2032 priv->style = style;
2036 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2037 const gchar *account_id)
2039 ModestFolderViewPrivate *priv;
2040 GtkTreeModel *model;
2042 g_return_if_fail (self);
2044 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2046 /* This will be used by the filter_row callback,
2047 * to decided which rows to show: */
2048 if (priv->visible_account_id) {
2049 g_free (priv->visible_account_id);
2050 priv->visible_account_id = NULL;
2053 priv->visible_account_id = g_strdup (account_id);
2056 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2057 if (GTK_IS_TREE_MODEL_FILTER (model))
2058 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2060 /* Save settings to gconf */
2061 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2062 MODEST_CONF_FOLDER_VIEW_KEY);
2066 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2068 ModestFolderViewPrivate *priv;
2070 g_return_val_if_fail (self, NULL);
2072 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2074 return (const gchar *) priv->visible_account_id;
2078 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2082 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2084 gtk_tree_model_get (model, iter,
2085 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2088 gboolean result = FALSE;
2089 if (type == TNY_FOLDER_TYPE_INBOX) {
2093 *inbox_iter = *iter;
2097 if (gtk_tree_model_iter_children (model, &child, iter)) {
2098 if (find_inbox_iter (model, &child, inbox_iter))
2102 } while (gtk_tree_model_iter_next (model, iter));
2111 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2113 GtkTreeModel *model;
2114 GtkTreeIter iter, inbox_iter;
2115 GtkTreeSelection *sel;
2116 GtkTreePath *path = NULL;
2118 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2122 expand_root_items (self);
2123 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2125 gtk_tree_model_get_iter_first (model, &iter);
2127 if (find_inbox_iter (model, &iter, &inbox_iter))
2128 path = gtk_tree_model_get_path (model, &inbox_iter);
2130 path = gtk_tree_path_new_first ();
2132 /* Select the row and free */
2133 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2134 gtk_tree_path_free (path);
2140 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2145 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2146 TnyFolder* a_folder;
2149 gtk_tree_model_get (model, iter,
2150 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2151 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2152 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2155 g_debug ("===> %s (%p ---- %p)", name, a_folder, folder);
2158 if (folder == a_folder) {
2159 g_object_unref (a_folder);
2160 *folder_iter = *iter;
2163 g_object_unref (a_folder);
2165 if (gtk_tree_model_iter_children (model, &child, iter)) {
2166 if (find_folder_iter (model, &child, folder_iter, folder))
2170 } while (gtk_tree_model_iter_next (model, iter));
2177 on_row_changed_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2178 ModestFolderView *self)
2180 ModestFolderViewPrivate *priv = NULL;
2181 GtkTreeSelection *sel;
2183 if (!MODEST_IS_FOLDER_VIEW(self))
2186 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2188 if (priv->folder_to_select) {
2190 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2193 path = gtk_tree_model_get_path (tree_model, iter);
2194 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2196 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2198 gtk_tree_selection_select_iter (sel, iter);
2199 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2201 gtk_tree_path_free (path);
2204 g_object_unref (priv->folder_to_select);
2205 priv->folder_to_select = NULL;
2211 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2212 gboolean after_change)
2214 GtkTreeModel *model;
2215 GtkTreeIter iter, folder_iter;
2216 GtkTreeSelection *sel;
2217 ModestFolderViewPrivate *priv = NULL;
2219 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2220 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2222 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2226 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2227 gtk_tree_selection_unselect_all (sel);
2229 if (priv->folder_to_select)
2230 g_object_unref(priv->folder_to_select);
2231 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2235 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2240 gtk_tree_model_get_iter_first (model, &iter);
2241 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2244 path = gtk_tree_model_get_path (model, &folder_iter);
2245 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2247 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2248 gtk_tree_selection_select_iter (sel, &folder_iter);
2249 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2251 gtk_tree_path_free (path);
2259 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2261 /* Copy selection */
2262 _clipboard_set_selected_data (folder_view, FALSE);
2266 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2268 ModestFolderViewPrivate *priv = NULL;
2269 GtkTreeModel *model = NULL;
2270 const gchar **hidding = NULL;
2271 guint i, n_selected;
2273 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2274 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2276 /* Copy selection */
2277 if (!_clipboard_set_selected_data (folder_view, TRUE))
2280 /* Get hidding ids */
2281 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2283 /* Clear hidding array created by previous cut operation */
2284 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2286 /* Copy hidding array */
2287 priv->n_selected = n_selected;
2288 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2289 for (i=0; i < n_selected; i++)
2290 priv->hidding_ids[i] = g_strdup(hidding[i]);
2292 /* Hide cut folders */
2293 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2294 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2298 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2301 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2302 priv->show_non_move = show;
2303 modest_folder_view_update_model(folder_view,
2304 TNY_ACCOUNT_STORE(modest_runtime_get_account_store()));
2307 /* Returns FALSE if it did not selected anything */
2309 _clipboard_set_selected_data (ModestFolderView *folder_view,
2312 ModestFolderViewPrivate *priv = NULL;
2313 TnyFolderStore *folder = NULL;
2314 gboolean retval = FALSE;
2316 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2317 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2319 /* Set selected data on clipboard */
2320 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2321 folder = modest_folder_view_get_selected (folder_view);
2323 /* Do not allow to select an account */
2324 if (TNY_IS_FOLDER (folder)) {
2325 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2330 g_object_unref (folder);
2336 _clear_hidding_filter (ModestFolderView *folder_view)
2338 ModestFolderViewPrivate *priv;
2341 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2342 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2344 if (priv->hidding_ids != NULL) {
2345 for (i=0; i < priv->n_selected; i++)
2346 g_free (priv->hidding_ids[i]);
2347 g_free(priv->hidding_ids);