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;
1967 GdkDragContext *context;
1972 dnd_helper_destroyer (DndHelper *helper)
1974 /* Free the helper */
1975 g_object_unref (helper->folder_view);
1976 gtk_tree_path_free (helper->source_row);
1977 g_slice_free (DndHelper, helper);
1981 xfer_cb (ModestMailOperation *mail_op,
1987 helper = (DndHelper *) user_data;
1989 if (modest_mail_operation_get_status (mail_op) ==
1990 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1996 /* Notify the drag source. Never call delete, the monitor will
1997 do the job if needed */
1998 gtk_drag_finish (helper->context, success, FALSE, helper->time);
2000 /* Free the helper */
2001 dnd_helper_destroyer (helper);
2005 xfer_msgs_cb (ModestMailOperation *mail_op,
2009 xfer_cb (mail_op, user_data);
2013 xfer_folder_cb (ModestMailOperation *mail_op,
2014 TnyFolder *new_folder,
2018 GtkWidget *folder_view;
2020 helper = (DndHelper *) user_data;
2021 folder_view = g_object_ref (helper->folder_view);
2024 xfer_cb (mail_op, user_data);
2026 /* Select the folder */
2028 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
2030 g_object_unref (folder_view);
2034 /* get the folder for the row the treepath refers to. */
2035 /* folder must be unref'd */
2036 static TnyFolderStore *
2037 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2040 TnyFolderStore *folder = NULL;
2042 if (gtk_tree_model_get_iter (model,&iter, path))
2043 gtk_tree_model_get (model, &iter,
2044 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2050 * This function is used by drag_data_received_cb to manage drag and
2051 * drop of a header, i.e, and drag from the header view to the folder
2055 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2056 GtkTreeModel *dest_model,
2057 GtkTreePath *dest_row,
2058 GtkSelectionData *selection_data,
2061 TnyList *headers = NULL;
2062 TnyFolder *folder = NULL;
2063 TnyFolderType folder_type;
2064 ModestMailOperation *mail_op = NULL;
2065 GtkTreeIter source_iter, dest_iter;
2066 ModestWindowMgr *mgr = NULL;
2067 ModestWindow *main_win = NULL;
2068 gchar **uris, **tmp;
2071 /* Build the list of headers */
2072 mgr = modest_runtime_get_window_mgr ();
2073 headers = tny_simple_list_new ();
2074 uris = modest_dnd_selection_data_get_paths (selection_data);
2077 while (*tmp != NULL) {
2082 path = gtk_tree_path_new_from_string (*tmp);
2083 gtk_tree_model_get_iter (source_model, &source_iter, path);
2084 gtk_tree_model_get (source_model, &source_iter,
2085 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2088 /* Do not enable d&d of headers already opened */
2089 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2090 tny_list_append (headers, G_OBJECT (header));
2092 /* Free and go on */
2093 gtk_tree_path_free (path);
2094 g_object_unref (header);
2099 /* Get the target folder */
2100 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2101 gtk_tree_model_get (dest_model, &dest_iter,
2102 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2105 if (!folder || !TNY_IS_FOLDER(folder)) {
2106 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2110 folder_type = modest_tny_folder_guess_folder_type (folder);
2111 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2112 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2113 goto cleanup; /* cannot move messages there */
2116 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2117 /* g_warning ("folder not writable"); */
2118 goto cleanup; /* verboten! */
2121 /* Ask for confirmation to move */
2122 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2124 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2128 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2130 if (response == GTK_RESPONSE_CANCEL)
2133 /* Transfer messages */
2134 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2135 modest_ui_actions_move_folder_error_handler,
2138 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2141 modest_mail_operation_xfer_msgs (mail_op,
2144 helper->delete_source,
2145 xfer_msgs_cb, helper);
2149 if (G_IS_OBJECT(mail_op))
2150 g_object_unref (G_OBJECT (mail_op));
2151 if (G_IS_OBJECT(folder))
2152 g_object_unref (G_OBJECT (folder));
2153 if (G_IS_OBJECT(headers))
2154 g_object_unref (headers);
2158 TnyFolderStore *src_folder;
2159 TnyFolderStore *dst_folder;
2160 ModestFolderView *folder_view;
2165 dnd_folder_info_destroyer (DndFolderInfo *info)
2167 if (info->src_folder)
2168 g_object_unref (info->src_folder);
2169 if (info->dst_folder)
2170 g_object_unref (info->dst_folder);
2171 if (info->folder_view)
2172 g_object_unref (info->folder_view);
2173 g_slice_free (DndFolderInfo, info);
2177 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2178 GtkWindow *parent_window,
2179 TnyAccount *account)
2181 time_t dnd_time = info->helper->time;
2182 GdkDragContext *context = info->helper->context;
2185 modest_ui_actions_on_account_connection_error (parent_window, account);
2187 /* Free the helper & info */
2188 dnd_helper_destroyer (info->helper);
2189 dnd_folder_info_destroyer (info);
2191 /* Notify the drag source. Never call delete, the monitor will
2192 do the job if needed */
2193 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2198 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2200 GtkWindow *parent_window,
2201 TnyAccount *account,
2204 DndFolderInfo *info = NULL;
2205 ModestMailOperation *mail_op;
2207 info = (DndFolderInfo *) user_data;
2209 if (err || canceled) {
2210 dnd_on_connection_failed_destroyer (info, parent_window, account);
2214 /* Do the mail operation */
2215 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2216 modest_ui_actions_move_folder_error_handler,
2217 info->src_folder, NULL);
2219 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2222 /* Transfer the folder */
2223 modest_mail_operation_xfer_folder (mail_op,
2224 TNY_FOLDER (info->src_folder),
2226 info->helper->delete_source,
2230 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2231 /* TNY_FOLDER (info->dst_folder), TRUE); */
2233 g_object_unref (G_OBJECT (mail_op));
2238 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2240 GtkWindow *parent_window,
2241 TnyAccount *account,
2244 DndFolderInfo *info = NULL;
2246 info = (DndFolderInfo *) user_data;
2248 if (err || canceled) {
2249 dnd_on_connection_failed_destroyer (info, parent_window, account);
2253 /* Connect to source folder and perform the copy/move */
2254 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2256 drag_and_drop_from_folder_view_src_folder_performer,
2261 * This function is used by drag_data_received_cb to manage drag and
2262 * drop of a folder, i.e, and drag from the folder view to the same
2266 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2267 GtkTreeModel *dest_model,
2268 GtkTreePath *dest_row,
2269 GtkSelectionData *selection_data,
2272 GtkTreeIter dest_iter, iter;
2273 TnyFolderStore *dest_folder = NULL;
2274 TnyFolderStore *folder = NULL;
2275 gboolean forbidden = FALSE;
2277 DndFolderInfo *info = NULL;
2279 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2281 g_warning ("%s: BUG: no main window", __FUNCTION__);
2286 /* check the folder rules for the destination */
2287 folder = tree_path_to_folder (dest_model, dest_row);
2288 if (TNY_IS_FOLDER(folder)) {
2289 ModestTnyFolderRules rules =
2290 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2291 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2292 } else if (TNY_IS_FOLDER_STORE(folder)) {
2293 /* enable local root as destination for folders */
2294 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2295 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2298 g_object_unref (folder);
2301 /* check the folder rules for the source */
2302 folder = tree_path_to_folder (source_model, helper->source_row);
2303 if (TNY_IS_FOLDER(folder)) {
2304 ModestTnyFolderRules rules =
2305 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2306 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2309 g_object_unref (folder);
2313 /* Check if the drag is possible */
2314 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2315 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2316 gtk_tree_path_free (helper->source_row);
2317 g_slice_free (DndHelper, helper);
2322 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2323 gtk_tree_model_get (dest_model, &dest_iter,
2324 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2326 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2327 gtk_tree_model_get (source_model, &iter,
2328 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2331 /* Create the info for the performer */
2332 info = g_slice_new (DndFolderInfo);
2333 info->src_folder = g_object_ref (folder);
2334 info->dst_folder = g_object_ref (dest_folder);
2335 info->folder_view = g_object_ref (helper->folder_view);
2336 info->helper = helper;
2338 /* Connect to the destination folder and perform the copy/move */
2339 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2341 drag_and_drop_from_folder_view_dst_folder_performer,
2345 g_object_unref (dest_folder);
2346 g_object_unref (folder);
2350 * This function receives the data set by the "drag-data-get" signal
2351 * handler. This information comes within the #GtkSelectionData. This
2352 * function will manage both the drags of folders of the treeview and
2353 * drags of headers of the header view widget.
2356 on_drag_data_received (GtkWidget *widget,
2357 GdkDragContext *context,
2360 GtkSelectionData *selection_data,
2365 GtkWidget *source_widget;
2366 GtkTreeModel *dest_model, *source_model;
2367 GtkTreePath *source_row, *dest_row;
2368 GtkTreeViewDropPosition pos;
2369 gboolean success = FALSE, delete_source = FALSE;
2370 DndHelper *helper = NULL;
2372 /* Do not allow further process */
2373 g_signal_stop_emission_by_name (widget, "drag-data-received");
2374 source_widget = gtk_drag_get_source_widget (context);
2376 /* Get the action */
2377 if (context->action == GDK_ACTION_MOVE) {
2378 delete_source = TRUE;
2380 /* Notify that there is no folder selected. We need to
2381 do this in order to update the headers view (and
2382 its monitors, because when moving, the old folder
2383 won't longer exist. We can not wait for the end of
2384 the operation, because the operation won't start if
2385 the folder is in use */
2386 if (source_widget == widget) {
2387 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2388 gtk_tree_selection_unselect_all (sel);
2392 /* Check if the get_data failed */
2393 if (selection_data == NULL || selection_data->length < 0)
2394 gtk_drag_finish (context, success, FALSE, time);
2396 /* Select the destination model */
2397 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2399 /* Get the path to the destination row. Can not call
2400 gtk_tree_view_get_drag_dest_row() because the source row
2401 is not selected anymore */
2402 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2405 /* Only allow drops IN other rows */
2407 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2408 pos == GTK_TREE_VIEW_DROP_AFTER)
2409 gtk_drag_finish (context, success, FALSE, time);
2411 /* Create the helper */
2412 helper = g_slice_new0 (DndHelper);
2413 helper->delete_source = delete_source;
2414 helper->context = context;
2415 helper->time = time;
2416 helper->folder_view = g_object_ref (widget);
2418 /* Drags from the header view */
2419 if (source_widget != widget) {
2420 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2422 drag_and_drop_from_header_view (source_model,
2428 /* Get the source model and row */
2429 gtk_tree_get_row_drag_data (selection_data,
2432 helper->source_row = gtk_tree_path_copy (source_row);
2434 drag_and_drop_from_folder_view (source_model,
2440 gtk_tree_path_free (source_row);
2444 gtk_tree_path_free (dest_row);
2448 * We define a "drag-drop" signal handler because we do not want to
2449 * use the default one, because the default one always calls
2450 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2451 * signal handler, because there we have all the information available
2452 * to know if the dnd was a success or not.
2455 drag_drop_cb (GtkWidget *widget,
2456 GdkDragContext *context,
2464 if (!context->targets)
2467 /* Check if we're dragging a folder row */
2468 target = gtk_drag_dest_find_target (widget, context, NULL);
2470 /* Request the data from the source. */
2471 gtk_drag_get_data(widget, context, target, time);
2477 * This function expands a node of a tree view if it's not expanded
2478 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2479 * does that, so that's why they're here.
2482 expand_row_timeout (gpointer data)
2484 GtkTreeView *tree_view = data;
2485 GtkTreePath *dest_path = NULL;
2486 GtkTreeViewDropPosition pos;
2487 gboolean result = FALSE;
2489 gdk_threads_enter ();
2491 gtk_tree_view_get_drag_dest_row (tree_view,
2496 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2497 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2498 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2499 gtk_tree_path_free (dest_path);
2503 gtk_tree_path_free (dest_path);
2508 gdk_threads_leave ();
2514 * This function is called whenever the pointer is moved over a widget
2515 * while dragging some data. It installs a timeout that will expand a
2516 * node of the treeview if not expanded yet. This function also calls
2517 * gdk_drag_status in order to set the suggested action that will be
2518 * used by the "drag-data-received" signal handler to know if we
2519 * should do a move or just a copy of the data.
2522 on_drag_motion (GtkWidget *widget,
2523 GdkDragContext *context,
2529 GtkTreeViewDropPosition pos;
2530 GtkTreePath *dest_row;
2531 GtkTreeModel *dest_model;
2532 ModestFolderViewPrivate *priv;
2533 GdkDragAction suggested_action;
2534 gboolean valid_location = FALSE;
2535 TnyFolderStore *folder = NULL;
2537 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2539 if (priv->timer_expander != 0) {
2540 g_source_remove (priv->timer_expander);
2541 priv->timer_expander = 0;
2544 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2549 /* Do not allow drops between folders */
2551 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2552 pos == GTK_TREE_VIEW_DROP_AFTER) {
2553 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2554 gdk_drag_status(context, 0, time);
2555 valid_location = FALSE;
2558 valid_location = TRUE;
2561 /* Check that the destination folder is writable */
2562 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2563 folder = tree_path_to_folder (dest_model, dest_row);
2564 if (folder && TNY_IS_FOLDER (folder)) {
2565 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2567 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2568 valid_location = FALSE;
2573 /* Expand the selected row after 1/2 second */
2574 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2575 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2577 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2579 /* Select the desired action. By default we pick MOVE */
2580 suggested_action = GDK_ACTION_MOVE;
2582 if (context->actions == GDK_ACTION_COPY)
2583 gdk_drag_status(context, GDK_ACTION_COPY, time);
2584 else if (context->actions == GDK_ACTION_MOVE)
2585 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2586 else if (context->actions & suggested_action)
2587 gdk_drag_status(context, suggested_action, time);
2589 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2593 g_object_unref (folder);
2595 gtk_tree_path_free (dest_row);
2597 g_signal_stop_emission_by_name (widget, "drag-motion");
2599 return valid_location;
2603 * This function sets the treeview as a source and a target for dnd
2604 * events. It also connects all the requirede signals.
2607 setup_drag_and_drop (GtkTreeView *self)
2609 /* Set up the folder view as a dnd destination. Set only the
2610 highlight flag, otherwise gtk will have a different
2612 gtk_drag_dest_set (GTK_WIDGET (self),
2613 GTK_DEST_DEFAULT_HIGHLIGHT,
2614 folder_view_drag_types,
2615 G_N_ELEMENTS (folder_view_drag_types),
2616 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2618 g_signal_connect (G_OBJECT (self),
2619 "drag_data_received",
2620 G_CALLBACK (on_drag_data_received),
2624 /* Set up the treeview as a dnd source */
2625 gtk_drag_source_set (GTK_WIDGET (self),
2627 folder_view_drag_types,
2628 G_N_ELEMENTS (folder_view_drag_types),
2629 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2631 g_signal_connect (G_OBJECT (self),
2633 G_CALLBACK (on_drag_motion),
2636 g_signal_connect (G_OBJECT (self),
2638 G_CALLBACK (on_drag_data_get),
2641 g_signal_connect (G_OBJECT (self),
2643 G_CALLBACK (drag_drop_cb),
2648 * This function manages the navigation through the folders using the
2649 * keyboard or the hardware keys in the device
2652 on_key_pressed (GtkWidget *self,
2656 GtkTreeSelection *selection;
2658 GtkTreeModel *model;
2659 gboolean retval = FALSE;
2661 /* Up and Down are automatically managed by the treeview */
2662 if (event->keyval == GDK_Return) {
2663 /* Expand/Collapse the selected row */
2664 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2665 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2668 path = gtk_tree_model_get_path (model, &iter);
2670 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2671 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2673 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2674 gtk_tree_path_free (path);
2676 /* No further processing */
2684 * We listen to the changes in the local folder account name key,
2685 * because we want to show the right name in the view. The local
2686 * folder account name corresponds to the device name in the Maemo
2687 * version. We do this because we do not want to query gconf on each
2688 * tree view refresh. It's better to cache it and change whenever
2692 on_configuration_key_changed (ModestConf* conf,
2694 ModestConfEvent event,
2695 ModestConfNotificationId id,
2696 ModestFolderView *self)
2698 ModestFolderViewPrivate *priv;
2701 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2702 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2704 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2705 g_free (priv->local_account_name);
2707 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2708 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2710 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2711 MODEST_CONF_DEVICE_NAME, NULL);
2713 /* Force a redraw */
2714 #if GTK_CHECK_VERSION(2, 8, 0)
2715 GtkTreeViewColumn * tree_column;
2717 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2718 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2719 gtk_tree_view_column_queue_resize (tree_column);
2721 gtk_widget_queue_draw (GTK_WIDGET (self));
2727 modest_folder_view_set_style (ModestFolderView *self,
2728 ModestFolderViewStyle style)
2730 ModestFolderViewPrivate *priv;
2732 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2733 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2734 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2736 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2739 priv->style = style;
2743 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2744 const gchar *account_id)
2746 ModestFolderViewPrivate *priv;
2747 GtkTreeModel *model;
2749 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2751 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2753 /* This will be used by the filter_row callback,
2754 * to decided which rows to show: */
2755 if (priv->visible_account_id) {
2756 g_free (priv->visible_account_id);
2757 priv->visible_account_id = NULL;
2760 priv->visible_account_id = g_strdup (account_id);
2763 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2764 if (GTK_IS_TREE_MODEL_FILTER (model))
2765 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2767 /* Save settings to gconf */
2768 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2769 MODEST_CONF_FOLDER_VIEW_KEY);
2773 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2775 ModestFolderViewPrivate *priv;
2777 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2779 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2781 return (const gchar *) priv->visible_account_id;
2785 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2789 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2791 gtk_tree_model_get (model, iter,
2792 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2795 gboolean result = FALSE;
2796 if (type == TNY_FOLDER_TYPE_INBOX) {
2800 *inbox_iter = *iter;
2804 if (gtk_tree_model_iter_children (model, &child, iter)) {
2805 if (find_inbox_iter (model, &child, inbox_iter))
2809 } while (gtk_tree_model_iter_next (model, iter));
2818 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2820 GtkTreeModel *model;
2821 GtkTreeIter iter, inbox_iter;
2822 GtkTreeSelection *sel;
2823 GtkTreePath *path = NULL;
2825 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2827 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2831 expand_root_items (self);
2832 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2834 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2835 g_warning ("%s: model is empty", __FUNCTION__);
2839 if (find_inbox_iter (model, &iter, &inbox_iter))
2840 path = gtk_tree_model_get_path (model, &inbox_iter);
2842 path = gtk_tree_path_new_first ();
2844 /* Select the row and free */
2845 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2846 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2847 gtk_tree_path_free (path);
2850 gtk_widget_grab_focus (GTK_WIDGET(self));
2856 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2861 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2862 TnyFolder* a_folder;
2865 gtk_tree_model_get (model, iter,
2866 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2867 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2868 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2872 if (folder == a_folder) {
2873 g_object_unref (a_folder);
2874 *folder_iter = *iter;
2877 g_object_unref (a_folder);
2879 if (gtk_tree_model_iter_children (model, &child, iter)) {
2880 if (find_folder_iter (model, &child, folder_iter, folder))
2884 } while (gtk_tree_model_iter_next (model, iter));
2891 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2894 ModestFolderView *self)
2896 ModestFolderViewPrivate *priv = NULL;
2897 GtkTreeSelection *sel;
2898 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2899 GObject *instance = NULL;
2901 if (!MODEST_IS_FOLDER_VIEW(self))
2904 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2906 priv->reexpand = TRUE;
2908 gtk_tree_model_get (tree_model, iter,
2909 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2910 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2912 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2913 priv->folder_to_select = g_object_ref (instance);
2915 g_object_unref (instance);
2918 if (priv->folder_to_select) {
2920 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2923 path = gtk_tree_model_get_path (tree_model, iter);
2924 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2926 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2928 gtk_tree_selection_select_iter (sel, iter);
2929 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2931 gtk_tree_path_free (path);
2936 modest_folder_view_disable_next_folder_selection (self);
2938 /* Refilter the model */
2939 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2945 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2947 ModestFolderViewPrivate *priv;
2949 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2951 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2953 if (priv->folder_to_select)
2954 g_object_unref(priv->folder_to_select);
2956 priv->folder_to_select = NULL;
2960 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2961 gboolean after_change)
2963 GtkTreeModel *model;
2964 GtkTreeIter iter, folder_iter;
2965 GtkTreeSelection *sel;
2966 ModestFolderViewPrivate *priv = NULL;
2968 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2969 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2971 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2974 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2975 gtk_tree_selection_unselect_all (sel);
2977 if (priv->folder_to_select)
2978 g_object_unref(priv->folder_to_select);
2979 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2983 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2988 /* Refilter the model, before selecting the folder */
2989 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2991 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2992 g_warning ("%s: model is empty", __FUNCTION__);
2996 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2999 path = gtk_tree_model_get_path (model, &folder_iter);
3000 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3002 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3003 gtk_tree_selection_select_iter (sel, &folder_iter);
3004 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3006 gtk_tree_path_free (path);
3014 modest_folder_view_copy_selection (ModestFolderView *self)
3016 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3018 /* Copy selection */
3019 _clipboard_set_selected_data (self, FALSE);
3023 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3025 ModestFolderViewPrivate *priv = NULL;
3026 GtkTreeModel *model = NULL;
3027 const gchar **hidding = NULL;
3028 guint i, n_selected;
3030 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3031 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3033 /* Copy selection */
3034 if (!_clipboard_set_selected_data (folder_view, TRUE))
3037 /* Get hidding ids */
3038 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3040 /* Clear hidding array created by previous cut operation */
3041 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3043 /* Copy hidding array */
3044 priv->n_selected = n_selected;
3045 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3046 for (i=0; i < n_selected; i++)
3047 priv->hidding_ids[i] = g_strdup(hidding[i]);
3049 /* Hide cut folders */
3050 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3051 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3055 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3056 ModestFolderView *folder_view_dst)
3058 GtkTreeModel *filter_model = NULL;
3059 GtkTreeModel *model = NULL;
3060 GtkTreeModel *new_filter_model = NULL;
3062 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3063 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3066 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3067 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3069 /* Build new filter model */
3070 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3071 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3075 /* Set copied model */
3076 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3077 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3078 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3081 g_object_unref (new_filter_model);
3085 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3088 GtkTreeModel *model = NULL;
3089 ModestFolderViewPrivate* priv;
3091 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3093 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3094 priv->show_non_move = show;
3095 /* modest_folder_view_update_model(folder_view, */
3096 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3098 /* Hide special folders */
3099 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3100 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3101 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3105 /* Returns FALSE if it did not selected anything */
3107 _clipboard_set_selected_data (ModestFolderView *folder_view,
3110 ModestFolderViewPrivate *priv = NULL;
3111 TnyFolderStore *folder = NULL;
3112 gboolean retval = FALSE;
3114 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3115 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3117 /* Set selected data on clipboard */
3118 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3119 folder = modest_folder_view_get_selected (folder_view);
3121 /* Do not allow to select an account */
3122 if (TNY_IS_FOLDER (folder)) {
3123 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3128 g_object_unref (folder);
3134 _clear_hidding_filter (ModestFolderView *folder_view)
3136 ModestFolderViewPrivate *priv;
3139 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3140 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3142 if (priv->hidding_ids != NULL) {
3143 for (i=0; i < priv->n_selected; i++)
3144 g_free (priv->hidding_ids[i]);
3145 g_free(priv->hidding_ids);
3151 on_display_name_changed (ModestAccountMgr *mgr,
3152 const gchar *account,
3155 ModestFolderView *self;
3157 self = MODEST_FOLDER_VIEW (user_data);
3159 /* Force a redraw */
3160 #if GTK_CHECK_VERSION(2, 8, 0)
3161 GtkTreeViewColumn * tree_column;
3163 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3164 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3165 gtk_tree_view_column_queue_resize (tree_column);
3167 gtk_widget_queue_draw (GTK_WIDGET (self));