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 GtkTreeViewClass *treeview_class;
245 gobject_class = (GObjectClass*) klass;
246 treeview_class = (GtkTreeViewClass*) klass;
248 parent_class = g_type_class_peek_parent (klass);
249 gobject_class->finalize = modest_folder_view_finalize;
251 g_type_class_add_private (gobject_class,
252 sizeof(ModestFolderViewPrivate));
254 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
255 g_signal_new ("folder_selection_changed",
256 G_TYPE_FROM_CLASS (gobject_class),
258 G_STRUCT_OFFSET (ModestFolderViewClass,
259 folder_selection_changed),
261 modest_marshal_VOID__POINTER_BOOLEAN,
262 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
265 * This signal is emitted whenever the currently selected
266 * folder display name is computed. Note that the name could
267 * be different to the folder name, because we could append
268 * the unread messages count to the folder name to build the
269 * folder display name
271 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
272 g_signal_new ("folder-display-name-changed",
273 G_TYPE_FROM_CLASS (gobject_class),
275 G_STRUCT_OFFSET (ModestFolderViewClass,
276 folder_display_name_changed),
278 g_cclosure_marshal_VOID__STRING,
279 G_TYPE_NONE, 1, G_TYPE_STRING);
281 treeview_class->select_cursor_parent = NULL;
285 /* Simplify checks for NULLs: */
287 strings_are_equal (const gchar *a, const gchar *b)
293 return (strcmp (a, b) == 0);
300 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
302 GObject *instance = NULL;
304 gtk_tree_model_get (model, iter,
305 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
309 return FALSE; /* keep walking */
311 if (!TNY_IS_ACCOUNT (instance)) {
312 g_object_unref (instance);
313 return FALSE; /* keep walking */
316 /* Check if this is the looked-for account: */
317 TnyAccount *this_account = TNY_ACCOUNT (instance);
318 TnyAccount *account = TNY_ACCOUNT (data);
320 const gchar *this_account_id = tny_account_get_id(this_account);
321 const gchar *account_id = tny_account_get_id(account);
322 g_object_unref (instance);
325 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
326 if (strings_are_equal(this_account_id, account_id)) {
327 /* Tell the model that the data has changed, so that
328 * it calls the cell_data_func callbacks again: */
329 /* TODO: This does not seem to actually cause the new string to be shown: */
330 gtk_tree_model_row_changed (model, path, iter);
332 return TRUE; /* stop walking */
335 return FALSE; /* keep walking */
340 ModestFolderView *self;
341 gchar *previous_name;
342 } GetMmcAccountNameData;
345 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
347 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
349 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
351 if (!strings_are_equal (
352 tny_account_get_name(TNY_ACCOUNT(account)),
353 data->previous_name)) {
355 /* Tell the model that the data has changed, so that
356 * it calls the cell_data_func callbacks again: */
357 ModestFolderView *self = data->self;
358 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
360 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
363 g_free (data->previous_name);
364 g_slice_free (GetMmcAccountNameData, data);
368 text_cell_data (GtkTreeViewColumn *column,
369 GtkCellRenderer *renderer,
370 GtkTreeModel *tree_model,
374 ModestFolderViewPrivate *priv;
375 GObject *rendobj = (GObject *) renderer;
377 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
378 GObject *instance = NULL;
380 gtk_tree_model_get (tree_model, iter,
381 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
382 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
383 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
385 if (!fname || !instance)
388 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
389 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
391 gchar *item_name = NULL;
392 gint item_weight = 400;
394 if (type != TNY_FOLDER_TYPE_ROOT) {
397 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
398 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
399 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
400 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
402 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
406 /* note: we cannot reliably get the counts from the tree model, we need
407 * to use explicit calls on tny_folder for some reason.
409 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
410 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
411 (type == TNY_FOLDER_TYPE_OUTBOX) ||
412 (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
413 number = tny_folder_get_all_count (TNY_FOLDER(instance));
415 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
417 /* Use bold font style if there are unread or unset messages */
419 if (type == TNY_FOLDER_TYPE_INBOX)
420 item_name = g_strdup_printf ("%s (%d)", _("mcen_me_folder_inbox"), number);
422 item_name = g_strdup_printf ("%s (%d)", fname, number);
425 if (type == TNY_FOLDER_TYPE_INBOX)
426 item_name = g_strdup (_("mcen_me_folder_inbox"));
428 item_name = g_strdup (fname);
432 } else if (TNY_IS_ACCOUNT (instance)) {
433 /* If it's a server account */
434 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
435 item_name = g_strdup (priv->local_account_name);
437 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
438 /* fname is only correct when the items are first
439 * added to the model, not when the account is
440 * changed later, so get the name from the account
442 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
445 item_name = g_strdup (fname);
451 item_name = g_strdup ("unknown");
453 if (item_name && item_weight) {
454 /* Set the name in the treeview cell: */
455 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
457 /* Notify display name observers */
458 /* TODO: What listens for this signal, and how can it use only the new name? */
459 if (((GObject *) priv->cur_folder_store) == instance) {
460 g_signal_emit (G_OBJECT(self),
461 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
468 /* If it is a Memory card account, make sure that we have the correct name.
469 * This function will be trigerred again when the name has been retrieved: */
470 if (TNY_IS_STORE_ACCOUNT (instance) &&
471 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
473 /* Get the account name asynchronously: */
474 GetMmcAccountNameData *callback_data =
475 g_slice_new0(GetMmcAccountNameData);
476 callback_data->self = self;
478 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
480 callback_data->previous_name = g_strdup (name);
482 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
483 on_get_mmc_account_name, callback_data);
487 g_object_unref (G_OBJECT (instance));
495 GdkPixbuf *pixbuf_open;
496 GdkPixbuf *pixbuf_close;
501 get_folder_icons (TnyFolderType type, GObject *instance)
503 GdkPixbuf *pixbuf = NULL;
504 GdkPixbuf *pixbuf_open = NULL;
505 GdkPixbuf *pixbuf_close = NULL;
506 ThreePixbufs *retval = g_slice_new (ThreePixbufs);
508 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
509 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
510 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
511 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
512 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
514 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
515 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
516 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
517 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
518 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
520 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
521 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
522 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
523 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
524 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
527 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
528 /* We include the MERGE type here because it's used to create
529 the local OUTBOX folder */
530 if (type == TNY_FOLDER_TYPE_NORMAL ||
531 type == TNY_FOLDER_TYPE_UNKNOWN) {
532 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
536 case TNY_FOLDER_TYPE_INVALID:
537 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
540 case TNY_FOLDER_TYPE_ROOT:
541 if (TNY_IS_ACCOUNT (instance)) {
543 if (modest_tny_account_is_virtual_local_folders (
544 TNY_ACCOUNT (instance))) {
547 avirt_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
548 MODEST_ICON_SIZE_SMALL));
550 if (!avirt_pixbuf_open) {
551 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
552 MODEST_ICON_SIZE_SMALL);
553 avirt_pixbuf_open = gdk_pixbuf_copy (avirt_pixbuf);
554 gdk_pixbuf_composite (emblem, avirt_pixbuf_open, 0, 0,
555 MIN (gdk_pixbuf_get_width (emblem),
556 gdk_pixbuf_get_width (avirt_pixbuf_open)),
557 MIN (gdk_pixbuf_get_height (emblem),
558 gdk_pixbuf_get_height (avirt_pixbuf_open)),
559 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
560 g_object_unref (emblem);
563 if (!avirt_pixbuf_close) {
564 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
565 MODEST_ICON_SIZE_SMALL);
566 avirt_pixbuf_close = gdk_pixbuf_copy (avirt_pixbuf);
567 gdk_pixbuf_composite (emblem, avirt_pixbuf_close, 0, 0,
568 MIN (gdk_pixbuf_get_width (emblem),
569 gdk_pixbuf_get_width (avirt_pixbuf_close)),
570 MIN (gdk_pixbuf_get_height (emblem),
571 gdk_pixbuf_get_height (avirt_pixbuf_close)),
572 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
573 g_object_unref (emblem);
577 pixbuf = g_object_ref (avirt_pixbuf);
578 pixbuf_open = g_object_ref (avirt_pixbuf_open);
579 pixbuf_close = g_object_ref (avirt_pixbuf_close);
583 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
585 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
587 ammc_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_MMC,
588 MODEST_ICON_SIZE_SMALL));
590 if (!ammc_pixbuf_open) {
591 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
592 MODEST_ICON_SIZE_SMALL);
593 ammc_pixbuf_open = gdk_pixbuf_copy (ammc_pixbuf);
594 gdk_pixbuf_composite (emblem, ammc_pixbuf_open, 0, 0,
595 MIN (gdk_pixbuf_get_width (emblem),
596 gdk_pixbuf_get_width (ammc_pixbuf_open)),
597 MIN (gdk_pixbuf_get_height (emblem),
598 gdk_pixbuf_get_height (ammc_pixbuf_open)),
599 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
600 g_object_unref (emblem);
603 if (!ammc_pixbuf_close) {
604 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
605 MODEST_ICON_SIZE_SMALL);
606 ammc_pixbuf_close = gdk_pixbuf_copy (ammc_pixbuf);
607 gdk_pixbuf_composite (emblem, ammc_pixbuf_close, 0, 0,
608 MIN (gdk_pixbuf_get_width (emblem),
609 gdk_pixbuf_get_width (ammc_pixbuf_close)),
610 MIN (gdk_pixbuf_get_height (emblem),
611 gdk_pixbuf_get_height (ammc_pixbuf_close)),
612 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
613 g_object_unref (emblem);
617 pixbuf = g_object_ref (ammc_pixbuf);
618 pixbuf_open = g_object_ref (ammc_pixbuf_open);
619 pixbuf_close = g_object_ref (ammc_pixbuf_close);
624 anorm_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT,
625 MODEST_ICON_SIZE_SMALL));
626 if (!anorm_pixbuf_open) {
627 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
628 MODEST_ICON_SIZE_SMALL);
629 anorm_pixbuf_open = gdk_pixbuf_copy (anorm_pixbuf);
630 gdk_pixbuf_composite (emblem, anorm_pixbuf_open, 0, 0,
631 MIN (gdk_pixbuf_get_width (emblem),
632 gdk_pixbuf_get_width (anorm_pixbuf_open)),
633 MIN (gdk_pixbuf_get_height (emblem),
634 gdk_pixbuf_get_height (anorm_pixbuf_open)),
635 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
636 g_object_unref (emblem);
639 if (!anorm_pixbuf_close) {
640 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
641 MODEST_ICON_SIZE_SMALL);
642 anorm_pixbuf_close = gdk_pixbuf_copy (anorm_pixbuf);
643 gdk_pixbuf_composite (emblem, anorm_pixbuf_close, 0, 0,
644 MIN (gdk_pixbuf_get_width (emblem),
645 gdk_pixbuf_get_width (anorm_pixbuf_close)),
646 MIN (gdk_pixbuf_get_height (emblem),
647 gdk_pixbuf_get_height (anorm_pixbuf_close)),
648 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
649 g_object_unref (emblem);
653 pixbuf = g_object_ref (anorm_pixbuf);
654 pixbuf_open = g_object_ref (anorm_pixbuf_open);
655 pixbuf_close = g_object_ref (anorm_pixbuf_close);
661 case TNY_FOLDER_TYPE_INBOX:
664 inbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX,
665 MODEST_ICON_SIZE_SMALL));
667 if (!inbox_pixbuf_open) {
668 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
669 MODEST_ICON_SIZE_SMALL);
670 inbox_pixbuf_open = gdk_pixbuf_copy (inbox_pixbuf);
671 gdk_pixbuf_composite (emblem, inbox_pixbuf_open, 0, 0,
672 MIN (gdk_pixbuf_get_width (emblem),
673 gdk_pixbuf_get_width (inbox_pixbuf_open)),
674 MIN (gdk_pixbuf_get_height (emblem),
675 gdk_pixbuf_get_height (inbox_pixbuf_open)),
676 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
677 g_object_unref (emblem);
680 if (!inbox_pixbuf_close) {
681 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
682 MODEST_ICON_SIZE_SMALL);
683 inbox_pixbuf_close = gdk_pixbuf_copy (inbox_pixbuf);
684 gdk_pixbuf_composite (emblem, inbox_pixbuf_close, 0, 0,
685 MIN (gdk_pixbuf_get_width (emblem),
686 gdk_pixbuf_get_width (inbox_pixbuf_close)),
687 MIN (gdk_pixbuf_get_height (emblem),
688 gdk_pixbuf_get_height (inbox_pixbuf_close)),
689 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
690 g_object_unref (emblem);
694 pixbuf = g_object_ref (inbox_pixbuf);
695 pixbuf_open = g_object_ref (inbox_pixbuf_open);
696 pixbuf_close = g_object_ref (inbox_pixbuf_close);
699 case TNY_FOLDER_TYPE_OUTBOX:
701 outbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX,
702 MODEST_ICON_SIZE_SMALL));
704 if (!outbox_pixbuf_open) {
705 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
706 MODEST_ICON_SIZE_SMALL);
707 outbox_pixbuf_open = gdk_pixbuf_copy (outbox_pixbuf);
708 gdk_pixbuf_composite (emblem, outbox_pixbuf_open, 0, 0,
709 MIN (gdk_pixbuf_get_width (emblem),
710 gdk_pixbuf_get_width (outbox_pixbuf_open)),
711 MIN (gdk_pixbuf_get_height (emblem),
712 gdk_pixbuf_get_height (outbox_pixbuf_open)),
713 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
714 g_object_unref (emblem);
717 if (!outbox_pixbuf_close) {
718 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
719 MODEST_ICON_SIZE_SMALL);
720 outbox_pixbuf_close = gdk_pixbuf_copy (outbox_pixbuf);
721 gdk_pixbuf_composite (emblem, outbox_pixbuf_close, 0, 0,
722 MIN (gdk_pixbuf_get_width (emblem),
723 gdk_pixbuf_get_width (outbox_pixbuf_close)),
724 MIN (gdk_pixbuf_get_height (emblem),
725 gdk_pixbuf_get_height (outbox_pixbuf_close)),
726 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
727 g_object_unref (emblem);
731 pixbuf = g_object_ref (outbox_pixbuf);
732 pixbuf_open = g_object_ref (outbox_pixbuf_open);
733 pixbuf_close = g_object_ref (outbox_pixbuf_close);
736 case TNY_FOLDER_TYPE_JUNK:
738 junk_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK,
739 MODEST_ICON_SIZE_SMALL));
740 if (!junk_pixbuf_open) {
741 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
742 MODEST_ICON_SIZE_SMALL);
743 junk_pixbuf_open = gdk_pixbuf_copy (junk_pixbuf);
744 gdk_pixbuf_composite (emblem, junk_pixbuf_open, 0, 0,
745 MIN (gdk_pixbuf_get_width (emblem),
746 gdk_pixbuf_get_width (junk_pixbuf_open)),
747 MIN (gdk_pixbuf_get_height (emblem),
748 gdk_pixbuf_get_height (junk_pixbuf_open)),
749 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
750 g_object_unref (emblem);
753 if (!junk_pixbuf_close) {
754 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
755 MODEST_ICON_SIZE_SMALL);
756 junk_pixbuf_close = gdk_pixbuf_copy (junk_pixbuf);
757 gdk_pixbuf_composite (emblem, junk_pixbuf_close, 0, 0,
758 MIN (gdk_pixbuf_get_width (emblem),
759 gdk_pixbuf_get_width (junk_pixbuf_close)),
760 MIN (gdk_pixbuf_get_height (emblem),
761 gdk_pixbuf_get_height (junk_pixbuf_close)),
762 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
763 g_object_unref (emblem);
767 pixbuf = g_object_ref (junk_pixbuf);
768 pixbuf_open = g_object_ref (junk_pixbuf_open);
769 pixbuf_close = g_object_ref (junk_pixbuf_close);
773 case TNY_FOLDER_TYPE_SENT:
775 sent_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_SENT,
776 MODEST_ICON_SIZE_SMALL));
778 if (!sent_pixbuf_open) {
779 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
780 MODEST_ICON_SIZE_SMALL);
781 sent_pixbuf_open = gdk_pixbuf_copy (sent_pixbuf);
782 gdk_pixbuf_composite (emblem, sent_pixbuf_open, 0, 0,
783 MIN (gdk_pixbuf_get_width (emblem),
784 gdk_pixbuf_get_width (sent_pixbuf_open)),
785 MIN (gdk_pixbuf_get_height (emblem),
786 gdk_pixbuf_get_height (sent_pixbuf_open)),
787 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
788 g_object_unref (emblem);
791 if (!sent_pixbuf_close) {
792 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
793 MODEST_ICON_SIZE_SMALL);
794 sent_pixbuf_close = gdk_pixbuf_copy (sent_pixbuf);
795 gdk_pixbuf_composite (emblem, sent_pixbuf_close, 0, 0,
796 MIN (gdk_pixbuf_get_width (emblem),
797 gdk_pixbuf_get_width (sent_pixbuf_close)),
798 MIN (gdk_pixbuf_get_height (emblem),
799 gdk_pixbuf_get_height (sent_pixbuf_close)),
800 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
801 g_object_unref (emblem);
805 pixbuf = g_object_ref (sent_pixbuf);
806 pixbuf_open = g_object_ref (sent_pixbuf_open);
807 pixbuf_close = g_object_ref (sent_pixbuf_close);
811 case TNY_FOLDER_TYPE_TRASH:
813 trash_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH,
814 MODEST_ICON_SIZE_SMALL));
815 if (!trash_pixbuf_open) {
816 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
817 MODEST_ICON_SIZE_SMALL);
818 trash_pixbuf_open = gdk_pixbuf_copy (trash_pixbuf);
819 gdk_pixbuf_composite (emblem, trash_pixbuf_open, 0, 0,
820 MIN (gdk_pixbuf_get_width (emblem),
821 gdk_pixbuf_get_width (trash_pixbuf_open)),
822 MIN (gdk_pixbuf_get_height (emblem),
823 gdk_pixbuf_get_height (trash_pixbuf_open)),
824 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
825 g_object_unref (emblem);
828 if (!trash_pixbuf_close) {
829 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
830 MODEST_ICON_SIZE_SMALL);
831 trash_pixbuf_close = gdk_pixbuf_copy (trash_pixbuf);
832 gdk_pixbuf_composite (emblem, trash_pixbuf_close, 0, 0,
833 MIN (gdk_pixbuf_get_width (emblem),
834 gdk_pixbuf_get_width (trash_pixbuf_close)),
835 MIN (gdk_pixbuf_get_height (emblem),
836 gdk_pixbuf_get_height (trash_pixbuf_close)),
837 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
838 g_object_unref (emblem);
842 pixbuf = g_object_ref (trash_pixbuf);
843 pixbuf_open = g_object_ref (trash_pixbuf_open);
844 pixbuf_close = g_object_ref (trash_pixbuf_close);
847 case TNY_FOLDER_TYPE_DRAFTS:
849 draft_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS,
850 MODEST_ICON_SIZE_SMALL));
852 if (!draft_pixbuf_open) {
853 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
854 MODEST_ICON_SIZE_SMALL);
855 draft_pixbuf_open = gdk_pixbuf_copy (draft_pixbuf);
856 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0,
857 MIN (gdk_pixbuf_get_width (emblem),
858 gdk_pixbuf_get_width (draft_pixbuf_open)),
859 MIN (gdk_pixbuf_get_height (emblem),
860 gdk_pixbuf_get_height (draft_pixbuf_open)),
861 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
862 g_object_unref (emblem);
865 if (!draft_pixbuf_close) {
866 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
867 MODEST_ICON_SIZE_SMALL);
868 draft_pixbuf_close = gdk_pixbuf_copy (draft_pixbuf);
869 gdk_pixbuf_composite (emblem, draft_pixbuf_close, 0, 0,
870 MIN (gdk_pixbuf_get_width (emblem),
871 gdk_pixbuf_get_width (draft_pixbuf_close)),
872 MIN (gdk_pixbuf_get_height (emblem),
873 gdk_pixbuf_get_height (draft_pixbuf_close)),
874 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
875 g_object_unref (emblem);
879 pixbuf = g_object_ref (draft_pixbuf);
880 pixbuf_open = g_object_ref (draft_pixbuf_open);
881 pixbuf_close = g_object_ref (draft_pixbuf_close);
884 case TNY_FOLDER_TYPE_NORMAL:
887 normal_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL,
888 MODEST_ICON_SIZE_SMALL));
890 if (!normal_pixbuf_open) {
891 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
892 MODEST_ICON_SIZE_SMALL);
893 normal_pixbuf_open = gdk_pixbuf_copy (normal_pixbuf);
894 gdk_pixbuf_composite (emblem, normal_pixbuf_open, 0, 0,
895 MIN (gdk_pixbuf_get_width (emblem),
896 gdk_pixbuf_get_width (normal_pixbuf_open)),
897 MIN (gdk_pixbuf_get_height (emblem),
898 gdk_pixbuf_get_height (normal_pixbuf_open)),
899 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
900 g_object_unref (emblem);
903 if (!normal_pixbuf_close) {
904 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
905 MODEST_ICON_SIZE_SMALL);
906 normal_pixbuf_close = gdk_pixbuf_copy (normal_pixbuf);
907 gdk_pixbuf_composite (emblem, normal_pixbuf_close, 0, 0,
908 MIN (gdk_pixbuf_get_width (emblem),
909 gdk_pixbuf_get_width (normal_pixbuf_close)),
910 MIN (gdk_pixbuf_get_height (emblem),
911 gdk_pixbuf_get_height (normal_pixbuf_close)),
912 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
913 g_object_unref (emblem);
917 pixbuf = g_object_ref (normal_pixbuf);
918 pixbuf_open = g_object_ref (normal_pixbuf_open);
919 pixbuf_close = g_object_ref (normal_pixbuf_close);
925 retval->pixbuf = pixbuf;
926 retval->pixbuf_open = pixbuf_open;
927 retval->pixbuf_close = pixbuf_close;
934 free_pixbufs (ThreePixbufs *pixbufs)
936 g_object_unref (pixbufs->pixbuf);
937 g_object_unref (pixbufs->pixbuf_open);
938 g_object_unref (pixbufs->pixbuf_close);
939 g_slice_free (ThreePixbufs, pixbufs);
943 icon_cell_data (GtkTreeViewColumn *column,
944 GtkCellRenderer *renderer,
945 GtkTreeModel *tree_model,
949 GObject *rendobj = NULL, *instance = NULL;
950 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
951 gboolean has_children;
952 ThreePixbufs *pixbufs;
954 rendobj = (GObject *) renderer;
956 gtk_tree_model_get (tree_model, iter,
957 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
958 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
964 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
965 pixbufs = get_folder_icons (type, instance);
966 g_object_unref (instance);
969 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
972 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
973 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
976 free_pixbufs (pixbufs);
982 add_columns (GtkWidget *treeview)
984 GtkTreeViewColumn *column;
985 GtkCellRenderer *renderer;
986 GtkTreeSelection *sel;
989 column = gtk_tree_view_column_new ();
991 /* Set icon and text render function */
992 renderer = gtk_cell_renderer_pixbuf_new();
993 gtk_tree_view_column_pack_start (column, renderer, FALSE);
994 gtk_tree_view_column_set_cell_data_func(column, renderer,
995 icon_cell_data, treeview, NULL);
997 renderer = gtk_cell_renderer_text_new();
998 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END,
999 "ellipsize-set", TRUE, NULL);
1000 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1001 gtk_tree_view_column_set_cell_data_func(column, renderer,
1002 text_cell_data, treeview, NULL);
1004 /* Set selection mode */
1005 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1006 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1008 /* Set treeview appearance */
1009 gtk_tree_view_column_set_spacing (column, 2);
1010 gtk_tree_view_column_set_resizable (column, TRUE);
1011 gtk_tree_view_column_set_fixed_width (column, TRUE);
1012 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1013 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1016 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1020 modest_folder_view_init (ModestFolderView *obj)
1022 ModestFolderViewPrivate *priv;
1025 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1027 priv->timer_expander = 0;
1028 priv->account_store = NULL;
1030 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1031 priv->cur_folder_store = NULL;
1032 priv->visible_account_id = NULL;
1033 priv->folder_to_select = NULL;
1035 priv->reexpand = TRUE;
1037 /* Initialize the local account name */
1038 conf = modest_runtime_get_conf();
1039 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1041 /* Init email clipboard */
1042 priv->clipboard = modest_runtime_get_email_clipboard ();
1043 priv->hidding_ids = NULL;
1044 priv->n_selected = 0;
1045 priv->reselect = FALSE;
1046 priv->show_non_move = TRUE;
1048 /* Build treeview */
1049 add_columns (GTK_WIDGET (obj));
1051 /* Setup drag and drop */
1052 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1054 /* Connect signals */
1055 g_signal_connect (G_OBJECT (obj),
1057 G_CALLBACK (on_key_pressed), NULL);
1059 priv->display_name_changed_signal =
1060 g_signal_connect (modest_runtime_get_account_mgr (),
1061 "display_name_changed",
1062 G_CALLBACK (on_display_name_changed),
1066 * Track changes in the local account name (in the device it
1067 * will be the device name)
1069 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1071 G_CALLBACK(on_configuration_key_changed),
1076 tny_account_store_view_init (gpointer g, gpointer iface_data)
1078 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1080 klass->set_account_store = modest_folder_view_set_account_store;
1084 modest_folder_view_finalize (GObject *obj)
1086 ModestFolderViewPrivate *priv;
1087 GtkTreeSelection *sel;
1089 g_return_if_fail (obj);
1091 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1093 if (priv->timer_expander != 0) {
1094 g_source_remove (priv->timer_expander);
1095 priv->timer_expander = 0;
1098 if (priv->account_store) {
1099 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1100 priv->account_inserted_signal);
1101 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1102 priv->account_removed_signal);
1103 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1104 priv->account_changed_signal);
1105 g_object_unref (G_OBJECT(priv->account_store));
1106 priv->account_store = NULL;
1110 g_object_unref (G_OBJECT (priv->query));
1114 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1115 if (priv->folder_to_select) {
1116 g_object_unref (G_OBJECT(priv->folder_to_select));
1117 priv->folder_to_select = NULL;
1120 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1122 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1124 g_free (priv->local_account_name);
1125 g_free (priv->visible_account_id);
1127 if (priv->conf_key_signal) {
1128 g_signal_handler_disconnect (modest_runtime_get_conf (),
1129 priv->conf_key_signal);
1130 priv->conf_key_signal = 0;
1133 if (priv->cur_folder_store) {
1134 if (TNY_IS_FOLDER(priv->cur_folder_store)) {
1135 ModestMailOperation *mail_op;
1137 mail_op = modest_mail_operation_new (NULL);
1138 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1140 modest_mail_operation_sync_folder (mail_op, TNY_FOLDER (priv->cur_folder_store), FALSE);
1141 g_object_unref (mail_op);
1144 g_object_unref (priv->cur_folder_store);
1145 priv->cur_folder_store = NULL;
1148 /* Clear hidding array created by cut operation */
1149 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1151 G_OBJECT_CLASS(parent_class)->finalize (obj);
1156 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1158 ModestFolderViewPrivate *priv;
1161 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1162 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1164 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1165 device = tny_account_store_get_device (account_store);
1167 if (G_UNLIKELY (priv->account_store)) {
1169 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1170 priv->account_inserted_signal))
1171 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1172 priv->account_inserted_signal);
1173 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1174 priv->account_removed_signal))
1175 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1176 priv->account_removed_signal);
1177 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1178 priv->account_changed_signal))
1179 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1180 priv->account_changed_signal);
1181 g_object_unref (G_OBJECT (priv->account_store));
1184 priv->account_store = g_object_ref (G_OBJECT (account_store));
1186 priv->account_removed_signal =
1187 g_signal_connect (G_OBJECT(account_store), "account_removed",
1188 G_CALLBACK (on_account_removed), self);
1190 priv->account_inserted_signal =
1191 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1192 G_CALLBACK (on_account_inserted), self);
1194 priv->account_changed_signal =
1195 g_signal_connect (G_OBJECT(account_store), "account_changed",
1196 G_CALLBACK (on_account_changed), self);
1198 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1200 g_object_unref (G_OBJECT (device));
1204 on_account_inserted (TnyAccountStore *account_store,
1205 TnyAccount *account,
1208 ModestFolderViewPrivate *priv;
1209 GtkTreeModel *sort_model, *filter_model;
1211 /* Ignore transport account insertions, we're not showing them
1212 in the folder view */
1213 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1216 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1218 /* If we're adding a new account, and there is no previous
1219 one, we need to select the visible server account */
1220 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1221 !priv->visible_account_id)
1222 modest_widget_memory_restore (modest_runtime_get_conf(),
1223 G_OBJECT (user_data),
1224 MODEST_CONF_FOLDER_VIEW_KEY);
1226 /* Get the inner model */
1227 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1228 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1230 /* Insert the account in the model */
1231 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1232 G_OBJECT (account));
1234 /* Refilter the model */
1235 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1240 on_account_changed (TnyAccountStore *account_store,
1241 TnyAccount *tny_account,
1244 ModestFolderViewPrivate *priv;
1245 GtkTreeModel *sort_model, *filter_model;
1247 /* Ignore transport account insertions, we're not showing them
1248 in the folder view */
1249 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1252 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1254 /* Get the inner model */
1255 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1256 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1258 /* Remove the account from the model */
1259 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1260 G_OBJECT (tny_account));
1262 /* Insert the account in the model */
1263 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1264 G_OBJECT (tny_account));
1266 /* Refilter the model */
1267 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1272 * Selects the first inbox or the local account in an idle
1275 on_idle_select_first_inbox_or_local (gpointer user_data)
1277 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1279 gdk_threads_enter ();
1280 modest_folder_view_select_first_inbox_or_local (self);
1281 gdk_threads_leave ();
1288 on_account_removed (TnyAccountStore *account_store,
1289 TnyAccount *account,
1292 ModestFolderView *self = NULL;
1293 ModestFolderViewPrivate *priv;
1294 GtkTreeModel *sort_model, *filter_model;
1295 GtkTreeSelection *sel = NULL;
1296 gboolean same_account_selected = FALSE;
1298 /* Ignore transport account removals, we're not showing them
1299 in the folder view */
1300 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1303 self = MODEST_FOLDER_VIEW (user_data);
1304 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1306 /* Invalidate the cur_folder_store only if the selected folder
1307 belongs to the account that is being removed */
1308 if (priv->cur_folder_store) {
1309 TnyAccount *selected_folder_account = NULL;
1311 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1312 selected_folder_account =
1313 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1315 selected_folder_account =
1316 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1319 if (selected_folder_account == account) {
1320 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1321 gtk_tree_selection_unselect_all (sel);
1322 same_account_selected = TRUE;
1324 g_object_unref (selected_folder_account);
1327 /* Invalidate row to select only if the folder to select
1328 belongs to the account that is being removed*/
1329 if (priv->folder_to_select) {
1330 TnyAccount *folder_to_select_account = NULL;
1332 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1333 if (folder_to_select_account == account) {
1334 modest_folder_view_disable_next_folder_selection (self);
1335 g_object_unref (priv->folder_to_select);
1336 priv->folder_to_select = NULL;
1338 g_object_unref (folder_to_select_account);
1341 /* Remove the account from the model */
1342 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1343 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1344 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1345 G_OBJECT (account));
1347 /* If the removed account is the currently viewed one then
1348 clear the configuration value. The new visible account will be the default account */
1349 if (priv->visible_account_id &&
1350 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1352 /* Clear the current visible account_id */
1353 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1355 /* Call the restore method, this will set the new visible account */
1356 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1357 MODEST_CONF_FOLDER_VIEW_KEY);
1360 /* Refilter the model */
1361 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1363 /* Select the first INBOX if the currently selected folder
1364 belongs to the account that is being deleted */
1365 if (same_account_selected)
1366 g_idle_add (on_idle_select_first_inbox_or_local, self);
1370 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1372 GtkTreeViewColumn *col;
1374 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1376 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1378 g_printerr ("modest: failed get column for title\n");
1382 gtk_tree_view_column_set_title (col, title);
1383 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1388 modest_folder_view_on_map (ModestFolderView *self,
1389 GdkEventExpose *event,
1392 ModestFolderViewPrivate *priv;
1394 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1396 /* This won't happen often */
1397 if (G_UNLIKELY (priv->reselect)) {
1398 /* Select the first inbox or the local account if not found */
1400 /* TODO: this could cause a lock at startup, so we
1401 comment it for the moment. We know that this will
1402 be a bug, because the INBOX is not selected, but we
1403 need to rewrite some parts of Modest to avoid the
1404 deathlock situation */
1405 /* TODO: check if this is still the case */
1406 priv->reselect = FALSE;
1407 modest_folder_view_select_first_inbox_or_local (self);
1408 /* Notify the display name observers */
1409 g_signal_emit (G_OBJECT(self),
1410 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1414 if (priv->reexpand) {
1415 expand_root_items (self);
1416 priv->reexpand = FALSE;
1423 modest_folder_view_new (TnyFolderStoreQuery *query)
1426 ModestFolderViewPrivate *priv;
1427 GtkTreeSelection *sel;
1429 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1430 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1433 priv->query = g_object_ref (query);
1435 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1436 priv->changed_signal = g_signal_connect (sel, "changed",
1437 G_CALLBACK (on_selection_changed), self);
1439 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1441 return GTK_WIDGET(self);
1444 /* this feels dirty; any other way to expand all the root items? */
1446 expand_root_items (ModestFolderView *self)
1449 GtkTreeModel *model;
1452 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1453 path = gtk_tree_path_new_first ();
1455 /* all folders should have child items, so.. */
1457 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1458 gtk_tree_path_next (path);
1459 } while (gtk_tree_model_get_iter (model, &iter, path));
1461 gtk_tree_path_free (path);
1465 * We use this function to implement the
1466 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1467 * account in this case, and the local folders.
1470 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1472 ModestFolderViewPrivate *priv;
1473 gboolean retval = TRUE;
1474 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1475 GObject *instance = NULL;
1476 const gchar *id = NULL;
1478 gboolean found = FALSE;
1479 gboolean cleared = FALSE;
1481 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1484 gtk_tree_model_get (model, iter,
1485 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1486 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1489 /* Do not show if there is no instance, this could indeed
1490 happen when the model is being modified while it's being
1491 drawn. This could occur for example when moving folders
1496 if (type == TNY_FOLDER_TYPE_ROOT) {
1497 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1498 account instead of a folder. */
1499 if (TNY_IS_ACCOUNT (instance)) {
1500 TnyAccount *acc = TNY_ACCOUNT (instance);
1501 const gchar *account_id = tny_account_get_id (acc);
1503 /* If it isn't a special folder,
1504 * don't show it unless it is the visible account: */
1505 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1506 !modest_tny_account_is_virtual_local_folders (acc) &&
1507 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1509 /* Show only the visible account id */
1510 if (priv->visible_account_id) {
1511 if (strcmp (account_id, priv->visible_account_id))
1518 /* Never show these to the user. They are merged into one folder
1519 * in the local-folders account instead: */
1520 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1525 /* Check hiding (if necessary) */
1526 cleared = modest_email_clipboard_cleared (priv->clipboard);
1527 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1528 id = tny_folder_get_id (TNY_FOLDER(instance));
1529 if (priv->hidding_ids != NULL)
1530 for (i=0; i < priv->n_selected && !found; i++)
1531 if (priv->hidding_ids[i] != NULL && id != NULL)
1532 found = (!strcmp (priv->hidding_ids[i], id));
1538 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1539 folder as no message can be move there according to UI specs */
1540 if (!priv->show_non_move) {
1542 case TNY_FOLDER_TYPE_OUTBOX:
1543 case TNY_FOLDER_TYPE_SENT:
1544 case TNY_FOLDER_TYPE_DRAFTS:
1547 case TNY_FOLDER_TYPE_UNKNOWN:
1548 case TNY_FOLDER_TYPE_NORMAL:
1549 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1550 if (type == TNY_FOLDER_TYPE_INVALID)
1551 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1553 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1554 type == TNY_FOLDER_TYPE_SENT
1555 || type == TNY_FOLDER_TYPE_DRAFTS)
1564 g_object_unref (instance);
1571 modest_folder_view_update_model (ModestFolderView *self,
1572 TnyAccountStore *account_store)
1574 ModestFolderViewPrivate *priv;
1575 GtkTreeModel *model /* , *old_model */;
1576 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1578 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1579 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1582 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1584 /* Notify that there is no folder selected */
1585 g_signal_emit (G_OBJECT(self),
1586 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1588 if (priv->cur_folder_store) {
1589 g_object_unref (priv->cur_folder_store);
1590 priv->cur_folder_store = NULL;
1593 /* FIXME: the local accounts are not shown when the query
1594 selects only the subscribed folders */
1595 model = tny_gtk_folder_store_tree_model_new (NULL);
1597 /* Get the accounts: */
1598 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1600 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1602 sortable = gtk_tree_model_sort_new_with_model (model);
1603 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1604 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1605 GTK_SORT_ASCENDING);
1606 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1607 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1608 cmp_rows, NULL, NULL);
1610 /* Create filter model */
1611 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1612 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1618 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1619 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1620 (GCallback) on_row_inserted_maybe_select_folder, self);
1622 g_object_unref (model);
1623 g_object_unref (filter_model);
1624 g_object_unref (sortable);
1626 /* Force a reselection of the INBOX next time the widget is shown */
1627 priv->reselect = TRUE;
1634 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1636 GtkTreeModel *model = NULL;
1637 TnyFolderStore *folder = NULL;
1639 ModestFolderView *tree_view = NULL;
1640 ModestFolderViewPrivate *priv = NULL;
1641 gboolean selected = FALSE;
1643 g_return_if_fail (sel);
1644 g_return_if_fail (user_data);
1646 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1648 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1650 tree_view = MODEST_FOLDER_VIEW (user_data);
1653 gtk_tree_model_get (model, &iter,
1654 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1657 /* If the folder is the same do not notify */
1658 if (folder && priv->cur_folder_store == folder) {
1659 g_object_unref (folder);
1664 /* Current folder was unselected */
1665 if (priv->cur_folder_store) {
1666 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1667 priv->cur_folder_store, FALSE);
1669 if (TNY_IS_FOLDER(priv->cur_folder_store))
1670 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1671 FALSE, NULL, NULL, NULL);
1673 /* FALSE --> don't expunge the messages */
1675 g_object_unref (priv->cur_folder_store);
1676 priv->cur_folder_store = NULL;
1679 /* New current references */
1680 priv->cur_folder_store = folder;
1682 /* New folder has been selected. Do not notify if there is
1683 nothing new selected */
1685 g_signal_emit (G_OBJECT(tree_view),
1686 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1687 0, priv->cur_folder_store, TRUE);
1692 modest_folder_view_get_selected (ModestFolderView *self)
1694 ModestFolderViewPrivate *priv;
1696 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1698 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1699 if (priv->cur_folder_store)
1700 g_object_ref (priv->cur_folder_store);
1702 return priv->cur_folder_store;
1706 get_cmp_rows_type_pos (GObject *folder)
1708 /* Remote accounts -> Local account -> MMC account .*/
1711 if (TNY_IS_ACCOUNT (folder) &&
1712 modest_tny_account_is_virtual_local_folders (
1713 TNY_ACCOUNT (folder))) {
1715 } else if (TNY_IS_ACCOUNT (folder)) {
1716 TnyAccount *account = TNY_ACCOUNT (folder);
1717 const gchar *account_id = tny_account_get_id (account);
1718 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1724 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1725 return -1; /* Should never happen */
1730 get_cmp_subfolder_type_pos (TnyFolderType t)
1732 /* Inbox, Outbox, Drafts, Sent, User */
1736 case TNY_FOLDER_TYPE_INBOX:
1739 case TNY_FOLDER_TYPE_OUTBOX:
1742 case TNY_FOLDER_TYPE_DRAFTS:
1745 case TNY_FOLDER_TYPE_SENT:
1754 * This function orders the mail accounts according to these rules:
1755 * 1st - remote accounts
1756 * 2nd - local account
1760 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1764 gchar *name1 = NULL;
1765 gchar *name2 = NULL;
1766 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1767 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1768 GObject *folder1 = NULL;
1769 GObject *folder2 = NULL;
1771 gtk_tree_model_get (tree_model, iter1,
1772 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1773 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1774 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1776 gtk_tree_model_get (tree_model, iter2,
1777 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1778 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1779 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1782 /* Return if we get no folder. This could happen when folder
1783 operations are happening. The model is updated after the
1784 folder copy/move actually occurs, so there could be
1785 situations where the model to be drawn is not correct */
1786 if (!folder1 || !folder2)
1789 if (type == TNY_FOLDER_TYPE_ROOT) {
1790 /* Compare the types, so that
1791 * Remote accounts -> Local account -> MMC account .*/
1792 const gint pos1 = get_cmp_rows_type_pos (folder1);
1793 const gint pos2 = get_cmp_rows_type_pos (folder2);
1794 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1795 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1798 else if (pos1 > pos2)
1801 /* Compare items of the same type: */
1803 TnyAccount *account1 = NULL;
1804 if (TNY_IS_ACCOUNT (folder1))
1805 account1 = TNY_ACCOUNT (folder1);
1807 TnyAccount *account2 = NULL;
1808 if (TNY_IS_ACCOUNT (folder2))
1809 account2 = TNY_ACCOUNT (folder2);
1811 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1812 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1814 if (!account_id && !account_id2) {
1816 } else if (!account_id) {
1818 } else if (!account_id2) {
1820 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1823 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1827 gint cmp1 = 0, cmp2 = 0;
1828 /* get the parent to know if it's a local folder */
1831 gboolean has_parent;
1832 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1834 GObject *parent_folder;
1835 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1836 gtk_tree_model_get (tree_model, &parent,
1837 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1838 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1840 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1841 TNY_IS_ACCOUNT (parent_folder)) {
1842 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1843 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1844 (TNY_FOLDER (folder1)));
1845 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1846 (TNY_FOLDER (folder2)));
1847 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1848 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1851 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1857 g_object_unref (parent_folder);
1860 /* if they are not local folders */
1862 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1863 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1867 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1869 cmp = (cmp1 - cmp2);
1874 g_object_unref(G_OBJECT(folder1));
1876 g_object_unref(G_OBJECT(folder2));
1884 /*****************************************************************************/
1885 /* DRAG and DROP stuff */
1886 /*****************************************************************************/
1888 * This function fills the #GtkSelectionData with the row and the
1889 * model that has been dragged. It's called when this widget is a
1890 * source for dnd after the event drop happened
1893 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1894 guint info, guint time, gpointer data)
1896 GtkTreeSelection *selection;
1897 GtkTreeModel *model;
1899 GtkTreePath *source_row;
1901 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1902 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1904 source_row = gtk_tree_model_get_path (model, &iter);
1905 gtk_tree_set_row_drag_data (selection_data,
1909 gtk_tree_path_free (source_row);
1913 typedef struct _DndHelper {
1914 ModestFolderView *folder_view;
1915 gboolean delete_source;
1916 GtkTreePath *source_row;
1917 GdkDragContext *context;
1922 dnd_helper_destroyer (DndHelper *helper)
1924 /* Free the helper */
1925 g_object_unref (helper->folder_view);
1926 gtk_tree_path_free (helper->source_row);
1927 g_slice_free (DndHelper, helper);
1931 xfer_cb (ModestMailOperation *mail_op,
1937 helper = (DndHelper *) user_data;
1939 if (modest_mail_operation_get_status (mail_op) ==
1940 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1946 /* Notify the drag source. Never call delete, the monitor will
1947 do the job if needed */
1948 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1950 /* Free the helper */
1951 dnd_helper_destroyer (helper);
1955 xfer_msgs_cb (ModestMailOperation *mail_op,
1959 xfer_cb (mail_op, user_data);
1963 xfer_folder_cb (ModestMailOperation *mail_op,
1964 TnyFolder *new_folder,
1968 GtkWidget *folder_view;
1970 helper = (DndHelper *) user_data;
1971 folder_view = g_object_ref (helper->folder_view);
1974 xfer_cb (mail_op, user_data);
1976 /* Select the folder */
1978 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1980 g_object_unref (folder_view);
1984 /* get the folder for the row the treepath refers to. */
1985 /* folder must be unref'd */
1986 static TnyFolderStore *
1987 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1990 TnyFolderStore *folder = NULL;
1992 if (gtk_tree_model_get_iter (model,&iter, path))
1993 gtk_tree_model_get (model, &iter,
1994 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2000 * This function is used by drag_data_received_cb to manage drag and
2001 * drop of a header, i.e, and drag from the header view to the folder
2005 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2006 GtkTreeModel *dest_model,
2007 GtkTreePath *dest_row,
2008 GtkSelectionData *selection_data,
2011 TnyList *headers = NULL;
2012 TnyFolder *folder = NULL;
2013 TnyFolderType folder_type;
2014 ModestMailOperation *mail_op = NULL;
2015 GtkTreeIter source_iter, dest_iter;
2016 ModestWindowMgr *mgr = NULL;
2017 ModestWindow *main_win = NULL;
2018 gchar **uris, **tmp;
2021 /* Build the list of headers */
2022 mgr = modest_runtime_get_window_mgr ();
2023 headers = tny_simple_list_new ();
2024 uris = modest_dnd_selection_data_get_paths (selection_data);
2027 while (*tmp != NULL) {
2032 path = gtk_tree_path_new_from_string (*tmp);
2033 gtk_tree_model_get_iter (source_model, &source_iter, path);
2034 gtk_tree_model_get (source_model, &source_iter,
2035 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2038 /* Do not enable d&d of headers already opened */
2039 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2040 tny_list_append (headers, G_OBJECT (header));
2042 /* Free and go on */
2043 gtk_tree_path_free (path);
2044 g_object_unref (header);
2049 /* Get the target folder */
2050 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2051 gtk_tree_model_get (dest_model, &dest_iter,
2052 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2055 if (!folder || !TNY_IS_FOLDER(folder)) {
2056 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2060 folder_type = modest_tny_folder_guess_folder_type (folder);
2061 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2062 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2063 goto cleanup; /* cannot move messages there */
2066 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2067 /* g_warning ("folder not writable"); */
2068 goto cleanup; /* verboten! */
2071 /* Ask for confirmation to move */
2072 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2074 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2078 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2080 if (response == GTK_RESPONSE_CANCEL)
2083 /* Transfer messages */
2084 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2085 modest_ui_actions_move_folder_error_handler,
2088 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2091 modest_mail_operation_xfer_msgs (mail_op,
2094 helper->delete_source,
2095 xfer_msgs_cb, helper);
2099 if (G_IS_OBJECT(mail_op))
2100 g_object_unref (G_OBJECT (mail_op));
2101 if (G_IS_OBJECT(folder))
2102 g_object_unref (G_OBJECT (folder));
2103 if (G_IS_OBJECT(headers))
2104 g_object_unref (headers);
2108 TnyFolderStore *src_folder;
2109 TnyFolderStore *dst_folder;
2110 ModestFolderView *folder_view;
2115 dnd_folder_info_destroyer (DndFolderInfo *info)
2117 if (info->src_folder)
2118 g_object_unref (info->src_folder);
2119 if (info->dst_folder)
2120 g_object_unref (info->dst_folder);
2121 if (info->folder_view)
2122 g_object_unref (info->folder_view);
2123 g_slice_free (DndFolderInfo, info);
2127 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2128 GtkWindow *parent_window,
2129 TnyAccount *account)
2131 time_t dnd_time = info->helper->time;
2132 GdkDragContext *context = info->helper->context;
2135 modest_ui_actions_on_account_connection_error (parent_window, account);
2137 /* Free the helper & info */
2138 dnd_helper_destroyer (info->helper);
2139 dnd_folder_info_destroyer (info);
2141 /* Notify the drag source. Never call delete, the monitor will
2142 do the job if needed */
2143 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2148 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2150 GtkWindow *parent_window,
2151 TnyAccount *account,
2154 DndFolderInfo *info = NULL;
2155 ModestMailOperation *mail_op;
2157 info = (DndFolderInfo *) user_data;
2159 if (err || canceled) {
2160 dnd_on_connection_failed_destroyer (info, parent_window, account);
2164 /* Do the mail operation */
2165 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2166 modest_ui_actions_move_folder_error_handler,
2167 info->src_folder, NULL);
2169 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2172 /* Transfer the folder */
2173 modest_mail_operation_xfer_folder (mail_op,
2174 TNY_FOLDER (info->src_folder),
2176 info->helper->delete_source,
2180 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2181 /* TNY_FOLDER (info->dst_folder), TRUE); */
2183 g_object_unref (G_OBJECT (mail_op));
2188 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2190 GtkWindow *parent_window,
2191 TnyAccount *account,
2194 DndFolderInfo *info = NULL;
2196 info = (DndFolderInfo *) user_data;
2198 if (err || canceled) {
2199 dnd_on_connection_failed_destroyer (info, parent_window, account);
2203 /* Connect to source folder and perform the copy/move */
2204 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2206 drag_and_drop_from_folder_view_src_folder_performer,
2211 * This function is used by drag_data_received_cb to manage drag and
2212 * drop of a folder, i.e, and drag from the folder view to the same
2216 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2217 GtkTreeModel *dest_model,
2218 GtkTreePath *dest_row,
2219 GtkSelectionData *selection_data,
2222 GtkTreeIter dest_iter, iter;
2223 TnyFolderStore *dest_folder = NULL;
2224 TnyFolderStore *folder = NULL;
2225 gboolean forbidden = FALSE;
2227 DndFolderInfo *info = NULL;
2229 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2231 g_warning ("%s: BUG: no main window", __FUNCTION__);
2236 /* check the folder rules for the destination */
2237 folder = tree_path_to_folder (dest_model, dest_row);
2238 if (TNY_IS_FOLDER(folder)) {
2239 ModestTnyFolderRules rules =
2240 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2241 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2242 } else if (TNY_IS_FOLDER_STORE(folder)) {
2243 /* enable local root as destination for folders */
2244 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2245 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2248 g_object_unref (folder);
2251 /* check the folder rules for the source */
2252 folder = tree_path_to_folder (source_model, helper->source_row);
2253 if (TNY_IS_FOLDER(folder)) {
2254 ModestTnyFolderRules rules =
2255 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2256 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2259 g_object_unref (folder);
2263 /* Check if the drag is possible */
2264 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2265 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2266 gtk_tree_path_free (helper->source_row);
2267 g_slice_free (DndHelper, helper);
2272 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2273 gtk_tree_model_get (dest_model, &dest_iter,
2274 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2276 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2277 gtk_tree_model_get (source_model, &iter,
2278 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2281 /* Create the info for the performer */
2282 info = g_slice_new (DndFolderInfo);
2283 info->src_folder = g_object_ref (folder);
2284 info->dst_folder = g_object_ref (dest_folder);
2285 info->folder_view = g_object_ref (helper->folder_view);
2286 info->helper = helper;
2288 /* Connect to the destination folder and perform the copy/move */
2289 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2291 drag_and_drop_from_folder_view_dst_folder_performer,
2295 g_object_unref (dest_folder);
2296 g_object_unref (folder);
2300 * This function receives the data set by the "drag-data-get" signal
2301 * handler. This information comes within the #GtkSelectionData. This
2302 * function will manage both the drags of folders of the treeview and
2303 * drags of headers of the header view widget.
2306 on_drag_data_received (GtkWidget *widget,
2307 GdkDragContext *context,
2310 GtkSelectionData *selection_data,
2315 GtkWidget *source_widget;
2316 GtkTreeModel *dest_model, *source_model;
2317 GtkTreePath *source_row, *dest_row;
2318 GtkTreeViewDropPosition pos;
2319 gboolean success = FALSE, delete_source = FALSE;
2320 DndHelper *helper = NULL;
2322 /* Do not allow further process */
2323 g_signal_stop_emission_by_name (widget, "drag-data-received");
2324 source_widget = gtk_drag_get_source_widget (context);
2326 /* Get the action */
2327 if (context->action == GDK_ACTION_MOVE) {
2328 delete_source = TRUE;
2330 /* Notify that there is no folder selected. We need to
2331 do this in order to update the headers view (and
2332 its monitors, because when moving, the old folder
2333 won't longer exist. We can not wait for the end of
2334 the operation, because the operation won't start if
2335 the folder is in use */
2336 if (source_widget == widget) {
2337 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2338 gtk_tree_selection_unselect_all (sel);
2342 /* Check if the get_data failed */
2343 if (selection_data == NULL || selection_data->length < 0)
2344 gtk_drag_finish (context, success, FALSE, time);
2346 /* Select the destination model */
2347 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2349 /* Get the path to the destination row. Can not call
2350 gtk_tree_view_get_drag_dest_row() because the source row
2351 is not selected anymore */
2352 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2355 /* Only allow drops IN other rows */
2357 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2358 pos == GTK_TREE_VIEW_DROP_AFTER)
2359 gtk_drag_finish (context, success, FALSE, time);
2361 /* Create the helper */
2362 helper = g_slice_new0 (DndHelper);
2363 helper->delete_source = delete_source;
2364 helper->context = context;
2365 helper->time = time;
2366 helper->folder_view = g_object_ref (widget);
2368 /* Drags from the header view */
2369 if (source_widget != widget) {
2370 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2372 drag_and_drop_from_header_view (source_model,
2378 /* Get the source model and row */
2379 gtk_tree_get_row_drag_data (selection_data,
2382 helper->source_row = gtk_tree_path_copy (source_row);
2384 drag_and_drop_from_folder_view (source_model,
2390 gtk_tree_path_free (source_row);
2394 gtk_tree_path_free (dest_row);
2398 * We define a "drag-drop" signal handler because we do not want to
2399 * use the default one, because the default one always calls
2400 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2401 * signal handler, because there we have all the information available
2402 * to know if the dnd was a success or not.
2405 drag_drop_cb (GtkWidget *widget,
2406 GdkDragContext *context,
2414 if (!context->targets)
2417 /* Check if we're dragging a folder row */
2418 target = gtk_drag_dest_find_target (widget, context, NULL);
2420 /* Request the data from the source. */
2421 gtk_drag_get_data(widget, context, target, time);
2427 * This function expands a node of a tree view if it's not expanded
2428 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2429 * does that, so that's why they're here.
2432 expand_row_timeout (gpointer data)
2434 GtkTreeView *tree_view = data;
2435 GtkTreePath *dest_path = NULL;
2436 GtkTreeViewDropPosition pos;
2437 gboolean result = FALSE;
2439 gdk_threads_enter ();
2441 gtk_tree_view_get_drag_dest_row (tree_view,
2446 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2447 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2448 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2449 gtk_tree_path_free (dest_path);
2453 gtk_tree_path_free (dest_path);
2458 gdk_threads_leave ();
2464 * This function is called whenever the pointer is moved over a widget
2465 * while dragging some data. It installs a timeout that will expand a
2466 * node of the treeview if not expanded yet. This function also calls
2467 * gdk_drag_status in order to set the suggested action that will be
2468 * used by the "drag-data-received" signal handler to know if we
2469 * should do a move or just a copy of the data.
2472 on_drag_motion (GtkWidget *widget,
2473 GdkDragContext *context,
2479 GtkTreeViewDropPosition pos;
2480 GtkTreePath *dest_row;
2481 GtkTreeModel *dest_model;
2482 ModestFolderViewPrivate *priv;
2483 GdkDragAction suggested_action;
2484 gboolean valid_location = FALSE;
2485 TnyFolderStore *folder = NULL;
2487 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2489 if (priv->timer_expander != 0) {
2490 g_source_remove (priv->timer_expander);
2491 priv->timer_expander = 0;
2494 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2499 /* Do not allow drops between folders */
2501 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2502 pos == GTK_TREE_VIEW_DROP_AFTER) {
2503 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2504 gdk_drag_status(context, 0, time);
2505 valid_location = FALSE;
2508 valid_location = TRUE;
2511 /* Check that the destination folder is writable */
2512 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2513 folder = tree_path_to_folder (dest_model, dest_row);
2514 if (folder && TNY_IS_FOLDER (folder)) {
2515 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2517 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2518 valid_location = FALSE;
2523 /* Expand the selected row after 1/2 second */
2524 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2525 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2527 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2529 /* Select the desired action. By default we pick MOVE */
2530 suggested_action = GDK_ACTION_MOVE;
2532 if (context->actions == GDK_ACTION_COPY)
2533 gdk_drag_status(context, GDK_ACTION_COPY, time);
2534 else if (context->actions == GDK_ACTION_MOVE)
2535 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2536 else if (context->actions & suggested_action)
2537 gdk_drag_status(context, suggested_action, time);
2539 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2543 g_object_unref (folder);
2545 gtk_tree_path_free (dest_row);
2547 g_signal_stop_emission_by_name (widget, "drag-motion");
2549 return valid_location;
2553 * This function sets the treeview as a source and a target for dnd
2554 * events. It also connects all the requirede signals.
2557 setup_drag_and_drop (GtkTreeView *self)
2559 /* Set up the folder view as a dnd destination. Set only the
2560 highlight flag, otherwise gtk will have a different
2562 gtk_drag_dest_set (GTK_WIDGET (self),
2563 GTK_DEST_DEFAULT_HIGHLIGHT,
2564 folder_view_drag_types,
2565 G_N_ELEMENTS (folder_view_drag_types),
2566 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2568 g_signal_connect (G_OBJECT (self),
2569 "drag_data_received",
2570 G_CALLBACK (on_drag_data_received),
2574 /* Set up the treeview as a dnd source */
2575 gtk_drag_source_set (GTK_WIDGET (self),
2577 folder_view_drag_types,
2578 G_N_ELEMENTS (folder_view_drag_types),
2579 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2581 g_signal_connect (G_OBJECT (self),
2583 G_CALLBACK (on_drag_motion),
2586 g_signal_connect (G_OBJECT (self),
2588 G_CALLBACK (on_drag_data_get),
2591 g_signal_connect (G_OBJECT (self),
2593 G_CALLBACK (drag_drop_cb),
2598 * This function manages the navigation through the folders using the
2599 * keyboard or the hardware keys in the device
2602 on_key_pressed (GtkWidget *self,
2606 GtkTreeSelection *selection;
2608 GtkTreeModel *model;
2609 gboolean retval = FALSE;
2611 /* Up and Down are automatically managed by the treeview */
2612 if (event->keyval == GDK_Return) {
2613 /* Expand/Collapse the selected row */
2614 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2615 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2618 path = gtk_tree_model_get_path (model, &iter);
2620 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2621 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2623 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2624 gtk_tree_path_free (path);
2626 /* No further processing */
2634 * We listen to the changes in the local folder account name key,
2635 * because we want to show the right name in the view. The local
2636 * folder account name corresponds to the device name in the Maemo
2637 * version. We do this because we do not want to query gconf on each
2638 * tree view refresh. It's better to cache it and change whenever
2642 on_configuration_key_changed (ModestConf* conf,
2644 ModestConfEvent event,
2645 ModestConfNotificationId id,
2646 ModestFolderView *self)
2648 ModestFolderViewPrivate *priv;
2651 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2652 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2654 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2655 g_free (priv->local_account_name);
2657 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2658 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2660 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2661 MODEST_CONF_DEVICE_NAME, NULL);
2663 /* Force a redraw */
2664 #if GTK_CHECK_VERSION(2, 8, 0)
2665 GtkTreeViewColumn * tree_column;
2667 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2668 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2669 gtk_tree_view_column_queue_resize (tree_column);
2671 gtk_widget_queue_draw (GTK_WIDGET (self));
2677 modest_folder_view_set_style (ModestFolderView *self,
2678 ModestFolderViewStyle style)
2680 ModestFolderViewPrivate *priv;
2682 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2683 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2684 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2686 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2689 priv->style = style;
2693 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2694 const gchar *account_id)
2696 ModestFolderViewPrivate *priv;
2697 GtkTreeModel *model;
2699 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2701 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2703 /* This will be used by the filter_row callback,
2704 * to decided which rows to show: */
2705 if (priv->visible_account_id) {
2706 g_free (priv->visible_account_id);
2707 priv->visible_account_id = NULL;
2710 priv->visible_account_id = g_strdup (account_id);
2713 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2714 if (GTK_IS_TREE_MODEL_FILTER (model))
2715 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2717 /* Save settings to gconf */
2718 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2719 MODEST_CONF_FOLDER_VIEW_KEY);
2723 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2725 ModestFolderViewPrivate *priv;
2727 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2729 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2731 return (const gchar *) priv->visible_account_id;
2735 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2739 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2741 gtk_tree_model_get (model, iter,
2742 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2745 gboolean result = FALSE;
2746 if (type == TNY_FOLDER_TYPE_INBOX) {
2750 *inbox_iter = *iter;
2754 if (gtk_tree_model_iter_children (model, &child, iter)) {
2755 if (find_inbox_iter (model, &child, inbox_iter))
2759 } while (gtk_tree_model_iter_next (model, iter));
2768 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2770 GtkTreeModel *model;
2771 GtkTreeIter iter, inbox_iter;
2772 GtkTreeSelection *sel;
2773 GtkTreePath *path = NULL;
2775 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2777 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2781 expand_root_items (self);
2782 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2784 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2785 g_warning ("%s: model is empty", __FUNCTION__);
2789 if (find_inbox_iter (model, &iter, &inbox_iter))
2790 path = gtk_tree_model_get_path (model, &inbox_iter);
2792 path = gtk_tree_path_new_first ();
2794 /* Select the row and free */
2795 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2796 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2797 gtk_tree_path_free (path);
2800 gtk_widget_grab_focus (GTK_WIDGET(self));
2806 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2811 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2812 TnyFolder* a_folder;
2815 gtk_tree_model_get (model, iter,
2816 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2817 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2818 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2822 if (folder == a_folder) {
2823 g_object_unref (a_folder);
2824 *folder_iter = *iter;
2827 g_object_unref (a_folder);
2829 if (gtk_tree_model_iter_children (model, &child, iter)) {
2830 if (find_folder_iter (model, &child, folder_iter, folder))
2834 } while (gtk_tree_model_iter_next (model, iter));
2841 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2844 ModestFolderView *self)
2846 ModestFolderViewPrivate *priv = NULL;
2847 GtkTreeSelection *sel;
2848 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2849 GObject *instance = NULL;
2851 if (!MODEST_IS_FOLDER_VIEW(self))
2854 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2856 priv->reexpand = TRUE;
2858 gtk_tree_model_get (tree_model, iter,
2859 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2860 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2862 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2863 priv->folder_to_select = g_object_ref (instance);
2865 g_object_unref (instance);
2868 if (priv->folder_to_select) {
2870 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2873 path = gtk_tree_model_get_path (tree_model, iter);
2874 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2876 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2878 gtk_tree_selection_select_iter (sel, iter);
2879 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2881 gtk_tree_path_free (path);
2886 modest_folder_view_disable_next_folder_selection (self);
2888 /* Refilter the model */
2889 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2895 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2897 ModestFolderViewPrivate *priv;
2899 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2901 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2903 if (priv->folder_to_select)
2904 g_object_unref(priv->folder_to_select);
2906 priv->folder_to_select = NULL;
2910 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2911 gboolean after_change)
2913 GtkTreeModel *model;
2914 GtkTreeIter iter, folder_iter;
2915 GtkTreeSelection *sel;
2916 ModestFolderViewPrivate *priv = NULL;
2918 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2919 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2921 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2924 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2925 gtk_tree_selection_unselect_all (sel);
2927 if (priv->folder_to_select)
2928 g_object_unref(priv->folder_to_select);
2929 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2933 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2938 /* Refilter the model, before selecting the folder */
2939 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2941 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2942 g_warning ("%s: model is empty", __FUNCTION__);
2946 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2949 path = gtk_tree_model_get_path (model, &folder_iter);
2950 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2952 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2953 gtk_tree_selection_select_iter (sel, &folder_iter);
2954 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2956 gtk_tree_path_free (path);
2964 modest_folder_view_copy_selection (ModestFolderView *self)
2966 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2968 /* Copy selection */
2969 _clipboard_set_selected_data (self, FALSE);
2973 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2975 ModestFolderViewPrivate *priv = NULL;
2976 GtkTreeModel *model = NULL;
2977 const gchar **hidding = NULL;
2978 guint i, n_selected;
2980 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2981 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2983 /* Copy selection */
2984 if (!_clipboard_set_selected_data (folder_view, TRUE))
2987 /* Get hidding ids */
2988 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2990 /* Clear hidding array created by previous cut operation */
2991 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2993 /* Copy hidding array */
2994 priv->n_selected = n_selected;
2995 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2996 for (i=0; i < n_selected; i++)
2997 priv->hidding_ids[i] = g_strdup(hidding[i]);
2999 /* Hide cut folders */
3000 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3001 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3005 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3006 ModestFolderView *folder_view_dst)
3008 GtkTreeModel *filter_model = NULL;
3009 GtkTreeModel *model = NULL;
3010 GtkTreeModel *new_filter_model = NULL;
3012 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3013 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3016 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3017 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3019 /* Build new filter model */
3020 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3021 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3025 /* Set copied model */
3026 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3027 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3028 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3031 g_object_unref (new_filter_model);
3035 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3038 GtkTreeModel *model = NULL;
3039 ModestFolderViewPrivate* priv;
3041 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3043 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3044 priv->show_non_move = show;
3045 /* modest_folder_view_update_model(folder_view, */
3046 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3048 /* Hide special folders */
3049 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3050 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3051 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3055 /* Returns FALSE if it did not selected anything */
3057 _clipboard_set_selected_data (ModestFolderView *folder_view,
3060 ModestFolderViewPrivate *priv = NULL;
3061 TnyFolderStore *folder = NULL;
3062 gboolean retval = FALSE;
3064 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3065 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3067 /* Set selected data on clipboard */
3068 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3069 folder = modest_folder_view_get_selected (folder_view);
3071 /* Do not allow to select an account */
3072 if (TNY_IS_FOLDER (folder)) {
3073 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3078 g_object_unref (folder);
3084 _clear_hidding_filter (ModestFolderView *folder_view)
3086 ModestFolderViewPrivate *priv;
3089 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3090 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3092 if (priv->hidding_ids != NULL) {
3093 for (i=0; i < priv->n_selected; i++)
3094 g_free (priv->hidding_ids[i]);
3095 g_free(priv->hidding_ids);
3101 on_display_name_changed (ModestAccountMgr *mgr,
3102 const gchar *account,
3105 ModestFolderView *self;
3107 self = MODEST_FOLDER_VIEW (user_data);
3109 /* Force a redraw */
3110 #if GTK_CHECK_VERSION(2, 8, 0)
3111 GtkTreeViewColumn * tree_column;
3113 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3114 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3115 gtk_tree_view_column_queue_resize (tree_column);
3117 gtk_widget_queue_draw (GTK_WIDGET (self));