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 modest_folder_view_select_first_inbox_or_local (self);
1289 on_account_removed (TnyAccountStore *account_store,
1290 TnyAccount *account,
1293 ModestFolderView *self = NULL;
1294 ModestFolderViewPrivate *priv;
1295 GtkTreeModel *sort_model, *filter_model;
1296 GtkTreeSelection *sel = NULL;
1297 gboolean same_account_selected = FALSE;
1299 /* Ignore transport account removals, we're not showing them
1300 in the folder view */
1301 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1304 self = MODEST_FOLDER_VIEW (user_data);
1305 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1307 /* Invalidate the cur_folder_store only if the selected folder
1308 belongs to the account that is being removed */
1309 if (priv->cur_folder_store) {
1310 TnyAccount *selected_folder_account = NULL;
1312 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1313 selected_folder_account =
1314 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1316 selected_folder_account =
1317 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1320 if (selected_folder_account == account) {
1321 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1322 gtk_tree_selection_unselect_all (sel);
1323 same_account_selected = TRUE;
1325 g_object_unref (selected_folder_account);
1328 /* Invalidate row to select only if the folder to select
1329 belongs to the account that is being removed*/
1330 if (priv->folder_to_select) {
1331 TnyAccount *folder_to_select_account = NULL;
1333 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1334 if (folder_to_select_account == account) {
1335 modest_folder_view_disable_next_folder_selection (self);
1336 g_object_unref (priv->folder_to_select);
1337 priv->folder_to_select = NULL;
1339 g_object_unref (folder_to_select_account);
1342 /* Remove the account from the model */
1343 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1344 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1345 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1346 G_OBJECT (account));
1348 /* If the removed account is the currently viewed one then
1349 clear the configuration value. The new visible account will be the default account */
1350 if (priv->visible_account_id &&
1351 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1353 /* Clear the current visible account_id */
1354 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1356 /* Call the restore method, this will set the new visible account */
1357 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1358 MODEST_CONF_FOLDER_VIEW_KEY);
1361 /* Refilter the model */
1362 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1364 /* Select the first INBOX if the currently selected folder
1365 belongs to the account that is being deleted */
1366 if (same_account_selected)
1367 g_idle_add (on_idle_select_first_inbox_or_local, self);
1371 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1373 GtkTreeViewColumn *col;
1375 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1377 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1379 g_printerr ("modest: failed get column for title\n");
1383 gtk_tree_view_column_set_title (col, title);
1384 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1389 modest_folder_view_on_map (ModestFolderView *self,
1390 GdkEventExpose *event,
1393 ModestFolderViewPrivate *priv;
1395 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1397 /* This won't happen often */
1398 if (G_UNLIKELY (priv->reselect)) {
1399 /* Select the first inbox or the local account if not found */
1401 /* TODO: this could cause a lock at startup, so we
1402 comment it for the moment. We know that this will
1403 be a bug, because the INBOX is not selected, but we
1404 need to rewrite some parts of Modest to avoid the
1405 deathlock situation */
1406 /* TODO: check if this is still the case */
1407 priv->reselect = FALSE;
1408 modest_folder_view_select_first_inbox_or_local (self);
1409 /* Notify the display name observers */
1410 g_signal_emit (G_OBJECT(self),
1411 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1415 if (priv->reexpand) {
1416 expand_root_items (self);
1417 priv->reexpand = FALSE;
1424 modest_folder_view_new (TnyFolderStoreQuery *query)
1427 ModestFolderViewPrivate *priv;
1428 GtkTreeSelection *sel;
1430 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1431 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1434 priv->query = g_object_ref (query);
1436 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1437 priv->changed_signal = g_signal_connect (sel, "changed",
1438 G_CALLBACK (on_selection_changed), self);
1440 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1442 return GTK_WIDGET(self);
1445 /* this feels dirty; any other way to expand all the root items? */
1447 expand_root_items (ModestFolderView *self)
1450 GtkTreeModel *model;
1453 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1454 path = gtk_tree_path_new_first ();
1456 /* all folders should have child items, so.. */
1458 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1459 gtk_tree_path_next (path);
1460 } while (gtk_tree_model_get_iter (model, &iter, path));
1462 gtk_tree_path_free (path);
1466 * We use this function to implement the
1467 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1468 * account in this case, and the local folders.
1471 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1473 ModestFolderViewPrivate *priv;
1474 gboolean retval = TRUE;
1475 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1476 GObject *instance = NULL;
1477 const gchar *id = NULL;
1479 gboolean found = FALSE;
1480 gboolean cleared = FALSE;
1482 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1483 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1485 gtk_tree_model_get (model, iter,
1486 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1487 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1490 /* Do not show if there is no instance, this could indeed
1491 happen when the model is being modified while it's being
1492 drawn. This could occur for example when moving folders
1497 if (type == TNY_FOLDER_TYPE_ROOT) {
1498 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1499 account instead of a folder. */
1500 if (TNY_IS_ACCOUNT (instance)) {
1501 TnyAccount *acc = TNY_ACCOUNT (instance);
1502 const gchar *account_id = tny_account_get_id (acc);
1504 /* If it isn't a special folder,
1505 * don't show it unless it is the visible account: */
1506 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1507 !modest_tny_account_is_virtual_local_folders (acc) &&
1508 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1510 /* Show only the visible account id */
1511 if (priv->visible_account_id) {
1512 if (strcmp (account_id, priv->visible_account_id))
1519 /* Never show these to the user. They are merged into one folder
1520 * in the local-folders account instead: */
1521 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1526 /* Check hiding (if necessary) */
1527 cleared = modest_email_clipboard_cleared (priv->clipboard);
1528 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1529 id = tny_folder_get_id (TNY_FOLDER(instance));
1530 if (priv->hidding_ids != NULL)
1531 for (i=0; i < priv->n_selected && !found; i++)
1532 if (priv->hidding_ids[i] != NULL && id != NULL)
1533 found = (!strcmp (priv->hidding_ids[i], id));
1539 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1540 folder as no message can be move there according to UI specs */
1541 if (!priv->show_non_move) {
1543 case TNY_FOLDER_TYPE_OUTBOX:
1544 case TNY_FOLDER_TYPE_SENT:
1545 case TNY_FOLDER_TYPE_DRAFTS:
1548 case TNY_FOLDER_TYPE_UNKNOWN:
1549 case TNY_FOLDER_TYPE_NORMAL:
1550 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1551 if (type == TNY_FOLDER_TYPE_INVALID)
1552 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1554 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1555 type == TNY_FOLDER_TYPE_SENT
1556 || type == TNY_FOLDER_TYPE_DRAFTS)
1565 g_object_unref (instance);
1572 modest_folder_view_update_model (ModestFolderView *self,
1573 TnyAccountStore *account_store)
1575 ModestFolderViewPrivate *priv;
1576 GtkTreeModel *model /* , *old_model */;
1577 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1579 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1580 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1583 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1585 /* Notify that there is no folder selected */
1586 g_signal_emit (G_OBJECT(self),
1587 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1589 if (priv->cur_folder_store) {
1590 g_object_unref (priv->cur_folder_store);
1591 priv->cur_folder_store = NULL;
1594 /* FIXME: the local accounts are not shown when the query
1595 selects only the subscribed folders */
1596 model = tny_gtk_folder_store_tree_model_new (NULL);
1598 /* Get the accounts: */
1599 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1601 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1603 sortable = gtk_tree_model_sort_new_with_model (model);
1604 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1605 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1606 GTK_SORT_ASCENDING);
1607 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1608 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1609 cmp_rows, NULL, NULL);
1611 /* Create filter model */
1612 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1613 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1619 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1620 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1621 (GCallback) on_row_inserted_maybe_select_folder, self);
1624 g_object_unref (model);
1625 g_object_unref (filter_model);
1626 g_object_unref (sortable);
1628 /* Force a reselection of the INBOX next time the widget is shown */
1629 priv->reselect = TRUE;
1636 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1638 GtkTreeModel *model = NULL;
1639 TnyFolderStore *folder = NULL;
1641 ModestFolderView *tree_view = NULL;
1642 ModestFolderViewPrivate *priv = NULL;
1643 gboolean selected = FALSE;
1645 g_return_if_fail (sel);
1646 g_return_if_fail (user_data);
1648 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1650 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1652 tree_view = MODEST_FOLDER_VIEW (user_data);
1655 gtk_tree_model_get (model, &iter,
1656 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1659 /* If the folder is the same do not notify */
1660 if (folder && priv->cur_folder_store == folder) {
1661 g_object_unref (folder);
1666 /* Current folder was unselected */
1667 if (priv->cur_folder_store) {
1668 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1669 priv->cur_folder_store, FALSE);
1671 if (TNY_IS_FOLDER(priv->cur_folder_store))
1672 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1673 FALSE, NULL, NULL, NULL);
1675 /* FALSE --> don't expunge the messages */
1677 g_object_unref (priv->cur_folder_store);
1678 priv->cur_folder_store = NULL;
1681 /* New current references */
1682 priv->cur_folder_store = folder;
1684 /* New folder has been selected. Do not notify if there is
1685 nothing new selected */
1687 g_signal_emit (G_OBJECT(tree_view),
1688 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1689 0, priv->cur_folder_store, TRUE);
1694 modest_folder_view_get_selected (ModestFolderView *self)
1696 ModestFolderViewPrivate *priv;
1698 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1700 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1701 if (priv->cur_folder_store)
1702 g_object_ref (priv->cur_folder_store);
1704 return priv->cur_folder_store;
1708 get_cmp_rows_type_pos (GObject *folder)
1710 /* Remote accounts -> Local account -> MMC account .*/
1713 if (TNY_IS_ACCOUNT (folder) &&
1714 modest_tny_account_is_virtual_local_folders (
1715 TNY_ACCOUNT (folder))) {
1717 } else if (TNY_IS_ACCOUNT (folder)) {
1718 TnyAccount *account = TNY_ACCOUNT (folder);
1719 const gchar *account_id = tny_account_get_id (account);
1720 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1726 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1727 return -1; /* Should never happen */
1732 get_cmp_subfolder_type_pos (TnyFolderType t)
1734 /* Inbox, Outbox, Drafts, Sent, User */
1738 case TNY_FOLDER_TYPE_INBOX:
1741 case TNY_FOLDER_TYPE_OUTBOX:
1744 case TNY_FOLDER_TYPE_DRAFTS:
1747 case TNY_FOLDER_TYPE_SENT:
1756 * This function orders the mail accounts according to these rules:
1757 * 1st - remote accounts
1758 * 2nd - local account
1762 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1766 gchar *name1 = NULL;
1767 gchar *name2 = NULL;
1768 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1769 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1770 GObject *folder1 = NULL;
1771 GObject *folder2 = NULL;
1773 gtk_tree_model_get (tree_model, iter1,
1774 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1775 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1776 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1778 gtk_tree_model_get (tree_model, iter2,
1779 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1780 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1781 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1784 /* Return if we get no folder. This could happen when folder
1785 operations are happening. The model is updated after the
1786 folder copy/move actually occurs, so there could be
1787 situations where the model to be drawn is not correct */
1788 if (!folder1 || !folder2)
1791 if (type == TNY_FOLDER_TYPE_ROOT) {
1792 /* Compare the types, so that
1793 * Remote accounts -> Local account -> MMC account .*/
1794 const gint pos1 = get_cmp_rows_type_pos (folder1);
1795 const gint pos2 = get_cmp_rows_type_pos (folder2);
1796 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1797 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1800 else if (pos1 > pos2)
1803 /* Compare items of the same type: */
1805 TnyAccount *account1 = NULL;
1806 if (TNY_IS_ACCOUNT (folder1))
1807 account1 = TNY_ACCOUNT (folder1);
1809 TnyAccount *account2 = NULL;
1810 if (TNY_IS_ACCOUNT (folder2))
1811 account2 = TNY_ACCOUNT (folder2);
1813 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1814 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1816 if (!account_id && !account_id2) {
1818 } else if (!account_id) {
1820 } else if (!account_id2) {
1822 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1825 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1829 gint cmp1 = 0, cmp2 = 0;
1830 /* get the parent to know if it's a local folder */
1833 gboolean has_parent;
1834 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1836 GObject *parent_folder;
1837 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1838 gtk_tree_model_get (tree_model, &parent,
1839 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1840 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1842 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1843 TNY_IS_ACCOUNT (parent_folder)) {
1844 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1845 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1846 (TNY_FOLDER (folder1)));
1847 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1848 (TNY_FOLDER (folder2)));
1849 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1850 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1853 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1859 g_object_unref (parent_folder);
1862 /* if they are not local folders */
1864 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1865 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1869 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1871 cmp = (cmp1 - cmp2);
1876 g_object_unref(G_OBJECT(folder1));
1878 g_object_unref(G_OBJECT(folder2));
1886 /*****************************************************************************/
1887 /* DRAG and DROP stuff */
1888 /*****************************************************************************/
1890 * This function fills the #GtkSelectionData with the row and the
1891 * model that has been dragged. It's called when this widget is a
1892 * source for dnd after the event drop happened
1895 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1896 guint info, guint time, gpointer data)
1898 GtkTreeSelection *selection;
1899 GtkTreeModel *model;
1901 GtkTreePath *source_row;
1903 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1904 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1906 source_row = gtk_tree_model_get_path (model, &iter);
1907 gtk_tree_set_row_drag_data (selection_data,
1911 gtk_tree_path_free (source_row);
1915 typedef struct _DndHelper {
1916 ModestFolderView *folder_view;
1917 gboolean delete_source;
1918 GtkTreePath *source_row;
1919 GdkDragContext *context;
1924 dnd_helper_destroyer (DndHelper *helper)
1926 /* Free the helper */
1927 g_object_unref (helper->folder_view);
1928 gtk_tree_path_free (helper->source_row);
1929 g_slice_free (DndHelper, helper);
1933 xfer_cb (ModestMailOperation *mail_op,
1939 helper = (DndHelper *) user_data;
1941 if (modest_mail_operation_get_status (mail_op) ==
1942 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1948 /* Notify the drag source. Never call delete, the monitor will
1949 do the job if needed */
1950 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1952 /* Free the helper */
1953 dnd_helper_destroyer (helper);
1957 xfer_msgs_cb (ModestMailOperation *mail_op,
1961 xfer_cb (mail_op, user_data);
1965 xfer_folder_cb (ModestMailOperation *mail_op,
1966 TnyFolder *new_folder,
1970 GtkWidget *folder_view;
1972 helper = (DndHelper *) user_data;
1973 folder_view = g_object_ref (helper->folder_view);
1976 xfer_cb (mail_op, user_data);
1978 /* Select the folder */
1980 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1982 g_object_unref (folder_view);
1986 /* get the folder for the row the treepath refers to. */
1987 /* folder must be unref'd */
1988 static TnyFolderStore *
1989 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1992 TnyFolderStore *folder = NULL;
1994 if (gtk_tree_model_get_iter (model,&iter, path))
1995 gtk_tree_model_get (model, &iter,
1996 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2002 * This function is used by drag_data_received_cb to manage drag and
2003 * drop of a header, i.e, and drag from the header view to the folder
2007 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2008 GtkTreeModel *dest_model,
2009 GtkTreePath *dest_row,
2010 GtkSelectionData *selection_data,
2013 TnyList *headers = NULL;
2014 TnyFolder *folder = NULL;
2015 TnyFolderType folder_type;
2016 ModestMailOperation *mail_op = NULL;
2017 GtkTreeIter source_iter, dest_iter;
2018 ModestWindowMgr *mgr = NULL;
2019 ModestWindow *main_win = NULL;
2020 gchar **uris, **tmp;
2023 /* Build the list of headers */
2024 mgr = modest_runtime_get_window_mgr ();
2025 headers = tny_simple_list_new ();
2026 uris = modest_dnd_selection_data_get_paths (selection_data);
2029 while (*tmp != NULL) {
2034 path = gtk_tree_path_new_from_string (*tmp);
2035 gtk_tree_model_get_iter (source_model, &source_iter, path);
2036 gtk_tree_model_get (source_model, &source_iter,
2037 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2040 /* Do not enable d&d of headers already opened */
2041 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2042 tny_list_append (headers, G_OBJECT (header));
2044 /* Free and go on */
2045 gtk_tree_path_free (path);
2046 g_object_unref (header);
2051 /* Get the target folder */
2052 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2053 gtk_tree_model_get (dest_model, &dest_iter,
2054 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2057 if (!folder || !TNY_IS_FOLDER(folder)) {
2058 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2062 folder_type = modest_tny_folder_guess_folder_type (folder);
2063 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2064 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2065 goto cleanup; /* cannot move messages there */
2068 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2069 /* g_warning ("folder not writable"); */
2070 goto cleanup; /* verboten! */
2073 /* Ask for confirmation to move */
2074 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2076 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2080 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2082 if (response == GTK_RESPONSE_CANCEL)
2085 /* Transfer messages */
2086 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2087 modest_ui_actions_move_folder_error_handler,
2090 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2093 modest_mail_operation_xfer_msgs (mail_op,
2096 helper->delete_source,
2097 xfer_msgs_cb, helper);
2101 if (G_IS_OBJECT(mail_op))
2102 g_object_unref (G_OBJECT (mail_op));
2103 if (G_IS_OBJECT(folder))
2104 g_object_unref (G_OBJECT (folder));
2105 if (G_IS_OBJECT(headers))
2106 g_object_unref (headers);
2110 TnyFolderStore *src_folder;
2111 TnyFolderStore *dst_folder;
2112 ModestFolderView *folder_view;
2117 dnd_folder_info_destroyer (DndFolderInfo *info)
2119 if (info->src_folder)
2120 g_object_unref (info->src_folder);
2121 if (info->dst_folder)
2122 g_object_unref (info->dst_folder);
2123 if (info->folder_view)
2124 g_object_unref (info->folder_view);
2125 g_slice_free (DndFolderInfo, info);
2129 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2130 GtkWindow *parent_window,
2131 TnyAccount *account)
2133 time_t dnd_time = info->helper->time;
2134 GdkDragContext *context = info->helper->context;
2137 modest_ui_actions_on_account_connection_error (parent_window, account);
2139 /* Free the helper & info */
2140 dnd_helper_destroyer (info->helper);
2141 dnd_folder_info_destroyer (info);
2143 /* Notify the drag source. Never call delete, the monitor will
2144 do the job if needed */
2145 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2150 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2152 GtkWindow *parent_window,
2153 TnyAccount *account,
2156 DndFolderInfo *info = NULL;
2157 ModestMailOperation *mail_op;
2159 info = (DndFolderInfo *) user_data;
2161 if (err || canceled) {
2162 dnd_on_connection_failed_destroyer (info, parent_window, account);
2166 /* Do the mail operation */
2167 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2168 modest_ui_actions_move_folder_error_handler,
2169 info->src_folder, NULL);
2171 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2174 /* Transfer the folder */
2175 modest_mail_operation_xfer_folder (mail_op,
2176 TNY_FOLDER (info->src_folder),
2178 info->helper->delete_source,
2182 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2183 /* TNY_FOLDER (info->dst_folder), TRUE); */
2185 g_object_unref (G_OBJECT (mail_op));
2190 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2192 GtkWindow *parent_window,
2193 TnyAccount *account,
2196 DndFolderInfo *info = NULL;
2198 info = (DndFolderInfo *) user_data;
2200 if (err || canceled) {
2201 dnd_on_connection_failed_destroyer (info, parent_window, account);
2205 /* Connect to source folder and perform the copy/move */
2206 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2208 drag_and_drop_from_folder_view_src_folder_performer,
2213 * This function is used by drag_data_received_cb to manage drag and
2214 * drop of a folder, i.e, and drag from the folder view to the same
2218 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2219 GtkTreeModel *dest_model,
2220 GtkTreePath *dest_row,
2221 GtkSelectionData *selection_data,
2224 GtkTreeIter dest_iter, iter;
2225 TnyFolderStore *dest_folder = NULL;
2226 TnyFolderStore *folder = NULL;
2227 gboolean forbidden = FALSE;
2229 DndFolderInfo *info = NULL;
2231 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2233 g_warning ("%s: BUG: no main window", __FUNCTION__);
2238 /* check the folder rules for the destination */
2239 folder = tree_path_to_folder (dest_model, dest_row);
2240 if (TNY_IS_FOLDER(folder)) {
2241 ModestTnyFolderRules rules =
2242 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2243 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2244 } else if (TNY_IS_FOLDER_STORE(folder)) {
2245 /* enable local root as destination for folders */
2246 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2247 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2250 g_object_unref (folder);
2253 /* check the folder rules for the source */
2254 folder = tree_path_to_folder (source_model, helper->source_row);
2255 if (TNY_IS_FOLDER(folder)) {
2256 ModestTnyFolderRules rules =
2257 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2258 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2261 g_object_unref (folder);
2265 /* Check if the drag is possible */
2266 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2267 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2268 gtk_tree_path_free (helper->source_row);
2269 g_slice_free (DndHelper, helper);
2274 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2275 gtk_tree_model_get (dest_model, &dest_iter,
2276 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2278 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2279 gtk_tree_model_get (source_model, &iter,
2280 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2283 /* Create the info for the performer */
2284 info = g_slice_new (DndFolderInfo);
2285 info->src_folder = g_object_ref (folder);
2286 info->dst_folder = g_object_ref (dest_folder);
2287 info->folder_view = g_object_ref (helper->folder_view);
2288 info->helper = helper;
2290 /* Connect to the destination folder and perform the copy/move */
2291 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2293 drag_and_drop_from_folder_view_dst_folder_performer,
2297 g_object_unref (dest_folder);
2298 g_object_unref (folder);
2302 * This function receives the data set by the "drag-data-get" signal
2303 * handler. This information comes within the #GtkSelectionData. This
2304 * function will manage both the drags of folders of the treeview and
2305 * drags of headers of the header view widget.
2308 on_drag_data_received (GtkWidget *widget,
2309 GdkDragContext *context,
2312 GtkSelectionData *selection_data,
2317 GtkWidget *source_widget;
2318 GtkTreeModel *dest_model, *source_model;
2319 GtkTreePath *source_row, *dest_row;
2320 GtkTreeViewDropPosition pos;
2321 gboolean success = FALSE, delete_source = FALSE;
2322 DndHelper *helper = NULL;
2324 /* Do not allow further process */
2325 g_signal_stop_emission_by_name (widget, "drag-data-received");
2326 source_widget = gtk_drag_get_source_widget (context);
2328 /* Get the action */
2329 if (context->action == GDK_ACTION_MOVE) {
2330 delete_source = TRUE;
2332 /* Notify that there is no folder selected. We need to
2333 do this in order to update the headers view (and
2334 its monitors, because when moving, the old folder
2335 won't longer exist. We can not wait for the end of
2336 the operation, because the operation won't start if
2337 the folder is in use */
2338 if (source_widget == widget) {
2339 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2340 gtk_tree_selection_unselect_all (sel);
2344 /* Check if the get_data failed */
2345 if (selection_data == NULL || selection_data->length < 0)
2346 gtk_drag_finish (context, success, FALSE, time);
2348 /* Select the destination model */
2349 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2351 /* Get the path to the destination row. Can not call
2352 gtk_tree_view_get_drag_dest_row() because the source row
2353 is not selected anymore */
2354 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2357 /* Only allow drops IN other rows */
2359 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2360 pos == GTK_TREE_VIEW_DROP_AFTER)
2361 gtk_drag_finish (context, success, FALSE, time);
2363 /* Create the helper */
2364 helper = g_slice_new0 (DndHelper);
2365 helper->delete_source = delete_source;
2366 helper->context = context;
2367 helper->time = time;
2368 helper->folder_view = g_object_ref (widget);
2370 /* Drags from the header view */
2371 if (source_widget != widget) {
2372 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2374 drag_and_drop_from_header_view (source_model,
2380 /* Get the source model and row */
2381 gtk_tree_get_row_drag_data (selection_data,
2384 helper->source_row = gtk_tree_path_copy (source_row);
2386 drag_and_drop_from_folder_view (source_model,
2392 gtk_tree_path_free (source_row);
2396 gtk_tree_path_free (dest_row);
2400 * We define a "drag-drop" signal handler because we do not want to
2401 * use the default one, because the default one always calls
2402 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2403 * signal handler, because there we have all the information available
2404 * to know if the dnd was a success or not.
2407 drag_drop_cb (GtkWidget *widget,
2408 GdkDragContext *context,
2416 if (!context->targets)
2419 /* Check if we're dragging a folder row */
2420 target = gtk_drag_dest_find_target (widget, context, NULL);
2422 /* Request the data from the source. */
2423 gtk_drag_get_data(widget, context, target, time);
2429 * This function expands a node of a tree view if it's not expanded
2430 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2431 * does that, so that's why they're here.
2434 expand_row_timeout (gpointer data)
2436 GtkTreeView *tree_view = data;
2437 GtkTreePath *dest_path = NULL;
2438 GtkTreeViewDropPosition pos;
2439 gboolean result = FALSE;
2441 gdk_threads_enter ();
2443 gtk_tree_view_get_drag_dest_row (tree_view,
2448 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2449 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2450 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2451 gtk_tree_path_free (dest_path);
2455 gtk_tree_path_free (dest_path);
2460 gdk_threads_leave ();
2466 * This function is called whenever the pointer is moved over a widget
2467 * while dragging some data. It installs a timeout that will expand a
2468 * node of the treeview if not expanded yet. This function also calls
2469 * gdk_drag_status in order to set the suggested action that will be
2470 * used by the "drag-data-received" signal handler to know if we
2471 * should do a move or just a copy of the data.
2474 on_drag_motion (GtkWidget *widget,
2475 GdkDragContext *context,
2481 GtkTreeViewDropPosition pos;
2482 GtkTreePath *dest_row;
2483 GtkTreeModel *dest_model;
2484 ModestFolderViewPrivate *priv;
2485 GdkDragAction suggested_action;
2486 gboolean valid_location = FALSE;
2487 TnyFolderStore *folder = NULL;
2489 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2491 if (priv->timer_expander != 0) {
2492 g_source_remove (priv->timer_expander);
2493 priv->timer_expander = 0;
2496 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2501 /* Do not allow drops between folders */
2503 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2504 pos == GTK_TREE_VIEW_DROP_AFTER) {
2505 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2506 gdk_drag_status(context, 0, time);
2507 valid_location = FALSE;
2510 valid_location = TRUE;
2513 /* Check that the destination folder is writable */
2514 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2515 folder = tree_path_to_folder (dest_model, dest_row);
2516 if (folder && TNY_IS_FOLDER (folder)) {
2517 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2519 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2520 valid_location = FALSE;
2525 /* Expand the selected row after 1/2 second */
2526 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2527 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2529 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2531 /* Select the desired action. By default we pick MOVE */
2532 suggested_action = GDK_ACTION_MOVE;
2534 if (context->actions == GDK_ACTION_COPY)
2535 gdk_drag_status(context, GDK_ACTION_COPY, time);
2536 else if (context->actions == GDK_ACTION_MOVE)
2537 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2538 else if (context->actions & suggested_action)
2539 gdk_drag_status(context, suggested_action, time);
2541 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2545 g_object_unref (folder);
2547 gtk_tree_path_free (dest_row);
2549 g_signal_stop_emission_by_name (widget, "drag-motion");
2551 return valid_location;
2555 * This function sets the treeview as a source and a target for dnd
2556 * events. It also connects all the requirede signals.
2559 setup_drag_and_drop (GtkTreeView *self)
2561 /* Set up the folder view as a dnd destination. Set only the
2562 highlight flag, otherwise gtk will have a different
2564 gtk_drag_dest_set (GTK_WIDGET (self),
2565 GTK_DEST_DEFAULT_HIGHLIGHT,
2566 folder_view_drag_types,
2567 G_N_ELEMENTS (folder_view_drag_types),
2568 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2570 g_signal_connect (G_OBJECT (self),
2571 "drag_data_received",
2572 G_CALLBACK (on_drag_data_received),
2576 /* Set up the treeview as a dnd source */
2577 gtk_drag_source_set (GTK_WIDGET (self),
2579 folder_view_drag_types,
2580 G_N_ELEMENTS (folder_view_drag_types),
2581 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2583 g_signal_connect (G_OBJECT (self),
2585 G_CALLBACK (on_drag_motion),
2588 g_signal_connect (G_OBJECT (self),
2590 G_CALLBACK (on_drag_data_get),
2593 g_signal_connect (G_OBJECT (self),
2595 G_CALLBACK (drag_drop_cb),
2600 * This function manages the navigation through the folders using the
2601 * keyboard or the hardware keys in the device
2604 on_key_pressed (GtkWidget *self,
2608 GtkTreeSelection *selection;
2610 GtkTreeModel *model;
2611 gboolean retval = FALSE;
2613 /* Up and Down are automatically managed by the treeview */
2614 if (event->keyval == GDK_Return) {
2615 /* Expand/Collapse the selected row */
2616 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2617 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2620 path = gtk_tree_model_get_path (model, &iter);
2622 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2623 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2625 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2626 gtk_tree_path_free (path);
2628 /* No further processing */
2636 * We listen to the changes in the local folder account name key,
2637 * because we want to show the right name in the view. The local
2638 * folder account name corresponds to the device name in the Maemo
2639 * version. We do this because we do not want to query gconf on each
2640 * tree view refresh. It's better to cache it and change whenever
2644 on_configuration_key_changed (ModestConf* conf,
2646 ModestConfEvent event,
2647 ModestConfNotificationId id,
2648 ModestFolderView *self)
2650 ModestFolderViewPrivate *priv;
2653 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2654 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2656 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2657 g_free (priv->local_account_name);
2659 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2660 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2662 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2663 MODEST_CONF_DEVICE_NAME, NULL);
2665 /* Force a redraw */
2666 #if GTK_CHECK_VERSION(2, 8, 0)
2667 GtkTreeViewColumn * tree_column;
2669 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2670 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2671 gtk_tree_view_column_queue_resize (tree_column);
2673 gtk_widget_queue_draw (GTK_WIDGET (self));
2679 modest_folder_view_set_style (ModestFolderView *self,
2680 ModestFolderViewStyle style)
2682 ModestFolderViewPrivate *priv;
2684 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2685 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2686 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2688 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2691 priv->style = style;
2695 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2696 const gchar *account_id)
2698 ModestFolderViewPrivate *priv;
2699 GtkTreeModel *model;
2701 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2703 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2705 /* This will be used by the filter_row callback,
2706 * to decided which rows to show: */
2707 if (priv->visible_account_id) {
2708 g_free (priv->visible_account_id);
2709 priv->visible_account_id = NULL;
2712 priv->visible_account_id = g_strdup (account_id);
2715 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2716 if (GTK_IS_TREE_MODEL_FILTER (model))
2717 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2719 /* Save settings to gconf */
2720 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2721 MODEST_CONF_FOLDER_VIEW_KEY);
2725 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2727 ModestFolderViewPrivate *priv;
2729 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2731 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2733 return (const gchar *) priv->visible_account_id;
2737 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2741 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2743 gtk_tree_model_get (model, iter,
2744 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2747 gboolean result = FALSE;
2748 if (type == TNY_FOLDER_TYPE_INBOX) {
2752 *inbox_iter = *iter;
2756 if (gtk_tree_model_iter_children (model, &child, iter)) {
2757 if (find_inbox_iter (model, &child, inbox_iter))
2761 } while (gtk_tree_model_iter_next (model, iter));
2770 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2772 GtkTreeModel *model;
2773 GtkTreeIter iter, inbox_iter;
2774 GtkTreeSelection *sel;
2775 GtkTreePath *path = NULL;
2777 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2779 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2783 expand_root_items (self);
2784 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2786 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2787 g_warning ("%s: model is empty", __FUNCTION__);
2791 if (find_inbox_iter (model, &iter, &inbox_iter))
2792 path = gtk_tree_model_get_path (model, &inbox_iter);
2794 path = gtk_tree_path_new_first ();
2796 /* Select the row and free */
2797 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2798 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2799 gtk_tree_path_free (path);
2802 gtk_widget_grab_focus (GTK_WIDGET(self));
2808 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2813 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2814 TnyFolder* a_folder;
2817 gtk_tree_model_get (model, iter,
2818 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2819 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2820 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2824 if (folder == a_folder) {
2825 g_object_unref (a_folder);
2826 *folder_iter = *iter;
2829 g_object_unref (a_folder);
2831 if (gtk_tree_model_iter_children (model, &child, iter)) {
2832 if (find_folder_iter (model, &child, folder_iter, folder))
2836 } while (gtk_tree_model_iter_next (model, iter));
2843 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2846 ModestFolderView *self)
2848 ModestFolderViewPrivate *priv = NULL;
2849 GtkTreeSelection *sel;
2850 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2851 GObject *instance = NULL;
2853 if (!MODEST_IS_FOLDER_VIEW(self))
2856 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2858 priv->reexpand = TRUE;
2860 gtk_tree_model_get (tree_model, iter,
2861 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2862 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2864 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2865 priv->folder_to_select = g_object_ref (instance);
2867 g_object_unref (instance);
2870 if (priv->folder_to_select) {
2872 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2875 path = gtk_tree_model_get_path (tree_model, iter);
2876 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2878 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2880 gtk_tree_selection_select_iter (sel, iter);
2881 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2883 gtk_tree_path_free (path);
2888 modest_folder_view_disable_next_folder_selection (self);
2890 /* Refilter the model */
2891 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2897 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2899 ModestFolderViewPrivate *priv;
2901 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2903 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2905 if (priv->folder_to_select)
2906 g_object_unref(priv->folder_to_select);
2908 priv->folder_to_select = NULL;
2912 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2913 gboolean after_change)
2915 GtkTreeModel *model;
2916 GtkTreeIter iter, folder_iter;
2917 GtkTreeSelection *sel;
2918 ModestFolderViewPrivate *priv = NULL;
2920 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2921 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2923 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2926 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2927 gtk_tree_selection_unselect_all (sel);
2929 if (priv->folder_to_select)
2930 g_object_unref(priv->folder_to_select);
2931 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2935 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2940 /* Refilter the model, before selecting the folder */
2941 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2943 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2944 g_warning ("%s: model is empty", __FUNCTION__);
2948 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2951 path = gtk_tree_model_get_path (model, &folder_iter);
2952 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2954 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2955 gtk_tree_selection_select_iter (sel, &folder_iter);
2956 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2958 gtk_tree_path_free (path);
2966 modest_folder_view_copy_selection (ModestFolderView *self)
2968 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2970 /* Copy selection */
2971 _clipboard_set_selected_data (self, FALSE);
2975 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2977 ModestFolderViewPrivate *priv = NULL;
2978 GtkTreeModel *model = NULL;
2979 const gchar **hidding = NULL;
2980 guint i, n_selected;
2982 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2983 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2985 /* Copy selection */
2986 if (!_clipboard_set_selected_data (folder_view, TRUE))
2989 /* Get hidding ids */
2990 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2992 /* Clear hidding array created by previous cut operation */
2993 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2995 /* Copy hidding array */
2996 priv->n_selected = n_selected;
2997 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2998 for (i=0; i < n_selected; i++)
2999 priv->hidding_ids[i] = g_strdup(hidding[i]);
3001 /* Hide cut folders */
3002 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3003 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3007 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3008 ModestFolderView *folder_view_dst)
3010 GtkTreeModel *filter_model = NULL;
3011 GtkTreeModel *model = NULL;
3012 GtkTreeModel *new_filter_model = NULL;
3014 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3015 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3018 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3019 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3021 /* Build new filter model */
3022 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3023 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3027 /* Set copied model */
3028 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3029 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3030 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3033 g_object_unref (new_filter_model);
3037 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3040 GtkTreeModel *model = NULL;
3041 ModestFolderViewPrivate* priv;
3043 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3045 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3046 priv->show_non_move = show;
3047 /* modest_folder_view_update_model(folder_view, */
3048 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3050 /* Hide special folders */
3051 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3052 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3053 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3057 /* Returns FALSE if it did not selected anything */
3059 _clipboard_set_selected_data (ModestFolderView *folder_view,
3062 ModestFolderViewPrivate *priv = NULL;
3063 TnyFolderStore *folder = NULL;
3064 gboolean retval = FALSE;
3066 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3067 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3069 /* Set selected data on clipboard */
3070 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3071 folder = modest_folder_view_get_selected (folder_view);
3073 /* Do not allow to select an account */
3074 if (TNY_IS_FOLDER (folder)) {
3075 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3080 g_object_unref (folder);
3086 _clear_hidding_filter (ModestFolderView *folder_view)
3088 ModestFolderViewPrivate *priv;
3091 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3092 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3094 if (priv->hidding_ids != NULL) {
3095 for (i=0; i < priv->n_selected; i++)
3096 g_free (priv->hidding_ids[i]);
3097 g_free(priv->hidding_ids);
3103 on_display_name_changed (ModestAccountMgr *mgr,
3104 const gchar *account,
3107 ModestFolderView *self;
3109 self = MODEST_FOLDER_VIEW (user_data);
3111 /* Force a redraw */
3112 #if GTK_CHECK_VERSION(2, 8, 0)
3113 GtkTreeViewColumn * tree_column;
3115 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3116 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3117 gtk_tree_view_column_queue_resize (tree_column);
3119 gtk_widget_queue_draw (GTK_WIDGET (self));