1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-store-tree-model.h>
36 #include <tny-gtk-header-list-model.h>
37 #include <tny-folder.h>
38 #include <tny-folder-store-observer.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <tny-camel-account.h>
45 #include <modest-tny-account.h>
46 #include <modest-tny-folder.h>
47 #include <modest-tny-local-folders-account.h>
48 #include <modest-tny-outbox-account.h>
49 #include <modest-marshal.h>
50 #include <modest-icon-names.h>
51 #include <modest-tny-account-store.h>
52 #include <modest-text-utils.h>
53 #include <modest-runtime.h>
54 #include "modest-folder-view.h"
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
58 #include "modest-dnd.h"
59 #include "widgets/modest-window.h"
61 /* Folder view drag types */
62 const GtkTargetEntry folder_view_drag_types[] =
64 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
65 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
68 /* 'private'/'protected' functions */
69 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
70 static void modest_folder_view_init (ModestFolderView *obj);
71 static void modest_folder_view_finalize (GObject *obj);
73 static void tny_account_store_view_init (gpointer g,
76 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
77 TnyAccountStore *account_store);
79 static void on_selection_changed (GtkTreeSelection *sel,
82 static void on_account_removed (TnyAccountStore *self,
86 static void on_account_inserted (TnyAccountStore *self,
90 static void on_account_changed (TnyAccountStore *self,
94 static gint cmp_rows (GtkTreeModel *tree_model,
99 static gboolean filter_row (GtkTreeModel *model,
103 static gboolean on_key_pressed (GtkWidget *self,
107 static void on_configuration_key_changed (ModestConf* conf,
109 ModestConfEvent event,
110 ModestConfNotificationId notification_id,
111 ModestFolderView *self);
114 static void on_drag_data_get (GtkWidget *widget,
115 GdkDragContext *context,
116 GtkSelectionData *selection_data,
121 static void on_drag_data_received (GtkWidget *widget,
122 GdkDragContext *context,
125 GtkSelectionData *selection_data,
130 static gboolean on_drag_motion (GtkWidget *widget,
131 GdkDragContext *context,
137 static void expand_root_items (ModestFolderView *self);
139 static gint expand_row_timeout (gpointer data);
141 static void setup_drag_and_drop (GtkTreeView *self);
143 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
146 static void _clear_hidding_filter (ModestFolderView *folder_view);
148 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
151 ModestFolderView *self);
153 static void on_display_name_changed (ModestAccountMgr *self,
154 const gchar *account,
158 FOLDER_SELECTION_CHANGED_SIGNAL,
159 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
163 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
164 struct _ModestFolderViewPrivate {
165 TnyAccountStore *account_store;
166 TnyFolderStore *cur_folder_store;
168 TnyFolder *folder_to_select; /* folder to select after the next update */
170 gulong changed_signal;
171 gulong account_inserted_signal;
172 gulong account_removed_signal;
173 gulong account_changed_signal;
174 gulong conf_key_signal;
175 gulong display_name_changed_signal;
177 /* not unref this object, its a singlenton */
178 ModestEmailClipboard *clipboard;
180 /* Filter tree model */
184 TnyFolderStoreQuery *query;
185 guint timer_expander;
187 gchar *local_account_name;
188 gchar *visible_account_id;
189 ModestFolderViewStyle style;
191 gboolean reselect; /* we use this to force a reselection of the INBOX */
192 gboolean show_non_move;
193 gboolean reexpand; /* next time we expose, we'll expand all root folders */
195 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
196 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
197 MODEST_TYPE_FOLDER_VIEW, \
198 ModestFolderViewPrivate))
200 static GObjectClass *parent_class = NULL;
202 static guint signals[LAST_SIGNAL] = {0};
205 modest_folder_view_get_type (void)
207 static GType my_type = 0;
209 static const GTypeInfo my_info = {
210 sizeof(ModestFolderViewClass),
211 NULL, /* base init */
212 NULL, /* base finalize */
213 (GClassInitFunc) modest_folder_view_class_init,
214 NULL, /* class finalize */
215 NULL, /* class data */
216 sizeof(ModestFolderView),
218 (GInstanceInitFunc) modest_folder_view_init,
222 static const GInterfaceInfo tny_account_store_view_info = {
223 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
224 NULL, /* interface_finalize */
225 NULL /* interface_data */
229 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
233 g_type_add_interface_static (my_type,
234 TNY_TYPE_ACCOUNT_STORE_VIEW,
235 &tny_account_store_view_info);
241 modest_folder_view_class_init (ModestFolderViewClass *klass)
243 GObjectClass *gobject_class;
244 GtkTreeViewClass *treeview_class;
245 gobject_class = (GObjectClass*) klass;
246 treeview_class = (GtkTreeViewClass*) klass;
248 parent_class = g_type_class_peek_parent (klass);
249 gobject_class->finalize = modest_folder_view_finalize;
251 g_type_class_add_private (gobject_class,
252 sizeof(ModestFolderViewPrivate));
254 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
255 g_signal_new ("folder_selection_changed",
256 G_TYPE_FROM_CLASS (gobject_class),
258 G_STRUCT_OFFSET (ModestFolderViewClass,
259 folder_selection_changed),
261 modest_marshal_VOID__POINTER_BOOLEAN,
262 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
265 * This signal is emitted whenever the currently selected
266 * folder display name is computed. Note that the name could
267 * be different to the folder name, because we could append
268 * the unread messages count to the folder name to build the
269 * folder display name
271 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
272 g_signal_new ("folder-display-name-changed",
273 G_TYPE_FROM_CLASS (gobject_class),
275 G_STRUCT_OFFSET (ModestFolderViewClass,
276 folder_display_name_changed),
278 g_cclosure_marshal_VOID__STRING,
279 G_TYPE_NONE, 1, G_TYPE_STRING);
281 treeview_class->select_cursor_parent = NULL;
285 /* Simplify checks for NULLs: */
287 strings_are_equal (const gchar *a, const gchar *b)
293 return (strcmp (a, b) == 0);
300 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
302 GObject *instance = NULL;
304 gtk_tree_model_get (model, iter,
305 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
309 return FALSE; /* keep walking */
311 if (!TNY_IS_ACCOUNT (instance)) {
312 g_object_unref (instance);
313 return FALSE; /* keep walking */
316 /* Check if this is the looked-for account: */
317 TnyAccount *this_account = TNY_ACCOUNT (instance);
318 TnyAccount *account = TNY_ACCOUNT (data);
320 const gchar *this_account_id = tny_account_get_id(this_account);
321 const gchar *account_id = tny_account_get_id(account);
322 g_object_unref (instance);
325 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
326 if (strings_are_equal(this_account_id, account_id)) {
327 /* Tell the model that the data has changed, so that
328 * it calls the cell_data_func callbacks again: */
329 /* TODO: This does not seem to actually cause the new string to be shown: */
330 gtk_tree_model_row_changed (model, path, iter);
332 return TRUE; /* stop walking */
335 return FALSE; /* keep walking */
340 ModestFolderView *self;
341 gchar *previous_name;
342 } GetMmcAccountNameData;
345 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
347 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
349 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
351 if (!strings_are_equal (
352 tny_account_get_name(TNY_ACCOUNT(account)),
353 data->previous_name)) {
355 /* Tell the model that the data has changed, so that
356 * it calls the cell_data_func callbacks again: */
357 ModestFolderView *self = data->self;
358 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
360 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
363 g_free (data->previous_name);
364 g_slice_free (GetMmcAccountNameData, data);
368 text_cell_data (GtkTreeViewColumn *column,
369 GtkCellRenderer *renderer,
370 GtkTreeModel *tree_model,
374 ModestFolderViewPrivate *priv;
375 GObject *rendobj = (GObject *) renderer;
377 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
378 GObject *instance = NULL;
380 gtk_tree_model_get (tree_model, iter,
381 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
382 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
383 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
393 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
394 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
396 gchar *item_name = NULL;
397 gint item_weight = 400;
399 if (type != TNY_FOLDER_TYPE_ROOT) {
402 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
403 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
404 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
405 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
407 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
411 /* note: we cannot reliably get the counts from the tree model, we need
412 * to use explicit calls on tny_folder for some reason.
414 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
415 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
416 (type == TNY_FOLDER_TYPE_OUTBOX) ||
417 (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
418 number = tny_folder_get_all_count (TNY_FOLDER(instance));
420 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
422 /* Use bold font style if there are unread or unset messages */
424 if (type == TNY_FOLDER_TYPE_INBOX)
425 item_name = g_strdup_printf ("%s (%d)", _("mcen_me_folder_inbox"), number);
427 item_name = g_strdup_printf ("%s (%d)", fname, number);
430 if (type == TNY_FOLDER_TYPE_INBOX)
431 item_name = g_strdup (_("mcen_me_folder_inbox"));
433 item_name = g_strdup (fname);
437 } else if (TNY_IS_ACCOUNT (instance)) {
438 /* If it's a server account */
439 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
440 item_name = g_strdup (priv->local_account_name);
442 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
443 /* fname is only correct when the items are first
444 * added to the model, not when the account is
445 * changed later, so get the name from the account
447 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
450 item_name = g_strdup (fname);
456 item_name = g_strdup ("unknown");
458 if (item_name && item_weight) {
459 /* Set the name in the treeview cell: */
460 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
462 /* Notify display name observers */
463 /* TODO: What listens for this signal, and how can it use only the new name? */
464 if (((GObject *) priv->cur_folder_store) == instance) {
465 g_signal_emit (G_OBJECT(self),
466 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
473 /* If it is a Memory card account, make sure that we have the correct name.
474 * This function will be trigerred again when the name has been retrieved: */
475 if (TNY_IS_STORE_ACCOUNT (instance) &&
476 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
478 /* Get the account name asynchronously: */
479 GetMmcAccountNameData *callback_data =
480 g_slice_new0(GetMmcAccountNameData);
481 callback_data->self = self;
483 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
485 callback_data->previous_name = g_strdup (name);
487 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
488 on_get_mmc_account_name, callback_data);
491 g_object_unref (G_OBJECT (instance));
498 GdkPixbuf *pixbuf_open;
499 GdkPixbuf *pixbuf_close;
504 get_folder_icons (TnyFolderType type, GObject *instance)
506 GdkPixbuf *pixbuf = NULL;
507 GdkPixbuf *pixbuf_open = NULL;
508 GdkPixbuf *pixbuf_close = NULL;
509 ThreePixbufs *retval = g_slice_new (ThreePixbufs);
511 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
512 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
513 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
514 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
515 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
517 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
518 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
519 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
520 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
521 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
523 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
524 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
525 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
526 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
527 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
530 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
531 /* We include the MERGE type here because it's used to create
532 the local OUTBOX folder */
533 if (type == TNY_FOLDER_TYPE_NORMAL ||
534 type == TNY_FOLDER_TYPE_UNKNOWN) {
535 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
539 case TNY_FOLDER_TYPE_INVALID:
540 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
543 case TNY_FOLDER_TYPE_ROOT:
544 if (TNY_IS_ACCOUNT (instance)) {
546 if (modest_tny_account_is_virtual_local_folders (
547 TNY_ACCOUNT (instance))) {
550 avirt_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
551 MODEST_ICON_SIZE_SMALL));
553 if (!avirt_pixbuf_open) {
554 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
555 MODEST_ICON_SIZE_SMALL);
556 avirt_pixbuf_open = gdk_pixbuf_copy (avirt_pixbuf);
557 gdk_pixbuf_composite (emblem, avirt_pixbuf_open, 0, 0,
558 MIN (gdk_pixbuf_get_width (emblem),
559 gdk_pixbuf_get_width (avirt_pixbuf_open)),
560 MIN (gdk_pixbuf_get_height (emblem),
561 gdk_pixbuf_get_height (avirt_pixbuf_open)),
562 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
563 g_object_unref (emblem);
566 if (!avirt_pixbuf_close) {
567 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
568 MODEST_ICON_SIZE_SMALL);
569 avirt_pixbuf_close = gdk_pixbuf_copy (avirt_pixbuf);
570 gdk_pixbuf_composite (emblem, avirt_pixbuf_close, 0, 0,
571 MIN (gdk_pixbuf_get_width (emblem),
572 gdk_pixbuf_get_width (avirt_pixbuf_close)),
573 MIN (gdk_pixbuf_get_height (emblem),
574 gdk_pixbuf_get_height (avirt_pixbuf_close)),
575 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
576 g_object_unref (emblem);
580 pixbuf = g_object_ref (avirt_pixbuf);
581 pixbuf_open = g_object_ref (avirt_pixbuf_open);
582 pixbuf_close = g_object_ref (avirt_pixbuf_close);
586 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
588 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
590 ammc_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_MMC,
591 MODEST_ICON_SIZE_SMALL));
593 if (!ammc_pixbuf_open) {
594 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
595 MODEST_ICON_SIZE_SMALL);
596 ammc_pixbuf_open = gdk_pixbuf_copy (ammc_pixbuf);
597 gdk_pixbuf_composite (emblem, ammc_pixbuf_open, 0, 0,
598 MIN (gdk_pixbuf_get_width (emblem),
599 gdk_pixbuf_get_width (ammc_pixbuf_open)),
600 MIN (gdk_pixbuf_get_height (emblem),
601 gdk_pixbuf_get_height (ammc_pixbuf_open)),
602 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
603 g_object_unref (emblem);
606 if (!ammc_pixbuf_close) {
607 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
608 MODEST_ICON_SIZE_SMALL);
609 ammc_pixbuf_close = gdk_pixbuf_copy (ammc_pixbuf);
610 gdk_pixbuf_composite (emblem, ammc_pixbuf_close, 0, 0,
611 MIN (gdk_pixbuf_get_width (emblem),
612 gdk_pixbuf_get_width (ammc_pixbuf_close)),
613 MIN (gdk_pixbuf_get_height (emblem),
614 gdk_pixbuf_get_height (ammc_pixbuf_close)),
615 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
616 g_object_unref (emblem);
620 pixbuf = g_object_ref (ammc_pixbuf);
621 pixbuf_open = g_object_ref (ammc_pixbuf_open);
622 pixbuf_close = g_object_ref (ammc_pixbuf_close);
627 anorm_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT,
628 MODEST_ICON_SIZE_SMALL));
629 if (!anorm_pixbuf_open) {
630 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
631 MODEST_ICON_SIZE_SMALL);
632 anorm_pixbuf_open = gdk_pixbuf_copy (anorm_pixbuf);
633 gdk_pixbuf_composite (emblem, anorm_pixbuf_open, 0, 0,
634 MIN (gdk_pixbuf_get_width (emblem),
635 gdk_pixbuf_get_width (anorm_pixbuf_open)),
636 MIN (gdk_pixbuf_get_height (emblem),
637 gdk_pixbuf_get_height (anorm_pixbuf_open)),
638 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
639 g_object_unref (emblem);
642 if (!anorm_pixbuf_close) {
643 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
644 MODEST_ICON_SIZE_SMALL);
645 anorm_pixbuf_close = gdk_pixbuf_copy (anorm_pixbuf);
646 gdk_pixbuf_composite (emblem, anorm_pixbuf_close, 0, 0,
647 MIN (gdk_pixbuf_get_width (emblem),
648 gdk_pixbuf_get_width (anorm_pixbuf_close)),
649 MIN (gdk_pixbuf_get_height (emblem),
650 gdk_pixbuf_get_height (anorm_pixbuf_close)),
651 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
652 g_object_unref (emblem);
656 pixbuf = g_object_ref (anorm_pixbuf);
657 pixbuf_open = g_object_ref (anorm_pixbuf_open);
658 pixbuf_close = g_object_ref (anorm_pixbuf_close);
664 case TNY_FOLDER_TYPE_INBOX:
667 inbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX,
668 MODEST_ICON_SIZE_SMALL));
670 if (!inbox_pixbuf_open) {
671 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
672 MODEST_ICON_SIZE_SMALL);
673 inbox_pixbuf_open = gdk_pixbuf_copy (inbox_pixbuf);
674 gdk_pixbuf_composite (emblem, inbox_pixbuf_open, 0, 0,
675 MIN (gdk_pixbuf_get_width (emblem),
676 gdk_pixbuf_get_width (inbox_pixbuf_open)),
677 MIN (gdk_pixbuf_get_height (emblem),
678 gdk_pixbuf_get_height (inbox_pixbuf_open)),
679 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
680 g_object_unref (emblem);
683 if (!inbox_pixbuf_close) {
684 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
685 MODEST_ICON_SIZE_SMALL);
686 inbox_pixbuf_close = gdk_pixbuf_copy (inbox_pixbuf);
687 gdk_pixbuf_composite (emblem, inbox_pixbuf_close, 0, 0,
688 MIN (gdk_pixbuf_get_width (emblem),
689 gdk_pixbuf_get_width (inbox_pixbuf_close)),
690 MIN (gdk_pixbuf_get_height (emblem),
691 gdk_pixbuf_get_height (inbox_pixbuf_close)),
692 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
693 g_object_unref (emblem);
697 pixbuf = g_object_ref (inbox_pixbuf);
698 pixbuf_open = g_object_ref (inbox_pixbuf_open);
699 pixbuf_close = g_object_ref (inbox_pixbuf_close);
702 case TNY_FOLDER_TYPE_OUTBOX:
704 outbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX,
705 MODEST_ICON_SIZE_SMALL));
707 if (!outbox_pixbuf_open) {
708 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
709 MODEST_ICON_SIZE_SMALL);
710 outbox_pixbuf_open = gdk_pixbuf_copy (outbox_pixbuf);
711 gdk_pixbuf_composite (emblem, outbox_pixbuf_open, 0, 0,
712 MIN (gdk_pixbuf_get_width (emblem),
713 gdk_pixbuf_get_width (outbox_pixbuf_open)),
714 MIN (gdk_pixbuf_get_height (emblem),
715 gdk_pixbuf_get_height (outbox_pixbuf_open)),
716 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
717 g_object_unref (emblem);
720 if (!outbox_pixbuf_close) {
721 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
722 MODEST_ICON_SIZE_SMALL);
723 outbox_pixbuf_close = gdk_pixbuf_copy (outbox_pixbuf);
724 gdk_pixbuf_composite (emblem, outbox_pixbuf_close, 0, 0,
725 MIN (gdk_pixbuf_get_width (emblem),
726 gdk_pixbuf_get_width (outbox_pixbuf_close)),
727 MIN (gdk_pixbuf_get_height (emblem),
728 gdk_pixbuf_get_height (outbox_pixbuf_close)),
729 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
730 g_object_unref (emblem);
734 pixbuf = g_object_ref (outbox_pixbuf);
735 pixbuf_open = g_object_ref (outbox_pixbuf_open);
736 pixbuf_close = g_object_ref (outbox_pixbuf_close);
739 case TNY_FOLDER_TYPE_JUNK:
741 junk_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK,
742 MODEST_ICON_SIZE_SMALL));
743 if (!junk_pixbuf_open) {
744 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
745 MODEST_ICON_SIZE_SMALL);
746 junk_pixbuf_open = gdk_pixbuf_copy (junk_pixbuf);
747 gdk_pixbuf_composite (emblem, junk_pixbuf_open, 0, 0,
748 MIN (gdk_pixbuf_get_width (emblem),
749 gdk_pixbuf_get_width (junk_pixbuf_open)),
750 MIN (gdk_pixbuf_get_height (emblem),
751 gdk_pixbuf_get_height (junk_pixbuf_open)),
752 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
753 g_object_unref (emblem);
756 if (!junk_pixbuf_close) {
757 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
758 MODEST_ICON_SIZE_SMALL);
759 junk_pixbuf_close = gdk_pixbuf_copy (junk_pixbuf);
760 gdk_pixbuf_composite (emblem, junk_pixbuf_close, 0, 0,
761 MIN (gdk_pixbuf_get_width (emblem),
762 gdk_pixbuf_get_width (junk_pixbuf_close)),
763 MIN (gdk_pixbuf_get_height (emblem),
764 gdk_pixbuf_get_height (junk_pixbuf_close)),
765 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
766 g_object_unref (emblem);
770 pixbuf = g_object_ref (junk_pixbuf);
771 pixbuf_open = g_object_ref (junk_pixbuf_open);
772 pixbuf_close = g_object_ref (junk_pixbuf_close);
776 case TNY_FOLDER_TYPE_SENT:
778 sent_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_SENT,
779 MODEST_ICON_SIZE_SMALL));
781 if (!sent_pixbuf_open) {
782 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
783 MODEST_ICON_SIZE_SMALL);
784 sent_pixbuf_open = gdk_pixbuf_copy (sent_pixbuf);
785 gdk_pixbuf_composite (emblem, sent_pixbuf_open, 0, 0,
786 MIN (gdk_pixbuf_get_width (emblem),
787 gdk_pixbuf_get_width (sent_pixbuf_open)),
788 MIN (gdk_pixbuf_get_height (emblem),
789 gdk_pixbuf_get_height (sent_pixbuf_open)),
790 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
791 g_object_unref (emblem);
794 if (!sent_pixbuf_close) {
795 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
796 MODEST_ICON_SIZE_SMALL);
797 sent_pixbuf_close = gdk_pixbuf_copy (sent_pixbuf);
798 gdk_pixbuf_composite (emblem, sent_pixbuf_close, 0, 0,
799 MIN (gdk_pixbuf_get_width (emblem),
800 gdk_pixbuf_get_width (sent_pixbuf_close)),
801 MIN (gdk_pixbuf_get_height (emblem),
802 gdk_pixbuf_get_height (sent_pixbuf_close)),
803 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
804 g_object_unref (emblem);
808 pixbuf = g_object_ref (sent_pixbuf);
809 pixbuf_open = g_object_ref (sent_pixbuf_open);
810 pixbuf_close = g_object_ref (sent_pixbuf_close);
814 case TNY_FOLDER_TYPE_TRASH:
816 trash_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH,
817 MODEST_ICON_SIZE_SMALL));
818 if (!trash_pixbuf_open) {
819 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
820 MODEST_ICON_SIZE_SMALL);
821 trash_pixbuf_open = gdk_pixbuf_copy (trash_pixbuf);
822 gdk_pixbuf_composite (emblem, trash_pixbuf_open, 0, 0,
823 MIN (gdk_pixbuf_get_width (emblem),
824 gdk_pixbuf_get_width (trash_pixbuf_open)),
825 MIN (gdk_pixbuf_get_height (emblem),
826 gdk_pixbuf_get_height (trash_pixbuf_open)),
827 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
828 g_object_unref (emblem);
831 if (!trash_pixbuf_close) {
832 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
833 MODEST_ICON_SIZE_SMALL);
834 trash_pixbuf_close = gdk_pixbuf_copy (trash_pixbuf);
835 gdk_pixbuf_composite (emblem, trash_pixbuf_close, 0, 0,
836 MIN (gdk_pixbuf_get_width (emblem),
837 gdk_pixbuf_get_width (trash_pixbuf_close)),
838 MIN (gdk_pixbuf_get_height (emblem),
839 gdk_pixbuf_get_height (trash_pixbuf_close)),
840 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
841 g_object_unref (emblem);
845 pixbuf = g_object_ref (trash_pixbuf);
846 pixbuf_open = g_object_ref (trash_pixbuf_open);
847 pixbuf_close = g_object_ref (trash_pixbuf_close);
850 case TNY_FOLDER_TYPE_DRAFTS:
852 draft_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS,
853 MODEST_ICON_SIZE_SMALL));
855 if (!draft_pixbuf_open) {
856 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
857 MODEST_ICON_SIZE_SMALL);
858 draft_pixbuf_open = gdk_pixbuf_copy (draft_pixbuf);
859 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0,
860 MIN (gdk_pixbuf_get_width (emblem),
861 gdk_pixbuf_get_width (draft_pixbuf_open)),
862 MIN (gdk_pixbuf_get_height (emblem),
863 gdk_pixbuf_get_height (draft_pixbuf_open)),
864 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
865 g_object_unref (emblem);
868 if (!draft_pixbuf_close) {
869 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
870 MODEST_ICON_SIZE_SMALL);
871 draft_pixbuf_close = gdk_pixbuf_copy (draft_pixbuf);
872 gdk_pixbuf_composite (emblem, draft_pixbuf_close, 0, 0,
873 MIN (gdk_pixbuf_get_width (emblem),
874 gdk_pixbuf_get_width (draft_pixbuf_close)),
875 MIN (gdk_pixbuf_get_height (emblem),
876 gdk_pixbuf_get_height (draft_pixbuf_close)),
877 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
878 g_object_unref (emblem);
882 pixbuf = g_object_ref (draft_pixbuf);
883 pixbuf_open = g_object_ref (draft_pixbuf_open);
884 pixbuf_close = g_object_ref (draft_pixbuf_close);
887 case TNY_FOLDER_TYPE_NORMAL:
890 normal_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL,
891 MODEST_ICON_SIZE_SMALL));
893 if (!normal_pixbuf_open) {
894 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
895 MODEST_ICON_SIZE_SMALL);
896 normal_pixbuf_open = gdk_pixbuf_copy (normal_pixbuf);
897 gdk_pixbuf_composite (emblem, normal_pixbuf_open, 0, 0,
898 MIN (gdk_pixbuf_get_width (emblem),
899 gdk_pixbuf_get_width (normal_pixbuf_open)),
900 MIN (gdk_pixbuf_get_height (emblem),
901 gdk_pixbuf_get_height (normal_pixbuf_open)),
902 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
903 g_object_unref (emblem);
906 if (!normal_pixbuf_close) {
907 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
908 MODEST_ICON_SIZE_SMALL);
909 normal_pixbuf_close = gdk_pixbuf_copy (normal_pixbuf);
910 gdk_pixbuf_composite (emblem, normal_pixbuf_close, 0, 0,
911 MIN (gdk_pixbuf_get_width (emblem),
912 gdk_pixbuf_get_width (normal_pixbuf_close)),
913 MIN (gdk_pixbuf_get_height (emblem),
914 gdk_pixbuf_get_height (normal_pixbuf_close)),
915 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
916 g_object_unref (emblem);
920 pixbuf = g_object_ref (normal_pixbuf);
921 pixbuf_open = g_object_ref (normal_pixbuf_open);
922 pixbuf_close = g_object_ref (normal_pixbuf_close);
928 retval->pixbuf = pixbuf;
929 retval->pixbuf_open = pixbuf_open;
930 retval->pixbuf_close = pixbuf_close;
937 free_pixbufs (ThreePixbufs *pixbufs)
939 g_object_unref (pixbufs->pixbuf);
940 g_object_unref (pixbufs->pixbuf_open);
941 g_object_unref (pixbufs->pixbuf_close);
942 g_slice_free (ThreePixbufs, pixbufs);
946 icon_cell_data (GtkTreeViewColumn *column,
947 GtkCellRenderer *renderer,
948 GtkTreeModel *tree_model,
952 GObject *rendobj = NULL, *instance = NULL;
953 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
954 gboolean has_children;
955 ThreePixbufs *pixbufs;
957 rendobj = (GObject *) renderer;
959 gtk_tree_model_get (tree_model, iter,
960 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
961 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
967 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
968 pixbufs = get_folder_icons (type, instance);
969 g_object_unref (instance);
972 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
975 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
976 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
979 free_pixbufs (pixbufs);
985 add_columns (GtkWidget *treeview)
987 GtkTreeViewColumn *column;
988 GtkCellRenderer *renderer;
989 GtkTreeSelection *sel;
992 column = gtk_tree_view_column_new ();
994 /* Set icon and text render function */
995 renderer = gtk_cell_renderer_pixbuf_new();
996 gtk_tree_view_column_pack_start (column, renderer, FALSE);
997 gtk_tree_view_column_set_cell_data_func(column, renderer,
998 icon_cell_data, treeview, NULL);
1000 renderer = gtk_cell_renderer_text_new();
1001 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END,
1002 "ellipsize-set", TRUE, NULL);
1003 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1004 gtk_tree_view_column_set_cell_data_func(column, renderer,
1005 text_cell_data, treeview, NULL);
1007 /* Set selection mode */
1008 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1009 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1011 /* Set treeview appearance */
1012 gtk_tree_view_column_set_spacing (column, 2);
1013 gtk_tree_view_column_set_resizable (column, TRUE);
1014 gtk_tree_view_column_set_fixed_width (column, TRUE);
1015 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1016 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1019 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1023 modest_folder_view_init (ModestFolderView *obj)
1025 ModestFolderViewPrivate *priv;
1028 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1030 priv->timer_expander = 0;
1031 priv->account_store = NULL;
1033 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1034 priv->cur_folder_store = NULL;
1035 priv->visible_account_id = NULL;
1036 priv->folder_to_select = NULL;
1038 priv->reexpand = TRUE;
1040 /* Initialize the local account name */
1041 conf = modest_runtime_get_conf();
1042 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1044 /* Init email clipboard */
1045 priv->clipboard = modest_runtime_get_email_clipboard ();
1046 priv->hidding_ids = NULL;
1047 priv->n_selected = 0;
1048 priv->reselect = FALSE;
1049 priv->show_non_move = TRUE;
1051 /* Build treeview */
1052 add_columns (GTK_WIDGET (obj));
1054 /* Setup drag and drop */
1055 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1057 /* Connect signals */
1058 g_signal_connect (G_OBJECT (obj),
1060 G_CALLBACK (on_key_pressed), NULL);
1062 priv->display_name_changed_signal =
1063 g_signal_connect (modest_runtime_get_account_mgr (),
1064 "display_name_changed",
1065 G_CALLBACK (on_display_name_changed),
1069 * Track changes in the local account name (in the device it
1070 * will be the device name)
1072 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1074 G_CALLBACK(on_configuration_key_changed),
1079 tny_account_store_view_init (gpointer g, gpointer iface_data)
1081 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1083 klass->set_account_store = modest_folder_view_set_account_store;
1087 modest_folder_view_finalize (GObject *obj)
1089 ModestFolderViewPrivate *priv;
1090 GtkTreeSelection *sel;
1092 g_return_if_fail (obj);
1094 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1096 if (priv->timer_expander != 0) {
1097 g_source_remove (priv->timer_expander);
1098 priv->timer_expander = 0;
1101 if (priv->account_store) {
1102 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1103 priv->account_inserted_signal);
1104 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1105 priv->account_removed_signal);
1106 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1107 priv->account_changed_signal);
1108 g_object_unref (G_OBJECT(priv->account_store));
1109 priv->account_store = NULL;
1113 g_object_unref (G_OBJECT (priv->query));
1117 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1118 if (priv->folder_to_select) {
1119 g_object_unref (G_OBJECT(priv->folder_to_select));
1120 priv->folder_to_select = NULL;
1123 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1125 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1127 g_free (priv->local_account_name);
1128 g_free (priv->visible_account_id);
1130 if (priv->conf_key_signal) {
1131 g_signal_handler_disconnect (modest_runtime_get_conf (),
1132 priv->conf_key_signal);
1133 priv->conf_key_signal = 0;
1136 if (priv->cur_folder_store) {
1137 if (TNY_IS_FOLDER(priv->cur_folder_store)) {
1138 ModestMailOperation *mail_op;
1140 mail_op = modest_mail_operation_new (NULL);
1141 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1143 modest_mail_operation_sync_folder (mail_op, TNY_FOLDER (priv->cur_folder_store), FALSE);
1144 g_object_unref (mail_op);
1147 g_object_unref (priv->cur_folder_store);
1148 priv->cur_folder_store = NULL;
1151 /* Clear hidding array created by cut operation */
1152 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1154 G_OBJECT_CLASS(parent_class)->finalize (obj);
1159 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1161 ModestFolderViewPrivate *priv;
1164 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1165 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1167 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1168 device = tny_account_store_get_device (account_store);
1170 if (G_UNLIKELY (priv->account_store)) {
1172 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1173 priv->account_inserted_signal))
1174 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1175 priv->account_inserted_signal);
1176 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1177 priv->account_removed_signal))
1178 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1179 priv->account_removed_signal);
1180 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1181 priv->account_changed_signal))
1182 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1183 priv->account_changed_signal);
1184 g_object_unref (G_OBJECT (priv->account_store));
1187 priv->account_store = g_object_ref (G_OBJECT (account_store));
1189 priv->account_removed_signal =
1190 g_signal_connect (G_OBJECT(account_store), "account_removed",
1191 G_CALLBACK (on_account_removed), self);
1193 priv->account_inserted_signal =
1194 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1195 G_CALLBACK (on_account_inserted), self);
1197 priv->account_changed_signal =
1198 g_signal_connect (G_OBJECT(account_store), "account_changed",
1199 G_CALLBACK (on_account_changed), self);
1201 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1203 g_object_unref (G_OBJECT (device));
1207 on_account_inserted (TnyAccountStore *account_store,
1208 TnyAccount *account,
1211 ModestFolderViewPrivate *priv;
1212 GtkTreeModel *sort_model, *filter_model;
1214 /* Ignore transport account insertions, we're not showing them
1215 in the folder view */
1216 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1219 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1221 /* If we're adding a new account, and there is no previous
1222 one, we need to select the visible server account */
1223 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1224 !priv->visible_account_id)
1225 modest_widget_memory_restore (modest_runtime_get_conf(),
1226 G_OBJECT (user_data),
1227 MODEST_CONF_FOLDER_VIEW_KEY);
1229 /* Get the inner model */
1230 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1231 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1233 /* Insert the account in the model */
1234 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1235 G_OBJECT (account));
1237 /* Refilter the model */
1238 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1243 on_account_changed (TnyAccountStore *account_store,
1244 TnyAccount *tny_account,
1247 ModestFolderViewPrivate *priv;
1248 GtkTreeModel *sort_model, *filter_model;
1250 /* Ignore transport account insertions, we're not showing them
1251 in the folder view */
1252 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1255 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1257 /* Get the inner model */
1258 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1259 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1261 /* Remove the account from the model */
1262 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1263 G_OBJECT (tny_account));
1265 /* Insert the account in the model */
1266 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1267 G_OBJECT (tny_account));
1269 /* Refilter the model */
1270 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1275 * Selects the first inbox or the local account in an idle
1278 on_idle_select_first_inbox_or_local (gpointer user_data)
1280 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1282 gdk_threads_enter ();
1283 modest_folder_view_select_first_inbox_or_local (self);
1284 gdk_threads_leave ();
1291 on_account_removed (TnyAccountStore *account_store,
1292 TnyAccount *account,
1295 ModestFolderView *self = NULL;
1296 ModestFolderViewPrivate *priv;
1297 GtkTreeModel *sort_model, *filter_model;
1298 GtkTreeSelection *sel = NULL;
1299 gboolean same_account_selected = FALSE;
1301 /* Ignore transport account removals, we're not showing them
1302 in the folder view */
1303 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1306 self = MODEST_FOLDER_VIEW (user_data);
1307 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1309 /* Invalidate the cur_folder_store only if the selected folder
1310 belongs to the account that is being removed */
1311 if (priv->cur_folder_store) {
1312 TnyAccount *selected_folder_account = NULL;
1314 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1315 selected_folder_account =
1316 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1318 selected_folder_account =
1319 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1322 if (selected_folder_account == account) {
1323 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1324 gtk_tree_selection_unselect_all (sel);
1325 same_account_selected = TRUE;
1327 g_object_unref (selected_folder_account);
1330 /* Invalidate row to select only if the folder to select
1331 belongs to the account that is being removed*/
1332 if (priv->folder_to_select) {
1333 TnyAccount *folder_to_select_account = NULL;
1335 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1336 if (folder_to_select_account == account) {
1337 modest_folder_view_disable_next_folder_selection (self);
1338 g_object_unref (priv->folder_to_select);
1339 priv->folder_to_select = NULL;
1341 g_object_unref (folder_to_select_account);
1344 /* Remove the account from the model */
1345 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1346 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1347 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1348 G_OBJECT (account));
1350 /* If the removed account is the currently viewed one then
1351 clear the configuration value. The new visible account will be the default account */
1352 if (priv->visible_account_id &&
1353 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1355 /* Clear the current visible account_id */
1356 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1358 /* Call the restore method, this will set the new visible account */
1359 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1360 MODEST_CONF_FOLDER_VIEW_KEY);
1363 /* Refilter the model */
1364 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1366 /* Select the first INBOX if the currently selected folder
1367 belongs to the account that is being deleted */
1368 if (same_account_selected)
1369 g_idle_add (on_idle_select_first_inbox_or_local, self);
1373 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1375 GtkTreeViewColumn *col;
1377 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1379 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1381 g_printerr ("modest: failed get column for title\n");
1385 gtk_tree_view_column_set_title (col, title);
1386 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1391 modest_folder_view_on_map (ModestFolderView *self,
1392 GdkEventExpose *event,
1395 ModestFolderViewPrivate *priv;
1397 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1399 /* This won't happen often */
1400 if (G_UNLIKELY (priv->reselect)) {
1401 /* Select the first inbox or the local account if not found */
1403 /* TODO: this could cause a lock at startup, so we
1404 comment it for the moment. We know that this will
1405 be a bug, because the INBOX is not selected, but we
1406 need to rewrite some parts of Modest to avoid the
1407 deathlock situation */
1408 /* TODO: check if this is still the case */
1409 priv->reselect = FALSE;
1410 modest_folder_view_select_first_inbox_or_local (self);
1411 /* Notify the display name observers */
1412 g_signal_emit (G_OBJECT(self),
1413 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1417 if (priv->reexpand) {
1418 expand_root_items (self);
1419 priv->reexpand = FALSE;
1426 modest_folder_view_new (TnyFolderStoreQuery *query)
1429 ModestFolderViewPrivate *priv;
1430 GtkTreeSelection *sel;
1432 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1433 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1436 priv->query = g_object_ref (query);
1438 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1439 priv->changed_signal = g_signal_connect (sel, "changed",
1440 G_CALLBACK (on_selection_changed), self);
1442 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1444 return GTK_WIDGET(self);
1447 /* this feels dirty; any other way to expand all the root items? */
1449 expand_root_items (ModestFolderView *self)
1452 GtkTreeModel *model;
1455 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1456 path = gtk_tree_path_new_first ();
1458 /* all folders should have child items, so.. */
1460 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1461 gtk_tree_path_next (path);
1462 } while (gtk_tree_model_get_iter (model, &iter, path));
1464 gtk_tree_path_free (path);
1468 * We use this function to implement the
1469 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1470 * account in this case, and the local folders.
1473 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1475 ModestFolderViewPrivate *priv;
1476 gboolean retval = TRUE;
1477 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1478 GObject *instance = NULL;
1479 const gchar *id = NULL;
1481 gboolean found = FALSE;
1482 gboolean cleared = FALSE;
1484 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1485 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1487 gtk_tree_model_get (model, iter,
1488 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1489 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1492 /* Do not show if there is no instance, this could indeed
1493 happen when the model is being modified while it's being
1494 drawn. This could occur for example when moving folders
1499 if (type == TNY_FOLDER_TYPE_ROOT) {
1500 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1501 account instead of a folder. */
1502 if (TNY_IS_ACCOUNT (instance)) {
1503 TnyAccount *acc = TNY_ACCOUNT (instance);
1504 const gchar *account_id = tny_account_get_id (acc);
1506 /* If it isn't a special folder,
1507 * don't show it unless it is the visible account: */
1508 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1509 !modest_tny_account_is_virtual_local_folders (acc) &&
1510 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1512 /* Show only the visible account id */
1513 if (priv->visible_account_id) {
1514 if (strcmp (account_id, priv->visible_account_id))
1521 /* Never show these to the user. They are merged into one folder
1522 * in the local-folders account instead: */
1523 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1528 /* Check hiding (if necessary) */
1529 cleared = modest_email_clipboard_cleared (priv->clipboard);
1530 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1531 id = tny_folder_get_id (TNY_FOLDER(instance));
1532 if (priv->hidding_ids != NULL)
1533 for (i=0; i < priv->n_selected && !found; i++)
1534 if (priv->hidding_ids[i] != NULL && id != NULL)
1535 found = (!strcmp (priv->hidding_ids[i], id));
1541 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1542 folder as no message can be move there according to UI specs */
1543 if (!priv->show_non_move) {
1545 case TNY_FOLDER_TYPE_OUTBOX:
1546 case TNY_FOLDER_TYPE_SENT:
1547 case TNY_FOLDER_TYPE_DRAFTS:
1550 case TNY_FOLDER_TYPE_UNKNOWN:
1551 case TNY_FOLDER_TYPE_NORMAL:
1552 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1553 if (type == TNY_FOLDER_TYPE_INVALID)
1554 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1556 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1557 type == TNY_FOLDER_TYPE_SENT
1558 || type == TNY_FOLDER_TYPE_DRAFTS)
1567 g_object_unref (instance);
1574 modest_folder_view_update_model (ModestFolderView *self,
1575 TnyAccountStore *account_store)
1577 ModestFolderViewPrivate *priv;
1578 GtkTreeModel *model /* , *old_model */;
1579 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1581 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1582 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1585 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1587 /* Notify that there is no folder selected */
1588 g_signal_emit (G_OBJECT(self),
1589 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1591 if (priv->cur_folder_store) {
1592 g_object_unref (priv->cur_folder_store);
1593 priv->cur_folder_store = NULL;
1596 /* FIXME: the local accounts are not shown when the query
1597 selects only the subscribed folders */
1598 model = tny_gtk_folder_store_tree_model_new (NULL);
1600 /* Get the accounts: */
1601 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1603 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1605 sortable = gtk_tree_model_sort_new_with_model (model);
1606 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1607 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1608 GTK_SORT_ASCENDING);
1609 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1610 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1611 cmp_rows, NULL, NULL);
1613 /* Create filter model */
1614 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1615 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1621 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1622 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1623 (GCallback) on_row_inserted_maybe_select_folder, self);
1626 g_object_unref (model);
1627 g_object_unref (filter_model);
1628 g_object_unref (sortable);
1630 /* Force a reselection of the INBOX next time the widget is shown */
1631 priv->reselect = TRUE;
1638 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1640 GtkTreeModel *model = NULL;
1641 TnyFolderStore *folder = NULL;
1643 ModestFolderView *tree_view = NULL;
1644 ModestFolderViewPrivate *priv = NULL;
1645 gboolean selected = FALSE;
1647 g_return_if_fail (sel);
1648 g_return_if_fail (user_data);
1650 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1652 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1654 tree_view = MODEST_FOLDER_VIEW (user_data);
1657 gtk_tree_model_get (model, &iter,
1658 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1661 /* If the folder is the same do not notify */
1662 if (folder && priv->cur_folder_store == folder) {
1663 g_object_unref (folder);
1668 /* Current folder was unselected */
1669 if (priv->cur_folder_store) {
1670 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1671 priv->cur_folder_store, FALSE);
1673 if (TNY_IS_FOLDER(priv->cur_folder_store))
1674 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1675 FALSE, NULL, NULL, NULL);
1677 /* FALSE --> don't expunge the messages */
1679 g_object_unref (priv->cur_folder_store);
1680 priv->cur_folder_store = NULL;
1683 /* New current references */
1684 priv->cur_folder_store = folder;
1686 /* New folder has been selected. Do not notify if there is
1687 nothing new selected */
1689 g_signal_emit (G_OBJECT(tree_view),
1690 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1691 0, priv->cur_folder_store, TRUE);
1696 modest_folder_view_get_selected (ModestFolderView *self)
1698 ModestFolderViewPrivate *priv;
1700 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1702 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1703 if (priv->cur_folder_store)
1704 g_object_ref (priv->cur_folder_store);
1706 return priv->cur_folder_store;
1710 get_cmp_rows_type_pos (GObject *folder)
1712 /* Remote accounts -> Local account -> MMC account .*/
1715 if (TNY_IS_ACCOUNT (folder) &&
1716 modest_tny_account_is_virtual_local_folders (
1717 TNY_ACCOUNT (folder))) {
1719 } else if (TNY_IS_ACCOUNT (folder)) {
1720 TnyAccount *account = TNY_ACCOUNT (folder);
1721 const gchar *account_id = tny_account_get_id (account);
1722 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1728 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1729 return -1; /* Should never happen */
1734 get_cmp_subfolder_type_pos (TnyFolderType t)
1736 /* Inbox, Outbox, Drafts, Sent, User */
1740 case TNY_FOLDER_TYPE_INBOX:
1743 case TNY_FOLDER_TYPE_OUTBOX:
1746 case TNY_FOLDER_TYPE_DRAFTS:
1749 case TNY_FOLDER_TYPE_SENT:
1758 * This function orders the mail accounts according to these rules:
1759 * 1st - remote accounts
1760 * 2nd - local account
1764 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1768 gchar *name1 = NULL;
1769 gchar *name2 = NULL;
1770 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1771 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1772 GObject *folder1 = NULL;
1773 GObject *folder2 = NULL;
1775 gtk_tree_model_get (tree_model, iter1,
1776 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1777 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1778 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1780 gtk_tree_model_get (tree_model, iter2,
1781 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1782 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1783 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1786 /* Return if we get no folder. This could happen when folder
1787 operations are happening. The model is updated after the
1788 folder copy/move actually occurs, so there could be
1789 situations where the model to be drawn is not correct */
1790 if (!folder1 || !folder2)
1793 if (type == TNY_FOLDER_TYPE_ROOT) {
1794 /* Compare the types, so that
1795 * Remote accounts -> Local account -> MMC account .*/
1796 const gint pos1 = get_cmp_rows_type_pos (folder1);
1797 const gint pos2 = get_cmp_rows_type_pos (folder2);
1798 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1799 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1802 else if (pos1 > pos2)
1805 /* Compare items of the same type: */
1807 TnyAccount *account1 = NULL;
1808 if (TNY_IS_ACCOUNT (folder1))
1809 account1 = TNY_ACCOUNT (folder1);
1811 TnyAccount *account2 = NULL;
1812 if (TNY_IS_ACCOUNT (folder2))
1813 account2 = TNY_ACCOUNT (folder2);
1815 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1816 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1818 if (!account_id && !account_id2) {
1820 } else if (!account_id) {
1822 } else if (!account_id2) {
1824 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1827 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1831 gint cmp1 = 0, cmp2 = 0;
1832 /* get the parent to know if it's a local folder */
1835 gboolean has_parent;
1836 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1838 GObject *parent_folder;
1839 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1840 gtk_tree_model_get (tree_model, &parent,
1841 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1842 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1844 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1845 TNY_IS_ACCOUNT (parent_folder)) {
1846 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1847 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1848 (TNY_FOLDER (folder1)));
1849 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1850 (TNY_FOLDER (folder2)));
1851 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1852 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1855 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1861 g_object_unref (parent_folder);
1864 /* if they are not local folders */
1866 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1867 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1871 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1873 cmp = (cmp1 - cmp2);
1878 g_object_unref(G_OBJECT(folder1));
1880 g_object_unref(G_OBJECT(folder2));
1888 /*****************************************************************************/
1889 /* DRAG and DROP stuff */
1890 /*****************************************************************************/
1892 * This function fills the #GtkSelectionData with the row and the
1893 * model that has been dragged. It's called when this widget is a
1894 * source for dnd after the event drop happened
1897 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1898 guint info, guint time, gpointer data)
1900 GtkTreeSelection *selection;
1901 GtkTreeModel *model;
1903 GtkTreePath *source_row;
1905 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1906 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1908 source_row = gtk_tree_model_get_path (model, &iter);
1909 gtk_tree_set_row_drag_data (selection_data,
1913 gtk_tree_path_free (source_row);
1917 typedef struct _DndHelper {
1918 ModestFolderView *folder_view;
1919 gboolean delete_source;
1920 GtkTreePath *source_row;
1921 GdkDragContext *context;
1926 dnd_helper_destroyer (DndHelper *helper)
1928 /* Free the helper */
1929 g_object_unref (helper->folder_view);
1930 gtk_tree_path_free (helper->source_row);
1931 g_slice_free (DndHelper, helper);
1935 xfer_cb (ModestMailOperation *mail_op,
1941 helper = (DndHelper *) user_data;
1943 if (modest_mail_operation_get_status (mail_op) ==
1944 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1950 /* Notify the drag source. Never call delete, the monitor will
1951 do the job if needed */
1952 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1954 /* Free the helper */
1955 dnd_helper_destroyer (helper);
1959 xfer_msgs_cb (ModestMailOperation *mail_op,
1963 xfer_cb (mail_op, user_data);
1967 xfer_folder_cb (ModestMailOperation *mail_op,
1968 TnyFolder *new_folder,
1972 GtkWidget *folder_view;
1974 helper = (DndHelper *) user_data;
1975 folder_view = g_object_ref (helper->folder_view);
1978 xfer_cb (mail_op, user_data);
1980 /* Select the folder */
1982 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1984 g_object_unref (folder_view);
1988 /* get the folder for the row the treepath refers to. */
1989 /* folder must be unref'd */
1990 static TnyFolderStore *
1991 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1994 TnyFolderStore *folder = NULL;
1996 if (gtk_tree_model_get_iter (model,&iter, path))
1997 gtk_tree_model_get (model, &iter,
1998 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2004 * This function is used by drag_data_received_cb to manage drag and
2005 * drop of a header, i.e, and drag from the header view to the folder
2009 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2010 GtkTreeModel *dest_model,
2011 GtkTreePath *dest_row,
2012 GtkSelectionData *selection_data,
2015 TnyList *headers = NULL;
2016 TnyFolder *folder = NULL;
2017 TnyFolderType folder_type;
2018 ModestMailOperation *mail_op = NULL;
2019 GtkTreeIter source_iter, dest_iter;
2020 ModestWindowMgr *mgr = NULL;
2021 ModestWindow *main_win = NULL;
2022 gchar **uris, **tmp;
2025 /* Build the list of headers */
2026 mgr = modest_runtime_get_window_mgr ();
2027 headers = tny_simple_list_new ();
2028 uris = modest_dnd_selection_data_get_paths (selection_data);
2031 while (*tmp != NULL) {
2036 path = gtk_tree_path_new_from_string (*tmp);
2037 gtk_tree_model_get_iter (source_model, &source_iter, path);
2038 gtk_tree_model_get (source_model, &source_iter,
2039 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2042 /* Do not enable d&d of headers already opened */
2043 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2044 tny_list_append (headers, G_OBJECT (header));
2046 /* Free and go on */
2047 gtk_tree_path_free (path);
2048 g_object_unref (header);
2053 /* Get the target folder */
2054 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2055 gtk_tree_model_get (dest_model, &dest_iter,
2056 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2059 if (!folder || !TNY_IS_FOLDER(folder)) {
2060 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2064 folder_type = modest_tny_folder_guess_folder_type (folder);
2065 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2066 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2067 goto cleanup; /* cannot move messages there */
2070 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2071 /* g_warning ("folder not writable"); */
2072 goto cleanup; /* verboten! */
2075 /* Ask for confirmation to move */
2076 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2078 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2082 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2084 if (response == GTK_RESPONSE_CANCEL)
2087 /* Transfer messages */
2088 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2089 modest_ui_actions_move_folder_error_handler,
2092 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2095 modest_mail_operation_xfer_msgs (mail_op,
2098 helper->delete_source,
2099 xfer_msgs_cb, helper);
2103 if (G_IS_OBJECT(mail_op))
2104 g_object_unref (G_OBJECT (mail_op));
2105 if (G_IS_OBJECT(folder))
2106 g_object_unref (G_OBJECT (folder));
2107 if (G_IS_OBJECT(headers))
2108 g_object_unref (headers);
2112 TnyFolderStore *src_folder;
2113 TnyFolderStore *dst_folder;
2114 ModestFolderView *folder_view;
2119 dnd_folder_info_destroyer (DndFolderInfo *info)
2121 if (info->src_folder)
2122 g_object_unref (info->src_folder);
2123 if (info->dst_folder)
2124 g_object_unref (info->dst_folder);
2125 if (info->folder_view)
2126 g_object_unref (info->folder_view);
2127 g_slice_free (DndFolderInfo, info);
2131 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2132 GtkWindow *parent_window,
2133 TnyAccount *account)
2135 time_t dnd_time = info->helper->time;
2136 GdkDragContext *context = info->helper->context;
2139 modest_ui_actions_on_account_connection_error (parent_window, account);
2141 /* Free the helper & info */
2142 dnd_helper_destroyer (info->helper);
2143 dnd_folder_info_destroyer (info);
2145 /* Notify the drag source. Never call delete, the monitor will
2146 do the job if needed */
2147 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2152 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2154 GtkWindow *parent_window,
2155 TnyAccount *account,
2158 DndFolderInfo *info = NULL;
2159 ModestMailOperation *mail_op;
2161 info = (DndFolderInfo *) user_data;
2163 if (err || canceled) {
2164 dnd_on_connection_failed_destroyer (info, parent_window, account);
2168 /* Do the mail operation */
2169 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2170 modest_ui_actions_move_folder_error_handler,
2171 info->src_folder, NULL);
2173 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2176 /* Transfer the folder */
2177 modest_mail_operation_xfer_folder (mail_op,
2178 TNY_FOLDER (info->src_folder),
2180 info->helper->delete_source,
2184 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2185 /* TNY_FOLDER (info->dst_folder), TRUE); */
2187 g_object_unref (G_OBJECT (mail_op));
2192 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2194 GtkWindow *parent_window,
2195 TnyAccount *account,
2198 DndFolderInfo *info = NULL;
2200 info = (DndFolderInfo *) user_data;
2202 if (err || canceled) {
2203 dnd_on_connection_failed_destroyer (info, parent_window, account);
2207 /* Connect to source folder and perform the copy/move */
2208 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2210 drag_and_drop_from_folder_view_src_folder_performer,
2215 * This function is used by drag_data_received_cb to manage drag and
2216 * drop of a folder, i.e, and drag from the folder view to the same
2220 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2221 GtkTreeModel *dest_model,
2222 GtkTreePath *dest_row,
2223 GtkSelectionData *selection_data,
2226 GtkTreeIter dest_iter, iter;
2227 TnyFolderStore *dest_folder = NULL;
2228 TnyFolderStore *folder = NULL;
2229 gboolean forbidden = FALSE;
2231 DndFolderInfo *info = NULL;
2233 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2235 g_warning ("%s: BUG: no main window", __FUNCTION__);
2240 /* check the folder rules for the destination */
2241 folder = tree_path_to_folder (dest_model, dest_row);
2242 if (TNY_IS_FOLDER(folder)) {
2243 ModestTnyFolderRules rules =
2244 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2245 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2246 } else if (TNY_IS_FOLDER_STORE(folder)) {
2247 /* enable local root as destination for folders */
2248 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2249 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2252 g_object_unref (folder);
2255 /* check the folder rules for the source */
2256 folder = tree_path_to_folder (source_model, helper->source_row);
2257 if (TNY_IS_FOLDER(folder)) {
2258 ModestTnyFolderRules rules =
2259 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2260 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2263 g_object_unref (folder);
2267 /* Check if the drag is possible */
2268 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2269 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2270 gtk_tree_path_free (helper->source_row);
2271 g_slice_free (DndHelper, helper);
2276 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2277 gtk_tree_model_get (dest_model, &dest_iter,
2278 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2280 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2281 gtk_tree_model_get (source_model, &iter,
2282 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2285 /* Create the info for the performer */
2286 info = g_slice_new (DndFolderInfo);
2287 info->src_folder = g_object_ref (folder);
2288 info->dst_folder = g_object_ref (dest_folder);
2289 info->folder_view = g_object_ref (helper->folder_view);
2290 info->helper = helper;
2292 /* Connect to the destination folder and perform the copy/move */
2293 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2295 drag_and_drop_from_folder_view_dst_folder_performer,
2299 g_object_unref (dest_folder);
2300 g_object_unref (folder);
2304 * This function receives the data set by the "drag-data-get" signal
2305 * handler. This information comes within the #GtkSelectionData. This
2306 * function will manage both the drags of folders of the treeview and
2307 * drags of headers of the header view widget.
2310 on_drag_data_received (GtkWidget *widget,
2311 GdkDragContext *context,
2314 GtkSelectionData *selection_data,
2319 GtkWidget *source_widget;
2320 GtkTreeModel *dest_model, *source_model;
2321 GtkTreePath *source_row, *dest_row;
2322 GtkTreeViewDropPosition pos;
2323 gboolean success = FALSE, delete_source = FALSE;
2324 DndHelper *helper = NULL;
2326 /* Do not allow further process */
2327 g_signal_stop_emission_by_name (widget, "drag-data-received");
2328 source_widget = gtk_drag_get_source_widget (context);
2330 /* Get the action */
2331 if (context->action == GDK_ACTION_MOVE) {
2332 delete_source = TRUE;
2334 /* Notify that there is no folder selected. We need to
2335 do this in order to update the headers view (and
2336 its monitors, because when moving, the old folder
2337 won't longer exist. We can not wait for the end of
2338 the operation, because the operation won't start if
2339 the folder is in use */
2340 if (source_widget == widget) {
2341 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2342 gtk_tree_selection_unselect_all (sel);
2346 /* Check if the get_data failed */
2347 if (selection_data == NULL || selection_data->length < 0)
2348 gtk_drag_finish (context, success, FALSE, time);
2350 /* Select the destination model */
2351 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2353 /* Get the path to the destination row. Can not call
2354 gtk_tree_view_get_drag_dest_row() because the source row
2355 is not selected anymore */
2356 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2359 /* Only allow drops IN other rows */
2361 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2362 pos == GTK_TREE_VIEW_DROP_AFTER)
2363 gtk_drag_finish (context, success, FALSE, time);
2365 /* Create the helper */
2366 helper = g_slice_new0 (DndHelper);
2367 helper->delete_source = delete_source;
2368 helper->context = context;
2369 helper->time = time;
2370 helper->folder_view = g_object_ref (widget);
2372 /* Drags from the header view */
2373 if (source_widget != widget) {
2374 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2376 drag_and_drop_from_header_view (source_model,
2382 /* Get the source model and row */
2383 gtk_tree_get_row_drag_data (selection_data,
2386 helper->source_row = gtk_tree_path_copy (source_row);
2388 drag_and_drop_from_folder_view (source_model,
2394 gtk_tree_path_free (source_row);
2398 gtk_tree_path_free (dest_row);
2402 * We define a "drag-drop" signal handler because we do not want to
2403 * use the default one, because the default one always calls
2404 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2405 * signal handler, because there we have all the information available
2406 * to know if the dnd was a success or not.
2409 drag_drop_cb (GtkWidget *widget,
2410 GdkDragContext *context,
2418 if (!context->targets)
2421 /* Check if we're dragging a folder row */
2422 target = gtk_drag_dest_find_target (widget, context, NULL);
2424 /* Request the data from the source. */
2425 gtk_drag_get_data(widget, context, target, time);
2431 * This function expands a node of a tree view if it's not expanded
2432 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2433 * does that, so that's why they're here.
2436 expand_row_timeout (gpointer data)
2438 GtkTreeView *tree_view = data;
2439 GtkTreePath *dest_path = NULL;
2440 GtkTreeViewDropPosition pos;
2441 gboolean result = FALSE;
2443 gdk_threads_enter ();
2445 gtk_tree_view_get_drag_dest_row (tree_view,
2450 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2451 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2452 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2453 gtk_tree_path_free (dest_path);
2457 gtk_tree_path_free (dest_path);
2462 gdk_threads_leave ();
2468 * This function is called whenever the pointer is moved over a widget
2469 * while dragging some data. It installs a timeout that will expand a
2470 * node of the treeview if not expanded yet. This function also calls
2471 * gdk_drag_status in order to set the suggested action that will be
2472 * used by the "drag-data-received" signal handler to know if we
2473 * should do a move or just a copy of the data.
2476 on_drag_motion (GtkWidget *widget,
2477 GdkDragContext *context,
2483 GtkTreeViewDropPosition pos;
2484 GtkTreePath *dest_row;
2485 GtkTreeModel *dest_model;
2486 ModestFolderViewPrivate *priv;
2487 GdkDragAction suggested_action;
2488 gboolean valid_location = FALSE;
2489 TnyFolderStore *folder = NULL;
2491 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2493 if (priv->timer_expander != 0) {
2494 g_source_remove (priv->timer_expander);
2495 priv->timer_expander = 0;
2498 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2503 /* Do not allow drops between folders */
2505 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2506 pos == GTK_TREE_VIEW_DROP_AFTER) {
2507 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2508 gdk_drag_status(context, 0, time);
2509 valid_location = FALSE;
2512 valid_location = TRUE;
2515 /* Check that the destination folder is writable */
2516 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2517 folder = tree_path_to_folder (dest_model, dest_row);
2518 if (folder && TNY_IS_FOLDER (folder)) {
2519 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2521 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2522 valid_location = FALSE;
2527 /* Expand the selected row after 1/2 second */
2528 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2529 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2531 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2533 /* Select the desired action. By default we pick MOVE */
2534 suggested_action = GDK_ACTION_MOVE;
2536 if (context->actions == GDK_ACTION_COPY)
2537 gdk_drag_status(context, GDK_ACTION_COPY, time);
2538 else if (context->actions == GDK_ACTION_MOVE)
2539 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2540 else if (context->actions & suggested_action)
2541 gdk_drag_status(context, suggested_action, time);
2543 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2547 g_object_unref (folder);
2549 gtk_tree_path_free (dest_row);
2551 g_signal_stop_emission_by_name (widget, "drag-motion");
2553 return valid_location;
2557 * This function sets the treeview as a source and a target for dnd
2558 * events. It also connects all the requirede signals.
2561 setup_drag_and_drop (GtkTreeView *self)
2563 /* Set up the folder view as a dnd destination. Set only the
2564 highlight flag, otherwise gtk will have a different
2566 gtk_drag_dest_set (GTK_WIDGET (self),
2567 GTK_DEST_DEFAULT_HIGHLIGHT,
2568 folder_view_drag_types,
2569 G_N_ELEMENTS (folder_view_drag_types),
2570 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2572 g_signal_connect (G_OBJECT (self),
2573 "drag_data_received",
2574 G_CALLBACK (on_drag_data_received),
2578 /* Set up the treeview as a dnd source */
2579 gtk_drag_source_set (GTK_WIDGET (self),
2581 folder_view_drag_types,
2582 G_N_ELEMENTS (folder_view_drag_types),
2583 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2585 g_signal_connect (G_OBJECT (self),
2587 G_CALLBACK (on_drag_motion),
2590 g_signal_connect (G_OBJECT (self),
2592 G_CALLBACK (on_drag_data_get),
2595 g_signal_connect (G_OBJECT (self),
2597 G_CALLBACK (drag_drop_cb),
2602 * This function manages the navigation through the folders using the
2603 * keyboard or the hardware keys in the device
2606 on_key_pressed (GtkWidget *self,
2610 GtkTreeSelection *selection;
2612 GtkTreeModel *model;
2613 gboolean retval = FALSE;
2615 /* Up and Down are automatically managed by the treeview */
2616 if (event->keyval == GDK_Return) {
2617 /* Expand/Collapse the selected row */
2618 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2619 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2622 path = gtk_tree_model_get_path (model, &iter);
2624 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2625 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2627 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2628 gtk_tree_path_free (path);
2630 /* No further processing */
2638 * We listen to the changes in the local folder account name key,
2639 * because we want to show the right name in the view. The local
2640 * folder account name corresponds to the device name in the Maemo
2641 * version. We do this because we do not want to query gconf on each
2642 * tree view refresh. It's better to cache it and change whenever
2646 on_configuration_key_changed (ModestConf* conf,
2648 ModestConfEvent event,
2649 ModestConfNotificationId id,
2650 ModestFolderView *self)
2652 ModestFolderViewPrivate *priv;
2655 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2656 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2658 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2659 g_free (priv->local_account_name);
2661 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2662 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2664 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2665 MODEST_CONF_DEVICE_NAME, NULL);
2667 /* Force a redraw */
2668 #if GTK_CHECK_VERSION(2, 8, 0)
2669 GtkTreeViewColumn * tree_column;
2671 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2672 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2673 gtk_tree_view_column_queue_resize (tree_column);
2675 gtk_widget_queue_draw (GTK_WIDGET (self));
2681 modest_folder_view_set_style (ModestFolderView *self,
2682 ModestFolderViewStyle style)
2684 ModestFolderViewPrivate *priv;
2686 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2687 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2688 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2690 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2693 priv->style = style;
2697 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2698 const gchar *account_id)
2700 ModestFolderViewPrivate *priv;
2701 GtkTreeModel *model;
2703 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2705 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2707 /* This will be used by the filter_row callback,
2708 * to decided which rows to show: */
2709 if (priv->visible_account_id) {
2710 g_free (priv->visible_account_id);
2711 priv->visible_account_id = NULL;
2714 priv->visible_account_id = g_strdup (account_id);
2717 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2718 if (GTK_IS_TREE_MODEL_FILTER (model))
2719 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2721 /* Save settings to gconf */
2722 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2723 MODEST_CONF_FOLDER_VIEW_KEY);
2727 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2729 ModestFolderViewPrivate *priv;
2731 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2733 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2735 return (const gchar *) priv->visible_account_id;
2739 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2743 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2745 gtk_tree_model_get (model, iter,
2746 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2749 gboolean result = FALSE;
2750 if (type == TNY_FOLDER_TYPE_INBOX) {
2754 *inbox_iter = *iter;
2758 if (gtk_tree_model_iter_children (model, &child, iter)) {
2759 if (find_inbox_iter (model, &child, inbox_iter))
2763 } while (gtk_tree_model_iter_next (model, iter));
2772 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2774 GtkTreeModel *model;
2775 GtkTreeIter iter, inbox_iter;
2776 GtkTreeSelection *sel;
2777 GtkTreePath *path = NULL;
2779 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2781 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2785 expand_root_items (self);
2786 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2788 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2789 g_warning ("%s: model is empty", __FUNCTION__);
2793 if (find_inbox_iter (model, &iter, &inbox_iter))
2794 path = gtk_tree_model_get_path (model, &inbox_iter);
2796 path = gtk_tree_path_new_first ();
2798 /* Select the row and free */
2799 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2800 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2801 gtk_tree_path_free (path);
2804 gtk_widget_grab_focus (GTK_WIDGET(self));
2810 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2815 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2816 TnyFolder* a_folder;
2819 gtk_tree_model_get (model, iter,
2820 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2821 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2822 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2826 if (folder == a_folder) {
2827 g_object_unref (a_folder);
2828 *folder_iter = *iter;
2831 g_object_unref (a_folder);
2833 if (gtk_tree_model_iter_children (model, &child, iter)) {
2834 if (find_folder_iter (model, &child, folder_iter, folder))
2838 } while (gtk_tree_model_iter_next (model, iter));
2845 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2848 ModestFolderView *self)
2850 ModestFolderViewPrivate *priv = NULL;
2851 GtkTreeSelection *sel;
2852 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2853 GObject *instance = NULL;
2855 if (!MODEST_IS_FOLDER_VIEW(self))
2858 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2860 priv->reexpand = TRUE;
2862 gtk_tree_model_get (tree_model, iter,
2863 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2864 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2866 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2867 priv->folder_to_select = g_object_ref (instance);
2869 g_object_unref (instance);
2872 if (priv->folder_to_select) {
2874 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2877 path = gtk_tree_model_get_path (tree_model, iter);
2878 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2880 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2882 gtk_tree_selection_select_iter (sel, iter);
2883 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2885 gtk_tree_path_free (path);
2890 modest_folder_view_disable_next_folder_selection (self);
2892 /* Refilter the model */
2893 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2899 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2901 ModestFolderViewPrivate *priv;
2903 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2905 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2907 if (priv->folder_to_select)
2908 g_object_unref(priv->folder_to_select);
2910 priv->folder_to_select = NULL;
2914 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2915 gboolean after_change)
2917 GtkTreeModel *model;
2918 GtkTreeIter iter, folder_iter;
2919 GtkTreeSelection *sel;
2920 ModestFolderViewPrivate *priv = NULL;
2922 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2923 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2925 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2928 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2929 gtk_tree_selection_unselect_all (sel);
2931 if (priv->folder_to_select)
2932 g_object_unref(priv->folder_to_select);
2933 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2937 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2942 /* Refilter the model, before selecting the folder */
2943 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2945 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2946 g_warning ("%s: model is empty", __FUNCTION__);
2950 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2953 path = gtk_tree_model_get_path (model, &folder_iter);
2954 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2956 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2957 gtk_tree_selection_select_iter (sel, &folder_iter);
2958 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2960 gtk_tree_path_free (path);
2968 modest_folder_view_copy_selection (ModestFolderView *self)
2970 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2972 /* Copy selection */
2973 _clipboard_set_selected_data (self, FALSE);
2977 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2979 ModestFolderViewPrivate *priv = NULL;
2980 GtkTreeModel *model = NULL;
2981 const gchar **hidding = NULL;
2982 guint i, n_selected;
2984 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2985 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2987 /* Copy selection */
2988 if (!_clipboard_set_selected_data (folder_view, TRUE))
2991 /* Get hidding ids */
2992 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2994 /* Clear hidding array created by previous cut operation */
2995 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2997 /* Copy hidding array */
2998 priv->n_selected = n_selected;
2999 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3000 for (i=0; i < n_selected; i++)
3001 priv->hidding_ids[i] = g_strdup(hidding[i]);
3003 /* Hide cut folders */
3004 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3005 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3009 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3010 ModestFolderView *folder_view_dst)
3012 GtkTreeModel *filter_model = NULL;
3013 GtkTreeModel *model = NULL;
3014 GtkTreeModel *new_filter_model = NULL;
3016 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3017 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3020 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3021 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3023 /* Build new filter model */
3024 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3025 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3029 /* Set copied model */
3030 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3031 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3032 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3035 g_object_unref (new_filter_model);
3039 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3042 GtkTreeModel *model = NULL;
3043 ModestFolderViewPrivate* priv;
3045 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3047 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3048 priv->show_non_move = show;
3049 /* modest_folder_view_update_model(folder_view, */
3050 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3052 /* Hide special folders */
3053 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3054 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3055 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3059 /* Returns FALSE if it did not selected anything */
3061 _clipboard_set_selected_data (ModestFolderView *folder_view,
3064 ModestFolderViewPrivate *priv = NULL;
3065 TnyFolderStore *folder = NULL;
3066 gboolean retval = FALSE;
3068 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3069 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3071 /* Set selected data on clipboard */
3072 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3073 folder = modest_folder_view_get_selected (folder_view);
3075 /* Do not allow to select an account */
3076 if (TNY_IS_FOLDER (folder)) {
3077 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3082 g_object_unref (folder);
3088 _clear_hidding_filter (ModestFolderView *folder_view)
3090 ModestFolderViewPrivate *priv;
3093 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3094 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3096 if (priv->hidding_ids != NULL) {
3097 for (i=0; i < priv->n_selected; i++)
3098 g_free (priv->hidding_ids[i]);
3099 g_free(priv->hidding_ids);
3105 on_display_name_changed (ModestAccountMgr *mgr,
3106 const gchar *account,
3109 ModestFolderView *self;
3111 self = MODEST_FOLDER_VIEW (user_data);
3113 /* Force a redraw */
3114 #if GTK_CHECK_VERSION(2, 8, 0)
3115 GtkTreeViewColumn * tree_column;
3117 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3118 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3119 gtk_tree_view_column_queue_resize (tree_column);
3121 gtk_widget_queue_draw (GTK_WIDGET (self));