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, *mmc_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, *mmc_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, *mmc_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 #ifdef MODEST_TOOLKIT_HILDON2
854 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
857 &anorm_pixbuf_close);
859 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
862 &normal_pixbuf_close);
868 case TNY_FOLDER_TYPE_INVALID:
869 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
872 case TNY_FOLDER_TYPE_ROOT:
873 if (TNY_IS_ACCOUNT (instance)) {
875 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
876 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
879 &avirt_pixbuf_close);
881 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
883 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
884 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
889 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
892 &anorm_pixbuf_close);
897 case TNY_FOLDER_TYPE_INBOX:
898 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
901 &inbox_pixbuf_close);
903 case TNY_FOLDER_TYPE_OUTBOX:
904 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
907 &outbox_pixbuf_close);
909 case TNY_FOLDER_TYPE_JUNK:
910 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
915 case TNY_FOLDER_TYPE_SENT:
916 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
921 case TNY_FOLDER_TYPE_TRASH:
922 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
925 &trash_pixbuf_close);
927 case TNY_FOLDER_TYPE_DRAFTS:
928 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
931 &draft_pixbuf_close);
933 case TNY_FOLDER_TYPE_ARCHIVE:
934 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
939 case TNY_FOLDER_TYPE_NORMAL:
941 /* Memory card folders could have an special icon */
942 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
943 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
948 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
951 &normal_pixbuf_close);
960 free_pixbufs (ThreePixbufs *pixbufs)
963 g_object_unref (pixbufs->pixbuf);
964 if (pixbufs->pixbuf_open)
965 g_object_unref (pixbufs->pixbuf_open);
966 if (pixbufs->pixbuf_close)
967 g_object_unref (pixbufs->pixbuf_close);
968 g_slice_free (ThreePixbufs, pixbufs);
972 icon_cell_data (GtkTreeViewColumn *column,
973 GtkCellRenderer *renderer,
974 GtkTreeModel *tree_model,
978 GObject *rendobj = NULL, *instance = NULL;
979 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
980 gboolean has_children;
981 ThreePixbufs *pixbufs;
983 rendobj = (GObject *) renderer;
985 gtk_tree_model_get (tree_model, iter,
987 INSTANCE_COLUMN, &instance,
993 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
994 pixbufs = get_folder_icons (type, instance);
995 g_object_unref (instance);
998 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1001 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1002 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1005 free_pixbufs (pixbufs);
1009 add_columns (GtkWidget *treeview)
1011 GtkTreeViewColumn *column;
1012 GtkCellRenderer *renderer;
1013 GtkTreeSelection *sel;
1014 ModestFolderViewPrivate *priv;
1016 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1019 column = gtk_tree_view_column_new ();
1021 /* Set icon and text render function */
1022 renderer = gtk_cell_renderer_pixbuf_new();
1023 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1024 gtk_tree_view_column_set_cell_data_func(column, renderer,
1025 icon_cell_data, treeview, NULL);
1027 renderer = gtk_cell_renderer_text_new();
1028 g_object_set (renderer,
1029 #ifdef MODEST_TOOLKIT_HILDON2
1030 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1032 "ellipsize", PANGO_ELLIPSIZE_END,
1034 "ellipsize-set", TRUE, NULL);
1035 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1036 gtk_tree_view_column_set_cell_data_func(column, renderer,
1037 text_cell_data, treeview, NULL);
1039 priv->messages_renderer = gtk_cell_renderer_text_new ();
1040 g_object_set (priv->messages_renderer,
1041 "scale", PANGO_SCALE_X_SMALL,
1043 "alignment", PANGO_ALIGN_RIGHT,
1047 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1048 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1049 messages_cell_data, treeview, NULL);
1051 /* Set selection mode */
1052 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1053 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1055 /* Set treeview appearance */
1056 gtk_tree_view_column_set_spacing (column, 2);
1057 gtk_tree_view_column_set_resizable (column, TRUE);
1058 gtk_tree_view_column_set_fixed_width (column, TRUE);
1059 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1060 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1063 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1067 modest_folder_view_init (ModestFolderView *obj)
1069 ModestFolderViewPrivate *priv;
1072 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1074 priv->timer_expander = 0;
1075 priv->account_store = NULL;
1077 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1078 priv->cur_folder_store = NULL;
1079 priv->visible_account_id = NULL;
1080 priv->folder_to_select = NULL;
1082 priv->reexpand = TRUE;
1084 /* Initialize the local account name */
1085 conf = modest_runtime_get_conf();
1086 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1088 /* Init email clipboard */
1089 priv->clipboard = modest_runtime_get_email_clipboard ();
1090 priv->hidding_ids = NULL;
1091 priv->n_selected = 0;
1092 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1093 priv->reselect = FALSE;
1094 priv->show_non_move = TRUE;
1096 /* Build treeview */
1097 add_columns (GTK_WIDGET (obj));
1099 /* Setup drag and drop */
1100 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1102 /* Connect signals */
1103 g_signal_connect (G_OBJECT (obj),
1105 G_CALLBACK (on_key_pressed), NULL);
1107 priv->display_name_changed_signal =
1108 g_signal_connect (modest_runtime_get_account_mgr (),
1109 "display_name_changed",
1110 G_CALLBACK (on_display_name_changed),
1114 * Track changes in the local account name (in the device it
1115 * will be the device name)
1117 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1119 G_CALLBACK(on_configuration_key_changed),
1123 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1129 tny_account_store_view_init (gpointer g, gpointer iface_data)
1131 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1133 klass->set_account_store = modest_folder_view_set_account_store;
1137 modest_folder_view_finalize (GObject *obj)
1139 ModestFolderViewPrivate *priv;
1140 GtkTreeSelection *sel;
1142 g_return_if_fail (obj);
1144 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1146 if (priv->timer_expander != 0) {
1147 g_source_remove (priv->timer_expander);
1148 priv->timer_expander = 0;
1151 if (priv->account_store) {
1152 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1153 priv->account_inserted_signal);
1154 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1155 priv->account_removed_signal);
1156 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1157 priv->account_changed_signal);
1158 g_object_unref (G_OBJECT(priv->account_store));
1159 priv->account_store = NULL;
1162 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1163 priv->display_name_changed_signal)) {
1164 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1165 priv->display_name_changed_signal);
1166 priv->display_name_changed_signal = 0;
1170 g_object_unref (G_OBJECT (priv->query));
1174 if (priv->folder_to_select) {
1175 g_object_unref (G_OBJECT(priv->folder_to_select));
1176 priv->folder_to_select = NULL;
1179 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1181 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1183 g_free (priv->local_account_name);
1184 g_free (priv->visible_account_id);
1186 if (priv->conf_key_signal) {
1187 g_signal_handler_disconnect (modest_runtime_get_conf (),
1188 priv->conf_key_signal);
1189 priv->conf_key_signal = 0;
1192 if (priv->cur_folder_store) {
1193 g_object_unref (priv->cur_folder_store);
1194 priv->cur_folder_store = NULL;
1197 /* Clear hidding array created by cut operation */
1198 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1200 G_OBJECT_CLASS(parent_class)->finalize (obj);
1205 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1207 ModestFolderViewPrivate *priv;
1210 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1211 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1213 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1214 device = tny_account_store_get_device (account_store);
1216 if (G_UNLIKELY (priv->account_store)) {
1218 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1219 priv->account_inserted_signal))
1220 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1221 priv->account_inserted_signal);
1222 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1223 priv->account_removed_signal))
1224 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1225 priv->account_removed_signal);
1226 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1227 priv->account_changed_signal))
1228 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1229 priv->account_changed_signal);
1230 g_object_unref (G_OBJECT (priv->account_store));
1233 priv->account_store = g_object_ref (G_OBJECT (account_store));
1235 priv->account_removed_signal =
1236 g_signal_connect (G_OBJECT(account_store), "account_removed",
1237 G_CALLBACK (on_account_removed), self);
1239 priv->account_inserted_signal =
1240 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1241 G_CALLBACK (on_account_inserted), self);
1243 priv->account_changed_signal =
1244 g_signal_connect (G_OBJECT(account_store), "account_changed",
1245 G_CALLBACK (on_account_changed), self);
1247 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1248 priv->reselect = FALSE;
1249 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1251 g_object_unref (G_OBJECT (device));
1255 on_account_inserted (TnyAccountStore *account_store,
1256 TnyAccount *account,
1259 ModestFolderViewPrivate *priv;
1260 GtkTreeModel *sort_model, *filter_model;
1262 /* Ignore transport account insertions, we're not showing them
1263 in the folder view */
1264 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1267 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1270 /* If we're adding a new account, and there is no previous
1271 one, we need to select the visible server account */
1272 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1273 !priv->visible_account_id)
1274 modest_widget_memory_restore (modest_runtime_get_conf(),
1275 G_OBJECT (user_data),
1276 MODEST_CONF_FOLDER_VIEW_KEY);
1278 if (!GTK_IS_TREE_VIEW(user_data)) {
1279 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1283 /* Get the inner model */
1284 /* check, is some rare cases, we did not get the right thing here,
1286 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1287 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1288 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1292 /* check, is some rare cases, we did not get the right thing here,
1294 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1295 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1296 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1300 /* Insert the account in the model */
1301 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1302 G_OBJECT (account));
1304 /* Refilter the model */
1305 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1310 same_account_selected (ModestFolderView *self,
1311 TnyAccount *account)
1313 ModestFolderViewPrivate *priv;
1314 gboolean same_account = FALSE;
1316 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1318 if (priv->cur_folder_store) {
1319 TnyAccount *selected_folder_account = NULL;
1321 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1322 selected_folder_account =
1323 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1325 selected_folder_account =
1326 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1329 if (selected_folder_account == account)
1330 same_account = TRUE;
1332 g_object_unref (selected_folder_account);
1334 return same_account;
1339 * Selects the first inbox or the local account in an idle
1342 on_idle_select_first_inbox_or_local (gpointer user_data)
1344 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1346 gdk_threads_enter ();
1347 modest_folder_view_select_first_inbox_or_local (self);
1348 gdk_threads_leave ();
1354 on_account_changed (TnyAccountStore *account_store,
1355 TnyAccount *tny_account,
1358 ModestFolderView *self;
1359 ModestFolderViewPrivate *priv;
1360 GtkTreeModel *sort_model, *filter_model;
1361 GtkTreeSelection *sel;
1362 gboolean same_account;
1364 /* Ignore transport account insertions, we're not showing them
1365 in the folder view */
1366 if (TNY_IS_TRANSPORT_ACCOUNT (tny_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 (user_data);
1377 /* Get the inner model */
1378 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1379 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1380 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1384 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1385 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1386 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1390 /* Invalidate the cur_folder_store only if the selected folder
1391 belongs to the account that is being removed */
1392 same_account = same_account_selected (self, tny_account);
1394 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1395 gtk_tree_selection_unselect_all (sel);
1398 /* Remove the account from the model */
1399 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1400 G_OBJECT (tny_account));
1402 /* Insert the account in the model */
1403 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1404 G_OBJECT (tny_account));
1406 /* Refilter the model */
1407 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1409 /* Select the first INBOX if the currently selected folder
1410 belongs to the account that is being deleted */
1411 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1412 g_idle_add (on_idle_select_first_inbox_or_local, self);
1416 on_account_removed (TnyAccountStore *account_store,
1417 TnyAccount *account,
1420 ModestFolderView *self = NULL;
1421 ModestFolderViewPrivate *priv;
1422 GtkTreeModel *sort_model, *filter_model;
1423 GtkTreeSelection *sel = NULL;
1424 gboolean same_account = FALSE;
1426 /* Ignore transport account removals, we're not showing them
1427 in the folder view */
1428 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1431 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1432 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1436 self = MODEST_FOLDER_VIEW (user_data);
1437 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1439 /* Invalidate the cur_folder_store only if the selected folder
1440 belongs to the account that is being removed */
1441 same_account = same_account_selected (self, account);
1443 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1444 gtk_tree_selection_unselect_all (sel);
1447 /* Invalidate row to select only if the folder to select
1448 belongs to the account that is being removed*/
1449 if (priv->folder_to_select) {
1450 TnyAccount *folder_to_select_account = NULL;
1452 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1453 if (folder_to_select_account == account) {
1454 modest_folder_view_disable_next_folder_selection (self);
1455 g_object_unref (priv->folder_to_select);
1456 priv->folder_to_select = NULL;
1458 g_object_unref (folder_to_select_account);
1461 /* Remove the account from the model */
1462 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1463 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1464 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1468 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1469 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1470 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1474 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1475 G_OBJECT (account));
1477 /* If the removed account is the currently viewed one then
1478 clear the configuration value. The new visible account will be the default account */
1479 if (priv->visible_account_id &&
1480 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1482 /* Clear the current visible account_id */
1483 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1485 /* Call the restore method, this will set the new visible account */
1486 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1487 MODEST_CONF_FOLDER_VIEW_KEY);
1490 /* Refilter the model */
1491 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1493 /* Select the first INBOX if the currently selected folder
1494 belongs to the account that is being deleted */
1496 g_idle_add (on_idle_select_first_inbox_or_local, self);
1500 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1502 GtkTreeViewColumn *col;
1504 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1506 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1508 g_printerr ("modest: failed get column for title\n");
1512 gtk_tree_view_column_set_title (col, title);
1513 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1518 modest_folder_view_on_map (ModestFolderView *self,
1519 GdkEventExpose *event,
1522 ModestFolderViewPrivate *priv;
1524 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1526 /* This won't happen often */
1527 if (G_UNLIKELY (priv->reselect)) {
1528 /* Select the first inbox or the local account if not found */
1530 /* TODO: this could cause a lock at startup, so we
1531 comment it for the moment. We know that this will
1532 be a bug, because the INBOX is not selected, but we
1533 need to rewrite some parts of Modest to avoid the
1534 deathlock situation */
1535 /* TODO: check if this is still the case */
1536 priv->reselect = FALSE;
1537 modest_folder_view_select_first_inbox_or_local (self);
1538 /* Notify the display name observers */
1539 g_signal_emit (G_OBJECT(self),
1540 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1544 if (priv->reexpand) {
1545 expand_root_items (self);
1546 priv->reexpand = FALSE;
1553 modest_folder_view_new (TnyFolderStoreQuery *query)
1556 ModestFolderViewPrivate *priv;
1557 GtkTreeSelection *sel;
1559 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1560 #ifdef MODEST_TOOLKIT_HILDON2
1561 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1564 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1567 priv->query = g_object_ref (query);
1569 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1570 priv->changed_signal = g_signal_connect (sel, "changed",
1571 G_CALLBACK (on_selection_changed), self);
1573 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1575 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1577 return GTK_WIDGET(self);
1580 /* this feels dirty; any other way to expand all the root items? */
1582 expand_root_items (ModestFolderView *self)
1585 GtkTreeModel *model;
1588 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1589 path = gtk_tree_path_new_first ();
1591 /* all folders should have child items, so.. */
1593 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1594 gtk_tree_path_next (path);
1595 } while (gtk_tree_model_get_iter (model, &iter, path));
1597 gtk_tree_path_free (path);
1601 * We use this function to implement the
1602 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1603 * account in this case, and the local folders.
1606 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1608 ModestFolderViewPrivate *priv;
1609 gboolean retval = TRUE;
1610 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1611 GObject *instance = NULL;
1612 const gchar *id = NULL;
1614 gboolean found = FALSE;
1615 gboolean cleared = FALSE;
1616 ModestTnyFolderRules rules = 0;
1618 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1619 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1621 gtk_tree_model_get (model, iter,
1623 INSTANCE_COLUMN, &instance,
1626 /* Do not show if there is no instance, this could indeed
1627 happen when the model is being modified while it's being
1628 drawn. This could occur for example when moving folders
1633 if (TNY_IS_ACCOUNT (instance)) {
1634 #ifdef MODEST_TOOLKIT_HILDON2
1635 /* In hildon 2.2 we don't show the account rows */
1638 TnyAccount *acc = TNY_ACCOUNT (instance);
1639 const gchar *account_id = tny_account_get_id (acc);
1641 /* If it isn't a special folder,
1642 * don't show it unless it is the visible account: */
1643 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1644 !modest_tny_account_is_virtual_local_folders (acc) &&
1645 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1647 /* Show only the visible account id */
1648 if (priv->visible_account_id) {
1649 if (strcmp (account_id, priv->visible_account_id))
1656 /* Never show these to the user. They are merged into one folder
1657 * in the local-folders account instead: */
1658 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1662 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1663 /* Only show special folders for current account if needed */
1664 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1665 TnyAccount *account;
1667 account = tny_folder_get_account (TNY_FOLDER (instance));
1669 if (TNY_IS_ACCOUNT (account)) {
1670 const gchar *account_id = tny_account_get_id (account);
1672 if (!modest_tny_account_is_virtual_local_folders (account) &&
1673 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1674 /* Show only the visible account id */
1675 if (priv->visible_account_id) {
1676 if (strcmp (account_id, priv->visible_account_id))
1680 g_object_unref (account);
1687 /* Check hiding (if necessary) */
1688 cleared = modest_email_clipboard_cleared (priv->clipboard);
1689 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1690 id = tny_folder_get_id (TNY_FOLDER(instance));
1691 if (priv->hidding_ids != NULL)
1692 for (i=0; i < priv->n_selected && !found; i++)
1693 if (priv->hidding_ids[i] != NULL && id != NULL)
1694 found = (!strcmp (priv->hidding_ids[i], id));
1699 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1700 folder as no message can be move there according to UI specs */
1701 if (!priv->show_non_move) {
1703 case TNY_FOLDER_TYPE_OUTBOX:
1704 case TNY_FOLDER_TYPE_SENT:
1705 case TNY_FOLDER_TYPE_DRAFTS:
1708 case TNY_FOLDER_TYPE_UNKNOWN:
1709 case TNY_FOLDER_TYPE_NORMAL:
1710 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1711 if (type == TNY_FOLDER_TYPE_INVALID)
1712 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1714 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1715 type == TNY_FOLDER_TYPE_SENT
1716 || type == TNY_FOLDER_TYPE_DRAFTS)
1724 /* apply special filters */
1725 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1726 if (TNY_IS_FOLDER (instance)) {
1727 TnyFolderCaps capabilities;
1729 capabilities = tny_folder_get_caps (TNY_FOLDER (instance));
1730 retval = !(capabilities & TNY_FOLDER_CAPS_NOCHILDREN);
1733 retval = ((type != TNY_FOLDER_TYPE_DRAFTS) &&
1734 (type != TNY_FOLDER_TYPE_OUTBOX) &&
1735 (type != TNY_FOLDER_TYPE_SENT));
1737 } else if (TNY_IS_ACCOUNT (instance)) {
1742 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1743 if (TNY_IS_FOLDER (instance)) {
1744 TnyFolderType guess_type;
1746 if (TNY_FOLDER_TYPE_NORMAL) {
1747 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1753 case TNY_FOLDER_TYPE_OUTBOX:
1754 case TNY_FOLDER_TYPE_SENT:
1755 case TNY_FOLDER_TYPE_DRAFTS:
1756 case TNY_FOLDER_TYPE_ARCHIVE:
1757 case TNY_FOLDER_TYPE_INBOX:
1760 case TNY_FOLDER_TYPE_UNKNOWN:
1761 case TNY_FOLDER_TYPE_NORMAL:
1767 } else if (TNY_IS_ACCOUNT (instance)) {
1772 if (retval && TNY_IS_FOLDER (instance)) {
1773 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1776 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1777 if (TNY_IS_FOLDER (instance)) {
1778 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1779 } else if (TNY_IS_ACCOUNT (instance)) {
1784 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1785 if (TNY_IS_FOLDER (instance)) {
1786 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1787 } else if (TNY_IS_ACCOUNT (instance)) {
1792 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1793 if (TNY_IS_FOLDER (instance)) {
1794 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1795 } else if (TNY_IS_ACCOUNT (instance)) {
1801 g_object_unref (instance);
1808 modest_folder_view_update_model (ModestFolderView *self,
1809 TnyAccountStore *account_store)
1811 ModestFolderViewPrivate *priv;
1812 GtkTreeModel *model /* , *old_model */;
1813 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1815 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1816 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1819 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1821 /* Notify that there is no folder selected */
1822 g_signal_emit (G_OBJECT(self),
1823 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1825 if (priv->cur_folder_store) {
1826 g_object_unref (priv->cur_folder_store);
1827 priv->cur_folder_store = NULL;
1830 /* FIXME: the local accounts are not shown when the query
1831 selects only the subscribed folders */
1832 #ifdef MODEST_TOOLKIT_HILDON2
1833 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1834 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1835 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1836 MODEST_FOLDER_PATH_SEPARATOR);
1838 model = tny_gtk_folder_store_tree_model_new (NULL);
1841 /* Get the accounts: */
1842 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1844 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1846 sortable = gtk_tree_model_sort_new_with_model (model);
1847 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1849 GTK_SORT_ASCENDING);
1850 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1852 cmp_rows, NULL, NULL);
1854 /* Create filter model */
1855 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1856 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1862 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1863 #ifndef MODEST_TOOLKIT_HILDON2
1864 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1865 (GCallback) on_row_inserted_maybe_select_folder, self);
1868 g_object_unref (model);
1869 g_object_unref (filter_model);
1870 g_object_unref (sortable);
1872 /* Force a reselection of the INBOX next time the widget is shown */
1873 priv->reselect = TRUE;
1880 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1882 GtkTreeModel *model = NULL;
1883 TnyFolderStore *folder = NULL;
1885 ModestFolderView *tree_view = NULL;
1886 ModestFolderViewPrivate *priv = NULL;
1887 gboolean selected = FALSE;
1889 g_return_if_fail (sel);
1890 g_return_if_fail (user_data);
1892 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1894 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1896 tree_view = MODEST_FOLDER_VIEW (user_data);
1899 gtk_tree_model_get (model, &iter,
1900 INSTANCE_COLUMN, &folder,
1903 /* If the folder is the same do not notify */
1904 if (folder && priv->cur_folder_store == folder) {
1905 g_object_unref (folder);
1910 /* Current folder was unselected */
1911 if (priv->cur_folder_store) {
1912 /* We must do this firstly because a libtinymail-camel
1913 implementation detail. If we issue the signal
1914 before doing the sync_async, then that signal could
1915 cause (and it actually does it) a free of the
1916 summary of the folder (because the main window will
1917 clear the headers view */
1918 if (TNY_IS_FOLDER(priv->cur_folder_store))
1919 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1920 FALSE, NULL, NULL, NULL);
1922 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1923 priv->cur_folder_store, FALSE);
1925 g_object_unref (priv->cur_folder_store);
1926 priv->cur_folder_store = NULL;
1929 /* New current references */
1930 priv->cur_folder_store = folder;
1932 /* New folder has been selected. Do not notify if there is
1933 nothing new selected */
1935 g_signal_emit (G_OBJECT(tree_view),
1936 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1937 0, priv->cur_folder_store, TRUE);
1942 on_row_activated (GtkTreeView *treeview,
1943 GtkTreePath *treepath,
1944 GtkTreeViewColumn *column,
1947 GtkTreeModel *model = NULL;
1948 TnyFolderStore *folder = NULL;
1950 ModestFolderView *self = NULL;
1951 ModestFolderViewPrivate *priv = NULL;
1953 g_return_if_fail (treeview);
1954 g_return_if_fail (user_data);
1956 self = MODEST_FOLDER_VIEW (user_data);
1957 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1959 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1961 if (!gtk_tree_model_get_iter (model, &iter, treepath))
1964 gtk_tree_model_get (model, &iter,
1965 INSTANCE_COLUMN, &folder,
1968 g_signal_emit (G_OBJECT(self),
1969 signals[FOLDER_ACTIVATED_SIGNAL],
1972 #ifdef MODEST_TOOLKIT_HILDON2
1973 HildonUIMode ui_mode;
1974 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
1975 if (ui_mode == HILDON_UI_MODE_NORMAL) {
1976 if (priv->cur_folder_store)
1977 g_object_unref (priv->cur_folder_store);
1978 priv->cur_folder_store = g_object_ref (folder);
1982 g_object_unref (folder);
1986 modest_folder_view_get_selected (ModestFolderView *self)
1988 ModestFolderViewPrivate *priv;
1990 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1993 if (priv->cur_folder_store)
1994 g_object_ref (priv->cur_folder_store);
1996 return priv->cur_folder_store;
2000 get_cmp_rows_type_pos (GObject *folder)
2002 /* Remote accounts -> Local account -> MMC account .*/
2005 if (TNY_IS_ACCOUNT (folder) &&
2006 modest_tny_account_is_virtual_local_folders (
2007 TNY_ACCOUNT (folder))) {
2009 } else if (TNY_IS_ACCOUNT (folder)) {
2010 TnyAccount *account = TNY_ACCOUNT (folder);
2011 const gchar *account_id = tny_account_get_id (account);
2012 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2018 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2019 return -1; /* Should never happen */
2024 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2026 TnyAccount *account;
2027 gboolean is_special;
2028 /* Inbox, Outbox, Drafts, Sent, User */
2031 if (!TNY_IS_FOLDER (folder_store))
2034 case TNY_FOLDER_TYPE_INBOX:
2036 account = tny_folder_get_account (folder_store);
2037 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2038 g_object_unref (account);
2039 return is_special?0:4;
2042 case TNY_FOLDER_TYPE_OUTBOX:
2045 case TNY_FOLDER_TYPE_DRAFTS:
2047 account = tny_folder_get_account (folder_store);
2048 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2049 g_object_unref (account);
2050 return is_special?1:4;
2053 case TNY_FOLDER_TYPE_SENT:
2055 account = tny_folder_get_account (folder_store);
2056 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2057 g_object_unref (account);
2058 return is_special?3:4;
2067 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2069 const gchar *a1_name, *a2_name;
2071 a1_name = tny_account_get_name (a1);
2072 a2_name = tny_account_get_name (a2);
2074 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2078 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2080 TnyAccount *a1, *a2;
2083 if (TNY_IS_ACCOUNT (s1)) {
2084 a1 = TNY_ACCOUNT (g_object_ref (s1));
2086 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2089 if (TNY_IS_ACCOUNT (s2)) {
2090 a2 = TNY_ACCOUNT (g_object_ref (s2));
2092 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2099 /* First we sort with the type of account */
2100 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2104 cmp = compare_account_names (a1, a2);
2107 g_object_unref (a1);
2108 g_object_unref (a2);
2114 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2116 gint is_account1, is_account2;
2118 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2119 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2121 return is_account2 - is_account1;
2125 * This function orders the mail accounts according to these rules:
2126 * 1st - remote accounts
2127 * 2nd - local account
2131 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2135 gchar *name1 = NULL;
2136 gchar *name2 = NULL;
2137 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2138 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2139 GObject *folder1 = NULL;
2140 GObject *folder2 = NULL;
2142 gtk_tree_model_get (tree_model, iter1,
2143 NAME_COLUMN, &name1,
2145 INSTANCE_COLUMN, &folder1,
2147 gtk_tree_model_get (tree_model, iter2,
2148 NAME_COLUMN, &name2,
2149 TYPE_COLUMN, &type2,
2150 INSTANCE_COLUMN, &folder2,
2153 /* Return if we get no folder. This could happen when folder
2154 operations are happening. The model is updated after the
2155 folder copy/move actually occurs, so there could be
2156 situations where the model to be drawn is not correct */
2157 if (!folder1 || !folder2)
2160 /* Sort by type. First the special folders, then the archives */
2161 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2165 /* Now we sort using the account of each folder */
2166 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2170 /* Each group is preceeded by its account */
2171 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2175 /* Pure sort by name */
2176 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2179 g_object_unref(G_OBJECT(folder1));
2181 g_object_unref(G_OBJECT(folder2));
2189 /*****************************************************************************/
2190 /* DRAG and DROP stuff */
2191 /*****************************************************************************/
2193 * This function fills the #GtkSelectionData with the row and the
2194 * model that has been dragged. It's called when this widget is a
2195 * source for dnd after the event drop happened
2198 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2199 guint info, guint time, gpointer data)
2201 GtkTreeSelection *selection;
2202 GtkTreeModel *model;
2204 GtkTreePath *source_row;
2206 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2207 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2209 source_row = gtk_tree_model_get_path (model, &iter);
2210 gtk_tree_set_row_drag_data (selection_data,
2214 gtk_tree_path_free (source_row);
2218 typedef struct _DndHelper {
2219 ModestFolderView *folder_view;
2220 gboolean delete_source;
2221 GtkTreePath *source_row;
2225 dnd_helper_destroyer (DndHelper *helper)
2227 /* Free the helper */
2228 gtk_tree_path_free (helper->source_row);
2229 g_slice_free (DndHelper, helper);
2233 xfer_folder_cb (ModestMailOperation *mail_op,
2234 TnyFolder *new_folder,
2238 /* Select the folder */
2239 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2245 /* get the folder for the row the treepath refers to. */
2246 /* folder must be unref'd */
2247 static TnyFolderStore *
2248 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2251 TnyFolderStore *folder = NULL;
2253 if (gtk_tree_model_get_iter (model,&iter, path))
2254 gtk_tree_model_get (model, &iter,
2255 INSTANCE_COLUMN, &folder,
2262 * This function is used by drag_data_received_cb to manage drag and
2263 * drop of a header, i.e, and drag from the header view to the folder
2267 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2268 GtkTreeModel *dest_model,
2269 GtkTreePath *dest_row,
2270 GtkSelectionData *selection_data)
2272 TnyList *headers = NULL;
2273 TnyFolder *folder = NULL, *src_folder = NULL;
2274 TnyFolderType folder_type;
2275 GtkTreeIter source_iter, dest_iter;
2276 ModestWindowMgr *mgr = NULL;
2277 ModestWindow *main_win = NULL;
2278 gchar **uris, **tmp;
2280 /* Build the list of headers */
2281 mgr = modest_runtime_get_window_mgr ();
2282 headers = tny_simple_list_new ();
2283 uris = modest_dnd_selection_data_get_paths (selection_data);
2286 while (*tmp != NULL) {
2289 gboolean first = TRUE;
2292 path = gtk_tree_path_new_from_string (*tmp);
2293 gtk_tree_model_get_iter (source_model, &source_iter, path);
2294 gtk_tree_model_get (source_model, &source_iter,
2295 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2298 /* Do not enable d&d of headers already opened */
2299 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2300 tny_list_append (headers, G_OBJECT (header));
2302 if (G_UNLIKELY (first)) {
2303 src_folder = tny_header_get_folder (header);
2307 /* Free and go on */
2308 gtk_tree_path_free (path);
2309 g_object_unref (header);
2314 /* This could happen ig we perform a d&d very quickly over the
2315 same row that row could dissapear because message is
2317 if (!TNY_IS_FOLDER (src_folder))
2320 /* Get the target folder */
2321 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2322 gtk_tree_model_get (dest_model, &dest_iter,
2326 if (!folder || !TNY_IS_FOLDER(folder)) {
2327 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2331 folder_type = modest_tny_folder_guess_folder_type (folder);
2332 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2333 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2334 goto cleanup; /* cannot move messages there */
2337 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2338 /* g_warning ("folder not writable"); */
2339 goto cleanup; /* verboten! */
2342 /* Ask for confirmation to move */
2343 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2345 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2349 /* Transfer messages */
2350 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2355 if (G_IS_OBJECT (src_folder))
2356 g_object_unref (src_folder);
2357 if (G_IS_OBJECT(folder))
2358 g_object_unref (G_OBJECT (folder));
2359 if (G_IS_OBJECT(headers))
2360 g_object_unref (headers);
2364 TnyFolderStore *src_folder;
2365 TnyFolderStore *dst_folder;
2366 ModestFolderView *folder_view;
2371 dnd_folder_info_destroyer (DndFolderInfo *info)
2373 if (info->src_folder)
2374 g_object_unref (info->src_folder);
2375 if (info->dst_folder)
2376 g_object_unref (info->dst_folder);
2377 g_slice_free (DndFolderInfo, info);
2381 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2382 GtkWindow *parent_window,
2383 TnyAccount *account)
2386 modest_ui_actions_on_account_connection_error (parent_window, account);
2388 /* Free the helper & info */
2389 dnd_helper_destroyer (info->helper);
2390 dnd_folder_info_destroyer (info);
2394 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2396 GtkWindow *parent_window,
2397 TnyAccount *account,
2400 DndFolderInfo *info = NULL;
2401 ModestMailOperation *mail_op;
2403 info = (DndFolderInfo *) user_data;
2405 if (err || canceled) {
2406 dnd_on_connection_failed_destroyer (info, parent_window, account);
2410 /* Do the mail operation */
2411 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2412 modest_ui_actions_move_folder_error_handler,
2413 info->src_folder, NULL);
2415 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2418 /* Transfer the folder */
2419 modest_mail_operation_xfer_folder (mail_op,
2420 TNY_FOLDER (info->src_folder),
2422 info->helper->delete_source,
2424 info->helper->folder_view);
2427 g_object_unref (G_OBJECT (mail_op));
2428 dnd_helper_destroyer (info->helper);
2429 dnd_folder_info_destroyer (info);
2434 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2436 GtkWindow *parent_window,
2437 TnyAccount *account,
2440 DndFolderInfo *info = NULL;
2442 info = (DndFolderInfo *) user_data;
2444 if (err || canceled) {
2445 dnd_on_connection_failed_destroyer (info, parent_window, account);
2449 /* Connect to source folder and perform the copy/move */
2450 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2452 drag_and_drop_from_folder_view_src_folder_performer,
2457 * This function is used by drag_data_received_cb to manage drag and
2458 * drop of a folder, i.e, and drag from the folder view to the same
2462 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2463 GtkTreeModel *dest_model,
2464 GtkTreePath *dest_row,
2465 GtkSelectionData *selection_data,
2468 GtkTreeIter dest_iter, iter;
2469 TnyFolderStore *dest_folder = NULL;
2470 TnyFolderStore *folder = NULL;
2471 gboolean forbidden = FALSE;
2473 DndFolderInfo *info = NULL;
2475 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2477 g_warning ("%s: BUG: no main window", __FUNCTION__);
2478 dnd_helper_destroyer (helper);
2483 /* check the folder rules for the destination */
2484 folder = tree_path_to_folder (dest_model, dest_row);
2485 if (TNY_IS_FOLDER(folder)) {
2486 ModestTnyFolderRules rules =
2487 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2488 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2489 } else if (TNY_IS_FOLDER_STORE(folder)) {
2490 /* enable local root as destination for folders */
2491 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2492 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2495 g_object_unref (folder);
2498 /* check the folder rules for the source */
2499 folder = tree_path_to_folder (source_model, helper->source_row);
2500 if (TNY_IS_FOLDER(folder)) {
2501 ModestTnyFolderRules rules =
2502 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2503 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2506 g_object_unref (folder);
2510 /* Check if the drag is possible */
2511 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2513 modest_platform_run_information_dialog ((GtkWindow *) win,
2514 _("mail_in_ui_folder_move_target_error"),
2516 /* Restore the previous selection */
2517 folder = tree_path_to_folder (source_model, helper->source_row);
2519 if (TNY_IS_FOLDER (folder))
2520 modest_folder_view_select_folder (helper->folder_view,
2521 TNY_FOLDER (folder), FALSE);
2522 g_object_unref (folder);
2524 dnd_helper_destroyer (helper);
2529 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2530 gtk_tree_model_get (dest_model, &dest_iter,
2533 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2534 gtk_tree_model_get (source_model, &iter,
2538 /* Create the info for the performer */
2539 info = g_slice_new0 (DndFolderInfo);
2540 info->src_folder = g_object_ref (folder);
2541 info->dst_folder = g_object_ref (dest_folder);
2542 info->helper = helper;
2544 /* Connect to the destination folder and perform the copy/move */
2545 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2547 drag_and_drop_from_folder_view_dst_folder_performer,
2551 g_object_unref (dest_folder);
2552 g_object_unref (folder);
2556 * This function receives the data set by the "drag-data-get" signal
2557 * handler. This information comes within the #GtkSelectionData. This
2558 * function will manage both the drags of folders of the treeview and
2559 * drags of headers of the header view widget.
2562 on_drag_data_received (GtkWidget *widget,
2563 GdkDragContext *context,
2566 GtkSelectionData *selection_data,
2571 GtkWidget *source_widget;
2572 GtkTreeModel *dest_model, *source_model;
2573 GtkTreePath *source_row, *dest_row;
2574 GtkTreeViewDropPosition pos;
2575 gboolean delete_source = FALSE;
2576 gboolean success = FALSE;
2578 /* Do not allow further process */
2579 g_signal_stop_emission_by_name (widget, "drag-data-received");
2580 source_widget = gtk_drag_get_source_widget (context);
2582 /* Get the action */
2583 if (context->action == GDK_ACTION_MOVE) {
2584 delete_source = TRUE;
2586 /* Notify that there is no folder selected. We need to
2587 do this in order to update the headers view (and
2588 its monitors, because when moving, the old folder
2589 won't longer exist. We can not wait for the end of
2590 the operation, because the operation won't start if
2591 the folder is in use */
2592 if (source_widget == widget) {
2593 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2594 gtk_tree_selection_unselect_all (sel);
2598 /* Check if the get_data failed */
2599 if (selection_data == NULL || selection_data->length < 0)
2602 /* Select the destination model */
2603 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2605 /* Get the path to the destination row. Can not call
2606 gtk_tree_view_get_drag_dest_row() because the source row
2607 is not selected anymore */
2608 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2611 /* Only allow drops IN other rows */
2613 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2614 pos == GTK_TREE_VIEW_DROP_AFTER)
2618 /* Drags from the header view */
2619 if (source_widget != widget) {
2620 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2622 drag_and_drop_from_header_view (source_model,
2627 DndHelper *helper = NULL;
2629 /* Get the source model and row */
2630 gtk_tree_get_row_drag_data (selection_data,
2634 /* Create the helper */
2635 helper = g_slice_new0 (DndHelper);
2636 helper->delete_source = delete_source;
2637 helper->source_row = gtk_tree_path_copy (source_row);
2638 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2640 drag_and_drop_from_folder_view (source_model,
2646 gtk_tree_path_free (source_row);
2650 gtk_tree_path_free (dest_row);
2653 /* Finish the drag and drop */
2654 gtk_drag_finish (context, success, FALSE, time);
2658 * We define a "drag-drop" signal handler because we do not want to
2659 * use the default one, because the default one always calls
2660 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2661 * signal handler, because there we have all the information available
2662 * to know if the dnd was a success or not.
2665 drag_drop_cb (GtkWidget *widget,
2666 GdkDragContext *context,
2674 if (!context->targets)
2677 /* Check if we're dragging a folder row */
2678 target = gtk_drag_dest_find_target (widget, context, NULL);
2680 /* Request the data from the source. */
2681 gtk_drag_get_data(widget, context, target, time);
2687 * This function expands a node of a tree view if it's not expanded
2688 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2689 * does that, so that's why they're here.
2692 expand_row_timeout (gpointer data)
2694 GtkTreeView *tree_view = data;
2695 GtkTreePath *dest_path = NULL;
2696 GtkTreeViewDropPosition pos;
2697 gboolean result = FALSE;
2699 gdk_threads_enter ();
2701 gtk_tree_view_get_drag_dest_row (tree_view,
2706 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2707 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2708 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2709 gtk_tree_path_free (dest_path);
2713 gtk_tree_path_free (dest_path);
2718 gdk_threads_leave ();
2724 * This function is called whenever the pointer is moved over a widget
2725 * while dragging some data. It installs a timeout that will expand a
2726 * node of the treeview if not expanded yet. This function also calls
2727 * gdk_drag_status in order to set the suggested action that will be
2728 * used by the "drag-data-received" signal handler to know if we
2729 * should do a move or just a copy of the data.
2732 on_drag_motion (GtkWidget *widget,
2733 GdkDragContext *context,
2739 GtkTreeViewDropPosition pos;
2740 GtkTreePath *dest_row;
2741 GtkTreeModel *dest_model;
2742 ModestFolderViewPrivate *priv;
2743 GdkDragAction suggested_action;
2744 gboolean valid_location = FALSE;
2745 TnyFolderStore *folder = NULL;
2747 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2749 if (priv->timer_expander != 0) {
2750 g_source_remove (priv->timer_expander);
2751 priv->timer_expander = 0;
2754 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2759 /* Do not allow drops between folders */
2761 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2762 pos == GTK_TREE_VIEW_DROP_AFTER) {
2763 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2764 gdk_drag_status(context, 0, time);
2765 valid_location = FALSE;
2768 valid_location = TRUE;
2771 /* Check that the destination folder is writable */
2772 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2773 folder = tree_path_to_folder (dest_model, dest_row);
2774 if (folder && TNY_IS_FOLDER (folder)) {
2775 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2777 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2778 valid_location = FALSE;
2783 /* Expand the selected row after 1/2 second */
2784 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2785 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2787 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2789 /* Select the desired action. By default we pick MOVE */
2790 suggested_action = GDK_ACTION_MOVE;
2792 if (context->actions == GDK_ACTION_COPY)
2793 gdk_drag_status(context, GDK_ACTION_COPY, time);
2794 else if (context->actions == GDK_ACTION_MOVE)
2795 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2796 else if (context->actions & suggested_action)
2797 gdk_drag_status(context, suggested_action, time);
2799 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2803 g_object_unref (folder);
2805 gtk_tree_path_free (dest_row);
2807 g_signal_stop_emission_by_name (widget, "drag-motion");
2809 return valid_location;
2813 * This function sets the treeview as a source and a target for dnd
2814 * events. It also connects all the requirede signals.
2817 setup_drag_and_drop (GtkTreeView *self)
2819 /* Set up the folder view as a dnd destination. Set only the
2820 highlight flag, otherwise gtk will have a different
2822 #ifdef MODEST_TOOLKIT_HILDON2
2825 gtk_drag_dest_set (GTK_WIDGET (self),
2826 GTK_DEST_DEFAULT_HIGHLIGHT,
2827 folder_view_drag_types,
2828 G_N_ELEMENTS (folder_view_drag_types),
2829 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2831 g_signal_connect (G_OBJECT (self),
2832 "drag_data_received",
2833 G_CALLBACK (on_drag_data_received),
2837 /* Set up the treeview as a dnd source */
2838 gtk_drag_source_set (GTK_WIDGET (self),
2840 folder_view_drag_types,
2841 G_N_ELEMENTS (folder_view_drag_types),
2842 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2844 g_signal_connect (G_OBJECT (self),
2846 G_CALLBACK (on_drag_motion),
2849 g_signal_connect (G_OBJECT (self),
2851 G_CALLBACK (on_drag_data_get),
2854 g_signal_connect (G_OBJECT (self),
2856 G_CALLBACK (drag_drop_cb),
2861 * This function manages the navigation through the folders using the
2862 * keyboard or the hardware keys in the device
2865 on_key_pressed (GtkWidget *self,
2869 GtkTreeSelection *selection;
2871 GtkTreeModel *model;
2872 gboolean retval = FALSE;
2874 /* Up and Down are automatically managed by the treeview */
2875 if (event->keyval == GDK_Return) {
2876 /* Expand/Collapse the selected row */
2877 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2878 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2881 path = gtk_tree_model_get_path (model, &iter);
2883 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2884 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2886 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2887 gtk_tree_path_free (path);
2889 /* No further processing */
2897 * We listen to the changes in the local folder account name key,
2898 * because we want to show the right name in the view. The local
2899 * folder account name corresponds to the device name in the Maemo
2900 * version. We do this because we do not want to query gconf on each
2901 * tree view refresh. It's better to cache it and change whenever
2905 on_configuration_key_changed (ModestConf* conf,
2907 ModestConfEvent event,
2908 ModestConfNotificationId id,
2909 ModestFolderView *self)
2911 ModestFolderViewPrivate *priv;
2914 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2915 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2917 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2918 g_free (priv->local_account_name);
2920 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2921 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2923 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2924 MODEST_CONF_DEVICE_NAME, NULL);
2926 /* Force a redraw */
2927 #if GTK_CHECK_VERSION(2, 8, 0)
2928 GtkTreeViewColumn * tree_column;
2930 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2932 gtk_tree_view_column_queue_resize (tree_column);
2934 gtk_widget_queue_draw (GTK_WIDGET (self));
2940 modest_folder_view_set_style (ModestFolderView *self,
2941 ModestFolderViewStyle style)
2943 ModestFolderViewPrivate *priv;
2945 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2946 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2947 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2949 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2952 priv->style = style;
2956 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2957 const gchar *account_id)
2959 ModestFolderViewPrivate *priv;
2960 GtkTreeModel *model;
2962 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2964 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2966 /* This will be used by the filter_row callback,
2967 * to decided which rows to show: */
2968 if (priv->visible_account_id) {
2969 g_free (priv->visible_account_id);
2970 priv->visible_account_id = NULL;
2973 priv->visible_account_id = g_strdup (account_id);
2976 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2977 if (GTK_IS_TREE_MODEL_FILTER (model))
2978 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2980 /* Save settings to gconf */
2981 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2982 MODEST_CONF_FOLDER_VIEW_KEY);
2986 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2988 ModestFolderViewPrivate *priv;
2990 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2994 return (const gchar *) priv->visible_account_id;
2998 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3002 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3004 gtk_tree_model_get (model, iter,
3008 gboolean result = FALSE;
3009 if (type == TNY_FOLDER_TYPE_INBOX) {
3013 *inbox_iter = *iter;
3017 if (gtk_tree_model_iter_children (model, &child, iter)) {
3018 if (find_inbox_iter (model, &child, inbox_iter))
3022 } while (gtk_tree_model_iter_next (model, iter));
3031 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3033 GtkTreeModel *model;
3034 GtkTreeIter iter, inbox_iter;
3035 GtkTreeSelection *sel;
3036 GtkTreePath *path = NULL;
3038 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3040 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3044 expand_root_items (self);
3045 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3047 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3048 g_warning ("%s: model is empty", __FUNCTION__);
3052 if (find_inbox_iter (model, &iter, &inbox_iter))
3053 path = gtk_tree_model_get_path (model, &inbox_iter);
3055 path = gtk_tree_path_new_first ();
3057 /* Select the row and free */
3058 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3059 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3060 gtk_tree_path_free (path);
3063 gtk_widget_grab_focus (GTK_WIDGET(self));
3069 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3074 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3075 TnyFolder* a_folder;
3078 gtk_tree_model_get (model, iter,
3079 INSTANCE_COLUMN, &a_folder,
3085 if (folder == a_folder) {
3086 g_object_unref (a_folder);
3087 *folder_iter = *iter;
3090 g_object_unref (a_folder);
3092 if (gtk_tree_model_iter_children (model, &child, iter)) {
3093 if (find_folder_iter (model, &child, folder_iter, folder))
3097 } while (gtk_tree_model_iter_next (model, iter));
3102 #ifndef MODEST_TOOLKIT_HILDON2
3104 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3107 ModestFolderView *self)
3109 ModestFolderViewPrivate *priv = NULL;
3110 GtkTreeSelection *sel;
3111 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3112 GObject *instance = NULL;
3114 if (!MODEST_IS_FOLDER_VIEW(self))
3117 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3119 priv->reexpand = TRUE;
3121 gtk_tree_model_get (tree_model, iter,
3123 INSTANCE_COLUMN, &instance,
3129 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3130 priv->folder_to_select = g_object_ref (instance);
3132 g_object_unref (instance);
3134 if (priv->folder_to_select) {
3136 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3139 path = gtk_tree_model_get_path (tree_model, iter);
3140 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3142 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3144 gtk_tree_selection_select_iter (sel, iter);
3145 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3147 gtk_tree_path_free (path);
3151 modest_folder_view_disable_next_folder_selection (self);
3153 /* Refilter the model */
3154 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3160 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3162 ModestFolderViewPrivate *priv;
3164 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3166 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3168 if (priv->folder_to_select)
3169 g_object_unref(priv->folder_to_select);
3171 priv->folder_to_select = NULL;
3175 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3176 gboolean after_change)
3178 GtkTreeModel *model;
3179 GtkTreeIter iter, folder_iter;
3180 GtkTreeSelection *sel;
3181 ModestFolderViewPrivate *priv = NULL;
3183 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3184 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3186 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3189 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3190 gtk_tree_selection_unselect_all (sel);
3192 if (priv->folder_to_select)
3193 g_object_unref(priv->folder_to_select);
3194 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3198 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3203 /* Refilter the model, before selecting the folder */
3204 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3206 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3207 g_warning ("%s: model is empty", __FUNCTION__);
3211 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3214 path = gtk_tree_model_get_path (model, &folder_iter);
3215 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3217 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3218 gtk_tree_selection_select_iter (sel, &folder_iter);
3219 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3221 gtk_tree_path_free (path);
3229 modest_folder_view_copy_selection (ModestFolderView *self)
3231 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3233 /* Copy selection */
3234 _clipboard_set_selected_data (self, FALSE);
3238 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3240 ModestFolderViewPrivate *priv = NULL;
3241 GtkTreeModel *model = NULL;
3242 const gchar **hidding = NULL;
3243 guint i, n_selected;
3245 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3246 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3248 /* Copy selection */
3249 if (!_clipboard_set_selected_data (folder_view, TRUE))
3252 /* Get hidding ids */
3253 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3255 /* Clear hidding array created by previous cut operation */
3256 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3258 /* Copy hidding array */
3259 priv->n_selected = n_selected;
3260 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3261 for (i=0; i < n_selected; i++)
3262 priv->hidding_ids[i] = g_strdup(hidding[i]);
3264 /* Hide cut folders */
3265 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3266 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3270 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3271 ModestFolderView *folder_view_dst)
3273 GtkTreeModel *filter_model = NULL;
3274 GtkTreeModel *model = NULL;
3275 GtkTreeModel *new_filter_model = NULL;
3277 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3278 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3281 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3282 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3284 /* Build new filter model */
3285 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3286 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3290 /* Set copied model */
3291 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3292 #ifndef MODEST_TOOLKIT_HILDON2
3293 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3294 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3298 g_object_unref (new_filter_model);
3302 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3305 GtkTreeModel *model = NULL;
3306 ModestFolderViewPrivate* priv;
3308 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3310 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3311 priv->show_non_move = show;
3312 /* modest_folder_view_update_model(folder_view, */
3313 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3315 /* Hide special folders */
3316 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3317 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3318 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3322 /* Returns FALSE if it did not selected anything */
3324 _clipboard_set_selected_data (ModestFolderView *folder_view,
3327 ModestFolderViewPrivate *priv = NULL;
3328 TnyFolderStore *folder = NULL;
3329 gboolean retval = FALSE;
3331 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3332 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3334 /* Set selected data on clipboard */
3335 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3336 folder = modest_folder_view_get_selected (folder_view);
3338 /* Do not allow to select an account */
3339 if (TNY_IS_FOLDER (folder)) {
3340 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3345 g_object_unref (folder);
3351 _clear_hidding_filter (ModestFolderView *folder_view)
3353 ModestFolderViewPrivate *priv;
3356 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3357 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3359 if (priv->hidding_ids != NULL) {
3360 for (i=0; i < priv->n_selected; i++)
3361 g_free (priv->hidding_ids[i]);
3362 g_free(priv->hidding_ids);
3368 on_display_name_changed (ModestAccountMgr *mgr,
3369 const gchar *account,
3372 ModestFolderView *self;
3374 self = MODEST_FOLDER_VIEW (user_data);
3376 /* Force a redraw */
3377 #if GTK_CHECK_VERSION(2, 8, 0)
3378 GtkTreeViewColumn * tree_column;
3380 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3382 gtk_tree_view_column_queue_resize (tree_column);
3384 gtk_widget_queue_draw (GTK_WIDGET (self));
3389 modest_folder_view_set_cell_style (ModestFolderView *self,
3390 ModestFolderViewCellStyle cell_style)
3392 ModestFolderViewPrivate *priv = NULL;
3394 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3395 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3397 priv->cell_style = cell_style;
3399 g_object_set (G_OBJECT (priv->messages_renderer),
3400 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3403 gtk_widget_queue_draw (GTK_WIDGET (self));
3407 update_style (ModestFolderView *self)
3409 ModestFolderViewPrivate *priv;
3410 GdkColor style_color;
3412 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3413 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3415 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3416 gdk_color_parse ("grey", &style_color);
3419 g_object_set (G_OBJECT (priv->messages_renderer),
3420 "foreground-gdk", &style_color,
3421 "foreground-set", TRUE,
3426 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3428 if (strcmp ("style", spec->name) == 0) {
3429 update_style (MODEST_FOLDER_VIEW (obj));
3430 gtk_widget_queue_draw (GTK_WIDGET (obj));
3435 modest_folder_view_set_filter (ModestFolderView *self,
3436 ModestFolderViewFilter filter)
3438 ModestFolderViewPrivate *priv;
3439 GtkTreeModel *filter_model;
3441 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3442 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3444 priv->filter = filter;
3446 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3447 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3448 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));