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 */
223 ModestFolderViewFilter filter;
225 TnyFolderStoreQuery *query;
226 guint timer_expander;
228 gchar *local_account_name;
229 gchar *visible_account_id;
230 ModestFolderViewStyle style;
231 ModestFolderViewCellStyle cell_style;
233 gboolean reselect; /* we use this to force a reselection of the INBOX */
234 gboolean show_non_move;
235 gboolean reexpand; /* next time we expose, we'll expand all root folders */
237 GtkCellRenderer *messages_renderer;
239 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
240 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
241 MODEST_TYPE_FOLDER_VIEW, \
242 ModestFolderViewPrivate))
244 static GObjectClass *parent_class = NULL;
246 static guint signals[LAST_SIGNAL] = {0};
249 modest_folder_view_get_type (void)
251 static GType my_type = 0;
253 static const GTypeInfo my_info = {
254 sizeof(ModestFolderViewClass),
255 NULL, /* base init */
256 NULL, /* base finalize */
257 (GClassInitFunc) modest_folder_view_class_init,
258 NULL, /* class finalize */
259 NULL, /* class data */
260 sizeof(ModestFolderView),
262 (GInstanceInitFunc) modest_folder_view_init,
266 static const GInterfaceInfo tny_account_store_view_info = {
267 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
268 NULL, /* interface_finalize */
269 NULL /* interface_data */
273 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
277 g_type_add_interface_static (my_type,
278 TNY_TYPE_ACCOUNT_STORE_VIEW,
279 &tny_account_store_view_info);
285 modest_folder_view_class_init (ModestFolderViewClass *klass)
287 GObjectClass *gobject_class;
288 GtkTreeViewClass *treeview_class;
289 gobject_class = (GObjectClass*) klass;
290 treeview_class = (GtkTreeViewClass*) klass;
292 parent_class = g_type_class_peek_parent (klass);
293 gobject_class->finalize = modest_folder_view_finalize;
295 g_type_class_add_private (gobject_class,
296 sizeof(ModestFolderViewPrivate));
298 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
299 g_signal_new ("folder_selection_changed",
300 G_TYPE_FROM_CLASS (gobject_class),
302 G_STRUCT_OFFSET (ModestFolderViewClass,
303 folder_selection_changed),
305 modest_marshal_VOID__POINTER_BOOLEAN,
306 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
309 * This signal is emitted whenever the currently selected
310 * folder display name is computed. Note that the name could
311 * be different to the folder name, because we could append
312 * the unread messages count to the folder name to build the
313 * folder display name
315 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
316 g_signal_new ("folder-display-name-changed",
317 G_TYPE_FROM_CLASS (gobject_class),
319 G_STRUCT_OFFSET (ModestFolderViewClass,
320 folder_display_name_changed),
322 g_cclosure_marshal_VOID__STRING,
323 G_TYPE_NONE, 1, G_TYPE_STRING);
325 signals[FOLDER_ACTIVATED_SIGNAL] =
326 g_signal_new ("folder_activated",
327 G_TYPE_FROM_CLASS (gobject_class),
329 G_STRUCT_OFFSET (ModestFolderViewClass,
332 g_cclosure_marshal_VOID__POINTER,
333 G_TYPE_NONE, 1, G_TYPE_POINTER);
335 treeview_class->select_cursor_parent = NULL;
337 #ifdef MODEST_TOOLKIT_HILDON2
338 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
344 /* Simplify checks for NULLs: */
346 strings_are_equal (const gchar *a, const gchar *b)
352 return (strcmp (a, b) == 0);
359 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
361 GObject *instance = NULL;
363 gtk_tree_model_get (model, iter,
364 INSTANCE_COLUMN, &instance,
368 return FALSE; /* keep walking */
370 if (!TNY_IS_ACCOUNT (instance)) {
371 g_object_unref (instance);
372 return FALSE; /* keep walking */
375 /* Check if this is the looked-for account: */
376 TnyAccount *this_account = TNY_ACCOUNT (instance);
377 TnyAccount *account = TNY_ACCOUNT (data);
379 const gchar *this_account_id = tny_account_get_id(this_account);
380 const gchar *account_id = tny_account_get_id(account);
381 g_object_unref (instance);
384 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
385 if (strings_are_equal(this_account_id, account_id)) {
386 /* Tell the model that the data has changed, so that
387 * it calls the cell_data_func callbacks again: */
388 /* TODO: This does not seem to actually cause the new string to be shown: */
389 gtk_tree_model_row_changed (model, path, iter);
391 return TRUE; /* stop walking */
394 return FALSE; /* keep walking */
399 ModestFolderView *self;
400 gchar *previous_name;
401 } GetMmcAccountNameData;
404 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
406 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
408 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
410 if (!strings_are_equal (
411 tny_account_get_name(TNY_ACCOUNT(account)),
412 data->previous_name)) {
414 /* Tell the model that the data has changed, so that
415 * it calls the cell_data_func callbacks again: */
416 ModestFolderView *self = data->self;
417 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
419 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
422 g_free (data->previous_name);
423 g_slice_free (GetMmcAccountNameData, data);
427 convert_parent_folders_to_dots (gchar **item_name)
431 gchar *last_separator;
433 if (item_name == NULL)
436 for (c = *item_name; *c != '\0'; c++) {
437 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
442 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
443 if (last_separator != NULL) {
444 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
451 buffer = g_string_new ("");
452 for (i = 0; i < n_parents; i++) {
453 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
455 buffer = g_string_append (buffer, last_separator);
457 *item_name = g_string_free (buffer, FALSE);
463 format_compact_style (gchar **item_name,
466 gboolean multiaccount,
467 gboolean *use_markup)
471 TnyFolderType folder_type;
473 if (!TNY_IS_FOLDER (instance))
476 folder = (TnyFolder *) instance;
478 folder_type = tny_folder_get_folder_type (folder);
479 is_special = (get_cmp_pos (folder_type, folder)!= 4);
481 if (!is_special || multiaccount) {
482 TnyAccount *account = tny_folder_get_account (folder);
483 const gchar *folder_name;
484 gboolean concat_folder_name = FALSE;
487 /* Should not happen */
491 /* convert parent folders to dots */
492 convert_parent_folders_to_dots (item_name);
494 folder_name = tny_folder_get_name (folder);
495 if (g_str_has_suffix (*item_name, folder_name)) {
496 gchar *offset = g_strrstr (*item_name, folder_name);
498 concat_folder_name = TRUE;
501 buffer = g_string_new ("");
503 buffer = g_string_append (buffer, *item_name);
504 if (concat_folder_name) {
505 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
506 buffer = g_string_append (buffer, folder_name);
507 if (bold) buffer = g_string_append (buffer, "</span>");
510 g_object_unref (account);
512 *item_name = g_string_free (buffer, FALSE);
520 text_cell_data (GtkTreeViewColumn *column,
521 GtkCellRenderer *renderer,
522 GtkTreeModel *tree_model,
526 ModestFolderViewPrivate *priv;
527 GObject *rendobj = (GObject *) renderer;
529 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
530 GObject *instance = NULL;
531 gboolean use_markup = FALSE;
533 gtk_tree_model_get (tree_model, iter,
536 INSTANCE_COLUMN, &instance,
538 if (!fname || !instance)
541 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
542 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
544 gchar *item_name = NULL;
545 gint item_weight = 400;
547 if (type != TNY_FOLDER_TYPE_ROOT) {
551 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
552 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
553 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
554 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
556 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
559 /* Sometimes an special folder is reported by the server as
560 NORMAL, like some versions of Dovecot */
561 if (type == TNY_FOLDER_TYPE_NORMAL ||
562 type == TNY_FOLDER_TYPE_UNKNOWN) {
563 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
567 if (type == TNY_FOLDER_TYPE_INBOX) {
569 fname = g_strdup (_("mcen_me_folder_inbox"));
572 /* note: we cannot reliably get the counts from the tree model, we need
573 * to use explicit calls on tny_folder for some reason.
575 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
576 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
577 (type == TNY_FOLDER_TYPE_OUTBOX) ||
578 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
579 number = tny_folder_get_all_count (TNY_FOLDER(instance));
582 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
586 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
587 item_name = g_strdup (fname);
594 /* Use bold font style if there are unread or unset messages */
596 item_name = g_strdup_printf ("%s (%d)", fname, number);
599 item_name = g_strdup (fname);
604 } else if (TNY_IS_ACCOUNT (instance)) {
605 /* If it's a server account */
606 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
607 item_name = g_strdup (priv->local_account_name);
609 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
610 /* fname is only correct when the items are first
611 * added to the model, not when the account is
612 * changed later, so get the name from the account
614 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
617 item_name = g_strdup (fname);
623 item_name = g_strdup ("unknown");
625 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
626 gboolean multiaccount;
628 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
629 /* Convert item_name to markup */
630 format_compact_style (&item_name, instance,
632 multiaccount, &use_markup);
635 if (item_name && item_weight) {
636 /* Set the name in the treeview cell: */
638 g_object_set (rendobj, "markup", item_name, NULL);
640 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
642 /* Notify display name observers */
643 /* TODO: What listens for this signal, and how can it use only the new name? */
644 if (((GObject *) priv->cur_folder_store) == instance) {
645 g_signal_emit (G_OBJECT(self),
646 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
653 /* If it is a Memory card account, make sure that we have the correct name.
654 * This function will be trigerred again when the name has been retrieved: */
655 if (TNY_IS_STORE_ACCOUNT (instance) &&
656 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
658 /* Get the account name asynchronously: */
659 GetMmcAccountNameData *callback_data =
660 g_slice_new0(GetMmcAccountNameData);
661 callback_data->self = self;
663 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
665 callback_data->previous_name = g_strdup (name);
667 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
668 on_get_mmc_account_name, callback_data);
672 g_object_unref (G_OBJECT (instance));
678 messages_cell_data (GtkTreeViewColumn *column,
679 GtkCellRenderer *renderer,
680 GtkTreeModel *tree_model,
684 ModestFolderView *self;
685 ModestFolderViewPrivate *priv;
686 GObject *rendobj = (GObject *) renderer;
687 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
688 GObject *instance = NULL;
689 gchar *item_name = NULL;
691 gtk_tree_model_get (tree_model, iter,
693 INSTANCE_COLUMN, &instance,
698 self = MODEST_FOLDER_VIEW (data);
699 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
702 if (type != TNY_FOLDER_TYPE_ROOT) {
706 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
707 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
708 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
710 /* Sometimes an special folder is reported by the server as
711 NORMAL, like some versions of Dovecot */
712 if (type == TNY_FOLDER_TYPE_NORMAL ||
713 type == TNY_FOLDER_TYPE_UNKNOWN) {
714 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
718 /* note: we cannot reliably get the counts from the tree model, we need
719 * to use explicit calls on tny_folder for some reason.
721 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
722 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
723 (type == TNY_FOLDER_TYPE_OUTBOX) ||
724 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
725 number = tny_folder_get_all_count (TNY_FOLDER(instance));
728 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
732 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
734 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
742 item_name = g_strdup ("");
745 /* Set the name in the treeview cell: */
746 g_object_set (rendobj,"text", item_name, NULL);
754 g_object_unref (G_OBJECT (instance));
760 GdkPixbuf *pixbuf_open;
761 GdkPixbuf *pixbuf_close;
765 static inline GdkPixbuf *
766 get_composite_pixbuf (const gchar *icon_name,
768 GdkPixbuf *base_pixbuf)
770 GdkPixbuf *emblem, *retval = NULL;
772 emblem = modest_platform_get_icon (icon_name, size);
774 retval = gdk_pixbuf_copy (base_pixbuf);
775 gdk_pixbuf_composite (emblem, retval, 0, 0,
776 MIN (gdk_pixbuf_get_width (emblem),
777 gdk_pixbuf_get_width (retval)),
778 MIN (gdk_pixbuf_get_height (emblem),
779 gdk_pixbuf_get_height (retval)),
780 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
781 g_object_unref (emblem);
786 static inline ThreePixbufs *
787 get_composite_icons (const gchar *icon_code,
789 GdkPixbuf **pixbuf_open,
790 GdkPixbuf **pixbuf_close)
792 ThreePixbufs *retval;
795 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
798 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
803 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
807 retval = g_slice_new0 (ThreePixbufs);
809 retval->pixbuf = g_object_ref (*pixbuf);
811 retval->pixbuf_open = g_object_ref (*pixbuf_open);
813 retval->pixbuf_close = g_object_ref (*pixbuf_close);
818 static inline ThreePixbufs*
819 get_folder_icons (TnyFolderType type, GObject *instance)
821 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
822 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
823 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
824 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
825 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
827 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
828 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
829 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
830 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
831 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
833 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
834 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
835 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
836 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
837 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
839 ThreePixbufs *retval = NULL;
841 /* Sometimes an special folder is reported by the server as
842 NORMAL, like some versions of Dovecot */
843 if (type == TNY_FOLDER_TYPE_NORMAL ||
844 type == TNY_FOLDER_TYPE_UNKNOWN) {
845 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
848 /* Remote folders should not be treated as special folders */
849 if (TNY_IS_FOLDER_STORE (instance) &&
850 !TNY_IS_ACCOUNT (instance) &&
851 type != TNY_FOLDER_TYPE_INBOX &&
852 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
853 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
856 &normal_pixbuf_close);
860 const gchar *icon_code;
862 case TNY_FOLDER_TYPE_INVALID:
863 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
866 case TNY_FOLDER_TYPE_ROOT:
867 if (TNY_IS_ACCOUNT (instance)) {
869 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
870 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
873 &avirt_pixbuf_close);
875 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
877 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
878 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
883 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
886 &anorm_pixbuf_close);
891 case TNY_FOLDER_TYPE_INBOX:
892 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
895 &inbox_pixbuf_close);
897 case TNY_FOLDER_TYPE_OUTBOX:
898 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
901 &outbox_pixbuf_close);
903 case TNY_FOLDER_TYPE_JUNK:
904 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
909 case TNY_FOLDER_TYPE_SENT:
910 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
915 case TNY_FOLDER_TYPE_TRASH:
916 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
919 &trash_pixbuf_close);
921 case TNY_FOLDER_TYPE_DRAFTS:
922 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
925 &draft_pixbuf_close);
927 case TNY_FOLDER_TYPE_NORMAL:
929 /* Memory card folders could have an special icon */
930 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
931 icon_code = MODEST_FOLDER_ICON_MMC_FOLDER;
933 icon_code = MODEST_FOLDER_ICON_NORMAL;
935 retval = get_composite_icons (icon_code,
938 &normal_pixbuf_close);
946 free_pixbufs (ThreePixbufs *pixbufs)
949 g_object_unref (pixbufs->pixbuf);
950 if (pixbufs->pixbuf_open)
951 g_object_unref (pixbufs->pixbuf_open);
952 if (pixbufs->pixbuf_close)
953 g_object_unref (pixbufs->pixbuf_close);
954 g_slice_free (ThreePixbufs, pixbufs);
958 icon_cell_data (GtkTreeViewColumn *column,
959 GtkCellRenderer *renderer,
960 GtkTreeModel *tree_model,
964 GObject *rendobj = NULL, *instance = NULL;
965 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
966 gboolean has_children;
967 ThreePixbufs *pixbufs;
969 rendobj = (GObject *) renderer;
971 gtk_tree_model_get (tree_model, iter,
973 INSTANCE_COLUMN, &instance,
979 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
980 pixbufs = get_folder_icons (type, instance);
981 g_object_unref (instance);
984 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
987 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
988 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
991 free_pixbufs (pixbufs);
995 add_columns (GtkWidget *treeview)
997 GtkTreeViewColumn *column;
998 GtkCellRenderer *renderer;
999 GtkTreeSelection *sel;
1000 ModestFolderViewPrivate *priv;
1002 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1005 column = gtk_tree_view_column_new ();
1007 /* Set icon and text render function */
1008 renderer = gtk_cell_renderer_pixbuf_new();
1009 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1010 gtk_tree_view_column_set_cell_data_func(column, renderer,
1011 icon_cell_data, treeview, NULL);
1013 renderer = gtk_cell_renderer_text_new();
1014 g_object_set (renderer,
1015 #ifdef MODEST_TOOLKIT_HILDON2
1016 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1018 "ellipsize", PANGO_ELLIPSIZE_END,
1020 "ellipsize-set", TRUE, NULL);
1021 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1022 gtk_tree_view_column_set_cell_data_func(column, renderer,
1023 text_cell_data, treeview, NULL);
1025 priv->messages_renderer = gtk_cell_renderer_text_new ();
1026 g_object_set (priv->messages_renderer,
1027 "scale", PANGO_SCALE_X_SMALL,
1029 "alignment", PANGO_ALIGN_RIGHT,
1033 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1034 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1035 messages_cell_data, treeview, NULL);
1037 /* Set selection mode */
1038 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1039 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1041 /* Set treeview appearance */
1042 gtk_tree_view_column_set_spacing (column, 2);
1043 gtk_tree_view_column_set_resizable (column, TRUE);
1044 gtk_tree_view_column_set_fixed_width (column, TRUE);
1045 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1046 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1049 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1053 modest_folder_view_init (ModestFolderView *obj)
1055 ModestFolderViewPrivate *priv;
1058 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1060 priv->timer_expander = 0;
1061 priv->account_store = NULL;
1063 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1064 priv->cur_folder_store = NULL;
1065 priv->visible_account_id = NULL;
1066 priv->folder_to_select = NULL;
1068 priv->reexpand = TRUE;
1070 /* Initialize the local account name */
1071 conf = modest_runtime_get_conf();
1072 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1074 /* Init email clipboard */
1075 priv->clipboard = modest_runtime_get_email_clipboard ();
1076 priv->hidding_ids = NULL;
1077 priv->n_selected = 0;
1078 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1079 priv->reselect = FALSE;
1080 priv->show_non_move = TRUE;
1082 /* Build treeview */
1083 add_columns (GTK_WIDGET (obj));
1085 /* Setup drag and drop */
1086 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1088 /* Connect signals */
1089 g_signal_connect (G_OBJECT (obj),
1091 G_CALLBACK (on_key_pressed), NULL);
1093 priv->display_name_changed_signal =
1094 g_signal_connect (modest_runtime_get_account_mgr (),
1095 "display_name_changed",
1096 G_CALLBACK (on_display_name_changed),
1100 * Track changes in the local account name (in the device it
1101 * will be the device name)
1103 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1105 G_CALLBACK(on_configuration_key_changed),
1109 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1115 tny_account_store_view_init (gpointer g, gpointer iface_data)
1117 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1119 klass->set_account_store = modest_folder_view_set_account_store;
1123 modest_folder_view_finalize (GObject *obj)
1125 ModestFolderViewPrivate *priv;
1126 GtkTreeSelection *sel;
1128 g_return_if_fail (obj);
1130 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1132 if (priv->timer_expander != 0) {
1133 g_source_remove (priv->timer_expander);
1134 priv->timer_expander = 0;
1137 if (priv->account_store) {
1138 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1139 priv->account_inserted_signal);
1140 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1141 priv->account_removed_signal);
1142 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1143 priv->account_changed_signal);
1144 g_object_unref (G_OBJECT(priv->account_store));
1145 priv->account_store = NULL;
1148 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1149 priv->display_name_changed_signal)) {
1150 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1151 priv->display_name_changed_signal);
1152 priv->display_name_changed_signal = 0;
1156 g_object_unref (G_OBJECT (priv->query));
1160 if (priv->folder_to_select) {
1161 g_object_unref (G_OBJECT(priv->folder_to_select));
1162 priv->folder_to_select = NULL;
1165 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1167 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1169 g_free (priv->local_account_name);
1170 g_free (priv->visible_account_id);
1172 if (priv->conf_key_signal) {
1173 g_signal_handler_disconnect (modest_runtime_get_conf (),
1174 priv->conf_key_signal);
1175 priv->conf_key_signal = 0;
1178 if (priv->cur_folder_store) {
1179 g_object_unref (priv->cur_folder_store);
1180 priv->cur_folder_store = NULL;
1183 /* Clear hidding array created by cut operation */
1184 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1186 G_OBJECT_CLASS(parent_class)->finalize (obj);
1191 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1193 ModestFolderViewPrivate *priv;
1196 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1197 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1199 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1200 device = tny_account_store_get_device (account_store);
1202 if (G_UNLIKELY (priv->account_store)) {
1204 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1205 priv->account_inserted_signal))
1206 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1207 priv->account_inserted_signal);
1208 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1209 priv->account_removed_signal))
1210 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1211 priv->account_removed_signal);
1212 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1213 priv->account_changed_signal))
1214 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1215 priv->account_changed_signal);
1216 g_object_unref (G_OBJECT (priv->account_store));
1219 priv->account_store = g_object_ref (G_OBJECT (account_store));
1221 priv->account_removed_signal =
1222 g_signal_connect (G_OBJECT(account_store), "account_removed",
1223 G_CALLBACK (on_account_removed), self);
1225 priv->account_inserted_signal =
1226 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1227 G_CALLBACK (on_account_inserted), self);
1229 priv->account_changed_signal =
1230 g_signal_connect (G_OBJECT(account_store), "account_changed",
1231 G_CALLBACK (on_account_changed), self);
1233 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1234 priv->reselect = FALSE;
1235 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1237 g_object_unref (G_OBJECT (device));
1241 on_account_inserted (TnyAccountStore *account_store,
1242 TnyAccount *account,
1245 ModestFolderViewPrivate *priv;
1246 GtkTreeModel *sort_model, *filter_model;
1248 /* Ignore transport account insertions, we're not showing them
1249 in the folder view */
1250 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1253 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1256 /* If we're adding a new account, and there is no previous
1257 one, we need to select the visible server account */
1258 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1259 !priv->visible_account_id)
1260 modest_widget_memory_restore (modest_runtime_get_conf(),
1261 G_OBJECT (user_data),
1262 MODEST_CONF_FOLDER_VIEW_KEY);
1264 if (!GTK_IS_TREE_VIEW(user_data)) {
1265 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1269 /* Get the inner model */
1270 /* check, is some rare cases, we did not get the right thing here,
1272 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1273 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1274 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1278 /* check, is some rare cases, we did not get the right thing here,
1280 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1281 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1282 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1286 /* Insert the account in the model */
1287 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1288 G_OBJECT (account));
1290 /* Refilter the model */
1291 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1296 same_account_selected (ModestFolderView *self,
1297 TnyAccount *account)
1299 ModestFolderViewPrivate *priv;
1300 gboolean same_account = FALSE;
1302 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1304 if (priv->cur_folder_store) {
1305 TnyAccount *selected_folder_account = NULL;
1307 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1308 selected_folder_account =
1309 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1311 selected_folder_account =
1312 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1315 if (selected_folder_account == account)
1316 same_account = TRUE;
1318 g_object_unref (selected_folder_account);
1320 return same_account;
1325 * Selects the first inbox or the local account in an idle
1328 on_idle_select_first_inbox_or_local (gpointer user_data)
1330 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1332 gdk_threads_enter ();
1333 modest_folder_view_select_first_inbox_or_local (self);
1334 gdk_threads_leave ();
1340 on_account_changed (TnyAccountStore *account_store,
1341 TnyAccount *tny_account,
1344 ModestFolderView *self;
1345 ModestFolderViewPrivate *priv;
1346 GtkTreeModel *sort_model, *filter_model;
1347 GtkTreeSelection *sel;
1348 gboolean same_account;
1350 /* Ignore transport account insertions, we're not showing them
1351 in the folder view */
1352 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1355 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1356 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1360 self = MODEST_FOLDER_VIEW (user_data);
1361 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1363 /* Get the inner model */
1364 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1365 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1366 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1370 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1371 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1372 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1376 /* Invalidate the cur_folder_store only if the selected folder
1377 belongs to the account that is being removed */
1378 same_account = same_account_selected (self, tny_account);
1380 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1381 gtk_tree_selection_unselect_all (sel);
1384 /* Remove the account from the model */
1385 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1386 G_OBJECT (tny_account));
1388 /* Insert the account in the model */
1389 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1390 G_OBJECT (tny_account));
1392 /* Refilter the model */
1393 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1395 /* Select the first INBOX if the currently selected folder
1396 belongs to the account that is being deleted */
1397 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1398 g_idle_add (on_idle_select_first_inbox_or_local, self);
1402 on_account_removed (TnyAccountStore *account_store,
1403 TnyAccount *account,
1406 ModestFolderView *self = NULL;
1407 ModestFolderViewPrivate *priv;
1408 GtkTreeModel *sort_model, *filter_model;
1409 GtkTreeSelection *sel = NULL;
1410 gboolean same_account = FALSE;
1412 /* Ignore transport account removals, we're not showing them
1413 in the folder view */
1414 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1417 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1418 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1422 self = MODEST_FOLDER_VIEW (user_data);
1423 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1425 /* Invalidate the cur_folder_store only if the selected folder
1426 belongs to the account that is being removed */
1427 same_account = same_account_selected (self, account);
1429 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1430 gtk_tree_selection_unselect_all (sel);
1433 /* Invalidate row to select only if the folder to select
1434 belongs to the account that is being removed*/
1435 if (priv->folder_to_select) {
1436 TnyAccount *folder_to_select_account = NULL;
1438 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1439 if (folder_to_select_account == account) {
1440 modest_folder_view_disable_next_folder_selection (self);
1441 g_object_unref (priv->folder_to_select);
1442 priv->folder_to_select = NULL;
1444 g_object_unref (folder_to_select_account);
1447 /* Remove the account from the model */
1448 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1449 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1450 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1454 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1455 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1456 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1460 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1461 G_OBJECT (account));
1463 /* If the removed account is the currently viewed one then
1464 clear the configuration value. The new visible account will be the default account */
1465 if (priv->visible_account_id &&
1466 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1468 /* Clear the current visible account_id */
1469 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1471 /* Call the restore method, this will set the new visible account */
1472 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1473 MODEST_CONF_FOLDER_VIEW_KEY);
1476 /* Refilter the model */
1477 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1479 /* Select the first INBOX if the currently selected folder
1480 belongs to the account that is being deleted */
1482 g_idle_add (on_idle_select_first_inbox_or_local, self);
1486 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1488 GtkTreeViewColumn *col;
1490 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1492 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1494 g_printerr ("modest: failed get column for title\n");
1498 gtk_tree_view_column_set_title (col, title);
1499 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1504 modest_folder_view_on_map (ModestFolderView *self,
1505 GdkEventExpose *event,
1508 ModestFolderViewPrivate *priv;
1510 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1512 /* This won't happen often */
1513 if (G_UNLIKELY (priv->reselect)) {
1514 /* Select the first inbox or the local account if not found */
1516 /* TODO: this could cause a lock at startup, so we
1517 comment it for the moment. We know that this will
1518 be a bug, because the INBOX is not selected, but we
1519 need to rewrite some parts of Modest to avoid the
1520 deathlock situation */
1521 /* TODO: check if this is still the case */
1522 priv->reselect = FALSE;
1523 modest_folder_view_select_first_inbox_or_local (self);
1524 /* Notify the display name observers */
1525 g_signal_emit (G_OBJECT(self),
1526 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1530 if (priv->reexpand) {
1531 expand_root_items (self);
1532 priv->reexpand = FALSE;
1539 modest_folder_view_new (TnyFolderStoreQuery *query)
1542 ModestFolderViewPrivate *priv;
1543 GtkTreeSelection *sel;
1545 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1546 #ifdef MODEST_TOOLKIT_HILDON2
1547 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1550 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1553 priv->query = g_object_ref (query);
1555 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1556 priv->changed_signal = g_signal_connect (sel, "changed",
1557 G_CALLBACK (on_selection_changed), self);
1559 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1561 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1563 return GTK_WIDGET(self);
1566 /* this feels dirty; any other way to expand all the root items? */
1568 expand_root_items (ModestFolderView *self)
1571 GtkTreeModel *model;
1574 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1575 path = gtk_tree_path_new_first ();
1577 /* all folders should have child items, so.. */
1579 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1580 gtk_tree_path_next (path);
1581 } while (gtk_tree_model_get_iter (model, &iter, path));
1583 gtk_tree_path_free (path);
1587 * We use this function to implement the
1588 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1589 * account in this case, and the local folders.
1592 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1594 ModestFolderViewPrivate *priv;
1595 gboolean retval = TRUE;
1596 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1597 GObject *instance = NULL;
1598 const gchar *id = NULL;
1600 gboolean found = FALSE;
1601 gboolean cleared = FALSE;
1602 ModestTnyFolderRules rules = 0;
1604 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1605 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1607 gtk_tree_model_get (model, iter,
1609 INSTANCE_COLUMN, &instance,
1612 /* Do not show if there is no instance, this could indeed
1613 happen when the model is being modified while it's being
1614 drawn. This could occur for example when moving folders
1619 if (TNY_IS_ACCOUNT (instance)) {
1620 #ifdef MODEST_TOOLKIT_HILDON2
1621 /* In hildon 2.2 we don't show the account rows */
1624 TnyAccount *acc = TNY_ACCOUNT (instance);
1625 const gchar *account_id = tny_account_get_id (acc);
1627 /* If it isn't a special folder,
1628 * don't show it unless it is the visible account: */
1629 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1630 !modest_tny_account_is_virtual_local_folders (acc) &&
1631 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1633 /* Show only the visible account id */
1634 if (priv->visible_account_id) {
1635 if (strcmp (account_id, priv->visible_account_id))
1642 /* Never show these to the user. They are merged into one folder
1643 * in the local-folders account instead: */
1644 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1648 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1649 /* Only show special folders for current account if needed */
1650 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1651 TnyAccount *account;
1653 account = tny_folder_get_account (TNY_FOLDER (instance));
1655 if (TNY_IS_ACCOUNT (account)) {
1656 const gchar *account_id = tny_account_get_id (account);
1658 if (!modest_tny_account_is_virtual_local_folders (account) &&
1659 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1660 /* Show only the visible account id */
1661 if (priv->visible_account_id) {
1662 if (strcmp (account_id, priv->visible_account_id))
1666 g_object_unref (account);
1673 /* Check hiding (if necessary) */
1674 cleared = modest_email_clipboard_cleared (priv->clipboard);
1675 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1676 id = tny_folder_get_id (TNY_FOLDER(instance));
1677 if (priv->hidding_ids != NULL)
1678 for (i=0; i < priv->n_selected && !found; i++)
1679 if (priv->hidding_ids[i] != NULL && id != NULL)
1680 found = (!strcmp (priv->hidding_ids[i], id));
1685 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1686 folder as no message can be move there according to UI specs */
1687 if (!priv->show_non_move) {
1689 case TNY_FOLDER_TYPE_OUTBOX:
1690 case TNY_FOLDER_TYPE_SENT:
1691 case TNY_FOLDER_TYPE_DRAFTS:
1694 case TNY_FOLDER_TYPE_UNKNOWN:
1695 case TNY_FOLDER_TYPE_NORMAL:
1696 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1697 if (type == TNY_FOLDER_TYPE_INVALID)
1698 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1700 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1701 type == TNY_FOLDER_TYPE_SENT
1702 || type == TNY_FOLDER_TYPE_DRAFTS)
1710 /* apply special filters */
1711 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1712 if (TNY_IS_FOLDER (instance)) {
1713 TnyFolderCaps capabilities;
1715 capabilities = tny_folder_get_caps (TNY_FOLDER (instance));
1716 retval = !(capabilities & TNY_FOLDER_CAPS_NOCHILDREN);
1719 retval = ((type != TNY_FOLDER_TYPE_DRAFTS) &&
1720 (type != TNY_FOLDER_TYPE_OUTBOX) &&
1721 (type != TNY_FOLDER_TYPE_SENT));
1723 } else if (TNY_IS_ACCOUNT (instance)) {
1728 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1729 if (TNY_IS_FOLDER (instance)) {
1730 TnyFolderType guess_type;
1732 if (TNY_FOLDER_TYPE_NORMAL) {
1733 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1739 case TNY_FOLDER_TYPE_OUTBOX:
1740 case TNY_FOLDER_TYPE_SENT:
1741 case TNY_FOLDER_TYPE_DRAFTS:
1742 case TNY_FOLDER_TYPE_ARCHIVE:
1743 case TNY_FOLDER_TYPE_INBOX:
1746 case TNY_FOLDER_TYPE_UNKNOWN:
1747 case TNY_FOLDER_TYPE_NORMAL:
1753 } else if (TNY_IS_ACCOUNT (instance)) {
1758 if (retval && TNY_IS_FOLDER (instance)) {
1759 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1762 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1763 if (TNY_IS_FOLDER (instance)) {
1764 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1765 } else if (TNY_IS_ACCOUNT (instance)) {
1770 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1771 if (TNY_IS_FOLDER (instance)) {
1772 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1773 } else if (TNY_IS_ACCOUNT (instance)) {
1778 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1779 if (TNY_IS_FOLDER (instance)) {
1780 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1781 } else if (TNY_IS_ACCOUNT (instance)) {
1787 g_object_unref (instance);
1794 modest_folder_view_update_model (ModestFolderView *self,
1795 TnyAccountStore *account_store)
1797 ModestFolderViewPrivate *priv;
1798 GtkTreeModel *model /* , *old_model */;
1799 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1801 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1802 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1805 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1807 /* Notify that there is no folder selected */
1808 g_signal_emit (G_OBJECT(self),
1809 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1811 if (priv->cur_folder_store) {
1812 g_object_unref (priv->cur_folder_store);
1813 priv->cur_folder_store = NULL;
1816 /* FIXME: the local accounts are not shown when the query
1817 selects only the subscribed folders */
1818 #ifdef MODEST_TOOLKIT_HILDON2
1819 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1820 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1821 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1822 MODEST_FOLDER_PATH_SEPARATOR);
1824 model = tny_gtk_folder_store_tree_model_new (NULL);
1827 /* Get the accounts: */
1828 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1830 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1832 sortable = gtk_tree_model_sort_new_with_model (model);
1833 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1835 GTK_SORT_ASCENDING);
1836 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1838 cmp_rows, NULL, NULL);
1840 /* Create filter model */
1841 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1842 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1848 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1849 #ifndef MODEST_TOOLKIT_HILDON2
1850 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1851 (GCallback) on_row_inserted_maybe_select_folder, self);
1854 g_object_unref (model);
1855 g_object_unref (filter_model);
1856 g_object_unref (sortable);
1858 /* Force a reselection of the INBOX next time the widget is shown */
1859 priv->reselect = TRUE;
1866 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1868 GtkTreeModel *model = NULL;
1869 TnyFolderStore *folder = NULL;
1871 ModestFolderView *tree_view = NULL;
1872 ModestFolderViewPrivate *priv = NULL;
1873 gboolean selected = FALSE;
1875 g_return_if_fail (sel);
1876 g_return_if_fail (user_data);
1878 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1880 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1882 tree_view = MODEST_FOLDER_VIEW (user_data);
1885 gtk_tree_model_get (model, &iter,
1886 INSTANCE_COLUMN, &folder,
1889 /* If the folder is the same do not notify */
1890 if (folder && priv->cur_folder_store == folder) {
1891 g_object_unref (folder);
1896 /* Current folder was unselected */
1897 if (priv->cur_folder_store) {
1898 /* We must do this firstly because a libtinymail-camel
1899 implementation detail. If we issue the signal
1900 before doing the sync_async, then that signal could
1901 cause (and it actually does it) a free of the
1902 summary of the folder (because the main window will
1903 clear the headers view */
1904 if (TNY_IS_FOLDER(priv->cur_folder_store))
1905 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1906 FALSE, NULL, NULL, NULL);
1908 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1909 priv->cur_folder_store, FALSE);
1911 g_object_unref (priv->cur_folder_store);
1912 priv->cur_folder_store = NULL;
1915 /* New current references */
1916 priv->cur_folder_store = folder;
1918 /* New folder has been selected. Do not notify if there is
1919 nothing new selected */
1921 g_signal_emit (G_OBJECT(tree_view),
1922 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1923 0, priv->cur_folder_store, TRUE);
1928 on_row_activated (GtkTreeView *treeview,
1929 GtkTreePath *treepath,
1930 GtkTreeViewColumn *column,
1933 GtkTreeModel *model = NULL;
1934 TnyFolderStore *folder = NULL;
1936 ModestFolderView *self = NULL;
1937 ModestFolderViewPrivate *priv = NULL;
1939 g_return_if_fail (treeview);
1940 g_return_if_fail (user_data);
1942 self = MODEST_FOLDER_VIEW (user_data);
1943 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1945 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1947 if (!gtk_tree_model_get_iter (model, &iter, treepath))
1950 gtk_tree_model_get (model, &iter,
1951 INSTANCE_COLUMN, &folder,
1954 g_signal_emit (G_OBJECT(self),
1955 signals[FOLDER_ACTIVATED_SIGNAL],
1958 #ifdef MODEST_TOOLKIT_HILDON2
1959 HildonUIMode ui_mode;
1960 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
1961 if (ui_mode == HILDON_UI_MODE_NORMAL) {
1962 if (priv->cur_folder_store)
1963 g_object_unref (priv->cur_folder_store);
1964 priv->cur_folder_store = g_object_ref (folder);
1968 g_object_unref (folder);
1972 modest_folder_view_get_selected (ModestFolderView *self)
1974 ModestFolderViewPrivate *priv;
1976 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1978 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1979 if (priv->cur_folder_store)
1980 g_object_ref (priv->cur_folder_store);
1982 return priv->cur_folder_store;
1986 get_cmp_rows_type_pos (GObject *folder)
1988 /* Remote accounts -> Local account -> MMC account .*/
1991 if (TNY_IS_ACCOUNT (folder) &&
1992 modest_tny_account_is_virtual_local_folders (
1993 TNY_ACCOUNT (folder))) {
1995 } else if (TNY_IS_ACCOUNT (folder)) {
1996 TnyAccount *account = TNY_ACCOUNT (folder);
1997 const gchar *account_id = tny_account_get_id (account);
1998 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2004 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2005 return -1; /* Should never happen */
2010 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2012 TnyAccount *account;
2013 gboolean is_special;
2014 /* Inbox, Outbox, Drafts, Sent, User */
2017 if (!TNY_IS_FOLDER (folder_store))
2020 case TNY_FOLDER_TYPE_INBOX:
2022 account = tny_folder_get_account (folder_store);
2023 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2024 g_object_unref (account);
2025 return is_special?0:4;
2028 case TNY_FOLDER_TYPE_OUTBOX:
2031 case TNY_FOLDER_TYPE_DRAFTS:
2033 account = tny_folder_get_account (folder_store);
2034 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2035 g_object_unref (account);
2036 return is_special?1:4;
2039 case TNY_FOLDER_TYPE_SENT:
2041 account = tny_folder_get_account (folder_store);
2042 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2043 g_object_unref (account);
2044 return is_special?3:4;
2053 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2055 const gchar *a1_name, *a2_name;
2057 a1_name = tny_account_get_name (a1);
2058 a2_name = tny_account_get_name (a2);
2060 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2064 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2066 TnyAccount *a1, *a2;
2069 if (TNY_IS_ACCOUNT (s1)) {
2070 a1 = TNY_ACCOUNT (g_object_ref (s1));
2072 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2075 if (TNY_IS_ACCOUNT (s2)) {
2076 a2 = TNY_ACCOUNT (g_object_ref (s2));
2078 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2085 /* First we sort with the type of account */
2086 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2090 cmp = compare_account_names (a1, a2);
2093 g_object_unref (a1);
2094 g_object_unref (a2);
2100 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2102 gint is_account1, is_account2;
2104 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2105 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2107 return is_account2 - is_account1;
2111 * This function orders the mail accounts according to these rules:
2112 * 1st - remote accounts
2113 * 2nd - local account
2117 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2121 gchar *name1 = NULL;
2122 gchar *name2 = NULL;
2123 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2124 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2125 GObject *folder1 = NULL;
2126 GObject *folder2 = NULL;
2128 gtk_tree_model_get (tree_model, iter1,
2129 NAME_COLUMN, &name1,
2131 INSTANCE_COLUMN, &folder1,
2133 gtk_tree_model_get (tree_model, iter2,
2134 NAME_COLUMN, &name2,
2135 TYPE_COLUMN, &type2,
2136 INSTANCE_COLUMN, &folder2,
2139 /* Return if we get no folder. This could happen when folder
2140 operations are happening. The model is updated after the
2141 folder copy/move actually occurs, so there could be
2142 situations where the model to be drawn is not correct */
2143 if (!folder1 || !folder2)
2146 /* Sort by type. First the special folders, then the archives */
2147 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2151 /* Now we sort using the account of each folder */
2152 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2156 /* Each group is preceeded by its account */
2157 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2161 /* Pure sort by name */
2162 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2165 g_object_unref(G_OBJECT(folder1));
2167 g_object_unref(G_OBJECT(folder2));
2175 /*****************************************************************************/
2176 /* DRAG and DROP stuff */
2177 /*****************************************************************************/
2179 * This function fills the #GtkSelectionData with the row and the
2180 * model that has been dragged. It's called when this widget is a
2181 * source for dnd after the event drop happened
2184 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2185 guint info, guint time, gpointer data)
2187 GtkTreeSelection *selection;
2188 GtkTreeModel *model;
2190 GtkTreePath *source_row;
2192 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2193 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2195 source_row = gtk_tree_model_get_path (model, &iter);
2196 gtk_tree_set_row_drag_data (selection_data,
2200 gtk_tree_path_free (source_row);
2204 typedef struct _DndHelper {
2205 ModestFolderView *folder_view;
2206 gboolean delete_source;
2207 GtkTreePath *source_row;
2211 dnd_helper_destroyer (DndHelper *helper)
2213 /* Free the helper */
2214 gtk_tree_path_free (helper->source_row);
2215 g_slice_free (DndHelper, helper);
2219 xfer_folder_cb (ModestMailOperation *mail_op,
2220 TnyFolder *new_folder,
2224 /* Select the folder */
2225 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2231 /* get the folder for the row the treepath refers to. */
2232 /* folder must be unref'd */
2233 static TnyFolderStore *
2234 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2237 TnyFolderStore *folder = NULL;
2239 if (gtk_tree_model_get_iter (model,&iter, path))
2240 gtk_tree_model_get (model, &iter,
2241 INSTANCE_COLUMN, &folder,
2248 * This function is used by drag_data_received_cb to manage drag and
2249 * drop of a header, i.e, and drag from the header view to the folder
2253 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2254 GtkTreeModel *dest_model,
2255 GtkTreePath *dest_row,
2256 GtkSelectionData *selection_data)
2258 TnyList *headers = NULL;
2259 TnyFolder *folder = NULL, *src_folder = NULL;
2260 TnyFolderType folder_type;
2261 GtkTreeIter source_iter, dest_iter;
2262 ModestWindowMgr *mgr = NULL;
2263 ModestWindow *main_win = NULL;
2264 gchar **uris, **tmp;
2266 /* Build the list of headers */
2267 mgr = modest_runtime_get_window_mgr ();
2268 headers = tny_simple_list_new ();
2269 uris = modest_dnd_selection_data_get_paths (selection_data);
2272 while (*tmp != NULL) {
2275 gboolean first = TRUE;
2278 path = gtk_tree_path_new_from_string (*tmp);
2279 gtk_tree_model_get_iter (source_model, &source_iter, path);
2280 gtk_tree_model_get (source_model, &source_iter,
2281 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2284 /* Do not enable d&d of headers already opened */
2285 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2286 tny_list_append (headers, G_OBJECT (header));
2288 if (G_UNLIKELY (first)) {
2289 src_folder = tny_header_get_folder (header);
2293 /* Free and go on */
2294 gtk_tree_path_free (path);
2295 g_object_unref (header);
2300 /* This could happen ig we perform a d&d very quickly over the
2301 same row that row could dissapear because message is
2303 if (!TNY_IS_FOLDER (src_folder))
2306 /* Get the target folder */
2307 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2308 gtk_tree_model_get (dest_model, &dest_iter,
2312 if (!folder || !TNY_IS_FOLDER(folder)) {
2313 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2317 folder_type = modest_tny_folder_guess_folder_type (folder);
2318 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2319 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2320 goto cleanup; /* cannot move messages there */
2323 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2324 /* g_warning ("folder not writable"); */
2325 goto cleanup; /* verboten! */
2328 /* Ask for confirmation to move */
2329 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2331 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2335 /* Transfer messages */
2336 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2341 if (G_IS_OBJECT (src_folder))
2342 g_object_unref (src_folder);
2343 if (G_IS_OBJECT(folder))
2344 g_object_unref (G_OBJECT (folder));
2345 if (G_IS_OBJECT(headers))
2346 g_object_unref (headers);
2350 TnyFolderStore *src_folder;
2351 TnyFolderStore *dst_folder;
2352 ModestFolderView *folder_view;
2357 dnd_folder_info_destroyer (DndFolderInfo *info)
2359 if (info->src_folder)
2360 g_object_unref (info->src_folder);
2361 if (info->dst_folder)
2362 g_object_unref (info->dst_folder);
2363 g_slice_free (DndFolderInfo, info);
2367 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2368 GtkWindow *parent_window,
2369 TnyAccount *account)
2372 modest_ui_actions_on_account_connection_error (parent_window, account);
2374 /* Free the helper & info */
2375 dnd_helper_destroyer (info->helper);
2376 dnd_folder_info_destroyer (info);
2380 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2382 GtkWindow *parent_window,
2383 TnyAccount *account,
2386 DndFolderInfo *info = NULL;
2387 ModestMailOperation *mail_op;
2389 info = (DndFolderInfo *) user_data;
2391 if (err || canceled) {
2392 dnd_on_connection_failed_destroyer (info, parent_window, account);
2396 /* Do the mail operation */
2397 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2398 modest_ui_actions_move_folder_error_handler,
2399 info->src_folder, NULL);
2401 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2404 /* Transfer the folder */
2405 modest_mail_operation_xfer_folder (mail_op,
2406 TNY_FOLDER (info->src_folder),
2408 info->helper->delete_source,
2410 info->helper->folder_view);
2413 g_object_unref (G_OBJECT (mail_op));
2414 dnd_helper_destroyer (info->helper);
2415 dnd_folder_info_destroyer (info);
2420 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2422 GtkWindow *parent_window,
2423 TnyAccount *account,
2426 DndFolderInfo *info = NULL;
2428 info = (DndFolderInfo *) user_data;
2430 if (err || canceled) {
2431 dnd_on_connection_failed_destroyer (info, parent_window, account);
2435 /* Connect to source folder and perform the copy/move */
2436 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2438 drag_and_drop_from_folder_view_src_folder_performer,
2443 * This function is used by drag_data_received_cb to manage drag and
2444 * drop of a folder, i.e, and drag from the folder view to the same
2448 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2449 GtkTreeModel *dest_model,
2450 GtkTreePath *dest_row,
2451 GtkSelectionData *selection_data,
2454 GtkTreeIter dest_iter, iter;
2455 TnyFolderStore *dest_folder = NULL;
2456 TnyFolderStore *folder = NULL;
2457 gboolean forbidden = FALSE;
2459 DndFolderInfo *info = NULL;
2461 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2463 g_warning ("%s: BUG: no main window", __FUNCTION__);
2464 dnd_helper_destroyer (helper);
2469 /* check the folder rules for the destination */
2470 folder = tree_path_to_folder (dest_model, dest_row);
2471 if (TNY_IS_FOLDER(folder)) {
2472 ModestTnyFolderRules rules =
2473 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2474 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2475 } else if (TNY_IS_FOLDER_STORE(folder)) {
2476 /* enable local root as destination for folders */
2477 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2478 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2481 g_object_unref (folder);
2484 /* check the folder rules for the source */
2485 folder = tree_path_to_folder (source_model, helper->source_row);
2486 if (TNY_IS_FOLDER(folder)) {
2487 ModestTnyFolderRules rules =
2488 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2489 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2492 g_object_unref (folder);
2496 /* Check if the drag is possible */
2497 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2499 modest_platform_run_information_dialog ((GtkWindow *) win,
2500 _("mail_in_ui_folder_move_target_error"),
2502 /* Restore the previous selection */
2503 folder = tree_path_to_folder (source_model, helper->source_row);
2505 if (TNY_IS_FOLDER (folder))
2506 modest_folder_view_select_folder (helper->folder_view,
2507 TNY_FOLDER (folder), FALSE);
2508 g_object_unref (folder);
2510 dnd_helper_destroyer (helper);
2515 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2516 gtk_tree_model_get (dest_model, &dest_iter,
2519 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2520 gtk_tree_model_get (source_model, &iter,
2524 /* Create the info for the performer */
2525 info = g_slice_new0 (DndFolderInfo);
2526 info->src_folder = g_object_ref (folder);
2527 info->dst_folder = g_object_ref (dest_folder);
2528 info->helper = helper;
2530 /* Connect to the destination folder and perform the copy/move */
2531 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2533 drag_and_drop_from_folder_view_dst_folder_performer,
2537 g_object_unref (dest_folder);
2538 g_object_unref (folder);
2542 * This function receives the data set by the "drag-data-get" signal
2543 * handler. This information comes within the #GtkSelectionData. This
2544 * function will manage both the drags of folders of the treeview and
2545 * drags of headers of the header view widget.
2548 on_drag_data_received (GtkWidget *widget,
2549 GdkDragContext *context,
2552 GtkSelectionData *selection_data,
2557 GtkWidget *source_widget;
2558 GtkTreeModel *dest_model, *source_model;
2559 GtkTreePath *source_row, *dest_row;
2560 GtkTreeViewDropPosition pos;
2561 gboolean delete_source = FALSE;
2562 gboolean success = FALSE;
2564 /* Do not allow further process */
2565 g_signal_stop_emission_by_name (widget, "drag-data-received");
2566 source_widget = gtk_drag_get_source_widget (context);
2568 /* Get the action */
2569 if (context->action == GDK_ACTION_MOVE) {
2570 delete_source = TRUE;
2572 /* Notify that there is no folder selected. We need to
2573 do this in order to update the headers view (and
2574 its monitors, because when moving, the old folder
2575 won't longer exist. We can not wait for the end of
2576 the operation, because the operation won't start if
2577 the folder is in use */
2578 if (source_widget == widget) {
2579 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2580 gtk_tree_selection_unselect_all (sel);
2584 /* Check if the get_data failed */
2585 if (selection_data == NULL || selection_data->length < 0)
2588 /* Select the destination model */
2589 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2591 /* Get the path to the destination row. Can not call
2592 gtk_tree_view_get_drag_dest_row() because the source row
2593 is not selected anymore */
2594 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2597 /* Only allow drops IN other rows */
2599 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2600 pos == GTK_TREE_VIEW_DROP_AFTER)
2604 /* Drags from the header view */
2605 if (source_widget != widget) {
2606 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2608 drag_and_drop_from_header_view (source_model,
2613 DndHelper *helper = NULL;
2615 /* Get the source model and row */
2616 gtk_tree_get_row_drag_data (selection_data,
2620 /* Create the helper */
2621 helper = g_slice_new0 (DndHelper);
2622 helper->delete_source = delete_source;
2623 helper->source_row = gtk_tree_path_copy (source_row);
2624 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2626 drag_and_drop_from_folder_view (source_model,
2632 gtk_tree_path_free (source_row);
2636 gtk_tree_path_free (dest_row);
2639 /* Finish the drag and drop */
2640 gtk_drag_finish (context, success, FALSE, time);
2644 * We define a "drag-drop" signal handler because we do not want to
2645 * use the default one, because the default one always calls
2646 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2647 * signal handler, because there we have all the information available
2648 * to know if the dnd was a success or not.
2651 drag_drop_cb (GtkWidget *widget,
2652 GdkDragContext *context,
2660 if (!context->targets)
2663 /* Check if we're dragging a folder row */
2664 target = gtk_drag_dest_find_target (widget, context, NULL);
2666 /* Request the data from the source. */
2667 gtk_drag_get_data(widget, context, target, time);
2673 * This function expands a node of a tree view if it's not expanded
2674 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2675 * does that, so that's why they're here.
2678 expand_row_timeout (gpointer data)
2680 GtkTreeView *tree_view = data;
2681 GtkTreePath *dest_path = NULL;
2682 GtkTreeViewDropPosition pos;
2683 gboolean result = FALSE;
2685 gdk_threads_enter ();
2687 gtk_tree_view_get_drag_dest_row (tree_view,
2692 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2693 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2694 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2695 gtk_tree_path_free (dest_path);
2699 gtk_tree_path_free (dest_path);
2704 gdk_threads_leave ();
2710 * This function is called whenever the pointer is moved over a widget
2711 * while dragging some data. It installs a timeout that will expand a
2712 * node of the treeview if not expanded yet. This function also calls
2713 * gdk_drag_status in order to set the suggested action that will be
2714 * used by the "drag-data-received" signal handler to know if we
2715 * should do a move or just a copy of the data.
2718 on_drag_motion (GtkWidget *widget,
2719 GdkDragContext *context,
2725 GtkTreeViewDropPosition pos;
2726 GtkTreePath *dest_row;
2727 GtkTreeModel *dest_model;
2728 ModestFolderViewPrivate *priv;
2729 GdkDragAction suggested_action;
2730 gboolean valid_location = FALSE;
2731 TnyFolderStore *folder = NULL;
2733 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2735 if (priv->timer_expander != 0) {
2736 g_source_remove (priv->timer_expander);
2737 priv->timer_expander = 0;
2740 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2745 /* Do not allow drops between folders */
2747 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2748 pos == GTK_TREE_VIEW_DROP_AFTER) {
2749 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2750 gdk_drag_status(context, 0, time);
2751 valid_location = FALSE;
2754 valid_location = TRUE;
2757 /* Check that the destination folder is writable */
2758 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2759 folder = tree_path_to_folder (dest_model, dest_row);
2760 if (folder && TNY_IS_FOLDER (folder)) {
2761 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2763 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2764 valid_location = FALSE;
2769 /* Expand the selected row after 1/2 second */
2770 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2771 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2773 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2775 /* Select the desired action. By default we pick MOVE */
2776 suggested_action = GDK_ACTION_MOVE;
2778 if (context->actions == GDK_ACTION_COPY)
2779 gdk_drag_status(context, GDK_ACTION_COPY, time);
2780 else if (context->actions == GDK_ACTION_MOVE)
2781 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2782 else if (context->actions & suggested_action)
2783 gdk_drag_status(context, suggested_action, time);
2785 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2789 g_object_unref (folder);
2791 gtk_tree_path_free (dest_row);
2793 g_signal_stop_emission_by_name (widget, "drag-motion");
2795 return valid_location;
2799 * This function sets the treeview as a source and a target for dnd
2800 * events. It also connects all the requirede signals.
2803 setup_drag_and_drop (GtkTreeView *self)
2805 /* Set up the folder view as a dnd destination. Set only the
2806 highlight flag, otherwise gtk will have a different
2808 #ifdef MODEST_TOOLKIT_HILDON2
2811 gtk_drag_dest_set (GTK_WIDGET (self),
2812 GTK_DEST_DEFAULT_HIGHLIGHT,
2813 folder_view_drag_types,
2814 G_N_ELEMENTS (folder_view_drag_types),
2815 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2817 g_signal_connect (G_OBJECT (self),
2818 "drag_data_received",
2819 G_CALLBACK (on_drag_data_received),
2823 /* Set up the treeview as a dnd source */
2824 gtk_drag_source_set (GTK_WIDGET (self),
2826 folder_view_drag_types,
2827 G_N_ELEMENTS (folder_view_drag_types),
2828 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2830 g_signal_connect (G_OBJECT (self),
2832 G_CALLBACK (on_drag_motion),
2835 g_signal_connect (G_OBJECT (self),
2837 G_CALLBACK (on_drag_data_get),
2840 g_signal_connect (G_OBJECT (self),
2842 G_CALLBACK (drag_drop_cb),
2847 * This function manages the navigation through the folders using the
2848 * keyboard or the hardware keys in the device
2851 on_key_pressed (GtkWidget *self,
2855 GtkTreeSelection *selection;
2857 GtkTreeModel *model;
2858 gboolean retval = FALSE;
2860 /* Up and Down are automatically managed by the treeview */
2861 if (event->keyval == GDK_Return) {
2862 /* Expand/Collapse the selected row */
2863 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2864 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2867 path = gtk_tree_model_get_path (model, &iter);
2869 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2870 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2872 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2873 gtk_tree_path_free (path);
2875 /* No further processing */
2883 * We listen to the changes in the local folder account name key,
2884 * because we want to show the right name in the view. The local
2885 * folder account name corresponds to the device name in the Maemo
2886 * version. We do this because we do not want to query gconf on each
2887 * tree view refresh. It's better to cache it and change whenever
2891 on_configuration_key_changed (ModestConf* conf,
2893 ModestConfEvent event,
2894 ModestConfNotificationId id,
2895 ModestFolderView *self)
2897 ModestFolderViewPrivate *priv;
2900 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2901 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2903 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2904 g_free (priv->local_account_name);
2906 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2907 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2909 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2910 MODEST_CONF_DEVICE_NAME, NULL);
2912 /* Force a redraw */
2913 #if GTK_CHECK_VERSION(2, 8, 0)
2914 GtkTreeViewColumn * tree_column;
2916 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2918 gtk_tree_view_column_queue_resize (tree_column);
2920 gtk_widget_queue_draw (GTK_WIDGET (self));
2926 modest_folder_view_set_style (ModestFolderView *self,
2927 ModestFolderViewStyle style)
2929 ModestFolderViewPrivate *priv;
2931 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2932 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2933 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2935 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2938 priv->style = style;
2942 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2943 const gchar *account_id)
2945 ModestFolderViewPrivate *priv;
2946 GtkTreeModel *model;
2948 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2950 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2952 /* This will be used by the filter_row callback,
2953 * to decided which rows to show: */
2954 if (priv->visible_account_id) {
2955 g_free (priv->visible_account_id);
2956 priv->visible_account_id = NULL;
2959 priv->visible_account_id = g_strdup (account_id);
2962 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2963 if (GTK_IS_TREE_MODEL_FILTER (model))
2964 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2966 /* Save settings to gconf */
2967 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2968 MODEST_CONF_FOLDER_VIEW_KEY);
2972 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2974 ModestFolderViewPrivate *priv;
2976 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2978 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2980 return (const gchar *) priv->visible_account_id;
2984 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2988 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2990 gtk_tree_model_get (model, iter,
2994 gboolean result = FALSE;
2995 if (type == TNY_FOLDER_TYPE_INBOX) {
2999 *inbox_iter = *iter;
3003 if (gtk_tree_model_iter_children (model, &child, iter)) {
3004 if (find_inbox_iter (model, &child, inbox_iter))
3008 } while (gtk_tree_model_iter_next (model, iter));
3017 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3019 GtkTreeModel *model;
3020 GtkTreeIter iter, inbox_iter;
3021 GtkTreeSelection *sel;
3022 GtkTreePath *path = NULL;
3024 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3026 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3030 expand_root_items (self);
3031 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3033 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3034 g_warning ("%s: model is empty", __FUNCTION__);
3038 if (find_inbox_iter (model, &iter, &inbox_iter))
3039 path = gtk_tree_model_get_path (model, &inbox_iter);
3041 path = gtk_tree_path_new_first ();
3043 /* Select the row and free */
3044 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3045 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3046 gtk_tree_path_free (path);
3049 gtk_widget_grab_focus (GTK_WIDGET(self));
3055 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3060 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3061 TnyFolder* a_folder;
3064 gtk_tree_model_get (model, iter,
3065 INSTANCE_COLUMN, &a_folder,
3071 if (folder == a_folder) {
3072 g_object_unref (a_folder);
3073 *folder_iter = *iter;
3076 g_object_unref (a_folder);
3078 if (gtk_tree_model_iter_children (model, &child, iter)) {
3079 if (find_folder_iter (model, &child, folder_iter, folder))
3083 } while (gtk_tree_model_iter_next (model, iter));
3088 #ifndef MODEST_TOOLKIT_HILDON2
3090 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3093 ModestFolderView *self)
3095 ModestFolderViewPrivate *priv = NULL;
3096 GtkTreeSelection *sel;
3097 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3098 GObject *instance = NULL;
3100 if (!MODEST_IS_FOLDER_VIEW(self))
3103 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3105 priv->reexpand = TRUE;
3107 gtk_tree_model_get (tree_model, iter,
3109 INSTANCE_COLUMN, &instance,
3115 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3116 priv->folder_to_select = g_object_ref (instance);
3118 g_object_unref (instance);
3120 if (priv->folder_to_select) {
3122 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3125 path = gtk_tree_model_get_path (tree_model, iter);
3126 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3128 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3130 gtk_tree_selection_select_iter (sel, iter);
3131 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3133 gtk_tree_path_free (path);
3137 modest_folder_view_disable_next_folder_selection (self);
3139 /* Refilter the model */
3140 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3146 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3148 ModestFolderViewPrivate *priv;
3150 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3152 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3154 if (priv->folder_to_select)
3155 g_object_unref(priv->folder_to_select);
3157 priv->folder_to_select = NULL;
3161 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3162 gboolean after_change)
3164 GtkTreeModel *model;
3165 GtkTreeIter iter, folder_iter;
3166 GtkTreeSelection *sel;
3167 ModestFolderViewPrivate *priv = NULL;
3169 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3170 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3172 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3175 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3176 gtk_tree_selection_unselect_all (sel);
3178 if (priv->folder_to_select)
3179 g_object_unref(priv->folder_to_select);
3180 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3184 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3189 /* Refilter the model, before selecting the folder */
3190 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3192 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3193 g_warning ("%s: model is empty", __FUNCTION__);
3197 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3200 path = gtk_tree_model_get_path (model, &folder_iter);
3201 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3203 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3204 gtk_tree_selection_select_iter (sel, &folder_iter);
3205 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3207 gtk_tree_path_free (path);
3215 modest_folder_view_copy_selection (ModestFolderView *self)
3217 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3219 /* Copy selection */
3220 _clipboard_set_selected_data (self, FALSE);
3224 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3226 ModestFolderViewPrivate *priv = NULL;
3227 GtkTreeModel *model = NULL;
3228 const gchar **hidding = NULL;
3229 guint i, n_selected;
3231 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3232 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3234 /* Copy selection */
3235 if (!_clipboard_set_selected_data (folder_view, TRUE))
3238 /* Get hidding ids */
3239 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3241 /* Clear hidding array created by previous cut operation */
3242 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3244 /* Copy hidding array */
3245 priv->n_selected = n_selected;
3246 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3247 for (i=0; i < n_selected; i++)
3248 priv->hidding_ids[i] = g_strdup(hidding[i]);
3250 /* Hide cut folders */
3251 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3252 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3256 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3257 ModestFolderView *folder_view_dst)
3259 GtkTreeModel *filter_model = NULL;
3260 GtkTreeModel *model = NULL;
3261 GtkTreeModel *new_filter_model = NULL;
3263 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3264 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3267 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3268 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3270 /* Build new filter model */
3271 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3272 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3276 /* Set copied model */
3277 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3278 #ifndef MODEST_TOOLKIT_HILDON2
3279 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3280 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3284 g_object_unref (new_filter_model);
3288 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3291 GtkTreeModel *model = NULL;
3292 ModestFolderViewPrivate* priv;
3294 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3296 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3297 priv->show_non_move = show;
3298 /* modest_folder_view_update_model(folder_view, */
3299 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3301 /* Hide special folders */
3302 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3303 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3304 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3308 /* Returns FALSE if it did not selected anything */
3310 _clipboard_set_selected_data (ModestFolderView *folder_view,
3313 ModestFolderViewPrivate *priv = NULL;
3314 TnyFolderStore *folder = NULL;
3315 gboolean retval = FALSE;
3317 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3318 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3320 /* Set selected data on clipboard */
3321 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3322 folder = modest_folder_view_get_selected (folder_view);
3324 /* Do not allow to select an account */
3325 if (TNY_IS_FOLDER (folder)) {
3326 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3331 g_object_unref (folder);
3337 _clear_hidding_filter (ModestFolderView *folder_view)
3339 ModestFolderViewPrivate *priv;
3342 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3343 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3345 if (priv->hidding_ids != NULL) {
3346 for (i=0; i < priv->n_selected; i++)
3347 g_free (priv->hidding_ids[i]);
3348 g_free(priv->hidding_ids);
3354 on_display_name_changed (ModestAccountMgr *mgr,
3355 const gchar *account,
3358 ModestFolderView *self;
3360 self = MODEST_FOLDER_VIEW (user_data);
3362 /* Force a redraw */
3363 #if GTK_CHECK_VERSION(2, 8, 0)
3364 GtkTreeViewColumn * tree_column;
3366 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3368 gtk_tree_view_column_queue_resize (tree_column);
3370 gtk_widget_queue_draw (GTK_WIDGET (self));
3375 modest_folder_view_set_cell_style (ModestFolderView *self,
3376 ModestFolderViewCellStyle cell_style)
3378 ModestFolderViewPrivate *priv = NULL;
3380 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3381 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3383 priv->cell_style = cell_style;
3385 g_object_set (G_OBJECT (priv->messages_renderer),
3386 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3389 gtk_widget_queue_draw (GTK_WIDGET (self));
3393 update_style (ModestFolderView *self)
3395 ModestFolderViewPrivate *priv;
3396 GdkColor style_color;
3398 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3399 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3401 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3402 gdk_color_parse ("grey", &style_color);
3405 g_object_set (G_OBJECT (priv->messages_renderer),
3406 "foreground-gdk", &style_color,
3407 "foreground-set", TRUE,
3412 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3414 if (strcmp ("style", spec->name) == 0) {
3415 update_style (MODEST_FOLDER_VIEW (obj));
3416 gtk_widget_queue_draw (GTK_WIDGET (obj));
3421 modest_folder_view_set_filter (ModestFolderView *self,
3422 ModestFolderViewFilter filter)
3424 ModestFolderViewPrivate *priv;
3425 GtkTreeModel *filter_model;
3427 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3428 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3430 priv->filter = filter;
3432 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3433 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3434 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));