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);
1146 g_object_unref (priv->cur_folder_store);
1147 priv->cur_folder_store = NULL;
1150 /* Clear hidding array created by cut operation */
1151 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1153 G_OBJECT_CLASS(parent_class)->finalize (obj);
1158 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1160 ModestFolderViewPrivate *priv;
1163 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1164 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1166 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1167 device = tny_account_store_get_device (account_store);
1169 if (G_UNLIKELY (priv->account_store)) {
1171 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1172 priv->account_inserted_signal))
1173 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1174 priv->account_inserted_signal);
1175 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1176 priv->account_removed_signal))
1177 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1178 priv->account_removed_signal);
1179 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1180 priv->account_changed_signal))
1181 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1182 priv->account_changed_signal);
1183 g_object_unref (G_OBJECT (priv->account_store));
1186 priv->account_store = g_object_ref (G_OBJECT (account_store));
1188 priv->account_removed_signal =
1189 g_signal_connect (G_OBJECT(account_store), "account_removed",
1190 G_CALLBACK (on_account_removed), self);
1192 priv->account_inserted_signal =
1193 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1194 G_CALLBACK (on_account_inserted), self);
1196 priv->account_changed_signal =
1197 g_signal_connect (G_OBJECT(account_store), "account_changed",
1198 G_CALLBACK (on_account_changed), self);
1200 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1202 g_object_unref (G_OBJECT (device));
1206 on_account_inserted (TnyAccountStore *account_store,
1207 TnyAccount *account,
1210 ModestFolderViewPrivate *priv;
1211 GtkTreeModel *sort_model, *filter_model;
1213 /* Ignore transport account insertions, we're not showing them
1214 in the folder view */
1215 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1218 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1220 /* If we're adding a new account, and there is no previous
1221 one, we need to select the visible server account */
1222 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1223 !priv->visible_account_id)
1224 modest_widget_memory_restore (modest_runtime_get_conf(),
1225 G_OBJECT (user_data),
1226 MODEST_CONF_FOLDER_VIEW_KEY);
1228 /* Get the inner model */
1229 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1230 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1232 /* Insert the account in the model */
1233 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1234 G_OBJECT (account));
1236 /* Refilter the model */
1237 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1242 on_account_changed (TnyAccountStore *account_store,
1243 TnyAccount *tny_account,
1246 ModestFolderViewPrivate *priv;
1247 GtkTreeModel *sort_model, *filter_model;
1249 /* Ignore transport account insertions, we're not showing them
1250 in the folder view */
1251 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1254 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1256 /* Get the inner model */
1257 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1258 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1260 /* Remove the account from the model */
1261 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1262 G_OBJECT (tny_account));
1264 /* Insert the account in the model */
1265 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1266 G_OBJECT (tny_account));
1268 /* Refilter the model */
1269 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1274 * Selects the first inbox or the local account in an idle
1277 on_idle_select_first_inbox_or_local (gpointer user_data)
1279 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1281 modest_folder_view_select_first_inbox_or_local (self);
1288 on_account_removed (TnyAccountStore *account_store,
1289 TnyAccount *account,
1292 ModestFolderView *self = NULL;
1293 ModestFolderViewPrivate *priv;
1294 GtkTreeModel *sort_model, *filter_model;
1295 GtkTreeSelection *sel = NULL;
1296 gboolean same_account_selected = FALSE;
1298 /* Ignore transport account removals, we're not showing them
1299 in the folder view */
1300 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1303 self = MODEST_FOLDER_VIEW (user_data);
1304 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1306 /* Invalidate the cur_folder_store only if the selected folder
1307 belongs to the account that is being removed */
1308 if (priv->cur_folder_store) {
1309 TnyAccount *selected_folder_account = NULL;
1311 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1312 selected_folder_account =
1313 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1315 selected_folder_account =
1316 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1319 if (selected_folder_account == account) {
1320 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1321 gtk_tree_selection_unselect_all (sel);
1322 same_account_selected = TRUE;
1324 g_object_unref (selected_folder_account);
1327 /* Invalidate row to select only if the folder to select
1328 belongs to the account that is being removed*/
1329 if (priv->folder_to_select) {
1330 TnyAccount *folder_to_select_account = NULL;
1332 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1333 if (folder_to_select_account == account) {
1334 modest_folder_view_disable_next_folder_selection (self);
1335 g_object_unref (priv->folder_to_select);
1336 priv->folder_to_select = NULL;
1338 g_object_unref (folder_to_select_account);
1341 /* Remove the account from the model */
1342 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1343 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1344 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1345 G_OBJECT (account));
1347 /* If the removed account is the currently viewed one then
1348 clear the configuration value. The new visible account will be the default account */
1349 if (priv->visible_account_id &&
1350 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1352 /* Clear the current visible account_id */
1353 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1355 /* Call the restore method, this will set the new visible account */
1356 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1357 MODEST_CONF_FOLDER_VIEW_KEY);
1360 /* Refilter the model */
1361 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1363 /* Select the first INBOX if the currently selected folder
1364 belongs to the account that is being deleted */
1365 if (same_account_selected)
1366 g_idle_add (on_idle_select_first_inbox_or_local, self);
1370 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1372 GtkTreeViewColumn *col;
1374 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1376 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1378 g_printerr ("modest: failed get column for title\n");
1382 gtk_tree_view_column_set_title (col, title);
1383 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1388 modest_folder_view_on_map (ModestFolderView *self,
1389 GdkEventExpose *event,
1392 ModestFolderViewPrivate *priv;
1394 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1396 /* This won't happen often */
1397 if (G_UNLIKELY (priv->reselect)) {
1398 /* Select the first inbox or the local account if not found */
1400 /* TODO: this could cause a lock at startup, so we
1401 comment it for the moment. We know that this will
1402 be a bug, because the INBOX is not selected, but we
1403 need to rewrite some parts of Modest to avoid the
1404 deathlock situation */
1405 /* TODO: check if this is still the case */
1406 priv->reselect = FALSE;
1407 modest_folder_view_select_first_inbox_or_local (self);
1408 /* Notify the display name observers */
1409 g_signal_emit (G_OBJECT(self),
1410 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1414 if (priv->reexpand) {
1415 expand_root_items (self);
1416 priv->reexpand = FALSE;
1423 modest_folder_view_new (TnyFolderStoreQuery *query)
1426 ModestFolderViewPrivate *priv;
1427 GtkTreeSelection *sel;
1429 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1430 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1433 priv->query = g_object_ref (query);
1435 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1436 priv->changed_signal = g_signal_connect (sel, "changed",
1437 G_CALLBACK (on_selection_changed), self);
1439 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1441 return GTK_WIDGET(self);
1444 /* this feels dirty; any other way to expand all the root items? */
1446 expand_root_items (ModestFolderView *self)
1449 GtkTreeModel *model;
1452 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1453 path = gtk_tree_path_new_first ();
1455 /* all folders should have child items, so.. */
1457 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1458 gtk_tree_path_next (path);
1459 } while (gtk_tree_model_get_iter (model, &iter, path));
1461 gtk_tree_path_free (path);
1465 * We use this function to implement the
1466 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1467 * account in this case, and the local folders.
1470 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1472 ModestFolderViewPrivate *priv;
1473 gboolean retval = TRUE;
1474 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1475 GObject *instance = NULL;
1476 const gchar *id = NULL;
1478 gboolean found = FALSE;
1479 gboolean cleared = FALSE;
1481 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1484 gtk_tree_model_get (model, iter,
1485 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1486 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1489 /* Do not show if there is no instance, this could indeed
1490 happen when the model is being modified while it's being
1491 drawn. This could occur for example when moving folders
1496 if (type == TNY_FOLDER_TYPE_ROOT) {
1497 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1498 account instead of a folder. */
1499 if (TNY_IS_ACCOUNT (instance)) {
1500 TnyAccount *acc = TNY_ACCOUNT (instance);
1501 const gchar *account_id = tny_account_get_id (acc);
1503 /* If it isn't a special folder,
1504 * don't show it unless it is the visible account: */
1505 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1506 !modest_tny_account_is_virtual_local_folders (acc) &&
1507 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1509 /* Show only the visible account id */
1510 if (priv->visible_account_id) {
1511 if (strcmp (account_id, priv->visible_account_id))
1518 /* Never show these to the user. They are merged into one folder
1519 * in the local-folders account instead: */
1520 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1525 /* Check hiding (if necessary) */
1526 cleared = modest_email_clipboard_cleared (priv->clipboard);
1527 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1528 id = tny_folder_get_id (TNY_FOLDER(instance));
1529 if (priv->hidding_ids != NULL)
1530 for (i=0; i < priv->n_selected && !found; i++)
1531 if (priv->hidding_ids[i] != NULL && id != NULL)
1532 found = (!strcmp (priv->hidding_ids[i], id));
1538 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1539 folder as no message can be move there according to UI specs */
1540 if (!priv->show_non_move) {
1542 case TNY_FOLDER_TYPE_OUTBOX:
1543 case TNY_FOLDER_TYPE_SENT:
1544 case TNY_FOLDER_TYPE_DRAFTS:
1547 case TNY_FOLDER_TYPE_UNKNOWN:
1548 case TNY_FOLDER_TYPE_NORMAL:
1549 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1550 if (type == TNY_FOLDER_TYPE_INVALID)
1551 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1553 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1554 type == TNY_FOLDER_TYPE_SENT
1555 || type == TNY_FOLDER_TYPE_DRAFTS)
1564 g_object_unref (instance);
1571 modest_folder_view_update_model (ModestFolderView *self,
1572 TnyAccountStore *account_store)
1574 ModestFolderViewPrivate *priv;
1575 GtkTreeModel *model /* , *old_model */;
1576 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1578 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1579 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1582 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1584 /* Notify that there is no folder selected */
1585 g_signal_emit (G_OBJECT(self),
1586 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1588 if (priv->cur_folder_store) {
1589 g_object_unref (priv->cur_folder_store);
1590 priv->cur_folder_store = NULL;
1593 /* FIXME: the local accounts are not shown when the query
1594 selects only the subscribed folders */
1595 model = tny_gtk_folder_store_tree_model_new (NULL);
1597 /* Get the accounts: */
1598 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1600 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1602 sortable = gtk_tree_model_sort_new_with_model (model);
1603 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1604 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1605 GTK_SORT_ASCENDING);
1606 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1607 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1608 cmp_rows, NULL, NULL);
1610 /* Create filter model */
1611 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1612 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1618 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1619 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1620 (GCallback) on_row_inserted_maybe_select_folder, self);
1623 g_object_unref (model);
1624 g_object_unref (filter_model);
1625 g_object_unref (sortable);
1627 /* Force a reselection of the INBOX next time the widget is shown */
1628 priv->reselect = TRUE;
1635 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1637 GtkTreeModel *model = NULL;
1638 TnyFolderStore *folder = NULL;
1640 ModestFolderView *tree_view = NULL;
1641 ModestFolderViewPrivate *priv = NULL;
1642 gboolean selected = FALSE;
1644 g_return_if_fail (sel);
1645 g_return_if_fail (user_data);
1647 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1649 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1651 tree_view = MODEST_FOLDER_VIEW (user_data);
1654 gtk_tree_model_get (model, &iter,
1655 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1658 /* If the folder is the same do not notify */
1659 if (folder && priv->cur_folder_store == folder) {
1660 g_object_unref (folder);
1665 /* Current folder was unselected */
1666 if (priv->cur_folder_store) {
1667 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1668 priv->cur_folder_store, FALSE);
1670 if (TNY_IS_FOLDER(priv->cur_folder_store))
1671 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1672 FALSE, NULL, NULL, NULL);
1674 /* FALSE --> don't expunge the messages */
1676 g_object_unref (priv->cur_folder_store);
1677 priv->cur_folder_store = NULL;
1680 /* New current references */
1681 priv->cur_folder_store = folder;
1683 /* New folder has been selected. Do not notify if there is
1684 nothing new selected */
1686 g_signal_emit (G_OBJECT(tree_view),
1687 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1688 0, priv->cur_folder_store, TRUE);
1693 modest_folder_view_get_selected (ModestFolderView *self)
1695 ModestFolderViewPrivate *priv;
1697 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1699 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1700 if (priv->cur_folder_store)
1701 g_object_ref (priv->cur_folder_store);
1703 return priv->cur_folder_store;
1707 get_cmp_rows_type_pos (GObject *folder)
1709 /* Remote accounts -> Local account -> MMC account .*/
1712 if (TNY_IS_ACCOUNT (folder) &&
1713 modest_tny_account_is_virtual_local_folders (
1714 TNY_ACCOUNT (folder))) {
1716 } else if (TNY_IS_ACCOUNT (folder)) {
1717 TnyAccount *account = TNY_ACCOUNT (folder);
1718 const gchar *account_id = tny_account_get_id (account);
1719 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1725 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1726 return -1; /* Should never happen */
1731 get_cmp_subfolder_type_pos (TnyFolderType t)
1733 /* Inbox, Outbox, Drafts, Sent, User */
1737 case TNY_FOLDER_TYPE_INBOX:
1740 case TNY_FOLDER_TYPE_OUTBOX:
1743 case TNY_FOLDER_TYPE_DRAFTS:
1746 case TNY_FOLDER_TYPE_SENT:
1755 * This function orders the mail accounts according to these rules:
1756 * 1st - remote accounts
1757 * 2nd - local account
1761 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1765 gchar *name1 = NULL;
1766 gchar *name2 = NULL;
1767 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1768 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1769 GObject *folder1 = NULL;
1770 GObject *folder2 = NULL;
1772 gtk_tree_model_get (tree_model, iter1,
1773 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1774 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1775 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1777 gtk_tree_model_get (tree_model, iter2,
1778 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1779 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1780 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1783 /* Return if we get no folder. This could happen when folder
1784 operations are happening. The model is updated after the
1785 folder copy/move actually occurs, so there could be
1786 situations where the model to be drawn is not correct */
1787 if (!folder1 || !folder2)
1790 if (type == TNY_FOLDER_TYPE_ROOT) {
1791 /* Compare the types, so that
1792 * Remote accounts -> Local account -> MMC account .*/
1793 const gint pos1 = get_cmp_rows_type_pos (folder1);
1794 const gint pos2 = get_cmp_rows_type_pos (folder2);
1795 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1796 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1799 else if (pos1 > pos2)
1802 /* Compare items of the same type: */
1804 TnyAccount *account1 = NULL;
1805 if (TNY_IS_ACCOUNT (folder1))
1806 account1 = TNY_ACCOUNT (folder1);
1808 TnyAccount *account2 = NULL;
1809 if (TNY_IS_ACCOUNT (folder2))
1810 account2 = TNY_ACCOUNT (folder2);
1812 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1813 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1815 if (!account_id && !account_id2) {
1817 } else if (!account_id) {
1819 } else if (!account_id2) {
1821 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1824 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1828 gint cmp1 = 0, cmp2 = 0;
1829 /* get the parent to know if it's a local folder */
1832 gboolean has_parent;
1833 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1835 GObject *parent_folder;
1836 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1837 gtk_tree_model_get (tree_model, &parent,
1838 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1839 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1841 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1842 TNY_IS_ACCOUNT (parent_folder)) {
1843 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1844 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1845 (TNY_FOLDER (folder1)));
1846 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1847 (TNY_FOLDER (folder2)));
1848 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1849 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1852 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1858 g_object_unref (parent_folder);
1861 /* if they are not local folders */
1863 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1864 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1868 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1870 cmp = (cmp1 - cmp2);
1875 g_object_unref(G_OBJECT(folder1));
1877 g_object_unref(G_OBJECT(folder2));
1885 /*****************************************************************************/
1886 /* DRAG and DROP stuff */
1887 /*****************************************************************************/
1889 * This function fills the #GtkSelectionData with the row and the
1890 * model that has been dragged. It's called when this widget is a
1891 * source for dnd after the event drop happened
1894 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1895 guint info, guint time, gpointer data)
1897 GtkTreeSelection *selection;
1898 GtkTreeModel *model;
1900 GtkTreePath *source_row;
1902 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1903 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1905 source_row = gtk_tree_model_get_path (model, &iter);
1906 gtk_tree_set_row_drag_data (selection_data,
1910 gtk_tree_path_free (source_row);
1914 typedef struct _DndHelper {
1915 ModestFolderView *folder_view;
1916 gboolean delete_source;
1917 GtkTreePath *source_row;
1918 GdkDragContext *context;
1923 dnd_helper_destroyer (DndHelper *helper)
1925 /* Free the helper */
1926 g_object_unref (helper->folder_view);
1927 gtk_tree_path_free (helper->source_row);
1928 g_slice_free (DndHelper, helper);
1932 xfer_cb (ModestMailOperation *mail_op,
1938 helper = (DndHelper *) user_data;
1940 if (modest_mail_operation_get_status (mail_op) ==
1941 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1947 /* Notify the drag source. Never call delete, the monitor will
1948 do the job if needed */
1949 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1951 /* Free the helper */
1952 dnd_helper_destroyer (helper);
1956 xfer_msgs_cb (ModestMailOperation *mail_op,
1960 xfer_cb (mail_op, user_data);
1964 xfer_folder_cb (ModestMailOperation *mail_op,
1965 TnyFolder *new_folder,
1969 GtkWidget *folder_view;
1971 helper = (DndHelper *) user_data;
1972 folder_view = g_object_ref (helper->folder_view);
1975 xfer_cb (mail_op, user_data);
1977 /* Select the folder */
1979 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1981 g_object_unref (folder_view);
1985 /* get the folder for the row the treepath refers to. */
1986 /* folder must be unref'd */
1987 static TnyFolderStore *
1988 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1991 TnyFolderStore *folder = NULL;
1993 if (gtk_tree_model_get_iter (model,&iter, path))
1994 gtk_tree_model_get (model, &iter,
1995 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2001 * This function is used by drag_data_received_cb to manage drag and
2002 * drop of a header, i.e, and drag from the header view to the folder
2006 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2007 GtkTreeModel *dest_model,
2008 GtkTreePath *dest_row,
2009 GtkSelectionData *selection_data,
2012 TnyList *headers = NULL;
2013 TnyFolder *folder = NULL;
2014 TnyFolderType folder_type;
2015 ModestMailOperation *mail_op = NULL;
2016 GtkTreeIter source_iter, dest_iter;
2017 ModestWindowMgr *mgr = NULL;
2018 ModestWindow *main_win = NULL;
2019 gchar **uris, **tmp;
2022 /* Build the list of headers */
2023 mgr = modest_runtime_get_window_mgr ();
2024 headers = tny_simple_list_new ();
2025 uris = modest_dnd_selection_data_get_paths (selection_data);
2028 while (*tmp != NULL) {
2033 path = gtk_tree_path_new_from_string (*tmp);
2034 gtk_tree_model_get_iter (source_model, &source_iter, path);
2035 gtk_tree_model_get (source_model, &source_iter,
2036 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2039 /* Do not enable d&d of headers already opened */
2040 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2041 tny_list_append (headers, G_OBJECT (header));
2043 /* Free and go on */
2044 gtk_tree_path_free (path);
2045 g_object_unref (header);
2050 /* Get the target folder */
2051 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2052 gtk_tree_model_get (dest_model, &dest_iter,
2053 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2056 if (!folder || !TNY_IS_FOLDER(folder)) {
2057 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2061 folder_type = modest_tny_folder_guess_folder_type (folder);
2062 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2063 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2064 goto cleanup; /* cannot move messages there */
2067 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2068 /* g_warning ("folder not writable"); */
2069 goto cleanup; /* verboten! */
2072 /* Ask for confirmation to move */
2073 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2075 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2079 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2081 if (response == GTK_RESPONSE_CANCEL)
2084 /* Transfer messages */
2085 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2086 modest_ui_actions_move_folder_error_handler,
2089 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2092 modest_mail_operation_xfer_msgs (mail_op,
2095 helper->delete_source,
2096 xfer_msgs_cb, helper);
2100 if (G_IS_OBJECT(mail_op))
2101 g_object_unref (G_OBJECT (mail_op));
2102 if (G_IS_OBJECT(folder))
2103 g_object_unref (G_OBJECT (folder));
2104 if (G_IS_OBJECT(headers))
2105 g_object_unref (headers);
2109 TnyFolderStore *src_folder;
2110 TnyFolderStore *dst_folder;
2111 ModestFolderView *folder_view;
2116 dnd_folder_info_destroyer (DndFolderInfo *info)
2118 if (info->src_folder)
2119 g_object_unref (info->src_folder);
2120 if (info->dst_folder)
2121 g_object_unref (info->dst_folder);
2122 if (info->folder_view)
2123 g_object_unref (info->folder_view);
2124 g_slice_free (DndFolderInfo, info);
2128 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2129 GtkWindow *parent_window,
2130 TnyAccount *account)
2132 time_t dnd_time = info->helper->time;
2133 GdkDragContext *context = info->helper->context;
2136 modest_ui_actions_on_account_connection_error (parent_window, account);
2138 /* Free the helper & info */
2139 dnd_helper_destroyer (info->helper);
2140 dnd_folder_info_destroyer (info);
2142 /* Notify the drag source. Never call delete, the monitor will
2143 do the job if needed */
2144 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2149 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2151 GtkWindow *parent_window,
2152 TnyAccount *account,
2155 DndFolderInfo *info = NULL;
2156 ModestMailOperation *mail_op;
2158 info = (DndFolderInfo *) user_data;
2160 if (err || canceled) {
2161 dnd_on_connection_failed_destroyer (info, parent_window, account);
2165 /* Do the mail operation */
2166 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2167 modest_ui_actions_move_folder_error_handler,
2168 info->src_folder, NULL);
2170 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2173 /* Transfer the folder */
2174 modest_mail_operation_xfer_folder (mail_op,
2175 TNY_FOLDER (info->src_folder),
2177 info->helper->delete_source,
2181 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2182 /* TNY_FOLDER (info->dst_folder), TRUE); */
2184 g_object_unref (G_OBJECT (mail_op));
2189 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2191 GtkWindow *parent_window,
2192 TnyAccount *account,
2195 DndFolderInfo *info = NULL;
2197 info = (DndFolderInfo *) user_data;
2199 if (err || canceled) {
2200 dnd_on_connection_failed_destroyer (info, parent_window, account);
2204 /* Connect to source folder and perform the copy/move */
2205 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2207 drag_and_drop_from_folder_view_src_folder_performer,
2212 * This function is used by drag_data_received_cb to manage drag and
2213 * drop of a folder, i.e, and drag from the folder view to the same
2217 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2218 GtkTreeModel *dest_model,
2219 GtkTreePath *dest_row,
2220 GtkSelectionData *selection_data,
2223 GtkTreeIter dest_iter, iter;
2224 TnyFolderStore *dest_folder = NULL;
2225 TnyFolderStore *folder = NULL;
2226 gboolean forbidden = FALSE;
2228 DndFolderInfo *info = NULL;
2230 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2232 g_warning ("%s: BUG: no main window", __FUNCTION__);
2237 /* check the folder rules for the destination */
2238 folder = tree_path_to_folder (dest_model, dest_row);
2239 if (TNY_IS_FOLDER(folder)) {
2240 ModestTnyFolderRules rules =
2241 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2242 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2243 } else if (TNY_IS_FOLDER_STORE(folder)) {
2244 /* enable local root as destination for folders */
2245 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2246 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2249 g_object_unref (folder);
2252 /* check the folder rules for the source */
2253 folder = tree_path_to_folder (source_model, helper->source_row);
2254 if (TNY_IS_FOLDER(folder)) {
2255 ModestTnyFolderRules rules =
2256 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2257 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2260 g_object_unref (folder);
2264 /* Check if the drag is possible */
2265 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2266 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2267 gtk_tree_path_free (helper->source_row);
2268 g_slice_free (DndHelper, helper);
2273 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2274 gtk_tree_model_get (dest_model, &dest_iter,
2275 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2277 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2278 gtk_tree_model_get (source_model, &iter,
2279 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2282 /* Create the info for the performer */
2283 info = g_slice_new (DndFolderInfo);
2284 info->src_folder = g_object_ref (folder);
2285 info->dst_folder = g_object_ref (dest_folder);
2286 info->folder_view = g_object_ref (helper->folder_view);
2287 info->helper = helper;
2289 /* Connect to the destination folder and perform the copy/move */
2290 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2292 drag_and_drop_from_folder_view_dst_folder_performer,
2296 g_object_unref (dest_folder);
2297 g_object_unref (folder);
2301 * This function receives the data set by the "drag-data-get" signal
2302 * handler. This information comes within the #GtkSelectionData. This
2303 * function will manage both the drags of folders of the treeview and
2304 * drags of headers of the header view widget.
2307 on_drag_data_received (GtkWidget *widget,
2308 GdkDragContext *context,
2311 GtkSelectionData *selection_data,
2316 GtkWidget *source_widget;
2317 GtkTreeModel *dest_model, *source_model;
2318 GtkTreePath *source_row, *dest_row;
2319 GtkTreeViewDropPosition pos;
2320 gboolean success = FALSE, delete_source = FALSE;
2321 DndHelper *helper = NULL;
2323 /* Do not allow further process */
2324 g_signal_stop_emission_by_name (widget, "drag-data-received");
2325 source_widget = gtk_drag_get_source_widget (context);
2327 /* Get the action */
2328 if (context->action == GDK_ACTION_MOVE) {
2329 delete_source = TRUE;
2331 /* Notify that there is no folder selected. We need to
2332 do this in order to update the headers view (and
2333 its monitors, because when moving, the old folder
2334 won't longer exist. We can not wait for the end of
2335 the operation, because the operation won't start if
2336 the folder is in use */
2337 if (source_widget == widget) {
2338 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2339 gtk_tree_selection_unselect_all (sel);
2343 /* Check if the get_data failed */
2344 if (selection_data == NULL || selection_data->length < 0)
2345 gtk_drag_finish (context, success, FALSE, time);
2347 /* Select the destination model */
2348 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2350 /* Get the path to the destination row. Can not call
2351 gtk_tree_view_get_drag_dest_row() because the source row
2352 is not selected anymore */
2353 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2356 /* Only allow drops IN other rows */
2358 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2359 pos == GTK_TREE_VIEW_DROP_AFTER)
2360 gtk_drag_finish (context, success, FALSE, time);
2362 /* Create the helper */
2363 helper = g_slice_new0 (DndHelper);
2364 helper->delete_source = delete_source;
2365 helper->context = context;
2366 helper->time = time;
2367 helper->folder_view = g_object_ref (widget);
2369 /* Drags from the header view */
2370 if (source_widget != widget) {
2371 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2373 drag_and_drop_from_header_view (source_model,
2379 /* Get the source model and row */
2380 gtk_tree_get_row_drag_data (selection_data,
2383 helper->source_row = gtk_tree_path_copy (source_row);
2385 drag_and_drop_from_folder_view (source_model,
2391 gtk_tree_path_free (source_row);
2395 gtk_tree_path_free (dest_row);
2399 * We define a "drag-drop" signal handler because we do not want to
2400 * use the default one, because the default one always calls
2401 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2402 * signal handler, because there we have all the information available
2403 * to know if the dnd was a success or not.
2406 drag_drop_cb (GtkWidget *widget,
2407 GdkDragContext *context,
2415 if (!context->targets)
2418 /* Check if we're dragging a folder row */
2419 target = gtk_drag_dest_find_target (widget, context, NULL);
2421 /* Request the data from the source. */
2422 gtk_drag_get_data(widget, context, target, time);
2428 * This function expands a node of a tree view if it's not expanded
2429 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2430 * does that, so that's why they're here.
2433 expand_row_timeout (gpointer data)
2435 GtkTreeView *tree_view = data;
2436 GtkTreePath *dest_path = NULL;
2437 GtkTreeViewDropPosition pos;
2438 gboolean result = FALSE;
2440 gdk_threads_enter ();
2442 gtk_tree_view_get_drag_dest_row (tree_view,
2447 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2448 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2449 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2450 gtk_tree_path_free (dest_path);
2454 gtk_tree_path_free (dest_path);
2459 gdk_threads_leave ();
2465 * This function is called whenever the pointer is moved over a widget
2466 * while dragging some data. It installs a timeout that will expand a
2467 * node of the treeview if not expanded yet. This function also calls
2468 * gdk_drag_status in order to set the suggested action that will be
2469 * used by the "drag-data-received" signal handler to know if we
2470 * should do a move or just a copy of the data.
2473 on_drag_motion (GtkWidget *widget,
2474 GdkDragContext *context,
2480 GtkTreeViewDropPosition pos;
2481 GtkTreePath *dest_row;
2482 GtkTreeModel *dest_model;
2483 ModestFolderViewPrivate *priv;
2484 GdkDragAction suggested_action;
2485 gboolean valid_location = FALSE;
2486 TnyFolderStore *folder = NULL;
2488 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2490 if (priv->timer_expander != 0) {
2491 g_source_remove (priv->timer_expander);
2492 priv->timer_expander = 0;
2495 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2500 /* Do not allow drops between folders */
2502 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2503 pos == GTK_TREE_VIEW_DROP_AFTER) {
2504 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2505 gdk_drag_status(context, 0, time);
2506 valid_location = FALSE;
2509 valid_location = TRUE;
2512 /* Check that the destination folder is writable */
2513 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2514 folder = tree_path_to_folder (dest_model, dest_row);
2515 if (folder && TNY_IS_FOLDER (folder)) {
2516 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2518 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2519 valid_location = FALSE;
2524 /* Expand the selected row after 1/2 second */
2525 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2526 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2528 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2530 /* Select the desired action. By default we pick MOVE */
2531 suggested_action = GDK_ACTION_MOVE;
2533 if (context->actions == GDK_ACTION_COPY)
2534 gdk_drag_status(context, GDK_ACTION_COPY, time);
2535 else if (context->actions == GDK_ACTION_MOVE)
2536 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2537 else if (context->actions & suggested_action)
2538 gdk_drag_status(context, suggested_action, time);
2540 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2544 g_object_unref (folder);
2546 gtk_tree_path_free (dest_row);
2548 g_signal_stop_emission_by_name (widget, "drag-motion");
2550 return valid_location;
2554 * This function sets the treeview as a source and a target for dnd
2555 * events. It also connects all the requirede signals.
2558 setup_drag_and_drop (GtkTreeView *self)
2560 /* Set up the folder view as a dnd destination. Set only the
2561 highlight flag, otherwise gtk will have a different
2563 gtk_drag_dest_set (GTK_WIDGET (self),
2564 GTK_DEST_DEFAULT_HIGHLIGHT,
2565 folder_view_drag_types,
2566 G_N_ELEMENTS (folder_view_drag_types),
2567 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2569 g_signal_connect (G_OBJECT (self),
2570 "drag_data_received",
2571 G_CALLBACK (on_drag_data_received),
2575 /* Set up the treeview as a dnd source */
2576 gtk_drag_source_set (GTK_WIDGET (self),
2578 folder_view_drag_types,
2579 G_N_ELEMENTS (folder_view_drag_types),
2580 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2582 g_signal_connect (G_OBJECT (self),
2584 G_CALLBACK (on_drag_motion),
2587 g_signal_connect (G_OBJECT (self),
2589 G_CALLBACK (on_drag_data_get),
2592 g_signal_connect (G_OBJECT (self),
2594 G_CALLBACK (drag_drop_cb),
2599 * This function manages the navigation through the folders using the
2600 * keyboard or the hardware keys in the device
2603 on_key_pressed (GtkWidget *self,
2607 GtkTreeSelection *selection;
2609 GtkTreeModel *model;
2610 gboolean retval = FALSE;
2612 /* Up and Down are automatically managed by the treeview */
2613 if (event->keyval == GDK_Return) {
2614 /* Expand/Collapse the selected row */
2615 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2616 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2619 path = gtk_tree_model_get_path (model, &iter);
2621 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2622 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2624 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2625 gtk_tree_path_free (path);
2627 /* No further processing */
2635 * We listen to the changes in the local folder account name key,
2636 * because we want to show the right name in the view. The local
2637 * folder account name corresponds to the device name in the Maemo
2638 * version. We do this because we do not want to query gconf on each
2639 * tree view refresh. It's better to cache it and change whenever
2643 on_configuration_key_changed (ModestConf* conf,
2645 ModestConfEvent event,
2646 ModestConfNotificationId id,
2647 ModestFolderView *self)
2649 ModestFolderViewPrivate *priv;
2652 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2653 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2655 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2656 g_free (priv->local_account_name);
2658 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2659 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2661 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2662 MODEST_CONF_DEVICE_NAME, NULL);
2664 /* Force a redraw */
2665 #if GTK_CHECK_VERSION(2, 8, 0)
2666 GtkTreeViewColumn * tree_column;
2668 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2669 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2670 gtk_tree_view_column_queue_resize (tree_column);
2672 gtk_widget_queue_draw (GTK_WIDGET (self));
2678 modest_folder_view_set_style (ModestFolderView *self,
2679 ModestFolderViewStyle style)
2681 ModestFolderViewPrivate *priv;
2683 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2684 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2685 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2687 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2690 priv->style = style;
2694 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2695 const gchar *account_id)
2697 ModestFolderViewPrivate *priv;
2698 GtkTreeModel *model;
2700 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2702 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2704 /* This will be used by the filter_row callback,
2705 * to decided which rows to show: */
2706 if (priv->visible_account_id) {
2707 g_free (priv->visible_account_id);
2708 priv->visible_account_id = NULL;
2711 priv->visible_account_id = g_strdup (account_id);
2714 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2715 if (GTK_IS_TREE_MODEL_FILTER (model))
2716 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2718 /* Save settings to gconf */
2719 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2720 MODEST_CONF_FOLDER_VIEW_KEY);
2724 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2726 ModestFolderViewPrivate *priv;
2728 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2730 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2732 return (const gchar *) priv->visible_account_id;
2736 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2740 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2742 gtk_tree_model_get (model, iter,
2743 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2746 gboolean result = FALSE;
2747 if (type == TNY_FOLDER_TYPE_INBOX) {
2751 *inbox_iter = *iter;
2755 if (gtk_tree_model_iter_children (model, &child, iter)) {
2756 if (find_inbox_iter (model, &child, inbox_iter))
2760 } while (gtk_tree_model_iter_next (model, iter));
2769 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2771 GtkTreeModel *model;
2772 GtkTreeIter iter, inbox_iter;
2773 GtkTreeSelection *sel;
2774 GtkTreePath *path = NULL;
2776 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2778 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2782 expand_root_items (self);
2783 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2785 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2786 g_warning ("%s: model is empty", __FUNCTION__);
2790 if (find_inbox_iter (model, &iter, &inbox_iter))
2791 path = gtk_tree_model_get_path (model, &inbox_iter);
2793 path = gtk_tree_path_new_first ();
2795 /* Select the row and free */
2796 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2797 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2798 gtk_tree_path_free (path);
2801 gtk_widget_grab_focus (GTK_WIDGET(self));
2807 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2812 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2813 TnyFolder* a_folder;
2816 gtk_tree_model_get (model, iter,
2817 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2818 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2819 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2823 if (folder == a_folder) {
2824 g_object_unref (a_folder);
2825 *folder_iter = *iter;
2828 g_object_unref (a_folder);
2830 if (gtk_tree_model_iter_children (model, &child, iter)) {
2831 if (find_folder_iter (model, &child, folder_iter, folder))
2835 } while (gtk_tree_model_iter_next (model, iter));
2842 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2845 ModestFolderView *self)
2847 ModestFolderViewPrivate *priv = NULL;
2848 GtkTreeSelection *sel;
2849 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2850 GObject *instance = NULL;
2852 if (!MODEST_IS_FOLDER_VIEW(self))
2855 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2857 priv->reexpand = TRUE;
2859 gtk_tree_model_get (tree_model, iter,
2860 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2861 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2863 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2864 priv->folder_to_select = g_object_ref (instance);
2866 g_object_unref (instance);
2869 if (priv->folder_to_select) {
2871 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2874 path = gtk_tree_model_get_path (tree_model, iter);
2875 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2877 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2879 gtk_tree_selection_select_iter (sel, iter);
2880 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2882 gtk_tree_path_free (path);
2887 modest_folder_view_disable_next_folder_selection (self);
2889 /* Refilter the model */
2890 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2896 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2898 ModestFolderViewPrivate *priv;
2900 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2902 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2904 if (priv->folder_to_select)
2905 g_object_unref(priv->folder_to_select);
2907 priv->folder_to_select = NULL;
2911 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2912 gboolean after_change)
2914 GtkTreeModel *model;
2915 GtkTreeIter iter, folder_iter;
2916 GtkTreeSelection *sel;
2917 ModestFolderViewPrivate *priv = NULL;
2919 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2920 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2922 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2925 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2926 gtk_tree_selection_unselect_all (sel);
2928 if (priv->folder_to_select)
2929 g_object_unref(priv->folder_to_select);
2930 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2934 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2939 /* Refilter the model, before selecting the folder */
2940 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2942 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2943 g_warning ("%s: model is empty", __FUNCTION__);
2947 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2950 path = gtk_tree_model_get_path (model, &folder_iter);
2951 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2953 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2954 gtk_tree_selection_select_iter (sel, &folder_iter);
2955 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2957 gtk_tree_path_free (path);
2965 modest_folder_view_copy_selection (ModestFolderView *self)
2967 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2969 /* Copy selection */
2970 _clipboard_set_selected_data (self, FALSE);
2974 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2976 ModestFolderViewPrivate *priv = NULL;
2977 GtkTreeModel *model = NULL;
2978 const gchar **hidding = NULL;
2979 guint i, n_selected;
2981 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2982 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2984 /* Copy selection */
2985 if (!_clipboard_set_selected_data (folder_view, TRUE))
2988 /* Get hidding ids */
2989 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2991 /* Clear hidding array created by previous cut operation */
2992 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2994 /* Copy hidding array */
2995 priv->n_selected = n_selected;
2996 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2997 for (i=0; i < n_selected; i++)
2998 priv->hidding_ids[i] = g_strdup(hidding[i]);
3000 /* Hide cut folders */
3001 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3002 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3006 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3007 ModestFolderView *folder_view_dst)
3009 GtkTreeModel *filter_model = NULL;
3010 GtkTreeModel *model = NULL;
3011 GtkTreeModel *new_filter_model = NULL;
3013 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3014 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3017 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3018 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3020 /* Build new filter model */
3021 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3022 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3026 /* Set copied model */
3027 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3028 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3029 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3032 g_object_unref (new_filter_model);
3036 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3039 GtkTreeModel *model = NULL;
3040 ModestFolderViewPrivate* priv;
3042 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3044 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3045 priv->show_non_move = show;
3046 /* modest_folder_view_update_model(folder_view, */
3047 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3049 /* Hide special folders */
3050 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3051 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3052 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3056 /* Returns FALSE if it did not selected anything */
3058 _clipboard_set_selected_data (ModestFolderView *folder_view,
3061 ModestFolderViewPrivate *priv = NULL;
3062 TnyFolderStore *folder = NULL;
3063 gboolean retval = FALSE;
3065 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3066 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3068 /* Set selected data on clipboard */
3069 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3070 folder = modest_folder_view_get_selected (folder_view);
3072 /* Do not allow to select an account */
3073 if (TNY_IS_FOLDER (folder)) {
3074 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3079 g_object_unref (folder);
3085 _clear_hidding_filter (ModestFolderView *folder_view)
3087 ModestFolderViewPrivate *priv;
3090 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3091 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3093 if (priv->hidding_ids != NULL) {
3094 for (i=0; i < priv->n_selected; i++)
3095 g_free (priv->hidding_ids[i]);
3096 g_free(priv->hidding_ids);
3102 on_display_name_changed (ModestAccountMgr *mgr,
3103 const gchar *account,
3106 ModestFolderView *self;
3108 self = MODEST_FOLDER_VIEW (user_data);
3110 /* Force a redraw */
3111 #if GTK_CHECK_VERSION(2, 8, 0)
3112 GtkTreeViewColumn * tree_column;
3114 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3115 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3116 gtk_tree_view_column_queue_resize (tree_column);
3118 gtk_widget_queue_draw (GTK_WIDGET (self));