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_func = modest_folder_view_set_account_store;
1078 modest_folder_view_finalize (GObject *obj)
1080 ModestFolderViewPrivate *priv;
1081 GtkTreeSelection *sel;
1083 g_return_if_fail (obj);
1085 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1087 if (priv->timer_expander != 0) {
1088 g_source_remove (priv->timer_expander);
1089 priv->timer_expander = 0;
1092 if (priv->account_store) {
1093 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1094 priv->account_inserted_signal);
1095 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1096 priv->account_removed_signal);
1097 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1098 priv->account_changed_signal);
1099 g_object_unref (G_OBJECT(priv->account_store));
1100 priv->account_store = NULL;
1104 g_object_unref (G_OBJECT (priv->query));
1108 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1109 if (priv->folder_to_select) {
1110 g_object_unref (G_OBJECT(priv->folder_to_select));
1111 priv->folder_to_select = NULL;
1114 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1116 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1118 g_free (priv->local_account_name);
1119 g_free (priv->visible_account_id);
1121 if (priv->conf_key_signal) {
1122 g_signal_handler_disconnect (modest_runtime_get_conf (),
1123 priv->conf_key_signal);
1124 priv->conf_key_signal = 0;
1127 if (priv->cur_folder_store) {
1128 if (TNY_IS_FOLDER(priv->cur_folder_store)) {
1129 ModestMailOperation *mail_op;
1131 mail_op = modest_mail_operation_new (NULL);
1132 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1134 modest_mail_operation_sync_folder (mail_op, TNY_FOLDER (priv->cur_folder_store), FALSE);
1137 g_object_unref (priv->cur_folder_store);
1138 priv->cur_folder_store = NULL;
1141 /* Clear hidding array created by cut operation */
1142 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1144 G_OBJECT_CLASS(parent_class)->finalize (obj);
1149 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1151 ModestFolderViewPrivate *priv;
1154 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1155 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1157 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1158 device = tny_account_store_get_device (account_store);
1160 if (G_UNLIKELY (priv->account_store)) {
1162 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1163 priv->account_inserted_signal))
1164 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1165 priv->account_inserted_signal);
1166 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1167 priv->account_removed_signal))
1168 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1169 priv->account_removed_signal);
1170 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1171 priv->account_changed_signal))
1172 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1173 priv->account_changed_signal);
1174 g_object_unref (G_OBJECT (priv->account_store));
1177 priv->account_store = g_object_ref (G_OBJECT (account_store));
1179 priv->account_removed_signal =
1180 g_signal_connect (G_OBJECT(account_store), "account_removed",
1181 G_CALLBACK (on_account_removed), self);
1183 priv->account_inserted_signal =
1184 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1185 G_CALLBACK (on_account_inserted), self);
1187 priv->account_changed_signal =
1188 g_signal_connect (G_OBJECT(account_store), "account_changed",
1189 G_CALLBACK (on_account_changed), self);
1191 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1193 g_object_unref (G_OBJECT (device));
1197 on_connection_status_changed (TnyAccount *self,
1198 TnyConnectionStatus status,
1201 /* If the account becomes online then refresh it */
1202 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1203 const gchar *acc_name;
1204 GtkWidget *my_window;
1206 my_window = gtk_widget_get_ancestor (GTK_WIDGET (user_data), MODEST_TYPE_WINDOW);
1207 acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (self);
1208 modest_ui_actions_do_send_receive (acc_name, FALSE, MODEST_WINDOW (my_window));
1213 on_account_inserted (TnyAccountStore *account_store,
1214 TnyAccount *account,
1217 ModestFolderViewPrivate *priv;
1218 GtkTreeModel *sort_model, *filter_model;
1220 /* Ignore transport account insertions, we're not showing them
1221 in the folder view */
1222 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1225 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1227 /* If we're adding a new account, and there is no previous
1228 one, we need to select the visible server account */
1229 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1230 !priv->visible_account_id)
1231 modest_widget_memory_restore (modest_runtime_get_conf(),
1232 G_OBJECT (user_data),
1233 MODEST_CONF_FOLDER_VIEW_KEY);
1235 /* Get the inner model */
1236 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1237 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1239 /* Insert the account in the model */
1240 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1241 G_OBJECT (account));
1244 /* When the store account gets online refresh it */
1245 g_signal_connect (account, "connection_status_changed",
1246 G_CALLBACK (on_connection_status_changed),
1247 MODEST_FOLDER_VIEW (user_data));
1249 /* Refilter the model */
1250 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1255 on_account_changed (TnyAccountStore *account_store,
1256 TnyAccount *tny_account,
1259 ModestFolderViewPrivate *priv;
1260 GtkTreeModel *sort_model, *filter_model;
1262 /* Ignore transport account insertions, we're not showing them
1263 in the folder view */
1264 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1267 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1269 /* Get the inner model */
1270 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1271 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1273 /* Remove the account from the model */
1274 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1275 G_OBJECT (tny_account));
1277 /* Insert the account in the model */
1278 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1279 G_OBJECT (tny_account));
1281 /* Refilter the model */
1282 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1287 * Selects the first inbox or the local account in an idle
1290 on_idle_select_first_inbox_or_local (gpointer user_data)
1292 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1294 modest_folder_view_select_first_inbox_or_local (self);
1301 on_account_removed (TnyAccountStore *account_store,
1302 TnyAccount *account,
1305 ModestFolderView *self = NULL;
1306 ModestFolderViewPrivate *priv;
1307 GtkTreeModel *sort_model, *filter_model;
1308 GtkTreeSelection *sel = NULL;
1309 gboolean same_account_selected = FALSE;
1311 /* Ignore transport account removals, we're not showing them
1312 in the folder view */
1313 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1316 self = MODEST_FOLDER_VIEW (user_data);
1317 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1319 /* Invalidate the cur_folder_store only if the selected folder
1320 belongs to the account that is being removed */
1321 if (priv->cur_folder_store) {
1322 TnyAccount *selected_folder_account = NULL;
1324 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1325 selected_folder_account =
1326 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1328 selected_folder_account =
1329 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1332 if (selected_folder_account == account) {
1333 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1334 gtk_tree_selection_unselect_all (sel);
1335 same_account_selected = TRUE;
1337 g_object_unref (selected_folder_account);
1340 /* Invalidate row to select only if the folder to select
1341 belongs to the account that is being removed*/
1342 if (priv->folder_to_select) {
1343 TnyAccount *folder_to_select_account = NULL;
1345 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1346 if (folder_to_select_account == account) {
1347 modest_folder_view_disable_next_folder_selection (self);
1348 g_object_unref (priv->folder_to_select);
1349 priv->folder_to_select = NULL;
1351 g_object_unref (folder_to_select_account);
1354 /* Remove the account from the model */
1355 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1356 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1357 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1358 G_OBJECT (account));
1360 /* If the removed account is the currently viewed one then
1361 clear the configuration value. The new visible account will be the default account */
1362 if (priv->visible_account_id &&
1363 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1365 /* Clear the current visible account_id */
1366 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1368 /* Call the restore method, this will set the new visible account */
1369 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1370 MODEST_CONF_FOLDER_VIEW_KEY);
1373 /* Refilter the model */
1374 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1376 /* Select the first INBOX if the currently selected folder
1377 belongs to the account that is being deleted */
1378 if (same_account_selected)
1379 g_idle_add (on_idle_select_first_inbox_or_local, self);
1383 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1385 GtkTreeViewColumn *col;
1387 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1389 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1391 g_printerr ("modest: failed get column for title\n");
1395 gtk_tree_view_column_set_title (col, title);
1396 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1401 modest_folder_view_on_map (ModestFolderView *self,
1402 GdkEventExpose *event,
1405 ModestFolderViewPrivate *priv;
1407 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1409 /* This won't happen often */
1410 if (G_UNLIKELY (priv->reselect)) {
1411 /* Select the first inbox or the local account if not found */
1413 /* TODO: this could cause a lock at startup, so we
1414 comment it for the moment. We know that this will
1415 be a bug, because the INBOX is not selected, but we
1416 need to rewrite some parts of Modest to avoid the
1417 deathlock situation */
1418 /* TODO: check if this is still the case */
1419 priv->reselect = FALSE;
1420 modest_folder_view_select_first_inbox_or_local (self);
1421 /* Notify the display name observers */
1422 g_signal_emit (G_OBJECT(self),
1423 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1427 if (priv->reexpand) {
1428 expand_root_items (self);
1429 priv->reexpand = FALSE;
1436 modest_folder_view_new (TnyFolderStoreQuery *query)
1439 ModestFolderViewPrivate *priv;
1440 GtkTreeSelection *sel;
1442 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1443 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1446 priv->query = g_object_ref (query);
1448 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1449 priv->changed_signal = g_signal_connect (sel, "changed",
1450 G_CALLBACK (on_selection_changed), self);
1452 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1454 return GTK_WIDGET(self);
1457 /* this feels dirty; any other way to expand all the root items? */
1459 expand_root_items (ModestFolderView *self)
1462 GtkTreeModel *model;
1465 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1466 path = gtk_tree_path_new_first ();
1468 /* all folders should have child items, so.. */
1470 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1471 gtk_tree_path_next (path);
1472 } while (gtk_tree_model_get_iter (model, &iter, path));
1474 gtk_tree_path_free (path);
1478 * We use this function to implement the
1479 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1480 * account in this case, and the local folders.
1483 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1485 ModestFolderViewPrivate *priv;
1486 gboolean retval = TRUE;
1487 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1488 GObject *instance = NULL;
1489 const gchar *id = NULL;
1491 gboolean found = FALSE;
1492 gboolean cleared = FALSE;
1494 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1495 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1497 gtk_tree_model_get (model, iter,
1498 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1499 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1502 /* Do not show if there is no instance, this could indeed
1503 happen when the model is being modified while it's being
1504 drawn. This could occur for example when moving folders
1509 if (type == TNY_FOLDER_TYPE_ROOT) {
1510 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1511 account instead of a folder. */
1512 if (TNY_IS_ACCOUNT (instance)) {
1513 TnyAccount *acc = TNY_ACCOUNT (instance);
1514 const gchar *account_id = tny_account_get_id (acc);
1516 /* If it isn't a special folder,
1517 * don't show it unless it is the visible account: */
1518 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1519 !modest_tny_account_is_virtual_local_folders (acc) &&
1520 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1522 /* Show only the visible account id */
1523 if (priv->visible_account_id) {
1524 if (strcmp (account_id, priv->visible_account_id))
1531 /* Never show these to the user. They are merged into one folder
1532 * in the local-folders account instead: */
1533 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1538 /* Check hiding (if necessary) */
1539 cleared = modest_email_clipboard_cleared (priv->clipboard);
1540 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1541 id = tny_folder_get_id (TNY_FOLDER(instance));
1542 if (priv->hidding_ids != NULL)
1543 for (i=0; i < priv->n_selected && !found; i++)
1544 if (priv->hidding_ids[i] != NULL && id != NULL)
1545 found = (!strcmp (priv->hidding_ids[i], id));
1551 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1552 folder as no message can be move there according to UI specs */
1553 if (!priv->show_non_move) {
1555 case TNY_FOLDER_TYPE_OUTBOX:
1556 case TNY_FOLDER_TYPE_SENT:
1557 case TNY_FOLDER_TYPE_DRAFTS:
1560 case TNY_FOLDER_TYPE_UNKNOWN:
1561 case TNY_FOLDER_TYPE_NORMAL:
1562 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1563 if (type == TNY_FOLDER_TYPE_INVALID)
1564 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1566 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1567 type == TNY_FOLDER_TYPE_SENT
1568 || type == TNY_FOLDER_TYPE_DRAFTS)
1577 g_object_unref (instance);
1584 modest_folder_view_update_model (ModestFolderView *self,
1585 TnyAccountStore *account_store)
1587 ModestFolderViewPrivate *priv;
1588 GtkTreeModel *model /* , *old_model */;
1589 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1591 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1592 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1595 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1597 /* Notify that there is no folder selected */
1598 g_signal_emit (G_OBJECT(self),
1599 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1601 if (priv->cur_folder_store) {
1602 g_object_unref (priv->cur_folder_store);
1603 priv->cur_folder_store = NULL;
1606 /* FIXME: the local accounts are not shown when the query
1607 selects only the subscribed folders */
1608 model = tny_gtk_folder_store_tree_model_new (NULL);
1610 /* Get the accounts: */
1611 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1613 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1615 sortable = gtk_tree_model_sort_new_with_model (model);
1616 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1617 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1618 GTK_SORT_ASCENDING);
1619 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1620 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1621 cmp_rows, NULL, NULL);
1623 /* Create filter model */
1624 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1625 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1631 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1632 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1633 (GCallback) on_row_inserted_maybe_select_folder, self);
1636 g_object_unref (model);
1637 g_object_unref (filter_model);
1638 g_object_unref (sortable);
1640 /* Force a reselection of the INBOX next time the widget is shown */
1641 priv->reselect = TRUE;
1648 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1650 GtkTreeModel *model = NULL;
1651 TnyFolderStore *folder = NULL;
1653 ModestFolderView *tree_view = NULL;
1654 ModestFolderViewPrivate *priv = NULL;
1655 gboolean selected = FALSE;
1657 g_return_if_fail (sel);
1658 g_return_if_fail (user_data);
1660 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1662 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1664 tree_view = MODEST_FOLDER_VIEW (user_data);
1667 gtk_tree_model_get (model, &iter,
1668 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1671 /* If the folder is the same do not notify */
1672 if (folder && priv->cur_folder_store == folder) {
1673 g_object_unref (folder);
1678 /* Current folder was unselected */
1679 if (priv->cur_folder_store) {
1680 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1681 priv->cur_folder_store, FALSE);
1683 if (TNY_IS_FOLDER(priv->cur_folder_store))
1684 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1685 FALSE, NULL, NULL, NULL);
1687 /* FALSE --> don't expunge the messages */
1689 g_object_unref (priv->cur_folder_store);
1690 priv->cur_folder_store = NULL;
1693 /* New current references */
1694 priv->cur_folder_store = folder;
1696 /* New folder has been selected. Do not notify if there is
1697 nothing new selected */
1699 g_signal_emit (G_OBJECT(tree_view),
1700 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1701 0, priv->cur_folder_store, TRUE);
1706 modest_folder_view_get_selected (ModestFolderView *self)
1708 ModestFolderViewPrivate *priv;
1710 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1712 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1713 if (priv->cur_folder_store)
1714 g_object_ref (priv->cur_folder_store);
1716 return priv->cur_folder_store;
1720 get_cmp_rows_type_pos (GObject *folder)
1722 /* Remote accounts -> Local account -> MMC account .*/
1725 if (TNY_IS_ACCOUNT (folder) &&
1726 modest_tny_account_is_virtual_local_folders (
1727 TNY_ACCOUNT (folder))) {
1729 } else if (TNY_IS_ACCOUNT (folder)) {
1730 TnyAccount *account = TNY_ACCOUNT (folder);
1731 const gchar *account_id = tny_account_get_id (account);
1732 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1738 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1739 return -1; /* Should never happen */
1744 get_cmp_subfolder_type_pos (TnyFolderType t)
1746 /* Inbox, Outbox, Drafts, Sent, User */
1750 case TNY_FOLDER_TYPE_INBOX:
1753 case TNY_FOLDER_TYPE_OUTBOX:
1756 case TNY_FOLDER_TYPE_DRAFTS:
1759 case TNY_FOLDER_TYPE_SENT:
1768 * This function orders the mail accounts according to these rules:
1769 * 1st - remote accounts
1770 * 2nd - local account
1774 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1778 gchar *name1 = NULL;
1779 gchar *name2 = NULL;
1780 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1781 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1782 GObject *folder1 = NULL;
1783 GObject *folder2 = NULL;
1785 gtk_tree_model_get (tree_model, iter1,
1786 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1787 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1788 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1790 gtk_tree_model_get (tree_model, iter2,
1791 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1792 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1793 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1796 /* Return if we get no folder. This could happen when folder
1797 operations are happening. The model is updated after the
1798 folder copy/move actually occurs, so there could be
1799 situations where the model to be drawn is not correct */
1800 if (!folder1 || !folder2)
1803 if (type == TNY_FOLDER_TYPE_ROOT) {
1804 /* Compare the types, so that
1805 * Remote accounts -> Local account -> MMC account .*/
1806 const gint pos1 = get_cmp_rows_type_pos (folder1);
1807 const gint pos2 = get_cmp_rows_type_pos (folder2);
1808 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1809 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1812 else if (pos1 > pos2)
1815 /* Compare items of the same type: */
1817 TnyAccount *account1 = NULL;
1818 if (TNY_IS_ACCOUNT (folder1))
1819 account1 = TNY_ACCOUNT (folder1);
1821 TnyAccount *account2 = NULL;
1822 if (TNY_IS_ACCOUNT (folder2))
1823 account2 = TNY_ACCOUNT (folder2);
1825 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1826 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1828 if (!account_id && !account_id2) {
1830 } else if (!account_id) {
1832 } else if (!account_id2) {
1834 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1837 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1841 gint cmp1 = 0, cmp2 = 0;
1842 /* get the parent to know if it's a local folder */
1845 gboolean has_parent;
1846 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1848 GObject *parent_folder;
1849 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1850 gtk_tree_model_get (tree_model, &parent,
1851 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1852 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1854 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1855 TNY_IS_ACCOUNT (parent_folder)) {
1856 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1857 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1858 (TNY_FOLDER (folder1)));
1859 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1860 (TNY_FOLDER (folder2)));
1861 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1862 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1865 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1871 g_object_unref (parent_folder);
1874 /* if they are not local folders */
1876 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1877 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1881 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1883 cmp = (cmp1 - cmp2);
1888 g_object_unref(G_OBJECT(folder1));
1890 g_object_unref(G_OBJECT(folder2));
1898 /*****************************************************************************/
1899 /* DRAG and DROP stuff */
1900 /*****************************************************************************/
1902 * This function fills the #GtkSelectionData with the row and the
1903 * model that has been dragged. It's called when this widget is a
1904 * source for dnd after the event drop happened
1907 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1908 guint info, guint time, gpointer data)
1910 GtkTreeSelection *selection;
1911 GtkTreeModel *model;
1913 GtkTreePath *source_row;
1915 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1916 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1918 source_row = gtk_tree_model_get_path (model, &iter);
1919 gtk_tree_set_row_drag_data (selection_data,
1923 gtk_tree_path_free (source_row);
1927 typedef struct _DndHelper {
1928 ModestFolderView *folder_view;
1929 gboolean delete_source;
1930 GtkTreePath *source_row;
1931 GdkDragContext *context;
1936 dnd_helper_destroyer (DndHelper *helper)
1938 /* Free the helper */
1939 g_object_unref (helper->folder_view);
1940 gtk_tree_path_free (helper->source_row);
1941 g_slice_free (DndHelper, helper);
1945 xfer_cb (ModestMailOperation *mail_op,
1951 helper = (DndHelper *) user_data;
1953 if (modest_mail_operation_get_status (mail_op) ==
1954 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1960 /* Notify the drag source. Never call delete, the monitor will
1961 do the job if needed */
1962 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1964 /* Free the helper */
1965 dnd_helper_destroyer (helper);
1969 xfer_msgs_cb (ModestMailOperation *mail_op,
1973 xfer_cb (mail_op, user_data);
1977 xfer_folder_cb (ModestMailOperation *mail_op,
1978 TnyFolder *new_folder,
1983 helper = (DndHelper *) user_data;
1986 xfer_cb (mail_op, user_data);
1988 /* Select the folder */
1990 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (helper->folder_view),
1995 /* get the folder for the row the treepath refers to. */
1996 /* folder must be unref'd */
1997 static TnyFolderStore *
1998 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2001 TnyFolderStore *folder = NULL;
2003 if (gtk_tree_model_get_iter (model,&iter, path))
2004 gtk_tree_model_get (model, &iter,
2005 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2011 * This function is used by drag_data_received_cb to manage drag and
2012 * drop of a header, i.e, and drag from the header view to the folder
2016 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2017 GtkTreeModel *dest_model,
2018 GtkTreePath *dest_row,
2019 GtkSelectionData *selection_data,
2022 TnyList *headers = NULL;
2023 TnyFolder *folder = NULL;
2024 TnyFolderType folder_type;
2025 ModestMailOperation *mail_op = NULL;
2026 GtkTreeIter source_iter, dest_iter;
2027 ModestWindowMgr *mgr = NULL;
2028 ModestWindow *main_win = NULL;
2029 gchar **uris, **tmp;
2032 /* Build the list of headers */
2033 mgr = modest_runtime_get_window_mgr ();
2034 headers = tny_simple_list_new ();
2035 uris = modest_dnd_selection_data_get_paths (selection_data);
2038 while (*tmp != NULL) {
2043 path = gtk_tree_path_new_from_string (*tmp);
2044 gtk_tree_model_get_iter (source_model, &source_iter, path);
2045 gtk_tree_model_get (source_model, &source_iter,
2046 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2049 /* Do not enable d&d of headers already opened */
2050 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2051 tny_list_append (headers, G_OBJECT (header));
2053 /* Free and go on */
2054 gtk_tree_path_free (path);
2055 g_object_unref (header);
2060 /* Get the target folder */
2061 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2062 gtk_tree_model_get (dest_model, &dest_iter,
2063 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2066 if (!folder || !TNY_IS_FOLDER(folder)) {
2067 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2071 folder_type = modest_tny_folder_guess_folder_type (folder);
2072 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2073 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2074 goto cleanup; /* cannot move messages there */
2077 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2078 /* g_warning ("folder not writable"); */
2079 goto cleanup; /* verboten! */
2082 /* Ask for confirmation to move */
2083 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2085 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2089 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2091 if (response == GTK_RESPONSE_CANCEL)
2094 /* Transfer messages */
2095 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2096 modest_ui_actions_move_folder_error_handler,
2099 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2102 modest_mail_operation_xfer_msgs (mail_op,
2105 helper->delete_source,
2106 xfer_msgs_cb, helper);
2110 if (G_IS_OBJECT(mail_op))
2111 g_object_unref (G_OBJECT (mail_op));
2112 if (G_IS_OBJECT(folder))
2113 g_object_unref (G_OBJECT (folder));
2114 if (G_IS_OBJECT(headers))
2115 g_object_unref (headers);
2119 TnyFolderStore *src_folder;
2120 TnyFolderStore *dst_folder;
2121 ModestFolderView *folder_view;
2126 dnd_folder_info_destroyer (DndFolderInfo *info)
2128 if (info->src_folder)
2129 g_object_unref (info->src_folder);
2130 if (info->dst_folder)
2131 g_object_unref (info->dst_folder);
2132 if (info->folder_view)
2133 g_object_unref (info->folder_view);
2134 g_slice_free (DndFolderInfo, info);
2138 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2139 GtkWindow *parent_window,
2140 TnyAccount *account)
2142 time_t dnd_time = info->helper->time;
2143 GdkDragContext *context = info->helper->context;
2146 modest_ui_actions_on_account_connection_error (parent_window, account);
2148 /* Free the helper & info */
2149 dnd_helper_destroyer (info->helper);
2150 dnd_folder_info_destroyer (info);
2152 /* Notify the drag source. Never call delete, the monitor will
2153 do the job if needed */
2154 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2159 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2161 GtkWindow *parent_window,
2162 TnyAccount *account,
2165 DndFolderInfo *info = NULL;
2166 ModestMailOperation *mail_op;
2168 info = (DndFolderInfo *) user_data;
2170 if (err || canceled) {
2171 dnd_on_connection_failed_destroyer (info, parent_window, account);
2175 /* Do the mail operation */
2176 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2177 modest_ui_actions_move_folder_error_handler,
2178 info->src_folder, NULL);
2180 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2183 /* Transfer the folder */
2184 modest_mail_operation_xfer_folder (mail_op,
2185 TNY_FOLDER (info->src_folder),
2187 info->helper->delete_source,
2191 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2192 /* TNY_FOLDER (info->dst_folder), TRUE); */
2194 g_object_unref (G_OBJECT (mail_op));
2199 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2201 GtkWindow *parent_window,
2202 TnyAccount *account,
2205 DndFolderInfo *info = NULL;
2207 info = (DndFolderInfo *) user_data;
2209 if (err || canceled) {
2210 dnd_on_connection_failed_destroyer (info, parent_window, account);
2214 /* Connect to source folder and perform the copy/move */
2215 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2217 drag_and_drop_from_folder_view_src_folder_performer,
2222 * This function is used by drag_data_received_cb to manage drag and
2223 * drop of a folder, i.e, and drag from the folder view to the same
2227 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2228 GtkTreeModel *dest_model,
2229 GtkTreePath *dest_row,
2230 GtkSelectionData *selection_data,
2233 GtkTreeIter dest_iter, iter;
2234 TnyFolderStore *dest_folder = NULL;
2235 TnyFolderStore *folder = NULL;
2236 gboolean forbidden = FALSE;
2238 DndFolderInfo *info = NULL;
2240 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2242 g_warning ("%s: BUG: no main window", __FUNCTION__);
2247 /* check the folder rules for the destination */
2248 folder = tree_path_to_folder (dest_model, dest_row);
2249 if (TNY_IS_FOLDER(folder)) {
2250 ModestTnyFolderRules rules =
2251 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2252 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2253 } else if (TNY_IS_FOLDER_STORE(folder)) {
2254 /* enable local root as destination for folders */
2255 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
2256 && TNY_IS_ACCOUNT (folder))
2259 g_object_unref (folder);
2262 /* check the folder rules for the source */
2263 folder = tree_path_to_folder (source_model, helper->source_row);
2264 if (TNY_IS_FOLDER(folder)) {
2265 ModestTnyFolderRules rules =
2266 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2267 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2270 g_object_unref (folder);
2274 /* Check if the drag is possible */
2275 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2276 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2277 gtk_tree_path_free (helper->source_row);
2278 g_slice_free (DndHelper, helper);
2283 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2284 gtk_tree_model_get (dest_model, &dest_iter,
2285 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2287 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2288 gtk_tree_model_get (source_model, &iter,
2289 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2292 /* Create the info for the performer */
2293 info = g_slice_new (DndFolderInfo);
2294 info->src_folder = g_object_ref (folder);
2295 info->dst_folder = g_object_ref (dest_folder);
2296 info->folder_view = g_object_ref (helper->folder_view);
2297 info->helper = helper;
2299 /* Connect to the destination folder and perform the copy/move */
2300 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2302 drag_and_drop_from_folder_view_dst_folder_performer,
2306 g_object_unref (dest_folder);
2307 g_object_unref (folder);
2311 * This function receives the data set by the "drag-data-get" signal
2312 * handler. This information comes within the #GtkSelectionData. This
2313 * function will manage both the drags of folders of the treeview and
2314 * drags of headers of the header view widget.
2317 on_drag_data_received (GtkWidget *widget,
2318 GdkDragContext *context,
2321 GtkSelectionData *selection_data,
2326 GtkWidget *source_widget;
2327 GtkTreeModel *dest_model, *source_model;
2328 GtkTreePath *source_row, *dest_row;
2329 GtkTreeViewDropPosition pos;
2330 gboolean success = FALSE, delete_source = FALSE;
2331 DndHelper *helper = NULL;
2333 /* Do not allow further process */
2334 g_signal_stop_emission_by_name (widget, "drag-data-received");
2335 source_widget = gtk_drag_get_source_widget (context);
2337 /* Get the action */
2338 if (context->action == GDK_ACTION_MOVE) {
2339 delete_source = TRUE;
2341 /* Notify that there is no folder selected. We need to
2342 do this in order to update the headers view (and
2343 its monitors, because when moving, the old folder
2344 won't longer exist. We can not wait for the end of
2345 the operation, because the operation won't start if
2346 the folder is in use */
2347 if (source_widget == widget) {
2348 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2349 gtk_tree_selection_unselect_all (sel);
2353 /* Check if the get_data failed */
2354 if (selection_data == NULL || selection_data->length < 0)
2355 gtk_drag_finish (context, success, FALSE, time);
2357 /* Select the destination model */
2358 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2360 /* Get the path to the destination row. Can not call
2361 gtk_tree_view_get_drag_dest_row() because the source row
2362 is not selected anymore */
2363 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2366 /* Only allow drops IN other rows */
2368 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2369 pos == GTK_TREE_VIEW_DROP_AFTER)
2370 gtk_drag_finish (context, success, FALSE, time);
2372 /* Create the helper */
2373 helper = g_slice_new0 (DndHelper);
2374 helper->delete_source = delete_source;
2375 helper->context = context;
2376 helper->time = time;
2377 helper->folder_view = g_object_ref (widget);
2379 /* Drags from the header view */
2380 if (source_widget != widget) {
2381 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2383 drag_and_drop_from_header_view (source_model,
2389 /* Get the source model and row */
2390 gtk_tree_get_row_drag_data (selection_data,
2393 helper->source_row = gtk_tree_path_copy (source_row);
2395 drag_and_drop_from_folder_view (source_model,
2401 gtk_tree_path_free (source_row);
2405 gtk_tree_path_free (dest_row);
2409 * We define a "drag-drop" signal handler because we do not want to
2410 * use the default one, because the default one always calls
2411 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2412 * signal handler, because there we have all the information available
2413 * to know if the dnd was a success or not.
2416 drag_drop_cb (GtkWidget *widget,
2417 GdkDragContext *context,
2425 if (!context->targets)
2428 /* Check if we're dragging a folder row */
2429 target = gtk_drag_dest_find_target (widget, context, NULL);
2431 /* Request the data from the source. */
2432 gtk_drag_get_data(widget, context, target, time);
2438 * This function expands a node of a tree view if it's not expanded
2439 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2440 * does that, so that's why they're here.
2443 expand_row_timeout (gpointer data)
2445 GtkTreeView *tree_view = data;
2446 GtkTreePath *dest_path = NULL;
2447 GtkTreeViewDropPosition pos;
2448 gboolean result = FALSE;
2450 gdk_threads_enter ();
2452 gtk_tree_view_get_drag_dest_row (tree_view,
2457 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2458 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2459 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2460 gtk_tree_path_free (dest_path);
2464 gtk_tree_path_free (dest_path);
2469 gdk_threads_leave ();
2475 * This function is called whenever the pointer is moved over a widget
2476 * while dragging some data. It installs a timeout that will expand a
2477 * node of the treeview if not expanded yet. This function also calls
2478 * gdk_drag_status in order to set the suggested action that will be
2479 * used by the "drag-data-received" signal handler to know if we
2480 * should do a move or just a copy of the data.
2483 on_drag_motion (GtkWidget *widget,
2484 GdkDragContext *context,
2490 GtkTreeViewDropPosition pos;
2491 GtkTreePath *dest_row;
2492 GtkTreeModel *dest_model;
2493 ModestFolderViewPrivate *priv;
2494 GdkDragAction suggested_action;
2495 gboolean valid_location = FALSE;
2496 TnyFolderStore *folder = NULL;
2498 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2500 if (priv->timer_expander != 0) {
2501 g_source_remove (priv->timer_expander);
2502 priv->timer_expander = 0;
2505 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2510 /* Do not allow drops between folders */
2512 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2513 pos == GTK_TREE_VIEW_DROP_AFTER) {
2514 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2515 gdk_drag_status(context, 0, time);
2516 valid_location = FALSE;
2519 valid_location = TRUE;
2522 /* Check that the destination folder is writable */
2523 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2524 folder = tree_path_to_folder (dest_model, dest_row);
2525 if (folder && TNY_IS_FOLDER (folder)) {
2526 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2528 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2529 valid_location = FALSE;
2534 /* Expand the selected row after 1/2 second */
2535 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2536 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2537 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2540 /* Select the desired action. By default we pick MOVE */
2541 suggested_action = GDK_ACTION_MOVE;
2543 if (context->actions == GDK_ACTION_COPY)
2544 gdk_drag_status(context, GDK_ACTION_COPY, time);
2545 else if (context->actions == GDK_ACTION_MOVE)
2546 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2547 else if (context->actions & suggested_action)
2548 gdk_drag_status(context, suggested_action, time);
2550 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2554 g_object_unref (folder);
2556 gtk_tree_path_free (dest_row);
2557 g_signal_stop_emission_by_name (widget, "drag-motion");
2559 return valid_location;
2563 * This function sets the treeview as a source and a target for dnd
2564 * events. It also connects all the requirede signals.
2567 setup_drag_and_drop (GtkTreeView *self)
2569 /* Set up the folder view as a dnd destination. Set only the
2570 highlight flag, otherwise gtk will have a different
2572 gtk_drag_dest_set (GTK_WIDGET (self),
2573 GTK_DEST_DEFAULT_HIGHLIGHT,
2574 folder_view_drag_types,
2575 G_N_ELEMENTS (folder_view_drag_types),
2576 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2578 g_signal_connect (G_OBJECT (self),
2579 "drag_data_received",
2580 G_CALLBACK (on_drag_data_received),
2584 /* Set up the treeview as a dnd source */
2585 gtk_drag_source_set (GTK_WIDGET (self),
2587 folder_view_drag_types,
2588 G_N_ELEMENTS (folder_view_drag_types),
2589 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2591 g_signal_connect (G_OBJECT (self),
2593 G_CALLBACK (on_drag_motion),
2596 g_signal_connect (G_OBJECT (self),
2598 G_CALLBACK (on_drag_data_get),
2601 g_signal_connect (G_OBJECT (self),
2603 G_CALLBACK (drag_drop_cb),
2608 * This function manages the navigation through the folders using the
2609 * keyboard or the hardware keys in the device
2612 on_key_pressed (GtkWidget *self,
2616 GtkTreeSelection *selection;
2618 GtkTreeModel *model;
2619 gboolean retval = FALSE;
2621 /* Up and Down are automatically managed by the treeview */
2622 if (event->keyval == GDK_Return) {
2623 /* Expand/Collapse the selected row */
2624 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2625 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2628 path = gtk_tree_model_get_path (model, &iter);
2630 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2631 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2633 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2634 gtk_tree_path_free (path);
2636 /* No further processing */
2644 * We listen to the changes in the local folder account name key,
2645 * because we want to show the right name in the view. The local
2646 * folder account name corresponds to the device name in the Maemo
2647 * version. We do this because we do not want to query gconf on each
2648 * tree view refresh. It's better to cache it and change whenever
2652 on_configuration_key_changed (ModestConf* conf,
2654 ModestConfEvent event,
2655 ModestConfNotificationId id,
2656 ModestFolderView *self)
2658 ModestFolderViewPrivate *priv;
2661 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2662 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2664 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2665 g_free (priv->local_account_name);
2667 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2668 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2670 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2671 MODEST_CONF_DEVICE_NAME, NULL);
2673 /* Force a redraw */
2674 #if GTK_CHECK_VERSION(2, 8, 0)
2675 GtkTreeViewColumn * tree_column;
2677 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2678 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2679 gtk_tree_view_column_queue_resize (tree_column);
2681 gtk_widget_queue_draw (GTK_WIDGET (self));
2687 modest_folder_view_set_style (ModestFolderView *self,
2688 ModestFolderViewStyle style)
2690 ModestFolderViewPrivate *priv;
2692 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2693 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2694 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2696 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2699 priv->style = style;
2703 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2704 const gchar *account_id)
2706 ModestFolderViewPrivate *priv;
2707 GtkTreeModel *model;
2709 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2711 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2713 /* This will be used by the filter_row callback,
2714 * to decided which rows to show: */
2715 if (priv->visible_account_id) {
2716 g_free (priv->visible_account_id);
2717 priv->visible_account_id = NULL;
2720 priv->visible_account_id = g_strdup (account_id);
2723 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2724 if (GTK_IS_TREE_MODEL_FILTER (model))
2725 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2727 /* Save settings to gconf */
2728 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2729 MODEST_CONF_FOLDER_VIEW_KEY);
2733 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2735 ModestFolderViewPrivate *priv;
2737 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2739 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2741 return (const gchar *) priv->visible_account_id;
2745 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2749 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2751 gtk_tree_model_get (model, iter,
2752 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2755 gboolean result = FALSE;
2756 if (type == TNY_FOLDER_TYPE_INBOX) {
2760 *inbox_iter = *iter;
2764 if (gtk_tree_model_iter_children (model, &child, iter)) {
2765 if (find_inbox_iter (model, &child, inbox_iter))
2769 } while (gtk_tree_model_iter_next (model, iter));
2778 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2780 GtkTreeModel *model;
2781 GtkTreeIter iter, inbox_iter;
2782 GtkTreeSelection *sel;
2783 GtkTreePath *path = NULL;
2785 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2787 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2791 expand_root_items (self);
2792 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2794 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2795 g_warning ("%s: model is empty", __FUNCTION__);
2799 if (find_inbox_iter (model, &iter, &inbox_iter))
2800 path = gtk_tree_model_get_path (model, &inbox_iter);
2802 path = gtk_tree_path_new_first ();
2804 /* Select the row and free */
2805 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2806 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2807 gtk_tree_path_free (path);
2810 gtk_widget_grab_focus (GTK_WIDGET(self));
2816 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2821 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2822 TnyFolder* a_folder;
2825 gtk_tree_model_get (model, iter,
2826 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2827 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2828 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2832 if (folder == a_folder) {
2833 g_object_unref (a_folder);
2834 *folder_iter = *iter;
2837 g_object_unref (a_folder);
2839 if (gtk_tree_model_iter_children (model, &child, iter)) {
2840 if (find_folder_iter (model, &child, folder_iter, folder))
2844 } while (gtk_tree_model_iter_next (model, iter));
2851 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2854 ModestFolderView *self)
2856 ModestFolderViewPrivate *priv = NULL;
2857 GtkTreeSelection *sel;
2858 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2859 GObject *instance = NULL;
2861 if (!MODEST_IS_FOLDER_VIEW(self))
2864 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2866 priv->reexpand = TRUE;
2868 gtk_tree_model_get (tree_model, iter,
2869 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2870 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2872 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2873 priv->folder_to_select = g_object_ref (instance);
2875 g_object_unref (instance);
2878 if (priv->folder_to_select) {
2880 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2883 path = gtk_tree_model_get_path (tree_model, iter);
2884 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2886 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2888 gtk_tree_selection_select_iter (sel, iter);
2889 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2891 gtk_tree_path_free (path);
2896 modest_folder_view_disable_next_folder_selection (self);
2898 /* Refilter the model */
2899 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2905 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2907 ModestFolderViewPrivate *priv;
2909 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2911 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2913 if (priv->folder_to_select)
2914 g_object_unref(priv->folder_to_select);
2916 priv->folder_to_select = NULL;
2920 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2921 gboolean after_change)
2923 GtkTreeModel *model;
2924 GtkTreeIter iter, folder_iter;
2925 GtkTreeSelection *sel;
2926 ModestFolderViewPrivate *priv = NULL;
2928 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2929 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2931 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2934 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2935 gtk_tree_selection_unselect_all (sel);
2937 if (priv->folder_to_select)
2938 g_object_unref(priv->folder_to_select);
2939 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2943 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2948 /* Refilter the model, before selecting the folder */
2949 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2951 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2952 g_warning ("%s: model is empty", __FUNCTION__);
2956 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2959 path = gtk_tree_model_get_path (model, &folder_iter);
2960 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2962 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2963 gtk_tree_selection_select_iter (sel, &folder_iter);
2964 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2966 gtk_tree_path_free (path);
2974 modest_folder_view_copy_selection (ModestFolderView *self)
2976 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2978 /* Copy selection */
2979 _clipboard_set_selected_data (self, FALSE);
2983 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2985 ModestFolderViewPrivate *priv = NULL;
2986 GtkTreeModel *model = NULL;
2987 const gchar **hidding = NULL;
2988 guint i, n_selected;
2990 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2991 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2993 /* Copy selection */
2994 if (!_clipboard_set_selected_data (folder_view, TRUE))
2997 /* Get hidding ids */
2998 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3000 /* Clear hidding array created by previous cut operation */
3001 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3003 /* Copy hidding array */
3004 priv->n_selected = n_selected;
3005 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3006 for (i=0; i < n_selected; i++)
3007 priv->hidding_ids[i] = g_strdup(hidding[i]);
3009 /* Hide cut folders */
3010 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3011 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3015 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3016 ModestFolderView *folder_view_dst)
3018 GtkTreeModel *filter_model = NULL;
3019 GtkTreeModel *model = NULL;
3020 GtkTreeModel *new_filter_model = NULL;
3022 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3023 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3026 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3027 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3029 /* Build new filter model */
3030 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3031 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3035 /* Set copied model */
3036 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3037 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3038 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3041 g_object_unref (new_filter_model);
3045 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3048 GtkTreeModel *model = NULL;
3049 ModestFolderViewPrivate* priv;
3051 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3053 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3054 priv->show_non_move = show;
3055 /* modest_folder_view_update_model(folder_view, */
3056 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3058 /* Hide special folders */
3059 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3060 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3061 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3065 /* Returns FALSE if it did not selected anything */
3067 _clipboard_set_selected_data (ModestFolderView *folder_view,
3070 ModestFolderViewPrivate *priv = NULL;
3071 TnyFolderStore *folder = NULL;
3072 gboolean retval = FALSE;
3074 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3075 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3077 /* Set selected data on clipboard */
3078 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3079 folder = modest_folder_view_get_selected (folder_view);
3081 /* Do not allow to select an account */
3082 if (TNY_IS_FOLDER (folder)) {
3083 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3088 g_object_unref (folder);
3094 _clear_hidding_filter (ModestFolderView *folder_view)
3096 ModestFolderViewPrivate *priv;
3099 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3100 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3102 if (priv->hidding_ids != NULL) {
3103 for (i=0; i < priv->n_selected; i++)
3104 g_free (priv->hidding_ids[i]);
3105 g_free(priv->hidding_ids);
3111 on_display_name_changed (ModestAccountMgr *mgr,
3112 const gchar *account,
3115 ModestFolderView *self;
3117 self = MODEST_FOLDER_VIEW (user_data);
3119 /* Force a redraw */
3120 #if GTK_CHECK_VERSION(2, 8, 0)
3121 GtkTreeViewColumn * tree_column;
3123 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3124 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3125 gtk_tree_view_column_queue_resize (tree_column);
3127 gtk_widget_queue_draw (GTK_WIDGET (self));