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, gpointer data);
81 static void on_account_removed (TnyAccountStore *self,
85 static void on_account_inserted (TnyAccountStore *self,
89 static void on_account_changed (TnyAccountStore *self,
93 static gint cmp_rows (GtkTreeModel *tree_model,
98 static gboolean filter_row (GtkTreeModel *model,
102 static gboolean on_key_pressed (GtkWidget *self,
106 static void on_configuration_key_changed (ModestConf* conf,
108 ModestConfEvent event,
109 ModestConfNotificationId notification_id,
110 ModestFolderView *self);
113 static void on_drag_data_get (GtkWidget *widget,
114 GdkDragContext *context,
115 GtkSelectionData *selection_data,
120 static void on_drag_data_received (GtkWidget *widget,
121 GdkDragContext *context,
124 GtkSelectionData *selection_data,
129 static gboolean on_drag_motion (GtkWidget *widget,
130 GdkDragContext *context,
136 static void expand_root_items (ModestFolderView *self);
138 static gint expand_row_timeout (gpointer data);
140 static void setup_drag_and_drop (GtkTreeView *self);
142 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
145 static void _clear_hidding_filter (ModestFolderView *folder_view);
147 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
150 ModestFolderView *self);
152 static void on_display_name_changed (ModestAccountMgr *self,
153 const gchar *account,
157 FOLDER_SELECTION_CHANGED_SIGNAL,
158 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
162 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
163 struct _ModestFolderViewPrivate {
164 TnyAccountStore *account_store;
165 TnyFolderStore *cur_folder_store;
167 TnyFolder *folder_to_select; /* folder to select after the next update */
169 gulong changed_signal;
170 gulong account_inserted_signal;
171 gulong account_removed_signal;
172 gulong account_changed_signal;
173 gulong conf_key_signal;
174 gulong display_name_changed_signal;
176 /* not unref this object, its a singlenton */
177 ModestEmailClipboard *clipboard;
179 /* Filter tree model */
183 TnyFolderStoreQuery *query;
184 guint timer_expander;
186 gchar *local_account_name;
187 gchar *visible_account_id;
188 ModestFolderViewStyle style;
190 gboolean reselect; /* we use this to force a reselection of the INBOX */
191 gboolean show_non_move;
192 gboolean reexpand; /* next time we expose, we'll expand all root folders */
194 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
195 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
196 MODEST_TYPE_FOLDER_VIEW, \
197 ModestFolderViewPrivate))
199 static GObjectClass *parent_class = NULL;
201 static guint signals[LAST_SIGNAL] = {0};
204 modest_folder_view_get_type (void)
206 static GType my_type = 0;
208 static const GTypeInfo my_info = {
209 sizeof(ModestFolderViewClass),
210 NULL, /* base init */
211 NULL, /* base finalize */
212 (GClassInitFunc) modest_folder_view_class_init,
213 NULL, /* class finalize */
214 NULL, /* class data */
215 sizeof(ModestFolderView),
217 (GInstanceInitFunc) modest_folder_view_init,
221 static const GInterfaceInfo tny_account_store_view_info = {
222 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
223 NULL, /* interface_finalize */
224 NULL /* interface_data */
228 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
232 g_type_add_interface_static (my_type,
233 TNY_TYPE_ACCOUNT_STORE_VIEW,
234 &tny_account_store_view_info);
240 modest_folder_view_class_init (ModestFolderViewClass *klass)
242 GObjectClass *gobject_class;
243 gobject_class = (GObjectClass*) klass;
245 parent_class = g_type_class_peek_parent (klass);
246 gobject_class->finalize = modest_folder_view_finalize;
248 g_type_class_add_private (gobject_class,
249 sizeof(ModestFolderViewPrivate));
251 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
252 g_signal_new ("folder_selection_changed",
253 G_TYPE_FROM_CLASS (gobject_class),
255 G_STRUCT_OFFSET (ModestFolderViewClass,
256 folder_selection_changed),
258 modest_marshal_VOID__POINTER_BOOLEAN,
259 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
262 * This signal is emitted whenever the currently selected
263 * folder display name is computed. Note that the name could
264 * be different to the folder name, because we could append
265 * the unread messages count to the folder name to build the
266 * folder display name
268 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
269 g_signal_new ("folder-display-name-changed",
270 G_TYPE_FROM_CLASS (gobject_class),
272 G_STRUCT_OFFSET (ModestFolderViewClass,
273 folder_display_name_changed),
275 g_cclosure_marshal_VOID__STRING,
276 G_TYPE_NONE, 1, G_TYPE_STRING);
279 /* Simplify checks for NULLs: */
281 strings_are_equal (const gchar *a, const gchar *b)
287 return (strcmp (a, b) == 0);
294 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
296 GObject *instance = NULL;
298 gtk_tree_model_get (model, iter,
299 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
303 return FALSE; /* keep walking */
305 if (!TNY_IS_ACCOUNT (instance)) {
306 g_object_unref (instance);
307 return FALSE; /* keep walking */
310 /* Check if this is the looked-for account: */
311 TnyAccount *this_account = TNY_ACCOUNT (instance);
312 TnyAccount *account = TNY_ACCOUNT (data);
314 const gchar *this_account_id = tny_account_get_id(this_account);
315 const gchar *account_id = tny_account_get_id(account);
316 g_object_unref (instance);
319 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
320 if (strings_are_equal(this_account_id, account_id)) {
321 /* Tell the model that the data has changed, so that
322 * it calls the cell_data_func callbacks again: */
323 /* TODO: This does not seem to actually cause the new string to be shown: */
324 gtk_tree_model_row_changed (model, path, iter);
326 return TRUE; /* stop walking */
329 return FALSE; /* keep walking */
334 ModestFolderView *self;
335 gchar *previous_name;
336 } GetMmcAccountNameData;
339 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
341 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
343 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
345 if (!strings_are_equal (
346 tny_account_get_name(TNY_ACCOUNT(account)),
347 data->previous_name)) {
349 /* Tell the model that the data has changed, so that
350 * it calls the cell_data_func callbacks again: */
351 ModestFolderView *self = data->self;
352 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
354 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
357 g_free (data->previous_name);
358 g_slice_free (GetMmcAccountNameData, data);
362 text_cell_data (GtkTreeViewColumn *column,
363 GtkCellRenderer *renderer,
364 GtkTreeModel *tree_model,
368 ModestFolderViewPrivate *priv;
369 GObject *rendobj = (GObject *) renderer;
371 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
372 GObject *instance = NULL;
374 gtk_tree_model_get (tree_model, iter,
375 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
376 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
377 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));
541 if (!avirt_pixbuf_open) {
542 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
543 avirt_pixbuf_open = gdk_pixbuf_copy (avirt_pixbuf);
544 gdk_pixbuf_composite (emblem, avirt_pixbuf_open, 0, 0,
545 MIN (gdk_pixbuf_get_width (emblem),
546 gdk_pixbuf_get_width (avirt_pixbuf_open)),
547 MIN (gdk_pixbuf_get_height (emblem),
548 gdk_pixbuf_get_height (avirt_pixbuf_open)),
549 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
550 g_object_unref (emblem);
553 if (!avirt_pixbuf_close) {
554 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
555 avirt_pixbuf_close = gdk_pixbuf_copy (avirt_pixbuf);
556 gdk_pixbuf_composite (emblem, avirt_pixbuf_close, 0, 0,
557 MIN (gdk_pixbuf_get_width (emblem),
558 gdk_pixbuf_get_width (avirt_pixbuf_close)),
559 MIN (gdk_pixbuf_get_height (emblem),
560 gdk_pixbuf_get_height (avirt_pixbuf_close)),
561 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
562 g_object_unref (emblem);
566 pixbuf = g_object_ref (avirt_pixbuf);
567 pixbuf_open = g_object_ref (avirt_pixbuf_open);
568 pixbuf_close = g_object_ref (avirt_pixbuf_close);
572 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
574 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
576 ammc_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_MMC));
578 if (!ammc_pixbuf_open) {
579 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
580 ammc_pixbuf_open = gdk_pixbuf_copy (ammc_pixbuf);
581 gdk_pixbuf_composite (emblem, ammc_pixbuf_open, 0, 0,
582 MIN (gdk_pixbuf_get_width (emblem),
583 gdk_pixbuf_get_width (ammc_pixbuf_open)),
584 MIN (gdk_pixbuf_get_height (emblem),
585 gdk_pixbuf_get_height (ammc_pixbuf_open)),
586 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
587 g_object_unref (emblem);
590 if (!ammc_pixbuf_close) {
591 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
592 ammc_pixbuf_close = gdk_pixbuf_copy (ammc_pixbuf);
593 gdk_pixbuf_composite (emblem, ammc_pixbuf_close, 0, 0,
594 MIN (gdk_pixbuf_get_width (emblem),
595 gdk_pixbuf_get_width (ammc_pixbuf_close)),
596 MIN (gdk_pixbuf_get_height (emblem),
597 gdk_pixbuf_get_height (ammc_pixbuf_close)),
598 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
599 g_object_unref (emblem);
603 pixbuf = g_object_ref (ammc_pixbuf);
604 pixbuf_open = g_object_ref (ammc_pixbuf_open);
605 pixbuf_close = g_object_ref (ammc_pixbuf_close);
610 anorm_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT));
612 if (!anorm_pixbuf_open) {
613 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
614 anorm_pixbuf_open = gdk_pixbuf_copy (anorm_pixbuf);
615 gdk_pixbuf_composite (emblem, anorm_pixbuf_open, 0, 0,
616 MIN (gdk_pixbuf_get_width (emblem),
617 gdk_pixbuf_get_width (anorm_pixbuf_open)),
618 MIN (gdk_pixbuf_get_height (emblem),
619 gdk_pixbuf_get_height (anorm_pixbuf_open)),
620 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
621 g_object_unref (emblem);
624 if (!anorm_pixbuf_close) {
625 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
626 anorm_pixbuf_close = gdk_pixbuf_copy (anorm_pixbuf);
627 gdk_pixbuf_composite (emblem, anorm_pixbuf_close, 0, 0,
628 MIN (gdk_pixbuf_get_width (emblem),
629 gdk_pixbuf_get_width (anorm_pixbuf_close)),
630 MIN (gdk_pixbuf_get_height (emblem),
631 gdk_pixbuf_get_height (anorm_pixbuf_close)),
632 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
633 g_object_unref (emblem);
637 pixbuf = g_object_ref (anorm_pixbuf);
638 pixbuf_open = g_object_ref (anorm_pixbuf_open);
639 pixbuf_close = g_object_ref (anorm_pixbuf_close);
645 case TNY_FOLDER_TYPE_INBOX:
648 inbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX));
650 if (!inbox_pixbuf_open) {
651 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
652 inbox_pixbuf_open = gdk_pixbuf_copy (inbox_pixbuf);
653 gdk_pixbuf_composite (emblem, inbox_pixbuf_open, 0, 0,
654 MIN (gdk_pixbuf_get_width (emblem),
655 gdk_pixbuf_get_width (inbox_pixbuf_open)),
656 MIN (gdk_pixbuf_get_height (emblem),
657 gdk_pixbuf_get_height (inbox_pixbuf_open)),
658 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
659 g_object_unref (emblem);
662 if (!inbox_pixbuf_close) {
663 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
664 inbox_pixbuf_close = gdk_pixbuf_copy (inbox_pixbuf);
665 gdk_pixbuf_composite (emblem, inbox_pixbuf_close, 0, 0,
666 MIN (gdk_pixbuf_get_width (emblem),
667 gdk_pixbuf_get_width (inbox_pixbuf_close)),
668 MIN (gdk_pixbuf_get_height (emblem),
669 gdk_pixbuf_get_height (inbox_pixbuf_close)),
670 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
671 g_object_unref (emblem);
675 pixbuf = g_object_ref (inbox_pixbuf);
676 pixbuf_open = g_object_ref (inbox_pixbuf_open);
677 pixbuf_close = g_object_ref (inbox_pixbuf_close);
680 case TNY_FOLDER_TYPE_OUTBOX:
682 outbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX));
684 if (!outbox_pixbuf_open) {
685 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
686 outbox_pixbuf_open = gdk_pixbuf_copy (outbox_pixbuf);
687 gdk_pixbuf_composite (emblem, outbox_pixbuf_open, 0, 0,
688 MIN (gdk_pixbuf_get_width (emblem),
689 gdk_pixbuf_get_width (outbox_pixbuf_open)),
690 MIN (gdk_pixbuf_get_height (emblem),
691 gdk_pixbuf_get_height (outbox_pixbuf_open)),
692 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
693 g_object_unref (emblem);
696 if (!outbox_pixbuf_close) {
697 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
698 outbox_pixbuf_close = gdk_pixbuf_copy (outbox_pixbuf);
699 gdk_pixbuf_composite (emblem, outbox_pixbuf_close, 0, 0,
700 MIN (gdk_pixbuf_get_width (emblem),
701 gdk_pixbuf_get_width (outbox_pixbuf_close)),
702 MIN (gdk_pixbuf_get_height (emblem),
703 gdk_pixbuf_get_height (outbox_pixbuf_close)),
704 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
705 g_object_unref (emblem);
709 pixbuf = g_object_ref (outbox_pixbuf);
710 pixbuf_open = g_object_ref (outbox_pixbuf_open);
711 pixbuf_close = g_object_ref (outbox_pixbuf_close);
714 case TNY_FOLDER_TYPE_JUNK:
716 junk_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK));
718 if (!junk_pixbuf_open) {
719 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
720 junk_pixbuf_open = gdk_pixbuf_copy (junk_pixbuf);
721 gdk_pixbuf_composite (emblem, junk_pixbuf_open, 0, 0,
722 MIN (gdk_pixbuf_get_width (emblem),
723 gdk_pixbuf_get_width (junk_pixbuf_open)),
724 MIN (gdk_pixbuf_get_height (emblem),
725 gdk_pixbuf_get_height (junk_pixbuf_open)),
726 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
727 g_object_unref (emblem);
730 if (!junk_pixbuf_close) {
731 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
732 junk_pixbuf_close = gdk_pixbuf_copy (junk_pixbuf);
733 gdk_pixbuf_composite (emblem, junk_pixbuf_close, 0, 0,
734 MIN (gdk_pixbuf_get_width (emblem),
735 gdk_pixbuf_get_width (junk_pixbuf_close)),
736 MIN (gdk_pixbuf_get_height (emblem),
737 gdk_pixbuf_get_height (junk_pixbuf_close)),
738 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
739 g_object_unref (emblem);
743 pixbuf = g_object_ref (junk_pixbuf);
744 pixbuf_open = g_object_ref (junk_pixbuf_open);
745 pixbuf_close = g_object_ref (junk_pixbuf_close);
748 case TNY_FOLDER_TYPE_SENT:
750 sent_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_SENT));
752 if (!junk_pixbuf_open) {
753 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
754 sent_pixbuf_open = gdk_pixbuf_copy (sent_pixbuf);
755 gdk_pixbuf_composite (emblem, sent_pixbuf_open, 0, 0,
756 MIN (gdk_pixbuf_get_width (emblem),
757 gdk_pixbuf_get_width (sent_pixbuf_open)),
758 MIN (gdk_pixbuf_get_height (emblem),
759 gdk_pixbuf_get_height (sent_pixbuf_open)),
760 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
761 g_object_unref (emblem);
764 if (!sent_pixbuf_close) {
765 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
766 sent_pixbuf_close = gdk_pixbuf_copy (sent_pixbuf);
767 gdk_pixbuf_composite (emblem, sent_pixbuf_close, 0, 0,
768 MIN (gdk_pixbuf_get_width (emblem),
769 gdk_pixbuf_get_width (sent_pixbuf_close)),
770 MIN (gdk_pixbuf_get_height (emblem),
771 gdk_pixbuf_get_height (sent_pixbuf_close)),
772 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
773 g_object_unref (emblem);
777 pixbuf = g_object_ref (sent_pixbuf);
778 pixbuf_open = g_object_ref (sent_pixbuf_open);
779 pixbuf_close = g_object_ref (sent_pixbuf_close);
782 case TNY_FOLDER_TYPE_TRASH:
784 trash_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH));
786 if (!junk_pixbuf_open) {
787 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
788 trash_pixbuf_open = gdk_pixbuf_copy (trash_pixbuf);
789 gdk_pixbuf_composite (emblem, trash_pixbuf_open, 0, 0,
790 MIN (gdk_pixbuf_get_width (emblem),
791 gdk_pixbuf_get_width (trash_pixbuf_open)),
792 MIN (gdk_pixbuf_get_height (emblem),
793 gdk_pixbuf_get_height (trash_pixbuf_open)),
794 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
795 g_object_unref (emblem);
798 if (!trash_pixbuf_close) {
799 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
800 trash_pixbuf_close = gdk_pixbuf_copy (trash_pixbuf);
801 gdk_pixbuf_composite (emblem, trash_pixbuf_close, 0, 0,
802 MIN (gdk_pixbuf_get_width (emblem),
803 gdk_pixbuf_get_width (trash_pixbuf_close)),
804 MIN (gdk_pixbuf_get_height (emblem),
805 gdk_pixbuf_get_height (trash_pixbuf_close)),
806 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
807 g_object_unref (emblem);
811 pixbuf = g_object_ref (trash_pixbuf);
812 pixbuf_open = g_object_ref (trash_pixbuf_open);
813 pixbuf_close = g_object_ref (trash_pixbuf_close);
816 case TNY_FOLDER_TYPE_DRAFTS:
818 draft_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS));
820 if (!junk_pixbuf_open) {
821 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
822 draft_pixbuf_open = gdk_pixbuf_copy (draft_pixbuf);
823 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0,
824 MIN (gdk_pixbuf_get_width (emblem),
825 gdk_pixbuf_get_width (draft_pixbuf_open)),
826 MIN (gdk_pixbuf_get_height (emblem),
827 gdk_pixbuf_get_height (draft_pixbuf_open)),
828 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
829 g_object_unref (emblem);
832 if (!draft_pixbuf_close) {
833 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
834 draft_pixbuf_close = gdk_pixbuf_copy (draft_pixbuf);
835 gdk_pixbuf_composite (emblem, draft_pixbuf_close, 0, 0,
836 MIN (gdk_pixbuf_get_width (emblem),
837 gdk_pixbuf_get_width (draft_pixbuf_close)),
838 MIN (gdk_pixbuf_get_height (emblem),
839 gdk_pixbuf_get_height (draft_pixbuf_close)),
840 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
841 g_object_unref (emblem);
845 pixbuf = g_object_ref (draft_pixbuf);
846 pixbuf_open = g_object_ref (draft_pixbuf_open);
847 pixbuf_close = g_object_ref (draft_pixbuf_close);
850 case TNY_FOLDER_TYPE_NORMAL:
853 normal_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL));
855 if (!junk_pixbuf_open) {
856 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
857 normal_pixbuf_open = gdk_pixbuf_copy (normal_pixbuf);
858 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0,
859 MIN (gdk_pixbuf_get_width (emblem),
860 gdk_pixbuf_get_width (normal_pixbuf_open)),
861 MIN (gdk_pixbuf_get_height (emblem),
862 gdk_pixbuf_get_height (normal_pixbuf_open)),
863 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
864 g_object_unref (emblem);
867 if (!normal_pixbuf_close) {
868 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
869 normal_pixbuf_close = gdk_pixbuf_copy (normal_pixbuf);
870 gdk_pixbuf_composite (emblem, normal_pixbuf_close, 0, 0,
871 MIN (gdk_pixbuf_get_width (emblem),
872 gdk_pixbuf_get_width (normal_pixbuf_close)),
873 MIN (gdk_pixbuf_get_height (emblem),
874 gdk_pixbuf_get_height (normal_pixbuf_close)),
875 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
876 g_object_unref (emblem);
880 pixbuf = g_object_ref (normal_pixbuf);
881 pixbuf_open = g_object_ref (normal_pixbuf_open);
882 pixbuf_close = g_object_ref (normal_pixbuf_close);
888 retval->pixbuf = pixbuf;
889 retval->pixbuf_open = pixbuf_open;
890 retval->pixbuf_close = pixbuf_close;
897 free_pixbufs (ThreePixbufs *pixbufs)
899 g_object_unref (pixbufs->pixbuf);
900 g_object_unref (pixbufs->pixbuf_open);
901 g_object_unref (pixbufs->pixbuf_close);
902 g_slice_free (ThreePixbufs, pixbufs);
906 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
907 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
909 GObject *rendobj = (GObject *) renderer, *instance = NULL;
910 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
911 gboolean has_children;
912 ThreePixbufs *pixbufs;
914 gtk_tree_model_get (tree_model, iter,
915 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
916 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
922 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
923 pixbufs = get_folder_icons (type, instance);
924 g_object_unref (instance);
927 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
930 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
931 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
934 free_pixbufs (pixbufs);
940 add_columns (GtkWidget *treeview)
942 GtkTreeViewColumn *column;
943 GtkCellRenderer *renderer;
944 GtkTreeSelection *sel;
947 column = gtk_tree_view_column_new ();
949 /* Set icon and text render function */
950 renderer = gtk_cell_renderer_pixbuf_new();
951 gtk_tree_view_column_pack_start (column, renderer, FALSE);
952 gtk_tree_view_column_set_cell_data_func(column, renderer,
953 icon_cell_data, treeview, NULL);
955 renderer = gtk_cell_renderer_text_new();
956 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END,
957 "ellipsize-set", TRUE, NULL);
958 gtk_tree_view_column_pack_start (column, renderer, TRUE);
959 gtk_tree_view_column_set_cell_data_func(column, renderer,
960 text_cell_data, treeview, NULL);
962 /* Set selection mode */
963 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
964 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
966 /* Set treeview appearance */
967 gtk_tree_view_column_set_spacing (column, 2);
968 gtk_tree_view_column_set_resizable (column, TRUE);
969 gtk_tree_view_column_set_fixed_width (column, TRUE);
970 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
971 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
974 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
978 modest_folder_view_init (ModestFolderView *obj)
980 ModestFolderViewPrivate *priv;
983 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
985 priv->timer_expander = 0;
986 priv->account_store = NULL;
988 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
989 priv->cur_folder_store = NULL;
990 priv->visible_account_id = NULL;
991 priv->folder_to_select = NULL;
993 priv->reexpand = TRUE;
995 /* Initialize the local account name */
996 conf = modest_runtime_get_conf();
997 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
999 /* Init email clipboard */
1000 priv->clipboard = modest_runtime_get_email_clipboard ();
1001 priv->hidding_ids = NULL;
1002 priv->n_selected = 0;
1003 priv->reselect = FALSE;
1004 priv->show_non_move = TRUE;
1006 /* Build treeview */
1007 add_columns (GTK_WIDGET (obj));
1009 /* Setup drag and drop */
1010 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1012 /* Connect signals */
1013 g_signal_connect (G_OBJECT (obj),
1015 G_CALLBACK (on_key_pressed), NULL);
1017 priv->display_name_changed_signal =
1018 g_signal_connect (modest_runtime_get_account_mgr (),
1019 "display_name_changed",
1020 G_CALLBACK (on_display_name_changed),
1024 * Track changes in the local account name (in the device it
1025 * will be the device name)
1027 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1029 G_CALLBACK(on_configuration_key_changed),
1034 tny_account_store_view_init (gpointer g, gpointer iface_data)
1036 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1038 klass->set_account_store_func = modest_folder_view_set_account_store;
1044 modest_folder_view_finalize (GObject *obj)
1046 ModestFolderViewPrivate *priv;
1047 GtkTreeSelection *sel;
1049 g_return_if_fail (obj);
1051 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1053 if (priv->timer_expander != 0) {
1054 g_source_remove (priv->timer_expander);
1055 priv->timer_expander = 0;
1058 if (priv->account_store) {
1059 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1060 priv->account_inserted_signal);
1061 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1062 priv->account_removed_signal);
1063 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1064 priv->account_changed_signal);
1065 g_object_unref (G_OBJECT(priv->account_store));
1066 priv->account_store = NULL;
1070 g_object_unref (G_OBJECT (priv->query));
1074 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1075 if (priv->folder_to_select) {
1076 g_object_unref (G_OBJECT(priv->folder_to_select));
1077 priv->folder_to_select = NULL;
1080 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1082 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1084 g_free (priv->local_account_name);
1085 g_free (priv->visible_account_id);
1087 if (priv->conf_key_signal) {
1088 g_signal_handler_disconnect (modest_runtime_get_conf (),
1089 priv->conf_key_signal);
1090 priv->conf_key_signal = 0;
1093 if (priv->cur_folder_store) {
1094 if (TNY_IS_FOLDER(priv->cur_folder_store))
1095 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
1096 /* FALSE --> expunge the message */
1098 g_object_unref (priv->cur_folder_store);
1099 priv->cur_folder_store = NULL;
1102 /* Clear hidding array created by cut operation */
1103 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1105 G_OBJECT_CLASS(parent_class)->finalize (obj);
1110 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1112 ModestFolderViewPrivate *priv;
1115 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1116 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1118 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1119 device = tny_account_store_get_device (account_store);
1121 if (G_UNLIKELY (priv->account_store)) {
1123 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1124 priv->account_inserted_signal))
1125 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1126 priv->account_inserted_signal);
1127 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1128 priv->account_removed_signal))
1129 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1130 priv->account_removed_signal);
1131 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1132 priv->account_changed_signal))
1133 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1134 priv->account_changed_signal);
1135 g_object_unref (G_OBJECT (priv->account_store));
1138 priv->account_store = g_object_ref (G_OBJECT (account_store));
1140 priv->account_removed_signal =
1141 g_signal_connect (G_OBJECT(account_store), "account_removed",
1142 G_CALLBACK (on_account_removed), self);
1144 priv->account_inserted_signal =
1145 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1146 G_CALLBACK (on_account_inserted), self);
1148 priv->account_changed_signal =
1149 g_signal_connect (G_OBJECT(account_store), "account_changed",
1150 G_CALLBACK (on_account_changed), self);
1152 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1154 g_object_unref (G_OBJECT (device));
1158 on_connection_status_changed (TnyAccount *self,
1159 TnyConnectionStatus status,
1162 /* If the account becomes online then refresh it */
1163 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1164 const gchar *acc_name;
1165 GtkWidget *my_window;
1167 my_window = gtk_widget_get_ancestor (GTK_WIDGET (user_data), MODEST_TYPE_WINDOW);
1168 acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (self);
1169 modest_ui_actions_do_send_receive (acc_name, MODEST_WINDOW (my_window));
1174 on_account_inserted (TnyAccountStore *account_store,
1175 TnyAccount *account,
1178 ModestFolderViewPrivate *priv;
1179 GtkTreeModel *sort_model, *filter_model;
1181 /* Ignore transport account insertions, we're not showing them
1182 in the folder view */
1183 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1186 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1188 /* If we're adding a new account, and there is no previous
1189 one, we need to select the visible server account */
1190 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1191 !priv->visible_account_id)
1192 modest_widget_memory_restore (modest_runtime_get_conf(),
1193 G_OBJECT (user_data),
1194 MODEST_CONF_FOLDER_VIEW_KEY);
1196 /* Get the inner model */
1197 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1198 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1200 /* Insert the account in the model */
1201 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1202 G_OBJECT (account));
1205 /* When the store account gets online refresh it */
1206 g_signal_connect (account, "connection_status_changed",
1207 G_CALLBACK (on_connection_status_changed),
1208 MODEST_FOLDER_VIEW (user_data));
1210 /* Refilter the model */
1211 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1216 on_account_changed (TnyAccountStore *account_store,
1217 TnyAccount *tny_account,
1221 ModestFolderViewPrivate *priv;
1222 GtkTreeModel *sort_model, *filter_model;
1224 /* Ignore transport account insertions, we're not showing them
1225 in the folder view */
1226 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1229 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1231 /* Get the inner model */
1232 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1233 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1235 /* Remove the account from the model */
1236 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1237 G_OBJECT (tny_account));
1239 /* Insert the account in the model */
1240 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1241 G_OBJECT (tny_account));
1243 /* Refilter the model */
1244 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1250 on_account_removed (TnyAccountStore *account_store,
1251 TnyAccount *account,
1254 ModestFolderView *self = NULL;
1255 ModestFolderViewPrivate *priv;
1256 GtkTreeModel *sort_model, *filter_model;
1257 GtkTreeSelection *sel = NULL;
1259 /* Ignore transport account removals, we're not showing them
1260 in the folder view */
1261 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1264 self = MODEST_FOLDER_VIEW (user_data);
1265 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1267 /* Invalidate the cur_folder_store only if the selected folder
1268 belongs to the account that is being removed */
1269 if (priv->cur_folder_store) {
1270 TnyAccount *selected_folder_account = NULL;
1272 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1273 selected_folder_account =
1274 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1276 selected_folder_account =
1277 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1280 if (selected_folder_account == account) {
1281 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1282 gtk_tree_selection_unselect_all (sel);
1284 g_object_unref (selected_folder_account);
1287 /* Invalidate row to select only if the folder to select
1288 belongs to the account that is being removed*/
1289 if (priv->folder_to_select) {
1290 TnyAccount *folder_to_select_account = NULL;
1292 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1293 if (folder_to_select_account == account) {
1294 /* modest_folder_view_disable_next_folder_selection (self); */
1295 g_object_unref (priv->folder_to_select);
1296 priv->folder_to_select = NULL;
1298 g_object_unref (folder_to_select_account);
1301 /* Remove the account from the model */
1302 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1303 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1304 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1305 G_OBJECT (account));
1307 /* If the removed account is the currently viewed one then
1308 clear the configuration value. The new visible account will be the default account */
1309 if (priv->visible_account_id &&
1310 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1312 /* Clear the current visible account_id */
1313 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1315 /* Call the restore method, this will set the new visible account */
1316 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1317 MODEST_CONF_FOLDER_VIEW_KEY);
1320 /* Refilter the model */
1321 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1323 /* Select the first INBOX if the currently selected folder
1324 belongs to the account that is being deleted */
1325 if (priv->cur_folder_store) {
1326 TnyAccount *folder_selected_account;
1328 folder_selected_account = (TNY_IS_FOLDER (priv->cur_folder_store)) ?
1329 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store)) :
1330 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1331 if (account == folder_selected_account)
1332 modest_folder_view_select_first_inbox_or_local (self);
1333 g_object_unref (folder_selected_account);
1338 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1340 GtkTreeViewColumn *col;
1342 g_return_if_fail (self);
1344 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1346 g_printerr ("modest: failed get column for title\n");
1350 gtk_tree_view_column_set_title (col, title);
1351 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1356 modest_folder_view_on_map (ModestFolderView *self,
1357 GdkEventExpose *event,
1360 ModestFolderViewPrivate *priv;
1362 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1364 /* This won't happen often */
1365 if (G_UNLIKELY (priv->reselect)) {
1366 /* Select the first inbox or the local account if not found */
1368 /* TODO: this could cause a lock at startup, so we
1369 comment it for the moment. We know that this will
1370 be a bug, because the INBOX is not selected, but we
1371 need to rewrite some parts of Modest to avoid the
1372 deathlock situation */
1373 /* TODO: check if this is still the case */
1374 priv->reselect = FALSE;
1375 modest_folder_view_select_first_inbox_or_local (self);
1376 /* Notify the display name observers */
1377 g_signal_emit (G_OBJECT(self),
1378 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1382 if (priv->reexpand) {
1383 expand_root_items (self);
1384 priv->reexpand = FALSE;
1391 modest_folder_view_new (TnyFolderStoreQuery *query)
1394 ModestFolderViewPrivate *priv;
1395 GtkTreeSelection *sel;
1397 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1398 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1401 priv->query = g_object_ref (query);
1403 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1404 priv->changed_signal = g_signal_connect (sel, "changed",
1405 G_CALLBACK (on_selection_changed), self);
1407 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1409 return GTK_WIDGET(self);
1412 /* this feels dirty; any other way to expand all the root items? */
1414 expand_root_items (ModestFolderView *self)
1417 GtkTreeModel *model;
1420 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1421 path = gtk_tree_path_new_first ();
1423 /* all folders should have child items, so.. */
1425 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1426 gtk_tree_path_next (path);
1427 } while (gtk_tree_model_get_iter (model, &iter, path));
1429 gtk_tree_path_free (path);
1433 * We use this function to implement the
1434 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1435 * account in this case, and the local folders.
1438 filter_row (GtkTreeModel *model,
1442 ModestFolderViewPrivate *priv;
1443 gboolean retval = TRUE;
1444 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1445 GObject *instance = NULL;
1446 const gchar *id = NULL;
1448 gboolean found = FALSE;
1449 gboolean cleared = FALSE;
1451 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1452 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1454 gtk_tree_model_get (model, iter,
1455 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1456 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1459 /* Do not show if there is no instance, this could indeed
1460 happen when the model is being modified while it's being
1461 drawn. This could occur for example when moving folders
1466 if (type == TNY_FOLDER_TYPE_ROOT) {
1467 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1468 account instead of a folder. */
1469 if (TNY_IS_ACCOUNT (instance)) {
1470 TnyAccount *acc = TNY_ACCOUNT (instance);
1471 const gchar *account_id = tny_account_get_id (acc);
1473 /* If it isn't a special folder,
1474 * don't show it unless it is the visible account: */
1475 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1476 !modest_tny_account_is_virtual_local_folders (acc) &&
1477 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1479 /* Show only the visible account id */
1480 if (priv->visible_account_id) {
1481 if (strcmp (account_id, priv->visible_account_id))
1488 /* Never show these to the user. They are merged into one folder
1489 * in the local-folders account instead: */
1490 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1495 /* Check hiding (if necessary) */
1496 cleared = modest_email_clipboard_cleared (priv->clipboard);
1497 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1498 id = tny_folder_get_id (TNY_FOLDER(instance));
1499 if (priv->hidding_ids != NULL)
1500 for (i=0; i < priv->n_selected && !found; i++)
1501 if (priv->hidding_ids[i] != NULL && id != NULL)
1502 found = (!strcmp (priv->hidding_ids[i], id));
1508 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1509 folder as no message can be move there according to UI specs */
1510 if (!priv->show_non_move) {
1512 case TNY_FOLDER_TYPE_OUTBOX:
1513 case TNY_FOLDER_TYPE_SENT:
1514 case TNY_FOLDER_TYPE_DRAFTS:
1517 case TNY_FOLDER_TYPE_UNKNOWN:
1518 case TNY_FOLDER_TYPE_NORMAL:
1519 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1520 if (type == TNY_FOLDER_TYPE_INVALID)
1521 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1523 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1524 type == TNY_FOLDER_TYPE_SENT
1525 || type == TNY_FOLDER_TYPE_DRAFTS)
1534 g_object_unref (instance);
1541 modest_folder_view_update_model (ModestFolderView *self,
1542 TnyAccountStore *account_store)
1544 ModestFolderViewPrivate *priv;
1545 GtkTreeModel *model /* , *old_model */;
1546 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1548 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1549 g_return_val_if_fail (account_store, FALSE);
1551 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1553 /* Notify that there is no folder selected */
1554 g_signal_emit (G_OBJECT(self),
1555 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1557 if (priv->cur_folder_store) {
1558 g_object_unref (priv->cur_folder_store);
1559 priv->cur_folder_store = NULL;
1562 /* FIXME: the local accounts are not shown when the query
1563 selects only the subscribed folders */
1564 model = tny_gtk_folder_store_tree_model_new (NULL);
1566 /* Get the accounts: */
1567 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1569 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1571 sortable = gtk_tree_model_sort_new_with_model (model);
1572 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1573 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1574 GTK_SORT_ASCENDING);
1575 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1576 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1577 cmp_rows, NULL, NULL);
1579 /* Create filter model */
1580 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1581 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1587 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1588 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1589 (GCallback) on_row_inserted_maybe_select_folder, self);
1592 g_object_unref (model);
1593 g_object_unref (filter_model);
1594 g_object_unref (sortable);
1596 /* Force a reselection of the INBOX next time the widget is shown */
1597 priv->reselect = TRUE;
1604 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1606 GtkTreeModel *model = NULL;
1607 TnyFolderStore *folder = NULL;
1609 ModestFolderView *tree_view = NULL;
1610 ModestFolderViewPrivate *priv = NULL;
1611 gboolean selected = FALSE;
1613 g_return_if_fail (sel);
1614 g_return_if_fail (user_data);
1616 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1618 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1620 /* Notify the display name observers */
1621 g_signal_emit (G_OBJECT(user_data),
1622 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1625 tree_view = MODEST_FOLDER_VIEW (user_data);
1628 gtk_tree_model_get (model, &iter,
1629 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1632 /* If the folder is the same do not notify */
1633 if (folder && priv->cur_folder_store == folder) {
1634 g_object_unref (folder);
1639 /* Current folder was unselected */
1640 if (priv->cur_folder_store) {
1641 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1642 priv->cur_folder_store, FALSE);
1644 if (TNY_IS_FOLDER(priv->cur_folder_store))
1645 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1646 FALSE, NULL, NULL, NULL);
1648 /* FALSE --> don't expunge the messages */
1650 g_object_unref (priv->cur_folder_store);
1651 priv->cur_folder_store = NULL;
1654 /* New current references */
1655 priv->cur_folder_store = folder;
1657 /* New folder has been selected */
1658 g_signal_emit (G_OBJECT(tree_view),
1659 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1660 0, priv->cur_folder_store, TRUE);
1664 modest_folder_view_get_selected (ModestFolderView *self)
1666 ModestFolderViewPrivate *priv;
1668 g_return_val_if_fail (self, NULL);
1670 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1671 if (priv->cur_folder_store)
1672 g_object_ref (priv->cur_folder_store);
1674 return priv->cur_folder_store;
1678 get_cmp_rows_type_pos (GObject *folder)
1680 /* Remote accounts -> Local account -> MMC account .*/
1683 if (TNY_IS_ACCOUNT (folder) &&
1684 modest_tny_account_is_virtual_local_folders (
1685 TNY_ACCOUNT (folder))) {
1687 } else if (TNY_IS_ACCOUNT (folder)) {
1688 TnyAccount *account = TNY_ACCOUNT (folder);
1689 const gchar *account_id = tny_account_get_id (account);
1690 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1696 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1697 return -1; /* Should never happen */
1702 get_cmp_subfolder_type_pos (TnyFolderType t)
1704 /* Inbox, Outbox, Drafts, Sent, User */
1708 case TNY_FOLDER_TYPE_INBOX:
1711 case TNY_FOLDER_TYPE_OUTBOX:
1714 case TNY_FOLDER_TYPE_DRAFTS:
1717 case TNY_FOLDER_TYPE_SENT:
1726 * This function orders the mail accounts according to these rules:
1727 * 1st - remote accounts
1728 * 2nd - local account
1732 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1736 gchar *name1 = NULL;
1737 gchar *name2 = NULL;
1738 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1739 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1740 GObject *folder1 = NULL;
1741 GObject *folder2 = NULL;
1743 gtk_tree_model_get (tree_model, iter1,
1744 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1745 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1746 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1748 gtk_tree_model_get (tree_model, iter2,
1749 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1750 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1751 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1754 /* Return if we get no folder. This could happen when folder
1755 operations are happening. The model is updated after the
1756 folder copy/move actually occurs, so there could be
1757 situations where the model to be drawn is not correct */
1758 if (!folder1 || !folder2)
1761 if (type == TNY_FOLDER_TYPE_ROOT) {
1762 /* Compare the types, so that
1763 * Remote accounts -> Local account -> MMC account .*/
1764 const gint pos1 = get_cmp_rows_type_pos (folder1);
1765 const gint pos2 = get_cmp_rows_type_pos (folder2);
1766 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1767 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1770 else if (pos1 > pos2)
1773 /* Compare items of the same type: */
1775 TnyAccount *account1 = NULL;
1776 if (TNY_IS_ACCOUNT (folder1))
1777 account1 = TNY_ACCOUNT (folder1);
1779 TnyAccount *account2 = NULL;
1780 if (TNY_IS_ACCOUNT (folder2))
1781 account2 = TNY_ACCOUNT (folder2);
1783 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1784 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1786 if (!account_id && !account_id2) {
1788 } else if (!account_id) {
1790 } else if (!account_id2) {
1792 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1795 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1799 gint cmp1 = 0, cmp2 = 0;
1800 /* get the parent to know if it's a local folder */
1803 gboolean has_parent;
1804 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1806 GObject *parent_folder;
1807 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1808 gtk_tree_model_get (tree_model, &parent,
1809 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1810 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1812 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1813 TNY_IS_ACCOUNT (parent_folder) &&
1814 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1815 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1816 (TNY_FOLDER (folder1)));
1817 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1818 (TNY_FOLDER (folder2)));
1820 g_object_unref (parent_folder);
1823 /* if they are not local folders */
1825 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1826 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1830 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1832 cmp = (cmp1 - cmp2);
1837 g_object_unref(G_OBJECT(folder1));
1839 g_object_unref(G_OBJECT(folder2));
1847 /*****************************************************************************/
1848 /* DRAG and DROP stuff */
1849 /*****************************************************************************/
1851 * This function fills the #GtkSelectionData with the row and the
1852 * model that has been dragged. It's called when this widget is a
1853 * source for dnd after the event drop happened
1856 on_drag_data_get (GtkWidget *widget,
1857 GdkDragContext *context,
1858 GtkSelectionData *selection_data,
1863 GtkTreeSelection *selection;
1864 GtkTreeModel *model;
1866 GtkTreePath *source_row;
1868 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1869 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1871 source_row = gtk_tree_model_get_path (model, &iter);
1872 gtk_tree_set_row_drag_data (selection_data,
1876 gtk_tree_path_free (source_row);
1880 typedef struct _DndHelper {
1881 gboolean delete_source;
1882 GtkTreePath *source_row;
1883 GdkDragContext *context;
1889 * This function is the callback of the
1890 * modest_mail_operation_xfer_msgs () and
1891 * modest_mail_operation_xfer_folder() calls. We check here if the
1892 * message/folder was correctly asynchronously transferred. The reason
1893 * to use the same callback is that the code is the same, it only has
1894 * to check that the operation went fine and then finalize the drag
1898 xfer_cb (ModestMailOperation *mail_op,
1904 helper = (DndHelper *) user_data;
1906 if (modest_mail_operation_get_status (mail_op) ==
1907 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1913 /* Notify the drag source. Never call delete, the monitor will
1914 do the job if needed */
1915 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1917 /* Free the helper */
1918 gtk_tree_path_free (helper->source_row);
1919 g_slice_free (DndHelper, helper);
1922 /* get the folder for the row the treepath refers to. */
1923 /* folder must be unref'd */
1924 static TnyFolderStore *
1925 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1928 TnyFolderStore *folder = NULL;
1930 if (gtk_tree_model_get_iter (model,&iter, path))
1931 gtk_tree_model_get (model, &iter,
1932 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1938 * This function is used by drag_data_received_cb to manage drag and
1939 * drop of a header, i.e, and drag from the header view to the folder
1943 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1944 GtkTreeModel *dest_model,
1945 GtkTreePath *dest_row,
1946 GtkSelectionData *selection_data,
1949 TnyList *headers = NULL;
1950 TnyFolder *folder = NULL;
1951 TnyFolderType folder_type;
1952 ModestMailOperation *mail_op = NULL;
1953 GtkTreeIter source_iter, dest_iter;
1954 ModestWindowMgr *mgr = NULL;
1955 ModestWindow *main_win = NULL;
1956 gchar **uris, **tmp;
1959 /* Build the list of headers */
1960 mgr = modest_runtime_get_window_mgr ();
1961 headers = tny_simple_list_new ();
1962 uris = modest_dnd_selection_data_get_paths (selection_data);
1965 while (*tmp != NULL) {
1970 path = gtk_tree_path_new_from_string (*tmp);
1971 gtk_tree_model_get_iter (source_model, &source_iter, path);
1972 gtk_tree_model_get (source_model, &source_iter,
1973 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1976 /* Do not enable d&d of headers already opened */
1977 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
1978 tny_list_append (headers, G_OBJECT (header));
1980 /* Free and go on */
1981 gtk_tree_path_free (path);
1982 g_object_unref (header);
1987 /* Get the target folder */
1988 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1989 gtk_tree_model_get (dest_model, &dest_iter,
1990 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1993 if (!folder || !TNY_IS_FOLDER(folder)) {
1994 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
1998 folder_type = modest_tny_folder_guess_folder_type (folder);
1999 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2000 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2001 goto cleanup; /* cannot move messages there */
2004 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2005 /* g_warning ("folder not writable"); */
2006 goto cleanup; /* verboten! */
2009 /* Ask for confirmation to move */
2010 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2012 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2016 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2018 if (response == GTK_RESPONSE_CANCEL)
2021 /* Transfer messages */
2022 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2023 modest_ui_actions_move_folder_error_handler,
2026 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2029 modest_mail_operation_xfer_msgs (mail_op,
2032 helper->delete_source,
2037 if (G_IS_OBJECT(mail_op))
2038 g_object_unref (G_OBJECT (mail_op));
2039 if (G_IS_OBJECT(folder))
2040 g_object_unref (G_OBJECT (folder));
2041 if (G_IS_OBJECT(headers))
2042 g_object_unref (headers);
2046 * This function is used by drag_data_received_cb to manage drag and
2047 * drop of a folder, i.e, and drag from the folder view to the same
2051 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2052 GtkTreeModel *dest_model,
2053 GtkTreePath *dest_row,
2054 GtkSelectionData *selection_data,
2057 ModestMailOperation *mail_op = NULL;
2058 GtkTreeIter dest_iter, iter;
2059 TnyFolderStore *dest_folder = NULL;
2060 TnyFolderStore *folder = NULL;
2061 gboolean forbidden = FALSE;
2064 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2066 g_warning ("%s: BUG: no main window", __FUNCTION__);
2071 /* check the folder rules for the destination */
2072 folder = tree_path_to_folder (dest_model, dest_row);
2073 if (TNY_IS_FOLDER(folder)) {
2074 ModestTnyFolderRules rules =
2075 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2076 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2077 } else if (TNY_IS_FOLDER_STORE(folder)) {
2078 /* enable local root as destination for folders */
2079 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
2080 && TNY_IS_ACCOUNT (folder))
2083 g_object_unref (folder);
2086 /* check the folder rules for the source */
2087 folder = tree_path_to_folder (source_model, helper->source_row);
2088 if (TNY_IS_FOLDER(folder)) {
2089 ModestTnyFolderRules rules =
2090 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2091 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2094 g_object_unref (folder);
2098 /* Check if the drag is possible */
2099 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2100 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2101 gtk_tree_path_free (helper->source_row);
2102 g_slice_free (DndHelper, helper);
2107 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2108 gtk_tree_model_get (dest_model, &dest_iter,
2109 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2111 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2112 gtk_tree_model_get (source_model, &iter,
2113 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2116 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
2117 if (modest_platform_connect_and_wait_if_network_folderstore (NULL, dest_folder) &&
2118 modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
2120 /* Do the mail operation */
2121 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) win,
2122 modest_ui_actions_move_folder_error_handler,
2125 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2128 modest_mail_operation_xfer_folder (mail_op,
2129 TNY_FOLDER (folder),
2131 helper->delete_source,
2135 g_object_unref (G_OBJECT (mail_op));
2139 g_object_unref (G_OBJECT (dest_folder));
2140 g_object_unref (G_OBJECT (folder));
2144 * This function receives the data set by the "drag-data-get" signal
2145 * handler. This information comes within the #GtkSelectionData. This
2146 * function will manage both the drags of folders of the treeview and
2147 * drags of headers of the header view widget.
2150 on_drag_data_received (GtkWidget *widget,
2151 GdkDragContext *context,
2154 GtkSelectionData *selection_data,
2159 GtkWidget *source_widget;
2160 GtkTreeModel *dest_model, *source_model;
2161 GtkTreePath *source_row, *dest_row;
2162 GtkTreeViewDropPosition pos;
2163 gboolean success = FALSE, delete_source = FALSE;
2164 DndHelper *helper = NULL;
2166 /* Do not allow further process */
2167 g_signal_stop_emission_by_name (widget, "drag-data-received");
2168 source_widget = gtk_drag_get_source_widget (context);
2170 /* Get the action */
2171 if (context->action == GDK_ACTION_MOVE) {
2172 delete_source = TRUE;
2174 /* Notify that there is no folder selected. We need to
2175 do this in order to update the headers view (and
2176 its monitors, because when moving, the old folder
2177 won't longer exist. We can not wait for the end of
2178 the operation, because the operation won't start if
2179 the folder is in use */
2180 if (source_widget == widget) {
2181 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2182 gtk_tree_selection_unselect_all (sel);
2186 /* Check if the get_data failed */
2187 if (selection_data == NULL || selection_data->length < 0)
2188 gtk_drag_finish (context, success, FALSE, time);
2190 /* Select the destination model */
2191 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2193 /* Get the path to the destination row. Can not call
2194 gtk_tree_view_get_drag_dest_row() because the source row
2195 is not selected anymore */
2196 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2199 /* Only allow drops IN other rows */
2201 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2202 pos == GTK_TREE_VIEW_DROP_AFTER)
2203 gtk_drag_finish (context, success, FALSE, time);
2205 /* Create the helper */
2206 helper = g_slice_new0 (DndHelper);
2207 helper->delete_source = delete_source;
2208 helper->context = context;
2209 helper->time = time;
2211 /* Drags from the header view */
2212 if (source_widget != widget) {
2213 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2215 drag_and_drop_from_header_view (source_model,
2221 /* Get the source model and row */
2222 gtk_tree_get_row_drag_data (selection_data,
2225 helper->source_row = gtk_tree_path_copy (source_row);
2227 drag_and_drop_from_folder_view (source_model,
2233 gtk_tree_path_free (source_row);
2237 gtk_tree_path_free (dest_row);
2241 * We define a "drag-drop" signal handler because we do not want to
2242 * use the default one, because the default one always calls
2243 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2244 * signal handler, because there we have all the information available
2245 * to know if the dnd was a success or not.
2248 drag_drop_cb (GtkWidget *widget,
2249 GdkDragContext *context,
2257 if (!context->targets)
2260 /* Check if we're dragging a folder row */
2261 target = gtk_drag_dest_find_target (widget, context, NULL);
2263 /* Request the data from the source. */
2264 gtk_drag_get_data(widget, context, target, time);
2270 * This function expands a node of a tree view if it's not expanded
2271 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2272 * does that, so that's why they're here.
2275 expand_row_timeout (gpointer data)
2277 GtkTreeView *tree_view = data;
2278 GtkTreePath *dest_path = NULL;
2279 GtkTreeViewDropPosition pos;
2280 gboolean result = FALSE;
2282 GDK_THREADS_ENTER ();
2284 gtk_tree_view_get_drag_dest_row (tree_view,
2289 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2290 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2291 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2292 gtk_tree_path_free (dest_path);
2296 gtk_tree_path_free (dest_path);
2301 GDK_THREADS_LEAVE ();
2307 * This function is called whenever the pointer is moved over a widget
2308 * while dragging some data. It installs a timeout that will expand a
2309 * node of the treeview if not expanded yet. This function also calls
2310 * gdk_drag_status in order to set the suggested action that will be
2311 * used by the "drag-data-received" signal handler to know if we
2312 * should do a move or just a copy of the data.
2315 on_drag_motion (GtkWidget *widget,
2316 GdkDragContext *context,
2322 GtkTreeViewDropPosition pos;
2323 GtkTreePath *dest_row;
2324 GtkTreeModel *dest_model;
2325 ModestFolderViewPrivate *priv;
2326 GdkDragAction suggested_action;
2327 gboolean valid_location = FALSE;
2328 TnyFolderStore *folder;
2330 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2332 if (priv->timer_expander != 0) {
2333 g_source_remove (priv->timer_expander);
2334 priv->timer_expander = 0;
2337 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2342 /* Do not allow drops between folders */
2344 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2345 pos == GTK_TREE_VIEW_DROP_AFTER) {
2346 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2347 gdk_drag_status(context, 0, time);
2348 valid_location = FALSE;
2351 valid_location = TRUE;
2354 /* Check that the destination folder is writable */
2355 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2356 folder = tree_path_to_folder (dest_model, dest_row);
2357 if (folder && TNY_IS_FOLDER (folder)) {
2358 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2360 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2361 valid_location = FALSE;
2365 g_object_unref (folder);
2367 /* Expand the selected row after 1/2 second */
2368 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2369 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2370 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2373 /* Select the desired action. By default we pick MOVE */
2374 suggested_action = GDK_ACTION_MOVE;
2376 if (context->actions == GDK_ACTION_COPY)
2377 gdk_drag_status(context, GDK_ACTION_COPY, time);
2378 else if (context->actions == GDK_ACTION_MOVE)
2379 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2380 else if (context->actions & suggested_action)
2381 gdk_drag_status(context, suggested_action, time);
2383 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2387 gtk_tree_path_free (dest_row);
2388 g_signal_stop_emission_by_name (widget, "drag-motion");
2390 return valid_location;
2394 * This function sets the treeview as a source and a target for dnd
2395 * events. It also connects all the requirede signals.
2398 setup_drag_and_drop (GtkTreeView *self)
2400 /* Set up the folder view as a dnd destination. Set only the
2401 highlight flag, otherwise gtk will have a different
2403 gtk_drag_dest_set (GTK_WIDGET (self),
2404 GTK_DEST_DEFAULT_HIGHLIGHT,
2405 folder_view_drag_types,
2406 G_N_ELEMENTS (folder_view_drag_types),
2407 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2409 g_signal_connect (G_OBJECT (self),
2410 "drag_data_received",
2411 G_CALLBACK (on_drag_data_received),
2415 /* Set up the treeview as a dnd source */
2416 gtk_drag_source_set (GTK_WIDGET (self),
2418 folder_view_drag_types,
2419 G_N_ELEMENTS (folder_view_drag_types),
2420 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2422 g_signal_connect (G_OBJECT (self),
2424 G_CALLBACK (on_drag_motion),
2427 g_signal_connect (G_OBJECT (self),
2429 G_CALLBACK (on_drag_data_get),
2432 g_signal_connect (G_OBJECT (self),
2434 G_CALLBACK (drag_drop_cb),
2439 * This function manages the navigation through the folders using the
2440 * keyboard or the hardware keys in the device
2443 on_key_pressed (GtkWidget *self,
2447 GtkTreeSelection *selection;
2449 GtkTreeModel *model;
2450 gboolean retval = FALSE;
2452 /* Up and Down are automatically managed by the treeview */
2453 if (event->keyval == GDK_Return) {
2454 /* Expand/Collapse the selected row */
2455 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2456 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2459 path = gtk_tree_model_get_path (model, &iter);
2461 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2462 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2464 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2465 gtk_tree_path_free (path);
2467 /* No further processing */
2475 * We listen to the changes in the local folder account name key,
2476 * because we want to show the right name in the view. The local
2477 * folder account name corresponds to the device name in the Maemo
2478 * version. We do this because we do not want to query gconf on each
2479 * tree view refresh. It's better to cache it and change whenever
2483 on_configuration_key_changed (ModestConf* conf,
2485 ModestConfEvent event,
2486 ModestConfNotificationId id,
2487 ModestFolderView *self)
2489 ModestFolderViewPrivate *priv;
2492 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2493 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2495 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2496 g_free (priv->local_account_name);
2498 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2499 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2501 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2502 MODEST_CONF_DEVICE_NAME, NULL);
2504 /* Force a redraw */
2505 #if GTK_CHECK_VERSION(2, 8, 0)
2506 GtkTreeViewColumn * tree_column;
2508 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2509 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2510 gtk_tree_view_column_queue_resize (tree_column);
2512 gtk_widget_queue_draw (GTK_WIDGET (self));
2518 modest_folder_view_set_style (ModestFolderView *self,
2519 ModestFolderViewStyle style)
2521 ModestFolderViewPrivate *priv;
2523 g_return_if_fail (self);
2525 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2527 priv->style = style;
2531 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2532 const gchar *account_id)
2534 ModestFolderViewPrivate *priv;
2535 GtkTreeModel *model;
2537 g_return_if_fail (self);
2539 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2541 /* This will be used by the filter_row callback,
2542 * to decided which rows to show: */
2543 if (priv->visible_account_id) {
2544 g_free (priv->visible_account_id);
2545 priv->visible_account_id = NULL;
2548 priv->visible_account_id = g_strdup (account_id);
2551 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2552 if (GTK_IS_TREE_MODEL_FILTER (model))
2553 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2555 /* Save settings to gconf */
2556 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2557 MODEST_CONF_FOLDER_VIEW_KEY);
2561 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2563 ModestFolderViewPrivate *priv;
2565 g_return_val_if_fail (self, NULL);
2567 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2569 return (const gchar *) priv->visible_account_id;
2573 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2577 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2579 gtk_tree_model_get (model, iter,
2580 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2583 gboolean result = FALSE;
2584 if (type == TNY_FOLDER_TYPE_INBOX) {
2588 *inbox_iter = *iter;
2592 if (gtk_tree_model_iter_children (model, &child, iter)) {
2593 if (find_inbox_iter (model, &child, inbox_iter))
2597 } while (gtk_tree_model_iter_next (model, iter));
2606 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2608 GtkTreeModel *model;
2609 GtkTreeIter iter, inbox_iter;
2610 GtkTreeSelection *sel;
2611 GtkTreePath *path = NULL;
2613 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2617 expand_root_items (self);
2618 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2620 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2621 g_warning ("%s: model is empty", __FUNCTION__);
2625 if (find_inbox_iter (model, &iter, &inbox_iter))
2626 path = gtk_tree_model_get_path (model, &inbox_iter);
2628 path = gtk_tree_path_new_first ();
2630 /* Select the row and free */
2631 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2632 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2633 gtk_tree_path_free (path);
2636 gtk_widget_grab_focus (GTK_WIDGET(self));
2642 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2647 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2648 TnyFolder* a_folder;
2651 gtk_tree_model_get (model, iter,
2652 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2653 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2654 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2658 if (folder == a_folder) {
2659 g_object_unref (a_folder);
2660 *folder_iter = *iter;
2663 g_object_unref (a_folder);
2665 if (gtk_tree_model_iter_children (model, &child, iter)) {
2666 if (find_folder_iter (model, &child, folder_iter, folder))
2670 } while (gtk_tree_model_iter_next (model, iter));
2677 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2678 ModestFolderView *self)
2680 ModestFolderViewPrivate *priv = NULL;
2681 GtkTreeSelection *sel;
2682 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2683 GObject *instance = NULL;
2685 if (!MODEST_IS_FOLDER_VIEW(self))
2688 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2690 priv->reexpand = TRUE;
2692 gtk_tree_model_get (tree_model, iter,
2693 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2694 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2696 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2697 priv->folder_to_select = g_object_ref (instance);
2699 g_object_unref (instance);
2702 if (priv->folder_to_select) {
2704 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2707 path = gtk_tree_model_get_path (tree_model, iter);
2708 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2710 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2712 gtk_tree_selection_select_iter (sel, iter);
2713 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2715 gtk_tree_path_free (path);
2720 modest_folder_view_disable_next_folder_selection (self);
2721 /* g_object_unref (priv->folder_to_select); */
2722 /* priv->folder_to_select = NULL; */
2728 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2730 ModestFolderViewPrivate *priv = NULL;
2732 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2733 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2735 if (priv->folder_to_select)
2736 g_object_unref(priv->folder_to_select);
2738 priv->folder_to_select = NULL;
2742 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2743 gboolean after_change)
2745 GtkTreeModel *model;
2746 GtkTreeIter iter, folder_iter;
2747 GtkTreeSelection *sel;
2748 ModestFolderViewPrivate *priv = NULL;
2750 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2751 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2753 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2757 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2758 gtk_tree_selection_unselect_all (sel);
2760 if (priv->folder_to_select)
2761 g_object_unref(priv->folder_to_select);
2762 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2766 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2770 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2771 g_warning ("%s: model is empty", __FUNCTION__);
2775 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2778 path = gtk_tree_model_get_path (model, &folder_iter);
2779 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2781 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2782 gtk_tree_selection_select_iter (sel, &folder_iter);
2783 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2785 gtk_tree_path_free (path);
2793 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2795 /* Copy selection */
2796 _clipboard_set_selected_data (folder_view, FALSE);
2800 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2802 ModestFolderViewPrivate *priv = NULL;
2803 GtkTreeModel *model = NULL;
2804 const gchar **hidding = NULL;
2805 guint i, n_selected;
2807 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2808 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2810 /* Copy selection */
2811 if (!_clipboard_set_selected_data (folder_view, TRUE))
2814 /* Get hidding ids */
2815 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2817 /* Clear hidding array created by previous cut operation */
2818 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2820 /* Copy hidding array */
2821 priv->n_selected = n_selected;
2822 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2823 for (i=0; i < n_selected; i++)
2824 priv->hidding_ids[i] = g_strdup(hidding[i]);
2826 /* Hide cut folders */
2827 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2828 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2832 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2833 ModestFolderView *folder_view_dst)
2835 GtkTreeModel *filter_model = NULL;
2836 GtkTreeModel *model = NULL;
2837 GtkTreeModel *new_filter_model = NULL;
2839 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2840 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2843 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2844 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2846 /* Build new filter model */
2847 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2848 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2852 /* Set copied model */
2853 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2854 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
2855 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
2858 g_object_unref (new_filter_model);
2862 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2865 GtkTreeModel *model = NULL;
2866 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2867 priv->show_non_move = show;
2868 /* modest_folder_view_update_model(folder_view, */
2869 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2871 /* Hide special folders */
2872 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2873 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2874 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2878 /* Returns FALSE if it did not selected anything */
2880 _clipboard_set_selected_data (ModestFolderView *folder_view,
2883 ModestFolderViewPrivate *priv = NULL;
2884 TnyFolderStore *folder = NULL;
2885 gboolean retval = FALSE;
2887 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2888 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2890 /* Set selected data on clipboard */
2891 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2892 folder = modest_folder_view_get_selected (folder_view);
2894 /* Do not allow to select an account */
2895 if (TNY_IS_FOLDER (folder)) {
2896 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2901 g_object_unref (folder);
2907 _clear_hidding_filter (ModestFolderView *folder_view)
2909 ModestFolderViewPrivate *priv;
2912 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2913 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2915 if (priv->hidding_ids != NULL) {
2916 for (i=0; i < priv->n_selected; i++)
2917 g_free (priv->hidding_ids[i]);
2918 g_free(priv->hidding_ids);
2924 on_display_name_changed (ModestAccountMgr *mgr,
2925 const gchar *account,
2928 ModestFolderView *self;
2930 self = MODEST_FOLDER_VIEW (user_data);
2932 /* Force a redraw */
2933 #if GTK_CHECK_VERSION(2, 8, 0)
2934 GtkTreeViewColumn * tree_column;
2936 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2937 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2938 gtk_tree_view_column_queue_resize (tree_column);
2940 gtk_widget_queue_draw (GTK_WIDGET (self));