1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-store-tree-model.h>
36 #include <tny-gtk-header-list-model.h>
37 #include <tny-folder.h>
38 #include <tny-folder-store-observer.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <tny-camel-account.h>
45 #include <modest-tny-account.h>
46 #include <modest-tny-folder.h>
47 #include <modest-tny-local-folders-account.h>
48 #include <modest-tny-outbox-account.h>
49 #include <modest-marshal.h>
50 #include <modest-icon-names.h>
51 #include <modest-tny-account-store.h>
52 #include <modest-text-utils.h>
53 #include <modest-runtime.h>
54 #include "modest-folder-view.h"
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
58 #include "modest-dnd.h"
59 #include "widgets/modest-window.h"
61 /* Folder view drag types */
62 const GtkTargetEntry folder_view_drag_types[] =
64 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
65 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
68 /* 'private'/'protected' functions */
69 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
70 static void modest_folder_view_init (ModestFolderView *obj);
71 static void modest_folder_view_finalize (GObject *obj);
73 static void tny_account_store_view_init (gpointer g,
76 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
77 TnyAccountStore *account_store);
79 static void on_selection_changed (GtkTreeSelection *sel,
82 static void on_account_removed (TnyAccountStore *self,
86 static void on_account_inserted (TnyAccountStore *self,
90 static void on_account_changed (TnyAccountStore *self,
94 static gint cmp_rows (GtkTreeModel *tree_model,
99 static gboolean filter_row (GtkTreeModel *model,
103 static gboolean on_key_pressed (GtkWidget *self,
107 static void on_configuration_key_changed (ModestConf* conf,
109 ModestConfEvent event,
110 ModestConfNotificationId notification_id,
111 ModestFolderView *self);
114 static void on_drag_data_get (GtkWidget *widget,
115 GdkDragContext *context,
116 GtkSelectionData *selection_data,
121 static void on_drag_data_received (GtkWidget *widget,
122 GdkDragContext *context,
125 GtkSelectionData *selection_data,
130 static gboolean on_drag_motion (GtkWidget *widget,
131 GdkDragContext *context,
137 static void expand_root_items (ModestFolderView *self);
139 static gint expand_row_timeout (gpointer data);
141 static void setup_drag_and_drop (GtkTreeView *self);
143 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
146 static void _clear_hidding_filter (ModestFolderView *folder_view);
148 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
151 ModestFolderView *self);
153 static void on_display_name_changed (ModestAccountMgr *self,
154 const gchar *account,
158 FOLDER_SELECTION_CHANGED_SIGNAL,
159 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
163 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
164 struct _ModestFolderViewPrivate {
165 TnyAccountStore *account_store;
166 TnyFolderStore *cur_folder_store;
168 TnyFolder *folder_to_select; /* folder to select after the next update */
170 gulong changed_signal;
171 gulong account_inserted_signal;
172 gulong account_removed_signal;
173 gulong account_changed_signal;
174 gulong conf_key_signal;
175 gulong display_name_changed_signal;
177 /* not unref this object, its a singlenton */
178 ModestEmailClipboard *clipboard;
180 /* Filter tree model */
184 TnyFolderStoreQuery *query;
185 guint timer_expander;
187 gchar *local_account_name;
188 gchar *visible_account_id;
189 ModestFolderViewStyle style;
191 gboolean reselect; /* we use this to force a reselection of the INBOX */
192 gboolean show_non_move;
193 gboolean reexpand; /* next time we expose, we'll expand all root folders */
195 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
196 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
197 MODEST_TYPE_FOLDER_VIEW, \
198 ModestFolderViewPrivate))
200 static GObjectClass *parent_class = NULL;
202 static guint signals[LAST_SIGNAL] = {0};
205 modest_folder_view_get_type (void)
207 static GType my_type = 0;
209 static const GTypeInfo my_info = {
210 sizeof(ModestFolderViewClass),
211 NULL, /* base init */
212 NULL, /* base finalize */
213 (GClassInitFunc) modest_folder_view_class_init,
214 NULL, /* class finalize */
215 NULL, /* class data */
216 sizeof(ModestFolderView),
218 (GInstanceInitFunc) modest_folder_view_init,
222 static const GInterfaceInfo tny_account_store_view_info = {
223 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
224 NULL, /* interface_finalize */
225 NULL /* interface_data */
229 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
233 g_type_add_interface_static (my_type,
234 TNY_TYPE_ACCOUNT_STORE_VIEW,
235 &tny_account_store_view_info);
241 modest_folder_view_class_init (ModestFolderViewClass *klass)
243 GObjectClass *gobject_class;
244 gobject_class = (GObjectClass*) klass;
246 parent_class = g_type_class_peek_parent (klass);
247 gobject_class->finalize = modest_folder_view_finalize;
249 g_type_class_add_private (gobject_class,
250 sizeof(ModestFolderViewPrivate));
252 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
253 g_signal_new ("folder_selection_changed",
254 G_TYPE_FROM_CLASS (gobject_class),
256 G_STRUCT_OFFSET (ModestFolderViewClass,
257 folder_selection_changed),
259 modest_marshal_VOID__POINTER_BOOLEAN,
260 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
263 * This signal is emitted whenever the currently selected
264 * folder display name is computed. Note that the name could
265 * be different to the folder name, because we could append
266 * the unread messages count to the folder name to build the
267 * folder display name
269 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
270 g_signal_new ("folder-display-name-changed",
271 G_TYPE_FROM_CLASS (gobject_class),
273 G_STRUCT_OFFSET (ModestFolderViewClass,
274 folder_display_name_changed),
276 g_cclosure_marshal_VOID__STRING,
277 G_TYPE_NONE, 1, G_TYPE_STRING);
280 /* Simplify checks for NULLs: */
282 strings_are_equal (const gchar *a, const gchar *b)
288 return (strcmp (a, b) == 0);
295 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
297 GObject *instance = NULL;
299 gtk_tree_model_get (model, iter,
300 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
304 return FALSE; /* keep walking */
306 if (!TNY_IS_ACCOUNT (instance)) {
307 g_object_unref (instance);
308 return FALSE; /* keep walking */
311 /* Check if this is the looked-for account: */
312 TnyAccount *this_account = TNY_ACCOUNT (instance);
313 TnyAccount *account = TNY_ACCOUNT (data);
315 const gchar *this_account_id = tny_account_get_id(this_account);
316 const gchar *account_id = tny_account_get_id(account);
317 g_object_unref (instance);
320 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
321 if (strings_are_equal(this_account_id, account_id)) {
322 /* Tell the model that the data has changed, so that
323 * it calls the cell_data_func callbacks again: */
324 /* TODO: This does not seem to actually cause the new string to be shown: */
325 gtk_tree_model_row_changed (model, path, iter);
327 return TRUE; /* stop walking */
330 return FALSE; /* keep walking */
335 ModestFolderView *self;
336 gchar *previous_name;
337 } GetMmcAccountNameData;
340 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
342 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
344 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
346 if (!strings_are_equal (
347 tny_account_get_name(TNY_ACCOUNT(account)),
348 data->previous_name)) {
350 /* Tell the model that the data has changed, so that
351 * it calls the cell_data_func callbacks again: */
352 ModestFolderView *self = data->self;
353 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
355 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
358 g_free (data->previous_name);
359 g_slice_free (GetMmcAccountNameData, data);
363 text_cell_data (GtkTreeViewColumn *column,
364 GtkCellRenderer *renderer,
365 GtkTreeModel *tree_model,
369 ModestFolderViewPrivate *priv;
370 GObject *rendobj = (GObject *) renderer;
372 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
373 GObject *instance = NULL;
375 gtk_tree_model_get (tree_model, iter,
376 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
377 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
378 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
388 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
389 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
391 gchar *item_name = NULL;
392 gint item_weight = 400;
394 if (type != TNY_FOLDER_TYPE_ROOT) {
397 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
398 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
399 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
400 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
402 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
406 /* note: we cannot reliably get the counts from the tree model, we need
407 * to use explicit calls on tny_folder for some reason.
409 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
410 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
411 (type == TNY_FOLDER_TYPE_OUTBOX) ||
412 (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
413 number = tny_folder_get_all_count (TNY_FOLDER(instance));
415 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
417 /* Use bold font style if there are unread or unset messages */
419 item_name = g_strdup_printf ("%s (%d)", fname, number);
422 item_name = g_strdup (fname);
426 } else if (TNY_IS_ACCOUNT (instance)) {
427 /* If it's a server account */
428 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
429 item_name = g_strdup (priv->local_account_name);
431 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
432 /* fname is only correct when the items are first
433 * added to the model, not when the account is
434 * changed later, so get the name from the account
436 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
439 item_name = g_strdup (fname);
445 item_name = g_strdup ("unknown");
447 if (item_name && item_weight) {
448 /* Set the name in the treeview cell: */
449 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
451 /* Notify display name observers */
452 /* TODO: What listens for this signal, and how can it use only the new name? */
453 if (((GObject *) priv->cur_folder_store) == instance) {
454 g_signal_emit (G_OBJECT(self),
455 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
462 /* If it is a Memory card account, make sure that we have the correct name.
463 * This function will be trigerred again when the name has been retrieved: */
464 if (TNY_IS_STORE_ACCOUNT (instance) &&
465 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
467 /* Get the account name asynchronously: */
468 GetMmcAccountNameData *callback_data =
469 g_slice_new0(GetMmcAccountNameData);
470 callback_data->self = self;
472 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
474 callback_data->previous_name = g_strdup (name);
476 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
477 on_get_mmc_account_name, callback_data);
480 g_object_unref (G_OBJECT (instance));
487 GdkPixbuf *pixbuf_open;
488 GdkPixbuf *pixbuf_close;
493 get_folder_icons (TnyFolderType type, GObject *instance)
495 GdkPixbuf *pixbuf = NULL;
496 GdkPixbuf *pixbuf_open = NULL;
497 GdkPixbuf *pixbuf_close = NULL;
498 ThreePixbufs *retval = g_slice_new (ThreePixbufs);
500 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
501 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
502 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
503 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
504 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
506 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
507 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
508 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
509 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
510 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
512 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
513 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
514 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
515 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
516 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
519 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
520 /* We include the MERGE type here because it's used to create
521 the local OUTBOX folder */
522 if (type == TNY_FOLDER_TYPE_NORMAL ||
523 type == TNY_FOLDER_TYPE_UNKNOWN) {
524 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
528 case TNY_FOLDER_TYPE_INVALID:
529 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
532 case TNY_FOLDER_TYPE_ROOT:
533 if (TNY_IS_ACCOUNT (instance)) {
535 if (modest_tny_account_is_virtual_local_folders (
536 TNY_ACCOUNT (instance))) {
539 avirt_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS));
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, normal_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,
1225 ModestFolderViewPrivate *priv;
1226 GtkTreeModel *sort_model, *filter_model;
1228 /* Ignore transport account insertions, we're not showing them
1229 in the folder view */
1230 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1233 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1235 /* Get the inner model */
1236 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1237 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1239 /* Remove the account from the model */
1240 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1241 G_OBJECT (tny_account));
1243 /* Insert the account in the model */
1244 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1245 G_OBJECT (tny_account));
1247 /* Refilter the model */
1248 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1253 * Selects the first inbox or the local account in an idle
1256 on_idle_select_first_inbox_or_local (gpointer user_data)
1258 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1260 modest_folder_view_select_first_inbox_or_local (self);
1267 on_account_removed (TnyAccountStore *account_store,
1268 TnyAccount *account,
1271 ModestFolderView *self = NULL;
1272 ModestFolderViewPrivate *priv;
1273 GtkTreeModel *sort_model, *filter_model;
1274 GtkTreeSelection *sel = NULL;
1275 gboolean same_account_selected = FALSE;
1277 /* Ignore transport account removals, we're not showing them
1278 in the folder view */
1279 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1282 self = MODEST_FOLDER_VIEW (user_data);
1283 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1285 /* Invalidate the cur_folder_store only if the selected folder
1286 belongs to the account that is being removed */
1287 if (priv->cur_folder_store) {
1288 TnyAccount *selected_folder_account = NULL;
1290 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1291 selected_folder_account =
1292 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1294 selected_folder_account =
1295 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1298 if (selected_folder_account == account) {
1299 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1300 gtk_tree_selection_unselect_all (sel);
1301 same_account_selected = TRUE;
1303 g_object_unref (selected_folder_account);
1306 /* Invalidate row to select only if the folder to select
1307 belongs to the account that is being removed*/
1308 if (priv->folder_to_select) {
1309 TnyAccount *folder_to_select_account = NULL;
1311 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1312 if (folder_to_select_account == account) {
1313 modest_folder_view_disable_next_folder_selection (self);
1314 g_object_unref (priv->folder_to_select);
1315 priv->folder_to_select = NULL;
1317 g_object_unref (folder_to_select_account);
1320 /* Remove the account from the model */
1321 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1322 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1323 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1324 G_OBJECT (account));
1326 /* If the removed account is the currently viewed one then
1327 clear the configuration value. The new visible account will be the default account */
1328 if (priv->visible_account_id &&
1329 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1331 /* Clear the current visible account_id */
1332 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1334 /* Call the restore method, this will set the new visible account */
1335 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1336 MODEST_CONF_FOLDER_VIEW_KEY);
1339 /* Refilter the model */
1340 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1342 /* Select the first INBOX if the currently selected folder
1343 belongs to the account that is being deleted */
1344 if (same_account_selected)
1345 g_idle_add (on_idle_select_first_inbox_or_local, self);
1349 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1351 GtkTreeViewColumn *col;
1353 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1355 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1357 g_printerr ("modest: failed get column for title\n");
1361 gtk_tree_view_column_set_title (col, title);
1362 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1367 modest_folder_view_on_map (ModestFolderView *self,
1368 GdkEventExpose *event,
1371 ModestFolderViewPrivate *priv;
1373 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1375 /* This won't happen often */
1376 if (G_UNLIKELY (priv->reselect)) {
1377 /* Select the first inbox or the local account if not found */
1379 /* TODO: this could cause a lock at startup, so we
1380 comment it for the moment. We know that this will
1381 be a bug, because the INBOX is not selected, but we
1382 need to rewrite some parts of Modest to avoid the
1383 deathlock situation */
1384 /* TODO: check if this is still the case */
1385 priv->reselect = FALSE;
1386 modest_folder_view_select_first_inbox_or_local (self);
1387 /* Notify the display name observers */
1388 g_signal_emit (G_OBJECT(self),
1389 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1393 if (priv->reexpand) {
1394 expand_root_items (self);
1395 priv->reexpand = FALSE;
1402 modest_folder_view_new (TnyFolderStoreQuery *query)
1405 ModestFolderViewPrivate *priv;
1406 GtkTreeSelection *sel;
1408 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1409 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1412 priv->query = g_object_ref (query);
1414 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1415 priv->changed_signal = g_signal_connect (sel, "changed",
1416 G_CALLBACK (on_selection_changed), self);
1418 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1420 return GTK_WIDGET(self);
1423 /* this feels dirty; any other way to expand all the root items? */
1425 expand_root_items (ModestFolderView *self)
1428 GtkTreeModel *model;
1431 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1432 path = gtk_tree_path_new_first ();
1434 /* all folders should have child items, so.. */
1436 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1437 gtk_tree_path_next (path);
1438 } while (gtk_tree_model_get_iter (model, &iter, path));
1440 gtk_tree_path_free (path);
1444 * We use this function to implement the
1445 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1446 * account in this case, and the local folders.
1449 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1451 ModestFolderViewPrivate *priv;
1452 gboolean retval = TRUE;
1453 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1454 GObject *instance = NULL;
1455 const gchar *id = NULL;
1457 gboolean found = FALSE;
1458 gboolean cleared = FALSE;
1460 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1461 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1463 gtk_tree_model_get (model, iter,
1464 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1465 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1468 /* Do not show if there is no instance, this could indeed
1469 happen when the model is being modified while it's being
1470 drawn. This could occur for example when moving folders
1475 if (type == TNY_FOLDER_TYPE_ROOT) {
1476 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1477 account instead of a folder. */
1478 if (TNY_IS_ACCOUNT (instance)) {
1479 TnyAccount *acc = TNY_ACCOUNT (instance);
1480 const gchar *account_id = tny_account_get_id (acc);
1482 /* If it isn't a special folder,
1483 * don't show it unless it is the visible account: */
1484 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1485 !modest_tny_account_is_virtual_local_folders (acc) &&
1486 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1488 /* Show only the visible account id */
1489 if (priv->visible_account_id) {
1490 if (strcmp (account_id, priv->visible_account_id))
1497 /* Never show these to the user. They are merged into one folder
1498 * in the local-folders account instead: */
1499 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1504 /* Check hiding (if necessary) */
1505 cleared = modest_email_clipboard_cleared (priv->clipboard);
1506 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1507 id = tny_folder_get_id (TNY_FOLDER(instance));
1508 if (priv->hidding_ids != NULL)
1509 for (i=0; i < priv->n_selected && !found; i++)
1510 if (priv->hidding_ids[i] != NULL && id != NULL)
1511 found = (!strcmp (priv->hidding_ids[i], id));
1517 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1518 folder as no message can be move there according to UI specs */
1519 if (!priv->show_non_move) {
1521 case TNY_FOLDER_TYPE_OUTBOX:
1522 case TNY_FOLDER_TYPE_SENT:
1523 case TNY_FOLDER_TYPE_DRAFTS:
1526 case TNY_FOLDER_TYPE_UNKNOWN:
1527 case TNY_FOLDER_TYPE_NORMAL:
1528 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1529 if (type == TNY_FOLDER_TYPE_INVALID)
1530 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1532 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1533 type == TNY_FOLDER_TYPE_SENT
1534 || type == TNY_FOLDER_TYPE_DRAFTS)
1543 g_object_unref (instance);
1550 modest_folder_view_update_model (ModestFolderView *self,
1551 TnyAccountStore *account_store)
1553 ModestFolderViewPrivate *priv;
1554 GtkTreeModel *model /* , *old_model */;
1555 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1557 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1558 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1561 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1563 /* Notify that there is no folder selected */
1564 g_signal_emit (G_OBJECT(self),
1565 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1567 if (priv->cur_folder_store) {
1568 g_object_unref (priv->cur_folder_store);
1569 priv->cur_folder_store = NULL;
1572 /* FIXME: the local accounts are not shown when the query
1573 selects only the subscribed folders */
1574 model = tny_gtk_folder_store_tree_model_new (NULL);
1576 /* Get the accounts: */
1577 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1579 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1581 sortable = gtk_tree_model_sort_new_with_model (model);
1582 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1583 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1584 GTK_SORT_ASCENDING);
1585 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1586 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1587 cmp_rows, NULL, NULL);
1589 /* Create filter model */
1590 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1591 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1597 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1598 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1599 (GCallback) on_row_inserted_maybe_select_folder, self);
1602 g_object_unref (model);
1603 g_object_unref (filter_model);
1604 g_object_unref (sortable);
1606 /* Force a reselection of the INBOX next time the widget is shown */
1607 priv->reselect = TRUE;
1614 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1616 GtkTreeModel *model = NULL;
1617 TnyFolderStore *folder = NULL;
1619 ModestFolderView *tree_view = NULL;
1620 ModestFolderViewPrivate *priv = NULL;
1621 gboolean selected = FALSE;
1623 g_return_if_fail (sel);
1624 g_return_if_fail (user_data);
1626 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1628 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1630 /* Notify the display name observers */
1631 g_signal_emit (G_OBJECT(user_data),
1632 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1635 tree_view = MODEST_FOLDER_VIEW (user_data);
1638 gtk_tree_model_get (model, &iter,
1639 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1642 /* If the folder is the same do not notify */
1643 if (folder && priv->cur_folder_store == folder) {
1644 g_object_unref (folder);
1649 /* Current folder was unselected */
1650 if (priv->cur_folder_store) {
1651 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1652 priv->cur_folder_store, FALSE);
1654 if (TNY_IS_FOLDER(priv->cur_folder_store))
1655 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1656 FALSE, NULL, NULL, NULL);
1658 /* FALSE --> don't expunge the messages */
1660 g_object_unref (priv->cur_folder_store);
1661 priv->cur_folder_store = NULL;
1664 /* New current references */
1665 priv->cur_folder_store = folder;
1667 /* New folder has been selected. Do not notify if there is
1668 nothing new selected */
1670 g_signal_emit (G_OBJECT(tree_view),
1671 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1672 0, priv->cur_folder_store, TRUE);
1677 modest_folder_view_get_selected (ModestFolderView *self)
1679 ModestFolderViewPrivate *priv;
1681 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1683 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1684 if (priv->cur_folder_store)
1685 g_object_ref (priv->cur_folder_store);
1687 return priv->cur_folder_store;
1691 get_cmp_rows_type_pos (GObject *folder)
1693 /* Remote accounts -> Local account -> MMC account .*/
1696 if (TNY_IS_ACCOUNT (folder) &&
1697 modest_tny_account_is_virtual_local_folders (
1698 TNY_ACCOUNT (folder))) {
1700 } else if (TNY_IS_ACCOUNT (folder)) {
1701 TnyAccount *account = TNY_ACCOUNT (folder);
1702 const gchar *account_id = tny_account_get_id (account);
1703 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1709 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1710 return -1; /* Should never happen */
1715 get_cmp_subfolder_type_pos (TnyFolderType t)
1717 /* Inbox, Outbox, Drafts, Sent, User */
1721 case TNY_FOLDER_TYPE_INBOX:
1724 case TNY_FOLDER_TYPE_OUTBOX:
1727 case TNY_FOLDER_TYPE_DRAFTS:
1730 case TNY_FOLDER_TYPE_SENT:
1739 * This function orders the mail accounts according to these rules:
1740 * 1st - remote accounts
1741 * 2nd - local account
1745 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1749 gchar *name1 = NULL;
1750 gchar *name2 = NULL;
1751 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1752 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1753 GObject *folder1 = NULL;
1754 GObject *folder2 = NULL;
1756 gtk_tree_model_get (tree_model, iter1,
1757 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1758 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1759 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1761 gtk_tree_model_get (tree_model, iter2,
1762 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1763 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1764 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1767 /* Return if we get no folder. This could happen when folder
1768 operations are happening. The model is updated after the
1769 folder copy/move actually occurs, so there could be
1770 situations where the model to be drawn is not correct */
1771 if (!folder1 || !folder2)
1774 if (type == TNY_FOLDER_TYPE_ROOT) {
1775 /* Compare the types, so that
1776 * Remote accounts -> Local account -> MMC account .*/
1777 const gint pos1 = get_cmp_rows_type_pos (folder1);
1778 const gint pos2 = get_cmp_rows_type_pos (folder2);
1779 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1780 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1783 else if (pos1 > pos2)
1786 /* Compare items of the same type: */
1788 TnyAccount *account1 = NULL;
1789 if (TNY_IS_ACCOUNT (folder1))
1790 account1 = TNY_ACCOUNT (folder1);
1792 TnyAccount *account2 = NULL;
1793 if (TNY_IS_ACCOUNT (folder2))
1794 account2 = TNY_ACCOUNT (folder2);
1796 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1797 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1799 if (!account_id && !account_id2) {
1801 } else if (!account_id) {
1803 } else if (!account_id2) {
1805 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1808 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1812 gint cmp1 = 0, cmp2 = 0;
1813 /* get the parent to know if it's a local folder */
1816 gboolean has_parent;
1817 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1819 GObject *parent_folder;
1820 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1821 gtk_tree_model_get (tree_model, &parent,
1822 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1823 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1825 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1826 TNY_IS_ACCOUNT (parent_folder) &&
1827 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1828 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1829 (TNY_FOLDER (folder1)));
1830 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1831 (TNY_FOLDER (folder2)));
1833 g_object_unref (parent_folder);
1836 /* if they are not local folders */
1838 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1839 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1843 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1845 cmp = (cmp1 - cmp2);
1850 g_object_unref(G_OBJECT(folder1));
1852 g_object_unref(G_OBJECT(folder2));
1860 /*****************************************************************************/
1861 /* DRAG and DROP stuff */
1862 /*****************************************************************************/
1864 * This function fills the #GtkSelectionData with the row and the
1865 * model that has been dragged. It's called when this widget is a
1866 * source for dnd after the event drop happened
1869 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1870 guint info, guint time, gpointer data)
1872 GtkTreeSelection *selection;
1873 GtkTreeModel *model;
1875 GtkTreePath *source_row;
1877 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1878 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1880 source_row = gtk_tree_model_get_path (model, &iter);
1881 gtk_tree_set_row_drag_data (selection_data,
1885 gtk_tree_path_free (source_row);
1889 typedef struct _DndHelper {
1890 gboolean delete_source;
1891 GtkTreePath *source_row;
1892 GdkDragContext *context;
1897 dnd_helper_destroyer (DndHelper *helper)
1899 /* Free the helper */
1900 gtk_tree_path_free (helper->source_row);
1901 g_slice_free (DndHelper, helper);
1905 * This function is the callback of the
1906 * modest_mail_operation_xfer_msgs () and
1907 * modest_mail_operation_xfer_folder() calls. We check here if the
1908 * message/folder was correctly asynchronously transferred. The reason
1909 * to use the same callback is that the code is the same, it only has
1910 * to check that the operation went fine and then finalize the drag
1914 xfer_cb (ModestMailOperation *mail_op,
1920 helper = (DndHelper *) user_data;
1922 if (modest_mail_operation_get_status (mail_op) ==
1923 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1929 /* Notify the drag source. Never call delete, the monitor will
1930 do the job if needed */
1931 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1933 /* Free the helper */
1934 dnd_helper_destroyer (helper);
1937 /* get the folder for the row the treepath refers to. */
1938 /* folder must be unref'd */
1939 static TnyFolderStore *
1940 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1943 TnyFolderStore *folder = NULL;
1945 if (gtk_tree_model_get_iter (model,&iter, path))
1946 gtk_tree_model_get (model, &iter,
1947 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1953 * This function is used by drag_data_received_cb to manage drag and
1954 * drop of a header, i.e, and drag from the header view to the folder
1958 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1959 GtkTreeModel *dest_model,
1960 GtkTreePath *dest_row,
1961 GtkSelectionData *selection_data,
1964 TnyList *headers = NULL;
1965 TnyFolder *folder = NULL;
1966 TnyFolderType folder_type;
1967 ModestMailOperation *mail_op = NULL;
1968 GtkTreeIter source_iter, dest_iter;
1969 ModestWindowMgr *mgr = NULL;
1970 ModestWindow *main_win = NULL;
1971 gchar **uris, **tmp;
1974 /* Build the list of headers */
1975 mgr = modest_runtime_get_window_mgr ();
1976 headers = tny_simple_list_new ();
1977 uris = modest_dnd_selection_data_get_paths (selection_data);
1980 while (*tmp != NULL) {
1985 path = gtk_tree_path_new_from_string (*tmp);
1986 gtk_tree_model_get_iter (source_model, &source_iter, path);
1987 gtk_tree_model_get (source_model, &source_iter,
1988 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1991 /* Do not enable d&d of headers already opened */
1992 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
1993 tny_list_append (headers, G_OBJECT (header));
1995 /* Free and go on */
1996 gtk_tree_path_free (path);
1997 g_object_unref (header);
2002 /* Get the target folder */
2003 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2004 gtk_tree_model_get (dest_model, &dest_iter,
2005 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2008 if (!folder || !TNY_IS_FOLDER(folder)) {
2009 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2013 folder_type = modest_tny_folder_guess_folder_type (folder);
2014 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2015 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2016 goto cleanup; /* cannot move messages there */
2019 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2020 /* g_warning ("folder not writable"); */
2021 goto cleanup; /* verboten! */
2024 /* Ask for confirmation to move */
2025 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2027 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2031 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2033 if (response == GTK_RESPONSE_CANCEL)
2036 /* Transfer messages */
2037 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2038 modest_ui_actions_move_folder_error_handler,
2041 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2044 modest_mail_operation_xfer_msgs (mail_op,
2047 helper->delete_source,
2052 if (G_IS_OBJECT(mail_op))
2053 g_object_unref (G_OBJECT (mail_op));
2054 if (G_IS_OBJECT(folder))
2055 g_object_unref (G_OBJECT (folder));
2056 if (G_IS_OBJECT(headers))
2057 g_object_unref (headers);
2061 TnyFolderStore *src_folder;
2062 TnyFolderStore *dst_folder;
2067 dnd_folder_info_destroyer (DndFolderInfo *info)
2069 if (info->src_folder)
2070 g_object_unref (info->src_folder);
2071 if (info->dst_folder)
2072 g_object_unref (info->dst_folder);
2073 g_slice_free (DndFolderInfo, info);
2077 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2078 GtkWindow *parent_window,
2079 TnyAccount *account)
2081 time_t dnd_time = info->helper->time;
2082 GdkDragContext *context = info->helper->context;
2085 modest_ui_actions_on_account_connection_error (parent_window, account);
2087 /* Free the helper & info */
2088 dnd_helper_destroyer (info->helper);
2089 dnd_folder_info_destroyer (info);
2091 /* Notify the drag source. Never call delete, the monitor will
2092 do the job if needed */
2093 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2098 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2100 GtkWindow *parent_window,
2101 TnyAccount *account,
2104 DndFolderInfo *info = NULL;
2105 ModestMailOperation *mail_op;
2107 info = (DndFolderInfo *) user_data;
2109 if (err || canceled) {
2110 dnd_on_connection_failed_destroyer (info, parent_window, account);
2114 /* Do the mail operation */
2115 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2116 modest_ui_actions_move_folder_error_handler,
2117 info->src_folder, NULL);
2119 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2122 /* Transfer the folder */
2123 modest_mail_operation_xfer_folder (mail_op,
2124 TNY_FOLDER (info->src_folder),
2126 info->helper->delete_source,
2130 g_object_unref (G_OBJECT (mail_op));
2135 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2137 GtkWindow *parent_window,
2138 TnyAccount *account,
2141 DndFolderInfo *info = NULL;
2143 info = (DndFolderInfo *) user_data;
2145 if (err || canceled) {
2146 dnd_on_connection_failed_destroyer (info, parent_window, account);
2150 /* Connect to source folder and perform the copy/move */
2151 modest_platform_connect_if_remote_and_perform (NULL,
2153 drag_and_drop_from_folder_view_src_folder_performer,
2158 * This function is used by drag_data_received_cb to manage drag and
2159 * drop of a folder, i.e, and drag from the folder view to the same
2163 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2164 GtkTreeModel *dest_model,
2165 GtkTreePath *dest_row,
2166 GtkSelectionData *selection_data,
2169 GtkTreeIter dest_iter, iter;
2170 TnyFolderStore *dest_folder = NULL;
2171 TnyFolderStore *folder = NULL;
2172 gboolean forbidden = FALSE;
2174 DndFolderInfo *info = NULL;
2176 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2178 g_warning ("%s: BUG: no main window", __FUNCTION__);
2183 /* check the folder rules for the destination */
2184 folder = tree_path_to_folder (dest_model, dest_row);
2185 if (TNY_IS_FOLDER(folder)) {
2186 ModestTnyFolderRules rules =
2187 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2188 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2189 } else if (TNY_IS_FOLDER_STORE(folder)) {
2190 /* enable local root as destination for folders */
2191 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
2192 && TNY_IS_ACCOUNT (folder))
2195 g_object_unref (folder);
2198 /* check the folder rules for the source */
2199 folder = tree_path_to_folder (source_model, helper->source_row);
2200 if (TNY_IS_FOLDER(folder)) {
2201 ModestTnyFolderRules rules =
2202 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2203 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2206 g_object_unref (folder);
2210 /* Check if the drag is possible */
2211 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2212 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2213 gtk_tree_path_free (helper->source_row);
2214 g_slice_free (DndHelper, helper);
2219 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2220 gtk_tree_model_get (dest_model, &dest_iter,
2221 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2223 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2224 gtk_tree_model_get (source_model, &iter,
2225 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2228 /* Create the info for the performer */
2229 info = g_slice_new (DndFolderInfo);
2230 info->src_folder = g_object_ref (folder);
2231 info->dst_folder = g_object_ref (dest_folder);
2232 info->helper = helper;
2234 /* Connect to the destination folder and perform the copy/move */
2235 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win),
2237 drag_and_drop_from_folder_view_dst_folder_performer,
2241 g_object_unref (dest_folder);
2242 g_object_unref (folder);
2246 * This function receives the data set by the "drag-data-get" signal
2247 * handler. This information comes within the #GtkSelectionData. This
2248 * function will manage both the drags of folders of the treeview and
2249 * drags of headers of the header view widget.
2252 on_drag_data_received (GtkWidget *widget,
2253 GdkDragContext *context,
2256 GtkSelectionData *selection_data,
2261 GtkWidget *source_widget;
2262 GtkTreeModel *dest_model, *source_model;
2263 GtkTreePath *source_row, *dest_row;
2264 GtkTreeViewDropPosition pos;
2265 gboolean success = FALSE, delete_source = FALSE;
2266 DndHelper *helper = NULL;
2268 /* Do not allow further process */
2269 g_signal_stop_emission_by_name (widget, "drag-data-received");
2270 source_widget = gtk_drag_get_source_widget (context);
2272 /* Get the action */
2273 if (context->action == GDK_ACTION_MOVE) {
2274 delete_source = TRUE;
2276 /* Notify that there is no folder selected. We need to
2277 do this in order to update the headers view (and
2278 its monitors, because when moving, the old folder
2279 won't longer exist. We can not wait for the end of
2280 the operation, because the operation won't start if
2281 the folder is in use */
2282 if (source_widget == widget) {
2283 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2284 gtk_tree_selection_unselect_all (sel);
2288 /* Check if the get_data failed */
2289 if (selection_data == NULL || selection_data->length < 0)
2290 gtk_drag_finish (context, success, FALSE, time);
2292 /* Select the destination model */
2293 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2295 /* Get the path to the destination row. Can not call
2296 gtk_tree_view_get_drag_dest_row() because the source row
2297 is not selected anymore */
2298 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2301 /* Only allow drops IN other rows */
2303 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2304 pos == GTK_TREE_VIEW_DROP_AFTER)
2305 gtk_drag_finish (context, success, FALSE, time);
2307 /* Create the helper */
2308 helper = g_slice_new0 (DndHelper);
2309 helper->delete_source = delete_source;
2310 helper->context = context;
2311 helper->time = time;
2313 /* Drags from the header view */
2314 if (source_widget != widget) {
2315 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2317 drag_and_drop_from_header_view (source_model,
2323 /* Get the source model and row */
2324 gtk_tree_get_row_drag_data (selection_data,
2327 helper->source_row = gtk_tree_path_copy (source_row);
2329 drag_and_drop_from_folder_view (source_model,
2335 gtk_tree_path_free (source_row);
2339 gtk_tree_path_free (dest_row);
2343 * We define a "drag-drop" signal handler because we do not want to
2344 * use the default one, because the default one always calls
2345 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2346 * signal handler, because there we have all the information available
2347 * to know if the dnd was a success or not.
2350 drag_drop_cb (GtkWidget *widget,
2351 GdkDragContext *context,
2359 if (!context->targets)
2362 /* Check if we're dragging a folder row */
2363 target = gtk_drag_dest_find_target (widget, context, NULL);
2365 /* Request the data from the source. */
2366 gtk_drag_get_data(widget, context, target, time);
2372 * This function expands a node of a tree view if it's not expanded
2373 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2374 * does that, so that's why they're here.
2377 expand_row_timeout (gpointer data)
2379 GtkTreeView *tree_view = data;
2380 GtkTreePath *dest_path = NULL;
2381 GtkTreeViewDropPosition pos;
2382 gboolean result = FALSE;
2384 gdk_threads_enter ();
2386 gtk_tree_view_get_drag_dest_row (tree_view,
2391 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2392 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2393 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2394 gtk_tree_path_free (dest_path);
2398 gtk_tree_path_free (dest_path);
2403 gdk_threads_leave ();
2409 * This function is called whenever the pointer is moved over a widget
2410 * while dragging some data. It installs a timeout that will expand a
2411 * node of the treeview if not expanded yet. This function also calls
2412 * gdk_drag_status in order to set the suggested action that will be
2413 * used by the "drag-data-received" signal handler to know if we
2414 * should do a move or just a copy of the data.
2417 on_drag_motion (GtkWidget *widget,
2418 GdkDragContext *context,
2424 GtkTreeViewDropPosition pos;
2425 GtkTreePath *dest_row;
2426 GtkTreeModel *dest_model;
2427 ModestFolderViewPrivate *priv;
2428 GdkDragAction suggested_action;
2429 gboolean valid_location = FALSE;
2430 TnyFolderStore *folder = NULL;
2432 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2434 if (priv->timer_expander != 0) {
2435 g_source_remove (priv->timer_expander);
2436 priv->timer_expander = 0;
2439 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2444 /* Do not allow drops between folders */
2446 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2447 pos == GTK_TREE_VIEW_DROP_AFTER) {
2448 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2449 gdk_drag_status(context, 0, time);
2450 valid_location = FALSE;
2453 valid_location = TRUE;
2456 /* Check that the destination folder is writable */
2457 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2458 folder = tree_path_to_folder (dest_model, dest_row);
2459 if (folder && TNY_IS_FOLDER (folder)) {
2460 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2462 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2463 valid_location = FALSE;
2468 /* Expand the selected row after 1/2 second */
2469 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2470 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2471 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2474 /* Select the desired action. By default we pick MOVE */
2475 suggested_action = GDK_ACTION_MOVE;
2477 if (context->actions == GDK_ACTION_COPY)
2478 gdk_drag_status(context, GDK_ACTION_COPY, time);
2479 else if (context->actions == GDK_ACTION_MOVE)
2480 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2481 else if (context->actions & suggested_action)
2482 gdk_drag_status(context, suggested_action, time);
2484 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2488 g_object_unref (folder);
2490 gtk_tree_path_free (dest_row);
2491 g_signal_stop_emission_by_name (widget, "drag-motion");
2493 return valid_location;
2497 * This function sets the treeview as a source and a target for dnd
2498 * events. It also connects all the requirede signals.
2501 setup_drag_and_drop (GtkTreeView *self)
2503 /* Set up the folder view as a dnd destination. Set only the
2504 highlight flag, otherwise gtk will have a different
2506 gtk_drag_dest_set (GTK_WIDGET (self),
2507 GTK_DEST_DEFAULT_HIGHLIGHT,
2508 folder_view_drag_types,
2509 G_N_ELEMENTS (folder_view_drag_types),
2510 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2512 g_signal_connect (G_OBJECT (self),
2513 "drag_data_received",
2514 G_CALLBACK (on_drag_data_received),
2518 /* Set up the treeview as a dnd source */
2519 gtk_drag_source_set (GTK_WIDGET (self),
2521 folder_view_drag_types,
2522 G_N_ELEMENTS (folder_view_drag_types),
2523 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2525 g_signal_connect (G_OBJECT (self),
2527 G_CALLBACK (on_drag_motion),
2530 g_signal_connect (G_OBJECT (self),
2532 G_CALLBACK (on_drag_data_get),
2535 g_signal_connect (G_OBJECT (self),
2537 G_CALLBACK (drag_drop_cb),
2542 * This function manages the navigation through the folders using the
2543 * keyboard or the hardware keys in the device
2546 on_key_pressed (GtkWidget *self,
2550 GtkTreeSelection *selection;
2552 GtkTreeModel *model;
2553 gboolean retval = FALSE;
2555 /* Up and Down are automatically managed by the treeview */
2556 if (event->keyval == GDK_Return) {
2557 /* Expand/Collapse the selected row */
2558 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2559 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2562 path = gtk_tree_model_get_path (model, &iter);
2564 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2565 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2567 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2568 gtk_tree_path_free (path);
2570 /* No further processing */
2578 * We listen to the changes in the local folder account name key,
2579 * because we want to show the right name in the view. The local
2580 * folder account name corresponds to the device name in the Maemo
2581 * version. We do this because we do not want to query gconf on each
2582 * tree view refresh. It's better to cache it and change whenever
2586 on_configuration_key_changed (ModestConf* conf,
2588 ModestConfEvent event,
2589 ModestConfNotificationId id,
2590 ModestFolderView *self)
2592 ModestFolderViewPrivate *priv;
2595 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2596 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2598 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2599 g_free (priv->local_account_name);
2601 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2602 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2604 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2605 MODEST_CONF_DEVICE_NAME, NULL);
2607 /* Force a redraw */
2608 #if GTK_CHECK_VERSION(2, 8, 0)
2609 GtkTreeViewColumn * tree_column;
2611 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2612 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2613 gtk_tree_view_column_queue_resize (tree_column);
2615 gtk_widget_queue_draw (GTK_WIDGET (self));
2621 modest_folder_view_set_style (ModestFolderView *self,
2622 ModestFolderViewStyle style)
2624 ModestFolderViewPrivate *priv;
2626 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2627 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2628 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2630 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2633 priv->style = style;
2637 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2638 const gchar *account_id)
2640 ModestFolderViewPrivate *priv;
2641 GtkTreeModel *model;
2643 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2645 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2647 /* This will be used by the filter_row callback,
2648 * to decided which rows to show: */
2649 if (priv->visible_account_id) {
2650 g_free (priv->visible_account_id);
2651 priv->visible_account_id = NULL;
2654 priv->visible_account_id = g_strdup (account_id);
2657 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2658 if (GTK_IS_TREE_MODEL_FILTER (model))
2659 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2661 /* Save settings to gconf */
2662 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2663 MODEST_CONF_FOLDER_VIEW_KEY);
2667 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2669 ModestFolderViewPrivate *priv;
2671 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2673 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2675 return (const gchar *) priv->visible_account_id;
2679 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2683 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2685 gtk_tree_model_get (model, iter,
2686 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2689 gboolean result = FALSE;
2690 if (type == TNY_FOLDER_TYPE_INBOX) {
2694 *inbox_iter = *iter;
2698 if (gtk_tree_model_iter_children (model, &child, iter)) {
2699 if (find_inbox_iter (model, &child, inbox_iter))
2703 } while (gtk_tree_model_iter_next (model, iter));
2712 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2714 GtkTreeModel *model;
2715 GtkTreeIter iter, inbox_iter;
2716 GtkTreeSelection *sel;
2717 GtkTreePath *path = NULL;
2719 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2721 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2725 expand_root_items (self);
2726 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2728 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2729 g_warning ("%s: model is empty", __FUNCTION__);
2733 if (find_inbox_iter (model, &iter, &inbox_iter))
2734 path = gtk_tree_model_get_path (model, &inbox_iter);
2736 path = gtk_tree_path_new_first ();
2738 /* Select the row and free */
2739 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2740 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2741 gtk_tree_path_free (path);
2744 gtk_widget_grab_focus (GTK_WIDGET(self));
2750 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2755 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2756 TnyFolder* a_folder;
2759 gtk_tree_model_get (model, iter,
2760 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2761 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2762 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2766 if (folder == a_folder) {
2767 g_object_unref (a_folder);
2768 *folder_iter = *iter;
2771 g_object_unref (a_folder);
2773 if (gtk_tree_model_iter_children (model, &child, iter)) {
2774 if (find_folder_iter (model, &child, folder_iter, folder))
2778 } while (gtk_tree_model_iter_next (model, iter));
2785 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2786 ModestFolderView *self)
2788 ModestFolderViewPrivate *priv = NULL;
2789 GtkTreeSelection *sel;
2790 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2791 GObject *instance = NULL;
2793 if (!MODEST_IS_FOLDER_VIEW(self))
2796 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2798 priv->reexpand = TRUE;
2800 gtk_tree_model_get (tree_model, iter,
2801 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2802 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2804 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2805 priv->folder_to_select = g_object_ref (instance);
2807 g_object_unref (instance);
2810 if (priv->folder_to_select) {
2812 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2815 path = gtk_tree_model_get_path (tree_model, iter);
2816 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2818 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2820 gtk_tree_selection_select_iter (sel, iter);
2821 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2823 gtk_tree_path_free (path);
2828 modest_folder_view_disable_next_folder_selection (self);
2829 /* g_object_unref (priv->folder_to_select); */
2830 /* priv->folder_to_select = NULL; */
2832 /* Refilter the model */
2833 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2839 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2841 ModestFolderViewPrivate *priv;
2843 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2845 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2847 if (priv->folder_to_select)
2848 g_object_unref(priv->folder_to_select);
2850 priv->folder_to_select = NULL;
2854 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2855 gboolean after_change)
2857 GtkTreeModel *model;
2858 GtkTreeIter iter, folder_iter;
2859 GtkTreeSelection *sel;
2860 ModestFolderViewPrivate *priv = NULL;
2862 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2863 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2865 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2868 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2869 gtk_tree_selection_unselect_all (sel);
2871 if (priv->folder_to_select)
2872 g_object_unref(priv->folder_to_select);
2873 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2877 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2881 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2882 g_warning ("%s: model is empty", __FUNCTION__);
2886 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2889 path = gtk_tree_model_get_path (model, &folder_iter);
2890 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2892 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2893 gtk_tree_selection_select_iter (sel, &folder_iter);
2894 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2896 gtk_tree_path_free (path);
2904 modest_folder_view_copy_selection (ModestFolderView *self)
2906 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2908 /* Copy selection */
2909 _clipboard_set_selected_data (self, FALSE);
2913 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2915 ModestFolderViewPrivate *priv = NULL;
2916 GtkTreeModel *model = NULL;
2917 const gchar **hidding = NULL;
2918 guint i, n_selected;
2920 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2921 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2923 /* Copy selection */
2924 if (!_clipboard_set_selected_data (folder_view, TRUE))
2927 /* Get hidding ids */
2928 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2930 /* Clear hidding array created by previous cut operation */
2931 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2933 /* Copy hidding array */
2934 priv->n_selected = n_selected;
2935 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2936 for (i=0; i < n_selected; i++)
2937 priv->hidding_ids[i] = g_strdup(hidding[i]);
2939 /* Hide cut folders */
2940 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2941 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2945 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2946 ModestFolderView *folder_view_dst)
2948 GtkTreeModel *filter_model = NULL;
2949 GtkTreeModel *model = NULL;
2950 GtkTreeModel *new_filter_model = NULL;
2952 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
2953 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
2956 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2957 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2959 /* Build new filter model */
2960 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2961 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2965 /* Set copied model */
2966 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2967 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
2968 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
2971 g_object_unref (new_filter_model);
2975 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2978 GtkTreeModel *model = NULL;
2979 ModestFolderViewPrivate* priv;
2981 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2983 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2984 priv->show_non_move = show;
2985 /* modest_folder_view_update_model(folder_view, */
2986 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2988 /* Hide special folders */
2989 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2990 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2991 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2995 /* Returns FALSE if it did not selected anything */
2997 _clipboard_set_selected_data (ModestFolderView *folder_view,
3000 ModestFolderViewPrivate *priv = NULL;
3001 TnyFolderStore *folder = NULL;
3002 gboolean retval = FALSE;
3004 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3005 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3007 /* Set selected data on clipboard */
3008 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3009 folder = modest_folder_view_get_selected (folder_view);
3011 /* Do not allow to select an account */
3012 if (TNY_IS_FOLDER (folder)) {
3013 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3018 g_object_unref (folder);
3024 _clear_hidding_filter (ModestFolderView *folder_view)
3026 ModestFolderViewPrivate *priv;
3029 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3030 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3032 if (priv->hidding_ids != NULL) {
3033 for (i=0; i < priv->n_selected; i++)
3034 g_free (priv->hidding_ids[i]);
3035 g_free(priv->hidding_ids);
3041 on_display_name_changed (ModestAccountMgr *mgr,
3042 const gchar *account,
3045 ModestFolderView *self;
3047 self = MODEST_FOLDER_VIEW (user_data);
3049 /* Force a redraw */
3050 #if GTK_CHECK_VERSION(2, 8, 0)
3051 GtkTreeViewColumn * tree_column;
3053 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3054 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3055 gtk_tree_view_column_queue_resize (tree_column);
3057 gtk_widget_queue_draw (GTK_WIDGET (self));