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 (!sent_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 (!trash_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 (!draft_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 (!normal_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,
907 GtkCellRenderer *renderer,
908 GtkTreeModel *tree_model,
912 GObject *rendobj = NULL, *instance = NULL;
913 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
914 gboolean has_children;
915 ThreePixbufs *pixbufs;
917 rendobj = (GObject *) renderer;
919 gtk_tree_model_get (tree_model, iter,
920 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
921 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
927 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
928 pixbufs = get_folder_icons (type, instance);
929 g_object_unref (instance);
932 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
935 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
936 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
939 free_pixbufs (pixbufs);
945 add_columns (GtkWidget *treeview)
947 GtkTreeViewColumn *column;
948 GtkCellRenderer *renderer;
949 GtkTreeSelection *sel;
952 column = gtk_tree_view_column_new ();
954 /* Set icon and text render function */
955 renderer = gtk_cell_renderer_pixbuf_new();
956 gtk_tree_view_column_pack_start (column, renderer, FALSE);
957 gtk_tree_view_column_set_cell_data_func(column, renderer,
958 icon_cell_data, treeview, NULL);
960 renderer = gtk_cell_renderer_text_new();
961 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END,
962 "ellipsize-set", TRUE, NULL);
963 gtk_tree_view_column_pack_start (column, renderer, TRUE);
964 gtk_tree_view_column_set_cell_data_func(column, renderer,
965 text_cell_data, treeview, NULL);
967 /* Set selection mode */
968 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
969 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
971 /* Set treeview appearance */
972 gtk_tree_view_column_set_spacing (column, 2);
973 gtk_tree_view_column_set_resizable (column, TRUE);
974 gtk_tree_view_column_set_fixed_width (column, TRUE);
975 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
976 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
979 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
983 modest_folder_view_init (ModestFolderView *obj)
985 ModestFolderViewPrivate *priv;
988 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
990 priv->timer_expander = 0;
991 priv->account_store = NULL;
993 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
994 priv->cur_folder_store = NULL;
995 priv->visible_account_id = NULL;
996 priv->folder_to_select = NULL;
998 priv->reexpand = TRUE;
1000 /* Initialize the local account name */
1001 conf = modest_runtime_get_conf();
1002 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1004 /* Init email clipboard */
1005 priv->clipboard = modest_runtime_get_email_clipboard ();
1006 priv->hidding_ids = NULL;
1007 priv->n_selected = 0;
1008 priv->reselect = FALSE;
1009 priv->show_non_move = TRUE;
1011 /* Build treeview */
1012 add_columns (GTK_WIDGET (obj));
1014 /* Setup drag and drop */
1015 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1017 /* Connect signals */
1018 g_signal_connect (G_OBJECT (obj),
1020 G_CALLBACK (on_key_pressed), NULL);
1022 priv->display_name_changed_signal =
1023 g_signal_connect (modest_runtime_get_account_mgr (),
1024 "display_name_changed",
1025 G_CALLBACK (on_display_name_changed),
1029 * Track changes in the local account name (in the device it
1030 * will be the device name)
1032 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1034 G_CALLBACK(on_configuration_key_changed),
1039 tny_account_store_view_init (gpointer g, gpointer iface_data)
1041 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1043 klass->set_account_store_func = modest_folder_view_set_account_store;
1049 modest_folder_view_finalize (GObject *obj)
1051 ModestFolderViewPrivate *priv;
1052 GtkTreeSelection *sel;
1054 g_return_if_fail (obj);
1056 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1058 if (priv->timer_expander != 0) {
1059 g_source_remove (priv->timer_expander);
1060 priv->timer_expander = 0;
1063 if (priv->account_store) {
1064 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1065 priv->account_inserted_signal);
1066 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1067 priv->account_removed_signal);
1068 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1069 priv->account_changed_signal);
1070 g_object_unref (G_OBJECT(priv->account_store));
1071 priv->account_store = NULL;
1075 g_object_unref (G_OBJECT (priv->query));
1079 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1080 if (priv->folder_to_select) {
1081 g_object_unref (G_OBJECT(priv->folder_to_select));
1082 priv->folder_to_select = NULL;
1085 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1087 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1089 g_free (priv->local_account_name);
1090 g_free (priv->visible_account_id);
1092 if (priv->conf_key_signal) {
1093 g_signal_handler_disconnect (modest_runtime_get_conf (),
1094 priv->conf_key_signal);
1095 priv->conf_key_signal = 0;
1098 if (priv->cur_folder_store) {
1099 if (TNY_IS_FOLDER(priv->cur_folder_store))
1100 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
1101 /* FALSE --> expunge the message */
1103 g_object_unref (priv->cur_folder_store);
1104 priv->cur_folder_store = NULL;
1107 /* Clear hidding array created by cut operation */
1108 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1110 G_OBJECT_CLASS(parent_class)->finalize (obj);
1115 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1117 ModestFolderViewPrivate *priv;
1120 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1121 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1123 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1124 device = tny_account_store_get_device (account_store);
1126 if (G_UNLIKELY (priv->account_store)) {
1128 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1129 priv->account_inserted_signal))
1130 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1131 priv->account_inserted_signal);
1132 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1133 priv->account_removed_signal))
1134 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1135 priv->account_removed_signal);
1136 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1137 priv->account_changed_signal))
1138 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1139 priv->account_changed_signal);
1140 g_object_unref (G_OBJECT (priv->account_store));
1143 priv->account_store = g_object_ref (G_OBJECT (account_store));
1145 priv->account_removed_signal =
1146 g_signal_connect (G_OBJECT(account_store), "account_removed",
1147 G_CALLBACK (on_account_removed), self);
1149 priv->account_inserted_signal =
1150 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1151 G_CALLBACK (on_account_inserted), self);
1153 priv->account_changed_signal =
1154 g_signal_connect (G_OBJECT(account_store), "account_changed",
1155 G_CALLBACK (on_account_changed), self);
1157 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1159 g_object_unref (G_OBJECT (device));
1163 on_connection_status_changed (TnyAccount *self,
1164 TnyConnectionStatus status,
1167 /* If the account becomes online then refresh it */
1168 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1169 const gchar *acc_name;
1170 GtkWidget *my_window;
1172 my_window = gtk_widget_get_ancestor (GTK_WIDGET (user_data), MODEST_TYPE_WINDOW);
1173 acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (self);
1174 modest_ui_actions_do_send_receive (acc_name, MODEST_WINDOW (my_window));
1179 on_account_inserted (TnyAccountStore *account_store,
1180 TnyAccount *account,
1183 ModestFolderViewPrivate *priv;
1184 GtkTreeModel *sort_model, *filter_model;
1186 /* Ignore transport account insertions, we're not showing them
1187 in the folder view */
1188 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1191 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1193 /* If we're adding a new account, and there is no previous
1194 one, we need to select the visible server account */
1195 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1196 !priv->visible_account_id)
1197 modest_widget_memory_restore (modest_runtime_get_conf(),
1198 G_OBJECT (user_data),
1199 MODEST_CONF_FOLDER_VIEW_KEY);
1201 /* Get the inner model */
1202 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1203 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1205 /* Insert the account in the model */
1206 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1207 G_OBJECT (account));
1210 /* When the store account gets online refresh it */
1211 g_signal_connect (account, "connection_status_changed",
1212 G_CALLBACK (on_connection_status_changed),
1213 MODEST_FOLDER_VIEW (user_data));
1215 /* Refilter the model */
1216 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1221 on_account_changed (TnyAccountStore *account_store,
1222 TnyAccount *tny_account,
1226 ModestFolderViewPrivate *priv;
1227 GtkTreeModel *sort_model, *filter_model;
1229 /* Ignore transport account insertions, we're not showing them
1230 in the folder view */
1231 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1234 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1236 /* Get the inner model */
1237 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1238 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1240 /* Remove the account from the model */
1241 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1242 G_OBJECT (tny_account));
1244 /* Insert the account in the model */
1245 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1246 G_OBJECT (tny_account));
1248 /* Refilter the model */
1249 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1255 on_account_removed (TnyAccountStore *account_store,
1256 TnyAccount *account,
1259 ModestFolderView *self = NULL;
1260 ModestFolderViewPrivate *priv;
1261 GtkTreeModel *sort_model, *filter_model;
1262 GtkTreeSelection *sel = NULL;
1264 /* Ignore transport account removals, we're not showing them
1265 in the folder view */
1266 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1269 self = MODEST_FOLDER_VIEW (user_data);
1270 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1272 /* Invalidate the cur_folder_store only if the selected folder
1273 belongs to the account that is being removed */
1274 if (priv->cur_folder_store) {
1275 TnyAccount *selected_folder_account = NULL;
1277 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1278 selected_folder_account =
1279 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1281 selected_folder_account =
1282 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1285 if (selected_folder_account == account) {
1286 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1287 gtk_tree_selection_unselect_all (sel);
1289 g_object_unref (selected_folder_account);
1292 /* Invalidate row to select only if the folder to select
1293 belongs to the account that is being removed*/
1294 if (priv->folder_to_select) {
1295 TnyAccount *folder_to_select_account = NULL;
1297 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1298 if (folder_to_select_account == account) {
1299 /* modest_folder_view_disable_next_folder_selection (self); */
1300 g_object_unref (priv->folder_to_select);
1301 priv->folder_to_select = NULL;
1303 g_object_unref (folder_to_select_account);
1306 /* Remove the account from the model */
1307 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1308 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1309 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1310 G_OBJECT (account));
1312 /* If the removed account is the currently viewed one then
1313 clear the configuration value. The new visible account will be the default account */
1314 if (priv->visible_account_id &&
1315 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1317 /* Clear the current visible account_id */
1318 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1320 /* Call the restore method, this will set the new visible account */
1321 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1322 MODEST_CONF_FOLDER_VIEW_KEY);
1325 /* Refilter the model */
1326 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1328 /* Select the first INBOX if the currently selected folder
1329 belongs to the account that is being deleted */
1330 if (priv->cur_folder_store) {
1331 TnyAccount *folder_selected_account;
1333 folder_selected_account = (TNY_IS_FOLDER (priv->cur_folder_store)) ?
1334 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store)) :
1335 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1336 if (account == folder_selected_account)
1337 modest_folder_view_select_first_inbox_or_local (self);
1338 g_object_unref (folder_selected_account);
1343 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1345 GtkTreeViewColumn *col;
1347 g_return_if_fail (self);
1349 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1351 g_printerr ("modest: failed get column for title\n");
1355 gtk_tree_view_column_set_title (col, title);
1356 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1361 modest_folder_view_on_map (ModestFolderView *self,
1362 GdkEventExpose *event,
1365 ModestFolderViewPrivate *priv;
1367 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1369 /* This won't happen often */
1370 if (G_UNLIKELY (priv->reselect)) {
1371 /* Select the first inbox or the local account if not found */
1373 /* TODO: this could cause a lock at startup, so we
1374 comment it for the moment. We know that this will
1375 be a bug, because the INBOX is not selected, but we
1376 need to rewrite some parts of Modest to avoid the
1377 deathlock situation */
1378 /* TODO: check if this is still the case */
1379 priv->reselect = FALSE;
1380 modest_folder_view_select_first_inbox_or_local (self);
1381 /* Notify the display name observers */
1382 g_signal_emit (G_OBJECT(self),
1383 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1387 if (priv->reexpand) {
1388 expand_root_items (self);
1389 priv->reexpand = FALSE;
1396 modest_folder_view_new (TnyFolderStoreQuery *query)
1399 ModestFolderViewPrivate *priv;
1400 GtkTreeSelection *sel;
1402 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1403 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1406 priv->query = g_object_ref (query);
1408 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1409 priv->changed_signal = g_signal_connect (sel, "changed",
1410 G_CALLBACK (on_selection_changed), self);
1412 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1414 return GTK_WIDGET(self);
1417 /* this feels dirty; any other way to expand all the root items? */
1419 expand_root_items (ModestFolderView *self)
1422 GtkTreeModel *model;
1425 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1426 path = gtk_tree_path_new_first ();
1428 /* all folders should have child items, so.. */
1430 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1431 gtk_tree_path_next (path);
1432 } while (gtk_tree_model_get_iter (model, &iter, path));
1434 gtk_tree_path_free (path);
1438 * We use this function to implement the
1439 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1440 * account in this case, and the local folders.
1443 filter_row (GtkTreeModel *model,
1447 ModestFolderViewPrivate *priv;
1448 gboolean retval = TRUE;
1449 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1450 GObject *instance = NULL;
1451 const gchar *id = NULL;
1453 gboolean found = FALSE;
1454 gboolean cleared = FALSE;
1456 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1457 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1459 gtk_tree_model_get (model, iter,
1460 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1461 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1464 /* Do not show if there is no instance, this could indeed
1465 happen when the model is being modified while it's being
1466 drawn. This could occur for example when moving folders
1471 if (type == TNY_FOLDER_TYPE_ROOT) {
1472 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1473 account instead of a folder. */
1474 if (TNY_IS_ACCOUNT (instance)) {
1475 TnyAccount *acc = TNY_ACCOUNT (instance);
1476 const gchar *account_id = tny_account_get_id (acc);
1478 /* If it isn't a special folder,
1479 * don't show it unless it is the visible account: */
1480 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1481 !modest_tny_account_is_virtual_local_folders (acc) &&
1482 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1484 /* Show only the visible account id */
1485 if (priv->visible_account_id) {
1486 if (strcmp (account_id, priv->visible_account_id))
1493 /* Never show these to the user. They are merged into one folder
1494 * in the local-folders account instead: */
1495 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1500 /* Check hiding (if necessary) */
1501 cleared = modest_email_clipboard_cleared (priv->clipboard);
1502 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1503 id = tny_folder_get_id (TNY_FOLDER(instance));
1504 if (priv->hidding_ids != NULL)
1505 for (i=0; i < priv->n_selected && !found; i++)
1506 if (priv->hidding_ids[i] != NULL && id != NULL)
1507 found = (!strcmp (priv->hidding_ids[i], id));
1513 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1514 folder as no message can be move there according to UI specs */
1515 if (!priv->show_non_move) {
1517 case TNY_FOLDER_TYPE_OUTBOX:
1518 case TNY_FOLDER_TYPE_SENT:
1519 case TNY_FOLDER_TYPE_DRAFTS:
1522 case TNY_FOLDER_TYPE_UNKNOWN:
1523 case TNY_FOLDER_TYPE_NORMAL:
1524 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1525 if (type == TNY_FOLDER_TYPE_INVALID)
1526 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1528 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1529 type == TNY_FOLDER_TYPE_SENT
1530 || type == TNY_FOLDER_TYPE_DRAFTS)
1539 g_object_unref (instance);
1546 modest_folder_view_update_model (ModestFolderView *self,
1547 TnyAccountStore *account_store)
1549 ModestFolderViewPrivate *priv;
1550 GtkTreeModel *model /* , *old_model */;
1551 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1553 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1554 g_return_val_if_fail (account_store, FALSE);
1556 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1558 /* Notify that there is no folder selected */
1559 g_signal_emit (G_OBJECT(self),
1560 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1562 if (priv->cur_folder_store) {
1563 g_object_unref (priv->cur_folder_store);
1564 priv->cur_folder_store = NULL;
1567 /* FIXME: the local accounts are not shown when the query
1568 selects only the subscribed folders */
1569 model = tny_gtk_folder_store_tree_model_new (NULL);
1571 /* Get the accounts: */
1572 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1574 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1576 sortable = gtk_tree_model_sort_new_with_model (model);
1577 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1578 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1579 GTK_SORT_ASCENDING);
1580 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1581 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1582 cmp_rows, NULL, NULL);
1584 /* Create filter model */
1585 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1586 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1592 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1593 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1594 (GCallback) on_row_inserted_maybe_select_folder, self);
1597 g_object_unref (model);
1598 g_object_unref (filter_model);
1599 g_object_unref (sortable);
1601 /* Force a reselection of the INBOX next time the widget is shown */
1602 priv->reselect = TRUE;
1609 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1611 GtkTreeModel *model = NULL;
1612 TnyFolderStore *folder = NULL;
1614 ModestFolderView *tree_view = NULL;
1615 ModestFolderViewPrivate *priv = NULL;
1616 gboolean selected = FALSE;
1618 g_return_if_fail (sel);
1619 g_return_if_fail (user_data);
1621 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1623 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1625 /* Notify the display name observers */
1626 g_signal_emit (G_OBJECT(user_data),
1627 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1630 tree_view = MODEST_FOLDER_VIEW (user_data);
1633 gtk_tree_model_get (model, &iter,
1634 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1637 /* If the folder is the same do not notify */
1638 if (folder && priv->cur_folder_store == folder) {
1639 g_object_unref (folder);
1644 /* Current folder was unselected */
1645 if (priv->cur_folder_store) {
1646 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1647 priv->cur_folder_store, FALSE);
1649 if (TNY_IS_FOLDER(priv->cur_folder_store))
1650 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1651 FALSE, NULL, NULL, NULL);
1653 /* FALSE --> don't expunge the messages */
1655 g_object_unref (priv->cur_folder_store);
1656 priv->cur_folder_store = NULL;
1659 /* New current references */
1660 priv->cur_folder_store = folder;
1662 /* New folder has been selected */
1663 g_signal_emit (G_OBJECT(tree_view),
1664 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1665 0, priv->cur_folder_store, TRUE);
1669 modest_folder_view_get_selected (ModestFolderView *self)
1671 ModestFolderViewPrivate *priv;
1673 g_return_val_if_fail (self, NULL);
1675 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1676 if (priv->cur_folder_store)
1677 g_object_ref (priv->cur_folder_store);
1679 return priv->cur_folder_store;
1683 get_cmp_rows_type_pos (GObject *folder)
1685 /* Remote accounts -> Local account -> MMC account .*/
1688 if (TNY_IS_ACCOUNT (folder) &&
1689 modest_tny_account_is_virtual_local_folders (
1690 TNY_ACCOUNT (folder))) {
1692 } else if (TNY_IS_ACCOUNT (folder)) {
1693 TnyAccount *account = TNY_ACCOUNT (folder);
1694 const gchar *account_id = tny_account_get_id (account);
1695 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1701 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1702 return -1; /* Should never happen */
1707 get_cmp_subfolder_type_pos (TnyFolderType t)
1709 /* Inbox, Outbox, Drafts, Sent, User */
1713 case TNY_FOLDER_TYPE_INBOX:
1716 case TNY_FOLDER_TYPE_OUTBOX:
1719 case TNY_FOLDER_TYPE_DRAFTS:
1722 case TNY_FOLDER_TYPE_SENT:
1731 * This function orders the mail accounts according to these rules:
1732 * 1st - remote accounts
1733 * 2nd - local account
1737 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1741 gchar *name1 = NULL;
1742 gchar *name2 = NULL;
1743 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1744 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1745 GObject *folder1 = NULL;
1746 GObject *folder2 = NULL;
1748 gtk_tree_model_get (tree_model, iter1,
1749 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1750 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1751 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1753 gtk_tree_model_get (tree_model, iter2,
1754 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1755 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1756 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1759 /* Return if we get no folder. This could happen when folder
1760 operations are happening. The model is updated after the
1761 folder copy/move actually occurs, so there could be
1762 situations where the model to be drawn is not correct */
1763 if (!folder1 || !folder2)
1766 if (type == TNY_FOLDER_TYPE_ROOT) {
1767 /* Compare the types, so that
1768 * Remote accounts -> Local account -> MMC account .*/
1769 const gint pos1 = get_cmp_rows_type_pos (folder1);
1770 const gint pos2 = get_cmp_rows_type_pos (folder2);
1771 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1772 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1775 else if (pos1 > pos2)
1778 /* Compare items of the same type: */
1780 TnyAccount *account1 = NULL;
1781 if (TNY_IS_ACCOUNT (folder1))
1782 account1 = TNY_ACCOUNT (folder1);
1784 TnyAccount *account2 = NULL;
1785 if (TNY_IS_ACCOUNT (folder2))
1786 account2 = TNY_ACCOUNT (folder2);
1788 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1789 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1791 if (!account_id && !account_id2) {
1793 } else if (!account_id) {
1795 } else if (!account_id2) {
1797 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1800 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1804 gint cmp1 = 0, cmp2 = 0;
1805 /* get the parent to know if it's a local folder */
1808 gboolean has_parent;
1809 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1811 GObject *parent_folder;
1812 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1813 gtk_tree_model_get (tree_model, &parent,
1814 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1815 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1817 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1818 TNY_IS_ACCOUNT (parent_folder) &&
1819 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1820 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1821 (TNY_FOLDER (folder1)));
1822 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1823 (TNY_FOLDER (folder2)));
1825 g_object_unref (parent_folder);
1828 /* if they are not local folders */
1830 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1831 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1835 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1837 cmp = (cmp1 - cmp2);
1842 g_object_unref(G_OBJECT(folder1));
1844 g_object_unref(G_OBJECT(folder2));
1852 /*****************************************************************************/
1853 /* DRAG and DROP stuff */
1854 /*****************************************************************************/
1856 * This function fills the #GtkSelectionData with the row and the
1857 * model that has been dragged. It's called when this widget is a
1858 * source for dnd after the event drop happened
1861 on_drag_data_get (GtkWidget *widget,
1862 GdkDragContext *context,
1863 GtkSelectionData *selection_data,
1868 GtkTreeSelection *selection;
1869 GtkTreeModel *model;
1871 GtkTreePath *source_row;
1873 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1874 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1876 source_row = gtk_tree_model_get_path (model, &iter);
1877 gtk_tree_set_row_drag_data (selection_data,
1881 gtk_tree_path_free (source_row);
1885 typedef struct _DndHelper {
1886 gboolean delete_source;
1887 GtkTreePath *source_row;
1888 GdkDragContext *context;
1893 dnd_helper_destroyer (DndHelper *helper)
1895 /* Free the helper */
1896 gtk_tree_path_free (helper->source_row);
1897 g_slice_free (DndHelper, helper);
1901 * This function is the callback of the
1902 * modest_mail_operation_xfer_msgs () and
1903 * modest_mail_operation_xfer_folder() calls. We check here if the
1904 * message/folder was correctly asynchronously transferred. The reason
1905 * to use the same callback is that the code is the same, it only has
1906 * to check that the operation went fine and then finalize the drag
1910 xfer_cb (ModestMailOperation *mail_op,
1916 helper = (DndHelper *) user_data;
1918 if (modest_mail_operation_get_status (mail_op) ==
1919 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1925 /* Notify the drag source. Never call delete, the monitor will
1926 do the job if needed */
1927 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1929 /* Free the helper */
1930 dnd_helper_destroyer (helper);
1933 /* get the folder for the row the treepath refers to. */
1934 /* folder must be unref'd */
1935 static TnyFolderStore *
1936 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1939 TnyFolderStore *folder = NULL;
1941 if (gtk_tree_model_get_iter (model,&iter, path))
1942 gtk_tree_model_get (model, &iter,
1943 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1949 * This function is used by drag_data_received_cb to manage drag and
1950 * drop of a header, i.e, and drag from the header view to the folder
1954 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1955 GtkTreeModel *dest_model,
1956 GtkTreePath *dest_row,
1957 GtkSelectionData *selection_data,
1960 TnyList *headers = NULL;
1961 TnyFolder *folder = NULL;
1962 TnyFolderType folder_type;
1963 ModestMailOperation *mail_op = NULL;
1964 GtkTreeIter source_iter, dest_iter;
1965 ModestWindowMgr *mgr = NULL;
1966 ModestWindow *main_win = NULL;
1967 gchar **uris, **tmp;
1970 /* Build the list of headers */
1971 mgr = modest_runtime_get_window_mgr ();
1972 headers = tny_simple_list_new ();
1973 uris = modest_dnd_selection_data_get_paths (selection_data);
1976 while (*tmp != NULL) {
1981 path = gtk_tree_path_new_from_string (*tmp);
1982 gtk_tree_model_get_iter (source_model, &source_iter, path);
1983 gtk_tree_model_get (source_model, &source_iter,
1984 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1987 /* Do not enable d&d of headers already opened */
1988 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
1989 tny_list_append (headers, G_OBJECT (header));
1991 /* Free and go on */
1992 gtk_tree_path_free (path);
1993 g_object_unref (header);
1998 /* Get the target folder */
1999 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2000 gtk_tree_model_get (dest_model, &dest_iter,
2001 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2004 if (!folder || !TNY_IS_FOLDER(folder)) {
2005 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2009 folder_type = modest_tny_folder_guess_folder_type (folder);
2010 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2011 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2012 goto cleanup; /* cannot move messages there */
2015 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2016 /* g_warning ("folder not writable"); */
2017 goto cleanup; /* verboten! */
2020 /* Ask for confirmation to move */
2021 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2023 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2027 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2029 if (response == GTK_RESPONSE_CANCEL)
2032 /* Transfer messages */
2033 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2034 modest_ui_actions_move_folder_error_handler,
2037 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2040 modest_mail_operation_xfer_msgs (mail_op,
2043 helper->delete_source,
2048 if (G_IS_OBJECT(mail_op))
2049 g_object_unref (G_OBJECT (mail_op));
2050 if (G_IS_OBJECT(folder))
2051 g_object_unref (G_OBJECT (folder));
2052 if (G_IS_OBJECT(headers))
2053 g_object_unref (headers);
2057 TnyFolderStore *src_folder;
2058 TnyFolderStore *dst_folder;
2063 dnd_folder_info_destroyer (DndFolderInfo *info)
2065 if (info->src_folder)
2066 g_object_unref (info->src_folder);
2067 if (info->dst_folder)
2068 g_object_unref (info->dst_folder);
2069 g_slice_free (DndFolderInfo, info);
2073 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2074 GtkWindow *parent_window,
2075 TnyAccount *account)
2077 time_t dnd_time = info->helper->time;
2078 GdkDragContext *context = info->helper->context;
2081 modest_ui_actions_on_account_connection_error (parent_window, account);
2083 /* Free the helper & info */
2084 dnd_helper_destroyer (info->helper);
2085 dnd_folder_info_destroyer (info);
2087 /* Notify the drag source. Never call delete, the monitor will
2088 do the job if needed */
2089 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2094 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2096 GtkWindow *parent_window,
2097 TnyAccount *account,
2100 DndFolderInfo *info = NULL;
2101 ModestMailOperation *mail_op;
2103 info = (DndFolderInfo *) user_data;
2105 if (err || canceled) {
2106 dnd_on_connection_failed_destroyer (info, parent_window, account);
2110 /* Do the mail operation */
2111 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2112 modest_ui_actions_move_folder_error_handler,
2113 info->src_folder, NULL);
2115 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2118 /* Transfer the folder */
2119 modest_mail_operation_xfer_folder (mail_op,
2120 TNY_FOLDER (info->src_folder),
2122 info->helper->delete_source,
2126 g_object_unref (G_OBJECT (mail_op));
2131 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2133 GtkWindow *parent_window,
2134 TnyAccount *account,
2137 DndFolderInfo *info = NULL;
2139 info = (DndFolderInfo *) user_data;
2141 if (err || canceled) {
2142 dnd_on_connection_failed_destroyer (info, parent_window, account);
2146 /* Connect to source folder and perform the copy/move */
2147 modest_platform_connect_and_perform_if_network_folderstore (NULL,
2149 drag_and_drop_from_folder_view_src_folder_performer,
2154 * This function is used by drag_data_received_cb to manage drag and
2155 * drop of a folder, i.e, and drag from the folder view to the same
2159 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2160 GtkTreeModel *dest_model,
2161 GtkTreePath *dest_row,
2162 GtkSelectionData *selection_data,
2165 GtkTreeIter dest_iter, iter;
2166 TnyFolderStore *dest_folder = NULL;
2167 TnyFolderStore *folder = NULL;
2168 gboolean forbidden = FALSE;
2170 DndFolderInfo *info = NULL;
2172 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2174 g_warning ("%s: BUG: no main window", __FUNCTION__);
2179 /* check the folder rules for the destination */
2180 folder = tree_path_to_folder (dest_model, dest_row);
2181 if (TNY_IS_FOLDER(folder)) {
2182 ModestTnyFolderRules rules =
2183 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2184 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2185 } else if (TNY_IS_FOLDER_STORE(folder)) {
2186 /* enable local root as destination for folders */
2187 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
2188 && TNY_IS_ACCOUNT (folder))
2191 g_object_unref (folder);
2194 /* check the folder rules for the source */
2195 folder = tree_path_to_folder (source_model, helper->source_row);
2196 if (TNY_IS_FOLDER(folder)) {
2197 ModestTnyFolderRules rules =
2198 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2199 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2202 g_object_unref (folder);
2206 /* Check if the drag is possible */
2207 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2208 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2209 gtk_tree_path_free (helper->source_row);
2210 g_slice_free (DndHelper, helper);
2215 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2216 gtk_tree_model_get (dest_model, &dest_iter,
2217 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2219 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2220 gtk_tree_model_get (source_model, &iter,
2221 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2224 /* Create the info for the performer */
2225 info = g_slice_new (DndFolderInfo);
2226 info->src_folder = g_object_ref (folder);
2227 info->dst_folder = g_object_ref (dest_folder);
2228 info->helper = helper;
2230 /* Connect to the destination folder and perform the copy/move */
2231 modest_platform_connect_and_perform_if_network_folderstore (GTK_WINDOW (win),
2233 drag_and_drop_from_folder_view_dst_folder_performer,
2237 g_object_unref (dest_folder);
2238 g_object_unref (folder);
2242 * This function receives the data set by the "drag-data-get" signal
2243 * handler. This information comes within the #GtkSelectionData. This
2244 * function will manage both the drags of folders of the treeview and
2245 * drags of headers of the header view widget.
2248 on_drag_data_received (GtkWidget *widget,
2249 GdkDragContext *context,
2252 GtkSelectionData *selection_data,
2257 GtkWidget *source_widget;
2258 GtkTreeModel *dest_model, *source_model;
2259 GtkTreePath *source_row, *dest_row;
2260 GtkTreeViewDropPosition pos;
2261 gboolean success = FALSE, delete_source = FALSE;
2262 DndHelper *helper = NULL;
2264 /* Do not allow further process */
2265 g_signal_stop_emission_by_name (widget, "drag-data-received");
2266 source_widget = gtk_drag_get_source_widget (context);
2268 /* Get the action */
2269 if (context->action == GDK_ACTION_MOVE) {
2270 delete_source = TRUE;
2272 /* Notify that there is no folder selected. We need to
2273 do this in order to update the headers view (and
2274 its monitors, because when moving, the old folder
2275 won't longer exist. We can not wait for the end of
2276 the operation, because the operation won't start if
2277 the folder is in use */
2278 if (source_widget == widget) {
2279 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2280 gtk_tree_selection_unselect_all (sel);
2284 /* Check if the get_data failed */
2285 if (selection_data == NULL || selection_data->length < 0)
2286 gtk_drag_finish (context, success, FALSE, time);
2288 /* Select the destination model */
2289 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2291 /* Get the path to the destination row. Can not call
2292 gtk_tree_view_get_drag_dest_row() because the source row
2293 is not selected anymore */
2294 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2297 /* Only allow drops IN other rows */
2299 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2300 pos == GTK_TREE_VIEW_DROP_AFTER)
2301 gtk_drag_finish (context, success, FALSE, time);
2303 /* Create the helper */
2304 helper = g_slice_new0 (DndHelper);
2305 helper->delete_source = delete_source;
2306 helper->context = context;
2307 helper->time = time;
2309 /* Drags from the header view */
2310 if (source_widget != widget) {
2311 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2313 drag_and_drop_from_header_view (source_model,
2319 /* Get the source model and row */
2320 gtk_tree_get_row_drag_data (selection_data,
2323 helper->source_row = gtk_tree_path_copy (source_row);
2325 drag_and_drop_from_folder_view (source_model,
2331 gtk_tree_path_free (source_row);
2335 gtk_tree_path_free (dest_row);
2339 * We define a "drag-drop" signal handler because we do not want to
2340 * use the default one, because the default one always calls
2341 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2342 * signal handler, because there we have all the information available
2343 * to know if the dnd was a success or not.
2346 drag_drop_cb (GtkWidget *widget,
2347 GdkDragContext *context,
2355 if (!context->targets)
2358 /* Check if we're dragging a folder row */
2359 target = gtk_drag_dest_find_target (widget, context, NULL);
2361 /* Request the data from the source. */
2362 gtk_drag_get_data(widget, context, target, time);
2368 * This function expands a node of a tree view if it's not expanded
2369 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2370 * does that, so that's why they're here.
2373 expand_row_timeout (gpointer data)
2375 GtkTreeView *tree_view = data;
2376 GtkTreePath *dest_path = NULL;
2377 GtkTreeViewDropPosition pos;
2378 gboolean result = FALSE;
2380 gdk_threads_enter ();
2382 gtk_tree_view_get_drag_dest_row (tree_view,
2387 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2388 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2389 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2390 gtk_tree_path_free (dest_path);
2394 gtk_tree_path_free (dest_path);
2399 gdk_threads_leave ();
2405 * This function is called whenever the pointer is moved over a widget
2406 * while dragging some data. It installs a timeout that will expand a
2407 * node of the treeview if not expanded yet. This function also calls
2408 * gdk_drag_status in order to set the suggested action that will be
2409 * used by the "drag-data-received" signal handler to know if we
2410 * should do a move or just a copy of the data.
2413 on_drag_motion (GtkWidget *widget,
2414 GdkDragContext *context,
2420 GtkTreeViewDropPosition pos;
2421 GtkTreePath *dest_row;
2422 GtkTreeModel *dest_model;
2423 ModestFolderViewPrivate *priv;
2424 GdkDragAction suggested_action;
2425 gboolean valid_location = FALSE;
2426 TnyFolderStore *folder;
2428 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2430 if (priv->timer_expander != 0) {
2431 g_source_remove (priv->timer_expander);
2432 priv->timer_expander = 0;
2435 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2440 /* Do not allow drops between folders */
2442 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2443 pos == GTK_TREE_VIEW_DROP_AFTER) {
2444 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2445 gdk_drag_status(context, 0, time);
2446 valid_location = FALSE;
2449 valid_location = TRUE;
2452 /* Check that the destination folder is writable */
2453 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2454 folder = tree_path_to_folder (dest_model, dest_row);
2455 if (folder && TNY_IS_FOLDER (folder)) {
2456 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2458 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2459 valid_location = FALSE;
2463 g_object_unref (folder);
2465 /* Expand the selected row after 1/2 second */
2466 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2467 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2468 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2471 /* Select the desired action. By default we pick MOVE */
2472 suggested_action = GDK_ACTION_MOVE;
2474 if (context->actions == GDK_ACTION_COPY)
2475 gdk_drag_status(context, GDK_ACTION_COPY, time);
2476 else if (context->actions == GDK_ACTION_MOVE)
2477 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2478 else if (context->actions & suggested_action)
2479 gdk_drag_status(context, suggested_action, time);
2481 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2485 gtk_tree_path_free (dest_row);
2486 g_signal_stop_emission_by_name (widget, "drag-motion");
2488 return valid_location;
2492 * This function sets the treeview as a source and a target for dnd
2493 * events. It also connects all the requirede signals.
2496 setup_drag_and_drop (GtkTreeView *self)
2498 /* Set up the folder view as a dnd destination. Set only the
2499 highlight flag, otherwise gtk will have a different
2501 gtk_drag_dest_set (GTK_WIDGET (self),
2502 GTK_DEST_DEFAULT_HIGHLIGHT,
2503 folder_view_drag_types,
2504 G_N_ELEMENTS (folder_view_drag_types),
2505 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2507 g_signal_connect (G_OBJECT (self),
2508 "drag_data_received",
2509 G_CALLBACK (on_drag_data_received),
2513 /* Set up the treeview as a dnd source */
2514 gtk_drag_source_set (GTK_WIDGET (self),
2516 folder_view_drag_types,
2517 G_N_ELEMENTS (folder_view_drag_types),
2518 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2520 g_signal_connect (G_OBJECT (self),
2522 G_CALLBACK (on_drag_motion),
2525 g_signal_connect (G_OBJECT (self),
2527 G_CALLBACK (on_drag_data_get),
2530 g_signal_connect (G_OBJECT (self),
2532 G_CALLBACK (drag_drop_cb),
2537 * This function manages the navigation through the folders using the
2538 * keyboard or the hardware keys in the device
2541 on_key_pressed (GtkWidget *self,
2545 GtkTreeSelection *selection;
2547 GtkTreeModel *model;
2548 gboolean retval = FALSE;
2550 /* Up and Down are automatically managed by the treeview */
2551 if (event->keyval == GDK_Return) {
2552 /* Expand/Collapse the selected row */
2553 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2554 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2557 path = gtk_tree_model_get_path (model, &iter);
2559 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2560 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2562 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2563 gtk_tree_path_free (path);
2565 /* No further processing */
2573 * We listen to the changes in the local folder account name key,
2574 * because we want to show the right name in the view. The local
2575 * folder account name corresponds to the device name in the Maemo
2576 * version. We do this because we do not want to query gconf on each
2577 * tree view refresh. It's better to cache it and change whenever
2581 on_configuration_key_changed (ModestConf* conf,
2583 ModestConfEvent event,
2584 ModestConfNotificationId id,
2585 ModestFolderView *self)
2587 ModestFolderViewPrivate *priv;
2590 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2591 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2593 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2594 g_free (priv->local_account_name);
2596 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2597 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2599 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2600 MODEST_CONF_DEVICE_NAME, NULL);
2602 /* Force a redraw */
2603 #if GTK_CHECK_VERSION(2, 8, 0)
2604 GtkTreeViewColumn * tree_column;
2606 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2607 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2608 gtk_tree_view_column_queue_resize (tree_column);
2610 gtk_widget_queue_draw (GTK_WIDGET (self));
2616 modest_folder_view_set_style (ModestFolderView *self,
2617 ModestFolderViewStyle style)
2619 ModestFolderViewPrivate *priv;
2621 g_return_if_fail (self);
2623 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2625 priv->style = style;
2629 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2630 const gchar *account_id)
2632 ModestFolderViewPrivate *priv;
2633 GtkTreeModel *model;
2635 g_return_if_fail (self);
2637 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2639 /* This will be used by the filter_row callback,
2640 * to decided which rows to show: */
2641 if (priv->visible_account_id) {
2642 g_free (priv->visible_account_id);
2643 priv->visible_account_id = NULL;
2646 priv->visible_account_id = g_strdup (account_id);
2649 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2650 if (GTK_IS_TREE_MODEL_FILTER (model))
2651 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2653 /* Save settings to gconf */
2654 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2655 MODEST_CONF_FOLDER_VIEW_KEY);
2659 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2661 ModestFolderViewPrivate *priv;
2663 g_return_val_if_fail (self, NULL);
2665 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2667 return (const gchar *) priv->visible_account_id;
2671 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2675 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2677 gtk_tree_model_get (model, iter,
2678 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2681 gboolean result = FALSE;
2682 if (type == TNY_FOLDER_TYPE_INBOX) {
2686 *inbox_iter = *iter;
2690 if (gtk_tree_model_iter_children (model, &child, iter)) {
2691 if (find_inbox_iter (model, &child, inbox_iter))
2695 } while (gtk_tree_model_iter_next (model, iter));
2704 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2706 GtkTreeModel *model;
2707 GtkTreeIter iter, inbox_iter;
2708 GtkTreeSelection *sel;
2709 GtkTreePath *path = NULL;
2711 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2715 expand_root_items (self);
2716 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2718 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2719 g_warning ("%s: model is empty", __FUNCTION__);
2723 if (find_inbox_iter (model, &iter, &inbox_iter))
2724 path = gtk_tree_model_get_path (model, &inbox_iter);
2726 path = gtk_tree_path_new_first ();
2728 /* Select the row and free */
2729 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2730 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2731 gtk_tree_path_free (path);
2734 gtk_widget_grab_focus (GTK_WIDGET(self));
2740 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2745 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2746 TnyFolder* a_folder;
2749 gtk_tree_model_get (model, iter,
2750 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2751 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2752 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2756 if (folder == a_folder) {
2757 g_object_unref (a_folder);
2758 *folder_iter = *iter;
2761 g_object_unref (a_folder);
2763 if (gtk_tree_model_iter_children (model, &child, iter)) {
2764 if (find_folder_iter (model, &child, folder_iter, folder))
2768 } while (gtk_tree_model_iter_next (model, iter));
2775 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2776 ModestFolderView *self)
2778 ModestFolderViewPrivate *priv = NULL;
2779 GtkTreeSelection *sel;
2780 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2781 GObject *instance = NULL;
2783 if (!MODEST_IS_FOLDER_VIEW(self))
2786 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2788 priv->reexpand = TRUE;
2790 gtk_tree_model_get (tree_model, iter,
2791 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2792 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2794 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2795 priv->folder_to_select = g_object_ref (instance);
2797 g_object_unref (instance);
2800 if (priv->folder_to_select) {
2802 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2805 path = gtk_tree_model_get_path (tree_model, iter);
2806 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2808 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2810 gtk_tree_selection_select_iter (sel, iter);
2811 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2813 gtk_tree_path_free (path);
2818 modest_folder_view_disable_next_folder_selection (self);
2819 /* g_object_unref (priv->folder_to_select); */
2820 /* priv->folder_to_select = NULL; */
2826 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2828 ModestFolderViewPrivate *priv = NULL;
2830 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2831 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2833 if (priv->folder_to_select)
2834 g_object_unref(priv->folder_to_select);
2836 priv->folder_to_select = NULL;
2840 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2841 gboolean after_change)
2843 GtkTreeModel *model;
2844 GtkTreeIter iter, folder_iter;
2845 GtkTreeSelection *sel;
2846 ModestFolderViewPrivate *priv = NULL;
2848 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2849 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2851 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2855 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2856 gtk_tree_selection_unselect_all (sel);
2858 if (priv->folder_to_select)
2859 g_object_unref(priv->folder_to_select);
2860 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2864 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2868 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2869 g_warning ("%s: model is empty", __FUNCTION__);
2873 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2876 path = gtk_tree_model_get_path (model, &folder_iter);
2877 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2879 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2880 gtk_tree_selection_select_iter (sel, &folder_iter);
2881 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2883 gtk_tree_path_free (path);
2891 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2893 /* Copy selection */
2894 _clipboard_set_selected_data (folder_view, FALSE);
2898 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2900 ModestFolderViewPrivate *priv = NULL;
2901 GtkTreeModel *model = NULL;
2902 const gchar **hidding = NULL;
2903 guint i, n_selected;
2905 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2906 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2908 /* Copy selection */
2909 if (!_clipboard_set_selected_data (folder_view, TRUE))
2912 /* Get hidding ids */
2913 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2915 /* Clear hidding array created by previous cut operation */
2916 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2918 /* Copy hidding array */
2919 priv->n_selected = n_selected;
2920 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2921 for (i=0; i < n_selected; i++)
2922 priv->hidding_ids[i] = g_strdup(hidding[i]);
2924 /* Hide cut folders */
2925 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2926 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2930 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2931 ModestFolderView *folder_view_dst)
2933 GtkTreeModel *filter_model = NULL;
2934 GtkTreeModel *model = NULL;
2935 GtkTreeModel *new_filter_model = NULL;
2937 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2938 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2941 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2942 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2944 /* Build new filter model */
2945 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2946 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2950 /* Set copied model */
2951 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2952 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
2953 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
2956 g_object_unref (new_filter_model);
2960 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2963 GtkTreeModel *model = NULL;
2964 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2965 priv->show_non_move = show;
2966 /* modest_folder_view_update_model(folder_view, */
2967 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2969 /* Hide special folders */
2970 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2971 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2972 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2976 /* Returns FALSE if it did not selected anything */
2978 _clipboard_set_selected_data (ModestFolderView *folder_view,
2981 ModestFolderViewPrivate *priv = NULL;
2982 TnyFolderStore *folder = NULL;
2983 gboolean retval = FALSE;
2985 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2986 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2988 /* Set selected data on clipboard */
2989 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2990 folder = modest_folder_view_get_selected (folder_view);
2992 /* Do not allow to select an account */
2993 if (TNY_IS_FOLDER (folder)) {
2994 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2999 g_object_unref (folder);
3005 _clear_hidding_filter (ModestFolderView *folder_view)
3007 ModestFolderViewPrivate *priv;
3010 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3011 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3013 if (priv->hidding_ids != NULL) {
3014 for (i=0; i < priv->n_selected; i++)
3015 g_free (priv->hidding_ids[i]);
3016 g_free(priv->hidding_ids);
3022 on_display_name_changed (ModestAccountMgr *mgr,
3023 const gchar *account,
3026 ModestFolderView *self;
3028 self = MODEST_FOLDER_VIEW (user_data);
3030 /* Force a redraw */
3031 #if GTK_CHECK_VERSION(2, 8, 0)
3032 GtkTreeViewColumn * tree_column;
3034 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3035 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3036 gtk_tree_view_column_queue_resize (tree_column);
3038 gtk_widget_queue_draw (GTK_WIDGET (self));