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_account_inserted (TnyAccountStore *account_store,
1196 TnyAccount *account,
1199 ModestFolderViewPrivate *priv;
1200 GtkTreeModel *sort_model, *filter_model;
1202 /* Ignore transport account insertions, we're not showing them
1203 in the folder view */
1204 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1207 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1209 /* If we're adding a new account, and there is no previous
1210 one, we need to select the visible server account */
1211 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1212 !priv->visible_account_id)
1213 modest_widget_memory_restore (modest_runtime_get_conf(),
1214 G_OBJECT (user_data),
1215 MODEST_CONF_FOLDER_VIEW_KEY);
1217 /* Get the inner model */
1218 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1219 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1221 /* Insert the account in the model */
1222 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1223 G_OBJECT (account));
1225 /* Refilter the model */
1226 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1231 on_account_changed (TnyAccountStore *account_store,
1232 TnyAccount *tny_account,
1235 ModestFolderViewPrivate *priv;
1236 GtkTreeModel *sort_model, *filter_model;
1238 /* Ignore transport account insertions, we're not showing them
1239 in the folder view */
1240 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1243 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1245 /* Get the inner model */
1246 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1247 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1249 /* Remove the account from the model */
1250 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1251 G_OBJECT (tny_account));
1253 /* Insert the account in the model */
1254 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1255 G_OBJECT (tny_account));
1257 /* Refilter the model */
1258 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1263 * Selects the first inbox or the local account in an idle
1266 on_idle_select_first_inbox_or_local (gpointer user_data)
1268 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1270 modest_folder_view_select_first_inbox_or_local (self);
1277 on_account_removed (TnyAccountStore *account_store,
1278 TnyAccount *account,
1281 ModestFolderView *self = NULL;
1282 ModestFolderViewPrivate *priv;
1283 GtkTreeModel *sort_model, *filter_model;
1284 GtkTreeSelection *sel = NULL;
1285 gboolean same_account_selected = FALSE;
1287 /* Ignore transport account removals, we're not showing them
1288 in the folder view */
1289 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1292 self = MODEST_FOLDER_VIEW (user_data);
1293 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1295 /* Invalidate the cur_folder_store only if the selected folder
1296 belongs to the account that is being removed */
1297 if (priv->cur_folder_store) {
1298 TnyAccount *selected_folder_account = NULL;
1300 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1301 selected_folder_account =
1302 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1304 selected_folder_account =
1305 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1308 if (selected_folder_account == account) {
1309 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1310 gtk_tree_selection_unselect_all (sel);
1311 same_account_selected = TRUE;
1313 g_object_unref (selected_folder_account);
1316 /* Invalidate row to select only if the folder to select
1317 belongs to the account that is being removed*/
1318 if (priv->folder_to_select) {
1319 TnyAccount *folder_to_select_account = NULL;
1321 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1322 if (folder_to_select_account == account) {
1323 modest_folder_view_disable_next_folder_selection (self);
1324 g_object_unref (priv->folder_to_select);
1325 priv->folder_to_select = NULL;
1327 g_object_unref (folder_to_select_account);
1330 /* Remove the account from the model */
1331 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1332 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1333 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1334 G_OBJECT (account));
1336 /* If the removed account is the currently viewed one then
1337 clear the configuration value. The new visible account will be the default account */
1338 if (priv->visible_account_id &&
1339 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1341 /* Clear the current visible account_id */
1342 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1344 /* Call the restore method, this will set the new visible account */
1345 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1346 MODEST_CONF_FOLDER_VIEW_KEY);
1349 /* Refilter the model */
1350 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1352 /* Select the first INBOX if the currently selected folder
1353 belongs to the account that is being deleted */
1354 if (same_account_selected)
1355 g_idle_add (on_idle_select_first_inbox_or_local, self);
1359 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1361 GtkTreeViewColumn *col;
1363 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1365 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1367 g_printerr ("modest: failed get column for title\n");
1371 gtk_tree_view_column_set_title (col, title);
1372 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1377 modest_folder_view_on_map (ModestFolderView *self,
1378 GdkEventExpose *event,
1381 ModestFolderViewPrivate *priv;
1383 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1385 /* This won't happen often */
1386 if (G_UNLIKELY (priv->reselect)) {
1387 /* Select the first inbox or the local account if not found */
1389 /* TODO: this could cause a lock at startup, so we
1390 comment it for the moment. We know that this will
1391 be a bug, because the INBOX is not selected, but we
1392 need to rewrite some parts of Modest to avoid the
1393 deathlock situation */
1394 /* TODO: check if this is still the case */
1395 priv->reselect = FALSE;
1396 modest_folder_view_select_first_inbox_or_local (self);
1397 /* Notify the display name observers */
1398 g_signal_emit (G_OBJECT(self),
1399 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1403 if (priv->reexpand) {
1404 expand_root_items (self);
1405 priv->reexpand = FALSE;
1412 modest_folder_view_new (TnyFolderStoreQuery *query)
1415 ModestFolderViewPrivate *priv;
1416 GtkTreeSelection *sel;
1418 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1419 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1422 priv->query = g_object_ref (query);
1424 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1425 priv->changed_signal = g_signal_connect (sel, "changed",
1426 G_CALLBACK (on_selection_changed), self);
1428 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1430 return GTK_WIDGET(self);
1433 /* this feels dirty; any other way to expand all the root items? */
1435 expand_root_items (ModestFolderView *self)
1438 GtkTreeModel *model;
1441 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1442 path = gtk_tree_path_new_first ();
1444 /* all folders should have child items, so.. */
1446 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1447 gtk_tree_path_next (path);
1448 } while (gtk_tree_model_get_iter (model, &iter, path));
1450 gtk_tree_path_free (path);
1454 * We use this function to implement the
1455 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1456 * account in this case, and the local folders.
1459 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1461 ModestFolderViewPrivate *priv;
1462 gboolean retval = TRUE;
1463 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1464 GObject *instance = NULL;
1465 const gchar *id = NULL;
1467 gboolean found = FALSE;
1468 gboolean cleared = FALSE;
1470 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1471 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1473 gtk_tree_model_get (model, iter,
1474 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1475 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1478 /* Do not show if there is no instance, this could indeed
1479 happen when the model is being modified while it's being
1480 drawn. This could occur for example when moving folders
1485 if (type == TNY_FOLDER_TYPE_ROOT) {
1486 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1487 account instead of a folder. */
1488 if (TNY_IS_ACCOUNT (instance)) {
1489 TnyAccount *acc = TNY_ACCOUNT (instance);
1490 const gchar *account_id = tny_account_get_id (acc);
1492 /* If it isn't a special folder,
1493 * don't show it unless it is the visible account: */
1494 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1495 !modest_tny_account_is_virtual_local_folders (acc) &&
1496 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1498 /* Show only the visible account id */
1499 if (priv->visible_account_id) {
1500 if (strcmp (account_id, priv->visible_account_id))
1507 /* Never show these to the user. They are merged into one folder
1508 * in the local-folders account instead: */
1509 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1514 /* Check hiding (if necessary) */
1515 cleared = modest_email_clipboard_cleared (priv->clipboard);
1516 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1517 id = tny_folder_get_id (TNY_FOLDER(instance));
1518 if (priv->hidding_ids != NULL)
1519 for (i=0; i < priv->n_selected && !found; i++)
1520 if (priv->hidding_ids[i] != NULL && id != NULL)
1521 found = (!strcmp (priv->hidding_ids[i], id));
1527 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1528 folder as no message can be move there according to UI specs */
1529 if (!priv->show_non_move) {
1531 case TNY_FOLDER_TYPE_OUTBOX:
1532 case TNY_FOLDER_TYPE_SENT:
1533 case TNY_FOLDER_TYPE_DRAFTS:
1536 case TNY_FOLDER_TYPE_UNKNOWN:
1537 case TNY_FOLDER_TYPE_NORMAL:
1538 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1539 if (type == TNY_FOLDER_TYPE_INVALID)
1540 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1542 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1543 type == TNY_FOLDER_TYPE_SENT
1544 || type == TNY_FOLDER_TYPE_DRAFTS)
1553 g_object_unref (instance);
1560 modest_folder_view_update_model (ModestFolderView *self,
1561 TnyAccountStore *account_store)
1563 ModestFolderViewPrivate *priv;
1564 GtkTreeModel *model /* , *old_model */;
1565 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1567 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1568 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1571 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1573 /* Notify that there is no folder selected */
1574 g_signal_emit (G_OBJECT(self),
1575 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1577 if (priv->cur_folder_store) {
1578 g_object_unref (priv->cur_folder_store);
1579 priv->cur_folder_store = NULL;
1582 /* FIXME: the local accounts are not shown when the query
1583 selects only the subscribed folders */
1584 model = tny_gtk_folder_store_tree_model_new (NULL);
1586 /* Get the accounts: */
1587 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1589 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1591 sortable = gtk_tree_model_sort_new_with_model (model);
1592 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1593 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1594 GTK_SORT_ASCENDING);
1595 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1596 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1597 cmp_rows, NULL, NULL);
1599 /* Create filter model */
1600 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1601 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1607 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1608 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1609 (GCallback) on_row_inserted_maybe_select_folder, self);
1612 g_object_unref (model);
1613 g_object_unref (filter_model);
1614 g_object_unref (sortable);
1616 /* Force a reselection of the INBOX next time the widget is shown */
1617 priv->reselect = TRUE;
1624 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1626 GtkTreeModel *model = NULL;
1627 TnyFolderStore *folder = NULL;
1629 ModestFolderView *tree_view = NULL;
1630 ModestFolderViewPrivate *priv = NULL;
1631 gboolean selected = FALSE;
1633 g_return_if_fail (sel);
1634 g_return_if_fail (user_data);
1636 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1638 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1640 tree_view = MODEST_FOLDER_VIEW (user_data);
1643 gtk_tree_model_get (model, &iter,
1644 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1647 /* If the folder is the same do not notify */
1648 if (folder && priv->cur_folder_store == folder) {
1649 g_object_unref (folder);
1654 /* Current folder was unselected */
1655 if (priv->cur_folder_store) {
1656 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1657 priv->cur_folder_store, FALSE);
1659 if (TNY_IS_FOLDER(priv->cur_folder_store))
1660 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1661 FALSE, NULL, NULL, NULL);
1663 /* FALSE --> don't expunge the messages */
1665 g_object_unref (priv->cur_folder_store);
1666 priv->cur_folder_store = NULL;
1669 /* New current references */
1670 priv->cur_folder_store = folder;
1672 /* New folder has been selected. Do not notify if there is
1673 nothing new selected */
1675 g_signal_emit (G_OBJECT(tree_view),
1676 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1677 0, priv->cur_folder_store, TRUE);
1682 modest_folder_view_get_selected (ModestFolderView *self)
1684 ModestFolderViewPrivate *priv;
1686 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1688 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1689 if (priv->cur_folder_store)
1690 g_object_ref (priv->cur_folder_store);
1692 return priv->cur_folder_store;
1696 get_cmp_rows_type_pos (GObject *folder)
1698 /* Remote accounts -> Local account -> MMC account .*/
1701 if (TNY_IS_ACCOUNT (folder) &&
1702 modest_tny_account_is_virtual_local_folders (
1703 TNY_ACCOUNT (folder))) {
1705 } else if (TNY_IS_ACCOUNT (folder)) {
1706 TnyAccount *account = TNY_ACCOUNT (folder);
1707 const gchar *account_id = tny_account_get_id (account);
1708 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1714 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1715 return -1; /* Should never happen */
1720 get_cmp_subfolder_type_pos (TnyFolderType t)
1722 /* Inbox, Outbox, Drafts, Sent, User */
1726 case TNY_FOLDER_TYPE_INBOX:
1729 case TNY_FOLDER_TYPE_OUTBOX:
1732 case TNY_FOLDER_TYPE_DRAFTS:
1735 case TNY_FOLDER_TYPE_SENT:
1744 * This function orders the mail accounts according to these rules:
1745 * 1st - remote accounts
1746 * 2nd - local account
1750 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1754 gchar *name1 = NULL;
1755 gchar *name2 = NULL;
1756 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1757 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1758 GObject *folder1 = NULL;
1759 GObject *folder2 = NULL;
1761 gtk_tree_model_get (tree_model, iter1,
1762 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1763 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1764 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1766 gtk_tree_model_get (tree_model, iter2,
1767 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1768 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1769 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1772 /* Return if we get no folder. This could happen when folder
1773 operations are happening. The model is updated after the
1774 folder copy/move actually occurs, so there could be
1775 situations where the model to be drawn is not correct */
1776 if (!folder1 || !folder2)
1779 if (type == TNY_FOLDER_TYPE_ROOT) {
1780 /* Compare the types, so that
1781 * Remote accounts -> Local account -> MMC account .*/
1782 const gint pos1 = get_cmp_rows_type_pos (folder1);
1783 const gint pos2 = get_cmp_rows_type_pos (folder2);
1784 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1785 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1788 else if (pos1 > pos2)
1791 /* Compare items of the same type: */
1793 TnyAccount *account1 = NULL;
1794 if (TNY_IS_ACCOUNT (folder1))
1795 account1 = TNY_ACCOUNT (folder1);
1797 TnyAccount *account2 = NULL;
1798 if (TNY_IS_ACCOUNT (folder2))
1799 account2 = TNY_ACCOUNT (folder2);
1801 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1802 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1804 if (!account_id && !account_id2) {
1806 } else if (!account_id) {
1808 } else if (!account_id2) {
1810 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1813 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1817 gint cmp1 = 0, cmp2 = 0;
1818 /* get the parent to know if it's a local folder */
1821 gboolean has_parent;
1822 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1824 GObject *parent_folder;
1825 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1826 gtk_tree_model_get (tree_model, &parent,
1827 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1828 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1830 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1831 TNY_IS_ACCOUNT (parent_folder)) {
1832 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1833 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1834 (TNY_FOLDER (folder1)));
1835 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1836 (TNY_FOLDER (folder2)));
1837 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1838 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1841 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1847 g_object_unref (parent_folder);
1850 /* if they are not local folders */
1852 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1853 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1857 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1859 cmp = (cmp1 - cmp2);
1864 g_object_unref(G_OBJECT(folder1));
1866 g_object_unref(G_OBJECT(folder2));
1874 /*****************************************************************************/
1875 /* DRAG and DROP stuff */
1876 /*****************************************************************************/
1878 * This function fills the #GtkSelectionData with the row and the
1879 * model that has been dragged. It's called when this widget is a
1880 * source for dnd after the event drop happened
1883 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1884 guint info, guint time, gpointer data)
1886 GtkTreeSelection *selection;
1887 GtkTreeModel *model;
1889 GtkTreePath *source_row;
1891 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1892 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1894 source_row = gtk_tree_model_get_path (model, &iter);
1895 gtk_tree_set_row_drag_data (selection_data,
1899 gtk_tree_path_free (source_row);
1903 typedef struct _DndHelper {
1904 ModestFolderView *folder_view;
1905 gboolean delete_source;
1906 GtkTreePath *source_row;
1907 GdkDragContext *context;
1912 dnd_helper_destroyer (DndHelper *helper)
1914 /* Free the helper */
1915 g_object_unref (helper->folder_view);
1916 gtk_tree_path_free (helper->source_row);
1917 g_slice_free (DndHelper, helper);
1921 xfer_cb (ModestMailOperation *mail_op,
1927 helper = (DndHelper *) user_data;
1929 if (modest_mail_operation_get_status (mail_op) ==
1930 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1936 /* Notify the drag source. Never call delete, the monitor will
1937 do the job if needed */
1938 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1940 /* Free the helper */
1941 dnd_helper_destroyer (helper);
1945 xfer_msgs_cb (ModestMailOperation *mail_op,
1949 xfer_cb (mail_op, user_data);
1953 xfer_folder_cb (ModestMailOperation *mail_op,
1954 TnyFolder *new_folder,
1958 GtkWidget *folder_view;
1960 helper = (DndHelper *) user_data;
1961 folder_view = g_object_ref (helper->folder_view);
1964 xfer_cb (mail_op, user_data);
1966 /* Select the folder */
1968 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1970 g_object_unref (folder_view);
1974 /* get the folder for the row the treepath refers to. */
1975 /* folder must be unref'd */
1976 static TnyFolderStore *
1977 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1980 TnyFolderStore *folder = NULL;
1982 if (gtk_tree_model_get_iter (model,&iter, path))
1983 gtk_tree_model_get (model, &iter,
1984 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1990 * This function is used by drag_data_received_cb to manage drag and
1991 * drop of a header, i.e, and drag from the header view to the folder
1995 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1996 GtkTreeModel *dest_model,
1997 GtkTreePath *dest_row,
1998 GtkSelectionData *selection_data,
2001 TnyList *headers = NULL;
2002 TnyFolder *folder = NULL;
2003 TnyFolderType folder_type;
2004 ModestMailOperation *mail_op = NULL;
2005 GtkTreeIter source_iter, dest_iter;
2006 ModestWindowMgr *mgr = NULL;
2007 ModestWindow *main_win = NULL;
2008 gchar **uris, **tmp;
2011 /* Build the list of headers */
2012 mgr = modest_runtime_get_window_mgr ();
2013 headers = tny_simple_list_new ();
2014 uris = modest_dnd_selection_data_get_paths (selection_data);
2017 while (*tmp != NULL) {
2022 path = gtk_tree_path_new_from_string (*tmp);
2023 gtk_tree_model_get_iter (source_model, &source_iter, path);
2024 gtk_tree_model_get (source_model, &source_iter,
2025 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2028 /* Do not enable d&d of headers already opened */
2029 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2030 tny_list_append (headers, G_OBJECT (header));
2032 /* Free and go on */
2033 gtk_tree_path_free (path);
2034 g_object_unref (header);
2039 /* Get the target folder */
2040 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2041 gtk_tree_model_get (dest_model, &dest_iter,
2042 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2045 if (!folder || !TNY_IS_FOLDER(folder)) {
2046 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2050 folder_type = modest_tny_folder_guess_folder_type (folder);
2051 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2052 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2053 goto cleanup; /* cannot move messages there */
2056 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2057 /* g_warning ("folder not writable"); */
2058 goto cleanup; /* verboten! */
2061 /* Ask for confirmation to move */
2062 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2064 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2068 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2070 if (response == GTK_RESPONSE_CANCEL)
2073 /* Transfer messages */
2074 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2075 modest_ui_actions_move_folder_error_handler,
2078 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2081 modest_mail_operation_xfer_msgs (mail_op,
2084 helper->delete_source,
2085 xfer_msgs_cb, helper);
2089 if (G_IS_OBJECT(mail_op))
2090 g_object_unref (G_OBJECT (mail_op));
2091 if (G_IS_OBJECT(folder))
2092 g_object_unref (G_OBJECT (folder));
2093 if (G_IS_OBJECT(headers))
2094 g_object_unref (headers);
2098 TnyFolderStore *src_folder;
2099 TnyFolderStore *dst_folder;
2100 ModestFolderView *folder_view;
2105 dnd_folder_info_destroyer (DndFolderInfo *info)
2107 if (info->src_folder)
2108 g_object_unref (info->src_folder);
2109 if (info->dst_folder)
2110 g_object_unref (info->dst_folder);
2111 if (info->folder_view)
2112 g_object_unref (info->folder_view);
2113 g_slice_free (DndFolderInfo, info);
2117 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2118 GtkWindow *parent_window,
2119 TnyAccount *account)
2121 time_t dnd_time = info->helper->time;
2122 GdkDragContext *context = info->helper->context;
2125 modest_ui_actions_on_account_connection_error (parent_window, account);
2127 /* Free the helper & info */
2128 dnd_helper_destroyer (info->helper);
2129 dnd_folder_info_destroyer (info);
2131 /* Notify the drag source. Never call delete, the monitor will
2132 do the job if needed */
2133 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2138 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2140 GtkWindow *parent_window,
2141 TnyAccount *account,
2144 DndFolderInfo *info = NULL;
2145 ModestMailOperation *mail_op;
2147 info = (DndFolderInfo *) user_data;
2149 if (err || canceled) {
2150 dnd_on_connection_failed_destroyer (info, parent_window, account);
2154 /* Do the mail operation */
2155 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2156 modest_ui_actions_move_folder_error_handler,
2157 info->src_folder, NULL);
2159 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2162 /* Transfer the folder */
2163 modest_mail_operation_xfer_folder (mail_op,
2164 TNY_FOLDER (info->src_folder),
2166 info->helper->delete_source,
2170 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2171 /* TNY_FOLDER (info->dst_folder), TRUE); */
2173 g_object_unref (G_OBJECT (mail_op));
2178 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2180 GtkWindow *parent_window,
2181 TnyAccount *account,
2184 DndFolderInfo *info = NULL;
2186 info = (DndFolderInfo *) user_data;
2188 if (err || canceled) {
2189 dnd_on_connection_failed_destroyer (info, parent_window, account);
2193 /* Connect to source folder and perform the copy/move */
2194 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2196 drag_and_drop_from_folder_view_src_folder_performer,
2201 * This function is used by drag_data_received_cb to manage drag and
2202 * drop of a folder, i.e, and drag from the folder view to the same
2206 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2207 GtkTreeModel *dest_model,
2208 GtkTreePath *dest_row,
2209 GtkSelectionData *selection_data,
2212 GtkTreeIter dest_iter, iter;
2213 TnyFolderStore *dest_folder = NULL;
2214 TnyFolderStore *folder = NULL;
2215 gboolean forbidden = FALSE;
2217 DndFolderInfo *info = NULL;
2219 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2221 g_warning ("%s: BUG: no main window", __FUNCTION__);
2226 /* check the folder rules for the destination */
2227 folder = tree_path_to_folder (dest_model, dest_row);
2228 if (TNY_IS_FOLDER(folder)) {
2229 ModestTnyFolderRules rules =
2230 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2231 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2232 } else if (TNY_IS_FOLDER_STORE(folder)) {
2233 /* enable local root as destination for folders */
2234 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2235 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2238 g_object_unref (folder);
2241 /* check the folder rules for the source */
2242 folder = tree_path_to_folder (source_model, helper->source_row);
2243 if (TNY_IS_FOLDER(folder)) {
2244 ModestTnyFolderRules rules =
2245 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2246 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2249 g_object_unref (folder);
2253 /* Check if the drag is possible */
2254 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2255 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2256 gtk_tree_path_free (helper->source_row);
2257 g_slice_free (DndHelper, helper);
2262 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2263 gtk_tree_model_get (dest_model, &dest_iter,
2264 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2266 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2267 gtk_tree_model_get (source_model, &iter,
2268 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2271 /* Create the info for the performer */
2272 info = g_slice_new (DndFolderInfo);
2273 info->src_folder = g_object_ref (folder);
2274 info->dst_folder = g_object_ref (dest_folder);
2275 info->folder_view = g_object_ref (helper->folder_view);
2276 info->helper = helper;
2278 /* Connect to the destination folder and perform the copy/move */
2279 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2281 drag_and_drop_from_folder_view_dst_folder_performer,
2285 g_object_unref (dest_folder);
2286 g_object_unref (folder);
2290 * This function receives the data set by the "drag-data-get" signal
2291 * handler. This information comes within the #GtkSelectionData. This
2292 * function will manage both the drags of folders of the treeview and
2293 * drags of headers of the header view widget.
2296 on_drag_data_received (GtkWidget *widget,
2297 GdkDragContext *context,
2300 GtkSelectionData *selection_data,
2305 GtkWidget *source_widget;
2306 GtkTreeModel *dest_model, *source_model;
2307 GtkTreePath *source_row, *dest_row;
2308 GtkTreeViewDropPosition pos;
2309 gboolean success = FALSE, delete_source = FALSE;
2310 DndHelper *helper = NULL;
2312 /* Do not allow further process */
2313 g_signal_stop_emission_by_name (widget, "drag-data-received");
2314 source_widget = gtk_drag_get_source_widget (context);
2316 /* Get the action */
2317 if (context->action == GDK_ACTION_MOVE) {
2318 delete_source = TRUE;
2320 /* Notify that there is no folder selected. We need to
2321 do this in order to update the headers view (and
2322 its monitors, because when moving, the old folder
2323 won't longer exist. We can not wait for the end of
2324 the operation, because the operation won't start if
2325 the folder is in use */
2326 if (source_widget == widget) {
2327 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2328 gtk_tree_selection_unselect_all (sel);
2332 /* Check if the get_data failed */
2333 if (selection_data == NULL || selection_data->length < 0)
2334 gtk_drag_finish (context, success, FALSE, time);
2336 /* Select the destination model */
2337 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2339 /* Get the path to the destination row. Can not call
2340 gtk_tree_view_get_drag_dest_row() because the source row
2341 is not selected anymore */
2342 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2345 /* Only allow drops IN other rows */
2347 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2348 pos == GTK_TREE_VIEW_DROP_AFTER)
2349 gtk_drag_finish (context, success, FALSE, time);
2351 /* Create the helper */
2352 helper = g_slice_new0 (DndHelper);
2353 helper->delete_source = delete_source;
2354 helper->context = context;
2355 helper->time = time;
2356 helper->folder_view = g_object_ref (widget);
2358 /* Drags from the header view */
2359 if (source_widget != widget) {
2360 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2362 drag_and_drop_from_header_view (source_model,
2368 /* Get the source model and row */
2369 gtk_tree_get_row_drag_data (selection_data,
2372 helper->source_row = gtk_tree_path_copy (source_row);
2374 drag_and_drop_from_folder_view (source_model,
2380 gtk_tree_path_free (source_row);
2384 gtk_tree_path_free (dest_row);
2388 * We define a "drag-drop" signal handler because we do not want to
2389 * use the default one, because the default one always calls
2390 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2391 * signal handler, because there we have all the information available
2392 * to know if the dnd was a success or not.
2395 drag_drop_cb (GtkWidget *widget,
2396 GdkDragContext *context,
2404 if (!context->targets)
2407 /* Check if we're dragging a folder row */
2408 target = gtk_drag_dest_find_target (widget, context, NULL);
2410 /* Request the data from the source. */
2411 gtk_drag_get_data(widget, context, target, time);
2417 * This function expands a node of a tree view if it's not expanded
2418 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2419 * does that, so that's why they're here.
2422 expand_row_timeout (gpointer data)
2424 GtkTreeView *tree_view = data;
2425 GtkTreePath *dest_path = NULL;
2426 GtkTreeViewDropPosition pos;
2427 gboolean result = FALSE;
2429 gdk_threads_enter ();
2431 gtk_tree_view_get_drag_dest_row (tree_view,
2436 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2437 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2438 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2439 gtk_tree_path_free (dest_path);
2443 gtk_tree_path_free (dest_path);
2448 gdk_threads_leave ();
2454 * This function is called whenever the pointer is moved over a widget
2455 * while dragging some data. It installs a timeout that will expand a
2456 * node of the treeview if not expanded yet. This function also calls
2457 * gdk_drag_status in order to set the suggested action that will be
2458 * used by the "drag-data-received" signal handler to know if we
2459 * should do a move or just a copy of the data.
2462 on_drag_motion (GtkWidget *widget,
2463 GdkDragContext *context,
2469 GtkTreeViewDropPosition pos;
2470 GtkTreePath *dest_row;
2471 GtkTreeModel *dest_model;
2472 ModestFolderViewPrivate *priv;
2473 GdkDragAction suggested_action;
2474 gboolean valid_location = FALSE;
2475 TnyFolderStore *folder = NULL;
2477 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2479 if (priv->timer_expander != 0) {
2480 g_source_remove (priv->timer_expander);
2481 priv->timer_expander = 0;
2484 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2489 /* Do not allow drops between folders */
2491 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2492 pos == GTK_TREE_VIEW_DROP_AFTER) {
2493 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2494 gdk_drag_status(context, 0, time);
2495 valid_location = FALSE;
2498 valid_location = TRUE;
2501 /* Check that the destination folder is writable */
2502 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2503 folder = tree_path_to_folder (dest_model, dest_row);
2504 if (folder && TNY_IS_FOLDER (folder)) {
2505 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2507 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2508 valid_location = FALSE;
2513 /* Expand the selected row after 1/2 second */
2514 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2515 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2516 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2519 /* Select the desired action. By default we pick MOVE */
2520 suggested_action = GDK_ACTION_MOVE;
2522 if (context->actions == GDK_ACTION_COPY)
2523 gdk_drag_status(context, GDK_ACTION_COPY, time);
2524 else if (context->actions == GDK_ACTION_MOVE)
2525 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2526 else if (context->actions & suggested_action)
2527 gdk_drag_status(context, suggested_action, time);
2529 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2533 g_object_unref (folder);
2535 gtk_tree_path_free (dest_row);
2536 g_signal_stop_emission_by_name (widget, "drag-motion");
2538 return valid_location;
2542 * This function sets the treeview as a source and a target for dnd
2543 * events. It also connects all the requirede signals.
2546 setup_drag_and_drop (GtkTreeView *self)
2548 /* Set up the folder view as a dnd destination. Set only the
2549 highlight flag, otherwise gtk will have a different
2551 gtk_drag_dest_set (GTK_WIDGET (self),
2552 GTK_DEST_DEFAULT_HIGHLIGHT,
2553 folder_view_drag_types,
2554 G_N_ELEMENTS (folder_view_drag_types),
2555 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2557 g_signal_connect (G_OBJECT (self),
2558 "drag_data_received",
2559 G_CALLBACK (on_drag_data_received),
2563 /* Set up the treeview as a dnd source */
2564 gtk_drag_source_set (GTK_WIDGET (self),
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),
2572 G_CALLBACK (on_drag_motion),
2575 g_signal_connect (G_OBJECT (self),
2577 G_CALLBACK (on_drag_data_get),
2580 g_signal_connect (G_OBJECT (self),
2582 G_CALLBACK (drag_drop_cb),
2587 * This function manages the navigation through the folders using the
2588 * keyboard or the hardware keys in the device
2591 on_key_pressed (GtkWidget *self,
2595 GtkTreeSelection *selection;
2597 GtkTreeModel *model;
2598 gboolean retval = FALSE;
2600 /* Up and Down are automatically managed by the treeview */
2601 if (event->keyval == GDK_Return) {
2602 /* Expand/Collapse the selected row */
2603 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2604 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2607 path = gtk_tree_model_get_path (model, &iter);
2609 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2610 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2612 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2613 gtk_tree_path_free (path);
2615 /* No further processing */
2623 * We listen to the changes in the local folder account name key,
2624 * because we want to show the right name in the view. The local
2625 * folder account name corresponds to the device name in the Maemo
2626 * version. We do this because we do not want to query gconf on each
2627 * tree view refresh. It's better to cache it and change whenever
2631 on_configuration_key_changed (ModestConf* conf,
2633 ModestConfEvent event,
2634 ModestConfNotificationId id,
2635 ModestFolderView *self)
2637 ModestFolderViewPrivate *priv;
2640 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2641 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2643 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2644 g_free (priv->local_account_name);
2646 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2647 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2649 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2650 MODEST_CONF_DEVICE_NAME, NULL);
2652 /* Force a redraw */
2653 #if GTK_CHECK_VERSION(2, 8, 0)
2654 GtkTreeViewColumn * tree_column;
2656 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2657 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2658 gtk_tree_view_column_queue_resize (tree_column);
2660 gtk_widget_queue_draw (GTK_WIDGET (self));
2666 modest_folder_view_set_style (ModestFolderView *self,
2667 ModestFolderViewStyle style)
2669 ModestFolderViewPrivate *priv;
2671 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2672 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2673 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2675 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2678 priv->style = style;
2682 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2683 const gchar *account_id)
2685 ModestFolderViewPrivate *priv;
2686 GtkTreeModel *model;
2688 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2690 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2692 /* This will be used by the filter_row callback,
2693 * to decided which rows to show: */
2694 if (priv->visible_account_id) {
2695 g_free (priv->visible_account_id);
2696 priv->visible_account_id = NULL;
2699 priv->visible_account_id = g_strdup (account_id);
2702 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2703 if (GTK_IS_TREE_MODEL_FILTER (model))
2704 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2706 /* Save settings to gconf */
2707 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2708 MODEST_CONF_FOLDER_VIEW_KEY);
2712 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2714 ModestFolderViewPrivate *priv;
2716 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2718 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2720 return (const gchar *) priv->visible_account_id;
2724 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2728 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2730 gtk_tree_model_get (model, iter,
2731 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2734 gboolean result = FALSE;
2735 if (type == TNY_FOLDER_TYPE_INBOX) {
2739 *inbox_iter = *iter;
2743 if (gtk_tree_model_iter_children (model, &child, iter)) {
2744 if (find_inbox_iter (model, &child, inbox_iter))
2748 } while (gtk_tree_model_iter_next (model, iter));
2757 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2759 GtkTreeModel *model;
2760 GtkTreeIter iter, inbox_iter;
2761 GtkTreeSelection *sel;
2762 GtkTreePath *path = NULL;
2764 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2766 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2770 expand_root_items (self);
2771 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2773 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2774 g_warning ("%s: model is empty", __FUNCTION__);
2778 if (find_inbox_iter (model, &iter, &inbox_iter))
2779 path = gtk_tree_model_get_path (model, &inbox_iter);
2781 path = gtk_tree_path_new_first ();
2783 /* Select the row and free */
2784 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2785 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2786 gtk_tree_path_free (path);
2789 gtk_widget_grab_focus (GTK_WIDGET(self));
2795 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2800 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2801 TnyFolder* a_folder;
2804 gtk_tree_model_get (model, iter,
2805 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2806 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2807 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2811 if (folder == a_folder) {
2812 g_object_unref (a_folder);
2813 *folder_iter = *iter;
2816 g_object_unref (a_folder);
2818 if (gtk_tree_model_iter_children (model, &child, iter)) {
2819 if (find_folder_iter (model, &child, folder_iter, folder))
2823 } while (gtk_tree_model_iter_next (model, iter));
2830 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2833 ModestFolderView *self)
2835 ModestFolderViewPrivate *priv = NULL;
2836 GtkTreeSelection *sel;
2837 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2838 GObject *instance = NULL;
2840 if (!MODEST_IS_FOLDER_VIEW(self))
2843 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2845 priv->reexpand = TRUE;
2847 gtk_tree_model_get (tree_model, iter,
2848 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2849 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2851 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2852 priv->folder_to_select = g_object_ref (instance);
2854 g_object_unref (instance);
2857 if (priv->folder_to_select) {
2859 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2862 path = gtk_tree_model_get_path (tree_model, iter);
2863 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2865 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2867 gtk_tree_selection_select_iter (sel, iter);
2868 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2870 gtk_tree_path_free (path);
2875 modest_folder_view_disable_next_folder_selection (self);
2877 /* Refilter the model */
2878 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2884 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2886 ModestFolderViewPrivate *priv;
2888 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2890 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2892 if (priv->folder_to_select)
2893 g_object_unref(priv->folder_to_select);
2895 priv->folder_to_select = NULL;
2899 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2900 gboolean after_change)
2902 GtkTreeModel *model;
2903 GtkTreeIter iter, folder_iter;
2904 GtkTreeSelection *sel;
2905 ModestFolderViewPrivate *priv = NULL;
2907 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2908 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2910 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2913 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2914 gtk_tree_selection_unselect_all (sel);
2916 if (priv->folder_to_select)
2917 g_object_unref(priv->folder_to_select);
2918 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2922 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2927 /* Refilter the model, before selecting the folder */
2928 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2930 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2931 g_warning ("%s: model is empty", __FUNCTION__);
2935 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2938 path = gtk_tree_model_get_path (model, &folder_iter);
2939 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2941 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2942 gtk_tree_selection_select_iter (sel, &folder_iter);
2943 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2945 gtk_tree_path_free (path);
2953 modest_folder_view_copy_selection (ModestFolderView *self)
2955 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2957 /* Copy selection */
2958 _clipboard_set_selected_data (self, FALSE);
2962 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2964 ModestFolderViewPrivate *priv = NULL;
2965 GtkTreeModel *model = NULL;
2966 const gchar **hidding = NULL;
2967 guint i, n_selected;
2969 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2970 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2972 /* Copy selection */
2973 if (!_clipboard_set_selected_data (folder_view, TRUE))
2976 /* Get hidding ids */
2977 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2979 /* Clear hidding array created by previous cut operation */
2980 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2982 /* Copy hidding array */
2983 priv->n_selected = n_selected;
2984 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2985 for (i=0; i < n_selected; i++)
2986 priv->hidding_ids[i] = g_strdup(hidding[i]);
2988 /* Hide cut folders */
2989 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2990 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2994 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2995 ModestFolderView *folder_view_dst)
2997 GtkTreeModel *filter_model = NULL;
2998 GtkTreeModel *model = NULL;
2999 GtkTreeModel *new_filter_model = NULL;
3001 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3002 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3005 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3006 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3008 /* Build new filter model */
3009 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3010 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3014 /* Set copied model */
3015 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3016 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3017 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3020 g_object_unref (new_filter_model);
3024 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3027 GtkTreeModel *model = NULL;
3028 ModestFolderViewPrivate* priv;
3030 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3032 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3033 priv->show_non_move = show;
3034 /* modest_folder_view_update_model(folder_view, */
3035 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3037 /* Hide special folders */
3038 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3039 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3040 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3044 /* Returns FALSE if it did not selected anything */
3046 _clipboard_set_selected_data (ModestFolderView *folder_view,
3049 ModestFolderViewPrivate *priv = NULL;
3050 TnyFolderStore *folder = NULL;
3051 gboolean retval = FALSE;
3053 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3054 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3056 /* Set selected data on clipboard */
3057 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3058 folder = modest_folder_view_get_selected (folder_view);
3060 /* Do not allow to select an account */
3061 if (TNY_IS_FOLDER (folder)) {
3062 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3067 g_object_unref (folder);
3073 _clear_hidding_filter (ModestFolderView *folder_view)
3075 ModestFolderViewPrivate *priv;
3078 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3079 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3081 if (priv->hidding_ids != NULL) {
3082 for (i=0; i < priv->n_selected; i++)
3083 g_free (priv->hidding_ids[i]);
3084 g_free(priv->hidding_ids);
3090 on_display_name_changed (ModestAccountMgr *mgr,
3091 const gchar *account,
3094 ModestFolderView *self;
3096 self = MODEST_FOLDER_VIEW (user_data);
3098 /* Force a redraw */
3099 #if GTK_CHECK_VERSION(2, 8, 0)
3100 GtkTreeViewColumn * tree_column;
3102 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3103 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3104 gtk_tree_view_column_queue_resize (tree_column);
3106 gtk_widget_queue_draw (GTK_WIDGET (self));