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 #ifdef MODEST_TOOLKIT_HILDON2
36 #include <tny-gtk-folder-list-store.h>
38 #include <tny-gtk-folder-store-tree-model.h>
40 #include <tny-gtk-header-list-model.h>
41 #include <tny-merge-folder.h>
42 #include <tny-folder.h>
43 #include <tny-folder-store-observer.h>
44 #include <tny-account-store.h>
45 #include <tny-account.h>
46 #include <tny-folder.h>
47 #include <tny-camel-folder.h>
48 #include <tny-simple-list.h>
49 #include <tny-camel-account.h>
50 #include <modest-tny-account.h>
51 #include <modest-tny-folder.h>
52 #include <modest-tny-local-folders-account.h>
53 #include <modest-tny-outbox-account.h>
54 #include <modest-marshal.h>
55 #include <modest-icon-names.h>
56 #include <modest-tny-account-store.h>
57 #include <modest-tny-local-folders-account.h>
58 #include <modest-text-utils.h>
59 #include <modest-runtime.h>
60 #include "modest-folder-view.h"
61 #include <modest-platform.h>
62 #include <modest-widget-memory.h>
63 #include <modest-ui-actions.h>
64 #include "modest-dnd.h"
65 #include <modest-ui-constants.h>
66 #include "widgets/modest-window.h"
68 /* Folder view drag types */
69 const GtkTargetEntry folder_view_drag_types[] =
71 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
72 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
75 /* Default icon sizes for Fremantle style are different */
76 #ifdef MODEST_TOOLKIT_HILDON2
77 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
79 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
82 /* Column names depending on we use list store or tree store */
83 #ifdef MODEST_TOOLKIT_HILDON2
84 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
85 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
86 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
87 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
88 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
90 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
91 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
92 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
93 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
94 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
97 /* 'private'/'protected' functions */
98 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
99 static void modest_folder_view_init (ModestFolderView *obj);
100 static void modest_folder_view_finalize (GObject *obj);
102 static void tny_account_store_view_init (gpointer g,
103 gpointer iface_data);
105 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
106 TnyAccountStore *account_store);
108 static void on_selection_changed (GtkTreeSelection *sel,
111 static void on_row_activated (GtkTreeView *treeview,
113 GtkTreeViewColumn *column,
116 static void on_account_removed (TnyAccountStore *self,
120 static void on_account_inserted (TnyAccountStore *self,
124 static void on_account_changed (TnyAccountStore *self,
128 static gint cmp_rows (GtkTreeModel *tree_model,
133 static gboolean filter_row (GtkTreeModel *model,
137 static gboolean on_key_pressed (GtkWidget *self,
141 static void on_configuration_key_changed (ModestConf* conf,
143 ModestConfEvent event,
144 ModestConfNotificationId notification_id,
145 ModestFolderView *self);
148 static void on_drag_data_get (GtkWidget *widget,
149 GdkDragContext *context,
150 GtkSelectionData *selection_data,
155 static void on_drag_data_received (GtkWidget *widget,
156 GdkDragContext *context,
159 GtkSelectionData *selection_data,
164 static gboolean on_drag_motion (GtkWidget *widget,
165 GdkDragContext *context,
171 static void expand_root_items (ModestFolderView *self);
173 static gint expand_row_timeout (gpointer data);
175 static void setup_drag_and_drop (GtkTreeView *self);
177 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
180 static void _clear_hidding_filter (ModestFolderView *folder_view);
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
186 ModestFolderView *self);
189 static void on_display_name_changed (ModestAccountMgr *self,
190 const gchar *account,
192 static void update_style (ModestFolderView *self);
193 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
197 FOLDER_SELECTION_CHANGED_SIGNAL,
198 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
199 FOLDER_ACTIVATED_SIGNAL,
203 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
204 struct _ModestFolderViewPrivate {
205 TnyAccountStore *account_store;
206 TnyFolderStore *cur_folder_store;
208 TnyFolder *folder_to_select; /* folder to select after the next update */
210 gulong changed_signal;
211 gulong account_inserted_signal;
212 gulong account_removed_signal;
213 gulong account_changed_signal;
214 gulong conf_key_signal;
215 gulong display_name_changed_signal;
217 /* not unref this object, its a singlenton */
218 ModestEmailClipboard *clipboard;
220 /* Filter tree model */
224 TnyFolderStoreQuery *query;
225 guint timer_expander;
227 gchar *local_account_name;
228 gchar *visible_account_id;
229 ModestFolderViewStyle style;
230 ModestFolderViewCellStyle cell_style;
232 gboolean reselect; /* we use this to force a reselection of the INBOX */
233 gboolean show_non_move;
234 gboolean reexpand; /* next time we expose, we'll expand all root folders */
236 GtkCellRenderer *messages_renderer;
238 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
239 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
240 MODEST_TYPE_FOLDER_VIEW, \
241 ModestFolderViewPrivate))
243 static GObjectClass *parent_class = NULL;
245 static guint signals[LAST_SIGNAL] = {0};
248 modest_folder_view_get_type (void)
250 static GType my_type = 0;
252 static const GTypeInfo my_info = {
253 sizeof(ModestFolderViewClass),
254 NULL, /* base init */
255 NULL, /* base finalize */
256 (GClassInitFunc) modest_folder_view_class_init,
257 NULL, /* class finalize */
258 NULL, /* class data */
259 sizeof(ModestFolderView),
261 (GInstanceInitFunc) modest_folder_view_init,
265 static const GInterfaceInfo tny_account_store_view_info = {
266 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
267 NULL, /* interface_finalize */
268 NULL /* interface_data */
272 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
276 g_type_add_interface_static (my_type,
277 TNY_TYPE_ACCOUNT_STORE_VIEW,
278 &tny_account_store_view_info);
284 modest_folder_view_class_init (ModestFolderViewClass *klass)
286 GObjectClass *gobject_class;
287 GtkTreeViewClass *treeview_class;
288 gobject_class = (GObjectClass*) klass;
289 treeview_class = (GtkTreeViewClass*) klass;
291 parent_class = g_type_class_peek_parent (klass);
292 gobject_class->finalize = modest_folder_view_finalize;
294 g_type_class_add_private (gobject_class,
295 sizeof(ModestFolderViewPrivate));
297 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
298 g_signal_new ("folder_selection_changed",
299 G_TYPE_FROM_CLASS (gobject_class),
301 G_STRUCT_OFFSET (ModestFolderViewClass,
302 folder_selection_changed),
304 modest_marshal_VOID__POINTER_BOOLEAN,
305 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
308 * This signal is emitted whenever the currently selected
309 * folder display name is computed. Note that the name could
310 * be different to the folder name, because we could append
311 * the unread messages count to the folder name to build the
312 * folder display name
314 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
315 g_signal_new ("folder-display-name-changed",
316 G_TYPE_FROM_CLASS (gobject_class),
318 G_STRUCT_OFFSET (ModestFolderViewClass,
319 folder_display_name_changed),
321 g_cclosure_marshal_VOID__STRING,
322 G_TYPE_NONE, 1, G_TYPE_STRING);
324 signals[FOLDER_ACTIVATED_SIGNAL] =
325 g_signal_new ("folder_activated",
326 G_TYPE_FROM_CLASS (gobject_class),
328 G_STRUCT_OFFSET (ModestFolderViewClass,
331 g_cclosure_marshal_VOID__POINTER,
332 G_TYPE_NONE, 1, G_TYPE_POINTER);
334 treeview_class->select_cursor_parent = NULL;
336 #ifdef MODEST_TOOLKIT_HILDON2
337 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
343 /* Simplify checks for NULLs: */
345 strings_are_equal (const gchar *a, const gchar *b)
351 return (strcmp (a, b) == 0);
358 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
360 GObject *instance = NULL;
362 gtk_tree_model_get (model, iter,
363 INSTANCE_COLUMN, &instance,
367 return FALSE; /* keep walking */
369 if (!TNY_IS_ACCOUNT (instance)) {
370 g_object_unref (instance);
371 return FALSE; /* keep walking */
374 /* Check if this is the looked-for account: */
375 TnyAccount *this_account = TNY_ACCOUNT (instance);
376 TnyAccount *account = TNY_ACCOUNT (data);
378 const gchar *this_account_id = tny_account_get_id(this_account);
379 const gchar *account_id = tny_account_get_id(account);
380 g_object_unref (instance);
383 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
384 if (strings_are_equal(this_account_id, account_id)) {
385 /* Tell the model that the data has changed, so that
386 * it calls the cell_data_func callbacks again: */
387 /* TODO: This does not seem to actually cause the new string to be shown: */
388 gtk_tree_model_row_changed (model, path, iter);
390 return TRUE; /* stop walking */
393 return FALSE; /* keep walking */
398 ModestFolderView *self;
399 gchar *previous_name;
400 } GetMmcAccountNameData;
403 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
405 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
407 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
409 if (!strings_are_equal (
410 tny_account_get_name(TNY_ACCOUNT(account)),
411 data->previous_name)) {
413 /* Tell the model that the data has changed, so that
414 * it calls the cell_data_func callbacks again: */
415 ModestFolderView *self = data->self;
416 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
418 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
421 g_free (data->previous_name);
422 g_slice_free (GetMmcAccountNameData, data);
426 format_compact_style (gchar **item_name,
429 gboolean multiaccount,
430 gboolean *use_markup)
434 TnyFolderType folder_type;
436 if (!TNY_IS_FOLDER (instance))
439 folder = (TnyFolder *) instance;
441 folder_type = tny_folder_get_folder_type (folder);
442 is_special = (get_cmp_pos (folder_type, folder)!= 4);
444 if (!is_special || multiaccount) {
445 TnyAccount *account = tny_folder_get_account (folder);
446 const gchar *folder_name;
447 gboolean concat_folder_name = FALSE;
450 /* Should not happen */
454 folder_name = tny_folder_get_name (folder);
455 if (g_str_has_suffix (*item_name, folder_name)) {
456 gchar *offset = g_strrstr (*item_name, folder_name);
458 concat_folder_name = TRUE;
461 buffer = g_string_new ("");
462 buffer = g_string_append (buffer, tny_account_get_name (account));
463 buffer = g_string_append (buffer, MODEST_FOLDER_PATH_SEPARATOR);
464 buffer = g_string_append (buffer, *item_name);
465 if (concat_folder_name) {
466 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
467 buffer = g_string_append (buffer, folder_name);
468 if (bold) buffer = g_string_append (buffer, "</span>");
471 g_object_unref (account);
473 *item_name = g_string_free (buffer, FALSE);
481 text_cell_data (GtkTreeViewColumn *column,
482 GtkCellRenderer *renderer,
483 GtkTreeModel *tree_model,
487 ModestFolderViewPrivate *priv;
488 GObject *rendobj = (GObject *) renderer;
490 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
491 GObject *instance = NULL;
492 gboolean use_markup = FALSE;
494 gtk_tree_model_get (tree_model, iter,
497 INSTANCE_COLUMN, &instance,
499 if (!fname || !instance)
502 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
503 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
505 gchar *item_name = NULL;
506 gint item_weight = 400;
508 if (type != TNY_FOLDER_TYPE_ROOT) {
512 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
513 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
514 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
515 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
517 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
520 /* Sometimes an special folder is reported by the server as
521 NORMAL, like some versions of Dovecot */
522 if (type == TNY_FOLDER_TYPE_NORMAL ||
523 type == TNY_FOLDER_TYPE_UNKNOWN) {
524 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
528 if (type == TNY_FOLDER_TYPE_INBOX) {
530 fname = g_strdup (_("mcen_me_folder_inbox"));
533 /* note: we cannot reliably get the counts from the tree model, we need
534 * to use explicit calls on tny_folder for some reason.
536 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
537 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
538 (type == TNY_FOLDER_TYPE_OUTBOX) ||
539 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
540 number = tny_folder_get_all_count (TNY_FOLDER(instance));
543 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
547 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
548 item_name = g_strdup (fname);
555 /* Use bold font style if there are unread or unset messages */
557 item_name = g_strdup_printf ("%s (%d)", fname, number);
560 item_name = g_strdup (fname);
565 } else if (TNY_IS_ACCOUNT (instance)) {
566 /* If it's a server account */
567 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
568 item_name = g_strdup (priv->local_account_name);
570 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
571 /* fname is only correct when the items are first
572 * added to the model, not when the account is
573 * changed later, so get the name from the account
575 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
578 item_name = g_strdup (fname);
584 item_name = g_strdup ("unknown");
586 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
587 gboolean multiaccount;
589 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
590 /* Convert item_name to markup */
591 format_compact_style (&item_name, instance,
593 multiaccount, &use_markup);
596 if (item_name && item_weight) {
597 /* Set the name in the treeview cell: */
599 g_object_set (rendobj, "markup", item_name, NULL);
601 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
603 /* Notify display name observers */
604 /* TODO: What listens for this signal, and how can it use only the new name? */
605 if (((GObject *) priv->cur_folder_store) == instance) {
606 g_signal_emit (G_OBJECT(self),
607 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
614 /* If it is a Memory card account, make sure that we have the correct name.
615 * This function will be trigerred again when the name has been retrieved: */
616 if (TNY_IS_STORE_ACCOUNT (instance) &&
617 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
619 /* Get the account name asynchronously: */
620 GetMmcAccountNameData *callback_data =
621 g_slice_new0(GetMmcAccountNameData);
622 callback_data->self = self;
624 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
626 callback_data->previous_name = g_strdup (name);
628 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
629 on_get_mmc_account_name, callback_data);
633 g_object_unref (G_OBJECT (instance));
639 messages_cell_data (GtkTreeViewColumn *column,
640 GtkCellRenderer *renderer,
641 GtkTreeModel *tree_model,
645 ModestFolderView *self;
646 ModestFolderViewPrivate *priv;
647 GObject *rendobj = (GObject *) renderer;
648 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
649 GObject *instance = NULL;
650 gchar *item_name = NULL;
652 gtk_tree_model_get (tree_model, iter,
654 INSTANCE_COLUMN, &instance,
659 self = MODEST_FOLDER_VIEW (data);
660 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
663 if (type != TNY_FOLDER_TYPE_ROOT) {
667 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
668 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
669 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
671 /* Sometimes an special folder is reported by the server as
672 NORMAL, like some versions of Dovecot */
673 if (type == TNY_FOLDER_TYPE_NORMAL ||
674 type == TNY_FOLDER_TYPE_UNKNOWN) {
675 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
679 /* note: we cannot reliably get the counts from the tree model, we need
680 * to use explicit calls on tny_folder for some reason.
682 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
683 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
684 (type == TNY_FOLDER_TYPE_OUTBOX) ||
685 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
686 number = tny_folder_get_all_count (TNY_FOLDER(instance));
689 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
693 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
695 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
703 item_name = g_strdup ("");
706 /* Set the name in the treeview cell: */
707 g_object_set (rendobj,"text", item_name, NULL);
715 g_object_unref (G_OBJECT (instance));
721 GdkPixbuf *pixbuf_open;
722 GdkPixbuf *pixbuf_close;
726 static inline GdkPixbuf *
727 get_composite_pixbuf (const gchar *icon_name,
729 GdkPixbuf *base_pixbuf)
731 GdkPixbuf *emblem, *retval = NULL;
733 emblem = modest_platform_get_icon (icon_name, size);
735 retval = gdk_pixbuf_copy (base_pixbuf);
736 gdk_pixbuf_composite (emblem, retval, 0, 0,
737 MIN (gdk_pixbuf_get_width (emblem),
738 gdk_pixbuf_get_width (retval)),
739 MIN (gdk_pixbuf_get_height (emblem),
740 gdk_pixbuf_get_height (retval)),
741 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
742 g_object_unref (emblem);
747 static inline ThreePixbufs *
748 get_composite_icons (const gchar *icon_code,
750 GdkPixbuf **pixbuf_open,
751 GdkPixbuf **pixbuf_close)
753 ThreePixbufs *retval;
756 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
759 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
764 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
768 retval = g_slice_new0 (ThreePixbufs);
770 retval->pixbuf = g_object_ref (*pixbuf);
772 retval->pixbuf_open = g_object_ref (*pixbuf_open);
774 retval->pixbuf_close = g_object_ref (*pixbuf_close);
780 get_folder_icons (TnyFolderType type, GObject *instance)
782 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
783 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
784 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
785 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
786 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
788 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
789 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
790 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
791 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
792 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
794 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
795 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
796 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
797 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
798 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
800 ThreePixbufs *retval = NULL;
802 /* Sometimes an special folder is reported by the server as
803 NORMAL, like some versions of Dovecot */
804 if (type == TNY_FOLDER_TYPE_NORMAL ||
805 type == TNY_FOLDER_TYPE_UNKNOWN) {
806 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
809 /* Remote folders should not be treated as special folders */
810 if (TNY_IS_FOLDER_STORE (instance) &&
811 !TNY_IS_ACCOUNT (instance) &&
812 type != TNY_FOLDER_TYPE_INBOX &&
813 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
814 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
817 &normal_pixbuf_close);
821 case TNY_FOLDER_TYPE_INVALID:
822 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
825 case TNY_FOLDER_TYPE_ROOT:
826 if (TNY_IS_ACCOUNT (instance)) {
828 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
829 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
832 &avirt_pixbuf_close);
834 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
836 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
837 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
842 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
845 &anorm_pixbuf_close);
850 case TNY_FOLDER_TYPE_INBOX:
851 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
854 &inbox_pixbuf_close);
856 case TNY_FOLDER_TYPE_OUTBOX:
857 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
860 &outbox_pixbuf_close);
862 case TNY_FOLDER_TYPE_JUNK:
863 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
868 case TNY_FOLDER_TYPE_SENT:
869 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
874 case TNY_FOLDER_TYPE_TRASH:
875 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
878 &trash_pixbuf_close);
880 case TNY_FOLDER_TYPE_DRAFTS:
881 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
884 &draft_pixbuf_close);
886 case TNY_FOLDER_TYPE_NORMAL:
888 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
891 &normal_pixbuf_close);
899 free_pixbufs (ThreePixbufs *pixbufs)
902 g_object_unref (pixbufs->pixbuf);
903 if (pixbufs->pixbuf_open)
904 g_object_unref (pixbufs->pixbuf_open);
905 if (pixbufs->pixbuf_close)
906 g_object_unref (pixbufs->pixbuf_close);
907 g_slice_free (ThreePixbufs, pixbufs);
911 icon_cell_data (GtkTreeViewColumn *column,
912 GtkCellRenderer *renderer,
913 GtkTreeModel *tree_model,
917 GObject *rendobj = NULL, *instance = NULL;
918 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
919 gboolean has_children;
920 ThreePixbufs *pixbufs;
922 rendobj = (GObject *) renderer;
924 gtk_tree_model_get (tree_model, iter,
926 INSTANCE_COLUMN, &instance,
932 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
933 pixbufs = get_folder_icons (type, instance);
934 g_object_unref (instance);
937 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
940 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
941 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
944 free_pixbufs (pixbufs);
948 add_columns (GtkWidget *treeview)
950 GtkTreeViewColumn *column;
951 GtkCellRenderer *renderer;
952 GtkTreeSelection *sel;
953 ModestFolderViewPrivate *priv;
955 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
958 column = gtk_tree_view_column_new ();
960 /* Set icon and text render function */
961 renderer = gtk_cell_renderer_pixbuf_new();
962 gtk_tree_view_column_pack_start (column, renderer, FALSE);
963 gtk_tree_view_column_set_cell_data_func(column, renderer,
964 icon_cell_data, treeview, NULL);
966 renderer = gtk_cell_renderer_text_new();
967 g_object_set (renderer,
968 #ifdef MODEST_TOOLKIT_HILDON2
969 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
971 "ellipsize", PANGO_ELLIPSIZE_END,
973 "ellipsize-set", TRUE, NULL);
974 gtk_tree_view_column_pack_start (column, renderer, TRUE);
975 gtk_tree_view_column_set_cell_data_func(column, renderer,
976 text_cell_data, treeview, NULL);
978 priv->messages_renderer = gtk_cell_renderer_text_new ();
979 g_object_set (priv->messages_renderer,
980 "scale", PANGO_SCALE_X_SMALL,
982 "alignment", PANGO_ALIGN_RIGHT,
986 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
987 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
988 messages_cell_data, treeview, NULL);
990 /* Set selection mode */
991 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
992 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
994 /* Set treeview appearance */
995 gtk_tree_view_column_set_spacing (column, 2);
996 gtk_tree_view_column_set_resizable (column, TRUE);
997 gtk_tree_view_column_set_fixed_width (column, TRUE);
998 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
999 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1002 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1006 modest_folder_view_init (ModestFolderView *obj)
1008 ModestFolderViewPrivate *priv;
1011 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1013 priv->timer_expander = 0;
1014 priv->account_store = NULL;
1016 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1017 priv->cur_folder_store = NULL;
1018 priv->visible_account_id = NULL;
1019 priv->folder_to_select = NULL;
1021 priv->reexpand = TRUE;
1023 /* Initialize the local account name */
1024 conf = modest_runtime_get_conf();
1025 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1027 /* Init email clipboard */
1028 priv->clipboard = modest_runtime_get_email_clipboard ();
1029 priv->hidding_ids = NULL;
1030 priv->n_selected = 0;
1031 priv->reselect = FALSE;
1032 priv->show_non_move = TRUE;
1034 /* Build treeview */
1035 add_columns (GTK_WIDGET (obj));
1037 /* Setup drag and drop */
1038 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1040 /* Connect signals */
1041 g_signal_connect (G_OBJECT (obj),
1043 G_CALLBACK (on_key_pressed), NULL);
1045 priv->display_name_changed_signal =
1046 g_signal_connect (modest_runtime_get_account_mgr (),
1047 "display_name_changed",
1048 G_CALLBACK (on_display_name_changed),
1052 * Track changes in the local account name (in the device it
1053 * will be the device name)
1055 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1057 G_CALLBACK(on_configuration_key_changed),
1061 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1067 tny_account_store_view_init (gpointer g, gpointer iface_data)
1069 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1071 klass->set_account_store = modest_folder_view_set_account_store;
1075 modest_folder_view_finalize (GObject *obj)
1077 ModestFolderViewPrivate *priv;
1078 GtkTreeSelection *sel;
1080 g_return_if_fail (obj);
1082 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1084 if (priv->timer_expander != 0) {
1085 g_source_remove (priv->timer_expander);
1086 priv->timer_expander = 0;
1089 if (priv->account_store) {
1090 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1091 priv->account_inserted_signal);
1092 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1093 priv->account_removed_signal);
1094 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1095 priv->account_changed_signal);
1096 g_object_unref (G_OBJECT(priv->account_store));
1097 priv->account_store = NULL;
1100 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1101 priv->display_name_changed_signal)) {
1102 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1103 priv->display_name_changed_signal);
1104 priv->display_name_changed_signal = 0;
1108 g_object_unref (G_OBJECT (priv->query));
1112 if (priv->folder_to_select) {
1113 g_object_unref (G_OBJECT(priv->folder_to_select));
1114 priv->folder_to_select = NULL;
1117 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1119 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1121 g_free (priv->local_account_name);
1122 g_free (priv->visible_account_id);
1124 if (priv->conf_key_signal) {
1125 g_signal_handler_disconnect (modest_runtime_get_conf (),
1126 priv->conf_key_signal);
1127 priv->conf_key_signal = 0;
1130 if (priv->cur_folder_store) {
1131 g_object_unref (priv->cur_folder_store);
1132 priv->cur_folder_store = NULL;
1135 /* Clear hidding array created by cut operation */
1136 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1138 G_OBJECT_CLASS(parent_class)->finalize (obj);
1143 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1145 ModestFolderViewPrivate *priv;
1148 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1149 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1151 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1152 device = tny_account_store_get_device (account_store);
1154 if (G_UNLIKELY (priv->account_store)) {
1156 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1157 priv->account_inserted_signal))
1158 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1159 priv->account_inserted_signal);
1160 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1161 priv->account_removed_signal))
1162 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1163 priv->account_removed_signal);
1164 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1165 priv->account_changed_signal))
1166 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1167 priv->account_changed_signal);
1168 g_object_unref (G_OBJECT (priv->account_store));
1171 priv->account_store = g_object_ref (G_OBJECT (account_store));
1173 priv->account_removed_signal =
1174 g_signal_connect (G_OBJECT(account_store), "account_removed",
1175 G_CALLBACK (on_account_removed), self);
1177 priv->account_inserted_signal =
1178 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1179 G_CALLBACK (on_account_inserted), self);
1181 priv->account_changed_signal =
1182 g_signal_connect (G_OBJECT(account_store), "account_changed",
1183 G_CALLBACK (on_account_changed), self);
1185 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1186 priv->reselect = FALSE;
1187 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1189 g_object_unref (G_OBJECT (device));
1193 on_account_inserted (TnyAccountStore *account_store,
1194 TnyAccount *account,
1197 ModestFolderViewPrivate *priv;
1198 GtkTreeModel *sort_model, *filter_model;
1200 /* Ignore transport account insertions, we're not showing them
1201 in the folder view */
1202 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1205 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1208 /* If we're adding a new account, and there is no previous
1209 one, we need to select the visible server account */
1210 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1211 !priv->visible_account_id)
1212 modest_widget_memory_restore (modest_runtime_get_conf(),
1213 G_OBJECT (user_data),
1214 MODEST_CONF_FOLDER_VIEW_KEY);
1216 if (!GTK_IS_TREE_VIEW(user_data)) {
1217 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1221 /* Get the inner model */
1222 /* check, is some rare cases, we did not get the right thing here,
1224 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1225 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1226 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1230 /* check, is some rare cases, we did not get the right thing here,
1232 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1233 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1234 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1238 /* Insert the account in the model */
1239 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1240 G_OBJECT (account));
1242 /* Refilter the model */
1243 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1248 same_account_selected (ModestFolderView *self,
1249 TnyAccount *account)
1251 ModestFolderViewPrivate *priv;
1252 gboolean same_account = FALSE;
1254 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1256 if (priv->cur_folder_store) {
1257 TnyAccount *selected_folder_account = NULL;
1259 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1260 selected_folder_account =
1261 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1263 selected_folder_account =
1264 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1267 if (selected_folder_account == account)
1268 same_account = TRUE;
1270 g_object_unref (selected_folder_account);
1272 return same_account;
1277 * Selects the first inbox or the local account in an idle
1280 on_idle_select_first_inbox_or_local (gpointer user_data)
1282 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1284 gdk_threads_enter ();
1285 modest_folder_view_select_first_inbox_or_local (self);
1286 gdk_threads_leave ();
1292 on_account_changed (TnyAccountStore *account_store,
1293 TnyAccount *tny_account,
1296 ModestFolderView *self;
1297 ModestFolderViewPrivate *priv;
1298 GtkTreeModel *sort_model, *filter_model;
1299 GtkTreeSelection *sel;
1300 gboolean same_account;
1302 /* Ignore transport account insertions, we're not showing them
1303 in the folder view */
1304 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1307 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1308 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1312 self = MODEST_FOLDER_VIEW (user_data);
1313 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1315 /* Get the inner model */
1316 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1317 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1318 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1322 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1323 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1324 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1328 /* Invalidate the cur_folder_store only if the selected folder
1329 belongs to the account that is being removed */
1330 same_account = same_account_selected (self, tny_account);
1332 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1333 gtk_tree_selection_unselect_all (sel);
1336 /* Remove the account from the model */
1337 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1338 G_OBJECT (tny_account));
1340 /* Insert the account in the model */
1341 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1342 G_OBJECT (tny_account));
1344 /* Refilter the model */
1345 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1347 /* Select the first INBOX if the currently selected folder
1348 belongs to the account that is being deleted */
1349 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1350 g_idle_add (on_idle_select_first_inbox_or_local, self);
1354 on_account_removed (TnyAccountStore *account_store,
1355 TnyAccount *account,
1358 ModestFolderView *self = NULL;
1359 ModestFolderViewPrivate *priv;
1360 GtkTreeModel *sort_model, *filter_model;
1361 GtkTreeSelection *sel = NULL;
1362 gboolean same_account = FALSE;
1364 /* Ignore transport account removals, we're not showing them
1365 in the folder view */
1366 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1369 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1370 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1374 self = MODEST_FOLDER_VIEW (user_data);
1375 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1377 /* Invalidate the cur_folder_store only if the selected folder
1378 belongs to the account that is being removed */
1379 same_account = same_account_selected (self, account);
1381 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1382 gtk_tree_selection_unselect_all (sel);
1385 /* Invalidate row to select only if the folder to select
1386 belongs to the account that is being removed*/
1387 if (priv->folder_to_select) {
1388 TnyAccount *folder_to_select_account = NULL;
1390 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1391 if (folder_to_select_account == account) {
1392 modest_folder_view_disable_next_folder_selection (self);
1393 g_object_unref (priv->folder_to_select);
1394 priv->folder_to_select = NULL;
1396 g_object_unref (folder_to_select_account);
1399 /* Remove the account from the model */
1400 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1401 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1402 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1406 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1407 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1408 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1412 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1413 G_OBJECT (account));
1415 /* If the removed account is the currently viewed one then
1416 clear the configuration value. The new visible account will be the default account */
1417 if (priv->visible_account_id &&
1418 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1420 /* Clear the current visible account_id */
1421 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1423 /* Call the restore method, this will set the new visible account */
1424 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1425 MODEST_CONF_FOLDER_VIEW_KEY);
1428 /* Refilter the model */
1429 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1431 /* Select the first INBOX if the currently selected folder
1432 belongs to the account that is being deleted */
1434 g_idle_add (on_idle_select_first_inbox_or_local, self);
1438 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1440 GtkTreeViewColumn *col;
1442 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1444 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1446 g_printerr ("modest: failed get column for title\n");
1450 gtk_tree_view_column_set_title (col, title);
1451 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1456 modest_folder_view_on_map (ModestFolderView *self,
1457 GdkEventExpose *event,
1460 ModestFolderViewPrivate *priv;
1462 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1464 /* This won't happen often */
1465 if (G_UNLIKELY (priv->reselect)) {
1466 /* Select the first inbox or the local account if not found */
1468 /* TODO: this could cause a lock at startup, so we
1469 comment it for the moment. We know that this will
1470 be a bug, because the INBOX is not selected, but we
1471 need to rewrite some parts of Modest to avoid the
1472 deathlock situation */
1473 /* TODO: check if this is still the case */
1474 priv->reselect = FALSE;
1475 modest_folder_view_select_first_inbox_or_local (self);
1476 /* Notify the display name observers */
1477 g_signal_emit (G_OBJECT(self),
1478 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1482 if (priv->reexpand) {
1483 expand_root_items (self);
1484 priv->reexpand = FALSE;
1491 modest_folder_view_new (TnyFolderStoreQuery *query)
1494 ModestFolderViewPrivate *priv;
1495 GtkTreeSelection *sel;
1497 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1498 #ifdef MODEST_TOOLKIT_HILDON2
1499 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1502 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1505 priv->query = g_object_ref (query);
1507 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1508 priv->changed_signal = g_signal_connect (sel, "changed",
1509 G_CALLBACK (on_selection_changed), self);
1511 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1513 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1515 return GTK_WIDGET(self);
1518 /* this feels dirty; any other way to expand all the root items? */
1520 expand_root_items (ModestFolderView *self)
1523 GtkTreeModel *model;
1526 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1527 path = gtk_tree_path_new_first ();
1529 /* all folders should have child items, so.. */
1531 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1532 gtk_tree_path_next (path);
1533 } while (gtk_tree_model_get_iter (model, &iter, path));
1535 gtk_tree_path_free (path);
1539 * We use this function to implement the
1540 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1541 * account in this case, and the local folders.
1544 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1546 ModestFolderViewPrivate *priv;
1547 gboolean retval = TRUE;
1548 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1549 GObject *instance = NULL;
1550 const gchar *id = NULL;
1552 gboolean found = FALSE;
1553 gboolean cleared = FALSE;
1555 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1556 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1558 gtk_tree_model_get (model, iter,
1560 INSTANCE_COLUMN, &instance,
1563 /* Do not show if there is no instance, this could indeed
1564 happen when the model is being modified while it's being
1565 drawn. This could occur for example when moving folders
1570 if (TNY_IS_ACCOUNT (instance)) {
1571 #ifdef MODEST_TOOLKIT_HILDON2
1572 /* In hildon 2.2 we don't show the account rows */
1575 TnyAccount *acc = TNY_ACCOUNT (instance);
1576 const gchar *account_id = tny_account_get_id (acc);
1578 /* If it isn't a special folder,
1579 * don't show it unless it is the visible account: */
1580 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1581 !modest_tny_account_is_virtual_local_folders (acc) &&
1582 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1584 /* Show only the visible account id */
1585 if (priv->visible_account_id) {
1586 if (strcmp (account_id, priv->visible_account_id))
1593 /* Never show these to the user. They are merged into one folder
1594 * in the local-folders account instead: */
1595 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1599 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1600 /* Only show special folders for current account if needed */
1601 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1602 TnyAccount *account;
1604 account = tny_folder_get_account (TNY_FOLDER (instance));
1606 if (TNY_IS_ACCOUNT (account)) {
1607 const gchar *account_id = tny_account_get_id (account);
1609 if (!modest_tny_account_is_virtual_local_folders (account) &&
1610 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1611 /* Show only the visible account id */
1612 if (priv->visible_account_id) {
1613 if (strcmp (account_id, priv->visible_account_id))
1617 g_object_unref (account);
1624 /* Check hiding (if necessary) */
1625 cleared = modest_email_clipboard_cleared (priv->clipboard);
1626 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1627 id = tny_folder_get_id (TNY_FOLDER(instance));
1628 if (priv->hidding_ids != NULL)
1629 for (i=0; i < priv->n_selected && !found; i++)
1630 if (priv->hidding_ids[i] != NULL && id != NULL)
1631 found = (!strcmp (priv->hidding_ids[i], id));
1636 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1637 folder as no message can be move there according to UI specs */
1638 if (!priv->show_non_move) {
1640 case TNY_FOLDER_TYPE_OUTBOX:
1641 case TNY_FOLDER_TYPE_SENT:
1642 case TNY_FOLDER_TYPE_DRAFTS:
1645 case TNY_FOLDER_TYPE_UNKNOWN:
1646 case TNY_FOLDER_TYPE_NORMAL:
1647 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1648 if (type == TNY_FOLDER_TYPE_INVALID)
1649 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1651 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1652 type == TNY_FOLDER_TYPE_SENT
1653 || type == TNY_FOLDER_TYPE_DRAFTS)
1662 g_object_unref (instance);
1669 modest_folder_view_update_model (ModestFolderView *self,
1670 TnyAccountStore *account_store)
1672 ModestFolderViewPrivate *priv;
1673 GtkTreeModel *model /* , *old_model */;
1674 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1676 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1677 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1680 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1682 /* Notify that there is no folder selected */
1683 g_signal_emit (G_OBJECT(self),
1684 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1686 if (priv->cur_folder_store) {
1687 g_object_unref (priv->cur_folder_store);
1688 priv->cur_folder_store = NULL;
1691 /* FIXME: the local accounts are not shown when the query
1692 selects only the subscribed folders */
1693 #ifdef MODEST_TOOLKIT_HILDON2
1694 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1695 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1696 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1697 MODEST_FOLDER_PATH_SEPARATOR);
1699 model = tny_gtk_folder_store_tree_model_new (NULL);
1702 /* Get the accounts: */
1703 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1705 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1707 sortable = gtk_tree_model_sort_new_with_model (model);
1708 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1710 GTK_SORT_ASCENDING);
1711 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1713 cmp_rows, NULL, NULL);
1715 /* Create filter model */
1716 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1717 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1723 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1724 #ifndef MODEST_TOOLKIT_HILDON2
1725 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1726 (GCallback) on_row_inserted_maybe_select_folder, self);
1729 g_object_unref (model);
1730 g_object_unref (filter_model);
1731 g_object_unref (sortable);
1733 /* Force a reselection of the INBOX next time the widget is shown */
1734 priv->reselect = TRUE;
1741 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1743 GtkTreeModel *model = NULL;
1744 TnyFolderStore *folder = NULL;
1746 ModestFolderView *tree_view = NULL;
1747 ModestFolderViewPrivate *priv = NULL;
1748 gboolean selected = FALSE;
1750 g_return_if_fail (sel);
1751 g_return_if_fail (user_data);
1753 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1755 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1757 tree_view = MODEST_FOLDER_VIEW (user_data);
1760 gtk_tree_model_get (model, &iter,
1761 INSTANCE_COLUMN, &folder,
1764 /* If the folder is the same do not notify */
1765 if (folder && priv->cur_folder_store == folder) {
1766 g_object_unref (folder);
1771 /* Current folder was unselected */
1772 if (priv->cur_folder_store) {
1773 /* We must do this firstly because a libtinymail-camel
1774 implementation detail. If we issue the signal
1775 before doing the sync_async, then that signal could
1776 cause (and it actually does it) a free of the
1777 summary of the folder (because the main window will
1778 clear the headers view */
1779 if (TNY_IS_FOLDER(priv->cur_folder_store))
1780 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1781 FALSE, NULL, NULL, NULL);
1783 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1784 priv->cur_folder_store, FALSE);
1786 g_object_unref (priv->cur_folder_store);
1787 priv->cur_folder_store = NULL;
1790 /* New current references */
1791 priv->cur_folder_store = folder;
1793 /* New folder has been selected. Do not notify if there is
1794 nothing new selected */
1796 g_signal_emit (G_OBJECT(tree_view),
1797 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1798 0, priv->cur_folder_store, TRUE);
1803 on_row_activated (GtkTreeView *treeview,
1804 GtkTreePath *treepath,
1805 GtkTreeViewColumn *column,
1808 GtkTreeModel *model = NULL;
1809 TnyFolderStore *folder = NULL;
1811 ModestFolderView *self = NULL;
1812 ModestFolderViewPrivate *priv = NULL;
1814 g_return_if_fail (treeview);
1815 g_return_if_fail (user_data);
1817 self = MODEST_FOLDER_VIEW (user_data);
1818 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1820 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1822 if (!gtk_tree_model_get_iter (model, &iter, treepath))
1825 gtk_tree_model_get (model, &iter,
1826 INSTANCE_COLUMN, &folder,
1829 g_signal_emit (G_OBJECT(self),
1830 signals[FOLDER_ACTIVATED_SIGNAL],
1833 #ifdef MODEST_TOOLKIT_HILDON2
1834 HildonUIMode ui_mode;
1835 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
1836 if (ui_mode == HILDON_UI_MODE_NORMAL) {
1837 if (priv->cur_folder_store)
1838 g_object_unref (priv->cur_folder_store);
1839 priv->cur_folder_store = g_object_ref (folder);
1843 g_object_unref (folder);
1847 modest_folder_view_get_selected (ModestFolderView *self)
1849 ModestFolderViewPrivate *priv;
1851 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1853 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1854 if (priv->cur_folder_store)
1855 g_object_ref (priv->cur_folder_store);
1857 return priv->cur_folder_store;
1861 get_cmp_rows_type_pos (GObject *folder)
1863 /* Remote accounts -> Local account -> MMC account .*/
1866 if (TNY_IS_ACCOUNT (folder) &&
1867 modest_tny_account_is_virtual_local_folders (
1868 TNY_ACCOUNT (folder))) {
1870 } else if (TNY_IS_ACCOUNT (folder)) {
1871 TnyAccount *account = TNY_ACCOUNT (folder);
1872 const gchar *account_id = tny_account_get_id (account);
1873 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1879 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1880 return -1; /* Should never happen */
1885 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
1887 TnyAccount *account;
1888 gboolean is_special;
1889 /* Inbox, Outbox, Drafts, Sent, User */
1892 if (!TNY_IS_FOLDER (folder_store))
1895 case TNY_FOLDER_TYPE_INBOX:
1897 account = tny_folder_get_account (folder_store);
1898 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
1899 g_object_unref (account);
1900 return is_special?0:4;
1903 case TNY_FOLDER_TYPE_OUTBOX:
1906 case TNY_FOLDER_TYPE_DRAFTS:
1908 account = tny_folder_get_account (folder_store);
1909 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1910 g_object_unref (account);
1911 return is_special?1:4;
1914 case TNY_FOLDER_TYPE_SENT:
1916 account = tny_folder_get_account (folder_store);
1917 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1918 g_object_unref (account);
1919 return is_special?3:4;
1928 compare_account_names (TnyAccount *a1, TnyAccount *a2)
1930 const gchar *a1_name, *a2_name;
1932 a1_name = tny_account_get_name (a1);
1933 a2_name = tny_account_get_name (a2);
1935 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
1939 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
1941 TnyAccount *a1, *a2;
1944 if (TNY_IS_ACCOUNT (s1)) {
1945 a1 = TNY_ACCOUNT (g_object_ref (s1));
1947 a1 = tny_folder_get_account (TNY_FOLDER (s1));
1950 if (TNY_IS_ACCOUNT (s2)) {
1951 a2 = TNY_ACCOUNT (g_object_ref (s2));
1953 a2 = tny_folder_get_account (TNY_FOLDER (s2));
1960 /* First we sort with the type of account */
1961 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
1965 cmp = compare_account_names (a1, a2);
1968 g_object_unref (a1);
1969 g_object_unref (a2);
1975 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
1977 gint is_account1, is_account2;
1979 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
1980 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
1982 return is_account2 - is_account1;
1986 * This function orders the mail accounts according to these rules:
1987 * 1st - remote accounts
1988 * 2nd - local account
1992 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1996 gchar *name1 = NULL;
1997 gchar *name2 = NULL;
1998 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1999 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2000 GObject *folder1 = NULL;
2001 GObject *folder2 = NULL;
2003 gtk_tree_model_get (tree_model, iter1,
2004 NAME_COLUMN, &name1,
2006 INSTANCE_COLUMN, &folder1,
2008 gtk_tree_model_get (tree_model, iter2,
2009 NAME_COLUMN, &name2,
2010 TYPE_COLUMN, &type2,
2011 INSTANCE_COLUMN, &folder2,
2014 /* Return if we get no folder. This could happen when folder
2015 operations are happening. The model is updated after the
2016 folder copy/move actually occurs, so there could be
2017 situations where the model to be drawn is not correct */
2018 if (!folder1 || !folder2)
2021 /* Sort by type. First the special folders, then the archives */
2022 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2026 /* Now we sort using the account of each folder */
2027 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2031 /* Each group is preceeded by its account */
2032 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2036 /* Pure sort by name */
2037 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2040 g_object_unref(G_OBJECT(folder1));
2042 g_object_unref(G_OBJECT(folder2));
2050 /*****************************************************************************/
2051 /* DRAG and DROP stuff */
2052 /*****************************************************************************/
2054 * This function fills the #GtkSelectionData with the row and the
2055 * model that has been dragged. It's called when this widget is a
2056 * source for dnd after the event drop happened
2059 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2060 guint info, guint time, gpointer data)
2062 GtkTreeSelection *selection;
2063 GtkTreeModel *model;
2065 GtkTreePath *source_row;
2067 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2068 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2070 source_row = gtk_tree_model_get_path (model, &iter);
2071 gtk_tree_set_row_drag_data (selection_data,
2075 gtk_tree_path_free (source_row);
2079 typedef struct _DndHelper {
2080 ModestFolderView *folder_view;
2081 gboolean delete_source;
2082 GtkTreePath *source_row;
2086 dnd_helper_destroyer (DndHelper *helper)
2088 /* Free the helper */
2089 gtk_tree_path_free (helper->source_row);
2090 g_slice_free (DndHelper, helper);
2094 xfer_folder_cb (ModestMailOperation *mail_op,
2095 TnyFolder *new_folder,
2099 /* Select the folder */
2100 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2106 /* get the folder for the row the treepath refers to. */
2107 /* folder must be unref'd */
2108 static TnyFolderStore *
2109 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2112 TnyFolderStore *folder = NULL;
2114 if (gtk_tree_model_get_iter (model,&iter, path))
2115 gtk_tree_model_get (model, &iter,
2116 INSTANCE_COLUMN, &folder,
2123 * This function is used by drag_data_received_cb to manage drag and
2124 * drop of a header, i.e, and drag from the header view to the folder
2128 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2129 GtkTreeModel *dest_model,
2130 GtkTreePath *dest_row,
2131 GtkSelectionData *selection_data)
2133 TnyList *headers = NULL;
2134 TnyFolder *folder = NULL, *src_folder = NULL;
2135 TnyFolderType folder_type;
2136 GtkTreeIter source_iter, dest_iter;
2137 ModestWindowMgr *mgr = NULL;
2138 ModestWindow *main_win = NULL;
2139 gchar **uris, **tmp;
2141 /* Build the list of headers */
2142 mgr = modest_runtime_get_window_mgr ();
2143 headers = tny_simple_list_new ();
2144 uris = modest_dnd_selection_data_get_paths (selection_data);
2147 while (*tmp != NULL) {
2150 gboolean first = TRUE;
2153 path = gtk_tree_path_new_from_string (*tmp);
2154 gtk_tree_model_get_iter (source_model, &source_iter, path);
2155 gtk_tree_model_get (source_model, &source_iter,
2156 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2159 /* Do not enable d&d of headers already opened */
2160 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2161 tny_list_append (headers, G_OBJECT (header));
2163 if (G_UNLIKELY (first)) {
2164 src_folder = tny_header_get_folder (header);
2168 /* Free and go on */
2169 gtk_tree_path_free (path);
2170 g_object_unref (header);
2175 /* This could happen ig we perform a d&d very quickly over the
2176 same row that row could dissapear because message is
2178 if (!TNY_IS_FOLDER (src_folder))
2181 /* Get the target folder */
2182 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2183 gtk_tree_model_get (dest_model, &dest_iter,
2187 if (!folder || !TNY_IS_FOLDER(folder)) {
2188 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2192 folder_type = modest_tny_folder_guess_folder_type (folder);
2193 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2194 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2195 goto cleanup; /* cannot move messages there */
2198 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2199 /* g_warning ("folder not writable"); */
2200 goto cleanup; /* verboten! */
2203 /* Ask for confirmation to move */
2204 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2206 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2210 /* Transfer messages */
2211 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2216 if (G_IS_OBJECT (src_folder))
2217 g_object_unref (src_folder);
2218 if (G_IS_OBJECT(folder))
2219 g_object_unref (G_OBJECT (folder));
2220 if (G_IS_OBJECT(headers))
2221 g_object_unref (headers);
2225 TnyFolderStore *src_folder;
2226 TnyFolderStore *dst_folder;
2227 ModestFolderView *folder_view;
2232 dnd_folder_info_destroyer (DndFolderInfo *info)
2234 if (info->src_folder)
2235 g_object_unref (info->src_folder);
2236 if (info->dst_folder)
2237 g_object_unref (info->dst_folder);
2238 g_slice_free (DndFolderInfo, info);
2242 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2243 GtkWindow *parent_window,
2244 TnyAccount *account)
2247 modest_ui_actions_on_account_connection_error (parent_window, account);
2249 /* Free the helper & info */
2250 dnd_helper_destroyer (info->helper);
2251 dnd_folder_info_destroyer (info);
2255 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2257 GtkWindow *parent_window,
2258 TnyAccount *account,
2261 DndFolderInfo *info = NULL;
2262 ModestMailOperation *mail_op;
2264 info = (DndFolderInfo *) user_data;
2266 if (err || canceled) {
2267 dnd_on_connection_failed_destroyer (info, parent_window, account);
2271 /* Do the mail operation */
2272 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2273 modest_ui_actions_move_folder_error_handler,
2274 info->src_folder, NULL);
2276 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2279 /* Transfer the folder */
2280 modest_mail_operation_xfer_folder (mail_op,
2281 TNY_FOLDER (info->src_folder),
2283 info->helper->delete_source,
2285 info->helper->folder_view);
2288 g_object_unref (G_OBJECT (mail_op));
2289 dnd_helper_destroyer (info->helper);
2290 dnd_folder_info_destroyer (info);
2295 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2297 GtkWindow *parent_window,
2298 TnyAccount *account,
2301 DndFolderInfo *info = NULL;
2303 info = (DndFolderInfo *) user_data;
2305 if (err || canceled) {
2306 dnd_on_connection_failed_destroyer (info, parent_window, account);
2310 /* Connect to source folder and perform the copy/move */
2311 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2313 drag_and_drop_from_folder_view_src_folder_performer,
2318 * This function is used by drag_data_received_cb to manage drag and
2319 * drop of a folder, i.e, and drag from the folder view to the same
2323 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2324 GtkTreeModel *dest_model,
2325 GtkTreePath *dest_row,
2326 GtkSelectionData *selection_data,
2329 GtkTreeIter dest_iter, iter;
2330 TnyFolderStore *dest_folder = NULL;
2331 TnyFolderStore *folder = NULL;
2332 gboolean forbidden = FALSE;
2334 DndFolderInfo *info = NULL;
2336 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2338 g_warning ("%s: BUG: no main window", __FUNCTION__);
2339 dnd_helper_destroyer (helper);
2344 /* check the folder rules for the destination */
2345 folder = tree_path_to_folder (dest_model, dest_row);
2346 if (TNY_IS_FOLDER(folder)) {
2347 ModestTnyFolderRules rules =
2348 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2349 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2350 } else if (TNY_IS_FOLDER_STORE(folder)) {
2351 /* enable local root as destination for folders */
2352 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2353 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2356 g_object_unref (folder);
2359 /* check the folder rules for the source */
2360 folder = tree_path_to_folder (source_model, helper->source_row);
2361 if (TNY_IS_FOLDER(folder)) {
2362 ModestTnyFolderRules rules =
2363 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2364 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2367 g_object_unref (folder);
2371 /* Check if the drag is possible */
2372 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2374 modest_platform_run_information_dialog ((GtkWindow *) win,
2375 _("mail_in_ui_folder_move_target_error"),
2377 /* Restore the previous selection */
2378 folder = tree_path_to_folder (source_model, helper->source_row);
2380 if (TNY_IS_FOLDER (folder))
2381 modest_folder_view_select_folder (helper->folder_view,
2382 TNY_FOLDER (folder), FALSE);
2383 g_object_unref (folder);
2385 dnd_helper_destroyer (helper);
2390 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2391 gtk_tree_model_get (dest_model, &dest_iter,
2394 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2395 gtk_tree_model_get (source_model, &iter,
2399 /* Create the info for the performer */
2400 info = g_slice_new0 (DndFolderInfo);
2401 info->src_folder = g_object_ref (folder);
2402 info->dst_folder = g_object_ref (dest_folder);
2403 info->helper = helper;
2405 /* Connect to the destination folder and perform the copy/move */
2406 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2408 drag_and_drop_from_folder_view_dst_folder_performer,
2412 g_object_unref (dest_folder);
2413 g_object_unref (folder);
2417 * This function receives the data set by the "drag-data-get" signal
2418 * handler. This information comes within the #GtkSelectionData. This
2419 * function will manage both the drags of folders of the treeview and
2420 * drags of headers of the header view widget.
2423 on_drag_data_received (GtkWidget *widget,
2424 GdkDragContext *context,
2427 GtkSelectionData *selection_data,
2432 GtkWidget *source_widget;
2433 GtkTreeModel *dest_model, *source_model;
2434 GtkTreePath *source_row, *dest_row;
2435 GtkTreeViewDropPosition pos;
2436 gboolean delete_source = FALSE;
2437 gboolean success = FALSE;
2439 /* Do not allow further process */
2440 g_signal_stop_emission_by_name (widget, "drag-data-received");
2441 source_widget = gtk_drag_get_source_widget (context);
2443 /* Get the action */
2444 if (context->action == GDK_ACTION_MOVE) {
2445 delete_source = TRUE;
2447 /* Notify that there is no folder selected. We need to
2448 do this in order to update the headers view (and
2449 its monitors, because when moving, the old folder
2450 won't longer exist. We can not wait for the end of
2451 the operation, because the operation won't start if
2452 the folder is in use */
2453 if (source_widget == widget) {
2454 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2455 gtk_tree_selection_unselect_all (sel);
2459 /* Check if the get_data failed */
2460 if (selection_data == NULL || selection_data->length < 0)
2463 /* Select the destination model */
2464 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2466 /* Get the path to the destination row. Can not call
2467 gtk_tree_view_get_drag_dest_row() because the source row
2468 is not selected anymore */
2469 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2472 /* Only allow drops IN other rows */
2474 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2475 pos == GTK_TREE_VIEW_DROP_AFTER)
2479 /* Drags from the header view */
2480 if (source_widget != widget) {
2481 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2483 drag_and_drop_from_header_view (source_model,
2488 DndHelper *helper = NULL;
2490 /* Get the source model and row */
2491 gtk_tree_get_row_drag_data (selection_data,
2495 /* Create the helper */
2496 helper = g_slice_new0 (DndHelper);
2497 helper->delete_source = delete_source;
2498 helper->source_row = gtk_tree_path_copy (source_row);
2499 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2501 drag_and_drop_from_folder_view (source_model,
2507 gtk_tree_path_free (source_row);
2511 gtk_tree_path_free (dest_row);
2514 /* Finish the drag and drop */
2515 gtk_drag_finish (context, success, FALSE, time);
2519 * We define a "drag-drop" signal handler because we do not want to
2520 * use the default one, because the default one always calls
2521 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2522 * signal handler, because there we have all the information available
2523 * to know if the dnd was a success or not.
2526 drag_drop_cb (GtkWidget *widget,
2527 GdkDragContext *context,
2535 if (!context->targets)
2538 /* Check if we're dragging a folder row */
2539 target = gtk_drag_dest_find_target (widget, context, NULL);
2541 /* Request the data from the source. */
2542 gtk_drag_get_data(widget, context, target, time);
2548 * This function expands a node of a tree view if it's not expanded
2549 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2550 * does that, so that's why they're here.
2553 expand_row_timeout (gpointer data)
2555 GtkTreeView *tree_view = data;
2556 GtkTreePath *dest_path = NULL;
2557 GtkTreeViewDropPosition pos;
2558 gboolean result = FALSE;
2560 gdk_threads_enter ();
2562 gtk_tree_view_get_drag_dest_row (tree_view,
2567 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2568 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2569 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2570 gtk_tree_path_free (dest_path);
2574 gtk_tree_path_free (dest_path);
2579 gdk_threads_leave ();
2585 * This function is called whenever the pointer is moved over a widget
2586 * while dragging some data. It installs a timeout that will expand a
2587 * node of the treeview if not expanded yet. This function also calls
2588 * gdk_drag_status in order to set the suggested action that will be
2589 * used by the "drag-data-received" signal handler to know if we
2590 * should do a move or just a copy of the data.
2593 on_drag_motion (GtkWidget *widget,
2594 GdkDragContext *context,
2600 GtkTreeViewDropPosition pos;
2601 GtkTreePath *dest_row;
2602 GtkTreeModel *dest_model;
2603 ModestFolderViewPrivate *priv;
2604 GdkDragAction suggested_action;
2605 gboolean valid_location = FALSE;
2606 TnyFolderStore *folder = NULL;
2608 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2610 if (priv->timer_expander != 0) {
2611 g_source_remove (priv->timer_expander);
2612 priv->timer_expander = 0;
2615 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2620 /* Do not allow drops between folders */
2622 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2623 pos == GTK_TREE_VIEW_DROP_AFTER) {
2624 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2625 gdk_drag_status(context, 0, time);
2626 valid_location = FALSE;
2629 valid_location = TRUE;
2632 /* Check that the destination folder is writable */
2633 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2634 folder = tree_path_to_folder (dest_model, dest_row);
2635 if (folder && TNY_IS_FOLDER (folder)) {
2636 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2638 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2639 valid_location = FALSE;
2644 /* Expand the selected row after 1/2 second */
2645 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2646 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2648 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2650 /* Select the desired action. By default we pick MOVE */
2651 suggested_action = GDK_ACTION_MOVE;
2653 if (context->actions == GDK_ACTION_COPY)
2654 gdk_drag_status(context, GDK_ACTION_COPY, time);
2655 else if (context->actions == GDK_ACTION_MOVE)
2656 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2657 else if (context->actions & suggested_action)
2658 gdk_drag_status(context, suggested_action, time);
2660 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2664 g_object_unref (folder);
2666 gtk_tree_path_free (dest_row);
2668 g_signal_stop_emission_by_name (widget, "drag-motion");
2670 return valid_location;
2674 * This function sets the treeview as a source and a target for dnd
2675 * events. It also connects all the requirede signals.
2678 setup_drag_and_drop (GtkTreeView *self)
2680 /* Set up the folder view as a dnd destination. Set only the
2681 highlight flag, otherwise gtk will have a different
2683 #ifdef MODEST_TOOLKIT_HILDON2
2686 gtk_drag_dest_set (GTK_WIDGET (self),
2687 GTK_DEST_DEFAULT_HIGHLIGHT,
2688 folder_view_drag_types,
2689 G_N_ELEMENTS (folder_view_drag_types),
2690 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2692 g_signal_connect (G_OBJECT (self),
2693 "drag_data_received",
2694 G_CALLBACK (on_drag_data_received),
2698 /* Set up the treeview as a dnd source */
2699 gtk_drag_source_set (GTK_WIDGET (self),
2701 folder_view_drag_types,
2702 G_N_ELEMENTS (folder_view_drag_types),
2703 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2705 g_signal_connect (G_OBJECT (self),
2707 G_CALLBACK (on_drag_motion),
2710 g_signal_connect (G_OBJECT (self),
2712 G_CALLBACK (on_drag_data_get),
2715 g_signal_connect (G_OBJECT (self),
2717 G_CALLBACK (drag_drop_cb),
2722 * This function manages the navigation through the folders using the
2723 * keyboard or the hardware keys in the device
2726 on_key_pressed (GtkWidget *self,
2730 GtkTreeSelection *selection;
2732 GtkTreeModel *model;
2733 gboolean retval = FALSE;
2735 /* Up and Down are automatically managed by the treeview */
2736 if (event->keyval == GDK_Return) {
2737 /* Expand/Collapse the selected row */
2738 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2739 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2742 path = gtk_tree_model_get_path (model, &iter);
2744 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2745 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2747 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2748 gtk_tree_path_free (path);
2750 /* No further processing */
2758 * We listen to the changes in the local folder account name key,
2759 * because we want to show the right name in the view. The local
2760 * folder account name corresponds to the device name in the Maemo
2761 * version. We do this because we do not want to query gconf on each
2762 * tree view refresh. It's better to cache it and change whenever
2766 on_configuration_key_changed (ModestConf* conf,
2768 ModestConfEvent event,
2769 ModestConfNotificationId id,
2770 ModestFolderView *self)
2772 ModestFolderViewPrivate *priv;
2775 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2776 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2778 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2779 g_free (priv->local_account_name);
2781 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2782 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2784 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2785 MODEST_CONF_DEVICE_NAME, NULL);
2787 /* Force a redraw */
2788 #if GTK_CHECK_VERSION(2, 8, 0)
2789 GtkTreeViewColumn * tree_column;
2791 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2793 gtk_tree_view_column_queue_resize (tree_column);
2795 gtk_widget_queue_draw (GTK_WIDGET (self));
2801 modest_folder_view_set_style (ModestFolderView *self,
2802 ModestFolderViewStyle style)
2804 ModestFolderViewPrivate *priv;
2806 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2807 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2808 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2810 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2813 priv->style = style;
2817 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2818 const gchar *account_id)
2820 ModestFolderViewPrivate *priv;
2821 GtkTreeModel *model;
2823 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2825 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2827 /* This will be used by the filter_row callback,
2828 * to decided which rows to show: */
2829 if (priv->visible_account_id) {
2830 g_free (priv->visible_account_id);
2831 priv->visible_account_id = NULL;
2834 priv->visible_account_id = g_strdup (account_id);
2837 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2838 if (GTK_IS_TREE_MODEL_FILTER (model))
2839 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2841 /* Save settings to gconf */
2842 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2843 MODEST_CONF_FOLDER_VIEW_KEY);
2847 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2849 ModestFolderViewPrivate *priv;
2851 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2853 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2855 return (const gchar *) priv->visible_account_id;
2859 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2863 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2865 gtk_tree_model_get (model, iter,
2869 gboolean result = FALSE;
2870 if (type == TNY_FOLDER_TYPE_INBOX) {
2874 *inbox_iter = *iter;
2878 if (gtk_tree_model_iter_children (model, &child, iter)) {
2879 if (find_inbox_iter (model, &child, inbox_iter))
2883 } while (gtk_tree_model_iter_next (model, iter));
2892 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2894 GtkTreeModel *model;
2895 GtkTreeIter iter, inbox_iter;
2896 GtkTreeSelection *sel;
2897 GtkTreePath *path = NULL;
2899 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2901 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2905 expand_root_items (self);
2906 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2908 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2909 g_warning ("%s: model is empty", __FUNCTION__);
2913 if (find_inbox_iter (model, &iter, &inbox_iter))
2914 path = gtk_tree_model_get_path (model, &inbox_iter);
2916 path = gtk_tree_path_new_first ();
2918 /* Select the row and free */
2919 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2920 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2921 gtk_tree_path_free (path);
2924 gtk_widget_grab_focus (GTK_WIDGET(self));
2930 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2935 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2936 TnyFolder* a_folder;
2939 gtk_tree_model_get (model, iter,
2940 INSTANCE_COLUMN, &a_folder,
2946 if (folder == a_folder) {
2947 g_object_unref (a_folder);
2948 *folder_iter = *iter;
2951 g_object_unref (a_folder);
2953 if (gtk_tree_model_iter_children (model, &child, iter)) {
2954 if (find_folder_iter (model, &child, folder_iter, folder))
2958 } while (gtk_tree_model_iter_next (model, iter));
2963 #ifndef MODEST_TOOLKIT_HILDON2
2965 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2968 ModestFolderView *self)
2970 ModestFolderViewPrivate *priv = NULL;
2971 GtkTreeSelection *sel;
2972 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2973 GObject *instance = NULL;
2975 if (!MODEST_IS_FOLDER_VIEW(self))
2978 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2980 priv->reexpand = TRUE;
2982 gtk_tree_model_get (tree_model, iter,
2984 INSTANCE_COLUMN, &instance,
2990 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2991 priv->folder_to_select = g_object_ref (instance);
2993 g_object_unref (instance);
2995 if (priv->folder_to_select) {
2997 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3000 path = gtk_tree_model_get_path (tree_model, iter);
3001 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3003 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3005 gtk_tree_selection_select_iter (sel, iter);
3006 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3008 gtk_tree_path_free (path);
3012 modest_folder_view_disable_next_folder_selection (self);
3014 /* Refilter the model */
3015 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3021 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3023 ModestFolderViewPrivate *priv;
3025 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3027 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3029 if (priv->folder_to_select)
3030 g_object_unref(priv->folder_to_select);
3032 priv->folder_to_select = NULL;
3036 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3037 gboolean after_change)
3039 GtkTreeModel *model;
3040 GtkTreeIter iter, folder_iter;
3041 GtkTreeSelection *sel;
3042 ModestFolderViewPrivate *priv = NULL;
3044 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3045 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3047 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3050 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3051 gtk_tree_selection_unselect_all (sel);
3053 if (priv->folder_to_select)
3054 g_object_unref(priv->folder_to_select);
3055 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3059 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3064 /* Refilter the model, before selecting the folder */
3065 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3067 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3068 g_warning ("%s: model is empty", __FUNCTION__);
3072 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3075 path = gtk_tree_model_get_path (model, &folder_iter);
3076 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3078 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3079 gtk_tree_selection_select_iter (sel, &folder_iter);
3080 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3082 gtk_tree_path_free (path);
3090 modest_folder_view_copy_selection (ModestFolderView *self)
3092 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3094 /* Copy selection */
3095 _clipboard_set_selected_data (self, FALSE);
3099 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3101 ModestFolderViewPrivate *priv = NULL;
3102 GtkTreeModel *model = NULL;
3103 const gchar **hidding = NULL;
3104 guint i, n_selected;
3106 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3107 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3109 /* Copy selection */
3110 if (!_clipboard_set_selected_data (folder_view, TRUE))
3113 /* Get hidding ids */
3114 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3116 /* Clear hidding array created by previous cut operation */
3117 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3119 /* Copy hidding array */
3120 priv->n_selected = n_selected;
3121 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3122 for (i=0; i < n_selected; i++)
3123 priv->hidding_ids[i] = g_strdup(hidding[i]);
3125 /* Hide cut folders */
3126 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3127 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3131 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3132 ModestFolderView *folder_view_dst)
3134 GtkTreeModel *filter_model = NULL;
3135 GtkTreeModel *model = NULL;
3136 GtkTreeModel *new_filter_model = NULL;
3138 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3139 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3142 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3143 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3145 /* Build new filter model */
3146 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3147 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3151 /* Set copied model */
3152 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3153 #ifndef MODEST_TOOLKIT_HILDON2
3154 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3155 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3159 g_object_unref (new_filter_model);
3163 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3166 GtkTreeModel *model = NULL;
3167 ModestFolderViewPrivate* priv;
3169 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3171 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3172 priv->show_non_move = show;
3173 /* modest_folder_view_update_model(folder_view, */
3174 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3176 /* Hide special folders */
3177 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3178 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3179 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3183 /* Returns FALSE if it did not selected anything */
3185 _clipboard_set_selected_data (ModestFolderView *folder_view,
3188 ModestFolderViewPrivate *priv = NULL;
3189 TnyFolderStore *folder = NULL;
3190 gboolean retval = FALSE;
3192 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3193 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3195 /* Set selected data on clipboard */
3196 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3197 folder = modest_folder_view_get_selected (folder_view);
3199 /* Do not allow to select an account */
3200 if (TNY_IS_FOLDER (folder)) {
3201 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3206 g_object_unref (folder);
3212 _clear_hidding_filter (ModestFolderView *folder_view)
3214 ModestFolderViewPrivate *priv;
3217 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3218 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3220 if (priv->hidding_ids != NULL) {
3221 for (i=0; i < priv->n_selected; i++)
3222 g_free (priv->hidding_ids[i]);
3223 g_free(priv->hidding_ids);