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 void expand_root_items (ModestFolderView *self);
129 static gint expand_row_timeout (gpointer data);
131 static void setup_drag_and_drop (GtkTreeView *self);
133 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
136 static void _clear_hidding_filter (ModestFolderView *folder_view);
138 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
141 ModestFolderView *self);
144 FOLDER_SELECTION_CHANGED_SIGNAL,
145 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
149 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
150 struct _ModestFolderViewPrivate {
151 TnyAccountStore *account_store;
152 TnyFolderStore *cur_folder_store;
154 TnyFolder *folder_to_select; /* folder to select after the next update */
156 ModestConfNotificationId notification_id;
158 gulong changed_signal;
159 gulong account_inserted_signal;
160 gulong account_removed_signal;
161 gulong account_changed_signal;
162 gulong conf_key_signal;
164 /* not unref this object, its a singlenton */
165 ModestEmailClipboard *clipboard;
167 /* Filter tree model */
171 TnyFolderStoreQuery *query;
172 guint timer_expander;
174 gchar *local_account_name;
175 gchar *visible_account_id;
176 ModestFolderViewStyle style;
178 gboolean reselect; /* we use this to force a reselection of the INBOX */
179 gboolean show_non_move;
181 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
182 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
183 MODEST_TYPE_FOLDER_VIEW, \
184 ModestFolderViewPrivate))
186 static GObjectClass *parent_class = NULL;
188 static guint signals[LAST_SIGNAL] = {0};
191 modest_folder_view_get_type (void)
193 static GType my_type = 0;
195 static const GTypeInfo my_info = {
196 sizeof(ModestFolderViewClass),
197 NULL, /* base init */
198 NULL, /* base finalize */
199 (GClassInitFunc) modest_folder_view_class_init,
200 NULL, /* class finalize */
201 NULL, /* class data */
202 sizeof(ModestFolderView),
204 (GInstanceInitFunc) modest_folder_view_init,
208 static const GInterfaceInfo tny_account_store_view_info = {
209 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
210 NULL, /* interface_finalize */
211 NULL /* interface_data */
215 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
219 g_type_add_interface_static (my_type,
220 TNY_TYPE_ACCOUNT_STORE_VIEW,
221 &tny_account_store_view_info);
227 modest_folder_view_class_init (ModestFolderViewClass *klass)
229 GObjectClass *gobject_class;
230 gobject_class = (GObjectClass*) klass;
232 parent_class = g_type_class_peek_parent (klass);
233 gobject_class->finalize = modest_folder_view_finalize;
235 g_type_class_add_private (gobject_class,
236 sizeof(ModestFolderViewPrivate));
238 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
239 g_signal_new ("folder_selection_changed",
240 G_TYPE_FROM_CLASS (gobject_class),
242 G_STRUCT_OFFSET (ModestFolderViewClass,
243 folder_selection_changed),
245 modest_marshal_VOID__POINTER_BOOLEAN,
246 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
249 * This signal is emitted whenever the currently selected
250 * folder display name is computed. Note that the name could
251 * be different to the folder name, because we could append
252 * the unread messages count to the folder name to build the
253 * folder display name
255 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
256 g_signal_new ("folder-display-name-changed",
257 G_TYPE_FROM_CLASS (gobject_class),
259 G_STRUCT_OFFSET (ModestFolderViewClass,
260 folder_display_name_changed),
262 g_cclosure_marshal_VOID__STRING,
263 G_TYPE_NONE, 1, G_TYPE_STRING);
270 ModestFolderView *self;
271 gchar *previous_name;
272 } GetMmcAccountNameData;
276 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
277 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
279 ModestFolderViewPrivate *priv;
284 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
285 GObject *instance = NULL;
287 g_return_if_fail (column);
288 g_return_if_fail (tree_model);
289 g_return_if_fail (iter != NULL);
291 gtk_tree_model_get (tree_model, iter,
292 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
293 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
294 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
295 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
296 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
298 rendobj = G_OBJECT(renderer);
308 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
309 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
311 gchar *item_name = NULL;
312 gint item_weight = 400;
314 if (type != TNY_FOLDER_TYPE_ROOT) {
317 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
318 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
319 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
320 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
322 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
326 /* Select the number to show: the unread or unsent messages */
327 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
332 /* Use bold font style if there are unread or unset messages */
334 item_name = g_strdup_printf ("%s (%d)", fname, number);
337 item_name = g_strdup (fname);
341 } else if (TNY_IS_ACCOUNT (instance)) {
342 /* If it's a server account */
343 if (modest_tny_account_is_virtual_local_folders (
344 TNY_ACCOUNT (instance))) {
345 item_name = g_strdup (priv->local_account_name);
347 } else if (modest_tny_account_is_memory_card_account (
348 TNY_ACCOUNT (instance))) {
349 /* fname is only correct when the items are first
350 * added to the model, not when the account is
351 * changed later, so get the name from the account
353 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
356 item_name = g_strdup (fname);
362 item_name = g_strdup ("unknown");
364 if (item_name && item_weight) {
365 /* Set the name in the treeview cell: */
366 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
368 /* Notify display name observers */
369 /* TODO: What listens for this signal, and how can it use only the new name? */
370 if (G_OBJECT (priv->cur_folder_store) == instance) {
371 g_signal_emit (G_OBJECT(self),
372 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
379 g_object_unref (G_OBJECT (instance));
384 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
385 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
387 GObject *rendobj = NULL, *instance = NULL;
388 GdkPixbuf *pixbuf = NULL;
389 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
390 const gchar *account_id = NULL;
391 gboolean has_children;
393 rendobj = G_OBJECT(renderer);
394 gtk_tree_model_get (tree_model, iter,
395 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
396 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
398 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
403 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
404 /* We include the MERGE type here because it's used to create
405 the local OUTBOX folder */
406 if (type == TNY_FOLDER_TYPE_NORMAL ||
407 type == TNY_FOLDER_TYPE_UNKNOWN) {
408 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
412 case TNY_FOLDER_TYPE_ROOT:
413 if (TNY_IS_ACCOUNT (instance)) {
415 if (modest_tny_account_is_virtual_local_folders (
416 TNY_ACCOUNT (instance))) {
417 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
420 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
422 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
423 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
425 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
429 case TNY_FOLDER_TYPE_INBOX:
430 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
432 case TNY_FOLDER_TYPE_OUTBOX:
433 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
435 case TNY_FOLDER_TYPE_JUNK:
436 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
438 case TNY_FOLDER_TYPE_SENT:
439 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
441 case TNY_FOLDER_TYPE_TRASH:
442 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
444 case TNY_FOLDER_TYPE_DRAFTS:
445 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
447 case TNY_FOLDER_TYPE_NORMAL:
449 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
453 g_object_unref (G_OBJECT (instance));
456 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
457 if (has_children && (pixbuf != NULL)) {
458 GdkPixbuf *open_pixbuf, *closed_pixbuf;
459 GdkPixbuf *open_emblem, *closed_emblem;
460 open_pixbuf = gdk_pixbuf_copy (pixbuf);
461 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
462 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
463 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
466 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
467 MIN (gdk_pixbuf_get_width (open_emblem),
468 gdk_pixbuf_get_width (open_pixbuf)),
469 MIN (gdk_pixbuf_get_height (open_emblem),
470 gdk_pixbuf_get_height (open_pixbuf)),
471 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
472 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
473 g_object_unref (open_emblem);
476 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
477 MIN (gdk_pixbuf_get_width (closed_emblem),
478 gdk_pixbuf_get_width (closed_pixbuf)),
479 MIN (gdk_pixbuf_get_height (closed_emblem),
480 gdk_pixbuf_get_height (closed_pixbuf)),
481 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
482 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
483 g_object_unref (closed_emblem);
486 g_object_unref (closed_pixbuf);
488 g_object_unref (open_pixbuf);
492 g_object_unref (pixbuf);
496 add_columns (GtkWidget *treeview)
498 GtkTreeViewColumn *column;
499 GtkCellRenderer *renderer;
500 GtkTreeSelection *sel;
503 column = gtk_tree_view_column_new ();
505 /* Set icon and text render function */
506 renderer = gtk_cell_renderer_pixbuf_new();
507 gtk_tree_view_column_pack_start (column, renderer, FALSE);
508 gtk_tree_view_column_set_cell_data_func(column, renderer,
509 icon_cell_data, treeview, NULL);
511 renderer = gtk_cell_renderer_text_new();
512 gtk_tree_view_column_pack_start (column, renderer, FALSE);
513 gtk_tree_view_column_set_cell_data_func(column, renderer,
514 text_cell_data, treeview, NULL);
516 /* Set selection mode */
517 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
518 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
520 /* Set treeview appearance */
521 gtk_tree_view_column_set_spacing (column, 2);
522 gtk_tree_view_column_set_resizable (column, TRUE);
523 gtk_tree_view_column_set_fixed_width (column, TRUE);
524 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
525 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
528 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
532 modest_folder_view_init (ModestFolderView *obj)
534 ModestFolderViewPrivate *priv;
537 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
539 priv->timer_expander = 0;
540 priv->account_store = NULL;
542 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
543 priv->cur_folder_store = NULL;
544 priv->visible_account_id = NULL;
545 priv->folder_to_select = NULL;
547 /* Initialize the local account name */
548 conf = modest_runtime_get_conf();
549 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
551 /* Init email clipboard */
552 priv->clipboard = modest_runtime_get_email_clipboard ();
553 priv->hidding_ids = NULL;
554 priv->n_selected = 0;
555 priv->reselect = FALSE;
556 priv->show_non_move = TRUE;
559 add_columns (GTK_WIDGET (obj));
561 /* Setup drag and drop */
562 setup_drag_and_drop (GTK_TREE_VIEW(obj));
564 /* Connect signals */
565 g_signal_connect (G_OBJECT (obj),
567 G_CALLBACK (on_key_pressed), NULL);
570 * Track changes in the local account name (in the device it
571 * will be the device name)
573 priv->notification_id = modest_conf_listen_to_namespace (conf,
574 MODEST_CONF_NAMESPACE);
575 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
577 G_CALLBACK(on_configuration_key_changed),
582 tny_account_store_view_init (gpointer g, gpointer iface_data)
584 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
586 klass->set_account_store_func = modest_folder_view_set_account_store;
592 modest_folder_view_finalize (GObject *obj)
594 ModestFolderViewPrivate *priv;
595 GtkTreeSelection *sel;
597 g_return_if_fail (obj);
599 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
601 if (priv->notification_id) {
602 modest_conf_forget_namespace (modest_runtime_get_conf (),
603 MODEST_CONF_NAMESPACE,
604 priv->notification_id);
607 if (priv->timer_expander != 0) {
608 g_source_remove (priv->timer_expander);
609 priv->timer_expander = 0;
612 if (priv->account_store) {
613 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
614 priv->account_inserted_signal);
615 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
616 priv->account_removed_signal);
617 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
618 priv->account_changed_signal);
619 g_object_unref (G_OBJECT(priv->account_store));
620 priv->account_store = NULL;
624 g_object_unref (G_OBJECT (priv->query));
628 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
629 if (priv->folder_to_select) {
630 g_object_unref (G_OBJECT(priv->folder_to_select));
631 priv->folder_to_select = NULL;
634 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
636 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
638 g_free (priv->local_account_name);
639 g_free (priv->visible_account_id);
641 if (priv->conf_key_signal) {
642 g_signal_handler_disconnect (modest_runtime_get_conf (),
643 priv->conf_key_signal);
644 priv->conf_key_signal = 0;
647 if (priv->cur_folder_store) {
648 if (TNY_IS_FOLDER(priv->cur_folder_store))
649 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
650 /* FALSE --> expunge the message */
652 g_object_unref (priv->cur_folder_store);
653 priv->cur_folder_store = NULL;
656 /* Clear hidding array created by cut operation */
657 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
659 G_OBJECT_CLASS(parent_class)->finalize (obj);
664 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
666 ModestFolderViewPrivate *priv;
669 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
670 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
672 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
673 device = tny_account_store_get_device (account_store);
675 if (G_UNLIKELY (priv->account_store)) {
677 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
678 priv->account_inserted_signal))
679 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
680 priv->account_inserted_signal);
681 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
682 priv->account_removed_signal))
683 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
684 priv->account_removed_signal);
685 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
686 priv->account_changed_signal))
687 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
688 priv->account_changed_signal);
689 g_object_unref (G_OBJECT (priv->account_store));
692 priv->account_store = g_object_ref (G_OBJECT (account_store));
694 priv->account_removed_signal =
695 g_signal_connect (G_OBJECT(account_store), "account_removed",
696 G_CALLBACK (on_account_removed), self);
698 priv->account_inserted_signal =
699 g_signal_connect (G_OBJECT(account_store), "account_inserted",
700 G_CALLBACK (on_account_inserted), self);
702 priv->account_changed_signal =
703 g_signal_connect (G_OBJECT(account_store), "account_changed",
704 G_CALLBACK (on_account_changed), self);
706 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
708 g_object_unref (G_OBJECT (device));
712 on_account_inserted (TnyAccountStore *account_store,
716 ModestFolderViewPrivate *priv;
717 GtkTreeModel *sort_model, *filter_model;
719 /* Ignore transport account insertions, we're not showing them
720 in the folder view */
721 if (TNY_IS_TRANSPORT_ACCOUNT (account))
724 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
726 /* If we're adding a new account, and there is no previous
727 one, we need to select the visible server account */
728 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
729 !priv->visible_account_id)
730 modest_widget_memory_restore (modest_runtime_get_conf(),
731 G_OBJECT (user_data),
732 MODEST_CONF_FOLDER_VIEW_KEY);
734 /* Get the inner model */
735 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
736 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
738 /* Insert the account in the model */
739 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
745 on_account_changed (TnyAccountStore *account_store, TnyAccount *tny_account,
754 on_account_removed (TnyAccountStore *account_store,
758 ModestFolderView *self = NULL;
759 ModestFolderViewPrivate *priv;
760 GtkTreeModel *sort_model, *filter_model;
761 GtkTreeSelection *sel = NULL;
763 /* Ignore transport account removals, we're not showing them
764 in the folder view */
765 if (TNY_IS_TRANSPORT_ACCOUNT (account))
768 self = MODEST_FOLDER_VIEW (user_data);
769 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
771 /* Invalidate the cur_folder_store only if the selected folder
772 belongs to the account that is being removed */
773 if (priv->cur_folder_store) {
774 TnyAccount *selected_folder_account = NULL;
776 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
777 selected_folder_account =
778 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
780 selected_folder_account =
781 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
784 if (selected_folder_account == account) {
785 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
786 gtk_tree_selection_unselect_all (sel);
788 g_object_unref (selected_folder_account);
791 /* Invalidate row to select only if the folder to select
792 belongs to the account that is being removed*/
793 if (priv->folder_to_select) {
794 TnyAccount *folder_to_select_account = NULL;
796 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
797 if (folder_to_select_account == account) {
798 /* modest_folder_view_disable_next_folder_selection (self); */
799 g_object_unref (priv->folder_to_select);
800 priv->folder_to_select = NULL;
802 g_object_unref (folder_to_select_account);
805 /* Remove the account from the model */
806 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
807 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
808 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
811 /* If the removed account is the currently viewed one then
812 clear the configuration value. The new visible account will be the default account */
813 if (priv->visible_account_id &&
814 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
816 /* Clear the current visible account_id */
817 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
819 /* Call the restore method, this will set the new visible account */
820 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
821 MODEST_CONF_FOLDER_VIEW_KEY);
824 /* Select the INBOX */
825 modest_folder_view_select_first_inbox_or_local (self);
829 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
831 GtkTreeViewColumn *col;
833 g_return_if_fail (self);
835 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
837 g_printerr ("modest: failed get column for title\n");
841 gtk_tree_view_column_set_title (col, title);
842 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
847 modest_folder_view_on_map (ModestFolderView *self,
848 GdkEventExpose *event,
851 ModestFolderViewPrivate *priv;
853 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
855 /* This won't happen often */
856 if (G_UNLIKELY (priv->reselect)) {
857 /* Select the first inbox or the local account if not found */
859 /* TODO: this could cause a lock at startup, so we
860 comment it for the moment. We know that this will
861 be a bug, because the INBOX is not selected, but we
862 need to rewrite some parts of Modest to avoid the
863 deathlock situation */
864 /* TODO: check if this is still the case */
865 priv->reselect = FALSE;
866 modest_folder_view_select_first_inbox_or_local (self);
867 /* Notify the display name observers */
868 g_signal_emit (G_OBJECT(self),
869 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
873 expand_root_items (self);
879 modest_folder_view_new (TnyFolderStoreQuery *query)
882 ModestFolderViewPrivate *priv;
883 GtkTreeSelection *sel;
885 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
886 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
889 priv->query = g_object_ref (query);
891 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
892 priv->changed_signal = g_signal_connect (sel, "changed",
893 G_CALLBACK (on_selection_changed), self);
895 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
897 return GTK_WIDGET(self);
900 /* this feels dirty; any other way to expand all the root items? */
902 expand_root_items (ModestFolderView *self)
905 path = gtk_tree_path_new_first ();
907 /* all folders should have child items, so.. */
908 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
909 gtk_tree_path_next (path);
911 gtk_tree_path_free (path);
915 * We use this function to implement the
916 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
917 * account in this case, and the local folders.
920 filter_row (GtkTreeModel *model,
924 ModestFolderViewPrivate *priv;
925 gboolean retval = TRUE;
926 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
927 GObject *instance = NULL;
928 const gchar *id = NULL;
930 gboolean found = FALSE;
931 gboolean cleared = FALSE;
933 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
934 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
936 gtk_tree_model_get (model, iter,
937 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
938 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
941 /* Do not show if there is no instance, this could indeed
942 happen when the model is being modified while it's being
943 drawn. This could occur for example when moving folders
948 if (type == TNY_FOLDER_TYPE_ROOT) {
949 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
950 account instead of a folder. */
951 if (TNY_IS_ACCOUNT (instance)) {
952 TnyAccount *acc = TNY_ACCOUNT (instance);
953 const gchar *account_id = tny_account_get_id (acc);
955 /* If it isn't a special folder,
956 * don't show it unless it is the visible account: */
957 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
958 !modest_tny_account_is_virtual_local_folders (acc) &&
959 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
961 /* Show only the visible account id */
962 if (priv->visible_account_id) {
963 if (strcmp (account_id, priv->visible_account_id))
970 /* Never show these to the user. They are merged into one folder
971 * in the local-folders account instead: */
972 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
977 /* Check hiding (if necessary) */
978 cleared = modest_email_clipboard_cleared (priv->clipboard);
979 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
980 id = tny_folder_get_id (TNY_FOLDER(instance));
981 if (priv->hidding_ids != NULL)
982 for (i=0; i < priv->n_selected && !found; i++)
983 if (priv->hidding_ids[i] != NULL && id != NULL)
984 found = (!strcmp (priv->hidding_ids[i], id));
990 /* If this is a move to dialog, hide Sent, Outbox and Drafts
991 folder as no message can be move there according to UI specs */
992 if (!priv->show_non_move)
996 case TNY_FOLDER_TYPE_OUTBOX:
997 case TNY_FOLDER_TYPE_SENT:
998 case TNY_FOLDER_TYPE_DRAFTS:
1001 case TNY_FOLDER_TYPE_UNKNOWN:
1002 case TNY_FOLDER_TYPE_NORMAL:
1003 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1004 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1005 || type == TNY_FOLDER_TYPE_DRAFTS)
1016 g_object_unref (instance);
1023 modest_folder_view_update_model (ModestFolderView *self,
1024 TnyAccountStore *account_store)
1026 ModestFolderViewPrivate *priv;
1027 GtkTreeModel *model /* , *old_model */;
1028 /* TnyAccount *local_account; */
1029 TnyList *model_as_list;
1031 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1032 g_return_val_if_fail (account_store, FALSE);
1034 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1036 /* Notify that there is no folder selected */
1037 g_signal_emit (G_OBJECT(self),
1038 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1040 if (priv->cur_folder_store) {
1041 g_object_unref (priv->cur_folder_store);
1042 priv->cur_folder_store = NULL;
1045 /* FIXME: the local accounts are not shown when the query
1046 selects only the subscribed folders. */
1047 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1048 model = tny_gtk_folder_store_tree_model_new (NULL);
1050 /* Deal with the model via its TnyList Interface,
1051 * filling the TnyList via a get_accounts() call: */
1052 model_as_list = TNY_LIST(model);
1054 /* Get the accounts: */
1055 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1057 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1058 g_object_unref (model_as_list);
1059 model_as_list = NULL;
1061 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1063 sortable = gtk_tree_model_sort_new_with_model (model);
1064 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1065 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1066 GTK_SORT_ASCENDING);
1067 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1068 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1069 cmp_rows, NULL, NULL);
1071 /* Create filter model */
1072 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1073 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1079 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1080 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1081 (GCallback) on_row_inserted_maybe_select_folder, self);
1084 g_object_unref (model);
1085 g_object_unref (filter_model);
1086 g_object_unref (sortable);
1088 /* Force a reselection of the INBOX next time the widget is shown */
1089 priv->reselect = TRUE;
1096 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1098 GtkTreeModel *model = NULL;
1099 TnyFolderStore *folder = NULL;
1101 ModestFolderView *tree_view = NULL;
1102 ModestFolderViewPrivate *priv = NULL;
1103 gboolean selected = FALSE;
1105 g_return_if_fail (sel);
1106 g_return_if_fail (user_data);
1108 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1110 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1111 /* if(!gtk_tree_selection_get_selected (sel, &model, &iter)) */
1114 /* Notify the display name observers */
1115 g_signal_emit (G_OBJECT(user_data),
1116 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1119 tree_view = MODEST_FOLDER_VIEW (user_data);
1122 gtk_tree_model_get (model, &iter,
1123 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1126 /* If the folder is the same do not notify */
1127 if (priv->cur_folder_store == folder && folder) {
1128 g_object_unref (folder);
1133 /* Current folder was unselected */
1134 if (priv->cur_folder_store) {
1135 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1136 priv->cur_folder_store, FALSE);
1138 if (TNY_IS_FOLDER(priv->cur_folder_store))
1139 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1140 FALSE, NULL, NULL, NULL);
1142 /* FALSE --> don't expunge the messages */
1144 g_object_unref (priv->cur_folder_store);
1145 priv->cur_folder_store = NULL;
1148 /* New current references */
1149 priv->cur_folder_store = folder;
1151 /* New folder has been selected */
1152 g_signal_emit (G_OBJECT(tree_view),
1153 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1154 0, priv->cur_folder_store, TRUE);
1158 modest_folder_view_get_selected (ModestFolderView *self)
1160 ModestFolderViewPrivate *priv;
1162 g_return_val_if_fail (self, NULL);
1164 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1165 if (priv->cur_folder_store)
1166 g_object_ref (priv->cur_folder_store);
1168 return priv->cur_folder_store;
1172 get_cmp_rows_type_pos (GObject *folder)
1174 /* Remote accounts -> Local account -> MMC account .*/
1177 if (TNY_IS_ACCOUNT (folder) &&
1178 modest_tny_account_is_virtual_local_folders (
1179 TNY_ACCOUNT (folder))) {
1181 } else if (TNY_IS_ACCOUNT (folder)) {
1182 TnyAccount *account = TNY_ACCOUNT (folder);
1183 const gchar *account_id = tny_account_get_id (account);
1184 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1190 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1191 return -1; /* Should never happen */
1196 get_cmp_subfolder_type_pos (TnyFolderType t)
1198 /* Inbox, Outbox, Drafts, Sent, User */
1202 case TNY_FOLDER_TYPE_INBOX:
1205 case TNY_FOLDER_TYPE_OUTBOX:
1208 case TNY_FOLDER_TYPE_DRAFTS:
1211 case TNY_FOLDER_TYPE_SENT:
1220 * This function orders the mail accounts according to these rules:
1221 * 1st - remote accounts
1222 * 2nd - local account
1226 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1230 gchar *name1 = NULL;
1231 gchar *name2 = NULL;
1232 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1233 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1234 GObject *folder1 = NULL;
1235 GObject *folder2 = NULL;
1237 gtk_tree_model_get (tree_model, iter1,
1238 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1239 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1240 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1242 gtk_tree_model_get (tree_model, iter2,
1243 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1244 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1245 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1248 /* Return if we get no folder. This could happen when folder
1249 operations are happening. The model is updated after the
1250 folder copy/move actually occurs, so there could be
1251 situations where the model to be drawn is not correct */
1252 if (!folder1 || !folder2)
1255 if (type == TNY_FOLDER_TYPE_ROOT) {
1256 /* Compare the types, so that
1257 * Remote accounts -> Local account -> MMC account .*/
1258 const gint pos1 = get_cmp_rows_type_pos (folder1);
1259 const gint pos2 = get_cmp_rows_type_pos (folder2);
1260 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1261 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1264 else if (pos1 > pos2)
1267 /* Compare items of the same type: */
1269 TnyAccount *account1 = NULL;
1270 if (TNY_IS_ACCOUNT (folder1))
1271 account1 = TNY_ACCOUNT (folder1);
1273 TnyAccount *account2 = NULL;
1274 if (TNY_IS_ACCOUNT (folder2))
1275 account2 = TNY_ACCOUNT (folder2);
1277 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1278 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1280 if (!account_id && !account_id2) {
1282 } else if (!account_id) {
1284 } else if (!account_id2) {
1286 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1289 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1293 gint cmp1 = 0, cmp2 = 0;
1294 /* get the parent to know if it's a local folder */
1297 gboolean has_parent;
1298 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1300 GObject *parent_folder;
1301 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1302 gtk_tree_model_get (tree_model, &parent,
1303 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1304 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1306 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1307 TNY_IS_ACCOUNT (parent_folder) &&
1308 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1309 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1310 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1312 g_object_unref (parent_folder);
1315 /* if they are not local folders */
1317 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1318 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1322 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1324 cmp = (cmp1 - cmp2);
1329 g_object_unref(G_OBJECT(folder1));
1331 g_object_unref(G_OBJECT(folder2));
1339 /*****************************************************************************/
1340 /* DRAG and DROP stuff */
1341 /*****************************************************************************/
1344 * This function fills the #GtkSelectionData with the row and the
1345 * model that has been dragged. It's called when this widget is a
1346 * source for dnd after the event drop happened
1349 on_drag_data_get (GtkWidget *widget,
1350 GdkDragContext *context,
1351 GtkSelectionData *selection_data,
1356 GtkTreeSelection *selection;
1357 GtkTreeModel *model;
1359 GtkTreePath *source_row;
1361 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1362 gtk_tree_selection_get_selected (selection, &model, &iter);
1363 source_row = gtk_tree_model_get_path (model, &iter);
1365 gtk_tree_set_row_drag_data (selection_data,
1369 gtk_tree_path_free (source_row);
1372 typedef struct _DndHelper {
1373 gboolean delete_source;
1374 GtkTreePath *source_row;
1375 GdkDragContext *context;
1381 * This function is the callback of the
1382 * modest_mail_operation_xfer_msgs () and
1383 * modest_mail_operation_xfer_folder() calls. We check here if the
1384 * message/folder was correctly asynchronously transferred. The reason
1385 * to use the same callback is that the code is the same, it only has
1386 * to check that the operation went fine and then finalize the drag
1390 on_progress_changed (ModestMailOperation *mail_op,
1391 ModestMailOperationState *state,
1397 helper = (DndHelper *) user_data;
1399 if (!state->finished)
1402 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1408 /* Notify the drag source. Never call delete, the monitor will
1409 do the job if needed */
1410 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1412 /* Free the helper */
1413 gtk_tree_path_free (helper->source_row);
1414 g_slice_free (DndHelper, helper);
1418 /* get the folder for the row the treepath refers to. */
1419 /* folder must be unref'd */
1421 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1424 TnyFolder *folder = NULL;
1426 if (gtk_tree_model_get_iter (model,&iter, path))
1427 gtk_tree_model_get (model, &iter,
1428 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1434 show_banner_move_target_error ()
1436 ModestWindow *main_window;
1438 main_window = modest_window_mgr_get_main_window(
1439 modest_runtime_get_window_mgr());
1441 modest_platform_information_banner(GTK_WIDGET(main_window),
1442 NULL, _("mail_in_ui_folder_move_target_error"));
1446 * This function is used by drag_data_received_cb to manage drag and
1447 * drop of a header, i.e, and drag from the header view to the folder
1451 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1452 GtkTreeModel *dest_model,
1453 GtkTreePath *dest_row,
1456 TnyList *headers = NULL;
1457 TnyHeader *header = NULL;
1458 TnyFolder *folder = NULL;
1459 ModestMailOperation *mail_op = NULL;
1460 GtkTreeIter source_iter;
1461 ModestWindowMgr *mgr = NULL; /*no need for unref*/
1462 ModestWindow *main_win = NULL; /*no need for unref*/
1464 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1465 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1466 g_return_if_fail (dest_row);
1467 g_return_if_fail (helper);
1470 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1471 gtk_tree_model_get (source_model, &source_iter,
1472 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1474 if (!TNY_IS_HEADER(header)) {
1475 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1479 /* Check if the selected message is in msg-view. If it is than
1480 * do not enable drag&drop on that. */
1481 mgr = modest_runtime_get_window_mgr ();
1482 if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1486 folder = tree_path_to_folder (dest_model, dest_row);
1487 if (!TNY_IS_FOLDER(folder)) {
1488 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1489 show_banner_move_target_error();
1492 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1493 g_debug ("folder rules: cannot write to that folder");
1497 headers = tny_simple_list_new ();
1498 tny_list_append (headers, G_OBJECT (header));
1500 main_win = modest_window_mgr_get_main_window(mgr);
1501 if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1502 == GTK_RESPONSE_CANCEL)
1505 /* Transfer message */
1506 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1508 modest_ui_actions_move_folder_error_handler,
1510 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1512 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1513 G_CALLBACK (on_progress_changed), helper);
1515 modest_mail_operation_xfer_msgs (mail_op,
1518 helper->delete_source,
1523 if (G_IS_OBJECT(mail_op))
1524 g_object_unref (G_OBJECT (mail_op));
1525 if (G_IS_OBJECT(header))
1526 g_object_unref (G_OBJECT (header));
1527 if (G_IS_OBJECT(folder))
1528 g_object_unref (G_OBJECT (folder));
1529 if (G_IS_OBJECT(headers))
1530 g_object_unref (headers);
1534 * This function is used by drag_data_received_cb to manage drag and
1535 * drop of a folder, i.e, and drag from the folder view to the same
1539 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1540 GtkTreeModel *dest_model,
1541 GtkTreePath *dest_row,
1542 GtkSelectionData *selection_data,
1545 ModestMailOperation *mail_op = NULL;
1546 GtkTreeIter dest_iter, iter;
1547 TnyFolderStore *dest_folder = NULL;
1548 TnyFolder *folder = NULL;
1549 gboolean forbidden = FALSE;
1552 /* check the folder rules for the destination */
1553 folder = tree_path_to_folder (dest_model, dest_row);
1554 if (TNY_IS_FOLDER(folder)) {
1555 ModestTnyFolderRules rules =
1556 modest_tny_folder_get_rules (folder);
1557 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1560 g_debug ("folder rules: cannot write to that folder");
1561 } else if (TNY_IS_FOLDER_STORE(folder)){
1562 /* enable local root as destination for folders */
1563 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1564 && TNY_IS_ACCOUNT (folder))
1567 g_object_unref (folder);
1570 /* check the folder rules for the source */
1571 folder = tree_path_to_folder (source_model, helper->source_row);
1572 if (TNY_IS_FOLDER(folder)) {
1573 ModestTnyFolderRules rules =
1574 modest_tny_folder_get_rules (folder);
1575 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1577 g_debug ("folder rules: cannot move that folder");
1580 g_object_unref (folder);
1584 /* Check if the drag is possible */
1585 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1586 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1587 gtk_tree_path_free (helper->source_row);
1588 g_slice_free (DndHelper, helper);
1593 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1594 gtk_tree_model_get (dest_model, &dest_iter,
1595 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1597 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1598 gtk_tree_model_get (source_model, &iter,
1599 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1602 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1603 if (modest_platform_connect_and_wait_if_network_folderstore (
1604 NULL, dest_folder) &&
1605 modest_platform_connect_and_wait_if_network_folderstore (
1606 NULL, TNY_FOLDER_STORE (folder))) {
1607 /* Do the mail operation */
1608 mail_op = modest_mail_operation_new_with_error_handling (
1609 MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1611 modest_ui_actions_move_folder_error_handler,
1613 modest_mail_operation_queue_add (
1614 modest_runtime_get_mail_operation_queue (),
1619 G_CALLBACK (on_progress_changed),
1622 modest_mail_operation_xfer_folder (mail_op,
1625 helper->delete_source,
1629 g_object_unref (G_OBJECT (mail_op));
1633 g_object_unref (G_OBJECT (dest_folder));
1634 g_object_unref (G_OBJECT (folder));
1638 * This function receives the data set by the "drag-data-get" signal
1639 * handler. This information comes within the #GtkSelectionData. This
1640 * function will manage both the drags of folders of the treeview and
1641 * drags of headers of the header view widget.
1644 on_drag_data_received (GtkWidget *widget,
1645 GdkDragContext *context,
1648 GtkSelectionData *selection_data,
1653 GtkWidget *source_widget;
1654 GtkTreeModel *dest_model, *source_model;
1655 GtkTreePath *source_row, *dest_row;
1656 GtkTreeViewDropPosition pos;
1657 gboolean success = FALSE, delete_source = FALSE;
1658 DndHelper *helper = NULL;
1660 /* Do not allow further process */
1661 g_signal_stop_emission_by_name (widget, "drag-data-received");
1662 source_widget = gtk_drag_get_source_widget (context);
1664 /* Get the action */
1665 if (context->action == GDK_ACTION_MOVE) {
1666 delete_source = TRUE;
1668 /* Notify that there is no folder selected. We need to
1669 do this in order to update the headers view (and
1670 its monitors, because when moving, the old folder
1671 won't longer exist. We can not wait for the end of
1672 the operation, because the operation won't start if
1673 the folder is in use */
1674 if (source_widget == widget) {
1675 ModestFolderViewPrivate *priv;
1677 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1678 if (priv->cur_folder_store) {
1679 g_object_unref (priv->cur_folder_store);
1680 priv->cur_folder_store = NULL;
1683 g_signal_emit (G_OBJECT (widget),
1684 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1688 /* Check if the get_data failed */
1689 if (selection_data == NULL || selection_data->length < 0)
1690 gtk_drag_finish (context, success, FALSE, time);
1692 /* Get the models */
1693 gtk_tree_get_row_drag_data (selection_data,
1697 /* Select the destination model */
1698 if (source_widget == widget) {
1699 dest_model = source_model;
1701 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1704 /* Get the path to the destination row. Can not call
1705 gtk_tree_view_get_drag_dest_row() because the source row
1706 is not selected anymore */
1707 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1710 /* Only allow drops IN other rows */
1711 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1712 gtk_drag_finish (context, success, FALSE, time);
1714 /* Create the helper */
1715 helper = g_slice_new0 (DndHelper);
1716 helper->delete_source = delete_source;
1717 helper->source_row = gtk_tree_path_copy (source_row);
1718 helper->context = context;
1719 helper->time = time;
1721 /* Drags from the header view */
1722 if (source_widget != widget) {
1724 drag_and_drop_from_header_view (source_model,
1731 drag_and_drop_from_folder_view (source_model,
1739 gtk_tree_path_free (source_row);
1740 gtk_tree_path_free (dest_row);
1744 * We define a "drag-drop" signal handler because we do not want to
1745 * use the default one, because the default one always calls
1746 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1747 * signal handler, because there we have all the information available
1748 * to know if the dnd was a success or not.
1751 drag_drop_cb (GtkWidget *widget,
1752 GdkDragContext *context,
1760 if (!context->targets)
1763 /* Check if we're dragging a folder row */
1764 target = gtk_drag_dest_find_target (widget, context, NULL);
1766 /* Request the data from the source. */
1767 gtk_drag_get_data(widget, context, target, time);
1773 * This function expands a node of a tree view if it's not expanded
1774 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1775 * does that, so that's why they're here.
1778 expand_row_timeout (gpointer data)
1780 GtkTreeView *tree_view = data;
1781 GtkTreePath *dest_path = NULL;
1782 GtkTreeViewDropPosition pos;
1783 gboolean result = FALSE;
1785 GDK_THREADS_ENTER ();
1787 gtk_tree_view_get_drag_dest_row (tree_view,
1792 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1793 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1794 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1795 gtk_tree_path_free (dest_path);
1799 gtk_tree_path_free (dest_path);
1804 GDK_THREADS_LEAVE ();
1810 * This function is called whenever the pointer is moved over a widget
1811 * while dragging some data. It installs a timeout that will expand a
1812 * node of the treeview if not expanded yet. This function also calls
1813 * gdk_drag_status in order to set the suggested action that will be
1814 * used by the "drag-data-received" signal handler to know if we
1815 * should do a move or just a copy of the data.
1818 on_drag_motion (GtkWidget *widget,
1819 GdkDragContext *context,
1825 GtkTreeViewDropPosition pos;
1826 GtkTreePath *dest_row;
1827 ModestFolderViewPrivate *priv;
1828 GdkDragAction suggested_action;
1829 gboolean valid_location = FALSE;
1831 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1833 if (priv->timer_expander != 0) {
1834 g_source_remove (priv->timer_expander);
1835 priv->timer_expander = 0;
1838 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1843 /* Do not allow drops between folders */
1845 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1846 pos == GTK_TREE_VIEW_DROP_AFTER) {
1847 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1848 gdk_drag_status(context, 0, time);
1849 valid_location = FALSE;
1852 valid_location = TRUE;
1855 /* Expand the selected row after 1/2 second */
1856 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1857 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1858 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1861 /* Select the desired action. By default we pick MOVE */
1862 suggested_action = GDK_ACTION_MOVE;
1864 if (context->actions == GDK_ACTION_COPY)
1865 gdk_drag_status(context, GDK_ACTION_COPY, time);
1866 else if (context->actions == GDK_ACTION_MOVE)
1867 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1868 else if (context->actions & suggested_action)
1869 gdk_drag_status(context, suggested_action, time);
1871 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1875 gtk_tree_path_free (dest_row);
1876 g_signal_stop_emission_by_name (widget, "drag-motion");
1877 return valid_location;
1881 /* Folder view drag types */
1882 const GtkTargetEntry folder_view_drag_types[] =
1884 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1885 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1889 * This function sets the treeview as a source and a target for dnd
1890 * events. It also connects all the requirede signals.
1893 setup_drag_and_drop (GtkTreeView *self)
1895 /* Set up the folder view as a dnd destination. Set only the
1896 highlight flag, otherwise gtk will have a different
1898 gtk_drag_dest_set (GTK_WIDGET (self),
1899 GTK_DEST_DEFAULT_HIGHLIGHT,
1900 folder_view_drag_types,
1901 G_N_ELEMENTS (folder_view_drag_types),
1902 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1904 g_signal_connect (G_OBJECT (self),
1905 "drag_data_received",
1906 G_CALLBACK (on_drag_data_received),
1910 /* Set up the treeview as a dnd source */
1911 gtk_drag_source_set (GTK_WIDGET (self),
1913 folder_view_drag_types,
1914 G_N_ELEMENTS (folder_view_drag_types),
1915 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1917 g_signal_connect (G_OBJECT (self),
1919 G_CALLBACK (on_drag_motion),
1922 g_signal_connect (G_OBJECT (self),
1924 G_CALLBACK (on_drag_data_get),
1927 g_signal_connect (G_OBJECT (self),
1929 G_CALLBACK (drag_drop_cb),
1934 * This function manages the navigation through the folders using the
1935 * keyboard or the hardware keys in the device
1938 on_key_pressed (GtkWidget *self,
1942 GtkTreeSelection *selection;
1944 GtkTreeModel *model;
1945 gboolean retval = FALSE;
1947 /* Up and Down are automatically managed by the treeview */
1948 if (event->keyval == GDK_Return) {
1949 /* Expand/Collapse the selected row */
1950 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1951 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1954 path = gtk_tree_model_get_path (model, &iter);
1956 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1957 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1959 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1960 gtk_tree_path_free (path);
1962 /* No further processing */
1970 * We listen to the changes in the local folder account name key,
1971 * because we want to show the right name in the view. The local
1972 * folder account name corresponds to the device name in the Maemo
1973 * version. We do this because we do not want to query gconf on each
1974 * tree view refresh. It's better to cache it and change whenever
1978 on_configuration_key_changed (ModestConf* conf,
1980 ModestConfEvent event,
1981 ModestConfNotificationId id,
1982 ModestFolderView *self)
1984 ModestFolderViewPrivate *priv;
1987 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1988 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1990 /* Do not listen for changes in other namespaces */
1991 if (priv->notification_id != id)
1994 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1995 g_free (priv->local_account_name);
1997 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1998 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2000 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2001 MODEST_CONF_DEVICE_NAME, NULL);
2003 /* Force a redraw */
2004 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2005 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2006 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2007 gtk_tree_view_column_queue_resize (tree_column);
2013 modest_folder_view_set_style (ModestFolderView *self,
2014 ModestFolderViewStyle style)
2016 ModestFolderViewPrivate *priv;
2018 g_return_if_fail (self);
2020 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2022 priv->style = style;
2026 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2027 const gchar *account_id)
2029 ModestFolderViewPrivate *priv;
2030 GtkTreeModel *model;
2032 g_return_if_fail (self);
2034 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2036 /* This will be used by the filter_row callback,
2037 * to decided which rows to show: */
2038 if (priv->visible_account_id) {
2039 g_free (priv->visible_account_id);
2040 priv->visible_account_id = NULL;
2043 priv->visible_account_id = g_strdup (account_id);
2046 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2047 if (GTK_IS_TREE_MODEL_FILTER (model))
2048 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2050 /* Save settings to gconf */
2051 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2052 MODEST_CONF_FOLDER_VIEW_KEY);
2056 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2058 ModestFolderViewPrivate *priv;
2060 g_return_val_if_fail (self, NULL);
2062 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2064 return (const gchar *) priv->visible_account_id;
2068 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2072 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2074 gtk_tree_model_get (model, iter,
2075 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2078 gboolean result = FALSE;
2079 if (type == TNY_FOLDER_TYPE_INBOX) {
2083 *inbox_iter = *iter;
2087 if (gtk_tree_model_iter_children (model, &child, iter)) {
2088 if (find_inbox_iter (model, &child, inbox_iter))
2092 } while (gtk_tree_model_iter_next (model, iter));
2101 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2103 GtkTreeModel *model;
2104 GtkTreeIter iter, inbox_iter;
2105 GtkTreeSelection *sel;
2106 GtkTreePath *path = NULL;
2108 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2112 expand_root_items (self);
2113 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2115 gtk_tree_model_get_iter_first (model, &iter);
2117 if (find_inbox_iter (model, &iter, &inbox_iter))
2118 path = gtk_tree_model_get_path (model, &inbox_iter);
2120 path = gtk_tree_path_new_first ();
2122 /* Select the row and free */
2123 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2124 gtk_tree_path_free (path);
2127 gtk_widget_grab_focus (GTK_WIDGET(self));
2133 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2138 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2139 TnyFolder* a_folder;
2142 gtk_tree_model_get (model, iter,
2143 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2144 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2145 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2149 if (folder == a_folder) {
2150 g_object_unref (a_folder);
2151 *folder_iter = *iter;
2154 g_object_unref (a_folder);
2156 if (gtk_tree_model_iter_children (model, &child, iter)) {
2157 if (find_folder_iter (model, &child, folder_iter, folder))
2161 } while (gtk_tree_model_iter_next (model, iter));
2168 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2169 ModestFolderView *self)
2171 ModestFolderViewPrivate *priv = NULL;
2172 GtkTreeSelection *sel;
2174 if (!MODEST_IS_FOLDER_VIEW(self))
2177 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2179 if (priv->folder_to_select) {
2181 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2184 path = gtk_tree_model_get_path (tree_model, iter);
2185 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2187 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2189 gtk_tree_selection_select_iter (sel, iter);
2190 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2192 gtk_tree_path_free (path);
2197 modest_folder_view_disable_next_folder_selection (self);
2198 /* g_object_unref (priv->folder_to_select); */
2199 /* priv->folder_to_select = NULL; */
2205 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2207 ModestFolderViewPrivate *priv = NULL;
2209 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2210 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2212 if (priv->folder_to_select)
2213 g_object_unref(priv->folder_to_select);
2215 priv->folder_to_select = NULL;
2219 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2220 gboolean after_change)
2222 GtkTreeModel *model;
2223 GtkTreeIter iter, folder_iter;
2224 GtkTreeSelection *sel;
2225 ModestFolderViewPrivate *priv = NULL;
2227 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2228 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2230 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2234 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2235 gtk_tree_selection_unselect_all (sel);
2237 if (priv->folder_to_select)
2238 g_object_unref(priv->folder_to_select);
2239 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2243 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2248 gtk_tree_model_get_iter_first (model, &iter);
2249 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2252 path = gtk_tree_model_get_path (model, &folder_iter);
2253 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2255 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2256 gtk_tree_selection_select_iter (sel, &folder_iter);
2257 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2259 gtk_tree_path_free (path);
2267 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2269 /* Copy selection */
2270 _clipboard_set_selected_data (folder_view, FALSE);
2274 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2276 ModestFolderViewPrivate *priv = NULL;
2277 GtkTreeModel *model = NULL;
2278 const gchar **hidding = NULL;
2279 guint i, n_selected;
2281 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2282 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2284 /* Copy selection */
2285 if (!_clipboard_set_selected_data (folder_view, TRUE))
2288 /* Get hidding ids */
2289 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2291 /* Clear hidding array created by previous cut operation */
2292 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2294 /* Copy hidding array */
2295 priv->n_selected = n_selected;
2296 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2297 for (i=0; i < n_selected; i++)
2298 priv->hidding_ids[i] = g_strdup(hidding[i]);
2300 /* Hide cut folders */
2301 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2302 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2306 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2307 ModestFolderView *folder_view_dst)
2309 GtkTreeModel *filter_model = NULL;
2310 GtkTreeModel *model = NULL;
2311 GtkTreeModel *new_filter_model = NULL;
2313 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2314 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2317 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2318 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2320 /* Build new filter model */
2321 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2322 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2326 /* Set copied model */
2327 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2330 g_object_unref (new_filter_model);
2334 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2337 GtkTreeModel *model = NULL;
2338 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2339 priv->show_non_move = show;
2340 /* modest_folder_view_update_model(folder_view, */
2341 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2343 /* Hide special folders */
2344 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2345 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2346 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2350 /* Returns FALSE if it did not selected anything */
2352 _clipboard_set_selected_data (ModestFolderView *folder_view,
2355 ModestFolderViewPrivate *priv = NULL;
2356 TnyFolderStore *folder = NULL;
2357 gboolean retval = FALSE;
2359 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2360 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2362 /* Set selected data on clipboard */
2363 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2364 folder = modest_folder_view_get_selected (folder_view);
2366 /* Do not allow to select an account */
2367 if (TNY_IS_FOLDER (folder)) {
2368 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2373 g_object_unref (folder);
2379 _clear_hidding_filter (ModestFolderView *folder_view)
2381 ModestFolderViewPrivate *priv;
2384 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2385 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2387 if (priv->hidding_ids != NULL) {
2388 for (i=0; i < priv->n_selected; i++)
2389 g_free (priv->hidding_ids[i]);
2390 g_free(priv->hidding_ids);