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 if (priv->folder_to_select) {
1115 g_object_unref (G_OBJECT(priv->folder_to_select));
1116 priv->folder_to_select = NULL;
1119 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1121 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1123 g_free (priv->local_account_name);
1124 g_free (priv->visible_account_id);
1126 if (priv->conf_key_signal) {
1127 g_signal_handler_disconnect (modest_runtime_get_conf (),
1128 priv->conf_key_signal);
1129 priv->conf_key_signal = 0;
1132 if (priv->cur_folder_store) {
1133 g_object_unref (priv->cur_folder_store);
1134 priv->cur_folder_store = NULL;
1137 /* Clear hidding array created by cut operation */
1138 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1140 G_OBJECT_CLASS(parent_class)->finalize (obj);
1145 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1147 ModestFolderViewPrivate *priv;
1150 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1151 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1153 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1154 device = tny_account_store_get_device (account_store);
1156 if (G_UNLIKELY (priv->account_store)) {
1158 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1159 priv->account_inserted_signal))
1160 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1161 priv->account_inserted_signal);
1162 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1163 priv->account_removed_signal))
1164 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1165 priv->account_removed_signal);
1166 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1167 priv->account_changed_signal))
1168 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1169 priv->account_changed_signal);
1170 g_object_unref (G_OBJECT (priv->account_store));
1173 priv->account_store = g_object_ref (G_OBJECT (account_store));
1175 priv->account_removed_signal =
1176 g_signal_connect (G_OBJECT(account_store), "account_removed",
1177 G_CALLBACK (on_account_removed), self);
1179 priv->account_inserted_signal =
1180 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1181 G_CALLBACK (on_account_inserted), self);
1183 priv->account_changed_signal =
1184 g_signal_connect (G_OBJECT(account_store), "account_changed",
1185 G_CALLBACK (on_account_changed), self);
1187 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1188 priv->reselect = FALSE;
1189 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1191 g_object_unref (G_OBJECT (device));
1195 on_account_inserted (TnyAccountStore *account_store,
1196 TnyAccount *account,
1199 ModestFolderViewPrivate *priv;
1200 GtkTreeModel *sort_model, *filter_model;
1202 /* Ignore transport account insertions, we're not showing them
1203 in the folder view */
1204 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1207 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1210 /* If we're adding a new account, and there is no previous
1211 one, we need to select the visible server account */
1212 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1213 !priv->visible_account_id)
1214 modest_widget_memory_restore (modest_runtime_get_conf(),
1215 G_OBJECT (user_data),
1216 MODEST_CONF_FOLDER_VIEW_KEY);
1218 if (!GTK_IS_TREE_VIEW(user_data)) {
1219 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1223 /* Get the inner model */
1224 /* check, is some rare cases, we did not get the right thing here,
1226 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1227 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1228 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1232 /* check, is some rare cases, we did not get the right thing here,
1234 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1235 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1236 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1240 /* Insert the account in the model */
1241 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1242 G_OBJECT (account));
1244 /* Refilter the model */
1245 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1250 on_account_changed (TnyAccountStore *account_store,
1251 TnyAccount *tny_account,
1254 ModestFolderViewPrivate *priv;
1255 GtkTreeModel *sort_model, *filter_model;
1256 GtkTreeSelection *sel;
1258 /* Ignore transport account insertions, we're not showing them
1259 in the folder view */
1260 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1263 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1264 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1269 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1271 /* Get the inner model */
1272 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1273 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1274 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1279 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1280 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1281 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1285 /* Unselect the folder, clear the header list */
1286 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (user_data));
1287 gtk_tree_selection_unselect_all (sel);
1289 /* Remove the account from the model */
1290 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1291 G_OBJECT (tny_account));
1293 /* Insert the account in the model */
1294 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1295 G_OBJECT (tny_account));
1297 /* Refilter the model */
1298 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1303 * Selects the first inbox or the local account in an idle
1306 on_idle_select_first_inbox_or_local (gpointer user_data)
1308 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1310 gdk_threads_enter ();
1311 modest_folder_view_select_first_inbox_or_local (self);
1312 gdk_threads_leave ();
1319 on_account_removed (TnyAccountStore *account_store,
1320 TnyAccount *account,
1323 ModestFolderView *self = NULL;
1324 ModestFolderViewPrivate *priv;
1325 GtkTreeModel *sort_model, *filter_model;
1326 GtkTreeSelection *sel = NULL;
1327 gboolean same_account_selected = FALSE;
1329 /* Ignore transport account removals, we're not showing them
1330 in the folder view */
1331 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1334 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1335 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1339 self = MODEST_FOLDER_VIEW (user_data);
1340 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1342 /* Invalidate the cur_folder_store only if the selected folder
1343 belongs to the account that is being removed */
1344 if (priv->cur_folder_store) {
1345 TnyAccount *selected_folder_account = NULL;
1347 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1348 selected_folder_account =
1349 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1351 selected_folder_account =
1352 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1355 if (selected_folder_account == account) {
1356 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1357 gtk_tree_selection_unselect_all (sel);
1358 same_account_selected = TRUE;
1360 g_object_unref (selected_folder_account);
1363 /* Invalidate row to select only if the folder to select
1364 belongs to the account that is being removed*/
1365 if (priv->folder_to_select) {
1366 TnyAccount *folder_to_select_account = NULL;
1368 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1369 if (folder_to_select_account == account) {
1370 modest_folder_view_disable_next_folder_selection (self);
1371 g_object_unref (priv->folder_to_select);
1372 priv->folder_to_select = NULL;
1374 g_object_unref (folder_to_select_account);
1377 /* Remove the account from the model */
1378 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1379 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1380 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1384 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1385 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1386 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1390 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1391 G_OBJECT (account));
1393 /* If the removed account is the currently viewed one then
1394 clear the configuration value. The new visible account will be the default account */
1395 if (priv->visible_account_id &&
1396 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1398 /* Clear the current visible account_id */
1399 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1401 /* Call the restore method, this will set the new visible account */
1402 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1403 MODEST_CONF_FOLDER_VIEW_KEY);
1406 /* Refilter the model */
1407 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1409 /* Select the first INBOX if the currently selected folder
1410 belongs to the account that is being deleted */
1411 if (same_account_selected)
1412 g_idle_add (on_idle_select_first_inbox_or_local, self);
1416 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1418 GtkTreeViewColumn *col;
1420 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1422 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1424 g_printerr ("modest: failed get column for title\n");
1428 gtk_tree_view_column_set_title (col, title);
1429 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1434 modest_folder_view_on_map (ModestFolderView *self,
1435 GdkEventExpose *event,
1438 ModestFolderViewPrivate *priv;
1440 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1442 /* This won't happen often */
1443 if (G_UNLIKELY (priv->reselect)) {
1444 /* Select the first inbox or the local account if not found */
1446 /* TODO: this could cause a lock at startup, so we
1447 comment it for the moment. We know that this will
1448 be a bug, because the INBOX is not selected, but we
1449 need to rewrite some parts of Modest to avoid the
1450 deathlock situation */
1451 /* TODO: check if this is still the case */
1452 priv->reselect = FALSE;
1453 modest_folder_view_select_first_inbox_or_local (self);
1454 /* Notify the display name observers */
1455 g_signal_emit (G_OBJECT(self),
1456 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1460 if (priv->reexpand) {
1461 expand_root_items (self);
1462 priv->reexpand = FALSE;
1469 modest_folder_view_new (TnyFolderStoreQuery *query)
1472 ModestFolderViewPrivate *priv;
1473 GtkTreeSelection *sel;
1475 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1476 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1479 priv->query = g_object_ref (query);
1481 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1482 priv->changed_signal = g_signal_connect (sel, "changed",
1483 G_CALLBACK (on_selection_changed), self);
1485 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1487 return GTK_WIDGET(self);
1490 /* this feels dirty; any other way to expand all the root items? */
1492 expand_root_items (ModestFolderView *self)
1495 GtkTreeModel *model;
1498 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1499 path = gtk_tree_path_new_first ();
1501 /* all folders should have child items, so.. */
1503 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1504 gtk_tree_path_next (path);
1505 } while (gtk_tree_model_get_iter (model, &iter, path));
1507 gtk_tree_path_free (path);
1511 * We use this function to implement the
1512 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1513 * account in this case, and the local folders.
1516 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1518 ModestFolderViewPrivate *priv;
1519 gboolean retval = TRUE;
1520 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1521 GObject *instance = NULL;
1522 const gchar *id = NULL;
1524 gboolean found = FALSE;
1525 gboolean cleared = FALSE;
1527 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1528 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1530 gtk_tree_model_get (model, iter,
1531 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1532 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1535 /* Do not show if there is no instance, this could indeed
1536 happen when the model is being modified while it's being
1537 drawn. This could occur for example when moving folders
1542 if (type == TNY_FOLDER_TYPE_ROOT) {
1543 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1544 account instead of a folder. */
1545 if (TNY_IS_ACCOUNT (instance)) {
1546 TnyAccount *acc = TNY_ACCOUNT (instance);
1547 const gchar *account_id = tny_account_get_id (acc);
1549 /* If it isn't a special folder,
1550 * don't show it unless it is the visible account: */
1551 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1552 !modest_tny_account_is_virtual_local_folders (acc) &&
1553 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1555 /* Show only the visible account id */
1556 if (priv->visible_account_id) {
1557 if (strcmp (account_id, priv->visible_account_id))
1564 /* Never show these to the user. They are merged into one folder
1565 * in the local-folders account instead: */
1566 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1571 /* Check hiding (if necessary) */
1572 cleared = modest_email_clipboard_cleared (priv->clipboard);
1573 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1574 id = tny_folder_get_id (TNY_FOLDER(instance));
1575 if (priv->hidding_ids != NULL)
1576 for (i=0; i < priv->n_selected && !found; i++)
1577 if (priv->hidding_ids[i] != NULL && id != NULL)
1578 found = (!strcmp (priv->hidding_ids[i], id));
1584 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1585 folder as no message can be move there according to UI specs */
1586 if (!priv->show_non_move) {
1588 case TNY_FOLDER_TYPE_OUTBOX:
1589 case TNY_FOLDER_TYPE_SENT:
1590 case TNY_FOLDER_TYPE_DRAFTS:
1593 case TNY_FOLDER_TYPE_UNKNOWN:
1594 case TNY_FOLDER_TYPE_NORMAL:
1595 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1596 if (type == TNY_FOLDER_TYPE_INVALID)
1597 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1599 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1600 type == TNY_FOLDER_TYPE_SENT
1601 || type == TNY_FOLDER_TYPE_DRAFTS)
1610 g_object_unref (instance);
1617 modest_folder_view_update_model (ModestFolderView *self,
1618 TnyAccountStore *account_store)
1620 ModestFolderViewPrivate *priv;
1621 GtkTreeModel *model /* , *old_model */;
1622 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1624 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1625 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1628 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1630 /* Notify that there is no folder selected */
1631 g_signal_emit (G_OBJECT(self),
1632 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1634 if (priv->cur_folder_store) {
1635 g_object_unref (priv->cur_folder_store);
1636 priv->cur_folder_store = NULL;
1639 /* FIXME: the local accounts are not shown when the query
1640 selects only the subscribed folders */
1641 model = tny_gtk_folder_store_tree_model_new (NULL);
1643 /* Get the accounts: */
1644 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1646 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1648 sortable = gtk_tree_model_sort_new_with_model (model);
1649 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1650 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1651 GTK_SORT_ASCENDING);
1652 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1653 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1654 cmp_rows, NULL, NULL);
1656 /* Create filter model */
1657 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1658 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1664 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1665 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1666 (GCallback) on_row_inserted_maybe_select_folder, self);
1668 g_object_unref (model);
1669 g_object_unref (filter_model);
1670 g_object_unref (sortable);
1672 /* Force a reselection of the INBOX next time the widget is shown */
1673 priv->reselect = TRUE;
1680 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1682 GtkTreeModel *model = NULL;
1683 TnyFolderStore *folder = NULL;
1685 ModestFolderView *tree_view = NULL;
1686 ModestFolderViewPrivate *priv = NULL;
1687 gboolean selected = FALSE;
1689 g_return_if_fail (sel);
1690 g_return_if_fail (user_data);
1692 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1694 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1696 tree_view = MODEST_FOLDER_VIEW (user_data);
1699 gtk_tree_model_get (model, &iter,
1700 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1703 /* If the folder is the same do not notify */
1704 if (folder && priv->cur_folder_store == folder) {
1705 g_object_unref (folder);
1710 /* Current folder was unselected */
1711 if (priv->cur_folder_store) {
1712 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1713 priv->cur_folder_store, FALSE);
1715 if (TNY_IS_FOLDER(priv->cur_folder_store))
1716 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1717 FALSE, NULL, NULL, NULL);
1719 /* FALSE --> don't expunge the messages */
1721 g_object_unref (priv->cur_folder_store);
1722 priv->cur_folder_store = NULL;
1725 /* New current references */
1726 priv->cur_folder_store = folder;
1728 /* New folder has been selected. Do not notify if there is
1729 nothing new selected */
1731 g_signal_emit (G_OBJECT(tree_view),
1732 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1733 0, priv->cur_folder_store, TRUE);
1738 modest_folder_view_get_selected (ModestFolderView *self)
1740 ModestFolderViewPrivate *priv;
1742 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1744 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1745 if (priv->cur_folder_store)
1746 g_object_ref (priv->cur_folder_store);
1748 return priv->cur_folder_store;
1752 get_cmp_rows_type_pos (GObject *folder)
1754 /* Remote accounts -> Local account -> MMC account .*/
1757 if (TNY_IS_ACCOUNT (folder) &&
1758 modest_tny_account_is_virtual_local_folders (
1759 TNY_ACCOUNT (folder))) {
1761 } else if (TNY_IS_ACCOUNT (folder)) {
1762 TnyAccount *account = TNY_ACCOUNT (folder);
1763 const gchar *account_id = tny_account_get_id (account);
1764 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1770 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1771 return -1; /* Should never happen */
1776 get_cmp_subfolder_type_pos (TnyFolderType t)
1778 /* Inbox, Outbox, Drafts, Sent, User */
1782 case TNY_FOLDER_TYPE_INBOX:
1785 case TNY_FOLDER_TYPE_OUTBOX:
1788 case TNY_FOLDER_TYPE_DRAFTS:
1791 case TNY_FOLDER_TYPE_SENT:
1800 * This function orders the mail accounts according to these rules:
1801 * 1st - remote accounts
1802 * 2nd - local account
1806 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1810 gchar *name1 = NULL;
1811 gchar *name2 = NULL;
1812 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1813 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1814 GObject *folder1 = NULL;
1815 GObject *folder2 = NULL;
1817 gtk_tree_model_get (tree_model, iter1,
1818 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1819 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1820 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1822 gtk_tree_model_get (tree_model, iter2,
1823 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1824 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1825 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1828 /* Return if we get no folder. This could happen when folder
1829 operations are happening. The model is updated after the
1830 folder copy/move actually occurs, so there could be
1831 situations where the model to be drawn is not correct */
1832 if (!folder1 || !folder2)
1835 if (type == TNY_FOLDER_TYPE_ROOT) {
1836 /* Compare the types, so that
1837 * Remote accounts -> Local account -> MMC account .*/
1838 const gint pos1 = get_cmp_rows_type_pos (folder1);
1839 const gint pos2 = get_cmp_rows_type_pos (folder2);
1840 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1841 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1844 else if (pos1 > pos2)
1847 /* Compare items of the same type: */
1849 TnyAccount *account1 = NULL;
1850 if (TNY_IS_ACCOUNT (folder1))
1851 account1 = TNY_ACCOUNT (folder1);
1853 TnyAccount *account2 = NULL;
1854 if (TNY_IS_ACCOUNT (folder2))
1855 account2 = TNY_ACCOUNT (folder2);
1857 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1858 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1860 if (!account_id && !account_id2) {
1862 } else if (!account_id) {
1864 } else if (!account_id2) {
1866 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1869 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1873 gint cmp1 = 0, cmp2 = 0;
1874 /* get the parent to know if it's a local folder */
1877 gboolean has_parent;
1878 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1880 GObject *parent_folder;
1881 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1882 gtk_tree_model_get (tree_model, &parent,
1883 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1884 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1886 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1887 TNY_IS_ACCOUNT (parent_folder)) {
1888 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1889 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1890 (TNY_FOLDER (folder1)));
1891 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1892 (TNY_FOLDER (folder2)));
1893 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1894 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1897 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1903 g_object_unref (parent_folder);
1906 /* if they are not local folders */
1908 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1909 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1913 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1915 cmp = (cmp1 - cmp2);
1920 g_object_unref(G_OBJECT(folder1));
1922 g_object_unref(G_OBJECT(folder2));
1930 /*****************************************************************************/
1931 /* DRAG and DROP stuff */
1932 /*****************************************************************************/
1934 * This function fills the #GtkSelectionData with the row and the
1935 * model that has been dragged. It's called when this widget is a
1936 * source for dnd after the event drop happened
1939 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1940 guint info, guint time, gpointer data)
1942 GtkTreeSelection *selection;
1943 GtkTreeModel *model;
1945 GtkTreePath *source_row;
1947 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1948 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1950 source_row = gtk_tree_model_get_path (model, &iter);
1951 gtk_tree_set_row_drag_data (selection_data,
1955 gtk_tree_path_free (source_row);
1959 typedef struct _DndHelper {
1960 ModestFolderView *folder_view;
1961 gboolean delete_source;
1962 GtkTreePath *source_row;
1966 dnd_helper_destroyer (DndHelper *helper)
1968 /* Free the helper */
1969 gtk_tree_path_free (helper->source_row);
1970 g_slice_free (DndHelper, helper);
1974 xfer_folder_cb (ModestMailOperation *mail_op,
1975 TnyFolder *new_folder,
1979 /* Select the folder */
1980 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
1986 /* get the folder for the row the treepath refers to. */
1987 /* folder must be unref'd */
1988 static TnyFolderStore *
1989 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1992 TnyFolderStore *folder = NULL;
1994 if (gtk_tree_model_get_iter (model,&iter, path))
1995 gtk_tree_model_get (model, &iter,
1996 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2003 * This function is used by drag_data_received_cb to manage drag and
2004 * drop of a header, i.e, and drag from the header view to the folder
2008 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2009 GtkTreeModel *dest_model,
2010 GtkTreePath *dest_row,
2011 GtkSelectionData *selection_data)
2013 TnyList *headers = NULL;
2014 TnyFolder *folder = NULL, *src_folder = NULL;
2015 TnyFolderType folder_type;
2016 GtkTreeIter source_iter, dest_iter;
2017 ModestWindowMgr *mgr = NULL;
2018 ModestWindow *main_win = NULL;
2019 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) {
2030 gboolean first = TRUE;
2033 path = gtk_tree_path_new_from_string (*tmp);
2034 gtk_tree_model_get_iter (source_model, &source_iter, path);
2035 gtk_tree_model_get (source_model, &source_iter,
2036 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2039 /* Do not enable d&d of headers already opened */
2040 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2041 tny_list_append (headers, G_OBJECT (header));
2043 if (G_UNLIKELY (first)) {
2044 src_folder = tny_header_get_folder (header);
2048 /* Free and go on */
2049 gtk_tree_path_free (path);
2050 g_object_unref (header);
2055 /* Get the target folder */
2056 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2057 gtk_tree_model_get (dest_model, &dest_iter,
2058 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2061 if (!folder || !TNY_IS_FOLDER(folder)) {
2062 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2066 folder_type = modest_tny_folder_guess_folder_type (folder);
2067 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2068 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2069 goto cleanup; /* cannot move messages there */
2072 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2073 /* g_warning ("folder not writable"); */
2074 goto cleanup; /* verboten! */
2077 /* Ask for confirmation to move */
2078 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2080 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2084 /* Transfer messages */
2085 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2090 if (G_IS_OBJECT (src_folder))
2091 g_object_unref (src_folder);
2092 if (G_IS_OBJECT(folder))
2093 g_object_unref (G_OBJECT (folder));
2094 if (G_IS_OBJECT(headers))
2095 g_object_unref (headers);
2099 TnyFolderStore *src_folder;
2100 TnyFolderStore *dst_folder;
2101 ModestFolderView *folder_view;
2106 dnd_folder_info_destroyer (DndFolderInfo *info)
2108 if (info->src_folder)
2109 g_object_unref (info->src_folder);
2110 if (info->dst_folder)
2111 g_object_unref (info->dst_folder);
2112 g_slice_free (DndFolderInfo, info);
2116 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2117 GtkWindow *parent_window,
2118 TnyAccount *account)
2121 modest_ui_actions_on_account_connection_error (parent_window, account);
2123 /* Free the helper & info */
2124 dnd_helper_destroyer (info->helper);
2125 dnd_folder_info_destroyer (info);
2129 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2131 GtkWindow *parent_window,
2132 TnyAccount *account,
2135 DndFolderInfo *info = NULL;
2136 ModestMailOperation *mail_op;
2138 info = (DndFolderInfo *) user_data;
2140 if (err || canceled) {
2141 dnd_on_connection_failed_destroyer (info, parent_window, account);
2145 /* Do the mail operation */
2146 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2147 modest_ui_actions_move_folder_error_handler,
2148 info->src_folder, NULL);
2150 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2153 /* Transfer the folder */
2154 modest_mail_operation_xfer_folder (mail_op,
2155 TNY_FOLDER (info->src_folder),
2157 info->helper->delete_source,
2159 info->helper->folder_view);
2162 g_object_unref (G_OBJECT (mail_op));
2163 dnd_helper_destroyer (info->helper);
2164 dnd_folder_info_destroyer (info);
2169 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2171 GtkWindow *parent_window,
2172 TnyAccount *account,
2175 DndFolderInfo *info = NULL;
2177 info = (DndFolderInfo *) user_data;
2179 if (err || canceled) {
2180 dnd_on_connection_failed_destroyer (info, parent_window, account);
2184 /* Connect to source folder and perform the copy/move */
2185 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2187 drag_and_drop_from_folder_view_src_folder_performer,
2192 * This function is used by drag_data_received_cb to manage drag and
2193 * drop of a folder, i.e, and drag from the folder view to the same
2197 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2198 GtkTreeModel *dest_model,
2199 GtkTreePath *dest_row,
2200 GtkSelectionData *selection_data,
2203 GtkTreeIter dest_iter, iter;
2204 TnyFolderStore *dest_folder = NULL;
2205 TnyFolderStore *folder = NULL;
2206 gboolean forbidden = FALSE;
2208 DndFolderInfo *info = NULL;
2210 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2212 g_warning ("%s: BUG: no main window", __FUNCTION__);
2213 dnd_helper_destroyer (helper);
2218 /* check the folder rules for the destination */
2219 folder = tree_path_to_folder (dest_model, dest_row);
2220 if (TNY_IS_FOLDER(folder)) {
2221 ModestTnyFolderRules rules =
2222 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2223 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2224 } else if (TNY_IS_FOLDER_STORE(folder)) {
2225 /* enable local root as destination for folders */
2226 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2227 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2230 g_object_unref (folder);
2233 /* check the folder rules for the source */
2234 folder = tree_path_to_folder (source_model, helper->source_row);
2235 if (TNY_IS_FOLDER(folder)) {
2236 ModestTnyFolderRules rules =
2237 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2238 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2241 g_object_unref (folder);
2245 /* Check if the drag is possible */
2246 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2247 dnd_helper_destroyer (helper);
2252 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2253 gtk_tree_model_get (dest_model, &dest_iter,
2254 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2256 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2257 gtk_tree_model_get (source_model, &iter,
2258 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2261 /* Create the info for the performer */
2262 info = g_slice_new (DndFolderInfo);
2263 info->src_folder = g_object_ref (folder);
2264 info->dst_folder = g_object_ref (dest_folder);
2265 info->helper = helper;
2267 /* Connect to the destination folder and perform the copy/move */
2268 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2270 drag_and_drop_from_folder_view_dst_folder_performer,
2274 g_object_unref (dest_folder);
2275 g_object_unref (folder);
2279 * This function receives the data set by the "drag-data-get" signal
2280 * handler. This information comes within the #GtkSelectionData. This
2281 * function will manage both the drags of folders of the treeview and
2282 * drags of headers of the header view widget.
2285 on_drag_data_received (GtkWidget *widget,
2286 GdkDragContext *context,
2289 GtkSelectionData *selection_data,
2294 GtkWidget *source_widget;
2295 GtkTreeModel *dest_model, *source_model;
2296 GtkTreePath *source_row, *dest_row;
2297 GtkTreeViewDropPosition pos;
2298 gboolean delete_source = FALSE;
2299 gboolean success = FALSE;
2301 /* Do not allow further process */
2302 g_signal_stop_emission_by_name (widget, "drag-data-received");
2303 source_widget = gtk_drag_get_source_widget (context);
2305 /* Get the action */
2306 if (context->action == GDK_ACTION_MOVE) {
2307 delete_source = TRUE;
2309 /* Notify that there is no folder selected. We need to
2310 do this in order to update the headers view (and
2311 its monitors, because when moving, the old folder
2312 won't longer exist. We can not wait for the end of
2313 the operation, because the operation won't start if
2314 the folder is in use */
2315 if (source_widget == widget) {
2316 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2317 gtk_tree_selection_unselect_all (sel);
2321 /* Check if the get_data failed */
2322 if (selection_data == NULL || selection_data->length < 0)
2325 /* Select the destination model */
2326 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2328 /* Get the path to the destination row. Can not call
2329 gtk_tree_view_get_drag_dest_row() because the source row
2330 is not selected anymore */
2331 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2334 /* Only allow drops IN other rows */
2336 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2337 pos == GTK_TREE_VIEW_DROP_AFTER)
2341 /* Drags from the header view */
2342 if (source_widget != widget) {
2343 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2345 drag_and_drop_from_header_view (source_model,
2350 DndHelper *helper = NULL;
2352 /* Get the source model and row */
2353 gtk_tree_get_row_drag_data (selection_data,
2357 /* Create the helper */
2358 helper = g_slice_new0 (DndHelper);
2359 helper->delete_source = delete_source;
2360 helper->source_row = gtk_tree_path_copy (source_row);
2361 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2363 drag_and_drop_from_folder_view (source_model,
2369 gtk_tree_path_free (source_row);
2373 gtk_tree_path_free (dest_row);
2376 /* Finish the drag and drop */
2377 gtk_drag_finish (context, success, FALSE, time);
2381 * We define a "drag-drop" signal handler because we do not want to
2382 * use the default one, because the default one always calls
2383 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2384 * signal handler, because there we have all the information available
2385 * to know if the dnd was a success or not.
2388 drag_drop_cb (GtkWidget *widget,
2389 GdkDragContext *context,
2397 if (!context->targets)
2400 /* Check if we're dragging a folder row */
2401 target = gtk_drag_dest_find_target (widget, context, NULL);
2403 /* Request the data from the source. */
2404 gtk_drag_get_data(widget, context, target, time);
2410 * This function expands a node of a tree view if it's not expanded
2411 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2412 * does that, so that's why they're here.
2415 expand_row_timeout (gpointer data)
2417 GtkTreeView *tree_view = data;
2418 GtkTreePath *dest_path = NULL;
2419 GtkTreeViewDropPosition pos;
2420 gboolean result = FALSE;
2422 gdk_threads_enter ();
2424 gtk_tree_view_get_drag_dest_row (tree_view,
2429 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2430 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2431 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2432 gtk_tree_path_free (dest_path);
2436 gtk_tree_path_free (dest_path);
2441 gdk_threads_leave ();
2447 * This function is called whenever the pointer is moved over a widget
2448 * while dragging some data. It installs a timeout that will expand a
2449 * node of the treeview if not expanded yet. This function also calls
2450 * gdk_drag_status in order to set the suggested action that will be
2451 * used by the "drag-data-received" signal handler to know if we
2452 * should do a move or just a copy of the data.
2455 on_drag_motion (GtkWidget *widget,
2456 GdkDragContext *context,
2462 GtkTreeViewDropPosition pos;
2463 GtkTreePath *dest_row;
2464 GtkTreeModel *dest_model;
2465 ModestFolderViewPrivate *priv;
2466 GdkDragAction suggested_action;
2467 gboolean valid_location = FALSE;
2468 TnyFolderStore *folder = NULL;
2470 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2472 if (priv->timer_expander != 0) {
2473 g_source_remove (priv->timer_expander);
2474 priv->timer_expander = 0;
2477 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2482 /* Do not allow drops between folders */
2484 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2485 pos == GTK_TREE_VIEW_DROP_AFTER) {
2486 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2487 gdk_drag_status(context, 0, time);
2488 valid_location = FALSE;
2491 valid_location = TRUE;
2494 /* Check that the destination folder is writable */
2495 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2496 folder = tree_path_to_folder (dest_model, dest_row);
2497 if (folder && TNY_IS_FOLDER (folder)) {
2498 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2500 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2501 valid_location = FALSE;
2506 /* Expand the selected row after 1/2 second */
2507 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2508 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2510 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2512 /* Select the desired action. By default we pick MOVE */
2513 suggested_action = GDK_ACTION_MOVE;
2515 if (context->actions == GDK_ACTION_COPY)
2516 gdk_drag_status(context, GDK_ACTION_COPY, time);
2517 else if (context->actions == GDK_ACTION_MOVE)
2518 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2519 else if (context->actions & suggested_action)
2520 gdk_drag_status(context, suggested_action, time);
2522 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2526 g_object_unref (folder);
2528 gtk_tree_path_free (dest_row);
2530 g_signal_stop_emission_by_name (widget, "drag-motion");
2532 return valid_location;
2536 * This function sets the treeview as a source and a target for dnd
2537 * events. It also connects all the requirede signals.
2540 setup_drag_and_drop (GtkTreeView *self)
2542 /* Set up the folder view as a dnd destination. Set only the
2543 highlight flag, otherwise gtk will have a different
2545 gtk_drag_dest_set (GTK_WIDGET (self),
2546 GTK_DEST_DEFAULT_HIGHLIGHT,
2547 folder_view_drag_types,
2548 G_N_ELEMENTS (folder_view_drag_types),
2549 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2551 g_signal_connect (G_OBJECT (self),
2552 "drag_data_received",
2553 G_CALLBACK (on_drag_data_received),
2557 /* Set up the treeview as a dnd source */
2558 gtk_drag_source_set (GTK_WIDGET (self),
2560 folder_view_drag_types,
2561 G_N_ELEMENTS (folder_view_drag_types),
2562 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2564 g_signal_connect (G_OBJECT (self),
2566 G_CALLBACK (on_drag_motion),
2569 g_signal_connect (G_OBJECT (self),
2571 G_CALLBACK (on_drag_data_get),
2574 g_signal_connect (G_OBJECT (self),
2576 G_CALLBACK (drag_drop_cb),
2581 * This function manages the navigation through the folders using the
2582 * keyboard or the hardware keys in the device
2585 on_key_pressed (GtkWidget *self,
2589 GtkTreeSelection *selection;
2591 GtkTreeModel *model;
2592 gboolean retval = FALSE;
2594 /* Up and Down are automatically managed by the treeview */
2595 if (event->keyval == GDK_Return) {
2596 /* Expand/Collapse the selected row */
2597 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2598 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2601 path = gtk_tree_model_get_path (model, &iter);
2603 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2604 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2606 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2607 gtk_tree_path_free (path);
2609 /* No further processing */
2617 * We listen to the changes in the local folder account name key,
2618 * because we want to show the right name in the view. The local
2619 * folder account name corresponds to the device name in the Maemo
2620 * version. We do this because we do not want to query gconf on each
2621 * tree view refresh. It's better to cache it and change whenever
2625 on_configuration_key_changed (ModestConf* conf,
2627 ModestConfEvent event,
2628 ModestConfNotificationId id,
2629 ModestFolderView *self)
2631 ModestFolderViewPrivate *priv;
2634 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2635 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2637 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2638 g_free (priv->local_account_name);
2640 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2641 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2643 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2644 MODEST_CONF_DEVICE_NAME, NULL);
2646 /* Force a redraw */
2647 #if GTK_CHECK_VERSION(2, 8, 0)
2648 GtkTreeViewColumn * tree_column;
2650 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2651 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2652 gtk_tree_view_column_queue_resize (tree_column);
2654 gtk_widget_queue_draw (GTK_WIDGET (self));
2660 modest_folder_view_set_style (ModestFolderView *self,
2661 ModestFolderViewStyle style)
2663 ModestFolderViewPrivate *priv;
2665 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2666 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2667 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2669 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2672 priv->style = style;
2676 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2677 const gchar *account_id)
2679 ModestFolderViewPrivate *priv;
2680 GtkTreeModel *model;
2682 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2684 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2686 /* This will be used by the filter_row callback,
2687 * to decided which rows to show: */
2688 if (priv->visible_account_id) {
2689 g_free (priv->visible_account_id);
2690 priv->visible_account_id = NULL;
2693 priv->visible_account_id = g_strdup (account_id);
2696 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2697 if (GTK_IS_TREE_MODEL_FILTER (model))
2698 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2700 /* Save settings to gconf */
2701 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2702 MODEST_CONF_FOLDER_VIEW_KEY);
2706 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2708 ModestFolderViewPrivate *priv;
2710 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2712 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2714 return (const gchar *) priv->visible_account_id;
2718 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2722 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2724 gtk_tree_model_get (model, iter,
2725 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2728 gboolean result = FALSE;
2729 if (type == TNY_FOLDER_TYPE_INBOX) {
2733 *inbox_iter = *iter;
2737 if (gtk_tree_model_iter_children (model, &child, iter)) {
2738 if (find_inbox_iter (model, &child, inbox_iter))
2742 } while (gtk_tree_model_iter_next (model, iter));
2751 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2753 GtkTreeModel *model;
2754 GtkTreeIter iter, inbox_iter;
2755 GtkTreeSelection *sel;
2756 GtkTreePath *path = NULL;
2758 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2760 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2764 expand_root_items (self);
2765 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2767 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2768 g_warning ("%s: model is empty", __FUNCTION__);
2772 if (find_inbox_iter (model, &iter, &inbox_iter))
2773 path = gtk_tree_model_get_path (model, &inbox_iter);
2775 path = gtk_tree_path_new_first ();
2777 /* Select the row and free */
2778 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2779 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2780 gtk_tree_path_free (path);
2783 gtk_widget_grab_focus (GTK_WIDGET(self));
2789 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2794 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2795 TnyFolder* a_folder;
2798 gtk_tree_model_get (model, iter,
2799 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2800 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2801 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2805 if (folder == a_folder) {
2806 g_object_unref (a_folder);
2807 *folder_iter = *iter;
2810 g_object_unref (a_folder);
2812 if (gtk_tree_model_iter_children (model, &child, iter)) {
2813 if (find_folder_iter (model, &child, folder_iter, folder))
2817 } while (gtk_tree_model_iter_next (model, iter));
2824 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2827 ModestFolderView *self)
2829 ModestFolderViewPrivate *priv = NULL;
2830 GtkTreeSelection *sel;
2831 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2832 GObject *instance = NULL;
2834 if (!MODEST_IS_FOLDER_VIEW(self))
2837 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2839 priv->reexpand = TRUE;
2841 gtk_tree_model_get (tree_model, iter,
2842 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2843 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2845 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2846 priv->folder_to_select = g_object_ref (instance);
2848 g_object_unref (instance);
2850 if (priv->folder_to_select) {
2852 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2855 path = gtk_tree_model_get_path (tree_model, iter);
2856 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2858 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2860 gtk_tree_selection_select_iter (sel, iter);
2861 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2863 gtk_tree_path_free (path);
2867 modest_folder_view_disable_next_folder_selection (self);
2869 /* Refilter the model */
2870 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2876 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2878 ModestFolderViewPrivate *priv;
2880 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2882 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2884 if (priv->folder_to_select)
2885 g_object_unref(priv->folder_to_select);
2887 priv->folder_to_select = NULL;
2891 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2892 gboolean after_change)
2894 GtkTreeModel *model;
2895 GtkTreeIter iter, folder_iter;
2896 GtkTreeSelection *sel;
2897 ModestFolderViewPrivate *priv = NULL;
2899 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2900 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2902 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2905 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2906 gtk_tree_selection_unselect_all (sel);
2908 if (priv->folder_to_select)
2909 g_object_unref(priv->folder_to_select);
2910 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2914 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2919 /* Refilter the model, before selecting the folder */
2920 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2922 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2923 g_warning ("%s: model is empty", __FUNCTION__);
2927 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2930 path = gtk_tree_model_get_path (model, &folder_iter);
2931 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2933 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2934 gtk_tree_selection_select_iter (sel, &folder_iter);
2935 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2937 gtk_tree_path_free (path);
2945 modest_folder_view_copy_selection (ModestFolderView *self)
2947 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2949 /* Copy selection */
2950 _clipboard_set_selected_data (self, FALSE);
2954 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2956 ModestFolderViewPrivate *priv = NULL;
2957 GtkTreeModel *model = NULL;
2958 const gchar **hidding = NULL;
2959 guint i, n_selected;
2961 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2962 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2964 /* Copy selection */
2965 if (!_clipboard_set_selected_data (folder_view, TRUE))
2968 /* Get hidding ids */
2969 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2971 /* Clear hidding array created by previous cut operation */
2972 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2974 /* Copy hidding array */
2975 priv->n_selected = n_selected;
2976 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2977 for (i=0; i < n_selected; i++)
2978 priv->hidding_ids[i] = g_strdup(hidding[i]);
2980 /* Hide cut folders */
2981 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2982 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2986 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2987 ModestFolderView *folder_view_dst)
2989 GtkTreeModel *filter_model = NULL;
2990 GtkTreeModel *model = NULL;
2991 GtkTreeModel *new_filter_model = NULL;
2993 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
2994 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
2997 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2998 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3000 /* Build new filter model */
3001 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3002 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3006 /* Set copied model */
3007 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3008 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3009 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3012 g_object_unref (new_filter_model);
3016 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3019 GtkTreeModel *model = NULL;
3020 ModestFolderViewPrivate* priv;
3022 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3024 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3025 priv->show_non_move = show;
3026 /* modest_folder_view_update_model(folder_view, */
3027 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3029 /* Hide special folders */
3030 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3031 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3032 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3036 /* Returns FALSE if it did not selected anything */
3038 _clipboard_set_selected_data (ModestFolderView *folder_view,
3041 ModestFolderViewPrivate *priv = NULL;
3042 TnyFolderStore *folder = NULL;
3043 gboolean retval = FALSE;
3045 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3046 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3048 /* Set selected data on clipboard */
3049 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3050 folder = modest_folder_view_get_selected (folder_view);
3052 /* Do not allow to select an account */
3053 if (TNY_IS_FOLDER (folder)) {
3054 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3059 g_object_unref (folder);
3065 _clear_hidding_filter (ModestFolderView *folder_view)
3067 ModestFolderViewPrivate *priv;
3070 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3071 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3073 if (priv->hidding_ids != NULL) {
3074 for (i=0; i < priv->n_selected; i++)
3075 g_free (priv->hidding_ids[i]);
3076 g_free(priv->hidding_ids);
3082 on_display_name_changed (ModestAccountMgr *mgr,
3083 const gchar *account,
3086 ModestFolderView *self;
3088 self = MODEST_FOLDER_VIEW (user_data);
3090 /* Force a redraw */
3091 #if GTK_CHECK_VERSION(2, 8, 0)
3092 GtkTreeViewColumn * tree_column;
3094 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3095 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3096 gtk_tree_view_column_queue_resize (tree_column);
3098 gtk_widget_queue_draw (GTK_WIDGET (self));