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;
1265 GtkTreeSelection *sel;
1267 /* Ignore transport account insertions, we're not showing them
1268 in the folder view */
1269 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1272 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1273 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1278 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1280 /* Get the inner model */
1281 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1282 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1283 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1288 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1289 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1290 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1294 /* Unselect the folder, clear the header list */
1295 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (user_data));
1296 gtk_tree_selection_unselect_all (sel);
1298 /* Remove the account from the model */
1299 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1300 G_OBJECT (tny_account));
1302 /* Insert the account in the model */
1303 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1304 G_OBJECT (tny_account));
1306 /* Refilter the model */
1307 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1312 * Selects the first inbox or the local account in an idle
1315 on_idle_select_first_inbox_or_local (gpointer user_data)
1317 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1319 gdk_threads_enter ();
1320 modest_folder_view_select_first_inbox_or_local (self);
1321 gdk_threads_leave ();
1328 on_account_removed (TnyAccountStore *account_store,
1329 TnyAccount *account,
1332 ModestFolderView *self = NULL;
1333 ModestFolderViewPrivate *priv;
1334 GtkTreeModel *sort_model, *filter_model;
1335 GtkTreeSelection *sel = NULL;
1336 gboolean same_account_selected = FALSE;
1338 /* Ignore transport account removals, we're not showing them
1339 in the folder view */
1340 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1343 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1344 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1348 self = MODEST_FOLDER_VIEW (user_data);
1349 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1351 /* Invalidate the cur_folder_store only if the selected folder
1352 belongs to the account that is being removed */
1353 if (priv->cur_folder_store) {
1354 TnyAccount *selected_folder_account = NULL;
1356 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1357 selected_folder_account =
1358 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1360 selected_folder_account =
1361 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1364 if (selected_folder_account == account) {
1365 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1366 gtk_tree_selection_unselect_all (sel);
1367 same_account_selected = TRUE;
1369 g_object_unref (selected_folder_account);
1372 /* Invalidate row to select only if the folder to select
1373 belongs to the account that is being removed*/
1374 if (priv->folder_to_select) {
1375 TnyAccount *folder_to_select_account = NULL;
1377 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1378 if (folder_to_select_account == account) {
1379 modest_folder_view_disable_next_folder_selection (self);
1380 g_object_unref (priv->folder_to_select);
1381 priv->folder_to_select = NULL;
1383 g_object_unref (folder_to_select_account);
1386 /* Remove the account from the model */
1387 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1388 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1389 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1393 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1394 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1395 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1399 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1400 G_OBJECT (account));
1402 /* If the removed account is the currently viewed one then
1403 clear the configuration value. The new visible account will be the default account */
1404 if (priv->visible_account_id &&
1405 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1407 /* Clear the current visible account_id */
1408 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1410 /* Call the restore method, this will set the new visible account */
1411 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1412 MODEST_CONF_FOLDER_VIEW_KEY);
1415 /* Refilter the model */
1416 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1418 /* Select the first INBOX if the currently selected folder
1419 belongs to the account that is being deleted */
1420 if (same_account_selected)
1421 g_idle_add (on_idle_select_first_inbox_or_local, self);
1425 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1427 GtkTreeViewColumn *col;
1429 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1431 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1433 g_printerr ("modest: failed get column for title\n");
1437 gtk_tree_view_column_set_title (col, title);
1438 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1443 modest_folder_view_on_map (ModestFolderView *self,
1444 GdkEventExpose *event,
1447 ModestFolderViewPrivate *priv;
1449 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1451 /* This won't happen often */
1452 if (G_UNLIKELY (priv->reselect)) {
1453 /* Select the first inbox or the local account if not found */
1455 /* TODO: this could cause a lock at startup, so we
1456 comment it for the moment. We know that this will
1457 be a bug, because the INBOX is not selected, but we
1458 need to rewrite some parts of Modest to avoid the
1459 deathlock situation */
1460 /* TODO: check if this is still the case */
1461 priv->reselect = FALSE;
1462 modest_folder_view_select_first_inbox_or_local (self);
1463 /* Notify the display name observers */
1464 g_signal_emit (G_OBJECT(self),
1465 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1469 if (priv->reexpand) {
1470 expand_root_items (self);
1471 priv->reexpand = FALSE;
1478 modest_folder_view_new (TnyFolderStoreQuery *query)
1481 ModestFolderViewPrivate *priv;
1482 GtkTreeSelection *sel;
1484 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1485 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1488 priv->query = g_object_ref (query);
1490 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1491 priv->changed_signal = g_signal_connect (sel, "changed",
1492 G_CALLBACK (on_selection_changed), self);
1494 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1496 return GTK_WIDGET(self);
1499 /* this feels dirty; any other way to expand all the root items? */
1501 expand_root_items (ModestFolderView *self)
1504 GtkTreeModel *model;
1507 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1508 path = gtk_tree_path_new_first ();
1510 /* all folders should have child items, so.. */
1512 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1513 gtk_tree_path_next (path);
1514 } while (gtk_tree_model_get_iter (model, &iter, path));
1516 gtk_tree_path_free (path);
1520 * We use this function to implement the
1521 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1522 * account in this case, and the local folders.
1525 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1527 ModestFolderViewPrivate *priv;
1528 gboolean retval = TRUE;
1529 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1530 GObject *instance = NULL;
1531 const gchar *id = NULL;
1533 gboolean found = FALSE;
1534 gboolean cleared = FALSE;
1536 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1537 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1539 gtk_tree_model_get (model, iter,
1540 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1541 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1544 /* Do not show if there is no instance, this could indeed
1545 happen when the model is being modified while it's being
1546 drawn. This could occur for example when moving folders
1551 if (type == TNY_FOLDER_TYPE_ROOT) {
1552 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1553 account instead of a folder. */
1554 if (TNY_IS_ACCOUNT (instance)) {
1555 TnyAccount *acc = TNY_ACCOUNT (instance);
1556 const gchar *account_id = tny_account_get_id (acc);
1558 /* If it isn't a special folder,
1559 * don't show it unless it is the visible account: */
1560 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1561 !modest_tny_account_is_virtual_local_folders (acc) &&
1562 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1564 /* Show only the visible account id */
1565 if (priv->visible_account_id) {
1566 if (strcmp (account_id, priv->visible_account_id))
1573 /* Never show these to the user. They are merged into one folder
1574 * in the local-folders account instead: */
1575 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1580 /* Check hiding (if necessary) */
1581 cleared = modest_email_clipboard_cleared (priv->clipboard);
1582 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1583 id = tny_folder_get_id (TNY_FOLDER(instance));
1584 if (priv->hidding_ids != NULL)
1585 for (i=0; i < priv->n_selected && !found; i++)
1586 if (priv->hidding_ids[i] != NULL && id != NULL)
1587 found = (!strcmp (priv->hidding_ids[i], id));
1593 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1594 folder as no message can be move there according to UI specs */
1595 if (!priv->show_non_move) {
1597 case TNY_FOLDER_TYPE_OUTBOX:
1598 case TNY_FOLDER_TYPE_SENT:
1599 case TNY_FOLDER_TYPE_DRAFTS:
1602 case TNY_FOLDER_TYPE_UNKNOWN:
1603 case TNY_FOLDER_TYPE_NORMAL:
1604 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1605 if (type == TNY_FOLDER_TYPE_INVALID)
1606 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1608 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1609 type == TNY_FOLDER_TYPE_SENT
1610 || type == TNY_FOLDER_TYPE_DRAFTS)
1619 g_object_unref (instance);
1626 modest_folder_view_update_model (ModestFolderView *self,
1627 TnyAccountStore *account_store)
1629 ModestFolderViewPrivate *priv;
1630 GtkTreeModel *model /* , *old_model */;
1631 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1633 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1634 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1637 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1639 /* Notify that there is no folder selected */
1640 g_signal_emit (G_OBJECT(self),
1641 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1643 if (priv->cur_folder_store) {
1644 g_object_unref (priv->cur_folder_store);
1645 priv->cur_folder_store = NULL;
1648 /* FIXME: the local accounts are not shown when the query
1649 selects only the subscribed folders */
1650 model = tny_gtk_folder_store_tree_model_new (NULL);
1652 /* Get the accounts: */
1653 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1655 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1657 sortable = gtk_tree_model_sort_new_with_model (model);
1658 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1659 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1660 GTK_SORT_ASCENDING);
1661 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1662 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1663 cmp_rows, NULL, NULL);
1665 /* Create filter model */
1666 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1667 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1673 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1674 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1675 (GCallback) on_row_inserted_maybe_select_folder, self);
1677 g_object_unref (model);
1678 g_object_unref (filter_model);
1679 g_object_unref (sortable);
1681 /* Force a reselection of the INBOX next time the widget is shown */
1682 priv->reselect = TRUE;
1689 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1691 GtkTreeModel *model = NULL;
1692 TnyFolderStore *folder = NULL;
1694 ModestFolderView *tree_view = NULL;
1695 ModestFolderViewPrivate *priv = NULL;
1696 gboolean selected = FALSE;
1698 g_return_if_fail (sel);
1699 g_return_if_fail (user_data);
1701 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1703 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1705 tree_view = MODEST_FOLDER_VIEW (user_data);
1708 gtk_tree_model_get (model, &iter,
1709 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1712 /* If the folder is the same do not notify */
1713 if (folder && priv->cur_folder_store == folder) {
1714 g_object_unref (folder);
1719 /* Current folder was unselected */
1720 if (priv->cur_folder_store) {
1721 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1722 priv->cur_folder_store, FALSE);
1724 if (TNY_IS_FOLDER(priv->cur_folder_store))
1725 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1726 FALSE, NULL, NULL, NULL);
1728 /* FALSE --> don't expunge the messages */
1730 g_object_unref (priv->cur_folder_store);
1731 priv->cur_folder_store = NULL;
1734 /* New current references */
1735 priv->cur_folder_store = folder;
1737 /* New folder has been selected. Do not notify if there is
1738 nothing new selected */
1740 g_signal_emit (G_OBJECT(tree_view),
1741 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1742 0, priv->cur_folder_store, TRUE);
1747 modest_folder_view_get_selected (ModestFolderView *self)
1749 ModestFolderViewPrivate *priv;
1751 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1753 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1754 if (priv->cur_folder_store)
1755 g_object_ref (priv->cur_folder_store);
1757 return priv->cur_folder_store;
1761 get_cmp_rows_type_pos (GObject *folder)
1763 /* Remote accounts -> Local account -> MMC account .*/
1766 if (TNY_IS_ACCOUNT (folder) &&
1767 modest_tny_account_is_virtual_local_folders (
1768 TNY_ACCOUNT (folder))) {
1770 } else if (TNY_IS_ACCOUNT (folder)) {
1771 TnyAccount *account = TNY_ACCOUNT (folder);
1772 const gchar *account_id = tny_account_get_id (account);
1773 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1779 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1780 return -1; /* Should never happen */
1785 get_cmp_subfolder_type_pos (TnyFolderType t)
1787 /* Inbox, Outbox, Drafts, Sent, User */
1791 case TNY_FOLDER_TYPE_INBOX:
1794 case TNY_FOLDER_TYPE_OUTBOX:
1797 case TNY_FOLDER_TYPE_DRAFTS:
1800 case TNY_FOLDER_TYPE_SENT:
1809 * This function orders the mail accounts according to these rules:
1810 * 1st - remote accounts
1811 * 2nd - local account
1815 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1819 gchar *name1 = NULL;
1820 gchar *name2 = NULL;
1821 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1822 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1823 GObject *folder1 = NULL;
1824 GObject *folder2 = NULL;
1826 gtk_tree_model_get (tree_model, iter1,
1827 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1828 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1829 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1831 gtk_tree_model_get (tree_model, iter2,
1832 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1833 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1834 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1837 /* Return if we get no folder. This could happen when folder
1838 operations are happening. The model is updated after the
1839 folder copy/move actually occurs, so there could be
1840 situations where the model to be drawn is not correct */
1841 if (!folder1 || !folder2)
1844 if (type == TNY_FOLDER_TYPE_ROOT) {
1845 /* Compare the types, so that
1846 * Remote accounts -> Local account -> MMC account .*/
1847 const gint pos1 = get_cmp_rows_type_pos (folder1);
1848 const gint pos2 = get_cmp_rows_type_pos (folder2);
1849 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1850 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1853 else if (pos1 > pos2)
1856 /* Compare items of the same type: */
1858 TnyAccount *account1 = NULL;
1859 if (TNY_IS_ACCOUNT (folder1))
1860 account1 = TNY_ACCOUNT (folder1);
1862 TnyAccount *account2 = NULL;
1863 if (TNY_IS_ACCOUNT (folder2))
1864 account2 = TNY_ACCOUNT (folder2);
1866 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1867 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1869 if (!account_id && !account_id2) {
1871 } else if (!account_id) {
1873 } else if (!account_id2) {
1875 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1878 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1882 gint cmp1 = 0, cmp2 = 0;
1883 /* get the parent to know if it's a local folder */
1886 gboolean has_parent;
1887 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1889 GObject *parent_folder;
1890 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1891 gtk_tree_model_get (tree_model, &parent,
1892 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1893 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1895 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1896 TNY_IS_ACCOUNT (parent_folder)) {
1897 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1898 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1899 (TNY_FOLDER (folder1)));
1900 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1901 (TNY_FOLDER (folder2)));
1902 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1903 if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1906 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1912 g_object_unref (parent_folder);
1915 /* if they are not local folders */
1917 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1918 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1922 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1924 cmp = (cmp1 - cmp2);
1929 g_object_unref(G_OBJECT(folder1));
1931 g_object_unref(G_OBJECT(folder2));
1939 /*****************************************************************************/
1940 /* DRAG and DROP stuff */
1941 /*****************************************************************************/
1943 * This function fills the #GtkSelectionData with the row and the
1944 * model that has been dragged. It's called when this widget is a
1945 * source for dnd after the event drop happened
1948 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1949 guint info, guint time, gpointer data)
1951 GtkTreeSelection *selection;
1952 GtkTreeModel *model;
1954 GtkTreePath *source_row;
1956 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1957 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1959 source_row = gtk_tree_model_get_path (model, &iter);
1960 gtk_tree_set_row_drag_data (selection_data,
1964 gtk_tree_path_free (source_row);
1968 typedef struct _DndHelper {
1969 ModestFolderView *folder_view;
1970 gboolean delete_source;
1971 GtkTreePath *source_row;
1975 dnd_helper_destroyer (DndHelper *helper)
1977 /* Free the helper */
1978 gtk_tree_path_free (helper->source_row);
1979 g_slice_free (DndHelper, helper);
1983 xfer_folder_cb (ModestMailOperation *mail_op,
1984 TnyFolder *new_folder,
1988 /* Select the folder */
1989 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
1995 /* get the folder for the row the treepath refers to. */
1996 /* folder must be unref'd */
1997 static TnyFolderStore *
1998 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2001 TnyFolderStore *folder = NULL;
2003 if (gtk_tree_model_get_iter (model,&iter, path))
2004 gtk_tree_model_get (model, &iter,
2005 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2012 * This function is used by drag_data_received_cb to manage drag and
2013 * drop of a header, i.e, and drag from the header view to the folder
2017 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2018 GtkTreeModel *dest_model,
2019 GtkTreePath *dest_row,
2020 GtkSelectionData *selection_data)
2022 TnyList *headers = NULL;
2023 TnyFolder *folder = NULL, *src_folder = NULL;
2024 TnyFolderType folder_type;
2025 GtkTreeIter source_iter, dest_iter;
2026 ModestWindowMgr *mgr = NULL;
2027 ModestWindow *main_win = NULL;
2028 gchar **uris, **tmp;
2030 /* Build the list of headers */
2031 mgr = modest_runtime_get_window_mgr ();
2032 headers = tny_simple_list_new ();
2033 uris = modest_dnd_selection_data_get_paths (selection_data);
2036 while (*tmp != NULL) {
2039 gboolean first = TRUE;
2042 path = gtk_tree_path_new_from_string (*tmp);
2043 gtk_tree_model_get_iter (source_model, &source_iter, path);
2044 gtk_tree_model_get (source_model, &source_iter,
2045 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2048 /* Do not enable d&d of headers already opened */
2049 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2050 tny_list_append (headers, G_OBJECT (header));
2052 if (G_UNLIKELY (first)) {
2053 src_folder = tny_header_get_folder (header);
2057 /* Free and go on */
2058 gtk_tree_path_free (path);
2059 g_object_unref (header);
2064 /* Get the target folder */
2065 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2066 gtk_tree_model_get (dest_model, &dest_iter,
2067 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2070 if (!folder || !TNY_IS_FOLDER(folder)) {
2071 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2075 folder_type = modest_tny_folder_guess_folder_type (folder);
2076 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2077 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2078 goto cleanup; /* cannot move messages there */
2081 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2082 /* g_warning ("folder not writable"); */
2083 goto cleanup; /* verboten! */
2086 /* Ask for confirmation to move */
2087 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2089 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2093 /* Transfer messages */
2094 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2099 if (G_IS_OBJECT (src_folder))
2100 g_object_unref (src_folder);
2101 if (G_IS_OBJECT(folder))
2102 g_object_unref (G_OBJECT (folder));
2103 if (G_IS_OBJECT(headers))
2104 g_object_unref (headers);
2108 TnyFolderStore *src_folder;
2109 TnyFolderStore *dst_folder;
2110 ModestFolderView *folder_view;
2115 dnd_folder_info_destroyer (DndFolderInfo *info)
2117 if (info->src_folder)
2118 g_object_unref (info->src_folder);
2119 if (info->dst_folder)
2120 g_object_unref (info->dst_folder);
2121 g_slice_free (DndFolderInfo, info);
2125 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2126 GtkWindow *parent_window,
2127 TnyAccount *account)
2130 modest_ui_actions_on_account_connection_error (parent_window, account);
2132 /* Free the helper & info */
2133 dnd_helper_destroyer (info->helper);
2134 dnd_folder_info_destroyer (info);
2138 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2140 GtkWindow *parent_window,
2141 TnyAccount *account,
2144 DndFolderInfo *info = NULL;
2145 ModestMailOperation *mail_op;
2147 info = (DndFolderInfo *) user_data;
2149 if (err || canceled) {
2150 dnd_on_connection_failed_destroyer (info, parent_window, account);
2154 /* Do the mail operation */
2155 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2156 modest_ui_actions_move_folder_error_handler,
2157 info->src_folder, NULL);
2159 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2162 /* Transfer the folder */
2163 modest_mail_operation_xfer_folder (mail_op,
2164 TNY_FOLDER (info->src_folder),
2166 info->helper->delete_source,
2168 info->helper->folder_view);
2171 g_object_unref (G_OBJECT (mail_op));
2172 dnd_helper_destroyer (info->helper);
2173 dnd_folder_info_destroyer (info);
2178 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2180 GtkWindow *parent_window,
2181 TnyAccount *account,
2184 DndFolderInfo *info = NULL;
2186 info = (DndFolderInfo *) user_data;
2188 if (err || canceled) {
2189 dnd_on_connection_failed_destroyer (info, parent_window, account);
2193 /* Connect to source folder and perform the copy/move */
2194 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2196 drag_and_drop_from_folder_view_src_folder_performer,
2201 * This function is used by drag_data_received_cb to manage drag and
2202 * drop of a folder, i.e, and drag from the folder view to the same
2206 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2207 GtkTreeModel *dest_model,
2208 GtkTreePath *dest_row,
2209 GtkSelectionData *selection_data,
2212 GtkTreeIter dest_iter, iter;
2213 TnyFolderStore *dest_folder = NULL;
2214 TnyFolderStore *folder = NULL;
2215 gboolean forbidden = FALSE;
2217 DndFolderInfo *info = NULL;
2219 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2221 g_warning ("%s: BUG: no main window", __FUNCTION__);
2222 dnd_helper_destroyer (helper);
2227 /* check the folder rules for the destination */
2228 folder = tree_path_to_folder (dest_model, dest_row);
2229 if (TNY_IS_FOLDER(folder)) {
2230 ModestTnyFolderRules rules =
2231 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2232 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2233 } else if (TNY_IS_FOLDER_STORE(folder)) {
2234 /* enable local root as destination for folders */
2235 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2236 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2239 g_object_unref (folder);
2242 /* check the folder rules for the source */
2243 folder = tree_path_to_folder (source_model, helper->source_row);
2244 if (TNY_IS_FOLDER(folder)) {
2245 ModestTnyFolderRules rules =
2246 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2247 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2250 g_object_unref (folder);
2254 /* Check if the drag is possible */
2255 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2256 dnd_helper_destroyer (helper);
2261 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2262 gtk_tree_model_get (dest_model, &dest_iter,
2263 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2265 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2266 gtk_tree_model_get (source_model, &iter,
2267 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2270 /* Create the info for the performer */
2271 info = g_slice_new (DndFolderInfo);
2272 info->src_folder = g_object_ref (folder);
2273 info->dst_folder = g_object_ref (dest_folder);
2274 info->helper = helper;
2276 /* Connect to the destination folder and perform the copy/move */
2277 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2279 drag_and_drop_from_folder_view_dst_folder_performer,
2283 g_object_unref (dest_folder);
2284 g_object_unref (folder);
2288 * This function receives the data set by the "drag-data-get" signal
2289 * handler. This information comes within the #GtkSelectionData. This
2290 * function will manage both the drags of folders of the treeview and
2291 * drags of headers of the header view widget.
2294 on_drag_data_received (GtkWidget *widget,
2295 GdkDragContext *context,
2298 GtkSelectionData *selection_data,
2303 GtkWidget *source_widget;
2304 GtkTreeModel *dest_model, *source_model;
2305 GtkTreePath *source_row, *dest_row;
2306 GtkTreeViewDropPosition pos;
2307 gboolean delete_source = FALSE;
2308 gboolean success = FALSE;
2310 /* Do not allow further process */
2311 g_signal_stop_emission_by_name (widget, "drag-data-received");
2312 source_widget = gtk_drag_get_source_widget (context);
2314 /* Get the action */
2315 if (context->action == GDK_ACTION_MOVE) {
2316 delete_source = TRUE;
2318 /* Notify that there is no folder selected. We need to
2319 do this in order to update the headers view (and
2320 its monitors, because when moving, the old folder
2321 won't longer exist. We can not wait for the end of
2322 the operation, because the operation won't start if
2323 the folder is in use */
2324 if (source_widget == widget) {
2325 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2326 gtk_tree_selection_unselect_all (sel);
2330 /* Check if the get_data failed */
2331 if (selection_data == NULL || selection_data->length < 0)
2334 /* Select the destination model */
2335 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2337 /* Get the path to the destination row. Can not call
2338 gtk_tree_view_get_drag_dest_row() because the source row
2339 is not selected anymore */
2340 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2343 /* Only allow drops IN other rows */
2345 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2346 pos == GTK_TREE_VIEW_DROP_AFTER)
2350 /* Drags from the header view */
2351 if (source_widget != widget) {
2352 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2354 drag_and_drop_from_header_view (source_model,
2359 DndHelper *helper = NULL;
2361 /* Get the source model and row */
2362 gtk_tree_get_row_drag_data (selection_data,
2366 /* Create the helper */
2367 helper = g_slice_new0 (DndHelper);
2368 helper->delete_source = delete_source;
2369 helper->source_row = gtk_tree_path_copy (source_row);
2370 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2372 drag_and_drop_from_folder_view (source_model,
2378 gtk_tree_path_free (source_row);
2382 gtk_tree_path_free (dest_row);
2385 /* Finish the drag and drop */
2386 gtk_drag_finish (context, success, FALSE, time);
2390 * We define a "drag-drop" signal handler because we do not want to
2391 * use the default one, because the default one always calls
2392 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2393 * signal handler, because there we have all the information available
2394 * to know if the dnd was a success or not.
2397 drag_drop_cb (GtkWidget *widget,
2398 GdkDragContext *context,
2406 if (!context->targets)
2409 /* Check if we're dragging a folder row */
2410 target = gtk_drag_dest_find_target (widget, context, NULL);
2412 /* Request the data from the source. */
2413 gtk_drag_get_data(widget, context, target, time);
2419 * This function expands a node of a tree view if it's not expanded
2420 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2421 * does that, so that's why they're here.
2424 expand_row_timeout (gpointer data)
2426 GtkTreeView *tree_view = data;
2427 GtkTreePath *dest_path = NULL;
2428 GtkTreeViewDropPosition pos;
2429 gboolean result = FALSE;
2431 gdk_threads_enter ();
2433 gtk_tree_view_get_drag_dest_row (tree_view,
2438 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2439 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2440 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2441 gtk_tree_path_free (dest_path);
2445 gtk_tree_path_free (dest_path);
2450 gdk_threads_leave ();
2456 * This function is called whenever the pointer is moved over a widget
2457 * while dragging some data. It installs a timeout that will expand a
2458 * node of the treeview if not expanded yet. This function also calls
2459 * gdk_drag_status in order to set the suggested action that will be
2460 * used by the "drag-data-received" signal handler to know if we
2461 * should do a move or just a copy of the data.
2464 on_drag_motion (GtkWidget *widget,
2465 GdkDragContext *context,
2471 GtkTreeViewDropPosition pos;
2472 GtkTreePath *dest_row;
2473 GtkTreeModel *dest_model;
2474 ModestFolderViewPrivate *priv;
2475 GdkDragAction suggested_action;
2476 gboolean valid_location = FALSE;
2477 TnyFolderStore *folder = NULL;
2479 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2481 if (priv->timer_expander != 0) {
2482 g_source_remove (priv->timer_expander);
2483 priv->timer_expander = 0;
2486 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2491 /* Do not allow drops between folders */
2493 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2494 pos == GTK_TREE_VIEW_DROP_AFTER) {
2495 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2496 gdk_drag_status(context, 0, time);
2497 valid_location = FALSE;
2500 valid_location = TRUE;
2503 /* Check that the destination folder is writable */
2504 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2505 folder = tree_path_to_folder (dest_model, dest_row);
2506 if (folder && TNY_IS_FOLDER (folder)) {
2507 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2509 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2510 valid_location = FALSE;
2515 /* Expand the selected row after 1/2 second */
2516 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2517 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2519 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2521 /* Select the desired action. By default we pick MOVE */
2522 suggested_action = GDK_ACTION_MOVE;
2524 if (context->actions == GDK_ACTION_COPY)
2525 gdk_drag_status(context, GDK_ACTION_COPY, time);
2526 else if (context->actions == GDK_ACTION_MOVE)
2527 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2528 else if (context->actions & suggested_action)
2529 gdk_drag_status(context, suggested_action, time);
2531 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2535 g_object_unref (folder);
2537 gtk_tree_path_free (dest_row);
2539 g_signal_stop_emission_by_name (widget, "drag-motion");
2541 return valid_location;
2545 * This function sets the treeview as a source and a target for dnd
2546 * events. It also connects all the requirede signals.
2549 setup_drag_and_drop (GtkTreeView *self)
2551 /* Set up the folder view as a dnd destination. Set only the
2552 highlight flag, otherwise gtk will have a different
2554 gtk_drag_dest_set (GTK_WIDGET (self),
2555 GTK_DEST_DEFAULT_HIGHLIGHT,
2556 folder_view_drag_types,
2557 G_N_ELEMENTS (folder_view_drag_types),
2558 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2560 g_signal_connect (G_OBJECT (self),
2561 "drag_data_received",
2562 G_CALLBACK (on_drag_data_received),
2566 /* Set up the treeview as a dnd source */
2567 gtk_drag_source_set (GTK_WIDGET (self),
2569 folder_view_drag_types,
2570 G_N_ELEMENTS (folder_view_drag_types),
2571 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2573 g_signal_connect (G_OBJECT (self),
2575 G_CALLBACK (on_drag_motion),
2578 g_signal_connect (G_OBJECT (self),
2580 G_CALLBACK (on_drag_data_get),
2583 g_signal_connect (G_OBJECT (self),
2585 G_CALLBACK (drag_drop_cb),
2590 * This function manages the navigation through the folders using the
2591 * keyboard or the hardware keys in the device
2594 on_key_pressed (GtkWidget *self,
2598 GtkTreeSelection *selection;
2600 GtkTreeModel *model;
2601 gboolean retval = FALSE;
2603 /* Up and Down are automatically managed by the treeview */
2604 if (event->keyval == GDK_Return) {
2605 /* Expand/Collapse the selected row */
2606 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2607 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2610 path = gtk_tree_model_get_path (model, &iter);
2612 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2613 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2615 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2616 gtk_tree_path_free (path);
2618 /* No further processing */
2626 * We listen to the changes in the local folder account name key,
2627 * because we want to show the right name in the view. The local
2628 * folder account name corresponds to the device name in the Maemo
2629 * version. We do this because we do not want to query gconf on each
2630 * tree view refresh. It's better to cache it and change whenever
2634 on_configuration_key_changed (ModestConf* conf,
2636 ModestConfEvent event,
2637 ModestConfNotificationId id,
2638 ModestFolderView *self)
2640 ModestFolderViewPrivate *priv;
2643 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2644 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2646 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2647 g_free (priv->local_account_name);
2649 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2650 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2652 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2653 MODEST_CONF_DEVICE_NAME, NULL);
2655 /* Force a redraw */
2656 #if GTK_CHECK_VERSION(2, 8, 0)
2657 GtkTreeViewColumn * tree_column;
2659 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2660 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2661 gtk_tree_view_column_queue_resize (tree_column);
2663 gtk_widget_queue_draw (GTK_WIDGET (self));
2669 modest_folder_view_set_style (ModestFolderView *self,
2670 ModestFolderViewStyle style)
2672 ModestFolderViewPrivate *priv;
2674 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2675 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2676 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2678 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2681 priv->style = style;
2685 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2686 const gchar *account_id)
2688 ModestFolderViewPrivate *priv;
2689 GtkTreeModel *model;
2691 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2693 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2695 /* This will be used by the filter_row callback,
2696 * to decided which rows to show: */
2697 if (priv->visible_account_id) {
2698 g_free (priv->visible_account_id);
2699 priv->visible_account_id = NULL;
2702 priv->visible_account_id = g_strdup (account_id);
2705 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2706 if (GTK_IS_TREE_MODEL_FILTER (model))
2707 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2709 /* Save settings to gconf */
2710 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2711 MODEST_CONF_FOLDER_VIEW_KEY);
2715 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2717 ModestFolderViewPrivate *priv;
2719 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2721 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2723 return (const gchar *) priv->visible_account_id;
2727 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2731 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2733 gtk_tree_model_get (model, iter,
2734 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2737 gboolean result = FALSE;
2738 if (type == TNY_FOLDER_TYPE_INBOX) {
2742 *inbox_iter = *iter;
2746 if (gtk_tree_model_iter_children (model, &child, iter)) {
2747 if (find_inbox_iter (model, &child, inbox_iter))
2751 } while (gtk_tree_model_iter_next (model, iter));
2760 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2762 GtkTreeModel *model;
2763 GtkTreeIter iter, inbox_iter;
2764 GtkTreeSelection *sel;
2765 GtkTreePath *path = NULL;
2767 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2769 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2773 expand_root_items (self);
2774 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2776 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2777 g_warning ("%s: model is empty", __FUNCTION__);
2781 if (find_inbox_iter (model, &iter, &inbox_iter))
2782 path = gtk_tree_model_get_path (model, &inbox_iter);
2784 path = gtk_tree_path_new_first ();
2786 /* Select the row and free */
2787 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2788 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2789 gtk_tree_path_free (path);
2792 gtk_widget_grab_focus (GTK_WIDGET(self));
2798 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2803 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2804 TnyFolder* a_folder;
2807 gtk_tree_model_get (model, iter,
2808 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2809 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2810 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2814 if (folder == a_folder) {
2815 g_object_unref (a_folder);
2816 *folder_iter = *iter;
2819 g_object_unref (a_folder);
2821 if (gtk_tree_model_iter_children (model, &child, iter)) {
2822 if (find_folder_iter (model, &child, folder_iter, folder))
2826 } while (gtk_tree_model_iter_next (model, iter));
2833 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2836 ModestFolderView *self)
2838 ModestFolderViewPrivate *priv = NULL;
2839 GtkTreeSelection *sel;
2840 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2841 GObject *instance = NULL;
2843 if (!MODEST_IS_FOLDER_VIEW(self))
2846 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2848 priv->reexpand = TRUE;
2850 gtk_tree_model_get (tree_model, iter,
2851 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2852 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2854 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2855 priv->folder_to_select = g_object_ref (instance);
2857 g_object_unref (instance);
2859 if (priv->folder_to_select) {
2861 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2864 path = gtk_tree_model_get_path (tree_model, iter);
2865 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2867 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2869 gtk_tree_selection_select_iter (sel, iter);
2870 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2872 gtk_tree_path_free (path);
2876 modest_folder_view_disable_next_folder_selection (self);
2878 /* Refilter the model */
2879 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2885 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2887 ModestFolderViewPrivate *priv;
2889 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2891 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2893 if (priv->folder_to_select)
2894 g_object_unref(priv->folder_to_select);
2896 priv->folder_to_select = NULL;
2900 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2901 gboolean after_change)
2903 GtkTreeModel *model;
2904 GtkTreeIter iter, folder_iter;
2905 GtkTreeSelection *sel;
2906 ModestFolderViewPrivate *priv = NULL;
2908 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2909 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2911 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2914 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2915 gtk_tree_selection_unselect_all (sel);
2917 if (priv->folder_to_select)
2918 g_object_unref(priv->folder_to_select);
2919 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2923 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2928 /* Refilter the model, before selecting the folder */
2929 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2931 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2932 g_warning ("%s: model is empty", __FUNCTION__);
2936 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2939 path = gtk_tree_model_get_path (model, &folder_iter);
2940 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2942 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2943 gtk_tree_selection_select_iter (sel, &folder_iter);
2944 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2946 gtk_tree_path_free (path);
2954 modest_folder_view_copy_selection (ModestFolderView *self)
2956 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2958 /* Copy selection */
2959 _clipboard_set_selected_data (self, FALSE);
2963 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2965 ModestFolderViewPrivate *priv = NULL;
2966 GtkTreeModel *model = NULL;
2967 const gchar **hidding = NULL;
2968 guint i, n_selected;
2970 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2971 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2973 /* Copy selection */
2974 if (!_clipboard_set_selected_data (folder_view, TRUE))
2977 /* Get hidding ids */
2978 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2980 /* Clear hidding array created by previous cut operation */
2981 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2983 /* Copy hidding array */
2984 priv->n_selected = n_selected;
2985 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2986 for (i=0; i < n_selected; i++)
2987 priv->hidding_ids[i] = g_strdup(hidding[i]);
2989 /* Hide cut folders */
2990 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2991 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2995 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2996 ModestFolderView *folder_view_dst)
2998 GtkTreeModel *filter_model = NULL;
2999 GtkTreeModel *model = NULL;
3000 GtkTreeModel *new_filter_model = NULL;
3002 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3003 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3006 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3007 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3009 /* Build new filter model */
3010 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3011 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3015 /* Set copied model */
3016 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3017 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3018 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3021 g_object_unref (new_filter_model);
3025 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3028 GtkTreeModel *model = NULL;
3029 ModestFolderViewPrivate* priv;
3031 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3033 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3034 priv->show_non_move = show;
3035 /* modest_folder_view_update_model(folder_view, */
3036 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3038 /* Hide special folders */
3039 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3040 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3041 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3045 /* Returns FALSE if it did not selected anything */
3047 _clipboard_set_selected_data (ModestFolderView *folder_view,
3050 ModestFolderViewPrivate *priv = NULL;
3051 TnyFolderStore *folder = NULL;
3052 gboolean retval = FALSE;
3054 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3055 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3057 /* Set selected data on clipboard */
3058 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3059 folder = modest_folder_view_get_selected (folder_view);
3061 /* Do not allow to select an account */
3062 if (TNY_IS_FOLDER (folder)) {
3063 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3068 g_object_unref (folder);
3074 _clear_hidding_filter (ModestFolderView *folder_view)
3076 ModestFolderViewPrivate *priv;
3079 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3080 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3082 if (priv->hidding_ids != NULL) {
3083 for (i=0; i < priv->n_selected; i++)
3084 g_free (priv->hidding_ids[i]);
3085 g_free(priv->hidding_ids);
3091 on_display_name_changed (ModestAccountMgr *mgr,
3092 const gchar *account,
3095 ModestFolderView *self;
3097 self = MODEST_FOLDER_VIEW (user_data);
3099 /* Force a redraw */
3100 #if GTK_CHECK_VERSION(2, 8, 0)
3101 GtkTreeViewColumn * tree_column;
3103 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3104 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3105 gtk_tree_view_column_queue_resize (tree_column);
3107 gtk_widget_queue_draw (GTK_WIDGET (self));