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);
1219 /* If we're adding a new account, and there is no previous
1220 one, we need to select the visible server account */
1221 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1222 !priv->visible_account_id)
1223 modest_widget_memory_restore (modest_runtime_get_conf(),
1224 G_OBJECT (user_data),
1225 MODEST_CONF_FOLDER_VIEW_KEY);
1227 if (!GTK_IS_TREE_VIEW(user_data)) {
1228 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1232 /* Get the inner model */
1233 /* check, is some rare cases, we did not get the right thing here,
1235 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1236 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1237 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1241 /* check, is some rare cases, we did not get the right thing here,
1243 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1244 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1245 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1249 /* Insert the account in the model */
1250 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1251 G_OBJECT (account));
1253 /* Refilter the model */
1254 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1259 on_account_changed (TnyAccountStore *account_store,
1260 TnyAccount *tny_account,
1263 ModestFolderViewPrivate *priv;
1264 GtkTreeModel *sort_model, *filter_model;
1266 /* Ignore transport account insertions, we're not showing them
1267 in the folder view */
1268 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1271 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1272 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1277 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1279 /* Get the inner model */
1280 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1281 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1282 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1287 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1288 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1289 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1293 /* Remove the account from the model */
1294 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1295 G_OBJECT (tny_account));
1297 /* Insert the account in the model */
1298 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1299 G_OBJECT (tny_account));
1301 /* Refilter the model */
1302 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1307 * Selects the first inbox or the local account in an idle
1310 on_idle_select_first_inbox_or_local (gpointer user_data)
1312 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1314 gdk_threads_enter ();
1315 modest_folder_view_select_first_inbox_or_local (self);
1316 gdk_threads_leave ();
1323 on_account_removed (TnyAccountStore *account_store,
1324 TnyAccount *account,
1327 ModestFolderView *self = NULL;
1328 ModestFolderViewPrivate *priv;
1329 GtkTreeModel *sort_model, *filter_model;
1330 GtkTreeSelection *sel = NULL;
1331 gboolean same_account_selected = FALSE;
1333 /* Ignore transport account removals, we're not showing them
1334 in the folder view */
1335 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1338 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1339 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1343 self = MODEST_FOLDER_VIEW (user_data);
1344 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1346 /* Invalidate the cur_folder_store only if the selected folder
1347 belongs to the account that is being removed */
1348 if (priv->cur_folder_store) {
1349 TnyAccount *selected_folder_account = NULL;
1351 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1352 selected_folder_account =
1353 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1355 selected_folder_account =
1356 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1359 if (selected_folder_account == account) {
1360 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1361 gtk_tree_selection_unselect_all (sel);
1362 same_account_selected = TRUE;
1364 g_object_unref (selected_folder_account);
1367 /* Invalidate row to select only if the folder to select
1368 belongs to the account that is being removed*/
1369 if (priv->folder_to_select) {
1370 TnyAccount *folder_to_select_account = NULL;
1372 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1373 if (folder_to_select_account == account) {
1374 modest_folder_view_disable_next_folder_selection (self);
1375 g_object_unref (priv->folder_to_select);
1376 priv->folder_to_select = NULL;
1378 g_object_unref (folder_to_select_account);
1381 /* Remove the account from the model */
1382 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1383 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1384 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1388 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1389 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1390 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1394 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1395 G_OBJECT (account));
1397 /* If the removed account is the currently viewed one then
1398 clear the configuration value. The new visible account will be the default account */
1399 if (priv->visible_account_id &&
1400 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1402 /* Clear the current visible account_id */
1403 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1405 /* Call the restore method, this will set the new visible account */
1406 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1407 MODEST_CONF_FOLDER_VIEW_KEY);
1410 /* Refilter the model */
1411 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1413 /* Select the first INBOX if the currently selected folder
1414 belongs to the account that is being deleted */
1415 if (same_account_selected)
1416 g_idle_add (on_idle_select_first_inbox_or_local, self);
1420 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1422 GtkTreeViewColumn *col;
1424 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1426 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1428 g_printerr ("modest: failed get column for title\n");
1432 gtk_tree_view_column_set_title (col, title);
1433 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1438 modest_folder_view_on_map (ModestFolderView *self,
1439 GdkEventExpose *event,
1442 ModestFolderViewPrivate *priv;
1444 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1446 /* This won't happen often */
1447 if (G_UNLIKELY (priv->reselect)) {
1448 /* Select the first inbox or the local account if not found */
1450 /* TODO: this could cause a lock at startup, so we
1451 comment it for the moment. We know that this will
1452 be a bug, because the INBOX is not selected, but we
1453 need to rewrite some parts of Modest to avoid the
1454 deathlock situation */
1455 /* TODO: check if this is still the case */
1456 priv->reselect = FALSE;
1457 modest_folder_view_select_first_inbox_or_local (self);
1458 /* Notify the display name observers */
1459 g_signal_emit (G_OBJECT(self),
1460 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1464 if (priv->reexpand) {
1465 expand_root_items (self);
1466 priv->reexpand = FALSE;
1473 modest_folder_view_new (TnyFolderStoreQuery *query)
1476 ModestFolderViewPrivate *priv;
1477 GtkTreeSelection *sel;
1479 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1480 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1483 priv->query = g_object_ref (query);
1485 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1486 priv->changed_signal = g_signal_connect (sel, "changed",
1487 G_CALLBACK (on_selection_changed), self);
1489 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1491 return GTK_WIDGET(self);
1494 /* this feels dirty; any other way to expand all the root items? */
1496 expand_root_items (ModestFolderView *self)
1499 GtkTreeModel *model;
1502 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1503 path = gtk_tree_path_new_first ();
1505 /* all folders should have child items, so.. */
1507 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1508 gtk_tree_path_next (path);
1509 } while (gtk_tree_model_get_iter (model, &iter, path));
1511 gtk_tree_path_free (path);
1515 * We use this function to implement the
1516 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1517 * account in this case, and the local folders.
1520 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1522 ModestFolderViewPrivate *priv;
1523 gboolean retval = TRUE;
1524 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1525 GObject *instance = NULL;
1526 const gchar *id = NULL;
1528 gboolean found = FALSE;
1529 gboolean cleared = FALSE;
1531 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1532 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1534 gtk_tree_model_get (model, iter,
1535 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1536 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1539 /* Do not show if there is no instance, this could indeed
1540 happen when the model is being modified while it's being
1541 drawn. This could occur for example when moving folders
1546 if (type == TNY_FOLDER_TYPE_ROOT) {
1547 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1548 account instead of a folder. */
1549 if (TNY_IS_ACCOUNT (instance)) {
1550 TnyAccount *acc = TNY_ACCOUNT (instance);
1551 const gchar *account_id = tny_account_get_id (acc);
1553 /* If it isn't a special folder,
1554 * don't show it unless it is the visible account: */
1555 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1556 !modest_tny_account_is_virtual_local_folders (acc) &&
1557 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1559 /* Show only the visible account id */
1560 if (priv->visible_account_id) {
1561 if (strcmp (account_id, priv->visible_account_id))
1568 /* Never show these to the user. They are merged into one folder
1569 * in the local-folders account instead: */
1570 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1575 /* Check hiding (if necessary) */
1576 cleared = modest_email_clipboard_cleared (priv->clipboard);
1577 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1578 id = tny_folder_get_id (TNY_FOLDER(instance));
1579 if (priv->hidding_ids != NULL)
1580 for (i=0; i < priv->n_selected && !found; i++)
1581 if (priv->hidding_ids[i] != NULL && id != NULL)
1582 found = (!strcmp (priv->hidding_ids[i], id));
1588 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1589 folder as no message can be move there according to UI specs */
1590 if (!priv->show_non_move) {
1592 case TNY_FOLDER_TYPE_OUTBOX:
1593 case TNY_FOLDER_TYPE_SENT:
1594 case TNY_FOLDER_TYPE_DRAFTS:
1597 case TNY_FOLDER_TYPE_UNKNOWN:
1598 case TNY_FOLDER_TYPE_NORMAL:
1599 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1600 if (type == TNY_FOLDER_TYPE_INVALID)
1601 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1603 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1604 type == TNY_FOLDER_TYPE_SENT
1605 || type == TNY_FOLDER_TYPE_DRAFTS)
1614 g_object_unref (instance);
1621 modest_folder_view_update_model (ModestFolderView *self,
1622 TnyAccountStore *account_store)
1624 ModestFolderViewPrivate *priv;
1625 GtkTreeModel *model /* , *old_model */;
1626 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1628 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1629 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1632 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1634 /* Notify that there is no folder selected */
1635 g_signal_emit (G_OBJECT(self),
1636 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1638 if (priv->cur_folder_store) {
1639 g_object_unref (priv->cur_folder_store);
1640 priv->cur_folder_store = NULL;
1643 /* FIXME: the local accounts are not shown when the query
1644 selects only the subscribed folders */
1645 model = tny_gtk_folder_store_tree_model_new (NULL);
1647 /* Get the accounts: */
1648 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1650 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1652 sortable = gtk_tree_model_sort_new_with_model (model);
1653 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1654 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1655 GTK_SORT_ASCENDING);
1656 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1657 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1658 cmp_rows, NULL, NULL);
1660 /* Create filter model */
1661 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1662 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1668 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1669 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1670 (GCallback) on_row_inserted_maybe_select_folder, self);
1672 g_object_unref (model);
1673 g_object_unref (filter_model);
1674 g_object_unref (sortable);
1676 /* Force a reselection of the INBOX next time the widget is shown */
1677 priv->reselect = TRUE;
1684 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1686 GtkTreeModel *model = NULL;
1687 TnyFolderStore *folder = NULL;
1689 ModestFolderView *tree_view = NULL;
1690 ModestFolderViewPrivate *priv = NULL;
1691 gboolean selected = FALSE;
1693 g_return_if_fail (sel);
1694 g_return_if_fail (user_data);
1696 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1698 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1700 tree_view = MODEST_FOLDER_VIEW (user_data);
1703 gtk_tree_model_get (model, &iter,
1704 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1707 /* If the folder is the same do not notify */
1708 if (folder && priv->cur_folder_store == folder) {
1709 g_object_unref (folder);
1714 /* Current folder was unselected */
1715 if (priv->cur_folder_store) {
1716 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1717 priv->cur_folder_store, FALSE);
1719 if (TNY_IS_FOLDER(priv->cur_folder_store))
1720 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1721 FALSE, NULL, NULL, NULL);
1723 /* FALSE --> don't expunge the messages */
1725 g_object_unref (priv->cur_folder_store);
1726 priv->cur_folder_store = NULL;
1729 /* New current references */
1730 priv->cur_folder_store = folder;
1732 /* New folder has been selected. Do not notify if there is
1733 nothing new selected */
1735 g_signal_emit (G_OBJECT(tree_view),
1736 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1737 0, priv->cur_folder_store, TRUE);
1742 modest_folder_view_get_selected (ModestFolderView *self)
1744 ModestFolderViewPrivate *priv;
1746 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1748 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1749 if (priv->cur_folder_store)
1750 g_object_ref (priv->cur_folder_store);
1752 return priv->cur_folder_store;
1756 get_cmp_rows_type_pos (GObject *folder)
1758 /* Remote accounts -> Local account -> MMC account .*/
1761 if (TNY_IS_ACCOUNT (folder) &&
1762 modest_tny_account_is_virtual_local_folders (
1763 TNY_ACCOUNT (folder))) {
1765 } else if (TNY_IS_ACCOUNT (folder)) {
1766 TnyAccount *account = TNY_ACCOUNT (folder);
1767 const gchar *account_id = tny_account_get_id (account);
1768 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1774 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1775 return -1; /* Should never happen */
1780 get_cmp_subfolder_type_pos (TnyFolderType t)
1782 /* Inbox, Outbox, Drafts, Sent, User */
1786 case TNY_FOLDER_TYPE_INBOX:
1789 case TNY_FOLDER_TYPE_OUTBOX:
1792 case TNY_FOLDER_TYPE_DRAFTS:
1795 case TNY_FOLDER_TYPE_SENT:
1804 * This function orders the mail accounts according to these rules:
1805 * 1st - remote accounts
1806 * 2nd - local account
1810 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1814 gchar *name1 = NULL;
1815 gchar *name2 = NULL;
1816 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1817 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1818 GObject *folder1 = NULL;
1819 GObject *folder2 = NULL;
1821 gtk_tree_model_get (tree_model, iter1,
1822 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1823 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1824 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1826 gtk_tree_model_get (tree_model, iter2,
1827 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1828 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1829 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1832 /* Return if we get no folder. This could happen when folder
1833 operations are happening. The model is updated after the
1834 folder copy/move actually occurs, so there could be
1835 situations where the model to be drawn is not correct */
1836 if (!folder1 || !folder2)
1839 if (type == TNY_FOLDER_TYPE_ROOT) {
1840 /* Compare the types, so that
1841 * Remote accounts -> Local account -> MMC account .*/
1842 const gint pos1 = get_cmp_rows_type_pos (folder1);
1843 const gint pos2 = get_cmp_rows_type_pos (folder2);
1844 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1845 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1848 else if (pos1 > pos2)
1851 /* Compare items of the same type: */
1853 TnyAccount *account1 = NULL;
1854 if (TNY_IS_ACCOUNT (folder1))
1855 account1 = TNY_ACCOUNT (folder1);
1857 TnyAccount *account2 = NULL;
1858 if (TNY_IS_ACCOUNT (folder2))
1859 account2 = TNY_ACCOUNT (folder2);
1861 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1862 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1864 if (!account_id && !account_id2) {
1866 } else if (!account_id) {
1868 } else if (!account_id2) {
1870 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1873 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1877 gint cmp1 = 0, cmp2 = 0;
1878 /* get the parent to know if it's a local folder */
1881 gboolean has_parent;
1882 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1884 GObject *parent_folder;
1885 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1886 gtk_tree_model_get (tree_model, &parent,
1887 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1888 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1890 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1891 TNY_IS_ACCOUNT (parent_folder)) {
1892 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1893 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1894 (TNY_FOLDER (folder1)));
1895 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1896 (TNY_FOLDER (folder2)));
1897 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1898 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1901 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1907 g_object_unref (parent_folder);
1910 /* if they are not local folders */
1912 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1913 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1917 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1919 cmp = (cmp1 - cmp2);
1924 g_object_unref(G_OBJECT(folder1));
1926 g_object_unref(G_OBJECT(folder2));
1934 /*****************************************************************************/
1935 /* DRAG and DROP stuff */
1936 /*****************************************************************************/
1938 * This function fills the #GtkSelectionData with the row and the
1939 * model that has been dragged. It's called when this widget is a
1940 * source for dnd after the event drop happened
1943 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1944 guint info, guint time, gpointer data)
1946 GtkTreeSelection *selection;
1947 GtkTreeModel *model;
1949 GtkTreePath *source_row;
1951 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1952 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1954 source_row = gtk_tree_model_get_path (model, &iter);
1955 gtk_tree_set_row_drag_data (selection_data,
1959 gtk_tree_path_free (source_row);
1963 typedef struct _DndHelper {
1964 ModestFolderView *folder_view;
1965 gboolean delete_source;
1966 GtkTreePath *source_row;
1970 dnd_helper_destroyer (DndHelper *helper)
1972 /* Free the helper */
1973 gtk_tree_path_free (helper->source_row);
1974 g_slice_free (DndHelper, helper);
1978 xfer_folder_cb (ModestMailOperation *mail_op,
1979 TnyFolder *new_folder,
1983 /* Select the folder */
1984 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
1990 /* get the folder for the row the treepath refers to. */
1991 /* folder must be unref'd */
1992 static TnyFolderStore *
1993 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1996 TnyFolderStore *folder = NULL;
1998 if (gtk_tree_model_get_iter (model,&iter, path))
1999 gtk_tree_model_get (model, &iter,
2000 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2007 * This function is used by drag_data_received_cb to manage drag and
2008 * drop of a header, i.e, and drag from the header view to the folder
2012 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2013 GtkTreeModel *dest_model,
2014 GtkTreePath *dest_row,
2015 GtkSelectionData *selection_data)
2017 TnyList *headers = NULL;
2018 TnyFolder *folder = NULL, *src_folder = NULL;
2019 TnyFolderType folder_type;
2020 GtkTreeIter source_iter, dest_iter;
2021 ModestWindowMgr *mgr = NULL;
2022 ModestWindow *main_win = NULL;
2023 gchar **uris, **tmp;
2025 /* Build the list of headers */
2026 mgr = modest_runtime_get_window_mgr ();
2027 headers = tny_simple_list_new ();
2028 uris = modest_dnd_selection_data_get_paths (selection_data);
2031 while (*tmp != NULL) {
2034 gboolean first = TRUE;
2037 path = gtk_tree_path_new_from_string (*tmp);
2038 gtk_tree_model_get_iter (source_model, &source_iter, path);
2039 gtk_tree_model_get (source_model, &source_iter,
2040 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2043 /* Do not enable d&d of headers already opened */
2044 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2045 tny_list_append (headers, G_OBJECT (header));
2047 if (G_UNLIKELY (first)) {
2048 src_folder = tny_header_get_folder (header);
2052 /* Free and go on */
2053 gtk_tree_path_free (path);
2054 g_object_unref (header);
2059 /* Get the target folder */
2060 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2061 gtk_tree_model_get (dest_model, &dest_iter,
2062 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2065 if (!folder || !TNY_IS_FOLDER(folder)) {
2066 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2070 folder_type = modest_tny_folder_guess_folder_type (folder);
2071 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2072 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2073 goto cleanup; /* cannot move messages there */
2076 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2077 /* g_warning ("folder not writable"); */
2078 goto cleanup; /* verboten! */
2081 /* Ask for confirmation to move */
2082 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2084 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2088 /* Transfer messages */
2089 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2094 if (G_IS_OBJECT (src_folder))
2095 g_object_unref (src_folder);
2096 if (G_IS_OBJECT(folder))
2097 g_object_unref (G_OBJECT (folder));
2098 if (G_IS_OBJECT(headers))
2099 g_object_unref (headers);
2103 TnyFolderStore *src_folder;
2104 TnyFolderStore *dst_folder;
2105 ModestFolderView *folder_view;
2110 dnd_folder_info_destroyer (DndFolderInfo *info)
2112 if (info->src_folder)
2113 g_object_unref (info->src_folder);
2114 if (info->dst_folder)
2115 g_object_unref (info->dst_folder);
2116 g_slice_free (DndFolderInfo, info);
2120 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2121 GtkWindow *parent_window,
2122 TnyAccount *account)
2125 modest_ui_actions_on_account_connection_error (parent_window, account);
2127 /* Free the helper & info */
2128 dnd_helper_destroyer (info->helper);
2129 dnd_folder_info_destroyer (info);
2133 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2135 GtkWindow *parent_window,
2136 TnyAccount *account,
2139 DndFolderInfo *info = NULL;
2140 ModestMailOperation *mail_op;
2142 info = (DndFolderInfo *) user_data;
2144 if (err || canceled) {
2145 dnd_on_connection_failed_destroyer (info, parent_window, account);
2149 /* Do the mail operation */
2150 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2151 modest_ui_actions_move_folder_error_handler,
2152 info->src_folder, NULL);
2154 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2157 /* Transfer the folder */
2158 modest_mail_operation_xfer_folder (mail_op,
2159 TNY_FOLDER (info->src_folder),
2161 info->helper->delete_source,
2163 info->helper->folder_view);
2166 g_object_unref (G_OBJECT (mail_op));
2167 dnd_helper_destroyer (info->helper);
2168 dnd_folder_info_destroyer (info);
2173 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2175 GtkWindow *parent_window,
2176 TnyAccount *account,
2179 DndFolderInfo *info = NULL;
2181 info = (DndFolderInfo *) user_data;
2183 if (err || canceled) {
2184 dnd_on_connection_failed_destroyer (info, parent_window, account);
2188 /* Connect to source folder and perform the copy/move */
2189 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2191 drag_and_drop_from_folder_view_src_folder_performer,
2196 * This function is used by drag_data_received_cb to manage drag and
2197 * drop of a folder, i.e, and drag from the folder view to the same
2201 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2202 GtkTreeModel *dest_model,
2203 GtkTreePath *dest_row,
2204 GtkSelectionData *selection_data,
2207 GtkTreeIter dest_iter, iter;
2208 TnyFolderStore *dest_folder = NULL;
2209 TnyFolderStore *folder = NULL;
2210 gboolean forbidden = FALSE;
2212 DndFolderInfo *info = NULL;
2214 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2216 g_warning ("%s: BUG: no main window", __FUNCTION__);
2217 dnd_helper_destroyer (helper);
2222 /* check the folder rules for the destination */
2223 folder = tree_path_to_folder (dest_model, dest_row);
2224 if (TNY_IS_FOLDER(folder)) {
2225 ModestTnyFolderRules rules =
2226 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2227 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2228 } else if (TNY_IS_FOLDER_STORE(folder)) {
2229 /* enable local root as destination for folders */
2230 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2231 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2234 g_object_unref (folder);
2237 /* check the folder rules for the source */
2238 folder = tree_path_to_folder (source_model, helper->source_row);
2239 if (TNY_IS_FOLDER(folder)) {
2240 ModestTnyFolderRules rules =
2241 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2242 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2245 g_object_unref (folder);
2249 /* Check if the drag is possible */
2250 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2251 dnd_helper_destroyer (helper);
2256 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2257 gtk_tree_model_get (dest_model, &dest_iter,
2258 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2260 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2261 gtk_tree_model_get (source_model, &iter,
2262 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2265 /* Create the info for the performer */
2266 info = g_slice_new (DndFolderInfo);
2267 info->src_folder = g_object_ref (folder);
2268 info->dst_folder = g_object_ref (dest_folder);
2269 info->helper = helper;
2271 /* Connect to the destination folder and perform the copy/move */
2272 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2274 drag_and_drop_from_folder_view_dst_folder_performer,
2278 g_object_unref (dest_folder);
2279 g_object_unref (folder);
2283 * This function receives the data set by the "drag-data-get" signal
2284 * handler. This information comes within the #GtkSelectionData. This
2285 * function will manage both the drags of folders of the treeview and
2286 * drags of headers of the header view widget.
2289 on_drag_data_received (GtkWidget *widget,
2290 GdkDragContext *context,
2293 GtkSelectionData *selection_data,
2298 GtkWidget *source_widget;
2299 GtkTreeModel *dest_model, *source_model;
2300 GtkTreePath *source_row, *dest_row;
2301 GtkTreeViewDropPosition pos;
2302 gboolean delete_source = FALSE;
2303 gboolean success = FALSE;
2305 /* Do not allow further process */
2306 g_signal_stop_emission_by_name (widget, "drag-data-received");
2307 source_widget = gtk_drag_get_source_widget (context);
2309 /* Get the action */
2310 if (context->action == GDK_ACTION_MOVE) {
2311 delete_source = TRUE;
2313 /* Notify that there is no folder selected. We need to
2314 do this in order to update the headers view (and
2315 its monitors, because when moving, the old folder
2316 won't longer exist. We can not wait for the end of
2317 the operation, because the operation won't start if
2318 the folder is in use */
2319 if (source_widget == widget) {
2320 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2321 gtk_tree_selection_unselect_all (sel);
2325 /* Check if the get_data failed */
2326 if (selection_data == NULL || selection_data->length < 0)
2329 /* Select the destination model */
2330 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2332 /* Get the path to the destination row. Can not call
2333 gtk_tree_view_get_drag_dest_row() because the source row
2334 is not selected anymore */
2335 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2338 /* Only allow drops IN other rows */
2340 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2341 pos == GTK_TREE_VIEW_DROP_AFTER)
2345 /* Drags from the header view */
2346 if (source_widget != widget) {
2347 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2349 drag_and_drop_from_header_view (source_model,
2354 DndHelper *helper = NULL;
2356 /* Get the source model and row */
2357 gtk_tree_get_row_drag_data (selection_data,
2361 /* Create the helper */
2362 helper = g_slice_new0 (DndHelper);
2363 helper->delete_source = delete_source;
2364 helper->source_row = gtk_tree_path_copy (source_row);
2365 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2367 drag_and_drop_from_folder_view (source_model,
2373 gtk_tree_path_free (source_row);
2377 gtk_tree_path_free (dest_row);
2380 /* Finish the drag and drop */
2381 gtk_drag_finish (context, success, FALSE, time);
2385 * We define a "drag-drop" signal handler because we do not want to
2386 * use the default one, because the default one always calls
2387 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2388 * signal handler, because there we have all the information available
2389 * to know if the dnd was a success or not.
2392 drag_drop_cb (GtkWidget *widget,
2393 GdkDragContext *context,
2401 if (!context->targets)
2404 /* Check if we're dragging a folder row */
2405 target = gtk_drag_dest_find_target (widget, context, NULL);
2407 /* Request the data from the source. */
2408 gtk_drag_get_data(widget, context, target, time);
2414 * This function expands a node of a tree view if it's not expanded
2415 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2416 * does that, so that's why they're here.
2419 expand_row_timeout (gpointer data)
2421 GtkTreeView *tree_view = data;
2422 GtkTreePath *dest_path = NULL;
2423 GtkTreeViewDropPosition pos;
2424 gboolean result = FALSE;
2426 gdk_threads_enter ();
2428 gtk_tree_view_get_drag_dest_row (tree_view,
2433 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2434 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2435 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2436 gtk_tree_path_free (dest_path);
2440 gtk_tree_path_free (dest_path);
2445 gdk_threads_leave ();
2451 * This function is called whenever the pointer is moved over a widget
2452 * while dragging some data. It installs a timeout that will expand a
2453 * node of the treeview if not expanded yet. This function also calls
2454 * gdk_drag_status in order to set the suggested action that will be
2455 * used by the "drag-data-received" signal handler to know if we
2456 * should do a move or just a copy of the data.
2459 on_drag_motion (GtkWidget *widget,
2460 GdkDragContext *context,
2466 GtkTreeViewDropPosition pos;
2467 GtkTreePath *dest_row;
2468 GtkTreeModel *dest_model;
2469 ModestFolderViewPrivate *priv;
2470 GdkDragAction suggested_action;
2471 gboolean valid_location = FALSE;
2472 TnyFolderStore *folder = NULL;
2474 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2476 if (priv->timer_expander != 0) {
2477 g_source_remove (priv->timer_expander);
2478 priv->timer_expander = 0;
2481 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2486 /* Do not allow drops between folders */
2488 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2489 pos == GTK_TREE_VIEW_DROP_AFTER) {
2490 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2491 gdk_drag_status(context, 0, time);
2492 valid_location = FALSE;
2495 valid_location = TRUE;
2498 /* Check that the destination folder is writable */
2499 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2500 folder = tree_path_to_folder (dest_model, dest_row);
2501 if (folder && TNY_IS_FOLDER (folder)) {
2502 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2504 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2505 valid_location = FALSE;
2510 /* Expand the selected row after 1/2 second */
2511 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2512 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2514 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2516 /* Select the desired action. By default we pick MOVE */
2517 suggested_action = GDK_ACTION_MOVE;
2519 if (context->actions == GDK_ACTION_COPY)
2520 gdk_drag_status(context, GDK_ACTION_COPY, time);
2521 else if (context->actions == GDK_ACTION_MOVE)
2522 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2523 else if (context->actions & suggested_action)
2524 gdk_drag_status(context, suggested_action, time);
2526 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2530 g_object_unref (folder);
2532 gtk_tree_path_free (dest_row);
2534 g_signal_stop_emission_by_name (widget, "drag-motion");
2536 return valid_location;
2540 * This function sets the treeview as a source and a target for dnd
2541 * events. It also connects all the requirede signals.
2544 setup_drag_and_drop (GtkTreeView *self)
2546 /* Set up the folder view as a dnd destination. Set only the
2547 highlight flag, otherwise gtk will have a different
2549 gtk_drag_dest_set (GTK_WIDGET (self),
2550 GTK_DEST_DEFAULT_HIGHLIGHT,
2551 folder_view_drag_types,
2552 G_N_ELEMENTS (folder_view_drag_types),
2553 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2555 g_signal_connect (G_OBJECT (self),
2556 "drag_data_received",
2557 G_CALLBACK (on_drag_data_received),
2561 /* Set up the treeview as a dnd source */
2562 gtk_drag_source_set (GTK_WIDGET (self),
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),
2570 G_CALLBACK (on_drag_motion),
2573 g_signal_connect (G_OBJECT (self),
2575 G_CALLBACK (on_drag_data_get),
2578 g_signal_connect (G_OBJECT (self),
2580 G_CALLBACK (drag_drop_cb),
2585 * This function manages the navigation through the folders using the
2586 * keyboard or the hardware keys in the device
2589 on_key_pressed (GtkWidget *self,
2593 GtkTreeSelection *selection;
2595 GtkTreeModel *model;
2596 gboolean retval = FALSE;
2598 /* Up and Down are automatically managed by the treeview */
2599 if (event->keyval == GDK_Return) {
2600 /* Expand/Collapse the selected row */
2601 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2602 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2605 path = gtk_tree_model_get_path (model, &iter);
2607 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2608 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2610 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2611 gtk_tree_path_free (path);
2613 /* No further processing */
2621 * We listen to the changes in the local folder account name key,
2622 * because we want to show the right name in the view. The local
2623 * folder account name corresponds to the device name in the Maemo
2624 * version. We do this because we do not want to query gconf on each
2625 * tree view refresh. It's better to cache it and change whenever
2629 on_configuration_key_changed (ModestConf* conf,
2631 ModestConfEvent event,
2632 ModestConfNotificationId id,
2633 ModestFolderView *self)
2635 ModestFolderViewPrivate *priv;
2638 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2639 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2641 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2642 g_free (priv->local_account_name);
2644 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2645 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2647 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2648 MODEST_CONF_DEVICE_NAME, NULL);
2650 /* Force a redraw */
2651 #if GTK_CHECK_VERSION(2, 8, 0)
2652 GtkTreeViewColumn * tree_column;
2654 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2655 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2656 gtk_tree_view_column_queue_resize (tree_column);
2658 gtk_widget_queue_draw (GTK_WIDGET (self));
2664 modest_folder_view_set_style (ModestFolderView *self,
2665 ModestFolderViewStyle style)
2667 ModestFolderViewPrivate *priv;
2669 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2670 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2671 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2673 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2676 priv->style = style;
2680 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2681 const gchar *account_id)
2683 ModestFolderViewPrivate *priv;
2684 GtkTreeModel *model;
2686 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2688 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2690 /* This will be used by the filter_row callback,
2691 * to decided which rows to show: */
2692 if (priv->visible_account_id) {
2693 g_free (priv->visible_account_id);
2694 priv->visible_account_id = NULL;
2697 priv->visible_account_id = g_strdup (account_id);
2700 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2701 if (GTK_IS_TREE_MODEL_FILTER (model))
2702 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2704 /* Save settings to gconf */
2705 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2706 MODEST_CONF_FOLDER_VIEW_KEY);
2710 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2712 ModestFolderViewPrivate *priv;
2714 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2716 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2718 return (const gchar *) priv->visible_account_id;
2722 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2726 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2728 gtk_tree_model_get (model, iter,
2729 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2732 gboolean result = FALSE;
2733 if (type == TNY_FOLDER_TYPE_INBOX) {
2737 *inbox_iter = *iter;
2741 if (gtk_tree_model_iter_children (model, &child, iter)) {
2742 if (find_inbox_iter (model, &child, inbox_iter))
2746 } while (gtk_tree_model_iter_next (model, iter));
2755 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2757 GtkTreeModel *model;
2758 GtkTreeIter iter, inbox_iter;
2759 GtkTreeSelection *sel;
2760 GtkTreePath *path = NULL;
2762 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2764 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2768 expand_root_items (self);
2769 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2771 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2772 g_warning ("%s: model is empty", __FUNCTION__);
2776 if (find_inbox_iter (model, &iter, &inbox_iter))
2777 path = gtk_tree_model_get_path (model, &inbox_iter);
2779 path = gtk_tree_path_new_first ();
2781 /* Select the row and free */
2782 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2783 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2784 gtk_tree_path_free (path);
2787 gtk_widget_grab_focus (GTK_WIDGET(self));
2793 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2798 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2799 TnyFolder* a_folder;
2802 gtk_tree_model_get (model, iter,
2803 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2804 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2805 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2809 if (folder == a_folder) {
2810 g_object_unref (a_folder);
2811 *folder_iter = *iter;
2814 g_object_unref (a_folder);
2816 if (gtk_tree_model_iter_children (model, &child, iter)) {
2817 if (find_folder_iter (model, &child, folder_iter, folder))
2821 } while (gtk_tree_model_iter_next (model, iter));
2828 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2831 ModestFolderView *self)
2833 ModestFolderViewPrivate *priv = NULL;
2834 GtkTreeSelection *sel;
2835 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2836 GObject *instance = NULL;
2838 if (!MODEST_IS_FOLDER_VIEW(self))
2841 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2843 priv->reexpand = TRUE;
2845 gtk_tree_model_get (tree_model, iter,
2846 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2847 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2849 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2850 priv->folder_to_select = g_object_ref (instance);
2852 g_object_unref (instance);
2855 if (priv->folder_to_select) {
2857 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2860 path = gtk_tree_model_get_path (tree_model, iter);
2861 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2863 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2865 gtk_tree_selection_select_iter (sel, iter);
2866 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2868 gtk_tree_path_free (path);
2873 modest_folder_view_disable_next_folder_selection (self);
2875 /* Refilter the model */
2876 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2882 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2884 ModestFolderViewPrivate *priv;
2886 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2888 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2890 if (priv->folder_to_select)
2891 g_object_unref(priv->folder_to_select);
2893 priv->folder_to_select = NULL;
2897 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2898 gboolean after_change)
2900 GtkTreeModel *model;
2901 GtkTreeIter iter, folder_iter;
2902 GtkTreeSelection *sel;
2903 ModestFolderViewPrivate *priv = NULL;
2905 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2906 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2908 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2911 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2912 gtk_tree_selection_unselect_all (sel);
2914 if (priv->folder_to_select)
2915 g_object_unref(priv->folder_to_select);
2916 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2920 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2925 /* Refilter the model, before selecting the folder */
2926 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2928 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2929 g_warning ("%s: model is empty", __FUNCTION__);
2933 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2936 path = gtk_tree_model_get_path (model, &folder_iter);
2937 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2939 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2940 gtk_tree_selection_select_iter (sel, &folder_iter);
2941 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2943 gtk_tree_path_free (path);
2951 modest_folder_view_copy_selection (ModestFolderView *self)
2953 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2955 /* Copy selection */
2956 _clipboard_set_selected_data (self, FALSE);
2960 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2962 ModestFolderViewPrivate *priv = NULL;
2963 GtkTreeModel *model = NULL;
2964 const gchar **hidding = NULL;
2965 guint i, n_selected;
2967 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2968 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2970 /* Copy selection */
2971 if (!_clipboard_set_selected_data (folder_view, TRUE))
2974 /* Get hidding ids */
2975 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2977 /* Clear hidding array created by previous cut operation */
2978 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2980 /* Copy hidding array */
2981 priv->n_selected = n_selected;
2982 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2983 for (i=0; i < n_selected; i++)
2984 priv->hidding_ids[i] = g_strdup(hidding[i]);
2986 /* Hide cut folders */
2987 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2988 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2992 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2993 ModestFolderView *folder_view_dst)
2995 GtkTreeModel *filter_model = NULL;
2996 GtkTreeModel *model = NULL;
2997 GtkTreeModel *new_filter_model = NULL;
2999 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3000 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3003 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3004 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3006 /* Build new filter model */
3007 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3008 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3012 /* Set copied model */
3013 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3014 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3015 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3018 g_object_unref (new_filter_model);
3022 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3025 GtkTreeModel *model = NULL;
3026 ModestFolderViewPrivate* priv;
3028 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3030 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3031 priv->show_non_move = show;
3032 /* modest_folder_view_update_model(folder_view, */
3033 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3035 /* Hide special folders */
3036 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3037 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3038 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3042 /* Returns FALSE if it did not selected anything */
3044 _clipboard_set_selected_data (ModestFolderView *folder_view,
3047 ModestFolderViewPrivate *priv = NULL;
3048 TnyFolderStore *folder = NULL;
3049 gboolean retval = FALSE;
3051 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3052 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3054 /* Set selected data on clipboard */
3055 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3056 folder = modest_folder_view_get_selected (folder_view);
3058 /* Do not allow to select an account */
3059 if (TNY_IS_FOLDER (folder)) {
3060 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3065 g_object_unref (folder);
3071 _clear_hidding_filter (ModestFolderView *folder_view)
3073 ModestFolderViewPrivate *priv;
3076 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3077 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3079 if (priv->hidding_ids != NULL) {
3080 for (i=0; i < priv->n_selected; i++)
3081 g_free (priv->hidding_ids[i]);
3082 g_free(priv->hidding_ids);
3088 on_display_name_changed (ModestAccountMgr *mgr,
3089 const gchar *account,
3092 ModestFolderView *self;
3094 self = MODEST_FOLDER_VIEW (user_data);
3096 /* Force a redraw */
3097 #if GTK_CHECK_VERSION(2, 8, 0)
3098 GtkTreeViewColumn * tree_column;
3100 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3101 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3102 gtk_tree_view_column_queue_resize (tree_column);
3104 gtk_widget_queue_draw (GTK_WIDGET (self));