1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-store-tree-model.h>
36 #include <tny-gtk-header-list-model.h>
37 #include <tny-folder.h>
38 #include <tny-folder-store-observer.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <tny-camel-account.h>
45 #include <modest-tny-account.h>
46 #include <modest-tny-folder.h>
47 #include <modest-tny-local-folders-account.h>
48 #include <modest-tny-outbox-account.h>
49 #include <modest-marshal.h>
50 #include <modest-icon-names.h>
51 #include <modest-tny-account-store.h>
52 #include <modest-text-utils.h>
53 #include <modest-runtime.h>
54 #include "modest-folder-view.h"
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
58 #include "modest-dnd.h"
59 #include "widgets/modest-window.h"
61 /* Folder view drag types */
62 const GtkTargetEntry folder_view_drag_types[] =
64 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
65 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
68 /* 'private'/'protected' functions */
69 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
70 static void modest_folder_view_init (ModestFolderView *obj);
71 static void modest_folder_view_finalize (GObject *obj);
73 static void tny_account_store_view_init (gpointer g,
76 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
77 TnyAccountStore *account_store);
79 static void on_selection_changed (GtkTreeSelection *sel,
82 static void on_account_removed (TnyAccountStore *self,
86 static void on_account_inserted (TnyAccountStore *self,
90 static void on_account_changed (TnyAccountStore *self,
94 static gint cmp_rows (GtkTreeModel *tree_model,
99 static gboolean filter_row (GtkTreeModel *model,
103 static gboolean on_key_pressed (GtkWidget *self,
107 static void on_configuration_key_changed (ModestConf* conf,
109 ModestConfEvent event,
110 ModestConfNotificationId notification_id,
111 ModestFolderView *self);
114 static void on_drag_data_get (GtkWidget *widget,
115 GdkDragContext *context,
116 GtkSelectionData *selection_data,
121 static void on_drag_data_received (GtkWidget *widget,
122 GdkDragContext *context,
125 GtkSelectionData *selection_data,
130 static gboolean on_drag_motion (GtkWidget *widget,
131 GdkDragContext *context,
137 static void expand_root_items (ModestFolderView *self);
139 static gint expand_row_timeout (gpointer data);
141 static void setup_drag_and_drop (GtkTreeView *self);
143 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
146 static void _clear_hidding_filter (ModestFolderView *folder_view);
148 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
151 ModestFolderView *self);
153 static void on_display_name_changed (ModestAccountMgr *self,
154 const gchar *account,
158 FOLDER_SELECTION_CHANGED_SIGNAL,
159 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
163 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
164 struct _ModestFolderViewPrivate {
165 TnyAccountStore *account_store;
166 TnyFolderStore *cur_folder_store;
168 TnyFolder *folder_to_select; /* folder to select after the next update */
170 gulong changed_signal;
171 gulong account_inserted_signal;
172 gulong account_removed_signal;
173 gulong account_changed_signal;
174 gulong conf_key_signal;
175 gulong display_name_changed_signal;
177 /* not unref this object, its a singlenton */
178 ModestEmailClipboard *clipboard;
180 /* Filter tree model */
184 TnyFolderStoreQuery *query;
185 guint timer_expander;
187 gchar *local_account_name;
188 gchar *visible_account_id;
189 ModestFolderViewStyle style;
191 gboolean reselect; /* we use this to force a reselection of the INBOX */
192 gboolean show_non_move;
193 gboolean reexpand; /* next time we expose, we'll expand all root folders */
195 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
196 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
197 MODEST_TYPE_FOLDER_VIEW, \
198 ModestFolderViewPrivate))
200 static GObjectClass *parent_class = NULL;
202 static guint signals[LAST_SIGNAL] = {0};
205 modest_folder_view_get_type (void)
207 static GType my_type = 0;
209 static const GTypeInfo my_info = {
210 sizeof(ModestFolderViewClass),
211 NULL, /* base init */
212 NULL, /* base finalize */
213 (GClassInitFunc) modest_folder_view_class_init,
214 NULL, /* class finalize */
215 NULL, /* class data */
216 sizeof(ModestFolderView),
218 (GInstanceInitFunc) modest_folder_view_init,
222 static const GInterfaceInfo tny_account_store_view_info = {
223 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
224 NULL, /* interface_finalize */
225 NULL /* interface_data */
229 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
233 g_type_add_interface_static (my_type,
234 TNY_TYPE_ACCOUNT_STORE_VIEW,
235 &tny_account_store_view_info);
241 modest_folder_view_class_init (ModestFolderViewClass *klass)
243 GObjectClass *gobject_class;
244 gobject_class = (GObjectClass*) klass;
246 parent_class = g_type_class_peek_parent (klass);
247 gobject_class->finalize = modest_folder_view_finalize;
249 g_type_class_add_private (gobject_class,
250 sizeof(ModestFolderViewPrivate));
252 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
253 g_signal_new ("folder_selection_changed",
254 G_TYPE_FROM_CLASS (gobject_class),
256 G_STRUCT_OFFSET (ModestFolderViewClass,
257 folder_selection_changed),
259 modest_marshal_VOID__POINTER_BOOLEAN,
260 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
263 * This signal is emitted whenever the currently selected
264 * folder display name is computed. Note that the name could
265 * be different to the folder name, because we could append
266 * the unread messages count to the folder name to build the
267 * folder display name
269 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
270 g_signal_new ("folder-display-name-changed",
271 G_TYPE_FROM_CLASS (gobject_class),
273 G_STRUCT_OFFSET (ModestFolderViewClass,
274 folder_display_name_changed),
276 g_cclosure_marshal_VOID__STRING,
277 G_TYPE_NONE, 1, G_TYPE_STRING);
280 /* Simplify checks for NULLs: */
282 strings_are_equal (const gchar *a, const gchar *b)
288 return (strcmp (a, b) == 0);
295 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
297 GObject *instance = NULL;
299 gtk_tree_model_get (model, iter,
300 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
304 return FALSE; /* keep walking */
306 if (!TNY_IS_ACCOUNT (instance)) {
307 g_object_unref (instance);
308 return FALSE; /* keep walking */
311 /* Check if this is the looked-for account: */
312 TnyAccount *this_account = TNY_ACCOUNT (instance);
313 TnyAccount *account = TNY_ACCOUNT (data);
315 const gchar *this_account_id = tny_account_get_id(this_account);
316 const gchar *account_id = tny_account_get_id(account);
317 g_object_unref (instance);
320 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
321 if (strings_are_equal(this_account_id, account_id)) {
322 /* Tell the model that the data has changed, so that
323 * it calls the cell_data_func callbacks again: */
324 /* TODO: This does not seem to actually cause the new string to be shown: */
325 gtk_tree_model_row_changed (model, path, iter);
327 return TRUE; /* stop walking */
330 return FALSE; /* keep walking */
335 ModestFolderView *self;
336 gchar *previous_name;
337 } GetMmcAccountNameData;
340 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
342 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
344 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
346 if (!strings_are_equal (
347 tny_account_get_name(TNY_ACCOUNT(account)),
348 data->previous_name)) {
350 /* Tell the model that the data has changed, so that
351 * it calls the cell_data_func callbacks again: */
352 ModestFolderView *self = data->self;
353 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
355 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
358 g_free (data->previous_name);
359 g_slice_free (GetMmcAccountNameData, data);
363 text_cell_data (GtkTreeViewColumn *column,
364 GtkCellRenderer *renderer,
365 GtkTreeModel *tree_model,
369 ModestFolderViewPrivate *priv;
370 GObject *rendobj = (GObject *) renderer;
372 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
373 GObject *instance = NULL;
375 gtk_tree_model_get (tree_model, iter,
376 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
377 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
378 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
388 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
389 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
391 gchar *item_name = NULL;
392 gint item_weight = 400;
394 if (type != TNY_FOLDER_TYPE_ROOT) {
397 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
398 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
399 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
400 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
402 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
406 /* note: we cannot reliably get the counts from the tree model, we need
407 * to use explicit calls on tny_folder for some reason.
409 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
410 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
411 (type == TNY_FOLDER_TYPE_OUTBOX) ||
412 (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
413 number = tny_folder_get_all_count (TNY_FOLDER(instance));
415 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
417 /* Use bold font style if there are unread or unset messages */
419 item_name = g_strdup_printf ("%s (%d)", fname, number);
422 item_name = g_strdup (fname);
426 } else if (TNY_IS_ACCOUNT (instance)) {
427 /* If it's a server account */
428 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
429 item_name = g_strdup (priv->local_account_name);
431 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
432 /* fname is only correct when the items are first
433 * added to the model, not when the account is
434 * changed later, so get the name from the account
436 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
439 item_name = g_strdup (fname);
445 item_name = g_strdup ("unknown");
447 if (item_name && item_weight) {
448 /* Set the name in the treeview cell: */
449 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
451 /* Notify display name observers */
452 /* TODO: What listens for this signal, and how can it use only the new name? */
453 if (((GObject *) priv->cur_folder_store) == instance) {
454 g_signal_emit (G_OBJECT(self),
455 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
462 /* If it is a Memory card account, make sure that we have the correct name.
463 * This function will be trigerred again when the name has been retrieved: */
464 if (TNY_IS_STORE_ACCOUNT (instance) &&
465 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
467 /* Get the account name asynchronously: */
468 GetMmcAccountNameData *callback_data =
469 g_slice_new0(GetMmcAccountNameData);
470 callback_data->self = self;
472 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
474 callback_data->previous_name = g_strdup (name);
476 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
477 on_get_mmc_account_name, callback_data);
480 g_object_unref (G_OBJECT (instance));
487 GdkPixbuf *pixbuf_open;
488 GdkPixbuf *pixbuf_close;
493 get_folder_icons (TnyFolderType type, GObject *instance)
495 GdkPixbuf *pixbuf = NULL;
496 GdkPixbuf *pixbuf_open = NULL;
497 GdkPixbuf *pixbuf_close = NULL;
498 ThreePixbufs *retval = g_slice_new (ThreePixbufs);
500 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
501 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
502 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
503 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
504 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
506 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
507 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
508 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
509 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
510 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
512 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
513 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
514 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
515 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
516 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
519 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
520 /* We include the MERGE type here because it's used to create
521 the local OUTBOX folder */
522 if (type == TNY_FOLDER_TYPE_NORMAL ||
523 type == TNY_FOLDER_TYPE_UNKNOWN) {
524 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
528 case TNY_FOLDER_TYPE_INVALID:
529 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
532 case TNY_FOLDER_TYPE_ROOT:
533 if (TNY_IS_ACCOUNT (instance)) {
535 if (modest_tny_account_is_virtual_local_folders (
536 TNY_ACCOUNT (instance))) {
539 avirt_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
540 MODEST_ICON_SIZE_SMALL));
542 if (!avirt_pixbuf_open) {
543 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
544 MODEST_ICON_SIZE_SMALL);
545 avirt_pixbuf_open = gdk_pixbuf_copy (avirt_pixbuf);
546 gdk_pixbuf_composite (emblem, avirt_pixbuf_open, 0, 0,
547 MIN (gdk_pixbuf_get_width (emblem),
548 gdk_pixbuf_get_width (avirt_pixbuf_open)),
549 MIN (gdk_pixbuf_get_height (emblem),
550 gdk_pixbuf_get_height (avirt_pixbuf_open)),
551 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
552 g_object_unref (emblem);
555 if (!avirt_pixbuf_close) {
556 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
557 MODEST_ICON_SIZE_SMALL);
558 avirt_pixbuf_close = gdk_pixbuf_copy (avirt_pixbuf);
559 gdk_pixbuf_composite (emblem, avirt_pixbuf_close, 0, 0,
560 MIN (gdk_pixbuf_get_width (emblem),
561 gdk_pixbuf_get_width (avirt_pixbuf_close)),
562 MIN (gdk_pixbuf_get_height (emblem),
563 gdk_pixbuf_get_height (avirt_pixbuf_close)),
564 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
565 g_object_unref (emblem);
569 pixbuf = g_object_ref (avirt_pixbuf);
570 pixbuf_open = g_object_ref (avirt_pixbuf_open);
571 pixbuf_close = g_object_ref (avirt_pixbuf_close);
575 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
577 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
579 ammc_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_MMC,
580 MODEST_ICON_SIZE_SMALL));
582 if (!ammc_pixbuf_open) {
583 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
584 MODEST_ICON_SIZE_SMALL);
585 ammc_pixbuf_open = gdk_pixbuf_copy (ammc_pixbuf);
586 gdk_pixbuf_composite (emblem, ammc_pixbuf_open, 0, 0,
587 MIN (gdk_pixbuf_get_width (emblem),
588 gdk_pixbuf_get_width (ammc_pixbuf_open)),
589 MIN (gdk_pixbuf_get_height (emblem),
590 gdk_pixbuf_get_height (ammc_pixbuf_open)),
591 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
592 g_object_unref (emblem);
595 if (!ammc_pixbuf_close) {
596 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
597 MODEST_ICON_SIZE_SMALL);
598 ammc_pixbuf_close = gdk_pixbuf_copy (ammc_pixbuf);
599 gdk_pixbuf_composite (emblem, ammc_pixbuf_close, 0, 0,
600 MIN (gdk_pixbuf_get_width (emblem),
601 gdk_pixbuf_get_width (ammc_pixbuf_close)),
602 MIN (gdk_pixbuf_get_height (emblem),
603 gdk_pixbuf_get_height (ammc_pixbuf_close)),
604 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
605 g_object_unref (emblem);
609 pixbuf = g_object_ref (ammc_pixbuf);
610 pixbuf_open = g_object_ref (ammc_pixbuf_open);
611 pixbuf_close = g_object_ref (ammc_pixbuf_close);
616 anorm_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT,
617 MODEST_ICON_SIZE_SMALL));
618 if (!anorm_pixbuf_open) {
619 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
620 MODEST_ICON_SIZE_SMALL);
621 anorm_pixbuf_open = gdk_pixbuf_copy (anorm_pixbuf);
622 gdk_pixbuf_composite (emblem, anorm_pixbuf_open, 0, 0,
623 MIN (gdk_pixbuf_get_width (emblem),
624 gdk_pixbuf_get_width (anorm_pixbuf_open)),
625 MIN (gdk_pixbuf_get_height (emblem),
626 gdk_pixbuf_get_height (anorm_pixbuf_open)),
627 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
628 g_object_unref (emblem);
631 if (!anorm_pixbuf_close) {
632 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
633 MODEST_ICON_SIZE_SMALL);
634 anorm_pixbuf_close = gdk_pixbuf_copy (anorm_pixbuf);
635 gdk_pixbuf_composite (emblem, anorm_pixbuf_close, 0, 0,
636 MIN (gdk_pixbuf_get_width (emblem),
637 gdk_pixbuf_get_width (anorm_pixbuf_close)),
638 MIN (gdk_pixbuf_get_height (emblem),
639 gdk_pixbuf_get_height (anorm_pixbuf_close)),
640 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
641 g_object_unref (emblem);
645 pixbuf = g_object_ref (anorm_pixbuf);
646 pixbuf_open = g_object_ref (anorm_pixbuf_open);
647 pixbuf_close = g_object_ref (anorm_pixbuf_close);
653 case TNY_FOLDER_TYPE_INBOX:
656 inbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX,
657 MODEST_ICON_SIZE_SMALL));
659 if (!inbox_pixbuf_open) {
660 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
661 MODEST_ICON_SIZE_SMALL);
662 inbox_pixbuf_open = gdk_pixbuf_copy (inbox_pixbuf);
663 gdk_pixbuf_composite (emblem, inbox_pixbuf_open, 0, 0,
664 MIN (gdk_pixbuf_get_width (emblem),
665 gdk_pixbuf_get_width (inbox_pixbuf_open)),
666 MIN (gdk_pixbuf_get_height (emblem),
667 gdk_pixbuf_get_height (inbox_pixbuf_open)),
668 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
669 g_object_unref (emblem);
672 if (!inbox_pixbuf_close) {
673 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
674 MODEST_ICON_SIZE_SMALL);
675 inbox_pixbuf_close = gdk_pixbuf_copy (inbox_pixbuf);
676 gdk_pixbuf_composite (emblem, inbox_pixbuf_close, 0, 0,
677 MIN (gdk_pixbuf_get_width (emblem),
678 gdk_pixbuf_get_width (inbox_pixbuf_close)),
679 MIN (gdk_pixbuf_get_height (emblem),
680 gdk_pixbuf_get_height (inbox_pixbuf_close)),
681 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
682 g_object_unref (emblem);
686 pixbuf = g_object_ref (inbox_pixbuf);
687 pixbuf_open = g_object_ref (inbox_pixbuf_open);
688 pixbuf_close = g_object_ref (inbox_pixbuf_close);
691 case TNY_FOLDER_TYPE_OUTBOX:
693 outbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX,
694 MODEST_ICON_SIZE_SMALL));
696 if (!outbox_pixbuf_open) {
697 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
698 MODEST_ICON_SIZE_SMALL);
699 outbox_pixbuf_open = gdk_pixbuf_copy (outbox_pixbuf);
700 gdk_pixbuf_composite (emblem, outbox_pixbuf_open, 0, 0,
701 MIN (gdk_pixbuf_get_width (emblem),
702 gdk_pixbuf_get_width (outbox_pixbuf_open)),
703 MIN (gdk_pixbuf_get_height (emblem),
704 gdk_pixbuf_get_height (outbox_pixbuf_open)),
705 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
706 g_object_unref (emblem);
709 if (!outbox_pixbuf_close) {
710 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
711 MODEST_ICON_SIZE_SMALL);
712 outbox_pixbuf_close = gdk_pixbuf_copy (outbox_pixbuf);
713 gdk_pixbuf_composite (emblem, outbox_pixbuf_close, 0, 0,
714 MIN (gdk_pixbuf_get_width (emblem),
715 gdk_pixbuf_get_width (outbox_pixbuf_close)),
716 MIN (gdk_pixbuf_get_height (emblem),
717 gdk_pixbuf_get_height (outbox_pixbuf_close)),
718 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
719 g_object_unref (emblem);
723 pixbuf = g_object_ref (outbox_pixbuf);
724 pixbuf_open = g_object_ref (outbox_pixbuf_open);
725 pixbuf_close = g_object_ref (outbox_pixbuf_close);
728 case TNY_FOLDER_TYPE_JUNK:
730 junk_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK,
731 MODEST_ICON_SIZE_SMALL));
732 if (!junk_pixbuf_open) {
733 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
734 MODEST_ICON_SIZE_SMALL);
735 junk_pixbuf_open = gdk_pixbuf_copy (junk_pixbuf);
736 gdk_pixbuf_composite (emblem, junk_pixbuf_open, 0, 0,
737 MIN (gdk_pixbuf_get_width (emblem),
738 gdk_pixbuf_get_width (junk_pixbuf_open)),
739 MIN (gdk_pixbuf_get_height (emblem),
740 gdk_pixbuf_get_height (junk_pixbuf_open)),
741 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
742 g_object_unref (emblem);
745 if (!junk_pixbuf_close) {
746 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
747 MODEST_ICON_SIZE_SMALL);
748 junk_pixbuf_close = gdk_pixbuf_copy (junk_pixbuf);
749 gdk_pixbuf_composite (emblem, junk_pixbuf_close, 0, 0,
750 MIN (gdk_pixbuf_get_width (emblem),
751 gdk_pixbuf_get_width (junk_pixbuf_close)),
752 MIN (gdk_pixbuf_get_height (emblem),
753 gdk_pixbuf_get_height (junk_pixbuf_close)),
754 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
755 g_object_unref (emblem);
759 pixbuf = g_object_ref (junk_pixbuf);
760 pixbuf_open = g_object_ref (junk_pixbuf_open);
761 pixbuf_close = g_object_ref (junk_pixbuf_close);
765 case TNY_FOLDER_TYPE_SENT:
767 sent_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_SENT,
768 MODEST_ICON_SIZE_SMALL));
770 if (!sent_pixbuf_open) {
771 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
772 MODEST_ICON_SIZE_SMALL);
773 sent_pixbuf_open = gdk_pixbuf_copy (sent_pixbuf);
774 gdk_pixbuf_composite (emblem, sent_pixbuf_open, 0, 0,
775 MIN (gdk_pixbuf_get_width (emblem),
776 gdk_pixbuf_get_width (sent_pixbuf_open)),
777 MIN (gdk_pixbuf_get_height (emblem),
778 gdk_pixbuf_get_height (sent_pixbuf_open)),
779 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
780 g_object_unref (emblem);
783 if (!sent_pixbuf_close) {
784 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
785 MODEST_ICON_SIZE_SMALL);
786 sent_pixbuf_close = gdk_pixbuf_copy (sent_pixbuf);
787 gdk_pixbuf_composite (emblem, sent_pixbuf_close, 0, 0,
788 MIN (gdk_pixbuf_get_width (emblem),
789 gdk_pixbuf_get_width (sent_pixbuf_close)),
790 MIN (gdk_pixbuf_get_height (emblem),
791 gdk_pixbuf_get_height (sent_pixbuf_close)),
792 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
793 g_object_unref (emblem);
797 pixbuf = g_object_ref (sent_pixbuf);
798 pixbuf_open = g_object_ref (sent_pixbuf_open);
799 pixbuf_close = g_object_ref (sent_pixbuf_close);
803 case TNY_FOLDER_TYPE_TRASH:
805 trash_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH,
806 MODEST_ICON_SIZE_SMALL));
807 if (!trash_pixbuf_open) {
808 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
809 MODEST_ICON_SIZE_SMALL);
810 trash_pixbuf_open = gdk_pixbuf_copy (trash_pixbuf);
811 gdk_pixbuf_composite (emblem, trash_pixbuf_open, 0, 0,
812 MIN (gdk_pixbuf_get_width (emblem),
813 gdk_pixbuf_get_width (trash_pixbuf_open)),
814 MIN (gdk_pixbuf_get_height (emblem),
815 gdk_pixbuf_get_height (trash_pixbuf_open)),
816 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
817 g_object_unref (emblem);
820 if (!trash_pixbuf_close) {
821 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
822 MODEST_ICON_SIZE_SMALL);
823 trash_pixbuf_close = gdk_pixbuf_copy (trash_pixbuf);
824 gdk_pixbuf_composite (emblem, trash_pixbuf_close, 0, 0,
825 MIN (gdk_pixbuf_get_width (emblem),
826 gdk_pixbuf_get_width (trash_pixbuf_close)),
827 MIN (gdk_pixbuf_get_height (emblem),
828 gdk_pixbuf_get_height (trash_pixbuf_close)),
829 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
830 g_object_unref (emblem);
834 pixbuf = g_object_ref (trash_pixbuf);
835 pixbuf_open = g_object_ref (trash_pixbuf_open);
836 pixbuf_close = g_object_ref (trash_pixbuf_close);
839 case TNY_FOLDER_TYPE_DRAFTS:
841 draft_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS,
842 MODEST_ICON_SIZE_SMALL));
844 if (!draft_pixbuf_open) {
845 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
846 MODEST_ICON_SIZE_SMALL);
847 draft_pixbuf_open = gdk_pixbuf_copy (draft_pixbuf);
848 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0,
849 MIN (gdk_pixbuf_get_width (emblem),
850 gdk_pixbuf_get_width (draft_pixbuf_open)),
851 MIN (gdk_pixbuf_get_height (emblem),
852 gdk_pixbuf_get_height (draft_pixbuf_open)),
853 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
854 g_object_unref (emblem);
857 if (!draft_pixbuf_close) {
858 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
859 MODEST_ICON_SIZE_SMALL);
860 draft_pixbuf_close = gdk_pixbuf_copy (draft_pixbuf);
861 gdk_pixbuf_composite (emblem, draft_pixbuf_close, 0, 0,
862 MIN (gdk_pixbuf_get_width (emblem),
863 gdk_pixbuf_get_width (draft_pixbuf_close)),
864 MIN (gdk_pixbuf_get_height (emblem),
865 gdk_pixbuf_get_height (draft_pixbuf_close)),
866 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
867 g_object_unref (emblem);
871 pixbuf = g_object_ref (draft_pixbuf);
872 pixbuf_open = g_object_ref (draft_pixbuf_open);
873 pixbuf_close = g_object_ref (draft_pixbuf_close);
876 case TNY_FOLDER_TYPE_NORMAL:
879 normal_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL,
880 MODEST_ICON_SIZE_SMALL));
882 if (!normal_pixbuf_open) {
883 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
884 MODEST_ICON_SIZE_SMALL);
885 normal_pixbuf_open = gdk_pixbuf_copy (normal_pixbuf);
886 gdk_pixbuf_composite (emblem, normal_pixbuf_open, 0, 0,
887 MIN (gdk_pixbuf_get_width (emblem),
888 gdk_pixbuf_get_width (normal_pixbuf_open)),
889 MIN (gdk_pixbuf_get_height (emblem),
890 gdk_pixbuf_get_height (normal_pixbuf_open)),
891 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
892 g_object_unref (emblem);
895 if (!normal_pixbuf_close) {
896 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
897 MODEST_ICON_SIZE_SMALL);
898 normal_pixbuf_close = gdk_pixbuf_copy (normal_pixbuf);
899 gdk_pixbuf_composite (emblem, normal_pixbuf_close, 0, 0,
900 MIN (gdk_pixbuf_get_width (emblem),
901 gdk_pixbuf_get_width (normal_pixbuf_close)),
902 MIN (gdk_pixbuf_get_height (emblem),
903 gdk_pixbuf_get_height (normal_pixbuf_close)),
904 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
905 g_object_unref (emblem);
909 pixbuf = g_object_ref (normal_pixbuf);
910 pixbuf_open = g_object_ref (normal_pixbuf_open);
911 pixbuf_close = g_object_ref (normal_pixbuf_close);
917 retval->pixbuf = pixbuf;
918 retval->pixbuf_open = pixbuf_open;
919 retval->pixbuf_close = pixbuf_close;
926 free_pixbufs (ThreePixbufs *pixbufs)
928 g_object_unref (pixbufs->pixbuf);
929 g_object_unref (pixbufs->pixbuf_open);
930 g_object_unref (pixbufs->pixbuf_close);
931 g_slice_free (ThreePixbufs, pixbufs);
935 icon_cell_data (GtkTreeViewColumn *column,
936 GtkCellRenderer *renderer,
937 GtkTreeModel *tree_model,
941 GObject *rendobj = NULL, *instance = NULL;
942 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
943 gboolean has_children;
944 ThreePixbufs *pixbufs;
946 rendobj = (GObject *) renderer;
948 gtk_tree_model_get (tree_model, iter,
949 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
950 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
956 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
957 pixbufs = get_folder_icons (type, instance);
958 g_object_unref (instance);
961 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
964 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
965 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
968 free_pixbufs (pixbufs);
974 add_columns (GtkWidget *treeview)
976 GtkTreeViewColumn *column;
977 GtkCellRenderer *renderer;
978 GtkTreeSelection *sel;
981 column = gtk_tree_view_column_new ();
983 /* Set icon and text render function */
984 renderer = gtk_cell_renderer_pixbuf_new();
985 gtk_tree_view_column_pack_start (column, renderer, FALSE);
986 gtk_tree_view_column_set_cell_data_func(column, renderer,
987 icon_cell_data, treeview, NULL);
989 renderer = gtk_cell_renderer_text_new();
990 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END,
991 "ellipsize-set", TRUE, NULL);
992 gtk_tree_view_column_pack_start (column, renderer, TRUE);
993 gtk_tree_view_column_set_cell_data_func(column, renderer,
994 text_cell_data, treeview, NULL);
996 /* Set selection mode */
997 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
998 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1000 /* Set treeview appearance */
1001 gtk_tree_view_column_set_spacing (column, 2);
1002 gtk_tree_view_column_set_resizable (column, TRUE);
1003 gtk_tree_view_column_set_fixed_width (column, TRUE);
1004 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1005 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1008 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1012 modest_folder_view_init (ModestFolderView *obj)
1014 ModestFolderViewPrivate *priv;
1017 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1019 priv->timer_expander = 0;
1020 priv->account_store = NULL;
1022 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1023 priv->cur_folder_store = NULL;
1024 priv->visible_account_id = NULL;
1025 priv->folder_to_select = NULL;
1027 priv->reexpand = TRUE;
1029 /* Initialize the local account name */
1030 conf = modest_runtime_get_conf();
1031 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1033 /* Init email clipboard */
1034 priv->clipboard = modest_runtime_get_email_clipboard ();
1035 priv->hidding_ids = NULL;
1036 priv->n_selected = 0;
1037 priv->reselect = FALSE;
1038 priv->show_non_move = TRUE;
1040 /* Build treeview */
1041 add_columns (GTK_WIDGET (obj));
1043 /* Setup drag and drop */
1044 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1046 /* Connect signals */
1047 g_signal_connect (G_OBJECT (obj),
1049 G_CALLBACK (on_key_pressed), NULL);
1051 priv->display_name_changed_signal =
1052 g_signal_connect (modest_runtime_get_account_mgr (),
1053 "display_name_changed",
1054 G_CALLBACK (on_display_name_changed),
1058 * Track changes in the local account name (in the device it
1059 * will be the device name)
1061 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1063 G_CALLBACK(on_configuration_key_changed),
1068 tny_account_store_view_init (gpointer g, gpointer iface_data)
1070 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1072 klass->set_account_store = modest_folder_view_set_account_store;
1076 modest_folder_view_finalize (GObject *obj)
1078 ModestFolderViewPrivate *priv;
1079 GtkTreeSelection *sel;
1081 g_return_if_fail (obj);
1083 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1085 if (priv->timer_expander != 0) {
1086 g_source_remove (priv->timer_expander);
1087 priv->timer_expander = 0;
1090 if (priv->account_store) {
1091 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1092 priv->account_inserted_signal);
1093 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1094 priv->account_removed_signal);
1095 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1096 priv->account_changed_signal);
1097 g_object_unref (G_OBJECT(priv->account_store));
1098 priv->account_store = NULL;
1102 g_object_unref (G_OBJECT (priv->query));
1106 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1107 if (priv->folder_to_select) {
1108 g_object_unref (G_OBJECT(priv->folder_to_select));
1109 priv->folder_to_select = NULL;
1112 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1114 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1116 g_free (priv->local_account_name);
1117 g_free (priv->visible_account_id);
1119 if (priv->conf_key_signal) {
1120 g_signal_handler_disconnect (modest_runtime_get_conf (),
1121 priv->conf_key_signal);
1122 priv->conf_key_signal = 0;
1125 if (priv->cur_folder_store) {
1126 if (TNY_IS_FOLDER(priv->cur_folder_store)) {
1127 ModestMailOperation *mail_op;
1129 mail_op = modest_mail_operation_new (NULL);
1130 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1132 modest_mail_operation_sync_folder (mail_op, TNY_FOLDER (priv->cur_folder_store), FALSE);
1135 g_object_unref (priv->cur_folder_store);
1136 priv->cur_folder_store = NULL;
1139 /* Clear hidding array created by cut operation */
1140 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1142 G_OBJECT_CLASS(parent_class)->finalize (obj);
1147 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1149 ModestFolderViewPrivate *priv;
1152 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1153 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1155 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1156 device = tny_account_store_get_device (account_store);
1158 if (G_UNLIKELY (priv->account_store)) {
1160 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1161 priv->account_inserted_signal))
1162 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1163 priv->account_inserted_signal);
1164 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1165 priv->account_removed_signal))
1166 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1167 priv->account_removed_signal);
1168 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1169 priv->account_changed_signal))
1170 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1171 priv->account_changed_signal);
1172 g_object_unref (G_OBJECT (priv->account_store));
1175 priv->account_store = g_object_ref (G_OBJECT (account_store));
1177 priv->account_removed_signal =
1178 g_signal_connect (G_OBJECT(account_store), "account_removed",
1179 G_CALLBACK (on_account_removed), self);
1181 priv->account_inserted_signal =
1182 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1183 G_CALLBACK (on_account_inserted), self);
1185 priv->account_changed_signal =
1186 g_signal_connect (G_OBJECT(account_store), "account_changed",
1187 G_CALLBACK (on_account_changed), self);
1189 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1191 g_object_unref (G_OBJECT (device));
1195 on_connection_status_changed (TnyAccount *self,
1196 TnyConnectionStatus status,
1199 /* If the account becomes online then refresh it */
1200 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1201 const gchar *acc_name;
1202 GtkWidget *my_window;
1204 my_window = gtk_widget_get_ancestor (GTK_WIDGET (user_data), MODEST_TYPE_WINDOW);
1205 acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (self);
1206 modest_ui_actions_do_send_receive (acc_name, FALSE, MODEST_WINDOW (my_window));
1211 on_account_inserted (TnyAccountStore *account_store,
1212 TnyAccount *account,
1215 ModestFolderViewPrivate *priv;
1216 GtkTreeModel *sort_model, *filter_model;
1218 /* Ignore transport account insertions, we're not showing them
1219 in the folder view */
1220 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1223 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1225 /* If we're adding a new account, and there is no previous
1226 one, we need to select the visible server account */
1227 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1228 !priv->visible_account_id)
1229 modest_widget_memory_restore (modest_runtime_get_conf(),
1230 G_OBJECT (user_data),
1231 MODEST_CONF_FOLDER_VIEW_KEY);
1233 /* Get the inner model */
1234 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1235 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1237 /* Insert the account in the model */
1238 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1239 G_OBJECT (account));
1242 /* When the store account gets online refresh it */
1243 g_signal_connect (account, "connection_status_changed",
1244 G_CALLBACK (on_connection_status_changed),
1245 MODEST_FOLDER_VIEW (user_data));
1247 /* Refilter the model */
1248 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1253 on_account_changed (TnyAccountStore *account_store,
1254 TnyAccount *tny_account,
1257 ModestFolderViewPrivate *priv;
1258 GtkTreeModel *sort_model, *filter_model;
1260 /* Ignore transport account insertions, we're not showing them
1261 in the folder view */
1262 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1265 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1267 /* Get the inner model */
1268 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1269 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1271 /* Remove the account from the model */
1272 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1273 G_OBJECT (tny_account));
1275 /* Insert the account in the model */
1276 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1277 G_OBJECT (tny_account));
1279 /* Refilter the model */
1280 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1285 * Selects the first inbox or the local account in an idle
1288 on_idle_select_first_inbox_or_local (gpointer user_data)
1290 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1292 modest_folder_view_select_first_inbox_or_local (self);
1299 on_account_removed (TnyAccountStore *account_store,
1300 TnyAccount *account,
1303 ModestFolderView *self = NULL;
1304 ModestFolderViewPrivate *priv;
1305 GtkTreeModel *sort_model, *filter_model;
1306 GtkTreeSelection *sel = NULL;
1307 gboolean same_account_selected = FALSE;
1309 /* Ignore transport account removals, we're not showing them
1310 in the folder view */
1311 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1314 self = MODEST_FOLDER_VIEW (user_data);
1315 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1317 /* Invalidate the cur_folder_store only if the selected folder
1318 belongs to the account that is being removed */
1319 if (priv->cur_folder_store) {
1320 TnyAccount *selected_folder_account = NULL;
1322 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1323 selected_folder_account =
1324 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1326 selected_folder_account =
1327 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1330 if (selected_folder_account == account) {
1331 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1332 gtk_tree_selection_unselect_all (sel);
1333 same_account_selected = TRUE;
1335 g_object_unref (selected_folder_account);
1338 /* Invalidate row to select only if the folder to select
1339 belongs to the account that is being removed*/
1340 if (priv->folder_to_select) {
1341 TnyAccount *folder_to_select_account = NULL;
1343 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1344 if (folder_to_select_account == account) {
1345 modest_folder_view_disable_next_folder_selection (self);
1346 g_object_unref (priv->folder_to_select);
1347 priv->folder_to_select = NULL;
1349 g_object_unref (folder_to_select_account);
1352 /* Remove the account from the model */
1353 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1354 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1355 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1356 G_OBJECT (account));
1358 /* If the removed account is the currently viewed one then
1359 clear the configuration value. The new visible account will be the default account */
1360 if (priv->visible_account_id &&
1361 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1363 /* Clear the current visible account_id */
1364 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1366 /* Call the restore method, this will set the new visible account */
1367 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1368 MODEST_CONF_FOLDER_VIEW_KEY);
1371 /* Refilter the model */
1372 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1374 /* Select the first INBOX if the currently selected folder
1375 belongs to the account that is being deleted */
1376 if (same_account_selected)
1377 g_idle_add (on_idle_select_first_inbox_or_local, self);
1381 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1383 GtkTreeViewColumn *col;
1385 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1387 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1389 g_printerr ("modest: failed get column for title\n");
1393 gtk_tree_view_column_set_title (col, title);
1394 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1399 modest_folder_view_on_map (ModestFolderView *self,
1400 GdkEventExpose *event,
1403 ModestFolderViewPrivate *priv;
1405 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1407 /* This won't happen often */
1408 if (G_UNLIKELY (priv->reselect)) {
1409 /* Select the first inbox or the local account if not found */
1411 /* TODO: this could cause a lock at startup, so we
1412 comment it for the moment. We know that this will
1413 be a bug, because the INBOX is not selected, but we
1414 need to rewrite some parts of Modest to avoid the
1415 deathlock situation */
1416 /* TODO: check if this is still the case */
1417 priv->reselect = FALSE;
1418 modest_folder_view_select_first_inbox_or_local (self);
1419 /* Notify the display name observers */
1420 g_signal_emit (G_OBJECT(self),
1421 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1425 if (priv->reexpand) {
1426 expand_root_items (self);
1427 priv->reexpand = FALSE;
1434 modest_folder_view_new (TnyFolderStoreQuery *query)
1437 ModestFolderViewPrivate *priv;
1438 GtkTreeSelection *sel;
1440 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1441 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1444 priv->query = g_object_ref (query);
1446 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1447 priv->changed_signal = g_signal_connect (sel, "changed",
1448 G_CALLBACK (on_selection_changed), self);
1450 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1452 return GTK_WIDGET(self);
1455 /* this feels dirty; any other way to expand all the root items? */
1457 expand_root_items (ModestFolderView *self)
1460 GtkTreeModel *model;
1463 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1464 path = gtk_tree_path_new_first ();
1466 /* all folders should have child items, so.. */
1468 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1469 gtk_tree_path_next (path);
1470 } while (gtk_tree_model_get_iter (model, &iter, path));
1472 gtk_tree_path_free (path);
1476 * We use this function to implement the
1477 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1478 * account in this case, and the local folders.
1481 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1483 ModestFolderViewPrivate *priv;
1484 gboolean retval = TRUE;
1485 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1486 GObject *instance = NULL;
1487 const gchar *id = NULL;
1489 gboolean found = FALSE;
1490 gboolean cleared = FALSE;
1492 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1493 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1495 gtk_tree_model_get (model, iter,
1496 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1497 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1500 /* Do not show if there is no instance, this could indeed
1501 happen when the model is being modified while it's being
1502 drawn. This could occur for example when moving folders
1507 if (type == TNY_FOLDER_TYPE_ROOT) {
1508 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1509 account instead of a folder. */
1510 if (TNY_IS_ACCOUNT (instance)) {
1511 TnyAccount *acc = TNY_ACCOUNT (instance);
1512 const gchar *account_id = tny_account_get_id (acc);
1514 /* If it isn't a special folder,
1515 * don't show it unless it is the visible account: */
1516 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1517 !modest_tny_account_is_virtual_local_folders (acc) &&
1518 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1520 /* Show only the visible account id */
1521 if (priv->visible_account_id) {
1522 if (strcmp (account_id, priv->visible_account_id))
1529 /* Never show these to the user. They are merged into one folder
1530 * in the local-folders account instead: */
1531 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1536 /* Check hiding (if necessary) */
1537 cleared = modest_email_clipboard_cleared (priv->clipboard);
1538 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1539 id = tny_folder_get_id (TNY_FOLDER(instance));
1540 if (priv->hidding_ids != NULL)
1541 for (i=0; i < priv->n_selected && !found; i++)
1542 if (priv->hidding_ids[i] != NULL && id != NULL)
1543 found = (!strcmp (priv->hidding_ids[i], id));
1549 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1550 folder as no message can be move there according to UI specs */
1551 if (!priv->show_non_move) {
1553 case TNY_FOLDER_TYPE_OUTBOX:
1554 case TNY_FOLDER_TYPE_SENT:
1555 case TNY_FOLDER_TYPE_DRAFTS:
1558 case TNY_FOLDER_TYPE_UNKNOWN:
1559 case TNY_FOLDER_TYPE_NORMAL:
1560 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1561 if (type == TNY_FOLDER_TYPE_INVALID)
1562 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1564 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1565 type == TNY_FOLDER_TYPE_SENT
1566 || type == TNY_FOLDER_TYPE_DRAFTS)
1575 g_object_unref (instance);
1582 modest_folder_view_update_model (ModestFolderView *self,
1583 TnyAccountStore *account_store)
1585 ModestFolderViewPrivate *priv;
1586 GtkTreeModel *model /* , *old_model */;
1587 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1589 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1590 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1593 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1595 /* Notify that there is no folder selected */
1596 g_signal_emit (G_OBJECT(self),
1597 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1599 if (priv->cur_folder_store) {
1600 g_object_unref (priv->cur_folder_store);
1601 priv->cur_folder_store = NULL;
1604 /* FIXME: the local accounts are not shown when the query
1605 selects only the subscribed folders */
1606 model = tny_gtk_folder_store_tree_model_new (NULL);
1608 /* Get the accounts: */
1609 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1611 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1613 sortable = gtk_tree_model_sort_new_with_model (model);
1614 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1615 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1616 GTK_SORT_ASCENDING);
1617 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1618 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1619 cmp_rows, NULL, NULL);
1621 /* Create filter model */
1622 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1623 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1629 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1630 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1631 (GCallback) on_row_inserted_maybe_select_folder, self);
1634 g_object_unref (model);
1635 g_object_unref (filter_model);
1636 g_object_unref (sortable);
1638 /* Force a reselection of the INBOX next time the widget is shown */
1639 priv->reselect = TRUE;
1646 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1648 GtkTreeModel *model = NULL;
1649 TnyFolderStore *folder = NULL;
1651 ModestFolderView *tree_view = NULL;
1652 ModestFolderViewPrivate *priv = NULL;
1653 gboolean selected = FALSE;
1655 g_return_if_fail (sel);
1656 g_return_if_fail (user_data);
1658 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1660 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1662 tree_view = MODEST_FOLDER_VIEW (user_data);
1665 gtk_tree_model_get (model, &iter,
1666 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1669 /* If the folder is the same do not notify */
1670 if (folder && priv->cur_folder_store == folder) {
1671 g_object_unref (folder);
1676 /* Current folder was unselected */
1677 if (priv->cur_folder_store) {
1678 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1679 priv->cur_folder_store, FALSE);
1681 if (TNY_IS_FOLDER(priv->cur_folder_store))
1682 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1683 FALSE, NULL, NULL, NULL);
1685 /* FALSE --> don't expunge the messages */
1687 g_object_unref (priv->cur_folder_store);
1688 priv->cur_folder_store = NULL;
1691 /* New current references */
1692 priv->cur_folder_store = folder;
1694 /* New folder has been selected. Do not notify if there is
1695 nothing new selected */
1697 g_signal_emit (G_OBJECT(tree_view),
1698 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1699 0, priv->cur_folder_store, TRUE);
1704 modest_folder_view_get_selected (ModestFolderView *self)
1706 ModestFolderViewPrivate *priv;
1708 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1710 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1711 if (priv->cur_folder_store)
1712 g_object_ref (priv->cur_folder_store);
1714 return priv->cur_folder_store;
1718 get_cmp_rows_type_pos (GObject *folder)
1720 /* Remote accounts -> Local account -> MMC account .*/
1723 if (TNY_IS_ACCOUNT (folder) &&
1724 modest_tny_account_is_virtual_local_folders (
1725 TNY_ACCOUNT (folder))) {
1727 } else if (TNY_IS_ACCOUNT (folder)) {
1728 TnyAccount *account = TNY_ACCOUNT (folder);
1729 const gchar *account_id = tny_account_get_id (account);
1730 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1736 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1737 return -1; /* Should never happen */
1742 get_cmp_subfolder_type_pos (TnyFolderType t)
1744 /* Inbox, Outbox, Drafts, Sent, User */
1748 case TNY_FOLDER_TYPE_INBOX:
1751 case TNY_FOLDER_TYPE_OUTBOX:
1754 case TNY_FOLDER_TYPE_DRAFTS:
1757 case TNY_FOLDER_TYPE_SENT:
1766 * This function orders the mail accounts according to these rules:
1767 * 1st - remote accounts
1768 * 2nd - local account
1772 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1776 gchar *name1 = NULL;
1777 gchar *name2 = NULL;
1778 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1779 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1780 GObject *folder1 = NULL;
1781 GObject *folder2 = NULL;
1783 gtk_tree_model_get (tree_model, iter1,
1784 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1785 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1786 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1788 gtk_tree_model_get (tree_model, iter2,
1789 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1790 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1791 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1794 /* Return if we get no folder. This could happen when folder
1795 operations are happening. The model is updated after the
1796 folder copy/move actually occurs, so there could be
1797 situations where the model to be drawn is not correct */
1798 if (!folder1 || !folder2)
1801 if (type == TNY_FOLDER_TYPE_ROOT) {
1802 /* Compare the types, so that
1803 * Remote accounts -> Local account -> MMC account .*/
1804 const gint pos1 = get_cmp_rows_type_pos (folder1);
1805 const gint pos2 = get_cmp_rows_type_pos (folder2);
1806 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1807 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1810 else if (pos1 > pos2)
1813 /* Compare items of the same type: */
1815 TnyAccount *account1 = NULL;
1816 if (TNY_IS_ACCOUNT (folder1))
1817 account1 = TNY_ACCOUNT (folder1);
1819 TnyAccount *account2 = NULL;
1820 if (TNY_IS_ACCOUNT (folder2))
1821 account2 = TNY_ACCOUNT (folder2);
1823 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1824 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1826 if (!account_id && !account_id2) {
1828 } else if (!account_id) {
1830 } else if (!account_id2) {
1832 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1835 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1839 gint cmp1 = 0, cmp2 = 0;
1840 /* get the parent to know if it's a local folder */
1843 gboolean has_parent;
1844 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1846 GObject *parent_folder;
1847 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1848 gtk_tree_model_get (tree_model, &parent,
1849 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1850 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1852 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1853 TNY_IS_ACCOUNT (parent_folder)) {
1854 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1855 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1856 (TNY_FOLDER (folder1)));
1857 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1858 (TNY_FOLDER (folder2)));
1859 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1860 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1863 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1869 g_object_unref (parent_folder);
1872 /* if they are not local folders */
1874 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1875 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1879 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1881 cmp = (cmp1 - cmp2);
1886 g_object_unref(G_OBJECT(folder1));
1888 g_object_unref(G_OBJECT(folder2));
1896 /*****************************************************************************/
1897 /* DRAG and DROP stuff */
1898 /*****************************************************************************/
1900 * This function fills the #GtkSelectionData with the row and the
1901 * model that has been dragged. It's called when this widget is a
1902 * source for dnd after the event drop happened
1905 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1906 guint info, guint time, gpointer data)
1908 GtkTreeSelection *selection;
1909 GtkTreeModel *model;
1911 GtkTreePath *source_row;
1913 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1914 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1916 source_row = gtk_tree_model_get_path (model, &iter);
1917 gtk_tree_set_row_drag_data (selection_data,
1921 gtk_tree_path_free (source_row);
1925 typedef struct _DndHelper {
1926 ModestFolderView *folder_view;
1927 gboolean delete_source;
1928 GtkTreePath *source_row;
1929 GdkDragContext *context;
1934 dnd_helper_destroyer (DndHelper *helper)
1936 /* Free the helper */
1937 g_object_unref (helper->folder_view);
1938 gtk_tree_path_free (helper->source_row);
1939 g_slice_free (DndHelper, helper);
1943 xfer_cb (ModestMailOperation *mail_op,
1949 helper = (DndHelper *) user_data;
1951 if (modest_mail_operation_get_status (mail_op) ==
1952 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1958 /* Notify the drag source. Never call delete, the monitor will
1959 do the job if needed */
1960 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1962 /* Free the helper */
1963 dnd_helper_destroyer (helper);
1967 xfer_msgs_cb (ModestMailOperation *mail_op,
1971 xfer_cb (mail_op, user_data);
1975 xfer_folder_cb (ModestMailOperation *mail_op,
1976 TnyFolder *new_folder,
1980 GtkWidget *folder_view;
1982 helper = (DndHelper *) user_data;
1983 folder_view = g_object_ref (helper->folder_view);
1986 xfer_cb (mail_op, user_data);
1988 /* Select the folder */
1990 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1992 g_object_unref (folder_view);
1996 /* get the folder for the row the treepath refers to. */
1997 /* folder must be unref'd */
1998 static TnyFolderStore *
1999 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2002 TnyFolderStore *folder = NULL;
2004 if (gtk_tree_model_get_iter (model,&iter, path))
2005 gtk_tree_model_get (model, &iter,
2006 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2012 * This function is used by drag_data_received_cb to manage drag and
2013 * drop of a header, i.e, and drag from the header view to the folder
2017 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2018 GtkTreeModel *dest_model,
2019 GtkTreePath *dest_row,
2020 GtkSelectionData *selection_data,
2023 TnyList *headers = NULL;
2024 TnyFolder *folder = NULL;
2025 TnyFolderType folder_type;
2026 ModestMailOperation *mail_op = NULL;
2027 GtkTreeIter source_iter, dest_iter;
2028 ModestWindowMgr *mgr = NULL;
2029 ModestWindow *main_win = NULL;
2030 gchar **uris, **tmp;
2033 /* Build the list of headers */
2034 mgr = modest_runtime_get_window_mgr ();
2035 headers = tny_simple_list_new ();
2036 uris = modest_dnd_selection_data_get_paths (selection_data);
2039 while (*tmp != NULL) {
2044 path = gtk_tree_path_new_from_string (*tmp);
2045 gtk_tree_model_get_iter (source_model, &source_iter, path);
2046 gtk_tree_model_get (source_model, &source_iter,
2047 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2050 /* Do not enable d&d of headers already opened */
2051 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2052 tny_list_append (headers, G_OBJECT (header));
2054 /* Free and go on */
2055 gtk_tree_path_free (path);
2056 g_object_unref (header);
2061 /* Get the target folder */
2062 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2063 gtk_tree_model_get (dest_model, &dest_iter,
2064 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2067 if (!folder || !TNY_IS_FOLDER(folder)) {
2068 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2072 folder_type = modest_tny_folder_guess_folder_type (folder);
2073 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2074 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2075 goto cleanup; /* cannot move messages there */
2078 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2079 /* g_warning ("folder not writable"); */
2080 goto cleanup; /* verboten! */
2083 /* Ask for confirmation to move */
2084 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2086 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2090 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2092 if (response == GTK_RESPONSE_CANCEL)
2095 /* Transfer messages */
2096 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2097 modest_ui_actions_move_folder_error_handler,
2100 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2103 modest_mail_operation_xfer_msgs (mail_op,
2106 helper->delete_source,
2107 xfer_msgs_cb, helper);
2111 if (G_IS_OBJECT(mail_op))
2112 g_object_unref (G_OBJECT (mail_op));
2113 if (G_IS_OBJECT(folder))
2114 g_object_unref (G_OBJECT (folder));
2115 if (G_IS_OBJECT(headers))
2116 g_object_unref (headers);
2120 TnyFolderStore *src_folder;
2121 TnyFolderStore *dst_folder;
2122 ModestFolderView *folder_view;
2127 dnd_folder_info_destroyer (DndFolderInfo *info)
2129 if (info->src_folder)
2130 g_object_unref (info->src_folder);
2131 if (info->dst_folder)
2132 g_object_unref (info->dst_folder);
2133 if (info->folder_view)
2134 g_object_unref (info->folder_view);
2135 g_slice_free (DndFolderInfo, info);
2139 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2140 GtkWindow *parent_window,
2141 TnyAccount *account)
2143 time_t dnd_time = info->helper->time;
2144 GdkDragContext *context = info->helper->context;
2147 modest_ui_actions_on_account_connection_error (parent_window, account);
2149 /* Free the helper & info */
2150 dnd_helper_destroyer (info->helper);
2151 dnd_folder_info_destroyer (info);
2153 /* Notify the drag source. Never call delete, the monitor will
2154 do the job if needed */
2155 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2160 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2162 GtkWindow *parent_window,
2163 TnyAccount *account,
2166 DndFolderInfo *info = NULL;
2167 ModestMailOperation *mail_op;
2169 info = (DndFolderInfo *) user_data;
2171 if (err || canceled) {
2172 dnd_on_connection_failed_destroyer (info, parent_window, account);
2176 /* Do the mail operation */
2177 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2178 modest_ui_actions_move_folder_error_handler,
2179 info->src_folder, NULL);
2181 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2184 /* Transfer the folder */
2185 modest_mail_operation_xfer_folder (mail_op,
2186 TNY_FOLDER (info->src_folder),
2188 info->helper->delete_source,
2192 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2193 /* TNY_FOLDER (info->dst_folder), TRUE); */
2195 g_object_unref (G_OBJECT (mail_op));
2200 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2202 GtkWindow *parent_window,
2203 TnyAccount *account,
2206 DndFolderInfo *info = NULL;
2208 info = (DndFolderInfo *) user_data;
2210 if (err || canceled) {
2211 dnd_on_connection_failed_destroyer (info, parent_window, account);
2215 /* Connect to source folder and perform the copy/move */
2216 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2218 drag_and_drop_from_folder_view_src_folder_performer,
2223 * This function is used by drag_data_received_cb to manage drag and
2224 * drop of a folder, i.e, and drag from the folder view to the same
2228 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2229 GtkTreeModel *dest_model,
2230 GtkTreePath *dest_row,
2231 GtkSelectionData *selection_data,
2234 GtkTreeIter dest_iter, iter;
2235 TnyFolderStore *dest_folder = NULL;
2236 TnyFolderStore *folder = NULL;
2237 gboolean forbidden = FALSE;
2239 DndFolderInfo *info = NULL;
2241 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2243 g_warning ("%s: BUG: no main window", __FUNCTION__);
2248 /* check the folder rules for the destination */
2249 folder = tree_path_to_folder (dest_model, dest_row);
2250 if (TNY_IS_FOLDER(folder)) {
2251 ModestTnyFolderRules rules =
2252 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2253 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2254 } else if (TNY_IS_FOLDER_STORE(folder)) {
2255 /* enable local root as destination for folders */
2256 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
2257 && TNY_IS_ACCOUNT (folder))
2260 g_object_unref (folder);
2263 /* check the folder rules for the source */
2264 folder = tree_path_to_folder (source_model, helper->source_row);
2265 if (TNY_IS_FOLDER(folder)) {
2266 ModestTnyFolderRules rules =
2267 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2268 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2271 g_object_unref (folder);
2275 /* Check if the drag is possible */
2276 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2277 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2278 gtk_tree_path_free (helper->source_row);
2279 g_slice_free (DndHelper, helper);
2284 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2285 gtk_tree_model_get (dest_model, &dest_iter,
2286 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2288 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2289 gtk_tree_model_get (source_model, &iter,
2290 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2293 /* Create the info for the performer */
2294 info = g_slice_new (DndFolderInfo);
2295 info->src_folder = g_object_ref (folder);
2296 info->dst_folder = g_object_ref (dest_folder);
2297 info->folder_view = g_object_ref (helper->folder_view);
2298 info->helper = helper;
2300 /* Connect to the destination folder and perform the copy/move */
2301 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2303 drag_and_drop_from_folder_view_dst_folder_performer,
2307 g_object_unref (dest_folder);
2308 g_object_unref (folder);
2312 * This function receives the data set by the "drag-data-get" signal
2313 * handler. This information comes within the #GtkSelectionData. This
2314 * function will manage both the drags of folders of the treeview and
2315 * drags of headers of the header view widget.
2318 on_drag_data_received (GtkWidget *widget,
2319 GdkDragContext *context,
2322 GtkSelectionData *selection_data,
2327 GtkWidget *source_widget;
2328 GtkTreeModel *dest_model, *source_model;
2329 GtkTreePath *source_row, *dest_row;
2330 GtkTreeViewDropPosition pos;
2331 gboolean success = FALSE, delete_source = FALSE;
2332 DndHelper *helper = NULL;
2334 /* Do not allow further process */
2335 g_signal_stop_emission_by_name (widget, "drag-data-received");
2336 source_widget = gtk_drag_get_source_widget (context);
2338 /* Get the action */
2339 if (context->action == GDK_ACTION_MOVE) {
2340 delete_source = TRUE;
2342 /* Notify that there is no folder selected. We need to
2343 do this in order to update the headers view (and
2344 its monitors, because when moving, the old folder
2345 won't longer exist. We can not wait for the end of
2346 the operation, because the operation won't start if
2347 the folder is in use */
2348 if (source_widget == widget) {
2349 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2350 gtk_tree_selection_unselect_all (sel);
2354 /* Check if the get_data failed */
2355 if (selection_data == NULL || selection_data->length < 0)
2356 gtk_drag_finish (context, success, FALSE, time);
2358 /* Select the destination model */
2359 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2361 /* Get the path to the destination row. Can not call
2362 gtk_tree_view_get_drag_dest_row() because the source row
2363 is not selected anymore */
2364 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2367 /* Only allow drops IN other rows */
2369 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2370 pos == GTK_TREE_VIEW_DROP_AFTER)
2371 gtk_drag_finish (context, success, FALSE, time);
2373 /* Create the helper */
2374 helper = g_slice_new0 (DndHelper);
2375 helper->delete_source = delete_source;
2376 helper->context = context;
2377 helper->time = time;
2378 helper->folder_view = g_object_ref (widget);
2380 /* Drags from the header view */
2381 if (source_widget != widget) {
2382 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2384 drag_and_drop_from_header_view (source_model,
2390 /* Get the source model and row */
2391 gtk_tree_get_row_drag_data (selection_data,
2394 helper->source_row = gtk_tree_path_copy (source_row);
2396 drag_and_drop_from_folder_view (source_model,
2402 gtk_tree_path_free (source_row);
2406 gtk_tree_path_free (dest_row);
2410 * We define a "drag-drop" signal handler because we do not want to
2411 * use the default one, because the default one always calls
2412 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2413 * signal handler, because there we have all the information available
2414 * to know if the dnd was a success or not.
2417 drag_drop_cb (GtkWidget *widget,
2418 GdkDragContext *context,
2426 if (!context->targets)
2429 /* Check if we're dragging a folder row */
2430 target = gtk_drag_dest_find_target (widget, context, NULL);
2432 /* Request the data from the source. */
2433 gtk_drag_get_data(widget, context, target, time);
2439 * This function expands a node of a tree view if it's not expanded
2440 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2441 * does that, so that's why they're here.
2444 expand_row_timeout (gpointer data)
2446 GtkTreeView *tree_view = data;
2447 GtkTreePath *dest_path = NULL;
2448 GtkTreeViewDropPosition pos;
2449 gboolean result = FALSE;
2451 gdk_threads_enter ();
2453 gtk_tree_view_get_drag_dest_row (tree_view,
2458 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2459 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2460 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2461 gtk_tree_path_free (dest_path);
2465 gtk_tree_path_free (dest_path);
2470 gdk_threads_leave ();
2476 * This function is called whenever the pointer is moved over a widget
2477 * while dragging some data. It installs a timeout that will expand a
2478 * node of the treeview if not expanded yet. This function also calls
2479 * gdk_drag_status in order to set the suggested action that will be
2480 * used by the "drag-data-received" signal handler to know if we
2481 * should do a move or just a copy of the data.
2484 on_drag_motion (GtkWidget *widget,
2485 GdkDragContext *context,
2491 GtkTreeViewDropPosition pos;
2492 GtkTreePath *dest_row;
2493 GtkTreeModel *dest_model;
2494 ModestFolderViewPrivate *priv;
2495 GdkDragAction suggested_action;
2496 gboolean valid_location = FALSE;
2497 TnyFolderStore *folder = NULL;
2499 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2501 if (priv->timer_expander != 0) {
2502 g_source_remove (priv->timer_expander);
2503 priv->timer_expander = 0;
2506 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2511 /* Do not allow drops between folders */
2513 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2514 pos == GTK_TREE_VIEW_DROP_AFTER) {
2515 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2516 gdk_drag_status(context, 0, time);
2517 valid_location = FALSE;
2520 valid_location = TRUE;
2523 /* Check that the destination folder is writable */
2524 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2525 folder = tree_path_to_folder (dest_model, dest_row);
2526 if (folder && TNY_IS_FOLDER (folder)) {
2527 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2529 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2530 valid_location = FALSE;
2535 /* Expand the selected row after 1/2 second */
2536 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2537 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2538 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2541 /* Select the desired action. By default we pick MOVE */
2542 suggested_action = GDK_ACTION_MOVE;
2544 if (context->actions == GDK_ACTION_COPY)
2545 gdk_drag_status(context, GDK_ACTION_COPY, time);
2546 else if (context->actions == GDK_ACTION_MOVE)
2547 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2548 else if (context->actions & suggested_action)
2549 gdk_drag_status(context, suggested_action, time);
2551 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2555 g_object_unref (folder);
2557 gtk_tree_path_free (dest_row);
2558 g_signal_stop_emission_by_name (widget, "drag-motion");
2560 return valid_location;
2564 * This function sets the treeview as a source and a target for dnd
2565 * events. It also connects all the requirede signals.
2568 setup_drag_and_drop (GtkTreeView *self)
2570 /* Set up the folder view as a dnd destination. Set only the
2571 highlight flag, otherwise gtk will have a different
2573 gtk_drag_dest_set (GTK_WIDGET (self),
2574 GTK_DEST_DEFAULT_HIGHLIGHT,
2575 folder_view_drag_types,
2576 G_N_ELEMENTS (folder_view_drag_types),
2577 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2579 g_signal_connect (G_OBJECT (self),
2580 "drag_data_received",
2581 G_CALLBACK (on_drag_data_received),
2585 /* Set up the treeview as a dnd source */
2586 gtk_drag_source_set (GTK_WIDGET (self),
2588 folder_view_drag_types,
2589 G_N_ELEMENTS (folder_view_drag_types),
2590 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2592 g_signal_connect (G_OBJECT (self),
2594 G_CALLBACK (on_drag_motion),
2597 g_signal_connect (G_OBJECT (self),
2599 G_CALLBACK (on_drag_data_get),
2602 g_signal_connect (G_OBJECT (self),
2604 G_CALLBACK (drag_drop_cb),
2609 * This function manages the navigation through the folders using the
2610 * keyboard or the hardware keys in the device
2613 on_key_pressed (GtkWidget *self,
2617 GtkTreeSelection *selection;
2619 GtkTreeModel *model;
2620 gboolean retval = FALSE;
2622 /* Up and Down are automatically managed by the treeview */
2623 if (event->keyval == GDK_Return) {
2624 /* Expand/Collapse the selected row */
2625 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2626 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2629 path = gtk_tree_model_get_path (model, &iter);
2631 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2632 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2634 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2635 gtk_tree_path_free (path);
2637 /* No further processing */
2645 * We listen to the changes in the local folder account name key,
2646 * because we want to show the right name in the view. The local
2647 * folder account name corresponds to the device name in the Maemo
2648 * version. We do this because we do not want to query gconf on each
2649 * tree view refresh. It's better to cache it and change whenever
2653 on_configuration_key_changed (ModestConf* conf,
2655 ModestConfEvent event,
2656 ModestConfNotificationId id,
2657 ModestFolderView *self)
2659 ModestFolderViewPrivate *priv;
2662 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2663 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2665 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2666 g_free (priv->local_account_name);
2668 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2669 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2671 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2672 MODEST_CONF_DEVICE_NAME, NULL);
2674 /* Force a redraw */
2675 #if GTK_CHECK_VERSION(2, 8, 0)
2676 GtkTreeViewColumn * tree_column;
2678 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2679 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2680 gtk_tree_view_column_queue_resize (tree_column);
2682 gtk_widget_queue_draw (GTK_WIDGET (self));
2688 modest_folder_view_set_style (ModestFolderView *self,
2689 ModestFolderViewStyle style)
2691 ModestFolderViewPrivate *priv;
2693 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2694 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2695 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2697 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2700 priv->style = style;
2704 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2705 const gchar *account_id)
2707 ModestFolderViewPrivate *priv;
2708 GtkTreeModel *model;
2710 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2712 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2714 /* This will be used by the filter_row callback,
2715 * to decided which rows to show: */
2716 if (priv->visible_account_id) {
2717 g_free (priv->visible_account_id);
2718 priv->visible_account_id = NULL;
2721 priv->visible_account_id = g_strdup (account_id);
2724 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2725 if (GTK_IS_TREE_MODEL_FILTER (model))
2726 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2728 /* Save settings to gconf */
2729 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2730 MODEST_CONF_FOLDER_VIEW_KEY);
2734 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2736 ModestFolderViewPrivate *priv;
2738 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2740 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2742 return (const gchar *) priv->visible_account_id;
2746 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2750 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2752 gtk_tree_model_get (model, iter,
2753 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2756 gboolean result = FALSE;
2757 if (type == TNY_FOLDER_TYPE_INBOX) {
2761 *inbox_iter = *iter;
2765 if (gtk_tree_model_iter_children (model, &child, iter)) {
2766 if (find_inbox_iter (model, &child, inbox_iter))
2770 } while (gtk_tree_model_iter_next (model, iter));
2779 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2781 GtkTreeModel *model;
2782 GtkTreeIter iter, inbox_iter;
2783 GtkTreeSelection *sel;
2784 GtkTreePath *path = NULL;
2786 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2788 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2792 expand_root_items (self);
2793 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2795 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2796 g_warning ("%s: model is empty", __FUNCTION__);
2800 if (find_inbox_iter (model, &iter, &inbox_iter))
2801 path = gtk_tree_model_get_path (model, &inbox_iter);
2803 path = gtk_tree_path_new_first ();
2805 /* Select the row and free */
2806 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2807 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2808 gtk_tree_path_free (path);
2811 gtk_widget_grab_focus (GTK_WIDGET(self));
2817 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2822 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2823 TnyFolder* a_folder;
2826 gtk_tree_model_get (model, iter,
2827 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2828 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2829 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2833 if (folder == a_folder) {
2834 g_object_unref (a_folder);
2835 *folder_iter = *iter;
2838 g_object_unref (a_folder);
2840 if (gtk_tree_model_iter_children (model, &child, iter)) {
2841 if (find_folder_iter (model, &child, folder_iter, folder))
2845 } while (gtk_tree_model_iter_next (model, iter));
2852 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2855 ModestFolderView *self)
2857 ModestFolderViewPrivate *priv = NULL;
2858 GtkTreeSelection *sel;
2859 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2860 GObject *instance = NULL;
2862 if (!MODEST_IS_FOLDER_VIEW(self))
2865 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2867 priv->reexpand = TRUE;
2869 gtk_tree_model_get (tree_model, iter,
2870 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2871 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2873 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2874 priv->folder_to_select = g_object_ref (instance);
2876 g_object_unref (instance);
2879 if (priv->folder_to_select) {
2881 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2884 path = gtk_tree_model_get_path (tree_model, iter);
2885 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2887 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2889 gtk_tree_selection_select_iter (sel, iter);
2890 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2892 gtk_tree_path_free (path);
2897 modest_folder_view_disable_next_folder_selection (self);
2899 /* Refilter the model */
2900 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2906 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2908 ModestFolderViewPrivate *priv;
2910 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2912 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2914 if (priv->folder_to_select)
2915 g_object_unref(priv->folder_to_select);
2917 priv->folder_to_select = NULL;
2921 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2922 gboolean after_change)
2924 GtkTreeModel *model;
2925 GtkTreeIter iter, folder_iter;
2926 GtkTreeSelection *sel;
2927 ModestFolderViewPrivate *priv = NULL;
2929 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2930 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2932 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2935 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2936 gtk_tree_selection_unselect_all (sel);
2938 if (priv->folder_to_select)
2939 g_object_unref(priv->folder_to_select);
2940 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2944 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2949 /* Refilter the model, before selecting the folder */
2950 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2952 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2953 g_warning ("%s: model is empty", __FUNCTION__);
2957 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2960 path = gtk_tree_model_get_path (model, &folder_iter);
2961 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2963 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2964 gtk_tree_selection_select_iter (sel, &folder_iter);
2965 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2967 gtk_tree_path_free (path);
2975 modest_folder_view_copy_selection (ModestFolderView *self)
2977 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2979 /* Copy selection */
2980 _clipboard_set_selected_data (self, FALSE);
2984 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2986 ModestFolderViewPrivate *priv = NULL;
2987 GtkTreeModel *model = NULL;
2988 const gchar **hidding = NULL;
2989 guint i, n_selected;
2991 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2994 /* Copy selection */
2995 if (!_clipboard_set_selected_data (folder_view, TRUE))
2998 /* Get hidding ids */
2999 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3001 /* Clear hidding array created by previous cut operation */
3002 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3004 /* Copy hidding array */
3005 priv->n_selected = n_selected;
3006 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3007 for (i=0; i < n_selected; i++)
3008 priv->hidding_ids[i] = g_strdup(hidding[i]);
3010 /* Hide cut folders */
3011 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3012 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3016 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3017 ModestFolderView *folder_view_dst)
3019 GtkTreeModel *filter_model = NULL;
3020 GtkTreeModel *model = NULL;
3021 GtkTreeModel *new_filter_model = NULL;
3023 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3024 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3027 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3028 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3030 /* Build new filter model */
3031 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3032 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3036 /* Set copied model */
3037 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3038 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3039 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3042 g_object_unref (new_filter_model);
3046 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3049 GtkTreeModel *model = NULL;
3050 ModestFolderViewPrivate* priv;
3052 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3054 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3055 priv->show_non_move = show;
3056 /* modest_folder_view_update_model(folder_view, */
3057 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3059 /* Hide special folders */
3060 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3061 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3062 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3066 /* Returns FALSE if it did not selected anything */
3068 _clipboard_set_selected_data (ModestFolderView *folder_view,
3071 ModestFolderViewPrivate *priv = NULL;
3072 TnyFolderStore *folder = NULL;
3073 gboolean retval = FALSE;
3075 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3076 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3078 /* Set selected data on clipboard */
3079 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3080 folder = modest_folder_view_get_selected (folder_view);
3082 /* Do not allow to select an account */
3083 if (TNY_IS_FOLDER (folder)) {
3084 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3089 g_object_unref (folder);
3095 _clear_hidding_filter (ModestFolderView *folder_view)
3097 ModestFolderViewPrivate *priv;
3100 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3101 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3103 if (priv->hidding_ids != NULL) {
3104 for (i=0; i < priv->n_selected; i++)
3105 g_free (priv->hidding_ids[i]);
3106 g_free(priv->hidding_ids);
3112 on_display_name_changed (ModestAccountMgr *mgr,
3113 const gchar *account,
3116 ModestFolderView *self;
3118 self = MODEST_FOLDER_VIEW (user_data);
3120 /* Force a redraw */
3121 #if GTK_CHECK_VERSION(2, 8, 0)
3122 GtkTreeViewColumn * tree_column;
3124 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3125 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3126 gtk_tree_view_column_queue_resize (tree_column);
3128 gtk_widget_queue_draw (GTK_WIDGET (self));