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 gobject_class = (GObjectClass*) klass;
246 parent_class = g_type_class_peek_parent (klass);
247 gobject_class->finalize = modest_folder_view_finalize;
249 g_type_class_add_private (gobject_class,
250 sizeof(ModestFolderViewPrivate));
252 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
253 g_signal_new ("folder_selection_changed",
254 G_TYPE_FROM_CLASS (gobject_class),
256 G_STRUCT_OFFSET (ModestFolderViewClass,
257 folder_selection_changed),
259 modest_marshal_VOID__POINTER_BOOLEAN,
260 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
263 * This signal is emitted whenever the currently selected
264 * folder display name is computed. Note that the name could
265 * be different to the folder name, because we could append
266 * the unread messages count to the folder name to build the
267 * folder display name
269 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
270 g_signal_new ("folder-display-name-changed",
271 G_TYPE_FROM_CLASS (gobject_class),
273 G_STRUCT_OFFSET (ModestFolderViewClass,
274 folder_display_name_changed),
276 g_cclosure_marshal_VOID__STRING,
277 G_TYPE_NONE, 1, G_TYPE_STRING);
280 /* Simplify checks for NULLs: */
282 strings_are_equal (const gchar *a, const gchar *b)
288 return (strcmp (a, b) == 0);
295 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
297 GObject *instance = NULL;
299 gtk_tree_model_get (model, iter,
300 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
304 return FALSE; /* keep walking */
306 if (!TNY_IS_ACCOUNT (instance)) {
307 g_object_unref (instance);
308 return FALSE; /* keep walking */
311 /* Check if this is the looked-for account: */
312 TnyAccount *this_account = TNY_ACCOUNT (instance);
313 TnyAccount *account = TNY_ACCOUNT (data);
315 const gchar *this_account_id = tny_account_get_id(this_account);
316 const gchar *account_id = tny_account_get_id(account);
317 g_object_unref (instance);
320 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
321 if (strings_are_equal(this_account_id, account_id)) {
322 /* Tell the model that the data has changed, so that
323 * it calls the cell_data_func callbacks again: */
324 /* TODO: This does not seem to actually cause the new string to be shown: */
325 gtk_tree_model_row_changed (model, path, iter);
327 return TRUE; /* stop walking */
330 return FALSE; /* keep walking */
335 ModestFolderView *self;
336 gchar *previous_name;
337 } GetMmcAccountNameData;
340 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
342 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
344 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
346 if (!strings_are_equal (
347 tny_account_get_name(TNY_ACCOUNT(account)),
348 data->previous_name)) {
350 /* Tell the model that the data has changed, so that
351 * it calls the cell_data_func callbacks again: */
352 ModestFolderView *self = data->self;
353 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
355 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
358 g_free (data->previous_name);
359 g_slice_free (GetMmcAccountNameData, data);
363 text_cell_data (GtkTreeViewColumn *column,
364 GtkCellRenderer *renderer,
365 GtkTreeModel *tree_model,
369 ModestFolderViewPrivate *priv;
370 GObject *rendobj = (GObject *) renderer;
372 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
373 GObject *instance = NULL;
375 gtk_tree_model_get (tree_model, iter,
376 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
377 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
378 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
388 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
389 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
391 gchar *item_name = NULL;
392 gint item_weight = 400;
394 if (type != TNY_FOLDER_TYPE_ROOT) {
397 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
398 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
399 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
400 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
402 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
406 /* note: we cannot reliably get the counts from the tree model, we need
407 * to use explicit calls on tny_folder for some reason.
409 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
410 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
411 (type == TNY_FOLDER_TYPE_OUTBOX) ||
412 (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
413 number = tny_folder_get_all_count (TNY_FOLDER(instance));
415 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
417 /* Use bold font style if there are unread or unset messages */
419 item_name = g_strdup_printf ("%s (%d)", fname, number);
422 item_name = g_strdup (fname);
426 } else if (TNY_IS_ACCOUNT (instance)) {
427 /* If it's a server account */
428 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
429 item_name = g_strdup (priv->local_account_name);
431 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
432 /* fname is only correct when the items are first
433 * added to the model, not when the account is
434 * changed later, so get the name from the account
436 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
439 item_name = g_strdup (fname);
445 item_name = g_strdup ("unknown");
447 if (item_name && item_weight) {
448 /* Set the name in the treeview cell: */
449 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
451 /* Notify display name observers */
452 /* TODO: What listens for this signal, and how can it use only the new name? */
453 if (((GObject *) priv->cur_folder_store) == instance) {
454 g_signal_emit (G_OBJECT(self),
455 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
462 /* If it is a Memory card account, make sure that we have the correct name.
463 * This function will be trigerred again when the name has been retrieved: */
464 if (TNY_IS_STORE_ACCOUNT (instance) &&
465 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
467 /* Get the account name asynchronously: */
468 GetMmcAccountNameData *callback_data =
469 g_slice_new0(GetMmcAccountNameData);
470 callback_data->self = self;
472 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
474 callback_data->previous_name = g_strdup (name);
476 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
477 on_get_mmc_account_name, callback_data);
480 g_object_unref (G_OBJECT (instance));
487 GdkPixbuf *pixbuf_open;
488 GdkPixbuf *pixbuf_close;
493 get_folder_icons (TnyFolderType type, GObject *instance)
495 GdkPixbuf *pixbuf = NULL;
496 GdkPixbuf *pixbuf_open = NULL;
497 GdkPixbuf *pixbuf_close = NULL;
498 ThreePixbufs *retval = g_slice_new (ThreePixbufs);
500 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
501 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
502 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
503 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
504 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
506 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
507 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
508 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
509 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
510 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
512 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
513 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
514 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
515 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
516 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
519 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
520 /* We include the MERGE type here because it's used to create
521 the local OUTBOX folder */
522 if (type == TNY_FOLDER_TYPE_NORMAL ||
523 type == TNY_FOLDER_TYPE_UNKNOWN) {
524 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
528 case TNY_FOLDER_TYPE_INVALID:
529 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
532 case TNY_FOLDER_TYPE_ROOT:
533 if (TNY_IS_ACCOUNT (instance)) {
535 if (modest_tny_account_is_virtual_local_folders (
536 TNY_ACCOUNT (instance))) {
539 avirt_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
540 MODEST_ICON_SIZE_SMALL));
542 if (!avirt_pixbuf_open) {
543 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
544 MODEST_ICON_SIZE_SMALL);
545 avirt_pixbuf_open = gdk_pixbuf_copy (avirt_pixbuf);
546 gdk_pixbuf_composite (emblem, avirt_pixbuf_open, 0, 0,
547 MIN (gdk_pixbuf_get_width (emblem),
548 gdk_pixbuf_get_width (avirt_pixbuf_open)),
549 MIN (gdk_pixbuf_get_height (emblem),
550 gdk_pixbuf_get_height (avirt_pixbuf_open)),
551 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
552 g_object_unref (emblem);
555 if (!avirt_pixbuf_close) {
556 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
557 MODEST_ICON_SIZE_SMALL);
558 avirt_pixbuf_close = gdk_pixbuf_copy (avirt_pixbuf);
559 gdk_pixbuf_composite (emblem, avirt_pixbuf_close, 0, 0,
560 MIN (gdk_pixbuf_get_width (emblem),
561 gdk_pixbuf_get_width (avirt_pixbuf_close)),
562 MIN (gdk_pixbuf_get_height (emblem),
563 gdk_pixbuf_get_height (avirt_pixbuf_close)),
564 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
565 g_object_unref (emblem);
569 pixbuf = g_object_ref (avirt_pixbuf);
570 pixbuf_open = g_object_ref (avirt_pixbuf_open);
571 pixbuf_close = g_object_ref (avirt_pixbuf_close);
575 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
577 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
579 ammc_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_MMC,
580 MODEST_ICON_SIZE_SMALL));
582 if (!ammc_pixbuf_open) {
583 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
584 MODEST_ICON_SIZE_SMALL);
585 ammc_pixbuf_open = gdk_pixbuf_copy (ammc_pixbuf);
586 gdk_pixbuf_composite (emblem, ammc_pixbuf_open, 0, 0,
587 MIN (gdk_pixbuf_get_width (emblem),
588 gdk_pixbuf_get_width (ammc_pixbuf_open)),
589 MIN (gdk_pixbuf_get_height (emblem),
590 gdk_pixbuf_get_height (ammc_pixbuf_open)),
591 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
592 g_object_unref (emblem);
595 if (!ammc_pixbuf_close) {
596 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
597 MODEST_ICON_SIZE_SMALL);
598 ammc_pixbuf_close = gdk_pixbuf_copy (ammc_pixbuf);
599 gdk_pixbuf_composite (emblem, ammc_pixbuf_close, 0, 0,
600 MIN (gdk_pixbuf_get_width (emblem),
601 gdk_pixbuf_get_width (ammc_pixbuf_close)),
602 MIN (gdk_pixbuf_get_height (emblem),
603 gdk_pixbuf_get_height (ammc_pixbuf_close)),
604 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
605 g_object_unref (emblem);
609 pixbuf = g_object_ref (ammc_pixbuf);
610 pixbuf_open = g_object_ref (ammc_pixbuf_open);
611 pixbuf_close = g_object_ref (ammc_pixbuf_close);
616 anorm_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT,
617 MODEST_ICON_SIZE_SMALL));
618 if (!anorm_pixbuf_open) {
619 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
620 MODEST_ICON_SIZE_SMALL);
621 anorm_pixbuf_open = gdk_pixbuf_copy (anorm_pixbuf);
622 gdk_pixbuf_composite (emblem, anorm_pixbuf_open, 0, 0,
623 MIN (gdk_pixbuf_get_width (emblem),
624 gdk_pixbuf_get_width (anorm_pixbuf_open)),
625 MIN (gdk_pixbuf_get_height (emblem),
626 gdk_pixbuf_get_height (anorm_pixbuf_open)),
627 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
628 g_object_unref (emblem);
631 if (!anorm_pixbuf_close) {
632 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
633 MODEST_ICON_SIZE_SMALL);
634 anorm_pixbuf_close = gdk_pixbuf_copy (anorm_pixbuf);
635 gdk_pixbuf_composite (emblem, anorm_pixbuf_close, 0, 0,
636 MIN (gdk_pixbuf_get_width (emblem),
637 gdk_pixbuf_get_width (anorm_pixbuf_close)),
638 MIN (gdk_pixbuf_get_height (emblem),
639 gdk_pixbuf_get_height (anorm_pixbuf_close)),
640 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
641 g_object_unref (emblem);
645 pixbuf = g_object_ref (anorm_pixbuf);
646 pixbuf_open = g_object_ref (anorm_pixbuf_open);
647 pixbuf_close = g_object_ref (anorm_pixbuf_close);
653 case TNY_FOLDER_TYPE_INBOX:
656 inbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX,
657 MODEST_ICON_SIZE_SMALL));
659 if (!inbox_pixbuf_open) {
660 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
661 MODEST_ICON_SIZE_SMALL);
662 inbox_pixbuf_open = gdk_pixbuf_copy (inbox_pixbuf);
663 gdk_pixbuf_composite (emblem, inbox_pixbuf_open, 0, 0,
664 MIN (gdk_pixbuf_get_width (emblem),
665 gdk_pixbuf_get_width (inbox_pixbuf_open)),
666 MIN (gdk_pixbuf_get_height (emblem),
667 gdk_pixbuf_get_height (inbox_pixbuf_open)),
668 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
669 g_object_unref (emblem);
672 if (!inbox_pixbuf_close) {
673 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
674 MODEST_ICON_SIZE_SMALL);
675 inbox_pixbuf_close = gdk_pixbuf_copy (inbox_pixbuf);
676 gdk_pixbuf_composite (emblem, inbox_pixbuf_close, 0, 0,
677 MIN (gdk_pixbuf_get_width (emblem),
678 gdk_pixbuf_get_width (inbox_pixbuf_close)),
679 MIN (gdk_pixbuf_get_height (emblem),
680 gdk_pixbuf_get_height (inbox_pixbuf_close)),
681 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
682 g_object_unref (emblem);
686 pixbuf = g_object_ref (inbox_pixbuf);
687 pixbuf_open = g_object_ref (inbox_pixbuf_open);
688 pixbuf_close = g_object_ref (inbox_pixbuf_close);
691 case TNY_FOLDER_TYPE_OUTBOX:
693 outbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX,
694 MODEST_ICON_SIZE_SMALL));
696 if (!outbox_pixbuf_open) {
697 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
698 MODEST_ICON_SIZE_SMALL);
699 outbox_pixbuf_open = gdk_pixbuf_copy (outbox_pixbuf);
700 gdk_pixbuf_composite (emblem, outbox_pixbuf_open, 0, 0,
701 MIN (gdk_pixbuf_get_width (emblem),
702 gdk_pixbuf_get_width (outbox_pixbuf_open)),
703 MIN (gdk_pixbuf_get_height (emblem),
704 gdk_pixbuf_get_height (outbox_pixbuf_open)),
705 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
706 g_object_unref (emblem);
709 if (!outbox_pixbuf_close) {
710 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
711 MODEST_ICON_SIZE_SMALL);
712 outbox_pixbuf_close = gdk_pixbuf_copy (outbox_pixbuf);
713 gdk_pixbuf_composite (emblem, outbox_pixbuf_close, 0, 0,
714 MIN (gdk_pixbuf_get_width (emblem),
715 gdk_pixbuf_get_width (outbox_pixbuf_close)),
716 MIN (gdk_pixbuf_get_height (emblem),
717 gdk_pixbuf_get_height (outbox_pixbuf_close)),
718 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
719 g_object_unref (emblem);
723 pixbuf = g_object_ref (outbox_pixbuf);
724 pixbuf_open = g_object_ref (outbox_pixbuf_open);
725 pixbuf_close = g_object_ref (outbox_pixbuf_close);
728 case TNY_FOLDER_TYPE_JUNK:
730 junk_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK,
731 MODEST_ICON_SIZE_SMALL));
732 if (!junk_pixbuf_open) {
733 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
734 MODEST_ICON_SIZE_SMALL);
735 junk_pixbuf_open = gdk_pixbuf_copy (junk_pixbuf);
736 gdk_pixbuf_composite (emblem, junk_pixbuf_open, 0, 0,
737 MIN (gdk_pixbuf_get_width (emblem),
738 gdk_pixbuf_get_width (junk_pixbuf_open)),
739 MIN (gdk_pixbuf_get_height (emblem),
740 gdk_pixbuf_get_height (junk_pixbuf_open)),
741 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
742 g_object_unref (emblem);
745 if (!junk_pixbuf_close) {
746 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
747 MODEST_ICON_SIZE_SMALL);
748 junk_pixbuf_close = gdk_pixbuf_copy (junk_pixbuf);
749 gdk_pixbuf_composite (emblem, junk_pixbuf_close, 0, 0,
750 MIN (gdk_pixbuf_get_width (emblem),
751 gdk_pixbuf_get_width (junk_pixbuf_close)),
752 MIN (gdk_pixbuf_get_height (emblem),
753 gdk_pixbuf_get_height (junk_pixbuf_close)),
754 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
755 g_object_unref (emblem);
759 pixbuf = g_object_ref (junk_pixbuf);
760 pixbuf_open = g_object_ref (junk_pixbuf_open);
761 pixbuf_close = g_object_ref (junk_pixbuf_close);
765 case TNY_FOLDER_TYPE_SENT:
767 sent_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_SENT,
768 MODEST_ICON_SIZE_SMALL));
770 if (!sent_pixbuf_open) {
771 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
772 MODEST_ICON_SIZE_SMALL);
773 sent_pixbuf_open = gdk_pixbuf_copy (sent_pixbuf);
774 gdk_pixbuf_composite (emblem, sent_pixbuf_open, 0, 0,
775 MIN (gdk_pixbuf_get_width (emblem),
776 gdk_pixbuf_get_width (sent_pixbuf_open)),
777 MIN (gdk_pixbuf_get_height (emblem),
778 gdk_pixbuf_get_height (sent_pixbuf_open)),
779 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
780 g_object_unref (emblem);
783 if (!sent_pixbuf_close) {
784 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
785 MODEST_ICON_SIZE_SMALL);
786 sent_pixbuf_close = gdk_pixbuf_copy (sent_pixbuf);
787 gdk_pixbuf_composite (emblem, sent_pixbuf_close, 0, 0,
788 MIN (gdk_pixbuf_get_width (emblem),
789 gdk_pixbuf_get_width (sent_pixbuf_close)),
790 MIN (gdk_pixbuf_get_height (emblem),
791 gdk_pixbuf_get_height (sent_pixbuf_close)),
792 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
793 g_object_unref (emblem);
797 pixbuf = g_object_ref (sent_pixbuf);
798 pixbuf_open = g_object_ref (sent_pixbuf_open);
799 pixbuf_close = g_object_ref (sent_pixbuf_close);
803 case TNY_FOLDER_TYPE_TRASH:
805 trash_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH,
806 MODEST_ICON_SIZE_SMALL));
807 if (!trash_pixbuf_open) {
808 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
809 MODEST_ICON_SIZE_SMALL);
810 trash_pixbuf_open = gdk_pixbuf_copy (trash_pixbuf);
811 gdk_pixbuf_composite (emblem, trash_pixbuf_open, 0, 0,
812 MIN (gdk_pixbuf_get_width (emblem),
813 gdk_pixbuf_get_width (trash_pixbuf_open)),
814 MIN (gdk_pixbuf_get_height (emblem),
815 gdk_pixbuf_get_height (trash_pixbuf_open)),
816 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
817 g_object_unref (emblem);
820 if (!trash_pixbuf_close) {
821 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
822 MODEST_ICON_SIZE_SMALL);
823 trash_pixbuf_close = gdk_pixbuf_copy (trash_pixbuf);
824 gdk_pixbuf_composite (emblem, trash_pixbuf_close, 0, 0,
825 MIN (gdk_pixbuf_get_width (emblem),
826 gdk_pixbuf_get_width (trash_pixbuf_close)),
827 MIN (gdk_pixbuf_get_height (emblem),
828 gdk_pixbuf_get_height (trash_pixbuf_close)),
829 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
830 g_object_unref (emblem);
834 pixbuf = g_object_ref (trash_pixbuf);
835 pixbuf_open = g_object_ref (trash_pixbuf_open);
836 pixbuf_close = g_object_ref (trash_pixbuf_close);
839 case TNY_FOLDER_TYPE_DRAFTS:
841 draft_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS,
842 MODEST_ICON_SIZE_SMALL));
844 if (!draft_pixbuf_open) {
845 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
846 MODEST_ICON_SIZE_SMALL);
847 draft_pixbuf_open = gdk_pixbuf_copy (draft_pixbuf);
848 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0,
849 MIN (gdk_pixbuf_get_width (emblem),
850 gdk_pixbuf_get_width (draft_pixbuf_open)),
851 MIN (gdk_pixbuf_get_height (emblem),
852 gdk_pixbuf_get_height (draft_pixbuf_open)),
853 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
854 g_object_unref (emblem);
857 if (!draft_pixbuf_close) {
858 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
859 MODEST_ICON_SIZE_SMALL);
860 draft_pixbuf_close = gdk_pixbuf_copy (draft_pixbuf);
861 gdk_pixbuf_composite (emblem, draft_pixbuf_close, 0, 0,
862 MIN (gdk_pixbuf_get_width (emblem),
863 gdk_pixbuf_get_width (draft_pixbuf_close)),
864 MIN (gdk_pixbuf_get_height (emblem),
865 gdk_pixbuf_get_height (draft_pixbuf_close)),
866 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
867 g_object_unref (emblem);
871 pixbuf = g_object_ref (draft_pixbuf);
872 pixbuf_open = g_object_ref (draft_pixbuf_open);
873 pixbuf_close = g_object_ref (draft_pixbuf_close);
876 case TNY_FOLDER_TYPE_NORMAL:
879 normal_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL,
880 MODEST_ICON_SIZE_SMALL));
882 if (!normal_pixbuf_open) {
883 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
884 MODEST_ICON_SIZE_SMALL);
885 normal_pixbuf_open = gdk_pixbuf_copy (normal_pixbuf);
886 gdk_pixbuf_composite (emblem, normal_pixbuf_open, 0, 0,
887 MIN (gdk_pixbuf_get_width (emblem),
888 gdk_pixbuf_get_width (normal_pixbuf_open)),
889 MIN (gdk_pixbuf_get_height (emblem),
890 gdk_pixbuf_get_height (normal_pixbuf_open)),
891 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
892 g_object_unref (emblem);
895 if (!normal_pixbuf_close) {
896 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
897 MODEST_ICON_SIZE_SMALL);
898 normal_pixbuf_close = gdk_pixbuf_copy (normal_pixbuf);
899 gdk_pixbuf_composite (emblem, normal_pixbuf_close, 0, 0,
900 MIN (gdk_pixbuf_get_width (emblem),
901 gdk_pixbuf_get_width (normal_pixbuf_close)),
902 MIN (gdk_pixbuf_get_height (emblem),
903 gdk_pixbuf_get_height (normal_pixbuf_close)),
904 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
905 g_object_unref (emblem);
909 pixbuf = g_object_ref (normal_pixbuf);
910 pixbuf_open = g_object_ref (normal_pixbuf_open);
911 pixbuf_close = g_object_ref (normal_pixbuf_close);
917 retval->pixbuf = pixbuf;
918 retval->pixbuf_open = pixbuf_open;
919 retval->pixbuf_close = pixbuf_close;
926 free_pixbufs (ThreePixbufs *pixbufs)
928 g_object_unref (pixbufs->pixbuf);
929 g_object_unref (pixbufs->pixbuf_open);
930 g_object_unref (pixbufs->pixbuf_close);
931 g_slice_free (ThreePixbufs, pixbufs);
935 icon_cell_data (GtkTreeViewColumn *column,
936 GtkCellRenderer *renderer,
937 GtkTreeModel *tree_model,
941 GObject *rendobj = NULL, *instance = NULL;
942 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
943 gboolean has_children;
944 ThreePixbufs *pixbufs;
946 rendobj = (GObject *) renderer;
948 gtk_tree_model_get (tree_model, iter,
949 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
950 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
956 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
957 pixbufs = get_folder_icons (type, instance);
958 g_object_unref (instance);
961 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
964 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
965 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
968 free_pixbufs (pixbufs);
974 add_columns (GtkWidget *treeview)
976 GtkTreeViewColumn *column;
977 GtkCellRenderer *renderer;
978 GtkTreeSelection *sel;
981 column = gtk_tree_view_column_new ();
983 /* Set icon and text render function */
984 renderer = gtk_cell_renderer_pixbuf_new();
985 gtk_tree_view_column_pack_start (column, renderer, FALSE);
986 gtk_tree_view_column_set_cell_data_func(column, renderer,
987 icon_cell_data, treeview, NULL);
989 renderer = gtk_cell_renderer_text_new();
990 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END,
991 "ellipsize-set", TRUE, NULL);
992 gtk_tree_view_column_pack_start (column, renderer, TRUE);
993 gtk_tree_view_column_set_cell_data_func(column, renderer,
994 text_cell_data, treeview, NULL);
996 /* Set selection mode */
997 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
998 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1000 /* Set treeview appearance */
1001 gtk_tree_view_column_set_spacing (column, 2);
1002 gtk_tree_view_column_set_resizable (column, TRUE);
1003 gtk_tree_view_column_set_fixed_width (column, TRUE);
1004 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1005 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1008 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1012 modest_folder_view_init (ModestFolderView *obj)
1014 ModestFolderViewPrivate *priv;
1017 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1019 priv->timer_expander = 0;
1020 priv->account_store = NULL;
1022 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1023 priv->cur_folder_store = NULL;
1024 priv->visible_account_id = NULL;
1025 priv->folder_to_select = NULL;
1027 priv->reexpand = TRUE;
1029 /* Initialize the local account name */
1030 conf = modest_runtime_get_conf();
1031 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1033 /* Init email clipboard */
1034 priv->clipboard = modest_runtime_get_email_clipboard ();
1035 priv->hidding_ids = NULL;
1036 priv->n_selected = 0;
1037 priv->reselect = FALSE;
1038 priv->show_non_move = TRUE;
1040 /* Build treeview */
1041 add_columns (GTK_WIDGET (obj));
1043 /* Setup drag and drop */
1044 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1046 /* Connect signals */
1047 g_signal_connect (G_OBJECT (obj),
1049 G_CALLBACK (on_key_pressed), NULL);
1051 priv->display_name_changed_signal =
1052 g_signal_connect (modest_runtime_get_account_mgr (),
1053 "display_name_changed",
1054 G_CALLBACK (on_display_name_changed),
1058 * Track changes in the local account name (in the device it
1059 * will be the device name)
1061 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1063 G_CALLBACK(on_configuration_key_changed),
1068 tny_account_store_view_init (gpointer g, gpointer iface_data)
1070 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1072 klass->set_account_store = modest_folder_view_set_account_store;
1076 modest_folder_view_finalize (GObject *obj)
1078 ModestFolderViewPrivate *priv;
1079 GtkTreeSelection *sel;
1081 g_return_if_fail (obj);
1083 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1085 if (priv->timer_expander != 0) {
1086 g_source_remove (priv->timer_expander);
1087 priv->timer_expander = 0;
1090 if (priv->account_store) {
1091 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1092 priv->account_inserted_signal);
1093 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1094 priv->account_removed_signal);
1095 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1096 priv->account_changed_signal);
1097 g_object_unref (G_OBJECT(priv->account_store));
1098 priv->account_store = NULL;
1102 g_object_unref (G_OBJECT (priv->query));
1106 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1107 if (priv->folder_to_select) {
1108 g_object_unref (G_OBJECT(priv->folder_to_select));
1109 priv->folder_to_select = NULL;
1112 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1114 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1116 g_free (priv->local_account_name);
1117 g_free (priv->visible_account_id);
1119 if (priv->conf_key_signal) {
1120 g_signal_handler_disconnect (modest_runtime_get_conf (),
1121 priv->conf_key_signal);
1122 priv->conf_key_signal = 0;
1125 if (priv->cur_folder_store) {
1126 if (TNY_IS_FOLDER(priv->cur_folder_store)) {
1127 ModestMailOperation *mail_op;
1129 mail_op = modest_mail_operation_new (NULL);
1130 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1132 modest_mail_operation_sync_folder (mail_op, TNY_FOLDER (priv->cur_folder_store), FALSE);
1135 g_object_unref (priv->cur_folder_store);
1136 priv->cur_folder_store = NULL;
1139 /* Clear hidding array created by cut operation */
1140 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1142 G_OBJECT_CLASS(parent_class)->finalize (obj);
1147 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1149 ModestFolderViewPrivate *priv;
1152 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1153 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1155 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1156 device = tny_account_store_get_device (account_store);
1158 if (G_UNLIKELY (priv->account_store)) {
1160 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1161 priv->account_inserted_signal))
1162 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1163 priv->account_inserted_signal);
1164 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1165 priv->account_removed_signal))
1166 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1167 priv->account_removed_signal);
1168 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1169 priv->account_changed_signal))
1170 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1171 priv->account_changed_signal);
1172 g_object_unref (G_OBJECT (priv->account_store));
1175 priv->account_store = g_object_ref (G_OBJECT (account_store));
1177 priv->account_removed_signal =
1178 g_signal_connect (G_OBJECT(account_store), "account_removed",
1179 G_CALLBACK (on_account_removed), self);
1181 priv->account_inserted_signal =
1182 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1183 G_CALLBACK (on_account_inserted), self);
1185 priv->account_changed_signal =
1186 g_signal_connect (G_OBJECT(account_store), "account_changed",
1187 G_CALLBACK (on_account_changed), self);
1189 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1191 g_object_unref (G_OBJECT (device));
1195 on_connection_status_changed (TnyAccount *self,
1196 TnyConnectionStatus status,
1199 /* If the account becomes online then refresh it */
1200 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1201 const gchar *acc_name;
1202 GtkWidget *my_window;
1204 my_window = gtk_widget_get_ancestor (GTK_WIDGET (user_data), MODEST_TYPE_WINDOW);
1205 acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (self);
1206 modest_ui_actions_do_send_receive (acc_name, FALSE, MODEST_WINDOW (my_window));
1211 on_account_inserted (TnyAccountStore *account_store,
1212 TnyAccount *account,
1215 ModestFolderViewPrivate *priv;
1216 GtkTreeModel *sort_model, *filter_model;
1218 /* Ignore transport account insertions, we're not showing them
1219 in the folder view */
1220 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1223 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1225 /* If we're adding a new account, and there is no previous
1226 one, we need to select the visible server account */
1227 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1228 !priv->visible_account_id)
1229 modest_widget_memory_restore (modest_runtime_get_conf(),
1230 G_OBJECT (user_data),
1231 MODEST_CONF_FOLDER_VIEW_KEY);
1233 /* Get the inner model */
1234 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1235 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1237 /* Insert the account in the model */
1238 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1239 G_OBJECT (account));
1242 /* When the store account gets online refresh it */
1243 g_signal_connect (account, "connection_status_changed",
1244 G_CALLBACK (on_connection_status_changed),
1245 MODEST_FOLDER_VIEW (user_data));
1247 /* Refilter the model */
1248 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1253 on_account_changed (TnyAccountStore *account_store,
1254 TnyAccount *tny_account,
1257 ModestFolderViewPrivate *priv;
1258 GtkTreeModel *sort_model, *filter_model;
1260 /* Ignore transport account insertions, we're not showing them
1261 in the folder view */
1262 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1265 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1267 /* Get the inner model */
1268 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1269 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1271 /* Remove the account from the model */
1272 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1273 G_OBJECT (tny_account));
1275 /* Insert the account in the model */
1276 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1277 G_OBJECT (tny_account));
1279 /* Refilter the model */
1280 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1285 * Selects the first inbox or the local account in an idle
1288 on_idle_select_first_inbox_or_local (gpointer user_data)
1290 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1292 modest_folder_view_select_first_inbox_or_local (self);
1299 on_account_removed (TnyAccountStore *account_store,
1300 TnyAccount *account,
1303 ModestFolderView *self = NULL;
1304 ModestFolderViewPrivate *priv;
1305 GtkTreeModel *sort_model, *filter_model;
1306 GtkTreeSelection *sel = NULL;
1307 gboolean same_account_selected = FALSE;
1309 /* Ignore transport account removals, we're not showing them
1310 in the folder view */
1311 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1314 self = MODEST_FOLDER_VIEW (user_data);
1315 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1317 /* Invalidate the cur_folder_store only if the selected folder
1318 belongs to the account that is being removed */
1319 if (priv->cur_folder_store) {
1320 TnyAccount *selected_folder_account = NULL;
1322 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1323 selected_folder_account =
1324 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1326 selected_folder_account =
1327 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1330 if (selected_folder_account == account) {
1331 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1332 gtk_tree_selection_unselect_all (sel);
1333 same_account_selected = TRUE;
1335 g_object_unref (selected_folder_account);
1338 /* Invalidate row to select only if the folder to select
1339 belongs to the account that is being removed*/
1340 if (priv->folder_to_select) {
1341 TnyAccount *folder_to_select_account = NULL;
1343 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1344 if (folder_to_select_account == account) {
1345 modest_folder_view_disable_next_folder_selection (self);
1346 g_object_unref (priv->folder_to_select);
1347 priv->folder_to_select = NULL;
1349 g_object_unref (folder_to_select_account);
1352 /* Remove the account from the model */
1353 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1354 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1355 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1356 G_OBJECT (account));
1358 /* If the removed account is the currently viewed one then
1359 clear the configuration value. The new visible account will be the default account */
1360 if (priv->visible_account_id &&
1361 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1363 /* Clear the current visible account_id */
1364 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1366 /* Call the restore method, this will set the new visible account */
1367 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1368 MODEST_CONF_FOLDER_VIEW_KEY);
1371 /* Refilter the model */
1372 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1374 /* Select the first INBOX if the currently selected folder
1375 belongs to the account that is being deleted */
1376 if (same_account_selected)
1377 g_idle_add (on_idle_select_first_inbox_or_local, self);
1381 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1383 GtkTreeViewColumn *col;
1385 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1387 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1389 g_printerr ("modest: failed get column for title\n");
1393 gtk_tree_view_column_set_title (col, title);
1394 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1399 modest_folder_view_on_map (ModestFolderView *self,
1400 GdkEventExpose *event,
1403 ModestFolderViewPrivate *priv;
1405 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1407 /* This won't happen often */
1408 if (G_UNLIKELY (priv->reselect)) {
1409 /* Select the first inbox or the local account if not found */
1411 /* TODO: this could cause a lock at startup, so we
1412 comment it for the moment. We know that this will
1413 be a bug, because the INBOX is not selected, but we
1414 need to rewrite some parts of Modest to avoid the
1415 deathlock situation */
1416 /* TODO: check if this is still the case */
1417 priv->reselect = FALSE;
1418 modest_folder_view_select_first_inbox_or_local (self);
1419 /* Notify the display name observers */
1420 g_signal_emit (G_OBJECT(self),
1421 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1425 if (priv->reexpand) {
1426 expand_root_items (self);
1427 priv->reexpand = FALSE;
1434 modest_folder_view_new (TnyFolderStoreQuery *query)
1437 ModestFolderViewPrivate *priv;
1438 GtkTreeSelection *sel;
1440 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1441 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1444 priv->query = g_object_ref (query);
1446 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1447 priv->changed_signal = g_signal_connect (sel, "changed",
1448 G_CALLBACK (on_selection_changed), self);
1450 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1452 return GTK_WIDGET(self);
1455 /* this feels dirty; any other way to expand all the root items? */
1457 expand_root_items (ModestFolderView *self)
1460 GtkTreeModel *model;
1463 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1464 path = gtk_tree_path_new_first ();
1466 /* all folders should have child items, so.. */
1468 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1469 gtk_tree_path_next (path);
1470 } while (gtk_tree_model_get_iter (model, &iter, path));
1472 gtk_tree_path_free (path);
1476 * We use this function to implement the
1477 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1478 * account in this case, and the local folders.
1481 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1483 ModestFolderViewPrivate *priv;
1484 gboolean retval = TRUE;
1485 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1486 GObject *instance = NULL;
1487 const gchar *id = NULL;
1489 gboolean found = FALSE;
1490 gboolean cleared = FALSE;
1492 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1493 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1495 gtk_tree_model_get (model, iter,
1496 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1497 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1500 /* Do not show if there is no instance, this could indeed
1501 happen when the model is being modified while it's being
1502 drawn. This could occur for example when moving folders
1507 if (type == TNY_FOLDER_TYPE_ROOT) {
1508 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1509 account instead of a folder. */
1510 if (TNY_IS_ACCOUNT (instance)) {
1511 TnyAccount *acc = TNY_ACCOUNT (instance);
1512 const gchar *account_id = tny_account_get_id (acc);
1514 /* If it isn't a special folder,
1515 * don't show it unless it is the visible account: */
1516 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1517 !modest_tny_account_is_virtual_local_folders (acc) &&
1518 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1520 /* Show only the visible account id */
1521 if (priv->visible_account_id) {
1522 if (strcmp (account_id, priv->visible_account_id))
1529 /* Never show these to the user. They are merged into one folder
1530 * in the local-folders account instead: */
1531 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1536 /* Check hiding (if necessary) */
1537 cleared = modest_email_clipboard_cleared (priv->clipboard);
1538 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1539 id = tny_folder_get_id (TNY_FOLDER(instance));
1540 if (priv->hidding_ids != NULL)
1541 for (i=0; i < priv->n_selected && !found; i++)
1542 if (priv->hidding_ids[i] != NULL && id != NULL)
1543 found = (!strcmp (priv->hidding_ids[i], id));
1549 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1550 folder as no message can be move there according to UI specs */
1551 if (!priv->show_non_move) {
1553 case TNY_FOLDER_TYPE_OUTBOX:
1554 case TNY_FOLDER_TYPE_SENT:
1555 case TNY_FOLDER_TYPE_DRAFTS:
1558 case TNY_FOLDER_TYPE_UNKNOWN:
1559 case TNY_FOLDER_TYPE_NORMAL:
1560 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1561 if (type == TNY_FOLDER_TYPE_INVALID)
1562 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1564 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1565 type == TNY_FOLDER_TYPE_SENT
1566 || type == TNY_FOLDER_TYPE_DRAFTS)
1575 g_object_unref (instance);
1582 modest_folder_view_update_model (ModestFolderView *self,
1583 TnyAccountStore *account_store)
1585 ModestFolderViewPrivate *priv;
1586 GtkTreeModel *model /* , *old_model */;
1587 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1589 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1590 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1593 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1595 /* Notify that there is no folder selected */
1596 g_signal_emit (G_OBJECT(self),
1597 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1599 if (priv->cur_folder_store) {
1600 g_object_unref (priv->cur_folder_store);
1601 priv->cur_folder_store = NULL;
1604 /* FIXME: the local accounts are not shown when the query
1605 selects only the subscribed folders */
1606 model = tny_gtk_folder_store_tree_model_new (NULL);
1608 /* Get the accounts: */
1609 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1611 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1613 sortable = gtk_tree_model_sort_new_with_model (model);
1614 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1615 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1616 GTK_SORT_ASCENDING);
1617 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1618 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1619 cmp_rows, NULL, NULL);
1621 /* Create filter model */
1622 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1623 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1629 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1630 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1631 (GCallback) on_row_inserted_maybe_select_folder, self);
1634 g_object_unref (model);
1635 g_object_unref (filter_model);
1636 g_object_unref (sortable);
1638 /* Force a reselection of the INBOX next time the widget is shown */
1639 priv->reselect = TRUE;
1646 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1648 GtkTreeModel *model = NULL;
1649 TnyFolderStore *folder = NULL;
1651 ModestFolderView *tree_view = NULL;
1652 ModestFolderViewPrivate *priv = NULL;
1653 gboolean selected = FALSE;
1655 g_return_if_fail (sel);
1656 g_return_if_fail (user_data);
1658 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1660 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1662 tree_view = MODEST_FOLDER_VIEW (user_data);
1665 gtk_tree_model_get (model, &iter,
1666 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1669 /* If the folder is the same do not notify */
1670 if (folder && priv->cur_folder_store == folder) {
1671 g_object_unref (folder);
1676 /* Current folder was unselected */
1677 if (priv->cur_folder_store) {
1678 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1679 priv->cur_folder_store, FALSE);
1681 if (TNY_IS_FOLDER(priv->cur_folder_store))
1682 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1683 FALSE, NULL, NULL, NULL);
1685 /* FALSE --> don't expunge the messages */
1687 g_object_unref (priv->cur_folder_store);
1688 priv->cur_folder_store = NULL;
1691 /* New current references */
1692 priv->cur_folder_store = folder;
1694 /* New folder has been selected. Do not notify if there is
1695 nothing new selected */
1697 g_signal_emit (G_OBJECT(tree_view),
1698 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1699 0, priv->cur_folder_store, TRUE);
1704 modest_folder_view_get_selected (ModestFolderView *self)
1706 ModestFolderViewPrivate *priv;
1708 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1710 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1711 if (priv->cur_folder_store)
1712 g_object_ref (priv->cur_folder_store);
1714 return priv->cur_folder_store;
1718 get_cmp_rows_type_pos (GObject *folder)
1720 /* Remote accounts -> Local account -> MMC account .*/
1723 if (TNY_IS_ACCOUNT (folder) &&
1724 modest_tny_account_is_virtual_local_folders (
1725 TNY_ACCOUNT (folder))) {
1727 } else if (TNY_IS_ACCOUNT (folder)) {
1728 TnyAccount *account = TNY_ACCOUNT (folder);
1729 const gchar *account_id = tny_account_get_id (account);
1730 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1736 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1737 return -1; /* Should never happen */
1742 get_cmp_subfolder_type_pos (TnyFolderType t)
1744 /* Inbox, Outbox, Drafts, Sent, User */
1748 case TNY_FOLDER_TYPE_INBOX:
1751 case TNY_FOLDER_TYPE_OUTBOX:
1754 case TNY_FOLDER_TYPE_DRAFTS:
1757 case TNY_FOLDER_TYPE_SENT:
1766 * This function orders the mail accounts according to these rules:
1767 * 1st - remote accounts
1768 * 2nd - local account
1772 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1776 gchar *name1 = NULL;
1777 gchar *name2 = NULL;
1778 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1779 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1780 GObject *folder1 = NULL;
1781 GObject *folder2 = NULL;
1783 gtk_tree_model_get (tree_model, iter1,
1784 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1785 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1786 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1788 gtk_tree_model_get (tree_model, iter2,
1789 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1790 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1791 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1794 /* Return if we get no folder. This could happen when folder
1795 operations are happening. The model is updated after the
1796 folder copy/move actually occurs, so there could be
1797 situations where the model to be drawn is not correct */
1798 if (!folder1 || !folder2)
1801 if (type == TNY_FOLDER_TYPE_ROOT) {
1802 /* Compare the types, so that
1803 * Remote accounts -> Local account -> MMC account .*/
1804 const gint pos1 = get_cmp_rows_type_pos (folder1);
1805 const gint pos2 = get_cmp_rows_type_pos (folder2);
1806 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1807 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1810 else if (pos1 > pos2)
1813 /* Compare items of the same type: */
1815 TnyAccount *account1 = NULL;
1816 if (TNY_IS_ACCOUNT (folder1))
1817 account1 = TNY_ACCOUNT (folder1);
1819 TnyAccount *account2 = NULL;
1820 if (TNY_IS_ACCOUNT (folder2))
1821 account2 = TNY_ACCOUNT (folder2);
1823 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1824 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1826 if (!account_id && !account_id2) {
1828 } else if (!account_id) {
1830 } else if (!account_id2) {
1832 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1835 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1839 gint cmp1 = 0, cmp2 = 0;
1840 /* get the parent to know if it's a local folder */
1843 gboolean has_parent;
1844 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1846 GObject *parent_folder;
1847 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1848 gtk_tree_model_get (tree_model, &parent,
1849 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1850 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1852 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1853 TNY_IS_ACCOUNT (parent_folder)) {
1854 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1855 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1856 (TNY_FOLDER (folder1)));
1857 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1858 (TNY_FOLDER (folder2)));
1859 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1860 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1863 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1869 g_object_unref (parent_folder);
1872 /* if they are not local folders */
1874 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1875 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1879 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1881 cmp = (cmp1 - cmp2);
1886 g_object_unref(G_OBJECT(folder1));
1888 g_object_unref(G_OBJECT(folder2));
1896 /*****************************************************************************/
1897 /* DRAG and DROP stuff */
1898 /*****************************************************************************/
1900 * This function fills the #GtkSelectionData with the row and the
1901 * model that has been dragged. It's called when this widget is a
1902 * source for dnd after the event drop happened
1905 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1906 guint info, guint time, gpointer data)
1908 GtkTreeSelection *selection;
1909 GtkTreeModel *model;
1911 GtkTreePath *source_row;
1913 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1914 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1916 source_row = gtk_tree_model_get_path (model, &iter);
1917 gtk_tree_set_row_drag_data (selection_data,
1921 gtk_tree_path_free (source_row);
1925 typedef struct _DndHelper {
1926 ModestFolderView *folder_view;
1927 gboolean delete_source;
1928 GtkTreePath *source_row;
1929 GdkDragContext *context;
1934 dnd_helper_destroyer (DndHelper *helper)
1936 /* Free the helper */
1937 g_object_unref (helper->folder_view);
1938 gtk_tree_path_free (helper->source_row);
1939 g_slice_free (DndHelper, helper);
1943 xfer_cb (ModestMailOperation *mail_op,
1949 helper = (DndHelper *) user_data;
1951 if (modest_mail_operation_get_status (mail_op) ==
1952 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1958 /* Notify the drag source. Never call delete, the monitor will
1959 do the job if needed */
1960 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1962 /* Free the helper */
1963 dnd_helper_destroyer (helper);
1967 xfer_msgs_cb (ModestMailOperation *mail_op,
1971 xfer_cb (mail_op, user_data);
1975 xfer_folder_cb (ModestMailOperation *mail_op,
1976 TnyFolder *new_folder,
1981 helper = (DndHelper *) user_data;
1984 xfer_cb (mail_op, user_data);
1986 /* Select the folder */
1988 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (helper->folder_view),
1993 /* get the folder for the row the treepath refers to. */
1994 /* folder must be unref'd */
1995 static TnyFolderStore *
1996 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1999 TnyFolderStore *folder = NULL;
2001 if (gtk_tree_model_get_iter (model,&iter, path))
2002 gtk_tree_model_get (model, &iter,
2003 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2009 * This function is used by drag_data_received_cb to manage drag and
2010 * drop of a header, i.e, and drag from the header view to the folder
2014 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2015 GtkTreeModel *dest_model,
2016 GtkTreePath *dest_row,
2017 GtkSelectionData *selection_data,
2020 TnyList *headers = NULL;
2021 TnyFolder *folder = NULL;
2022 TnyFolderType folder_type;
2023 ModestMailOperation *mail_op = NULL;
2024 GtkTreeIter source_iter, dest_iter;
2025 ModestWindowMgr *mgr = NULL;
2026 ModestWindow *main_win = NULL;
2027 gchar **uris, **tmp;
2030 /* Build the list of headers */
2031 mgr = modest_runtime_get_window_mgr ();
2032 headers = tny_simple_list_new ();
2033 uris = modest_dnd_selection_data_get_paths (selection_data);
2036 while (*tmp != NULL) {
2041 path = gtk_tree_path_new_from_string (*tmp);
2042 gtk_tree_model_get_iter (source_model, &source_iter, path);
2043 gtk_tree_model_get (source_model, &source_iter,
2044 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2047 /* Do not enable d&d of headers already opened */
2048 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2049 tny_list_append (headers, G_OBJECT (header));
2051 /* Free and go on */
2052 gtk_tree_path_free (path);
2053 g_object_unref (header);
2058 /* Get the target folder */
2059 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2060 gtk_tree_model_get (dest_model, &dest_iter,
2061 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2064 if (!folder || !TNY_IS_FOLDER(folder)) {
2065 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2069 folder_type = modest_tny_folder_guess_folder_type (folder);
2070 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2071 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2072 goto cleanup; /* cannot move messages there */
2075 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2076 /* g_warning ("folder not writable"); */
2077 goto cleanup; /* verboten! */
2080 /* Ask for confirmation to move */
2081 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2083 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2087 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2089 if (response == GTK_RESPONSE_CANCEL)
2092 /* Transfer messages */
2093 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2094 modest_ui_actions_move_folder_error_handler,
2097 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2100 modest_mail_operation_xfer_msgs (mail_op,
2103 helper->delete_source,
2104 xfer_msgs_cb, helper);
2108 if (G_IS_OBJECT(mail_op))
2109 g_object_unref (G_OBJECT (mail_op));
2110 if (G_IS_OBJECT(folder))
2111 g_object_unref (G_OBJECT (folder));
2112 if (G_IS_OBJECT(headers))
2113 g_object_unref (headers);
2117 TnyFolderStore *src_folder;
2118 TnyFolderStore *dst_folder;
2119 ModestFolderView *folder_view;
2124 dnd_folder_info_destroyer (DndFolderInfo *info)
2126 if (info->src_folder)
2127 g_object_unref (info->src_folder);
2128 if (info->dst_folder)
2129 g_object_unref (info->dst_folder);
2130 if (info->folder_view)
2131 g_object_unref (info->folder_view);
2132 g_slice_free (DndFolderInfo, info);
2136 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2137 GtkWindow *parent_window,
2138 TnyAccount *account)
2140 time_t dnd_time = info->helper->time;
2141 GdkDragContext *context = info->helper->context;
2144 modest_ui_actions_on_account_connection_error (parent_window, account);
2146 /* Free the helper & info */
2147 dnd_helper_destroyer (info->helper);
2148 dnd_folder_info_destroyer (info);
2150 /* Notify the drag source. Never call delete, the monitor will
2151 do the job if needed */
2152 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2157 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2159 GtkWindow *parent_window,
2160 TnyAccount *account,
2163 DndFolderInfo *info = NULL;
2164 ModestMailOperation *mail_op;
2166 info = (DndFolderInfo *) user_data;
2168 if (err || canceled) {
2169 dnd_on_connection_failed_destroyer (info, parent_window, account);
2173 /* Do the mail operation */
2174 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2175 modest_ui_actions_move_folder_error_handler,
2176 info->src_folder, NULL);
2178 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2181 /* Transfer the folder */
2182 modest_mail_operation_xfer_folder (mail_op,
2183 TNY_FOLDER (info->src_folder),
2185 info->helper->delete_source,
2189 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2190 /* TNY_FOLDER (info->dst_folder), TRUE); */
2192 g_object_unref (G_OBJECT (mail_op));
2197 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2199 GtkWindow *parent_window,
2200 TnyAccount *account,
2203 DndFolderInfo *info = NULL;
2205 info = (DndFolderInfo *) user_data;
2207 if (err || canceled) {
2208 dnd_on_connection_failed_destroyer (info, parent_window, account);
2212 /* Connect to source folder and perform the copy/move */
2213 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2215 drag_and_drop_from_folder_view_src_folder_performer,
2220 * This function is used by drag_data_received_cb to manage drag and
2221 * drop of a folder, i.e, and drag from the folder view to the same
2225 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2226 GtkTreeModel *dest_model,
2227 GtkTreePath *dest_row,
2228 GtkSelectionData *selection_data,
2231 GtkTreeIter dest_iter, iter;
2232 TnyFolderStore *dest_folder = NULL;
2233 TnyFolderStore *folder = NULL;
2234 gboolean forbidden = FALSE;
2236 DndFolderInfo *info = NULL;
2238 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2240 g_warning ("%s: BUG: no main window", __FUNCTION__);
2245 /* check the folder rules for the destination */
2246 folder = tree_path_to_folder (dest_model, dest_row);
2247 if (TNY_IS_FOLDER(folder)) {
2248 ModestTnyFolderRules rules =
2249 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2250 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2251 } else if (TNY_IS_FOLDER_STORE(folder)) {
2252 /* enable local root as destination for folders */
2253 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
2254 && TNY_IS_ACCOUNT (folder))
2257 g_object_unref (folder);
2260 /* check the folder rules for the source */
2261 folder = tree_path_to_folder (source_model, helper->source_row);
2262 if (TNY_IS_FOLDER(folder)) {
2263 ModestTnyFolderRules rules =
2264 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2265 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2268 g_object_unref (folder);
2272 /* Check if the drag is possible */
2273 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2274 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2275 gtk_tree_path_free (helper->source_row);
2276 g_slice_free (DndHelper, helper);
2281 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2282 gtk_tree_model_get (dest_model, &dest_iter,
2283 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2285 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2286 gtk_tree_model_get (source_model, &iter,
2287 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2290 /* Create the info for the performer */
2291 info = g_slice_new (DndFolderInfo);
2292 info->src_folder = g_object_ref (folder);
2293 info->dst_folder = g_object_ref (dest_folder);
2294 info->folder_view = g_object_ref (helper->folder_view);
2295 info->helper = helper;
2297 /* Connect to the destination folder and perform the copy/move */
2298 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2300 drag_and_drop_from_folder_view_dst_folder_performer,
2304 g_object_unref (dest_folder);
2305 g_object_unref (folder);
2309 * This function receives the data set by the "drag-data-get" signal
2310 * handler. This information comes within the #GtkSelectionData. This
2311 * function will manage both the drags of folders of the treeview and
2312 * drags of headers of the header view widget.
2315 on_drag_data_received (GtkWidget *widget,
2316 GdkDragContext *context,
2319 GtkSelectionData *selection_data,
2324 GtkWidget *source_widget;
2325 GtkTreeModel *dest_model, *source_model;
2326 GtkTreePath *source_row, *dest_row;
2327 GtkTreeViewDropPosition pos;
2328 gboolean success = FALSE, delete_source = FALSE;
2329 DndHelper *helper = NULL;
2331 /* Do not allow further process */
2332 g_signal_stop_emission_by_name (widget, "drag-data-received");
2333 source_widget = gtk_drag_get_source_widget (context);
2335 /* Get the action */
2336 if (context->action == GDK_ACTION_MOVE) {
2337 delete_source = TRUE;
2339 /* Notify that there is no folder selected. We need to
2340 do this in order to update the headers view (and
2341 its monitors, because when moving, the old folder
2342 won't longer exist. We can not wait for the end of
2343 the operation, because the operation won't start if
2344 the folder is in use */
2345 if (source_widget == widget) {
2346 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2347 gtk_tree_selection_unselect_all (sel);
2351 /* Check if the get_data failed */
2352 if (selection_data == NULL || selection_data->length < 0)
2353 gtk_drag_finish (context, success, FALSE, time);
2355 /* Select the destination model */
2356 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2358 /* Get the path to the destination row. Can not call
2359 gtk_tree_view_get_drag_dest_row() because the source row
2360 is not selected anymore */
2361 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2364 /* Only allow drops IN other rows */
2366 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2367 pos == GTK_TREE_VIEW_DROP_AFTER)
2368 gtk_drag_finish (context, success, FALSE, time);
2370 /* Create the helper */
2371 helper = g_slice_new0 (DndHelper);
2372 helper->delete_source = delete_source;
2373 helper->context = context;
2374 helper->time = time;
2375 helper->folder_view = g_object_ref (widget);
2377 /* Drags from the header view */
2378 if (source_widget != widget) {
2379 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2381 drag_and_drop_from_header_view (source_model,
2387 /* Get the source model and row */
2388 gtk_tree_get_row_drag_data (selection_data,
2391 helper->source_row = gtk_tree_path_copy (source_row);
2393 drag_and_drop_from_folder_view (source_model,
2399 gtk_tree_path_free (source_row);
2403 gtk_tree_path_free (dest_row);
2407 * We define a "drag-drop" signal handler because we do not want to
2408 * use the default one, because the default one always calls
2409 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2410 * signal handler, because there we have all the information available
2411 * to know if the dnd was a success or not.
2414 drag_drop_cb (GtkWidget *widget,
2415 GdkDragContext *context,
2423 if (!context->targets)
2426 /* Check if we're dragging a folder row */
2427 target = gtk_drag_dest_find_target (widget, context, NULL);
2429 /* Request the data from the source. */
2430 gtk_drag_get_data(widget, context, target, time);
2436 * This function expands a node of a tree view if it's not expanded
2437 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2438 * does that, so that's why they're here.
2441 expand_row_timeout (gpointer data)
2443 GtkTreeView *tree_view = data;
2444 GtkTreePath *dest_path = NULL;
2445 GtkTreeViewDropPosition pos;
2446 gboolean result = FALSE;
2448 gdk_threads_enter ();
2450 gtk_tree_view_get_drag_dest_row (tree_view,
2455 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2456 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2457 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2458 gtk_tree_path_free (dest_path);
2462 gtk_tree_path_free (dest_path);
2467 gdk_threads_leave ();
2473 * This function is called whenever the pointer is moved over a widget
2474 * while dragging some data. It installs a timeout that will expand a
2475 * node of the treeview if not expanded yet. This function also calls
2476 * gdk_drag_status in order to set the suggested action that will be
2477 * used by the "drag-data-received" signal handler to know if we
2478 * should do a move or just a copy of the data.
2481 on_drag_motion (GtkWidget *widget,
2482 GdkDragContext *context,
2488 GtkTreeViewDropPosition pos;
2489 GtkTreePath *dest_row;
2490 GtkTreeModel *dest_model;
2491 ModestFolderViewPrivate *priv;
2492 GdkDragAction suggested_action;
2493 gboolean valid_location = FALSE;
2494 TnyFolderStore *folder = NULL;
2496 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2498 if (priv->timer_expander != 0) {
2499 g_source_remove (priv->timer_expander);
2500 priv->timer_expander = 0;
2503 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2508 /* Do not allow drops between folders */
2510 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2511 pos == GTK_TREE_VIEW_DROP_AFTER) {
2512 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2513 gdk_drag_status(context, 0, time);
2514 valid_location = FALSE;
2517 valid_location = TRUE;
2520 /* Check that the destination folder is writable */
2521 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2522 folder = tree_path_to_folder (dest_model, dest_row);
2523 if (folder && TNY_IS_FOLDER (folder)) {
2524 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2526 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2527 valid_location = FALSE;
2532 /* Expand the selected row after 1/2 second */
2533 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2534 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2535 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2538 /* Select the desired action. By default we pick MOVE */
2539 suggested_action = GDK_ACTION_MOVE;
2541 if (context->actions == GDK_ACTION_COPY)
2542 gdk_drag_status(context, GDK_ACTION_COPY, time);
2543 else if (context->actions == GDK_ACTION_MOVE)
2544 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2545 else if (context->actions & suggested_action)
2546 gdk_drag_status(context, suggested_action, time);
2548 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2552 g_object_unref (folder);
2554 gtk_tree_path_free (dest_row);
2555 g_signal_stop_emission_by_name (widget, "drag-motion");
2557 return valid_location;
2561 * This function sets the treeview as a source and a target for dnd
2562 * events. It also connects all the requirede signals.
2565 setup_drag_and_drop (GtkTreeView *self)
2567 /* Set up the folder view as a dnd destination. Set only the
2568 highlight flag, otherwise gtk will have a different
2570 gtk_drag_dest_set (GTK_WIDGET (self),
2571 GTK_DEST_DEFAULT_HIGHLIGHT,
2572 folder_view_drag_types,
2573 G_N_ELEMENTS (folder_view_drag_types),
2574 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2576 g_signal_connect (G_OBJECT (self),
2577 "drag_data_received",
2578 G_CALLBACK (on_drag_data_received),
2582 /* Set up the treeview as a dnd source */
2583 gtk_drag_source_set (GTK_WIDGET (self),
2585 folder_view_drag_types,
2586 G_N_ELEMENTS (folder_view_drag_types),
2587 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2589 g_signal_connect (G_OBJECT (self),
2591 G_CALLBACK (on_drag_motion),
2594 g_signal_connect (G_OBJECT (self),
2596 G_CALLBACK (on_drag_data_get),
2599 g_signal_connect (G_OBJECT (self),
2601 G_CALLBACK (drag_drop_cb),
2606 * This function manages the navigation through the folders using the
2607 * keyboard or the hardware keys in the device
2610 on_key_pressed (GtkWidget *self,
2614 GtkTreeSelection *selection;
2616 GtkTreeModel *model;
2617 gboolean retval = FALSE;
2619 /* Up and Down are automatically managed by the treeview */
2620 if (event->keyval == GDK_Return) {
2621 /* Expand/Collapse the selected row */
2622 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2623 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2626 path = gtk_tree_model_get_path (model, &iter);
2628 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2629 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2631 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2632 gtk_tree_path_free (path);
2634 /* No further processing */
2642 * We listen to the changes in the local folder account name key,
2643 * because we want to show the right name in the view. The local
2644 * folder account name corresponds to the device name in the Maemo
2645 * version. We do this because we do not want to query gconf on each
2646 * tree view refresh. It's better to cache it and change whenever
2650 on_configuration_key_changed (ModestConf* conf,
2652 ModestConfEvent event,
2653 ModestConfNotificationId id,
2654 ModestFolderView *self)
2656 ModestFolderViewPrivate *priv;
2659 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2660 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2662 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2663 g_free (priv->local_account_name);
2665 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2666 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2668 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2669 MODEST_CONF_DEVICE_NAME, NULL);
2671 /* Force a redraw */
2672 #if GTK_CHECK_VERSION(2, 8, 0)
2673 GtkTreeViewColumn * tree_column;
2675 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2676 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2677 gtk_tree_view_column_queue_resize (tree_column);
2679 gtk_widget_queue_draw (GTK_WIDGET (self));
2685 modest_folder_view_set_style (ModestFolderView *self,
2686 ModestFolderViewStyle style)
2688 ModestFolderViewPrivate *priv;
2690 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2691 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2692 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2694 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2697 priv->style = style;
2701 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2702 const gchar *account_id)
2704 ModestFolderViewPrivate *priv;
2705 GtkTreeModel *model;
2707 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2709 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2711 /* This will be used by the filter_row callback,
2712 * to decided which rows to show: */
2713 if (priv->visible_account_id) {
2714 g_free (priv->visible_account_id);
2715 priv->visible_account_id = NULL;
2718 priv->visible_account_id = g_strdup (account_id);
2721 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2722 if (GTK_IS_TREE_MODEL_FILTER (model))
2723 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2725 /* Save settings to gconf */
2726 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2727 MODEST_CONF_FOLDER_VIEW_KEY);
2731 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2733 ModestFolderViewPrivate *priv;
2735 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2737 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2739 return (const gchar *) priv->visible_account_id;
2743 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2747 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2749 gtk_tree_model_get (model, iter,
2750 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2753 gboolean result = FALSE;
2754 if (type == TNY_FOLDER_TYPE_INBOX) {
2758 *inbox_iter = *iter;
2762 if (gtk_tree_model_iter_children (model, &child, iter)) {
2763 if (find_inbox_iter (model, &child, inbox_iter))
2767 } while (gtk_tree_model_iter_next (model, iter));
2776 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2778 GtkTreeModel *model;
2779 GtkTreeIter iter, inbox_iter;
2780 GtkTreeSelection *sel;
2781 GtkTreePath *path = NULL;
2783 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2785 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2789 expand_root_items (self);
2790 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2792 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2793 g_warning ("%s: model is empty", __FUNCTION__);
2797 if (find_inbox_iter (model, &iter, &inbox_iter))
2798 path = gtk_tree_model_get_path (model, &inbox_iter);
2800 path = gtk_tree_path_new_first ();
2802 /* Select the row and free */
2803 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2804 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2805 gtk_tree_path_free (path);
2808 gtk_widget_grab_focus (GTK_WIDGET(self));
2814 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2819 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2820 TnyFolder* a_folder;
2823 gtk_tree_model_get (model, iter,
2824 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2825 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2826 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2830 if (folder == a_folder) {
2831 g_object_unref (a_folder);
2832 *folder_iter = *iter;
2835 g_object_unref (a_folder);
2837 if (gtk_tree_model_iter_children (model, &child, iter)) {
2838 if (find_folder_iter (model, &child, folder_iter, folder))
2842 } while (gtk_tree_model_iter_next (model, iter));
2849 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2852 ModestFolderView *self)
2854 ModestFolderViewPrivate *priv = NULL;
2855 GtkTreeSelection *sel;
2856 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2857 GObject *instance = NULL;
2859 if (!MODEST_IS_FOLDER_VIEW(self))
2862 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2864 priv->reexpand = TRUE;
2866 gtk_tree_model_get (tree_model, iter,
2867 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2868 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2870 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2871 priv->folder_to_select = g_object_ref (instance);
2873 g_object_unref (instance);
2876 if (priv->folder_to_select) {
2878 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2881 path = gtk_tree_model_get_path (tree_model, iter);
2882 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2884 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2886 gtk_tree_selection_select_iter (sel, iter);
2887 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2889 gtk_tree_path_free (path);
2894 modest_folder_view_disable_next_folder_selection (self);
2896 /* Refilter the model */
2897 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2903 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2905 ModestFolderViewPrivate *priv;
2907 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2909 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2911 if (priv->folder_to_select)
2912 g_object_unref(priv->folder_to_select);
2914 priv->folder_to_select = NULL;
2918 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2919 gboolean after_change)
2921 GtkTreeModel *model;
2922 GtkTreeIter iter, folder_iter;
2923 GtkTreeSelection *sel;
2924 ModestFolderViewPrivate *priv = NULL;
2926 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2927 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2929 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2932 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2933 gtk_tree_selection_unselect_all (sel);
2935 if (priv->folder_to_select)
2936 g_object_unref(priv->folder_to_select);
2937 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2941 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2946 /* Refilter the model, before selecting the folder */
2947 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2949 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2950 g_warning ("%s: model is empty", __FUNCTION__);
2954 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2957 path = gtk_tree_model_get_path (model, &folder_iter);
2958 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2960 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2961 gtk_tree_selection_select_iter (sel, &folder_iter);
2962 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2964 gtk_tree_path_free (path);
2972 modest_folder_view_copy_selection (ModestFolderView *self)
2974 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2976 /* Copy selection */
2977 _clipboard_set_selected_data (self, FALSE);
2981 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2983 ModestFolderViewPrivate *priv = NULL;
2984 GtkTreeModel *model = NULL;
2985 const gchar **hidding = NULL;
2986 guint i, n_selected;
2988 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2989 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2991 /* Copy selection */
2992 if (!_clipboard_set_selected_data (folder_view, TRUE))
2995 /* Get hidding ids */
2996 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2998 /* Clear hidding array created by previous cut operation */
2999 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3001 /* Copy hidding array */
3002 priv->n_selected = n_selected;
3003 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3004 for (i=0; i < n_selected; i++)
3005 priv->hidding_ids[i] = g_strdup(hidding[i]);
3007 /* Hide cut folders */
3008 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3009 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3013 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3014 ModestFolderView *folder_view_dst)
3016 GtkTreeModel *filter_model = NULL;
3017 GtkTreeModel *model = NULL;
3018 GtkTreeModel *new_filter_model = NULL;
3020 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3021 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3024 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3025 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3027 /* Build new filter model */
3028 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3029 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3033 /* Set copied model */
3034 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3035 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3036 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3039 g_object_unref (new_filter_model);
3043 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3046 GtkTreeModel *model = NULL;
3047 ModestFolderViewPrivate* priv;
3049 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3051 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3052 priv->show_non_move = show;
3053 /* modest_folder_view_update_model(folder_view, */
3054 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3056 /* Hide special folders */
3057 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3058 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3059 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3063 /* Returns FALSE if it did not selected anything */
3065 _clipboard_set_selected_data (ModestFolderView *folder_view,
3068 ModestFolderViewPrivate *priv = NULL;
3069 TnyFolderStore *folder = NULL;
3070 gboolean retval = FALSE;
3072 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3073 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3075 /* Set selected data on clipboard */
3076 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3077 folder = modest_folder_view_get_selected (folder_view);
3079 /* Do not allow to select an account */
3080 if (TNY_IS_FOLDER (folder)) {
3081 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3086 g_object_unref (folder);
3092 _clear_hidding_filter (ModestFolderView *folder_view)
3094 ModestFolderViewPrivate *priv;
3097 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3098 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3100 if (priv->hidding_ids != NULL) {
3101 for (i=0; i < priv->n_selected; i++)
3102 g_free (priv->hidding_ids[i]);
3103 g_free(priv->hidding_ids);
3109 on_display_name_changed (ModestAccountMgr *mgr,
3110 const gchar *account,
3113 ModestFolderView *self;
3115 self = MODEST_FOLDER_VIEW (user_data);
3117 /* Force a redraw */
3118 #if GTK_CHECK_VERSION(2, 8, 0)
3119 GtkTreeViewColumn * tree_column;
3121 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3122 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3123 gtk_tree_view_column_queue_resize (tree_column);
3125 gtk_widget_queue_draw (GTK_WIDGET (self));