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);
1199 priv->reselect = FALSE;
1200 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1202 g_object_unref (G_OBJECT (device));
1206 on_account_inserted (TnyAccountStore *account_store,
1207 TnyAccount *account,
1210 ModestFolderViewPrivate *priv;
1211 GtkTreeModel *sort_model, *filter_model;
1213 /* Ignore transport account insertions, we're not showing them
1214 in the folder view */
1215 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1218 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1221 /* If we're adding a new account, and there is no previous
1222 one, we need to select the visible server account */
1223 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1224 !priv->visible_account_id)
1225 modest_widget_memory_restore (modest_runtime_get_conf(),
1226 G_OBJECT (user_data),
1227 MODEST_CONF_FOLDER_VIEW_KEY);
1229 if (!GTK_IS_TREE_VIEW(user_data)) {
1230 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1234 /* Get the inner model */
1235 /* check, is some rare cases, we did not get the right thing here,
1237 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1238 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1239 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1243 /* check, is some rare cases, we did not get the right thing here,
1245 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1246 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1247 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1251 /* Insert the account in the model */
1252 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1253 G_OBJECT (account));
1255 /* Refilter the model */
1256 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1261 on_account_changed (TnyAccountStore *account_store,
1262 TnyAccount *tny_account,
1265 ModestFolderViewPrivate *priv;
1266 GtkTreeModel *sort_model, *filter_model;
1267 GtkTreeSelection *sel;
1269 /* Ignore transport account insertions, we're not showing them
1270 in the folder view */
1271 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1274 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1275 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1280 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1282 /* Get the inner model */
1283 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1284 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1285 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1290 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1291 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1292 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1296 /* Unselect the folder, clear the header list */
1297 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (user_data));
1298 gtk_tree_selection_unselect_all (sel);
1300 /* Remove the account from the model */
1301 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1302 G_OBJECT (tny_account));
1304 /* Insert the account in the model */
1305 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1306 G_OBJECT (tny_account));
1308 /* Refilter the model */
1309 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1314 * Selects the first inbox or the local account in an idle
1317 on_idle_select_first_inbox_or_local (gpointer user_data)
1319 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1321 gdk_threads_enter ();
1322 modest_folder_view_select_first_inbox_or_local (self);
1323 gdk_threads_leave ();
1330 on_account_removed (TnyAccountStore *account_store,
1331 TnyAccount *account,
1334 ModestFolderView *self = NULL;
1335 ModestFolderViewPrivate *priv;
1336 GtkTreeModel *sort_model, *filter_model;
1337 GtkTreeSelection *sel = NULL;
1338 gboolean same_account_selected = FALSE;
1340 /* Ignore transport account removals, we're not showing them
1341 in the folder view */
1342 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1345 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1346 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1350 self = MODEST_FOLDER_VIEW (user_data);
1351 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1353 /* Invalidate the cur_folder_store only if the selected folder
1354 belongs to the account that is being removed */
1355 if (priv->cur_folder_store) {
1356 TnyAccount *selected_folder_account = NULL;
1358 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1359 selected_folder_account =
1360 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1362 selected_folder_account =
1363 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1366 if (selected_folder_account == account) {
1367 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1368 gtk_tree_selection_unselect_all (sel);
1369 same_account_selected = TRUE;
1371 g_object_unref (selected_folder_account);
1374 /* Invalidate row to select only if the folder to select
1375 belongs to the account that is being removed*/
1376 if (priv->folder_to_select) {
1377 TnyAccount *folder_to_select_account = NULL;
1379 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1380 if (folder_to_select_account == account) {
1381 modest_folder_view_disable_next_folder_selection (self);
1382 g_object_unref (priv->folder_to_select);
1383 priv->folder_to_select = NULL;
1385 g_object_unref (folder_to_select_account);
1388 /* Remove the account from the model */
1389 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1390 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1391 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1395 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1396 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1397 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1401 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1402 G_OBJECT (account));
1404 /* If the removed account is the currently viewed one then
1405 clear the configuration value. The new visible account will be the default account */
1406 if (priv->visible_account_id &&
1407 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1409 /* Clear the current visible account_id */
1410 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1412 /* Call the restore method, this will set the new visible account */
1413 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1414 MODEST_CONF_FOLDER_VIEW_KEY);
1417 /* Refilter the model */
1418 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1420 /* Select the first INBOX if the currently selected folder
1421 belongs to the account that is being deleted */
1422 if (same_account_selected)
1423 g_idle_add (on_idle_select_first_inbox_or_local, self);
1427 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1429 GtkTreeViewColumn *col;
1431 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1433 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1435 g_printerr ("modest: failed get column for title\n");
1439 gtk_tree_view_column_set_title (col, title);
1440 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1445 modest_folder_view_on_map (ModestFolderView *self,
1446 GdkEventExpose *event,
1449 ModestFolderViewPrivate *priv;
1451 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1453 /* This won't happen often */
1454 if (G_UNLIKELY (priv->reselect)) {
1455 /* Select the first inbox or the local account if not found */
1457 /* TODO: this could cause a lock at startup, so we
1458 comment it for the moment. We know that this will
1459 be a bug, because the INBOX is not selected, but we
1460 need to rewrite some parts of Modest to avoid the
1461 deathlock situation */
1462 /* TODO: check if this is still the case */
1463 priv->reselect = FALSE;
1464 modest_folder_view_select_first_inbox_or_local (self);
1465 /* Notify the display name observers */
1466 g_signal_emit (G_OBJECT(self),
1467 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1471 if (priv->reexpand) {
1472 expand_root_items (self);
1473 priv->reexpand = FALSE;
1480 modest_folder_view_new (TnyFolderStoreQuery *query)
1483 ModestFolderViewPrivate *priv;
1484 GtkTreeSelection *sel;
1486 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1487 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1490 priv->query = g_object_ref (query);
1492 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1493 priv->changed_signal = g_signal_connect (sel, "changed",
1494 G_CALLBACK (on_selection_changed), self);
1496 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1498 return GTK_WIDGET(self);
1501 /* this feels dirty; any other way to expand all the root items? */
1503 expand_root_items (ModestFolderView *self)
1506 GtkTreeModel *model;
1509 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1510 path = gtk_tree_path_new_first ();
1512 /* all folders should have child items, so.. */
1514 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1515 gtk_tree_path_next (path);
1516 } while (gtk_tree_model_get_iter (model, &iter, path));
1518 gtk_tree_path_free (path);
1522 * We use this function to implement the
1523 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1524 * account in this case, and the local folders.
1527 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1529 ModestFolderViewPrivate *priv;
1530 gboolean retval = TRUE;
1531 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1532 GObject *instance = NULL;
1533 const gchar *id = NULL;
1535 gboolean found = FALSE;
1536 gboolean cleared = FALSE;
1538 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1539 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1541 gtk_tree_model_get (model, iter,
1542 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1543 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1546 /* Do not show if there is no instance, this could indeed
1547 happen when the model is being modified while it's being
1548 drawn. This could occur for example when moving folders
1553 if (type == TNY_FOLDER_TYPE_ROOT) {
1554 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1555 account instead of a folder. */
1556 if (TNY_IS_ACCOUNT (instance)) {
1557 TnyAccount *acc = TNY_ACCOUNT (instance);
1558 const gchar *account_id = tny_account_get_id (acc);
1560 /* If it isn't a special folder,
1561 * don't show it unless it is the visible account: */
1562 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1563 !modest_tny_account_is_virtual_local_folders (acc) &&
1564 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1566 /* Show only the visible account id */
1567 if (priv->visible_account_id) {
1568 if (strcmp (account_id, priv->visible_account_id))
1575 /* Never show these to the user. They are merged into one folder
1576 * in the local-folders account instead: */
1577 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1582 /* Check hiding (if necessary) */
1583 cleared = modest_email_clipboard_cleared (priv->clipboard);
1584 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1585 id = tny_folder_get_id (TNY_FOLDER(instance));
1586 if (priv->hidding_ids != NULL)
1587 for (i=0; i < priv->n_selected && !found; i++)
1588 if (priv->hidding_ids[i] != NULL && id != NULL)
1589 found = (!strcmp (priv->hidding_ids[i], id));
1595 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1596 folder as no message can be move there according to UI specs */
1597 if (!priv->show_non_move) {
1599 case TNY_FOLDER_TYPE_OUTBOX:
1600 case TNY_FOLDER_TYPE_SENT:
1601 case TNY_FOLDER_TYPE_DRAFTS:
1604 case TNY_FOLDER_TYPE_UNKNOWN:
1605 case TNY_FOLDER_TYPE_NORMAL:
1606 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1607 if (type == TNY_FOLDER_TYPE_INVALID)
1608 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1610 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1611 type == TNY_FOLDER_TYPE_SENT
1612 || type == TNY_FOLDER_TYPE_DRAFTS)
1621 g_object_unref (instance);
1628 modest_folder_view_update_model (ModestFolderView *self,
1629 TnyAccountStore *account_store)
1631 ModestFolderViewPrivate *priv;
1632 GtkTreeModel *model /* , *old_model */;
1633 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1635 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1636 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1639 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1641 /* Notify that there is no folder selected */
1642 g_signal_emit (G_OBJECT(self),
1643 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1645 if (priv->cur_folder_store) {
1646 g_object_unref (priv->cur_folder_store);
1647 priv->cur_folder_store = NULL;
1650 /* FIXME: the local accounts are not shown when the query
1651 selects only the subscribed folders */
1652 model = tny_gtk_folder_store_tree_model_new (NULL);
1654 /* Get the accounts: */
1655 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1657 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1659 sortable = gtk_tree_model_sort_new_with_model (model);
1660 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1661 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1662 GTK_SORT_ASCENDING);
1663 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1664 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1665 cmp_rows, NULL, NULL);
1667 /* Create filter model */
1668 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1669 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1675 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1676 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1677 (GCallback) on_row_inserted_maybe_select_folder, self);
1679 g_object_unref (model);
1680 g_object_unref (filter_model);
1681 g_object_unref (sortable);
1683 /* Force a reselection of the INBOX next time the widget is shown */
1684 priv->reselect = TRUE;
1691 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1693 GtkTreeModel *model = NULL;
1694 TnyFolderStore *folder = NULL;
1696 ModestFolderView *tree_view = NULL;
1697 ModestFolderViewPrivate *priv = NULL;
1698 gboolean selected = FALSE;
1700 g_return_if_fail (sel);
1701 g_return_if_fail (user_data);
1703 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1705 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1707 tree_view = MODEST_FOLDER_VIEW (user_data);
1710 gtk_tree_model_get (model, &iter,
1711 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1714 /* If the folder is the same do not notify */
1715 if (folder && priv->cur_folder_store == folder) {
1716 g_object_unref (folder);
1721 /* Current folder was unselected */
1722 if (priv->cur_folder_store) {
1723 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1724 priv->cur_folder_store, FALSE);
1726 if (TNY_IS_FOLDER(priv->cur_folder_store))
1727 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1728 FALSE, NULL, NULL, NULL);
1730 /* FALSE --> don't expunge the messages */
1732 g_object_unref (priv->cur_folder_store);
1733 priv->cur_folder_store = NULL;
1736 /* New current references */
1737 priv->cur_folder_store = folder;
1739 /* New folder has been selected. Do not notify if there is
1740 nothing new selected */
1742 g_signal_emit (G_OBJECT(tree_view),
1743 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1744 0, priv->cur_folder_store, TRUE);
1749 modest_folder_view_get_selected (ModestFolderView *self)
1751 ModestFolderViewPrivate *priv;
1753 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1755 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1756 if (priv->cur_folder_store)
1757 g_object_ref (priv->cur_folder_store);
1759 return priv->cur_folder_store;
1763 get_cmp_rows_type_pos (GObject *folder)
1765 /* Remote accounts -> Local account -> MMC account .*/
1768 if (TNY_IS_ACCOUNT (folder) &&
1769 modest_tny_account_is_virtual_local_folders (
1770 TNY_ACCOUNT (folder))) {
1772 } else if (TNY_IS_ACCOUNT (folder)) {
1773 TnyAccount *account = TNY_ACCOUNT (folder);
1774 const gchar *account_id = tny_account_get_id (account);
1775 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1781 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1782 return -1; /* Should never happen */
1787 get_cmp_subfolder_type_pos (TnyFolderType t)
1789 /* Inbox, Outbox, Drafts, Sent, User */
1793 case TNY_FOLDER_TYPE_INBOX:
1796 case TNY_FOLDER_TYPE_OUTBOX:
1799 case TNY_FOLDER_TYPE_DRAFTS:
1802 case TNY_FOLDER_TYPE_SENT:
1811 * This function orders the mail accounts according to these rules:
1812 * 1st - remote accounts
1813 * 2nd - local account
1817 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1821 gchar *name1 = NULL;
1822 gchar *name2 = NULL;
1823 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1824 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1825 GObject *folder1 = NULL;
1826 GObject *folder2 = NULL;
1828 gtk_tree_model_get (tree_model, iter1,
1829 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1830 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1831 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1833 gtk_tree_model_get (tree_model, iter2,
1834 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1835 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1836 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1839 /* Return if we get no folder. This could happen when folder
1840 operations are happening. The model is updated after the
1841 folder copy/move actually occurs, so there could be
1842 situations where the model to be drawn is not correct */
1843 if (!folder1 || !folder2)
1846 if (type == TNY_FOLDER_TYPE_ROOT) {
1847 /* Compare the types, so that
1848 * Remote accounts -> Local account -> MMC account .*/
1849 const gint pos1 = get_cmp_rows_type_pos (folder1);
1850 const gint pos2 = get_cmp_rows_type_pos (folder2);
1851 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1852 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1855 else if (pos1 > pos2)
1858 /* Compare items of the same type: */
1860 TnyAccount *account1 = NULL;
1861 if (TNY_IS_ACCOUNT (folder1))
1862 account1 = TNY_ACCOUNT (folder1);
1864 TnyAccount *account2 = NULL;
1865 if (TNY_IS_ACCOUNT (folder2))
1866 account2 = TNY_ACCOUNT (folder2);
1868 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1869 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1871 if (!account_id && !account_id2) {
1873 } else if (!account_id) {
1875 } else if (!account_id2) {
1877 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1880 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1884 gint cmp1 = 0, cmp2 = 0;
1885 /* get the parent to know if it's a local folder */
1888 gboolean has_parent;
1889 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1891 GObject *parent_folder;
1892 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1893 gtk_tree_model_get (tree_model, &parent,
1894 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1895 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1897 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1898 TNY_IS_ACCOUNT (parent_folder)) {
1899 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1900 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1901 (TNY_FOLDER (folder1)));
1902 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1903 (TNY_FOLDER (folder2)));
1904 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1905 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1908 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1914 g_object_unref (parent_folder);
1917 /* if they are not local folders */
1919 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1920 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1924 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1926 cmp = (cmp1 - cmp2);
1931 g_object_unref(G_OBJECT(folder1));
1933 g_object_unref(G_OBJECT(folder2));
1941 /*****************************************************************************/
1942 /* DRAG and DROP stuff */
1943 /*****************************************************************************/
1945 * This function fills the #GtkSelectionData with the row and the
1946 * model that has been dragged. It's called when this widget is a
1947 * source for dnd after the event drop happened
1950 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1951 guint info, guint time, gpointer data)
1953 GtkTreeSelection *selection;
1954 GtkTreeModel *model;
1956 GtkTreePath *source_row;
1958 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1959 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1961 source_row = gtk_tree_model_get_path (model, &iter);
1962 gtk_tree_set_row_drag_data (selection_data,
1966 gtk_tree_path_free (source_row);
1970 typedef struct _DndHelper {
1971 ModestFolderView *folder_view;
1972 gboolean delete_source;
1973 GtkTreePath *source_row;
1977 dnd_helper_destroyer (DndHelper *helper)
1979 /* Free the helper */
1980 gtk_tree_path_free (helper->source_row);
1981 g_slice_free (DndHelper, helper);
1985 xfer_folder_cb (ModestMailOperation *mail_op,
1986 TnyFolder *new_folder,
1990 /* Select the folder */
1991 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
1997 /* get the folder for the row the treepath refers to. */
1998 /* folder must be unref'd */
1999 static TnyFolderStore *
2000 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2003 TnyFolderStore *folder = NULL;
2005 if (gtk_tree_model_get_iter (model,&iter, path))
2006 gtk_tree_model_get (model, &iter,
2007 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2014 * This function is used by drag_data_received_cb to manage drag and
2015 * drop of a header, i.e, and drag from the header view to the folder
2019 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2020 GtkTreeModel *dest_model,
2021 GtkTreePath *dest_row,
2022 GtkSelectionData *selection_data)
2024 TnyList *headers = NULL;
2025 TnyFolder *folder = NULL, *src_folder = NULL;
2026 TnyFolderType folder_type;
2027 GtkTreeIter source_iter, dest_iter;
2028 ModestWindowMgr *mgr = NULL;
2029 ModestWindow *main_win = NULL;
2030 gchar **uris, **tmp;
2032 /* Build the list of headers */
2033 mgr = modest_runtime_get_window_mgr ();
2034 headers = tny_simple_list_new ();
2035 uris = modest_dnd_selection_data_get_paths (selection_data);
2038 while (*tmp != NULL) {
2041 gboolean first = TRUE;
2044 path = gtk_tree_path_new_from_string (*tmp);
2045 gtk_tree_model_get_iter (source_model, &source_iter, path);
2046 gtk_tree_model_get (source_model, &source_iter,
2047 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2050 /* Do not enable d&d of headers already opened */
2051 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2052 tny_list_append (headers, G_OBJECT (header));
2054 if (G_UNLIKELY (first)) {
2055 src_folder = tny_header_get_folder (header);
2059 /* Free and go on */
2060 gtk_tree_path_free (path);
2061 g_object_unref (header);
2066 /* Get the target folder */
2067 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2068 gtk_tree_model_get (dest_model, &dest_iter,
2069 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2072 if (!folder || !TNY_IS_FOLDER(folder)) {
2073 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2077 folder_type = modest_tny_folder_guess_folder_type (folder);
2078 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2079 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2080 goto cleanup; /* cannot move messages there */
2083 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2084 /* g_warning ("folder not writable"); */
2085 goto cleanup; /* verboten! */
2088 /* Ask for confirmation to move */
2089 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2091 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2095 /* Transfer messages */
2096 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2101 if (G_IS_OBJECT (src_folder))
2102 g_object_unref (src_folder);
2103 if (G_IS_OBJECT(folder))
2104 g_object_unref (G_OBJECT (folder));
2105 if (G_IS_OBJECT(headers))
2106 g_object_unref (headers);
2110 TnyFolderStore *src_folder;
2111 TnyFolderStore *dst_folder;
2112 ModestFolderView *folder_view;
2117 dnd_folder_info_destroyer (DndFolderInfo *info)
2119 if (info->src_folder)
2120 g_object_unref (info->src_folder);
2121 if (info->dst_folder)
2122 g_object_unref (info->dst_folder);
2123 g_slice_free (DndFolderInfo, info);
2127 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2128 GtkWindow *parent_window,
2129 TnyAccount *account)
2132 modest_ui_actions_on_account_connection_error (parent_window, account);
2134 /* Free the helper & info */
2135 dnd_helper_destroyer (info->helper);
2136 dnd_folder_info_destroyer (info);
2140 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2142 GtkWindow *parent_window,
2143 TnyAccount *account,
2146 DndFolderInfo *info = NULL;
2147 ModestMailOperation *mail_op;
2149 info = (DndFolderInfo *) user_data;
2151 if (err || canceled) {
2152 dnd_on_connection_failed_destroyer (info, parent_window, account);
2156 /* Do the mail operation */
2157 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2158 modest_ui_actions_move_folder_error_handler,
2159 info->src_folder, NULL);
2161 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2164 /* Transfer the folder */
2165 modest_mail_operation_xfer_folder (mail_op,
2166 TNY_FOLDER (info->src_folder),
2168 info->helper->delete_source,
2170 info->helper->folder_view);
2173 g_object_unref (G_OBJECT (mail_op));
2174 dnd_helper_destroyer (info->helper);
2175 dnd_folder_info_destroyer (info);
2180 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2182 GtkWindow *parent_window,
2183 TnyAccount *account,
2186 DndFolderInfo *info = NULL;
2188 info = (DndFolderInfo *) user_data;
2190 if (err || canceled) {
2191 dnd_on_connection_failed_destroyer (info, parent_window, account);
2195 /* Connect to source folder and perform the copy/move */
2196 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2198 drag_and_drop_from_folder_view_src_folder_performer,
2203 * This function is used by drag_data_received_cb to manage drag and
2204 * drop of a folder, i.e, and drag from the folder view to the same
2208 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2209 GtkTreeModel *dest_model,
2210 GtkTreePath *dest_row,
2211 GtkSelectionData *selection_data,
2214 GtkTreeIter dest_iter, iter;
2215 TnyFolderStore *dest_folder = NULL;
2216 TnyFolderStore *folder = NULL;
2217 gboolean forbidden = FALSE;
2219 DndFolderInfo *info = NULL;
2221 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2223 g_warning ("%s: BUG: no main window", __FUNCTION__);
2224 dnd_helper_destroyer (helper);
2229 /* check the folder rules for the destination */
2230 folder = tree_path_to_folder (dest_model, dest_row);
2231 if (TNY_IS_FOLDER(folder)) {
2232 ModestTnyFolderRules rules =
2233 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2234 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2235 } else if (TNY_IS_FOLDER_STORE(folder)) {
2236 /* enable local root as destination for folders */
2237 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2238 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2241 g_object_unref (folder);
2244 /* check the folder rules for the source */
2245 folder = tree_path_to_folder (source_model, helper->source_row);
2246 if (TNY_IS_FOLDER(folder)) {
2247 ModestTnyFolderRules rules =
2248 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2249 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2252 g_object_unref (folder);
2256 /* Check if the drag is possible */
2257 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2258 dnd_helper_destroyer (helper);
2263 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2264 gtk_tree_model_get (dest_model, &dest_iter,
2265 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2267 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2268 gtk_tree_model_get (source_model, &iter,
2269 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2272 /* Create the info for the performer */
2273 info = g_slice_new (DndFolderInfo);
2274 info->src_folder = g_object_ref (folder);
2275 info->dst_folder = g_object_ref (dest_folder);
2276 info->helper = helper;
2278 /* Connect to the destination folder and perform the copy/move */
2279 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2281 drag_and_drop_from_folder_view_dst_folder_performer,
2285 g_object_unref (dest_folder);
2286 g_object_unref (folder);
2290 * This function receives the data set by the "drag-data-get" signal
2291 * handler. This information comes within the #GtkSelectionData. This
2292 * function will manage both the drags of folders of the treeview and
2293 * drags of headers of the header view widget.
2296 on_drag_data_received (GtkWidget *widget,
2297 GdkDragContext *context,
2300 GtkSelectionData *selection_data,
2305 GtkWidget *source_widget;
2306 GtkTreeModel *dest_model, *source_model;
2307 GtkTreePath *source_row, *dest_row;
2308 GtkTreeViewDropPosition pos;
2309 gboolean delete_source = FALSE;
2310 gboolean success = FALSE;
2312 /* Do not allow further process */
2313 g_signal_stop_emission_by_name (widget, "drag-data-received");
2314 source_widget = gtk_drag_get_source_widget (context);
2316 /* Get the action */
2317 if (context->action == GDK_ACTION_MOVE) {
2318 delete_source = TRUE;
2320 /* Notify that there is no folder selected. We need to
2321 do this in order to update the headers view (and
2322 its monitors, because when moving, the old folder
2323 won't longer exist. We can not wait for the end of
2324 the operation, because the operation won't start if
2325 the folder is in use */
2326 if (source_widget == widget) {
2327 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2328 gtk_tree_selection_unselect_all (sel);
2332 /* Check if the get_data failed */
2333 if (selection_data == NULL || selection_data->length < 0)
2336 /* Select the destination model */
2337 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2339 /* Get the path to the destination row. Can not call
2340 gtk_tree_view_get_drag_dest_row() because the source row
2341 is not selected anymore */
2342 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2345 /* Only allow drops IN other rows */
2347 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2348 pos == GTK_TREE_VIEW_DROP_AFTER)
2352 /* Drags from the header view */
2353 if (source_widget != widget) {
2354 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2356 drag_and_drop_from_header_view (source_model,
2361 DndHelper *helper = NULL;
2363 /* Get the source model and row */
2364 gtk_tree_get_row_drag_data (selection_data,
2368 /* Create the helper */
2369 helper = g_slice_new0 (DndHelper);
2370 helper->delete_source = delete_source;
2371 helper->source_row = gtk_tree_path_copy (source_row);
2372 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2374 drag_and_drop_from_folder_view (source_model,
2380 gtk_tree_path_free (source_row);
2384 gtk_tree_path_free (dest_row);
2387 /* Finish the drag and drop */
2388 gtk_drag_finish (context, success, FALSE, time);
2392 * We define a "drag-drop" signal handler because we do not want to
2393 * use the default one, because the default one always calls
2394 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2395 * signal handler, because there we have all the information available
2396 * to know if the dnd was a success or not.
2399 drag_drop_cb (GtkWidget *widget,
2400 GdkDragContext *context,
2408 if (!context->targets)
2411 /* Check if we're dragging a folder row */
2412 target = gtk_drag_dest_find_target (widget, context, NULL);
2414 /* Request the data from the source. */
2415 gtk_drag_get_data(widget, context, target, time);
2421 * This function expands a node of a tree view if it's not expanded
2422 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2423 * does that, so that's why they're here.
2426 expand_row_timeout (gpointer data)
2428 GtkTreeView *tree_view = data;
2429 GtkTreePath *dest_path = NULL;
2430 GtkTreeViewDropPosition pos;
2431 gboolean result = FALSE;
2433 gdk_threads_enter ();
2435 gtk_tree_view_get_drag_dest_row (tree_view,
2440 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2441 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2442 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2443 gtk_tree_path_free (dest_path);
2447 gtk_tree_path_free (dest_path);
2452 gdk_threads_leave ();
2458 * This function is called whenever the pointer is moved over a widget
2459 * while dragging some data. It installs a timeout that will expand a
2460 * node of the treeview if not expanded yet. This function also calls
2461 * gdk_drag_status in order to set the suggested action that will be
2462 * used by the "drag-data-received" signal handler to know if we
2463 * should do a move or just a copy of the data.
2466 on_drag_motion (GtkWidget *widget,
2467 GdkDragContext *context,
2473 GtkTreeViewDropPosition pos;
2474 GtkTreePath *dest_row;
2475 GtkTreeModel *dest_model;
2476 ModestFolderViewPrivate *priv;
2477 GdkDragAction suggested_action;
2478 gboolean valid_location = FALSE;
2479 TnyFolderStore *folder = NULL;
2481 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2483 if (priv->timer_expander != 0) {
2484 g_source_remove (priv->timer_expander);
2485 priv->timer_expander = 0;
2488 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2493 /* Do not allow drops between folders */
2495 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2496 pos == GTK_TREE_VIEW_DROP_AFTER) {
2497 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2498 gdk_drag_status(context, 0, time);
2499 valid_location = FALSE;
2502 valid_location = TRUE;
2505 /* Check that the destination folder is writable */
2506 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2507 folder = tree_path_to_folder (dest_model, dest_row);
2508 if (folder && TNY_IS_FOLDER (folder)) {
2509 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2511 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2512 valid_location = FALSE;
2517 /* Expand the selected row after 1/2 second */
2518 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2519 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2521 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2523 /* Select the desired action. By default we pick MOVE */
2524 suggested_action = GDK_ACTION_MOVE;
2526 if (context->actions == GDK_ACTION_COPY)
2527 gdk_drag_status(context, GDK_ACTION_COPY, time);
2528 else if (context->actions == GDK_ACTION_MOVE)
2529 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2530 else if (context->actions & suggested_action)
2531 gdk_drag_status(context, suggested_action, time);
2533 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2537 g_object_unref (folder);
2539 gtk_tree_path_free (dest_row);
2541 g_signal_stop_emission_by_name (widget, "drag-motion");
2543 return valid_location;
2547 * This function sets the treeview as a source and a target for dnd
2548 * events. It also connects all the requirede signals.
2551 setup_drag_and_drop (GtkTreeView *self)
2553 /* Set up the folder view as a dnd destination. Set only the
2554 highlight flag, otherwise gtk will have a different
2556 gtk_drag_dest_set (GTK_WIDGET (self),
2557 GTK_DEST_DEFAULT_HIGHLIGHT,
2558 folder_view_drag_types,
2559 G_N_ELEMENTS (folder_view_drag_types),
2560 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2562 g_signal_connect (G_OBJECT (self),
2563 "drag_data_received",
2564 G_CALLBACK (on_drag_data_received),
2568 /* Set up the treeview as a dnd source */
2569 gtk_drag_source_set (GTK_WIDGET (self),
2571 folder_view_drag_types,
2572 G_N_ELEMENTS (folder_view_drag_types),
2573 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2575 g_signal_connect (G_OBJECT (self),
2577 G_CALLBACK (on_drag_motion),
2580 g_signal_connect (G_OBJECT (self),
2582 G_CALLBACK (on_drag_data_get),
2585 g_signal_connect (G_OBJECT (self),
2587 G_CALLBACK (drag_drop_cb),
2592 * This function manages the navigation through the folders using the
2593 * keyboard or the hardware keys in the device
2596 on_key_pressed (GtkWidget *self,
2600 GtkTreeSelection *selection;
2602 GtkTreeModel *model;
2603 gboolean retval = FALSE;
2605 /* Up and Down are automatically managed by the treeview */
2606 if (event->keyval == GDK_Return) {
2607 /* Expand/Collapse the selected row */
2608 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2609 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2612 path = gtk_tree_model_get_path (model, &iter);
2614 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2615 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2617 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2618 gtk_tree_path_free (path);
2620 /* No further processing */
2628 * We listen to the changes in the local folder account name key,
2629 * because we want to show the right name in the view. The local
2630 * folder account name corresponds to the device name in the Maemo
2631 * version. We do this because we do not want to query gconf on each
2632 * tree view refresh. It's better to cache it and change whenever
2636 on_configuration_key_changed (ModestConf* conf,
2638 ModestConfEvent event,
2639 ModestConfNotificationId id,
2640 ModestFolderView *self)
2642 ModestFolderViewPrivate *priv;
2645 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2646 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2648 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2649 g_free (priv->local_account_name);
2651 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2652 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2654 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2655 MODEST_CONF_DEVICE_NAME, NULL);
2657 /* Force a redraw */
2658 #if GTK_CHECK_VERSION(2, 8, 0)
2659 GtkTreeViewColumn * tree_column;
2661 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2662 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2663 gtk_tree_view_column_queue_resize (tree_column);
2665 gtk_widget_queue_draw (GTK_WIDGET (self));
2671 modest_folder_view_set_style (ModestFolderView *self,
2672 ModestFolderViewStyle style)
2674 ModestFolderViewPrivate *priv;
2676 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2677 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2678 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2680 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2683 priv->style = style;
2687 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2688 const gchar *account_id)
2690 ModestFolderViewPrivate *priv;
2691 GtkTreeModel *model;
2693 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2695 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2697 /* This will be used by the filter_row callback,
2698 * to decided which rows to show: */
2699 if (priv->visible_account_id) {
2700 g_free (priv->visible_account_id);
2701 priv->visible_account_id = NULL;
2704 priv->visible_account_id = g_strdup (account_id);
2707 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2708 if (GTK_IS_TREE_MODEL_FILTER (model))
2709 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2711 /* Save settings to gconf */
2712 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2713 MODEST_CONF_FOLDER_VIEW_KEY);
2717 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2719 ModestFolderViewPrivate *priv;
2721 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2723 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2725 return (const gchar *) priv->visible_account_id;
2729 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2733 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2735 gtk_tree_model_get (model, iter,
2736 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2739 gboolean result = FALSE;
2740 if (type == TNY_FOLDER_TYPE_INBOX) {
2744 *inbox_iter = *iter;
2748 if (gtk_tree_model_iter_children (model, &child, iter)) {
2749 if (find_inbox_iter (model, &child, inbox_iter))
2753 } while (gtk_tree_model_iter_next (model, iter));
2762 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2764 GtkTreeModel *model;
2765 GtkTreeIter iter, inbox_iter;
2766 GtkTreeSelection *sel;
2767 GtkTreePath *path = NULL;
2769 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2771 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2775 expand_root_items (self);
2776 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2778 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2779 g_warning ("%s: model is empty", __FUNCTION__);
2783 if (find_inbox_iter (model, &iter, &inbox_iter))
2784 path = gtk_tree_model_get_path (model, &inbox_iter);
2786 path = gtk_tree_path_new_first ();
2788 /* Select the row and free */
2789 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2790 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2791 gtk_tree_path_free (path);
2794 gtk_widget_grab_focus (GTK_WIDGET(self));
2800 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2805 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2806 TnyFolder* a_folder;
2809 gtk_tree_model_get (model, iter,
2810 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2811 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2812 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2816 if (folder == a_folder) {
2817 g_object_unref (a_folder);
2818 *folder_iter = *iter;
2821 g_object_unref (a_folder);
2823 if (gtk_tree_model_iter_children (model, &child, iter)) {
2824 if (find_folder_iter (model, &child, folder_iter, folder))
2828 } while (gtk_tree_model_iter_next (model, iter));
2835 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2838 ModestFolderView *self)
2840 ModestFolderViewPrivate *priv = NULL;
2841 GtkTreeSelection *sel;
2842 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2843 GObject *instance = NULL;
2845 if (!MODEST_IS_FOLDER_VIEW(self))
2848 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2850 priv->reexpand = TRUE;
2852 gtk_tree_model_get (tree_model, iter,
2853 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2854 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2856 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2857 priv->folder_to_select = g_object_ref (instance);
2859 g_object_unref (instance);
2861 if (priv->folder_to_select) {
2863 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2866 path = gtk_tree_model_get_path (tree_model, iter);
2867 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2869 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2871 gtk_tree_selection_select_iter (sel, iter);
2872 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2874 gtk_tree_path_free (path);
2878 modest_folder_view_disable_next_folder_selection (self);
2880 /* Refilter the model */
2881 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2887 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2889 ModestFolderViewPrivate *priv;
2891 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2893 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2895 if (priv->folder_to_select)
2896 g_object_unref(priv->folder_to_select);
2898 priv->folder_to_select = NULL;
2902 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2903 gboolean after_change)
2905 GtkTreeModel *model;
2906 GtkTreeIter iter, folder_iter;
2907 GtkTreeSelection *sel;
2908 ModestFolderViewPrivate *priv = NULL;
2910 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2911 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2913 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2916 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2917 gtk_tree_selection_unselect_all (sel);
2919 if (priv->folder_to_select)
2920 g_object_unref(priv->folder_to_select);
2921 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2925 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2930 /* Refilter the model, before selecting the folder */
2931 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2933 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2934 g_warning ("%s: model is empty", __FUNCTION__);
2938 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2941 path = gtk_tree_model_get_path (model, &folder_iter);
2942 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2944 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2945 gtk_tree_selection_select_iter (sel, &folder_iter);
2946 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2948 gtk_tree_path_free (path);
2956 modest_folder_view_copy_selection (ModestFolderView *self)
2958 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2960 /* Copy selection */
2961 _clipboard_set_selected_data (self, FALSE);
2965 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2967 ModestFolderViewPrivate *priv = NULL;
2968 GtkTreeModel *model = NULL;
2969 const gchar **hidding = NULL;
2970 guint i, n_selected;
2972 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2973 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2975 /* Copy selection */
2976 if (!_clipboard_set_selected_data (folder_view, TRUE))
2979 /* Get hidding ids */
2980 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2982 /* Clear hidding array created by previous cut operation */
2983 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2985 /* Copy hidding array */
2986 priv->n_selected = n_selected;
2987 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2988 for (i=0; i < n_selected; i++)
2989 priv->hidding_ids[i] = g_strdup(hidding[i]);
2991 /* Hide cut folders */
2992 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2993 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2997 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2998 ModestFolderView *folder_view_dst)
3000 GtkTreeModel *filter_model = NULL;
3001 GtkTreeModel *model = NULL;
3002 GtkTreeModel *new_filter_model = NULL;
3004 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3005 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3008 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3009 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3011 /* Build new filter model */
3012 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3013 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3017 /* Set copied model */
3018 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3019 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3020 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3023 g_object_unref (new_filter_model);
3027 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3030 GtkTreeModel *model = NULL;
3031 ModestFolderViewPrivate* priv;
3033 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3035 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3036 priv->show_non_move = show;
3037 /* modest_folder_view_update_model(folder_view, */
3038 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3040 /* Hide special folders */
3041 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3042 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3043 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3047 /* Returns FALSE if it did not selected anything */
3049 _clipboard_set_selected_data (ModestFolderView *folder_view,
3052 ModestFolderViewPrivate *priv = NULL;
3053 TnyFolderStore *folder = NULL;
3054 gboolean retval = FALSE;
3056 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3057 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3059 /* Set selected data on clipboard */
3060 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3061 folder = modest_folder_view_get_selected (folder_view);
3063 /* Do not allow to select an account */
3064 if (TNY_IS_FOLDER (folder)) {
3065 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3070 g_object_unref (folder);
3076 _clear_hidding_filter (ModestFolderView *folder_view)
3078 ModestFolderViewPrivate *priv;
3081 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3082 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3084 if (priv->hidding_ids != NULL) {
3085 for (i=0; i < priv->n_selected; i++)
3086 g_free (priv->hidding_ids[i]);
3087 g_free(priv->hidding_ids);
3093 on_display_name_changed (ModestAccountMgr *mgr,
3094 const gchar *account,
3097 ModestFolderView *self;
3099 self = MODEST_FOLDER_VIEW (user_data);
3101 /* Force a redraw */
3102 #if GTK_CHECK_VERSION(2, 8, 0)
3103 GtkTreeViewColumn * tree_column;
3105 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3106 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3107 gtk_tree_view_column_queue_resize (tree_column);
3109 gtk_widget_queue_draw (GTK_WIDGET (self));