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 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1273 /* Get the inner model */
1274 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1275 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1277 /* Remove the account from the model */
1278 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1279 G_OBJECT (tny_account));
1281 /* Insert the account in the model */
1282 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1283 G_OBJECT (tny_account));
1285 /* Refilter the model */
1286 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1291 * Selects the first inbox or the local account in an idle
1294 on_idle_select_first_inbox_or_local (gpointer user_data)
1296 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1298 gdk_threads_enter ();
1299 modest_folder_view_select_first_inbox_or_local (self);
1300 gdk_threads_leave ();
1307 on_account_removed (TnyAccountStore *account_store,
1308 TnyAccount *account,
1311 ModestFolderView *self = NULL;
1312 ModestFolderViewPrivate *priv;
1313 GtkTreeModel *sort_model, *filter_model;
1314 GtkTreeSelection *sel = NULL;
1315 gboolean same_account_selected = FALSE;
1317 /* Ignore transport account removals, we're not showing them
1318 in the folder view */
1319 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1322 self = MODEST_FOLDER_VIEW (user_data);
1323 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1325 /* Invalidate the cur_folder_store only if the selected folder
1326 belongs to the account that is being removed */
1327 if (priv->cur_folder_store) {
1328 TnyAccount *selected_folder_account = NULL;
1330 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1331 selected_folder_account =
1332 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1334 selected_folder_account =
1335 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1338 if (selected_folder_account == account) {
1339 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1340 gtk_tree_selection_unselect_all (sel);
1341 same_account_selected = TRUE;
1343 g_object_unref (selected_folder_account);
1346 /* Invalidate row to select only if the folder to select
1347 belongs to the account that is being removed*/
1348 if (priv->folder_to_select) {
1349 TnyAccount *folder_to_select_account = NULL;
1351 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1352 if (folder_to_select_account == account) {
1353 modest_folder_view_disable_next_folder_selection (self);
1354 g_object_unref (priv->folder_to_select);
1355 priv->folder_to_select = NULL;
1357 g_object_unref (folder_to_select_account);
1360 /* Remove the account from the model */
1361 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1362 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1363 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1364 G_OBJECT (account));
1366 /* If the removed account is the currently viewed one then
1367 clear the configuration value. The new visible account will be the default account */
1368 if (priv->visible_account_id &&
1369 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1371 /* Clear the current visible account_id */
1372 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1374 /* Call the restore method, this will set the new visible account */
1375 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1376 MODEST_CONF_FOLDER_VIEW_KEY);
1379 /* Refilter the model */
1380 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1382 /* Select the first INBOX if the currently selected folder
1383 belongs to the account that is being deleted */
1384 if (same_account_selected)
1385 g_idle_add (on_idle_select_first_inbox_or_local, self);
1389 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1391 GtkTreeViewColumn *col;
1393 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1395 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1397 g_printerr ("modest: failed get column for title\n");
1401 gtk_tree_view_column_set_title (col, title);
1402 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1407 modest_folder_view_on_map (ModestFolderView *self,
1408 GdkEventExpose *event,
1411 ModestFolderViewPrivate *priv;
1413 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1415 /* This won't happen often */
1416 if (G_UNLIKELY (priv->reselect)) {
1417 /* Select the first inbox or the local account if not found */
1419 /* TODO: this could cause a lock at startup, so we
1420 comment it for the moment. We know that this will
1421 be a bug, because the INBOX is not selected, but we
1422 need to rewrite some parts of Modest to avoid the
1423 deathlock situation */
1424 /* TODO: check if this is still the case */
1425 priv->reselect = FALSE;
1426 modest_folder_view_select_first_inbox_or_local (self);
1427 /* Notify the display name observers */
1428 g_signal_emit (G_OBJECT(self),
1429 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1433 if (priv->reexpand) {
1434 expand_root_items (self);
1435 priv->reexpand = FALSE;
1442 modest_folder_view_new (TnyFolderStoreQuery *query)
1445 ModestFolderViewPrivate *priv;
1446 GtkTreeSelection *sel;
1448 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1449 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1452 priv->query = g_object_ref (query);
1454 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1455 priv->changed_signal = g_signal_connect (sel, "changed",
1456 G_CALLBACK (on_selection_changed), self);
1458 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1460 return GTK_WIDGET(self);
1463 /* this feels dirty; any other way to expand all the root items? */
1465 expand_root_items (ModestFolderView *self)
1468 GtkTreeModel *model;
1471 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1472 path = gtk_tree_path_new_first ();
1474 /* all folders should have child items, so.. */
1476 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1477 gtk_tree_path_next (path);
1478 } while (gtk_tree_model_get_iter (model, &iter, path));
1480 gtk_tree_path_free (path);
1484 * We use this function to implement the
1485 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1486 * account in this case, and the local folders.
1489 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1491 ModestFolderViewPrivate *priv;
1492 gboolean retval = TRUE;
1493 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1494 GObject *instance = NULL;
1495 const gchar *id = NULL;
1497 gboolean found = FALSE;
1498 gboolean cleared = FALSE;
1500 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1501 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1503 gtk_tree_model_get (model, iter,
1504 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1505 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1508 /* Do not show if there is no instance, this could indeed
1509 happen when the model is being modified while it's being
1510 drawn. This could occur for example when moving folders
1515 if (type == TNY_FOLDER_TYPE_ROOT) {
1516 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1517 account instead of a folder. */
1518 if (TNY_IS_ACCOUNT (instance)) {
1519 TnyAccount *acc = TNY_ACCOUNT (instance);
1520 const gchar *account_id = tny_account_get_id (acc);
1522 /* If it isn't a special folder,
1523 * don't show it unless it is the visible account: */
1524 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1525 !modest_tny_account_is_virtual_local_folders (acc) &&
1526 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1528 /* Show only the visible account id */
1529 if (priv->visible_account_id) {
1530 if (strcmp (account_id, priv->visible_account_id))
1537 /* Never show these to the user. They are merged into one folder
1538 * in the local-folders account instead: */
1539 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1544 /* Check hiding (if necessary) */
1545 cleared = modest_email_clipboard_cleared (priv->clipboard);
1546 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1547 id = tny_folder_get_id (TNY_FOLDER(instance));
1548 if (priv->hidding_ids != NULL)
1549 for (i=0; i < priv->n_selected && !found; i++)
1550 if (priv->hidding_ids[i] != NULL && id != NULL)
1551 found = (!strcmp (priv->hidding_ids[i], id));
1557 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1558 folder as no message can be move there according to UI specs */
1559 if (!priv->show_non_move) {
1561 case TNY_FOLDER_TYPE_OUTBOX:
1562 case TNY_FOLDER_TYPE_SENT:
1563 case TNY_FOLDER_TYPE_DRAFTS:
1566 case TNY_FOLDER_TYPE_UNKNOWN:
1567 case TNY_FOLDER_TYPE_NORMAL:
1568 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1569 if (type == TNY_FOLDER_TYPE_INVALID)
1570 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1572 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1573 type == TNY_FOLDER_TYPE_SENT
1574 || type == TNY_FOLDER_TYPE_DRAFTS)
1583 g_object_unref (instance);
1590 modest_folder_view_update_model (ModestFolderView *self,
1591 TnyAccountStore *account_store)
1593 ModestFolderViewPrivate *priv;
1594 GtkTreeModel *model /* , *old_model */;
1595 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1597 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1598 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1601 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1603 /* Notify that there is no folder selected */
1604 g_signal_emit (G_OBJECT(self),
1605 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1607 if (priv->cur_folder_store) {
1608 g_object_unref (priv->cur_folder_store);
1609 priv->cur_folder_store = NULL;
1612 /* FIXME: the local accounts are not shown when the query
1613 selects only the subscribed folders */
1614 model = tny_gtk_folder_store_tree_model_new (NULL);
1616 /* Get the accounts: */
1617 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1619 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1621 sortable = gtk_tree_model_sort_new_with_model (model);
1622 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1623 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1624 GTK_SORT_ASCENDING);
1625 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1626 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1627 cmp_rows, NULL, NULL);
1629 /* Create filter model */
1630 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1631 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1637 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1638 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1639 (GCallback) on_row_inserted_maybe_select_folder, self);
1641 g_object_unref (model);
1642 g_object_unref (filter_model);
1643 g_object_unref (sortable);
1645 /* Force a reselection of the INBOX next time the widget is shown */
1646 priv->reselect = TRUE;
1653 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1655 GtkTreeModel *model = NULL;
1656 TnyFolderStore *folder = NULL;
1658 ModestFolderView *tree_view = NULL;
1659 ModestFolderViewPrivate *priv = NULL;
1660 gboolean selected = FALSE;
1662 g_return_if_fail (sel);
1663 g_return_if_fail (user_data);
1665 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1667 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1669 tree_view = MODEST_FOLDER_VIEW (user_data);
1672 gtk_tree_model_get (model, &iter,
1673 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1676 /* If the folder is the same do not notify */
1677 if (folder && priv->cur_folder_store == folder) {
1678 g_object_unref (folder);
1683 /* Current folder was unselected */
1684 if (priv->cur_folder_store) {
1685 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1686 priv->cur_folder_store, FALSE);
1688 if (TNY_IS_FOLDER(priv->cur_folder_store))
1689 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1690 FALSE, NULL, NULL, NULL);
1692 /* FALSE --> don't expunge the messages */
1694 g_object_unref (priv->cur_folder_store);
1695 priv->cur_folder_store = NULL;
1698 /* New current references */
1699 priv->cur_folder_store = folder;
1701 /* New folder has been selected. Do not notify if there is
1702 nothing new selected */
1704 g_signal_emit (G_OBJECT(tree_view),
1705 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1706 0, priv->cur_folder_store, TRUE);
1711 modest_folder_view_get_selected (ModestFolderView *self)
1713 ModestFolderViewPrivate *priv;
1715 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1717 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1718 if (priv->cur_folder_store)
1719 g_object_ref (priv->cur_folder_store);
1721 return priv->cur_folder_store;
1725 get_cmp_rows_type_pos (GObject *folder)
1727 /* Remote accounts -> Local account -> MMC account .*/
1730 if (TNY_IS_ACCOUNT (folder) &&
1731 modest_tny_account_is_virtual_local_folders (
1732 TNY_ACCOUNT (folder))) {
1734 } else if (TNY_IS_ACCOUNT (folder)) {
1735 TnyAccount *account = TNY_ACCOUNT (folder);
1736 const gchar *account_id = tny_account_get_id (account);
1737 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1743 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1744 return -1; /* Should never happen */
1749 get_cmp_subfolder_type_pos (TnyFolderType t)
1751 /* Inbox, Outbox, Drafts, Sent, User */
1755 case TNY_FOLDER_TYPE_INBOX:
1758 case TNY_FOLDER_TYPE_OUTBOX:
1761 case TNY_FOLDER_TYPE_DRAFTS:
1764 case TNY_FOLDER_TYPE_SENT:
1773 * This function orders the mail accounts according to these rules:
1774 * 1st - remote accounts
1775 * 2nd - local account
1779 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1783 gchar *name1 = NULL;
1784 gchar *name2 = NULL;
1785 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1786 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1787 GObject *folder1 = NULL;
1788 GObject *folder2 = NULL;
1790 gtk_tree_model_get (tree_model, iter1,
1791 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1792 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1793 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1795 gtk_tree_model_get (tree_model, iter2,
1796 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1797 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1798 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1801 /* Return if we get no folder. This could happen when folder
1802 operations are happening. The model is updated after the
1803 folder copy/move actually occurs, so there could be
1804 situations where the model to be drawn is not correct */
1805 if (!folder1 || !folder2)
1808 if (type == TNY_FOLDER_TYPE_ROOT) {
1809 /* Compare the types, so that
1810 * Remote accounts -> Local account -> MMC account .*/
1811 const gint pos1 = get_cmp_rows_type_pos (folder1);
1812 const gint pos2 = get_cmp_rows_type_pos (folder2);
1813 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1814 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1817 else if (pos1 > pos2)
1820 /* Compare items of the same type: */
1822 TnyAccount *account1 = NULL;
1823 if (TNY_IS_ACCOUNT (folder1))
1824 account1 = TNY_ACCOUNT (folder1);
1826 TnyAccount *account2 = NULL;
1827 if (TNY_IS_ACCOUNT (folder2))
1828 account2 = TNY_ACCOUNT (folder2);
1830 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1831 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1833 if (!account_id && !account_id2) {
1835 } else if (!account_id) {
1837 } else if (!account_id2) {
1839 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1842 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1846 gint cmp1 = 0, cmp2 = 0;
1847 /* get the parent to know if it's a local folder */
1850 gboolean has_parent;
1851 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1853 GObject *parent_folder;
1854 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1855 gtk_tree_model_get (tree_model, &parent,
1856 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1857 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1859 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1860 TNY_IS_ACCOUNT (parent_folder)) {
1861 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1862 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1863 (TNY_FOLDER (folder1)));
1864 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1865 (TNY_FOLDER (folder2)));
1866 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1867 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1870 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1876 g_object_unref (parent_folder);
1879 /* if they are not local folders */
1881 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1882 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1886 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1888 cmp = (cmp1 - cmp2);
1893 g_object_unref(G_OBJECT(folder1));
1895 g_object_unref(G_OBJECT(folder2));
1903 /*****************************************************************************/
1904 /* DRAG and DROP stuff */
1905 /*****************************************************************************/
1907 * This function fills the #GtkSelectionData with the row and the
1908 * model that has been dragged. It's called when this widget is a
1909 * source for dnd after the event drop happened
1912 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1913 guint info, guint time, gpointer data)
1915 GtkTreeSelection *selection;
1916 GtkTreeModel *model;
1918 GtkTreePath *source_row;
1920 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1921 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1923 source_row = gtk_tree_model_get_path (model, &iter);
1924 gtk_tree_set_row_drag_data (selection_data,
1928 gtk_tree_path_free (source_row);
1932 typedef struct _DndHelper {
1933 ModestFolderView *folder_view;
1934 gboolean delete_source;
1935 GtkTreePath *source_row;
1936 GdkDragContext *context;
1941 dnd_helper_destroyer (DndHelper *helper)
1943 /* Free the helper */
1944 g_object_unref (helper->folder_view);
1945 gtk_tree_path_free (helper->source_row);
1946 g_slice_free (DndHelper, helper);
1950 xfer_cb (ModestMailOperation *mail_op,
1956 helper = (DndHelper *) user_data;
1958 if (modest_mail_operation_get_status (mail_op) ==
1959 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1965 /* Notify the drag source. Never call delete, the monitor will
1966 do the job if needed */
1967 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1969 /* Free the helper */
1970 dnd_helper_destroyer (helper);
1974 xfer_msgs_cb (ModestMailOperation *mail_op,
1978 xfer_cb (mail_op, user_data);
1982 xfer_folder_cb (ModestMailOperation *mail_op,
1983 TnyFolder *new_folder,
1987 GtkWidget *folder_view;
1989 helper = (DndHelper *) user_data;
1990 folder_view = g_object_ref (helper->folder_view);
1993 xfer_cb (mail_op, user_data);
1995 /* Select the folder */
1997 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1999 g_object_unref (folder_view);
2003 /* get the folder for the row the treepath refers to. */
2004 /* folder must be unref'd */
2005 static TnyFolderStore *
2006 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2009 TnyFolderStore *folder = NULL;
2011 if (gtk_tree_model_get_iter (model,&iter, path))
2012 gtk_tree_model_get (model, &iter,
2013 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2019 * This function is used by drag_data_received_cb to manage drag and
2020 * drop of a header, i.e, and drag from the header view to the folder
2024 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2025 GtkTreeModel *dest_model,
2026 GtkTreePath *dest_row,
2027 GtkSelectionData *selection_data,
2030 TnyList *headers = NULL;
2031 TnyFolder *folder = NULL;
2032 TnyFolderType folder_type;
2033 ModestMailOperation *mail_op = NULL;
2034 GtkTreeIter source_iter, dest_iter;
2035 ModestWindowMgr *mgr = NULL;
2036 ModestWindow *main_win = NULL;
2037 gchar **uris, **tmp;
2040 /* Build the list of headers */
2041 mgr = modest_runtime_get_window_mgr ();
2042 headers = tny_simple_list_new ();
2043 uris = modest_dnd_selection_data_get_paths (selection_data);
2046 while (*tmp != NULL) {
2051 path = gtk_tree_path_new_from_string (*tmp);
2052 gtk_tree_model_get_iter (source_model, &source_iter, path);
2053 gtk_tree_model_get (source_model, &source_iter,
2054 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2057 /* Do not enable d&d of headers already opened */
2058 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2059 tny_list_append (headers, G_OBJECT (header));
2061 /* Free and go on */
2062 gtk_tree_path_free (path);
2063 g_object_unref (header);
2068 /* Get the target folder */
2069 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2070 gtk_tree_model_get (dest_model, &dest_iter,
2071 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2074 if (!folder || !TNY_IS_FOLDER(folder)) {
2075 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2079 folder_type = modest_tny_folder_guess_folder_type (folder);
2080 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2081 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2082 goto cleanup; /* cannot move messages there */
2085 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2086 /* g_warning ("folder not writable"); */
2087 goto cleanup; /* verboten! */
2090 /* Ask for confirmation to move */
2091 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2093 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2097 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2099 if (response == GTK_RESPONSE_CANCEL)
2102 /* Transfer messages */
2103 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2104 modest_ui_actions_move_folder_error_handler,
2107 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2110 modest_mail_operation_xfer_msgs (mail_op,
2113 helper->delete_source,
2114 xfer_msgs_cb, helper);
2118 if (G_IS_OBJECT(mail_op))
2119 g_object_unref (G_OBJECT (mail_op));
2120 if (G_IS_OBJECT(folder))
2121 g_object_unref (G_OBJECT (folder));
2122 if (G_IS_OBJECT(headers))
2123 g_object_unref (headers);
2127 TnyFolderStore *src_folder;
2128 TnyFolderStore *dst_folder;
2129 ModestFolderView *folder_view;
2134 dnd_folder_info_destroyer (DndFolderInfo *info)
2136 if (info->src_folder)
2137 g_object_unref (info->src_folder);
2138 if (info->dst_folder)
2139 g_object_unref (info->dst_folder);
2140 if (info->folder_view)
2141 g_object_unref (info->folder_view);
2142 g_slice_free (DndFolderInfo, info);
2146 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2147 GtkWindow *parent_window,
2148 TnyAccount *account)
2150 time_t dnd_time = info->helper->time;
2151 GdkDragContext *context = info->helper->context;
2154 modest_ui_actions_on_account_connection_error (parent_window, account);
2156 /* Free the helper & info */
2157 dnd_helper_destroyer (info->helper);
2158 dnd_folder_info_destroyer (info);
2160 /* Notify the drag source. Never call delete, the monitor will
2161 do the job if needed */
2162 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2167 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2169 GtkWindow *parent_window,
2170 TnyAccount *account,
2173 DndFolderInfo *info = NULL;
2174 ModestMailOperation *mail_op;
2176 info = (DndFolderInfo *) user_data;
2178 if (err || canceled) {
2179 dnd_on_connection_failed_destroyer (info, parent_window, account);
2183 /* Do the mail operation */
2184 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2185 modest_ui_actions_move_folder_error_handler,
2186 info->src_folder, NULL);
2188 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2191 /* Transfer the folder */
2192 modest_mail_operation_xfer_folder (mail_op,
2193 TNY_FOLDER (info->src_folder),
2195 info->helper->delete_source,
2199 /* modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2200 /* TNY_FOLDER (info->dst_folder), TRUE); */
2202 g_object_unref (G_OBJECT (mail_op));
2207 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2209 GtkWindow *parent_window,
2210 TnyAccount *account,
2213 DndFolderInfo *info = NULL;
2215 info = (DndFolderInfo *) user_data;
2217 if (err || canceled) {
2218 dnd_on_connection_failed_destroyer (info, parent_window, account);
2222 /* Connect to source folder and perform the copy/move */
2223 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2225 drag_and_drop_from_folder_view_src_folder_performer,
2230 * This function is used by drag_data_received_cb to manage drag and
2231 * drop of a folder, i.e, and drag from the folder view to the same
2235 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2236 GtkTreeModel *dest_model,
2237 GtkTreePath *dest_row,
2238 GtkSelectionData *selection_data,
2241 GtkTreeIter dest_iter, iter;
2242 TnyFolderStore *dest_folder = NULL;
2243 TnyFolderStore *folder = NULL;
2244 gboolean forbidden = FALSE;
2246 DndFolderInfo *info = NULL;
2248 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2250 g_warning ("%s: BUG: no main window", __FUNCTION__);
2255 /* check the folder rules for the destination */
2256 folder = tree_path_to_folder (dest_model, dest_row);
2257 if (TNY_IS_FOLDER(folder)) {
2258 ModestTnyFolderRules rules =
2259 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2260 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2261 } else if (TNY_IS_FOLDER_STORE(folder)) {
2262 /* enable local root as destination for folders */
2263 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2264 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2267 g_object_unref (folder);
2270 /* check the folder rules for the source */
2271 folder = tree_path_to_folder (source_model, helper->source_row);
2272 if (TNY_IS_FOLDER(folder)) {
2273 ModestTnyFolderRules rules =
2274 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2275 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2278 g_object_unref (folder);
2282 /* Check if the drag is possible */
2283 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2284 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2285 gtk_tree_path_free (helper->source_row);
2286 g_slice_free (DndHelper, helper);
2291 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2292 gtk_tree_model_get (dest_model, &dest_iter,
2293 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2295 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2296 gtk_tree_model_get (source_model, &iter,
2297 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2300 /* Create the info for the performer */
2301 info = g_slice_new (DndFolderInfo);
2302 info->src_folder = g_object_ref (folder);
2303 info->dst_folder = g_object_ref (dest_folder);
2304 info->folder_view = g_object_ref (helper->folder_view);
2305 info->helper = helper;
2307 /* Connect to the destination folder and perform the copy/move */
2308 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2310 drag_and_drop_from_folder_view_dst_folder_performer,
2314 g_object_unref (dest_folder);
2315 g_object_unref (folder);
2319 * This function receives the data set by the "drag-data-get" signal
2320 * handler. This information comes within the #GtkSelectionData. This
2321 * function will manage both the drags of folders of the treeview and
2322 * drags of headers of the header view widget.
2325 on_drag_data_received (GtkWidget *widget,
2326 GdkDragContext *context,
2329 GtkSelectionData *selection_data,
2334 GtkWidget *source_widget;
2335 GtkTreeModel *dest_model, *source_model;
2336 GtkTreePath *source_row, *dest_row;
2337 GtkTreeViewDropPosition pos;
2338 gboolean success = FALSE, delete_source = FALSE;
2339 DndHelper *helper = NULL;
2341 /* Do not allow further process */
2342 g_signal_stop_emission_by_name (widget, "drag-data-received");
2343 source_widget = gtk_drag_get_source_widget (context);
2345 /* Get the action */
2346 if (context->action == GDK_ACTION_MOVE) {
2347 delete_source = TRUE;
2349 /* Notify that there is no folder selected. We need to
2350 do this in order to update the headers view (and
2351 its monitors, because when moving, the old folder
2352 won't longer exist. We can not wait for the end of
2353 the operation, because the operation won't start if
2354 the folder is in use */
2355 if (source_widget == widget) {
2356 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2357 gtk_tree_selection_unselect_all (sel);
2361 /* Check if the get_data failed */
2362 if (selection_data == NULL || selection_data->length < 0)
2363 gtk_drag_finish (context, success, FALSE, time);
2365 /* Select the destination model */
2366 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2368 /* Get the path to the destination row. Can not call
2369 gtk_tree_view_get_drag_dest_row() because the source row
2370 is not selected anymore */
2371 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2374 /* Only allow drops IN other rows */
2376 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2377 pos == GTK_TREE_VIEW_DROP_AFTER)
2378 gtk_drag_finish (context, success, FALSE, time);
2380 /* Create the helper */
2381 helper = g_slice_new0 (DndHelper);
2382 helper->delete_source = delete_source;
2383 helper->context = context;
2384 helper->time = time;
2385 helper->folder_view = g_object_ref (widget);
2387 /* Drags from the header view */
2388 if (source_widget != widget) {
2389 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2391 drag_and_drop_from_header_view (source_model,
2397 /* Get the source model and row */
2398 gtk_tree_get_row_drag_data (selection_data,
2401 helper->source_row = gtk_tree_path_copy (source_row);
2403 drag_and_drop_from_folder_view (source_model,
2409 gtk_tree_path_free (source_row);
2413 gtk_tree_path_free (dest_row);
2417 * We define a "drag-drop" signal handler because we do not want to
2418 * use the default one, because the default one always calls
2419 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2420 * signal handler, because there we have all the information available
2421 * to know if the dnd was a success or not.
2424 drag_drop_cb (GtkWidget *widget,
2425 GdkDragContext *context,
2433 if (!context->targets)
2436 /* Check if we're dragging a folder row */
2437 target = gtk_drag_dest_find_target (widget, context, NULL);
2439 /* Request the data from the source. */
2440 gtk_drag_get_data(widget, context, target, time);
2446 * This function expands a node of a tree view if it's not expanded
2447 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2448 * does that, so that's why they're here.
2451 expand_row_timeout (gpointer data)
2453 GtkTreeView *tree_view = data;
2454 GtkTreePath *dest_path = NULL;
2455 GtkTreeViewDropPosition pos;
2456 gboolean result = FALSE;
2458 gdk_threads_enter ();
2460 gtk_tree_view_get_drag_dest_row (tree_view,
2465 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2466 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2467 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2468 gtk_tree_path_free (dest_path);
2472 gtk_tree_path_free (dest_path);
2477 gdk_threads_leave ();
2483 * This function is called whenever the pointer is moved over a widget
2484 * while dragging some data. It installs a timeout that will expand a
2485 * node of the treeview if not expanded yet. This function also calls
2486 * gdk_drag_status in order to set the suggested action that will be
2487 * used by the "drag-data-received" signal handler to know if we
2488 * should do a move or just a copy of the data.
2491 on_drag_motion (GtkWidget *widget,
2492 GdkDragContext *context,
2498 GtkTreeViewDropPosition pos;
2499 GtkTreePath *dest_row;
2500 GtkTreeModel *dest_model;
2501 ModestFolderViewPrivate *priv;
2502 GdkDragAction suggested_action;
2503 gboolean valid_location = FALSE;
2504 TnyFolderStore *folder = NULL;
2506 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2508 if (priv->timer_expander != 0) {
2509 g_source_remove (priv->timer_expander);
2510 priv->timer_expander = 0;
2513 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2518 /* Do not allow drops between folders */
2520 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2521 pos == GTK_TREE_VIEW_DROP_AFTER) {
2522 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2523 gdk_drag_status(context, 0, time);
2524 valid_location = FALSE;
2527 valid_location = TRUE;
2530 /* Check that the destination folder is writable */
2531 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2532 folder = tree_path_to_folder (dest_model, dest_row);
2533 if (folder && TNY_IS_FOLDER (folder)) {
2534 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2536 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2537 valid_location = FALSE;
2542 /* Expand the selected row after 1/2 second */
2543 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2544 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2546 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2548 /* Select the desired action. By default we pick MOVE */
2549 suggested_action = GDK_ACTION_MOVE;
2551 if (context->actions == GDK_ACTION_COPY)
2552 gdk_drag_status(context, GDK_ACTION_COPY, time);
2553 else if (context->actions == GDK_ACTION_MOVE)
2554 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2555 else if (context->actions & suggested_action)
2556 gdk_drag_status(context, suggested_action, time);
2558 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2562 g_object_unref (folder);
2564 gtk_tree_path_free (dest_row);
2566 g_signal_stop_emission_by_name (widget, "drag-motion");
2568 return valid_location;
2572 * This function sets the treeview as a source and a target for dnd
2573 * events. It also connects all the requirede signals.
2576 setup_drag_and_drop (GtkTreeView *self)
2578 /* Set up the folder view as a dnd destination. Set only the
2579 highlight flag, otherwise gtk will have a different
2581 gtk_drag_dest_set (GTK_WIDGET (self),
2582 GTK_DEST_DEFAULT_HIGHLIGHT,
2583 folder_view_drag_types,
2584 G_N_ELEMENTS (folder_view_drag_types),
2585 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2587 g_signal_connect (G_OBJECT (self),
2588 "drag_data_received",
2589 G_CALLBACK (on_drag_data_received),
2593 /* Set up the treeview as a dnd source */
2594 gtk_drag_source_set (GTK_WIDGET (self),
2596 folder_view_drag_types,
2597 G_N_ELEMENTS (folder_view_drag_types),
2598 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2600 g_signal_connect (G_OBJECT (self),
2602 G_CALLBACK (on_drag_motion),
2605 g_signal_connect (G_OBJECT (self),
2607 G_CALLBACK (on_drag_data_get),
2610 g_signal_connect (G_OBJECT (self),
2612 G_CALLBACK (drag_drop_cb),
2617 * This function manages the navigation through the folders using the
2618 * keyboard or the hardware keys in the device
2621 on_key_pressed (GtkWidget *self,
2625 GtkTreeSelection *selection;
2627 GtkTreeModel *model;
2628 gboolean retval = FALSE;
2630 /* Up and Down are automatically managed by the treeview */
2631 if (event->keyval == GDK_Return) {
2632 /* Expand/Collapse the selected row */
2633 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2634 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2637 path = gtk_tree_model_get_path (model, &iter);
2639 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2640 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2642 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2643 gtk_tree_path_free (path);
2645 /* No further processing */
2653 * We listen to the changes in the local folder account name key,
2654 * because we want to show the right name in the view. The local
2655 * folder account name corresponds to the device name in the Maemo
2656 * version. We do this because we do not want to query gconf on each
2657 * tree view refresh. It's better to cache it and change whenever
2661 on_configuration_key_changed (ModestConf* conf,
2663 ModestConfEvent event,
2664 ModestConfNotificationId id,
2665 ModestFolderView *self)
2667 ModestFolderViewPrivate *priv;
2670 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2671 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2673 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2674 g_free (priv->local_account_name);
2676 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2677 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2679 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2680 MODEST_CONF_DEVICE_NAME, NULL);
2682 /* Force a redraw */
2683 #if GTK_CHECK_VERSION(2, 8, 0)
2684 GtkTreeViewColumn * tree_column;
2686 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2687 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2688 gtk_tree_view_column_queue_resize (tree_column);
2690 gtk_widget_queue_draw (GTK_WIDGET (self));
2696 modest_folder_view_set_style (ModestFolderView *self,
2697 ModestFolderViewStyle style)
2699 ModestFolderViewPrivate *priv;
2701 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2702 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2703 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2705 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2708 priv->style = style;
2712 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2713 const gchar *account_id)
2715 ModestFolderViewPrivate *priv;
2716 GtkTreeModel *model;
2718 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2720 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2722 /* This will be used by the filter_row callback,
2723 * to decided which rows to show: */
2724 if (priv->visible_account_id) {
2725 g_free (priv->visible_account_id);
2726 priv->visible_account_id = NULL;
2729 priv->visible_account_id = g_strdup (account_id);
2732 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2733 if (GTK_IS_TREE_MODEL_FILTER (model))
2734 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2736 /* Save settings to gconf */
2737 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2738 MODEST_CONF_FOLDER_VIEW_KEY);
2742 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2744 ModestFolderViewPrivate *priv;
2746 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2748 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2750 return (const gchar *) priv->visible_account_id;
2754 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2758 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2760 gtk_tree_model_get (model, iter,
2761 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2764 gboolean result = FALSE;
2765 if (type == TNY_FOLDER_TYPE_INBOX) {
2769 *inbox_iter = *iter;
2773 if (gtk_tree_model_iter_children (model, &child, iter)) {
2774 if (find_inbox_iter (model, &child, inbox_iter))
2778 } while (gtk_tree_model_iter_next (model, iter));
2787 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2789 GtkTreeModel *model;
2790 GtkTreeIter iter, inbox_iter;
2791 GtkTreeSelection *sel;
2792 GtkTreePath *path = NULL;
2794 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2796 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2800 expand_root_items (self);
2801 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2803 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2804 g_warning ("%s: model is empty", __FUNCTION__);
2808 if (find_inbox_iter (model, &iter, &inbox_iter))
2809 path = gtk_tree_model_get_path (model, &inbox_iter);
2811 path = gtk_tree_path_new_first ();
2813 /* Select the row and free */
2814 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2815 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2816 gtk_tree_path_free (path);
2819 gtk_widget_grab_focus (GTK_WIDGET(self));
2825 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2830 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2831 TnyFolder* a_folder;
2834 gtk_tree_model_get (model, iter,
2835 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2836 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2837 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2841 if (folder == a_folder) {
2842 g_object_unref (a_folder);
2843 *folder_iter = *iter;
2846 g_object_unref (a_folder);
2848 if (gtk_tree_model_iter_children (model, &child, iter)) {
2849 if (find_folder_iter (model, &child, folder_iter, folder))
2853 } while (gtk_tree_model_iter_next (model, iter));
2860 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2863 ModestFolderView *self)
2865 ModestFolderViewPrivate *priv = NULL;
2866 GtkTreeSelection *sel;
2867 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2868 GObject *instance = NULL;
2870 if (!MODEST_IS_FOLDER_VIEW(self))
2873 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2875 priv->reexpand = TRUE;
2877 gtk_tree_model_get (tree_model, iter,
2878 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2879 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2881 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2882 priv->folder_to_select = g_object_ref (instance);
2884 g_object_unref (instance);
2887 if (priv->folder_to_select) {
2889 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2892 path = gtk_tree_model_get_path (tree_model, iter);
2893 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2895 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2897 gtk_tree_selection_select_iter (sel, iter);
2898 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2900 gtk_tree_path_free (path);
2905 modest_folder_view_disable_next_folder_selection (self);
2907 /* Refilter the model */
2908 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2914 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2916 ModestFolderViewPrivate *priv;
2918 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2920 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2922 if (priv->folder_to_select)
2923 g_object_unref(priv->folder_to_select);
2925 priv->folder_to_select = NULL;
2929 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2930 gboolean after_change)
2932 GtkTreeModel *model;
2933 GtkTreeIter iter, folder_iter;
2934 GtkTreeSelection *sel;
2935 ModestFolderViewPrivate *priv = NULL;
2937 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2938 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2940 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2943 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2944 gtk_tree_selection_unselect_all (sel);
2946 if (priv->folder_to_select)
2947 g_object_unref(priv->folder_to_select);
2948 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2952 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2957 /* Refilter the model, before selecting the folder */
2958 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2960 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2961 g_warning ("%s: model is empty", __FUNCTION__);
2965 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2968 path = gtk_tree_model_get_path (model, &folder_iter);
2969 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2971 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2972 gtk_tree_selection_select_iter (sel, &folder_iter);
2973 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2975 gtk_tree_path_free (path);
2983 modest_folder_view_copy_selection (ModestFolderView *self)
2985 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2987 /* Copy selection */
2988 _clipboard_set_selected_data (self, FALSE);
2992 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2994 ModestFolderViewPrivate *priv = NULL;
2995 GtkTreeModel *model = NULL;
2996 const gchar **hidding = NULL;
2997 guint i, n_selected;
2999 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3000 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3002 /* Copy selection */
3003 if (!_clipboard_set_selected_data (folder_view, TRUE))
3006 /* Get hidding ids */
3007 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3009 /* Clear hidding array created by previous cut operation */
3010 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3012 /* Copy hidding array */
3013 priv->n_selected = n_selected;
3014 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3015 for (i=0; i < n_selected; i++)
3016 priv->hidding_ids[i] = g_strdup(hidding[i]);
3018 /* Hide cut folders */
3019 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3020 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3024 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3025 ModestFolderView *folder_view_dst)
3027 GtkTreeModel *filter_model = NULL;
3028 GtkTreeModel *model = NULL;
3029 GtkTreeModel *new_filter_model = NULL;
3031 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3032 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3035 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3036 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3038 /* Build new filter model */
3039 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3040 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3044 /* Set copied model */
3045 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3046 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3047 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3050 g_object_unref (new_filter_model);
3054 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3057 GtkTreeModel *model = NULL;
3058 ModestFolderViewPrivate* priv;
3060 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3062 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3063 priv->show_non_move = show;
3064 /* modest_folder_view_update_model(folder_view, */
3065 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3067 /* Hide special folders */
3068 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3069 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3070 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3074 /* Returns FALSE if it did not selected anything */
3076 _clipboard_set_selected_data (ModestFolderView *folder_view,
3079 ModestFolderViewPrivate *priv = NULL;
3080 TnyFolderStore *folder = NULL;
3081 gboolean retval = FALSE;
3083 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3084 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3086 /* Set selected data on clipboard */
3087 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3088 folder = modest_folder_view_get_selected (folder_view);
3090 /* Do not allow to select an account */
3091 if (TNY_IS_FOLDER (folder)) {
3092 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3097 g_object_unref (folder);
3103 _clear_hidding_filter (ModestFolderView *folder_view)
3105 ModestFolderViewPrivate *priv;
3108 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3109 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3111 if (priv->hidding_ids != NULL) {
3112 for (i=0; i < priv->n_selected; i++)
3113 g_free (priv->hidding_ids[i]);
3114 g_free(priv->hidding_ids);
3120 on_display_name_changed (ModestAccountMgr *mgr,
3121 const gchar *account,
3124 ModestFolderView *self;
3126 self = MODEST_FOLDER_VIEW (user_data);
3128 /* Force a redraw */
3129 #if GTK_CHECK_VERSION(2, 8, 0)
3130 GtkTreeViewColumn * tree_column;
3132 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3133 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3134 gtk_tree_view_column_queue_resize (tree_column);
3136 gtk_widget_queue_draw (GTK_WIDGET (self));