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 if (type == TNY_FOLDER_TYPE_INBOX)
420 item_name = g_strdup_printf ("%s (%d)", _("mcen_me_folder_inbox"), number);
422 item_name = g_strdup_printf ("%s (%d)", fname, number);
425 if (type == TNY_FOLDER_TYPE_INBOX)
426 item_name = g_strdup (_("mcen_me_folder_inbox"));
428 item_name = g_strdup (fname);
432 } else if (TNY_IS_ACCOUNT (instance)) {
433 /* If it's a server account */
434 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
435 item_name = g_strdup (priv->local_account_name);
437 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
438 /* fname is only correct when the items are first
439 * added to the model, not when the account is
440 * changed later, so get the name from the account
442 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
445 item_name = g_strdup (fname);
451 item_name = g_strdup ("unknown");
453 if (item_name && item_weight) {
454 /* Set the name in the treeview cell: */
455 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
457 /* Notify display name observers */
458 /* TODO: What listens for this signal, and how can it use only the new name? */
459 if (((GObject *) priv->cur_folder_store) == instance) {
460 g_signal_emit (G_OBJECT(self),
461 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
468 /* If it is a Memory card account, make sure that we have the correct name.
469 * This function will be trigerred again when the name has been retrieved: */
470 if (TNY_IS_STORE_ACCOUNT (instance) &&
471 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
473 /* Get the account name asynchronously: */
474 GetMmcAccountNameData *callback_data =
475 g_slice_new0(GetMmcAccountNameData);
476 callback_data->self = self;
478 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
480 callback_data->previous_name = g_strdup (name);
482 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
483 on_get_mmc_account_name, callback_data);
486 g_object_unref (G_OBJECT (instance));
493 GdkPixbuf *pixbuf_open;
494 GdkPixbuf *pixbuf_close;
499 get_folder_icons (TnyFolderType type, GObject *instance)
501 GdkPixbuf *pixbuf = NULL;
502 GdkPixbuf *pixbuf_open = NULL;
503 GdkPixbuf *pixbuf_close = NULL;
504 ThreePixbufs *retval = g_slice_new (ThreePixbufs);
506 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
507 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
508 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
509 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
510 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
512 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
513 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
514 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
515 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
516 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
518 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
519 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
520 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
521 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
522 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
525 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
526 /* We include the MERGE type here because it's used to create
527 the local OUTBOX folder */
528 if (type == TNY_FOLDER_TYPE_NORMAL ||
529 type == TNY_FOLDER_TYPE_UNKNOWN) {
530 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
534 case TNY_FOLDER_TYPE_INVALID:
535 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
538 case TNY_FOLDER_TYPE_ROOT:
539 if (TNY_IS_ACCOUNT (instance)) {
541 if (modest_tny_account_is_virtual_local_folders (
542 TNY_ACCOUNT (instance))) {
545 avirt_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
546 MODEST_ICON_SIZE_SMALL));
548 if (!avirt_pixbuf_open) {
549 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
550 MODEST_ICON_SIZE_SMALL);
551 avirt_pixbuf_open = gdk_pixbuf_copy (avirt_pixbuf);
552 gdk_pixbuf_composite (emblem, avirt_pixbuf_open, 0, 0,
553 MIN (gdk_pixbuf_get_width (emblem),
554 gdk_pixbuf_get_width (avirt_pixbuf_open)),
555 MIN (gdk_pixbuf_get_height (emblem),
556 gdk_pixbuf_get_height (avirt_pixbuf_open)),
557 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
558 g_object_unref (emblem);
561 if (!avirt_pixbuf_close) {
562 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
563 MODEST_ICON_SIZE_SMALL);
564 avirt_pixbuf_close = gdk_pixbuf_copy (avirt_pixbuf);
565 gdk_pixbuf_composite (emblem, avirt_pixbuf_close, 0, 0,
566 MIN (gdk_pixbuf_get_width (emblem),
567 gdk_pixbuf_get_width (avirt_pixbuf_close)),
568 MIN (gdk_pixbuf_get_height (emblem),
569 gdk_pixbuf_get_height (avirt_pixbuf_close)),
570 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
571 g_object_unref (emblem);
575 pixbuf = g_object_ref (avirt_pixbuf);
576 pixbuf_open = g_object_ref (avirt_pixbuf_open);
577 pixbuf_close = g_object_ref (avirt_pixbuf_close);
581 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
583 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
585 ammc_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_MMC,
586 MODEST_ICON_SIZE_SMALL));
588 if (!ammc_pixbuf_open) {
589 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
590 MODEST_ICON_SIZE_SMALL);
591 ammc_pixbuf_open = gdk_pixbuf_copy (ammc_pixbuf);
592 gdk_pixbuf_composite (emblem, ammc_pixbuf_open, 0, 0,
593 MIN (gdk_pixbuf_get_width (emblem),
594 gdk_pixbuf_get_width (ammc_pixbuf_open)),
595 MIN (gdk_pixbuf_get_height (emblem),
596 gdk_pixbuf_get_height (ammc_pixbuf_open)),
597 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
598 g_object_unref (emblem);
601 if (!ammc_pixbuf_close) {
602 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
603 MODEST_ICON_SIZE_SMALL);
604 ammc_pixbuf_close = gdk_pixbuf_copy (ammc_pixbuf);
605 gdk_pixbuf_composite (emblem, ammc_pixbuf_close, 0, 0,
606 MIN (gdk_pixbuf_get_width (emblem),
607 gdk_pixbuf_get_width (ammc_pixbuf_close)),
608 MIN (gdk_pixbuf_get_height (emblem),
609 gdk_pixbuf_get_height (ammc_pixbuf_close)),
610 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
611 g_object_unref (emblem);
615 pixbuf = g_object_ref (ammc_pixbuf);
616 pixbuf_open = g_object_ref (ammc_pixbuf_open);
617 pixbuf_close = g_object_ref (ammc_pixbuf_close);
622 anorm_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT,
623 MODEST_ICON_SIZE_SMALL));
624 if (!anorm_pixbuf_open) {
625 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
626 MODEST_ICON_SIZE_SMALL);
627 anorm_pixbuf_open = gdk_pixbuf_copy (anorm_pixbuf);
628 gdk_pixbuf_composite (emblem, anorm_pixbuf_open, 0, 0,
629 MIN (gdk_pixbuf_get_width (emblem),
630 gdk_pixbuf_get_width (anorm_pixbuf_open)),
631 MIN (gdk_pixbuf_get_height (emblem),
632 gdk_pixbuf_get_height (anorm_pixbuf_open)),
633 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
634 g_object_unref (emblem);
637 if (!anorm_pixbuf_close) {
638 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
639 MODEST_ICON_SIZE_SMALL);
640 anorm_pixbuf_close = gdk_pixbuf_copy (anorm_pixbuf);
641 gdk_pixbuf_composite (emblem, anorm_pixbuf_close, 0, 0,
642 MIN (gdk_pixbuf_get_width (emblem),
643 gdk_pixbuf_get_width (anorm_pixbuf_close)),
644 MIN (gdk_pixbuf_get_height (emblem),
645 gdk_pixbuf_get_height (anorm_pixbuf_close)),
646 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
647 g_object_unref (emblem);
651 pixbuf = g_object_ref (anorm_pixbuf);
652 pixbuf_open = g_object_ref (anorm_pixbuf_open);
653 pixbuf_close = g_object_ref (anorm_pixbuf_close);
659 case TNY_FOLDER_TYPE_INBOX:
662 inbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX,
663 MODEST_ICON_SIZE_SMALL));
665 if (!inbox_pixbuf_open) {
666 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
667 MODEST_ICON_SIZE_SMALL);
668 inbox_pixbuf_open = gdk_pixbuf_copy (inbox_pixbuf);
669 gdk_pixbuf_composite (emblem, inbox_pixbuf_open, 0, 0,
670 MIN (gdk_pixbuf_get_width (emblem),
671 gdk_pixbuf_get_width (inbox_pixbuf_open)),
672 MIN (gdk_pixbuf_get_height (emblem),
673 gdk_pixbuf_get_height (inbox_pixbuf_open)),
674 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
675 g_object_unref (emblem);
678 if (!inbox_pixbuf_close) {
679 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
680 MODEST_ICON_SIZE_SMALL);
681 inbox_pixbuf_close = gdk_pixbuf_copy (inbox_pixbuf);
682 gdk_pixbuf_composite (emblem, inbox_pixbuf_close, 0, 0,
683 MIN (gdk_pixbuf_get_width (emblem),
684 gdk_pixbuf_get_width (inbox_pixbuf_close)),
685 MIN (gdk_pixbuf_get_height (emblem),
686 gdk_pixbuf_get_height (inbox_pixbuf_close)),
687 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
688 g_object_unref (emblem);
692 pixbuf = g_object_ref (inbox_pixbuf);
693 pixbuf_open = g_object_ref (inbox_pixbuf_open);
694 pixbuf_close = g_object_ref (inbox_pixbuf_close);
697 case TNY_FOLDER_TYPE_OUTBOX:
699 outbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX,
700 MODEST_ICON_SIZE_SMALL));
702 if (!outbox_pixbuf_open) {
703 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
704 MODEST_ICON_SIZE_SMALL);
705 outbox_pixbuf_open = gdk_pixbuf_copy (outbox_pixbuf);
706 gdk_pixbuf_composite (emblem, outbox_pixbuf_open, 0, 0,
707 MIN (gdk_pixbuf_get_width (emblem),
708 gdk_pixbuf_get_width (outbox_pixbuf_open)),
709 MIN (gdk_pixbuf_get_height (emblem),
710 gdk_pixbuf_get_height (outbox_pixbuf_open)),
711 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
712 g_object_unref (emblem);
715 if (!outbox_pixbuf_close) {
716 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
717 MODEST_ICON_SIZE_SMALL);
718 outbox_pixbuf_close = gdk_pixbuf_copy (outbox_pixbuf);
719 gdk_pixbuf_composite (emblem, outbox_pixbuf_close, 0, 0,
720 MIN (gdk_pixbuf_get_width (emblem),
721 gdk_pixbuf_get_width (outbox_pixbuf_close)),
722 MIN (gdk_pixbuf_get_height (emblem),
723 gdk_pixbuf_get_height (outbox_pixbuf_close)),
724 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
725 g_object_unref (emblem);
729 pixbuf = g_object_ref (outbox_pixbuf);
730 pixbuf_open = g_object_ref (outbox_pixbuf_open);
731 pixbuf_close = g_object_ref (outbox_pixbuf_close);
734 case TNY_FOLDER_TYPE_JUNK:
736 junk_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK,
737 MODEST_ICON_SIZE_SMALL));
738 if (!junk_pixbuf_open) {
739 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
740 MODEST_ICON_SIZE_SMALL);
741 junk_pixbuf_open = gdk_pixbuf_copy (junk_pixbuf);
742 gdk_pixbuf_composite (emblem, junk_pixbuf_open, 0, 0,
743 MIN (gdk_pixbuf_get_width (emblem),
744 gdk_pixbuf_get_width (junk_pixbuf_open)),
745 MIN (gdk_pixbuf_get_height (emblem),
746 gdk_pixbuf_get_height (junk_pixbuf_open)),
747 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
748 g_object_unref (emblem);
751 if (!junk_pixbuf_close) {
752 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
753 MODEST_ICON_SIZE_SMALL);
754 junk_pixbuf_close = gdk_pixbuf_copy (junk_pixbuf);
755 gdk_pixbuf_composite (emblem, junk_pixbuf_close, 0, 0,
756 MIN (gdk_pixbuf_get_width (emblem),
757 gdk_pixbuf_get_width (junk_pixbuf_close)),
758 MIN (gdk_pixbuf_get_height (emblem),
759 gdk_pixbuf_get_height (junk_pixbuf_close)),
760 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
761 g_object_unref (emblem);
765 pixbuf = g_object_ref (junk_pixbuf);
766 pixbuf_open = g_object_ref (junk_pixbuf_open);
767 pixbuf_close = g_object_ref (junk_pixbuf_close);
771 case TNY_FOLDER_TYPE_SENT:
773 sent_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_SENT,
774 MODEST_ICON_SIZE_SMALL));
776 if (!sent_pixbuf_open) {
777 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
778 MODEST_ICON_SIZE_SMALL);
779 sent_pixbuf_open = gdk_pixbuf_copy (sent_pixbuf);
780 gdk_pixbuf_composite (emblem, sent_pixbuf_open, 0, 0,
781 MIN (gdk_pixbuf_get_width (emblem),
782 gdk_pixbuf_get_width (sent_pixbuf_open)),
783 MIN (gdk_pixbuf_get_height (emblem),
784 gdk_pixbuf_get_height (sent_pixbuf_open)),
785 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
786 g_object_unref (emblem);
789 if (!sent_pixbuf_close) {
790 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
791 MODEST_ICON_SIZE_SMALL);
792 sent_pixbuf_close = gdk_pixbuf_copy (sent_pixbuf);
793 gdk_pixbuf_composite (emblem, sent_pixbuf_close, 0, 0,
794 MIN (gdk_pixbuf_get_width (emblem),
795 gdk_pixbuf_get_width (sent_pixbuf_close)),
796 MIN (gdk_pixbuf_get_height (emblem),
797 gdk_pixbuf_get_height (sent_pixbuf_close)),
798 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
799 g_object_unref (emblem);
803 pixbuf = g_object_ref (sent_pixbuf);
804 pixbuf_open = g_object_ref (sent_pixbuf_open);
805 pixbuf_close = g_object_ref (sent_pixbuf_close);
809 case TNY_FOLDER_TYPE_TRASH:
811 trash_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH,
812 MODEST_ICON_SIZE_SMALL));
813 if (!trash_pixbuf_open) {
814 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
815 MODEST_ICON_SIZE_SMALL);
816 trash_pixbuf_open = gdk_pixbuf_copy (trash_pixbuf);
817 gdk_pixbuf_composite (emblem, trash_pixbuf_open, 0, 0,
818 MIN (gdk_pixbuf_get_width (emblem),
819 gdk_pixbuf_get_width (trash_pixbuf_open)),
820 MIN (gdk_pixbuf_get_height (emblem),
821 gdk_pixbuf_get_height (trash_pixbuf_open)),
822 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
823 g_object_unref (emblem);
826 if (!trash_pixbuf_close) {
827 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
828 MODEST_ICON_SIZE_SMALL);
829 trash_pixbuf_close = gdk_pixbuf_copy (trash_pixbuf);
830 gdk_pixbuf_composite (emblem, trash_pixbuf_close, 0, 0,
831 MIN (gdk_pixbuf_get_width (emblem),
832 gdk_pixbuf_get_width (trash_pixbuf_close)),
833 MIN (gdk_pixbuf_get_height (emblem),
834 gdk_pixbuf_get_height (trash_pixbuf_close)),
835 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
836 g_object_unref (emblem);
840 pixbuf = g_object_ref (trash_pixbuf);
841 pixbuf_open = g_object_ref (trash_pixbuf_open);
842 pixbuf_close = g_object_ref (trash_pixbuf_close);
845 case TNY_FOLDER_TYPE_DRAFTS:
847 draft_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS,
848 MODEST_ICON_SIZE_SMALL));
850 if (!draft_pixbuf_open) {
851 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
852 MODEST_ICON_SIZE_SMALL);
853 draft_pixbuf_open = gdk_pixbuf_copy (draft_pixbuf);
854 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0,
855 MIN (gdk_pixbuf_get_width (emblem),
856 gdk_pixbuf_get_width (draft_pixbuf_open)),
857 MIN (gdk_pixbuf_get_height (emblem),
858 gdk_pixbuf_get_height (draft_pixbuf_open)),
859 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
860 g_object_unref (emblem);
863 if (!draft_pixbuf_close) {
864 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
865 MODEST_ICON_SIZE_SMALL);
866 draft_pixbuf_close = gdk_pixbuf_copy (draft_pixbuf);
867 gdk_pixbuf_composite (emblem, draft_pixbuf_close, 0, 0,
868 MIN (gdk_pixbuf_get_width (emblem),
869 gdk_pixbuf_get_width (draft_pixbuf_close)),
870 MIN (gdk_pixbuf_get_height (emblem),
871 gdk_pixbuf_get_height (draft_pixbuf_close)),
872 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
873 g_object_unref (emblem);
877 pixbuf = g_object_ref (draft_pixbuf);
878 pixbuf_open = g_object_ref (draft_pixbuf_open);
879 pixbuf_close = g_object_ref (draft_pixbuf_close);
882 case TNY_FOLDER_TYPE_NORMAL:
885 normal_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL,
886 MODEST_ICON_SIZE_SMALL));
888 if (!normal_pixbuf_open) {
889 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
890 MODEST_ICON_SIZE_SMALL);
891 normal_pixbuf_open = gdk_pixbuf_copy (normal_pixbuf);
892 gdk_pixbuf_composite (emblem, normal_pixbuf_open, 0, 0,
893 MIN (gdk_pixbuf_get_width (emblem),
894 gdk_pixbuf_get_width (normal_pixbuf_open)),
895 MIN (gdk_pixbuf_get_height (emblem),
896 gdk_pixbuf_get_height (normal_pixbuf_open)),
897 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
898 g_object_unref (emblem);
901 if (!normal_pixbuf_close) {
902 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
903 MODEST_ICON_SIZE_SMALL);
904 normal_pixbuf_close = gdk_pixbuf_copy (normal_pixbuf);
905 gdk_pixbuf_composite (emblem, normal_pixbuf_close, 0, 0,
906 MIN (gdk_pixbuf_get_width (emblem),
907 gdk_pixbuf_get_width (normal_pixbuf_close)),
908 MIN (gdk_pixbuf_get_height (emblem),
909 gdk_pixbuf_get_height (normal_pixbuf_close)),
910 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
911 g_object_unref (emblem);
915 pixbuf = g_object_ref (normal_pixbuf);
916 pixbuf_open = g_object_ref (normal_pixbuf_open);
917 pixbuf_close = g_object_ref (normal_pixbuf_close);
923 retval->pixbuf = pixbuf;
924 retval->pixbuf_open = pixbuf_open;
925 retval->pixbuf_close = pixbuf_close;
932 free_pixbufs (ThreePixbufs *pixbufs)
934 g_object_unref (pixbufs->pixbuf);
935 g_object_unref (pixbufs->pixbuf_open);
936 g_object_unref (pixbufs->pixbuf_close);
937 g_slice_free (ThreePixbufs, pixbufs);
941 icon_cell_data (GtkTreeViewColumn *column,
942 GtkCellRenderer *renderer,
943 GtkTreeModel *tree_model,
947 GObject *rendobj = NULL, *instance = NULL;
948 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
949 gboolean has_children;
950 ThreePixbufs *pixbufs;
952 rendobj = (GObject *) renderer;
954 gtk_tree_model_get (tree_model, iter,
955 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
956 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
962 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
963 pixbufs = get_folder_icons (type, instance);
964 g_object_unref (instance);
967 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
970 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
971 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
974 free_pixbufs (pixbufs);
980 add_columns (GtkWidget *treeview)
982 GtkTreeViewColumn *column;
983 GtkCellRenderer *renderer;
984 GtkTreeSelection *sel;
987 column = gtk_tree_view_column_new ();
989 /* Set icon and text render function */
990 renderer = gtk_cell_renderer_pixbuf_new();
991 gtk_tree_view_column_pack_start (column, renderer, FALSE);
992 gtk_tree_view_column_set_cell_data_func(column, renderer,
993 icon_cell_data, treeview, NULL);
995 renderer = gtk_cell_renderer_text_new();
996 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END,
997 "ellipsize-set", TRUE, NULL);
998 gtk_tree_view_column_pack_start (column, renderer, TRUE);
999 gtk_tree_view_column_set_cell_data_func(column, renderer,
1000 text_cell_data, treeview, NULL);
1002 /* Set selection mode */
1003 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1004 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1006 /* Set treeview appearance */
1007 gtk_tree_view_column_set_spacing (column, 2);
1008 gtk_tree_view_column_set_resizable (column, TRUE);
1009 gtk_tree_view_column_set_fixed_width (column, TRUE);
1010 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1011 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1014 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1018 modest_folder_view_init (ModestFolderView *obj)
1020 ModestFolderViewPrivate *priv;
1023 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1025 priv->timer_expander = 0;
1026 priv->account_store = NULL;
1028 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1029 priv->cur_folder_store = NULL;
1030 priv->visible_account_id = NULL;
1031 priv->folder_to_select = NULL;
1033 priv->reexpand = TRUE;
1035 /* Initialize the local account name */
1036 conf = modest_runtime_get_conf();
1037 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1039 /* Init email clipboard */
1040 priv->clipboard = modest_runtime_get_email_clipboard ();
1041 priv->hidding_ids = NULL;
1042 priv->n_selected = 0;
1043 priv->reselect = FALSE;
1044 priv->show_non_move = TRUE;
1046 /* Build treeview */
1047 add_columns (GTK_WIDGET (obj));
1049 /* Setup drag and drop */
1050 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1052 /* Connect signals */
1053 g_signal_connect (G_OBJECT (obj),
1055 G_CALLBACK (on_key_pressed), NULL);
1057 priv->display_name_changed_signal =
1058 g_signal_connect (modest_runtime_get_account_mgr (),
1059 "display_name_changed",
1060 G_CALLBACK (on_display_name_changed),
1064 * Track changes in the local account name (in the device it
1065 * will be the device name)
1067 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1069 G_CALLBACK(on_configuration_key_changed),
1074 tny_account_store_view_init (gpointer g, gpointer iface_data)
1076 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1078 klass->set_account_store = modest_folder_view_set_account_store;
1082 modest_folder_view_finalize (GObject *obj)
1084 ModestFolderViewPrivate *priv;
1085 GtkTreeSelection *sel;
1087 g_return_if_fail (obj);
1089 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1091 if (priv->timer_expander != 0) {
1092 g_source_remove (priv->timer_expander);
1093 priv->timer_expander = 0;
1096 if (priv->account_store) {
1097 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1098 priv->account_inserted_signal);
1099 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1100 priv->account_removed_signal);
1101 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1102 priv->account_changed_signal);
1103 g_object_unref (G_OBJECT(priv->account_store));
1104 priv->account_store = NULL;
1108 g_object_unref (G_OBJECT (priv->query));
1112 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1113 if (priv->folder_to_select) {
1114 g_object_unref (G_OBJECT(priv->folder_to_select));
1115 priv->folder_to_select = NULL;
1118 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1120 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1122 g_free (priv->local_account_name);
1123 g_free (priv->visible_account_id);
1125 if (priv->conf_key_signal) {
1126 g_signal_handler_disconnect (modest_runtime_get_conf (),
1127 priv->conf_key_signal);
1128 priv->conf_key_signal = 0;
1131 if (priv->cur_folder_store) {
1132 if (TNY_IS_FOLDER(priv->cur_folder_store)) {
1133 ModestMailOperation *mail_op;
1135 mail_op = modest_mail_operation_new (NULL);
1136 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1138 modest_mail_operation_sync_folder (mail_op, TNY_FOLDER (priv->cur_folder_store), FALSE);
1141 g_object_unref (priv->cur_folder_store);
1142 priv->cur_folder_store = NULL;
1145 /* Clear hidding array created by cut operation */
1146 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1148 G_OBJECT_CLASS(parent_class)->finalize (obj);
1153 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1155 ModestFolderViewPrivate *priv;
1158 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1159 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1161 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1162 device = tny_account_store_get_device (account_store);
1164 if (G_UNLIKELY (priv->account_store)) {
1166 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1167 priv->account_inserted_signal))
1168 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1169 priv->account_inserted_signal);
1170 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1171 priv->account_removed_signal))
1172 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1173 priv->account_removed_signal);
1174 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1175 priv->account_changed_signal))
1176 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1177 priv->account_changed_signal);
1178 g_object_unref (G_OBJECT (priv->account_store));
1181 priv->account_store = g_object_ref (G_OBJECT (account_store));
1183 priv->account_removed_signal =
1184 g_signal_connect (G_OBJECT(account_store), "account_removed",
1185 G_CALLBACK (on_account_removed), self);
1187 priv->account_inserted_signal =
1188 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1189 G_CALLBACK (on_account_inserted), self);
1191 priv->account_changed_signal =
1192 g_signal_connect (G_OBJECT(account_store), "account_changed",
1193 G_CALLBACK (on_account_changed), self);
1195 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1197 g_object_unref (G_OBJECT (device));
1201 on_account_inserted (TnyAccountStore *account_store,
1202 TnyAccount *account,
1205 ModestFolderViewPrivate *priv;
1206 GtkTreeModel *sort_model, *filter_model;
1208 /* Ignore transport account insertions, we're not showing them
1209 in the folder view */
1210 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1213 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1215 /* If we're adding a new account, and there is no previous
1216 one, we need to select the visible server account */
1217 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1218 !priv->visible_account_id)
1219 modest_widget_memory_restore (modest_runtime_get_conf(),
1220 G_OBJECT (user_data),
1221 MODEST_CONF_FOLDER_VIEW_KEY);
1223 /* Get the inner model */
1224 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1225 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1227 /* Insert the account in the model */
1228 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1229 G_OBJECT (account));
1231 /* Refilter the model */
1232 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1237 on_account_changed (TnyAccountStore *account_store,
1238 TnyAccount *tny_account,
1241 ModestFolderViewPrivate *priv;
1242 GtkTreeModel *sort_model, *filter_model;
1244 /* Ignore transport account insertions, we're not showing them
1245 in the folder view */
1246 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1249 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1251 /* Get the inner model */
1252 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1253 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1255 /* Remove the account from the model */
1256 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1257 G_OBJECT (tny_account));
1259 /* Insert the account in the model */
1260 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1261 G_OBJECT (tny_account));
1263 /* Refilter the model */
1264 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1269 * Selects the first inbox or the local account in an idle
1272 on_idle_select_first_inbox_or_local (gpointer user_data)
1274 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1276 modest_folder_view_select_first_inbox_or_local (self);
1283 on_account_removed (TnyAccountStore *account_store,
1284 TnyAccount *account,
1287 ModestFolderView *self = NULL;
1288 ModestFolderViewPrivate *priv;
1289 GtkTreeModel *sort_model, *filter_model;
1290 GtkTreeSelection *sel = NULL;
1291 gboolean same_account_selected = FALSE;
1293 /* Ignore transport account removals, we're not showing them
1294 in the folder view */
1295 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1298 self = MODEST_FOLDER_VIEW (user_data);
1299 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1301 /* Invalidate the cur_folder_store only if the selected folder
1302 belongs to the account that is being removed */
1303 if (priv->cur_folder_store) {
1304 TnyAccount *selected_folder_account = NULL;
1306 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1307 selected_folder_account =
1308 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1310 selected_folder_account =
1311 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1314 if (selected_folder_account == account) {
1315 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1316 gtk_tree_selection_unselect_all (sel);
1317 same_account_selected = TRUE;
1319 g_object_unref (selected_folder_account);
1322 /* Invalidate row to select only if the folder to select
1323 belongs to the account that is being removed*/
1324 if (priv->folder_to_select) {
1325 TnyAccount *folder_to_select_account = NULL;
1327 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1328 if (folder_to_select_account == account) {
1329 modest_folder_view_disable_next_folder_selection (self);
1330 g_object_unref (priv->folder_to_select);
1331 priv->folder_to_select = NULL;
1333 g_object_unref (folder_to_select_account);
1336 /* Remove the account from the model */
1337 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1338 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1339 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1340 G_OBJECT (account));
1342 /* If the removed account is the currently viewed one then
1343 clear the configuration value. The new visible account will be the default account */
1344 if (priv->visible_account_id &&
1345 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1347 /* Clear the current visible account_id */
1348 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1350 /* Call the restore method, this will set the new visible account */
1351 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1352 MODEST_CONF_FOLDER_VIEW_KEY);
1355 /* Refilter the model */
1356 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1358 /* Select the first INBOX if the currently selected folder
1359 belongs to the account that is being deleted */
1360 if (same_account_selected)
1361 g_idle_add (on_idle_select_first_inbox_or_local, self);
1365 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1367 GtkTreeViewColumn *col;
1369 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1371 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1373 g_printerr ("modest: failed get column for title\n");
1377 gtk_tree_view_column_set_title (col, title);
1378 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1383 modest_folder_view_on_map (ModestFolderView *self,
1384 GdkEventExpose *event,
1387 ModestFolderViewPrivate *priv;
1389 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1391 /* This won't happen often */
1392 if (G_UNLIKELY (priv->reselect)) {
1393 /* Select the first inbox or the local account if not found */
1395 /* TODO: this could cause a lock at startup, so we
1396 comment it for the moment. We know that this will
1397 be a bug, because the INBOX is not selected, but we
1398 need to rewrite some parts of Modest to avoid the
1399 deathlock situation */
1400 /* TODO: check if this is still the case */
1401 priv->reselect = FALSE;
1402 modest_folder_view_select_first_inbox_or_local (self);
1403 /* Notify the display name observers */
1404 g_signal_emit (G_OBJECT(self),
1405 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1409 if (priv->reexpand) {
1410 expand_root_items (self);
1411 priv->reexpand = FALSE;
1418 modest_folder_view_new (TnyFolderStoreQuery *query)
1421 ModestFolderViewPrivate *priv;
1422 GtkTreeSelection *sel;
1424 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1425 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1428 priv->query = g_object_ref (query);
1430 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1431 priv->changed_signal = g_signal_connect (sel, "changed",
1432 G_CALLBACK (on_selection_changed), self);
1434 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1436 return GTK_WIDGET(self);
1439 /* this feels dirty; any other way to expand all the root items? */
1441 expand_root_items (ModestFolderView *self)
1444 GtkTreeModel *model;
1447 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1448 path = gtk_tree_path_new_first ();
1450 /* all folders should have child items, so.. */
1452 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1453 gtk_tree_path_next (path);
1454 } while (gtk_tree_model_get_iter (model, &iter, path));
1456 gtk_tree_path_free (path);
1460 * We use this function to implement the
1461 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1462 * account in this case, and the local folders.
1465 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1467 ModestFolderViewPrivate *priv;
1468 gboolean retval = TRUE;
1469 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1470 GObject *instance = NULL;
1471 const gchar *id = NULL;
1473 gboolean found = FALSE;
1474 gboolean cleared = FALSE;
1476 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1477 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1479 gtk_tree_model_get (model, iter,
1480 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1481 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1484 /* Do not show if there is no instance, this could indeed
1485 happen when the model is being modified while it's being
1486 drawn. This could occur for example when moving folders
1491 if (type == TNY_FOLDER_TYPE_ROOT) {
1492 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1493 account instead of a folder. */
1494 if (TNY_IS_ACCOUNT (instance)) {
1495 TnyAccount *acc = TNY_ACCOUNT (instance);
1496 const gchar *account_id = tny_account_get_id (acc);
1498 /* If it isn't a special folder,
1499 * don't show it unless it is the visible account: */
1500 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1501 !modest_tny_account_is_virtual_local_folders (acc) &&
1502 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1504 /* Show only the visible account id */
1505 if (priv->visible_account_id) {
1506 if (strcmp (account_id, priv->visible_account_id))
1513 /* Never show these to the user. They are merged into one folder
1514 * in the local-folders account instead: */
1515 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1520 /* Check hiding (if necessary) */
1521 cleared = modest_email_clipboard_cleared (priv->clipboard);
1522 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1523 id = tny_folder_get_id (TNY_FOLDER(instance));
1524 if (priv->hidding_ids != NULL)
1525 for (i=0; i < priv->n_selected && !found; i++)
1526 if (priv->hidding_ids[i] != NULL && id != NULL)
1527 found = (!strcmp (priv->hidding_ids[i], id));
1533 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1534 folder as no message can be move there according to UI specs */
1535 if (!priv->show_non_move) {
1537 case TNY_FOLDER_TYPE_OUTBOX:
1538 case TNY_FOLDER_TYPE_SENT:
1539 case TNY_FOLDER_TYPE_DRAFTS:
1542 case TNY_FOLDER_TYPE_UNKNOWN:
1543 case TNY_FOLDER_TYPE_NORMAL:
1544 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1545 if (type == TNY_FOLDER_TYPE_INVALID)
1546 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1548 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1549 type == TNY_FOLDER_TYPE_SENT
1550 || type == TNY_FOLDER_TYPE_DRAFTS)
1559 g_object_unref (instance);
1566 modest_folder_view_update_model (ModestFolderView *self,
1567 TnyAccountStore *account_store)
1569 ModestFolderViewPrivate *priv;
1570 GtkTreeModel *model /* , *old_model */;
1571 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1573 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1574 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1577 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1579 /* Notify that there is no folder selected */
1580 g_signal_emit (G_OBJECT(self),
1581 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1583 if (priv->cur_folder_store) {
1584 g_object_unref (priv->cur_folder_store);
1585 priv->cur_folder_store = NULL;
1588 /* FIXME: the local accounts are not shown when the query
1589 selects only the subscribed folders */
1590 model = tny_gtk_folder_store_tree_model_new (NULL);
1592 /* Get the accounts: */
1593 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1595 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1597 sortable = gtk_tree_model_sort_new_with_model (model);
1598 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1599 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1600 GTK_SORT_ASCENDING);
1601 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1602 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1603 cmp_rows, NULL, NULL);
1605 /* Create filter model */
1606 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1607 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1613 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1614 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1615 (GCallback) on_row_inserted_maybe_select_folder, self);
1618 g_object_unref (model);
1619 g_object_unref (filter_model);
1620 g_object_unref (sortable);
1622 /* Force a reselection of the INBOX next time the widget is shown */
1623 priv->reselect = TRUE;
1630 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1632 GtkTreeModel *model = NULL;
1633 TnyFolderStore *folder = NULL;
1635 ModestFolderView *tree_view = NULL;
1636 ModestFolderViewPrivate *priv = NULL;
1637 gboolean selected = FALSE;
1639 g_return_if_fail (sel);
1640 g_return_if_fail (user_data);
1642 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1644 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1646 tree_view = MODEST_FOLDER_VIEW (user_data);
1649 gtk_tree_model_get (model, &iter,
1650 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1653 /* If the folder is the same do not notify */
1654 if (folder && priv->cur_folder_store == folder) {
1655 g_object_unref (folder);
1660 /* Current folder was unselected */
1661 if (priv->cur_folder_store) {
1662 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1663 priv->cur_folder_store, FALSE);
1665 if (TNY_IS_FOLDER(priv->cur_folder_store))
1666 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1667 FALSE, NULL, NULL, NULL);
1669 /* FALSE --> don't expunge the messages */
1671 g_object_unref (priv->cur_folder_store);
1672 priv->cur_folder_store = NULL;
1675 /* New current references */
1676 priv->cur_folder_store = folder;
1678 /* New folder has been selected. Do not notify if there is
1679 nothing new selected */
1681 g_signal_emit (G_OBJECT(tree_view),
1682 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1683 0, priv->cur_folder_store, TRUE);
1688 modest_folder_view_get_selected (ModestFolderView *self)
1690 ModestFolderViewPrivate *priv;
1692 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1694 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1695 if (priv->cur_folder_store)
1696 g_object_ref (priv->cur_folder_store);
1698 return priv->cur_folder_store;
1702 get_cmp_rows_type_pos (GObject *folder)
1704 /* Remote accounts -> Local account -> MMC account .*/
1707 if (TNY_IS_ACCOUNT (folder) &&
1708 modest_tny_account_is_virtual_local_folders (
1709 TNY_ACCOUNT (folder))) {
1711 } else if (TNY_IS_ACCOUNT (folder)) {
1712 TnyAccount *account = TNY_ACCOUNT (folder);
1713 const gchar *account_id = tny_account_get_id (account);
1714 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1720 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1721 return -1; /* Should never happen */
1726 get_cmp_subfolder_type_pos (TnyFolderType t)
1728 /* Inbox, Outbox, Drafts, Sent, User */
1732 case TNY_FOLDER_TYPE_INBOX:
1735 case TNY_FOLDER_TYPE_OUTBOX:
1738 case TNY_FOLDER_TYPE_DRAFTS:
1741 case TNY_FOLDER_TYPE_SENT:
1750 * This function orders the mail accounts according to these rules:
1751 * 1st - remote accounts
1752 * 2nd - local account
1756 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1760 gchar *name1 = NULL;
1761 gchar *name2 = NULL;
1762 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1763 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1764 GObject *folder1 = NULL;
1765 GObject *folder2 = NULL;
1767 gtk_tree_model_get (tree_model, iter1,
1768 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1769 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1770 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1772 gtk_tree_model_get (tree_model, iter2,
1773 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1774 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1775 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1778 /* Return if we get no folder. This could happen when folder
1779 operations are happening. The model is updated after the
1780 folder copy/move actually occurs, so there could be
1781 situations where the model to be drawn is not correct */
1782 if (!folder1 || !folder2)
1785 if (type == TNY_FOLDER_TYPE_ROOT) {
1786 /* Compare the types, so that
1787 * Remote accounts -> Local account -> MMC account .*/
1788 const gint pos1 = get_cmp_rows_type_pos (folder1);
1789 const gint pos2 = get_cmp_rows_type_pos (folder2);
1790 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1791 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1794 else if (pos1 > pos2)
1797 /* Compare items of the same type: */
1799 TnyAccount *account1 = NULL;
1800 if (TNY_IS_ACCOUNT (folder1))
1801 account1 = TNY_ACCOUNT (folder1);
1803 TnyAccount *account2 = NULL;
1804 if (TNY_IS_ACCOUNT (folder2))
1805 account2 = TNY_ACCOUNT (folder2);
1807 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1808 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1810 if (!account_id && !account_id2) {
1812 } else if (!account_id) {
1814 } else if (!account_id2) {
1816 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1819 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1823 gint cmp1 = 0, cmp2 = 0;
1824 /* get the parent to know if it's a local folder */
1827 gboolean has_parent;
1828 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1830 GObject *parent_folder;
1831 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1832 gtk_tree_model_get (tree_model, &parent,
1833 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1834 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1836 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1837 TNY_IS_ACCOUNT (parent_folder)) {
1838 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1839 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1840 (TNY_FOLDER (folder1)));
1841 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1842 (TNY_FOLDER (folder2)));
1843 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1844 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1847 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1853 g_object_unref (parent_folder);
1856 /* if they are not local folders */
1858 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1859 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1863 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1865 cmp = (cmp1 - cmp2);
1870 g_object_unref(G_OBJECT(folder1));
1872 g_object_unref(G_OBJECT(folder2));
1880 /*****************************************************************************/
1881 /* DRAG and DROP stuff */
1882 /*****************************************************************************/
1884 * This function fills the #GtkSelectionData with the row and the
1885 * model that has been dragged. It's called when this widget is a
1886 * source for dnd after the event drop happened
1889 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1890 guint info, guint time, gpointer data)
1892 GtkTreeSelection *selection;
1893 GtkTreeModel *model;
1895 GtkTreePath *source_row;
1897 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1898 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1900 source_row = gtk_tree_model_get_path (model, &iter);
1901 gtk_tree_set_row_drag_data (selection_data,
1905 gtk_tree_path_free (source_row);
1909 typedef struct _DndHelper {
1910 ModestFolderView *folder_view;
1911 gboolean delete_source;
1912 GtkTreePath *source_row;
1913 GdkDragContext *context;
1918 dnd_helper_destroyer (DndHelper *helper)
1920 /* Free the helper */
1921 g_object_unref (helper->folder_view);
1922 gtk_tree_path_free (helper->source_row);
1923 g_slice_free (DndHelper, helper);
1927 xfer_cb (ModestMailOperation *mail_op,
1933 helper = (DndHelper *) user_data;
1935 if (modest_mail_operation_get_status (mail_op) ==
1936 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1942 /* Notify the drag source. Never call delete, the monitor will
1943 do the job if needed */
1944 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1946 /* Free the helper */
1947 dnd_helper_destroyer (helper);
1951 xfer_msgs_cb (ModestMailOperation *mail_op,
1955 xfer_cb (mail_op, user_data);
1959 xfer_folder_cb (ModestMailOperation *mail_op,
1960 TnyFolder *new_folder,
1964 GtkWidget *folder_view;
1966 helper = (DndHelper *) user_data;
1967 folder_view = g_object_ref (helper->folder_view);
1970 xfer_cb (mail_op, user_data);
1972 /* Select the folder */
1974 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1976 g_object_unref (folder_view);
1980 /* get the folder for the row the treepath refers to. */
1981 /* folder must be unref'd */
1982 static TnyFolderStore *
1983 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1986 TnyFolderStore *folder = NULL;
1988 if (gtk_tree_model_get_iter (model,&iter, path))
1989 gtk_tree_model_get (model, &iter,
1990 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1996 * This function is used by drag_data_received_cb to manage drag and
1997 * drop of a header, i.e, and drag from the header view to the folder
2001 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2002 GtkTreeModel *dest_model,
2003 GtkTreePath *dest_row,
2004 GtkSelectionData *selection_data,
2007 TnyList *headers = NULL;
2008 TnyFolder *folder = NULL;
2009 TnyFolderType folder_type;
2010 ModestMailOperation *mail_op = NULL;
2011 GtkTreeIter source_iter, dest_iter;
2012 ModestWindowMgr *mgr = NULL;
2013 ModestWindow *main_win = NULL;
2014 gchar **uris, **tmp;
2017 /* Build the list of headers */
2018 mgr = modest_runtime_get_window_mgr ();
2019 headers = tny_simple_list_new ();
2020 uris = modest_dnd_selection_data_get_paths (selection_data);
2023 while (*tmp != NULL) {
2028 path = gtk_tree_path_new_from_string (*tmp);
2029 gtk_tree_model_get_iter (source_model, &source_iter, path);
2030 gtk_tree_model_get (source_model, &source_iter,
2031 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2034 /* Do not enable d&d of headers already opened */
2035 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2036 tny_list_append (headers, G_OBJECT (header));
2038 /* Free and go on */
2039 gtk_tree_path_free (path);
2040 g_object_unref (header);
2045 /* Get the target folder */
2046 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2047 gtk_tree_model_get (dest_model, &dest_iter,
2048 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2051 if (!folder || !TNY_IS_FOLDER(folder)) {
2052 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2056 folder_type = modest_tny_folder_guess_folder_type (folder);
2057 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2058 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2059 goto cleanup; /* cannot move messages there */
2062 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2063 /* g_warning ("folder not writable"); */
2064 goto cleanup; /* verboten! */
2067 /* Ask for confirmation to move */
2068 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2070 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2074 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2076 if (response == GTK_RESPONSE_CANCEL)
2079 /* Transfer messages */
2080 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2081 modest_ui_actions_move_folder_error_handler,
2084 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2087 modest_mail_operation_xfer_msgs (mail_op,
2090 helper->delete_source,
2091 xfer_msgs_cb, helper);
2095 if (G_IS_OBJECT(mail_op))
2096 g_object_unref (G_OBJECT (mail_op));
2097 if (G_IS_OBJECT(folder))
2098 g_object_unref (G_OBJECT (folder));
2099 if (G_IS_OBJECT(headers))
2100 g_object_unref (headers);
2104 TnyFolderStore *src_folder;
2105 TnyFolderStore *dst_folder;
2106 ModestFolderView *folder_view;
2111 dnd_folder_info_destroyer (DndFolderInfo *info)
2113 if (info->src_folder)
2114 g_object_unref (info->src_folder);
2115 if (info->dst_folder)
2116 g_object_unref (info->dst_folder);
2117 if (info->folder_view)
2118 g_object_unref (info->folder_view);
2119 g_slice_free (DndFolderInfo, info);
2123 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2124 GtkWindow *parent_window,
2125 TnyAccount *account)
2127 time_t dnd_time = info->helper->time;
2128 GdkDragContext *context = info->helper->context;
2131 modest_ui_actions_on_account_connection_error (parent_window, account);
2133 /* Free the helper & info */
2134 dnd_helper_destroyer (info->helper);
2135 dnd_folder_info_destroyer (info);
2137 /* Notify the drag source. Never call delete, the monitor will
2138 do the job if needed */
2139 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2144 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2146 GtkWindow *parent_window,
2147 TnyAccount *account,
2150 DndFolderInfo *info = NULL;
2151 ModestMailOperation *mail_op;
2153 info = (DndFolderInfo *) user_data;
2155 if (err || canceled) {
2156 dnd_on_connection_failed_destroyer (info, parent_window, account);
2160 /* Do the mail operation */
2161 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2162 modest_ui_actions_move_folder_error_handler,
2163 info->src_folder, NULL);
2165 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2168 /* Transfer the folder */
2169 modest_mail_operation_xfer_folder (mail_op,
2170 TNY_FOLDER (info->src_folder),
2172 info->helper->delete_source,
2176 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2177 /* TNY_FOLDER (info->dst_folder), TRUE); */
2179 g_object_unref (G_OBJECT (mail_op));
2184 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2186 GtkWindow *parent_window,
2187 TnyAccount *account,
2190 DndFolderInfo *info = NULL;
2192 info = (DndFolderInfo *) user_data;
2194 if (err || canceled) {
2195 dnd_on_connection_failed_destroyer (info, parent_window, account);
2199 /* Connect to source folder and perform the copy/move */
2200 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2202 drag_and_drop_from_folder_view_src_folder_performer,
2207 * This function is used by drag_data_received_cb to manage drag and
2208 * drop of a folder, i.e, and drag from the folder view to the same
2212 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2213 GtkTreeModel *dest_model,
2214 GtkTreePath *dest_row,
2215 GtkSelectionData *selection_data,
2218 GtkTreeIter dest_iter, iter;
2219 TnyFolderStore *dest_folder = NULL;
2220 TnyFolderStore *folder = NULL;
2221 gboolean forbidden = FALSE;
2223 DndFolderInfo *info = NULL;
2225 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2227 g_warning ("%s: BUG: no main window", __FUNCTION__);
2232 /* check the folder rules for the destination */
2233 folder = tree_path_to_folder (dest_model, dest_row);
2234 if (TNY_IS_FOLDER(folder)) {
2235 ModestTnyFolderRules rules =
2236 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2237 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2238 } else if (TNY_IS_FOLDER_STORE(folder)) {
2239 /* enable local root as destination for folders */
2240 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2241 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2244 g_object_unref (folder);
2247 /* check the folder rules for the source */
2248 folder = tree_path_to_folder (source_model, helper->source_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_MOVEABLE;
2255 g_object_unref (folder);
2259 /* Check if the drag is possible */
2260 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2261 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2262 gtk_tree_path_free (helper->source_row);
2263 g_slice_free (DndHelper, helper);
2268 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2269 gtk_tree_model_get (dest_model, &dest_iter,
2270 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2272 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2273 gtk_tree_model_get (source_model, &iter,
2274 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2277 /* Create the info for the performer */
2278 info = g_slice_new (DndFolderInfo);
2279 info->src_folder = g_object_ref (folder);
2280 info->dst_folder = g_object_ref (dest_folder);
2281 info->folder_view = g_object_ref (helper->folder_view);
2282 info->helper = helper;
2284 /* Connect to the destination folder and perform the copy/move */
2285 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2287 drag_and_drop_from_folder_view_dst_folder_performer,
2291 g_object_unref (dest_folder);
2292 g_object_unref (folder);
2296 * This function receives the data set by the "drag-data-get" signal
2297 * handler. This information comes within the #GtkSelectionData. This
2298 * function will manage both the drags of folders of the treeview and
2299 * drags of headers of the header view widget.
2302 on_drag_data_received (GtkWidget *widget,
2303 GdkDragContext *context,
2306 GtkSelectionData *selection_data,
2311 GtkWidget *source_widget;
2312 GtkTreeModel *dest_model, *source_model;
2313 GtkTreePath *source_row, *dest_row;
2314 GtkTreeViewDropPosition pos;
2315 gboolean success = FALSE, delete_source = FALSE;
2316 DndHelper *helper = NULL;
2318 /* Do not allow further process */
2319 g_signal_stop_emission_by_name (widget, "drag-data-received");
2320 source_widget = gtk_drag_get_source_widget (context);
2322 /* Get the action */
2323 if (context->action == GDK_ACTION_MOVE) {
2324 delete_source = TRUE;
2326 /* Notify that there is no folder selected. We need to
2327 do this in order to update the headers view (and
2328 its monitors, because when moving, the old folder
2329 won't longer exist. We can not wait for the end of
2330 the operation, because the operation won't start if
2331 the folder is in use */
2332 if (source_widget == widget) {
2333 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2334 gtk_tree_selection_unselect_all (sel);
2338 /* Check if the get_data failed */
2339 if (selection_data == NULL || selection_data->length < 0)
2340 gtk_drag_finish (context, success, FALSE, time);
2342 /* Select the destination model */
2343 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2345 /* Get the path to the destination row. Can not call
2346 gtk_tree_view_get_drag_dest_row() because the source row
2347 is not selected anymore */
2348 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2351 /* Only allow drops IN other rows */
2353 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2354 pos == GTK_TREE_VIEW_DROP_AFTER)
2355 gtk_drag_finish (context, success, FALSE, time);
2357 /* Create the helper */
2358 helper = g_slice_new0 (DndHelper);
2359 helper->delete_source = delete_source;
2360 helper->context = context;
2361 helper->time = time;
2362 helper->folder_view = g_object_ref (widget);
2364 /* Drags from the header view */
2365 if (source_widget != widget) {
2366 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2368 drag_and_drop_from_header_view (source_model,
2374 /* Get the source model and row */
2375 gtk_tree_get_row_drag_data (selection_data,
2378 helper->source_row = gtk_tree_path_copy (source_row);
2380 drag_and_drop_from_folder_view (source_model,
2386 gtk_tree_path_free (source_row);
2390 gtk_tree_path_free (dest_row);
2394 * We define a "drag-drop" signal handler because we do not want to
2395 * use the default one, because the default one always calls
2396 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2397 * signal handler, because there we have all the information available
2398 * to know if the dnd was a success or not.
2401 drag_drop_cb (GtkWidget *widget,
2402 GdkDragContext *context,
2410 if (!context->targets)
2413 /* Check if we're dragging a folder row */
2414 target = gtk_drag_dest_find_target (widget, context, NULL);
2416 /* Request the data from the source. */
2417 gtk_drag_get_data(widget, context, target, time);
2423 * This function expands a node of a tree view if it's not expanded
2424 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2425 * does that, so that's why they're here.
2428 expand_row_timeout (gpointer data)
2430 GtkTreeView *tree_view = data;
2431 GtkTreePath *dest_path = NULL;
2432 GtkTreeViewDropPosition pos;
2433 gboolean result = FALSE;
2435 gdk_threads_enter ();
2437 gtk_tree_view_get_drag_dest_row (tree_view,
2442 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2443 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2444 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2445 gtk_tree_path_free (dest_path);
2449 gtk_tree_path_free (dest_path);
2454 gdk_threads_leave ();
2460 * This function is called whenever the pointer is moved over a widget
2461 * while dragging some data. It installs a timeout that will expand a
2462 * node of the treeview if not expanded yet. This function also calls
2463 * gdk_drag_status in order to set the suggested action that will be
2464 * used by the "drag-data-received" signal handler to know if we
2465 * should do a move or just a copy of the data.
2468 on_drag_motion (GtkWidget *widget,
2469 GdkDragContext *context,
2475 GtkTreeViewDropPosition pos;
2476 GtkTreePath *dest_row;
2477 GtkTreeModel *dest_model;
2478 ModestFolderViewPrivate *priv;
2479 GdkDragAction suggested_action;
2480 gboolean valid_location = FALSE;
2481 TnyFolderStore *folder = NULL;
2483 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2485 if (priv->timer_expander != 0) {
2486 g_source_remove (priv->timer_expander);
2487 priv->timer_expander = 0;
2490 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2495 /* Do not allow drops between folders */
2497 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2498 pos == GTK_TREE_VIEW_DROP_AFTER) {
2499 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2500 gdk_drag_status(context, 0, time);
2501 valid_location = FALSE;
2504 valid_location = TRUE;
2507 /* Check that the destination folder is writable */
2508 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2509 folder = tree_path_to_folder (dest_model, dest_row);
2510 if (folder && TNY_IS_FOLDER (folder)) {
2511 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2513 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2514 valid_location = FALSE;
2519 /* Expand the selected row after 1/2 second */
2520 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2521 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2523 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2525 /* Select the desired action. By default we pick MOVE */
2526 suggested_action = GDK_ACTION_MOVE;
2528 if (context->actions == GDK_ACTION_COPY)
2529 gdk_drag_status(context, GDK_ACTION_COPY, time);
2530 else if (context->actions == GDK_ACTION_MOVE)
2531 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2532 else if (context->actions & suggested_action)
2533 gdk_drag_status(context, suggested_action, time);
2535 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2539 g_object_unref (folder);
2541 gtk_tree_path_free (dest_row);
2543 g_signal_stop_emission_by_name (widget, "drag-motion");
2545 return valid_location;
2549 * This function sets the treeview as a source and a target for dnd
2550 * events. It also connects all the requirede signals.
2553 setup_drag_and_drop (GtkTreeView *self)
2555 /* Set up the folder view as a dnd destination. Set only the
2556 highlight flag, otherwise gtk will have a different
2558 gtk_drag_dest_set (GTK_WIDGET (self),
2559 GTK_DEST_DEFAULT_HIGHLIGHT,
2560 folder_view_drag_types,
2561 G_N_ELEMENTS (folder_view_drag_types),
2562 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2564 g_signal_connect (G_OBJECT (self),
2565 "drag_data_received",
2566 G_CALLBACK (on_drag_data_received),
2570 /* Set up the treeview as a dnd source */
2571 gtk_drag_source_set (GTK_WIDGET (self),
2573 folder_view_drag_types,
2574 G_N_ELEMENTS (folder_view_drag_types),
2575 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2577 g_signal_connect (G_OBJECT (self),
2579 G_CALLBACK (on_drag_motion),
2582 g_signal_connect (G_OBJECT (self),
2584 G_CALLBACK (on_drag_data_get),
2587 g_signal_connect (G_OBJECT (self),
2589 G_CALLBACK (drag_drop_cb),
2594 * This function manages the navigation through the folders using the
2595 * keyboard or the hardware keys in the device
2598 on_key_pressed (GtkWidget *self,
2602 GtkTreeSelection *selection;
2604 GtkTreeModel *model;
2605 gboolean retval = FALSE;
2607 /* Up and Down are automatically managed by the treeview */
2608 if (event->keyval == GDK_Return) {
2609 /* Expand/Collapse the selected row */
2610 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2611 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2614 path = gtk_tree_model_get_path (model, &iter);
2616 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2617 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2619 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2620 gtk_tree_path_free (path);
2622 /* No further processing */
2630 * We listen to the changes in the local folder account name key,
2631 * because we want to show the right name in the view. The local
2632 * folder account name corresponds to the device name in the Maemo
2633 * version. We do this because we do not want to query gconf on each
2634 * tree view refresh. It's better to cache it and change whenever
2638 on_configuration_key_changed (ModestConf* conf,
2640 ModestConfEvent event,
2641 ModestConfNotificationId id,
2642 ModestFolderView *self)
2644 ModestFolderViewPrivate *priv;
2647 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2648 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2650 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2651 g_free (priv->local_account_name);
2653 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2654 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2656 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2657 MODEST_CONF_DEVICE_NAME, NULL);
2659 /* Force a redraw */
2660 #if GTK_CHECK_VERSION(2, 8, 0)
2661 GtkTreeViewColumn * tree_column;
2663 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2664 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2665 gtk_tree_view_column_queue_resize (tree_column);
2667 gtk_widget_queue_draw (GTK_WIDGET (self));
2673 modest_folder_view_set_style (ModestFolderView *self,
2674 ModestFolderViewStyle style)
2676 ModestFolderViewPrivate *priv;
2678 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2679 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2680 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2682 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2685 priv->style = style;
2689 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2690 const gchar *account_id)
2692 ModestFolderViewPrivate *priv;
2693 GtkTreeModel *model;
2695 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2697 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2699 /* This will be used by the filter_row callback,
2700 * to decided which rows to show: */
2701 if (priv->visible_account_id) {
2702 g_free (priv->visible_account_id);
2703 priv->visible_account_id = NULL;
2706 priv->visible_account_id = g_strdup (account_id);
2709 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2710 if (GTK_IS_TREE_MODEL_FILTER (model))
2711 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2713 /* Save settings to gconf */
2714 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2715 MODEST_CONF_FOLDER_VIEW_KEY);
2719 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2721 ModestFolderViewPrivate *priv;
2723 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2725 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2727 return (const gchar *) priv->visible_account_id;
2731 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2735 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2737 gtk_tree_model_get (model, iter,
2738 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2741 gboolean result = FALSE;
2742 if (type == TNY_FOLDER_TYPE_INBOX) {
2746 *inbox_iter = *iter;
2750 if (gtk_tree_model_iter_children (model, &child, iter)) {
2751 if (find_inbox_iter (model, &child, inbox_iter))
2755 } while (gtk_tree_model_iter_next (model, iter));
2764 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2766 GtkTreeModel *model;
2767 GtkTreeIter iter, inbox_iter;
2768 GtkTreeSelection *sel;
2769 GtkTreePath *path = NULL;
2771 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2773 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2777 expand_root_items (self);
2778 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2780 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2781 g_warning ("%s: model is empty", __FUNCTION__);
2785 if (find_inbox_iter (model, &iter, &inbox_iter))
2786 path = gtk_tree_model_get_path (model, &inbox_iter);
2788 path = gtk_tree_path_new_first ();
2790 /* Select the row and free */
2791 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2792 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2793 gtk_tree_path_free (path);
2796 gtk_widget_grab_focus (GTK_WIDGET(self));
2802 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2807 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2808 TnyFolder* a_folder;
2811 gtk_tree_model_get (model, iter,
2812 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2813 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2814 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2818 if (folder == a_folder) {
2819 g_object_unref (a_folder);
2820 *folder_iter = *iter;
2823 g_object_unref (a_folder);
2825 if (gtk_tree_model_iter_children (model, &child, iter)) {
2826 if (find_folder_iter (model, &child, folder_iter, folder))
2830 } while (gtk_tree_model_iter_next (model, iter));
2837 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2840 ModestFolderView *self)
2842 ModestFolderViewPrivate *priv = NULL;
2843 GtkTreeSelection *sel;
2844 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2845 GObject *instance = NULL;
2847 if (!MODEST_IS_FOLDER_VIEW(self))
2850 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2852 priv->reexpand = TRUE;
2854 gtk_tree_model_get (tree_model, iter,
2855 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2856 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2858 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2859 priv->folder_to_select = g_object_ref (instance);
2861 g_object_unref (instance);
2864 if (priv->folder_to_select) {
2866 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2869 path = gtk_tree_model_get_path (tree_model, iter);
2870 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2872 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2874 gtk_tree_selection_select_iter (sel, iter);
2875 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2877 gtk_tree_path_free (path);
2882 modest_folder_view_disable_next_folder_selection (self);
2884 /* Refilter the model */
2885 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2891 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2893 ModestFolderViewPrivate *priv;
2895 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2897 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2899 if (priv->folder_to_select)
2900 g_object_unref(priv->folder_to_select);
2902 priv->folder_to_select = NULL;
2906 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2907 gboolean after_change)
2909 GtkTreeModel *model;
2910 GtkTreeIter iter, folder_iter;
2911 GtkTreeSelection *sel;
2912 ModestFolderViewPrivate *priv = NULL;
2914 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2915 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2917 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2920 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2921 gtk_tree_selection_unselect_all (sel);
2923 if (priv->folder_to_select)
2924 g_object_unref(priv->folder_to_select);
2925 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2929 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2934 /* Refilter the model, before selecting the folder */
2935 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2937 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2938 g_warning ("%s: model is empty", __FUNCTION__);
2942 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2945 path = gtk_tree_model_get_path (model, &folder_iter);
2946 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2948 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2949 gtk_tree_selection_select_iter (sel, &folder_iter);
2950 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2952 gtk_tree_path_free (path);
2960 modest_folder_view_copy_selection (ModestFolderView *self)
2962 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2964 /* Copy selection */
2965 _clipboard_set_selected_data (self, FALSE);
2969 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2971 ModestFolderViewPrivate *priv = NULL;
2972 GtkTreeModel *model = NULL;
2973 const gchar **hidding = NULL;
2974 guint i, n_selected;
2976 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2977 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2979 /* Copy selection */
2980 if (!_clipboard_set_selected_data (folder_view, TRUE))
2983 /* Get hidding ids */
2984 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2986 /* Clear hidding array created by previous cut operation */
2987 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2989 /* Copy hidding array */
2990 priv->n_selected = n_selected;
2991 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2992 for (i=0; i < n_selected; i++)
2993 priv->hidding_ids[i] = g_strdup(hidding[i]);
2995 /* Hide cut folders */
2996 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2997 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3001 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3002 ModestFolderView *folder_view_dst)
3004 GtkTreeModel *filter_model = NULL;
3005 GtkTreeModel *model = NULL;
3006 GtkTreeModel *new_filter_model = NULL;
3008 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3009 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3012 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3013 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3015 /* Build new filter model */
3016 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3017 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3021 /* Set copied model */
3022 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3023 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3024 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3027 g_object_unref (new_filter_model);
3031 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3034 GtkTreeModel *model = NULL;
3035 ModestFolderViewPrivate* priv;
3037 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3039 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3040 priv->show_non_move = show;
3041 /* modest_folder_view_update_model(folder_view, */
3042 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3044 /* Hide special folders */
3045 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3046 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3047 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3051 /* Returns FALSE if it did not selected anything */
3053 _clipboard_set_selected_data (ModestFolderView *folder_view,
3056 ModestFolderViewPrivate *priv = NULL;
3057 TnyFolderStore *folder = NULL;
3058 gboolean retval = FALSE;
3060 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3061 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3063 /* Set selected data on clipboard */
3064 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3065 folder = modest_folder_view_get_selected (folder_view);
3067 /* Do not allow to select an account */
3068 if (TNY_IS_FOLDER (folder)) {
3069 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3074 g_object_unref (folder);
3080 _clear_hidding_filter (ModestFolderView *folder_view)
3082 ModestFolderViewPrivate *priv;
3085 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3086 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3088 if (priv->hidding_ids != NULL) {
3089 for (i=0; i < priv->n_selected; i++)
3090 g_free (priv->hidding_ids[i]);
3091 g_free(priv->hidding_ids);
3097 on_display_name_changed (ModestAccountMgr *mgr,
3098 const gchar *account,
3101 ModestFolderView *self;
3103 self = MODEST_FOLDER_VIEW (user_data);
3105 /* Force a redraw */
3106 #if GTK_CHECK_VERSION(2, 8, 0)
3107 GtkTreeViewColumn * tree_column;
3109 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3110 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3111 gtk_tree_view_column_queue_resize (tree_column);
3113 gtk_widget_queue_draw (GTK_WIDGET (self));