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-dnd.h>
56 #include <modest-platform.h>
57 #include <modest-widget-memory.h>
58 #include <modest-ui-actions.h>
60 /* 'private'/'protected' functions */
61 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
62 static void modest_folder_view_init (ModestFolderView *obj);
63 static void modest_folder_view_finalize (GObject *obj);
65 static void tny_account_store_view_init (gpointer g,
68 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
69 TnyAccountStore *account_store);
71 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
73 static void on_account_removed (TnyAccountStore *self,
77 static void on_account_inserted (TnyAccountStore *self,
81 static void on_account_changed (TnyAccountStore *self,
85 static gint cmp_rows (GtkTreeModel *tree_model,
90 static gboolean filter_row (GtkTreeModel *model,
94 static gboolean on_key_pressed (GtkWidget *self,
98 static void on_configuration_key_changed (ModestConf* conf,
100 ModestConfEvent event,
101 ModestConfNotificationId notification_id,
102 ModestFolderView *self);
105 static void on_drag_data_get (GtkWidget *widget,
106 GdkDragContext *context,
107 GtkSelectionData *selection_data,
112 static void on_drag_data_received (GtkWidget *widget,
113 GdkDragContext *context,
116 GtkSelectionData *selection_data,
121 static gboolean on_drag_motion (GtkWidget *widget,
122 GdkDragContext *context,
128 static void expand_root_items (ModestFolderView *self);
130 static gint expand_row_timeout (gpointer data);
132 static void setup_drag_and_drop (GtkTreeView *self);
134 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
137 static void _clear_hidding_filter (ModestFolderView *folder_view);
139 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
142 ModestFolderView *self);
145 FOLDER_SELECTION_CHANGED_SIGNAL,
146 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
150 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
151 struct _ModestFolderViewPrivate {
152 TnyAccountStore *account_store;
153 TnyFolderStore *cur_folder_store;
155 TnyFolder *folder_to_select; /* folder to select after the next update */
157 ModestConfNotificationId notification_id;
159 gulong changed_signal;
160 gulong account_inserted_signal;
161 gulong account_removed_signal;
162 gulong account_changed_signal;
163 gulong conf_key_signal;
165 /* not unref this object, its a singlenton */
166 ModestEmailClipboard *clipboard;
168 /* Filter tree model */
172 TnyFolderStoreQuery *query;
173 guint timer_expander;
175 gchar *local_account_name;
176 gchar *visible_account_id;
177 ModestFolderViewStyle style;
179 gboolean reselect; /* we use this to force a reselection of the INBOX */
180 gboolean show_non_move;
181 gboolean reexpand; /* next time we expose, we'll expand all root folders */
183 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
184 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
185 MODEST_TYPE_FOLDER_VIEW, \
186 ModestFolderViewPrivate))
188 static GObjectClass *parent_class = NULL;
190 static guint signals[LAST_SIGNAL] = {0};
193 modest_folder_view_get_type (void)
195 static GType my_type = 0;
197 static const GTypeInfo my_info = {
198 sizeof(ModestFolderViewClass),
199 NULL, /* base init */
200 NULL, /* base finalize */
201 (GClassInitFunc) modest_folder_view_class_init,
202 NULL, /* class finalize */
203 NULL, /* class data */
204 sizeof(ModestFolderView),
206 (GInstanceInitFunc) modest_folder_view_init,
210 static const GInterfaceInfo tny_account_store_view_info = {
211 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
212 NULL, /* interface_finalize */
213 NULL /* interface_data */
217 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
221 g_type_add_interface_static (my_type,
222 TNY_TYPE_ACCOUNT_STORE_VIEW,
223 &tny_account_store_view_info);
229 modest_folder_view_class_init (ModestFolderViewClass *klass)
231 GObjectClass *gobject_class;
232 gobject_class = (GObjectClass*) klass;
234 parent_class = g_type_class_peek_parent (klass);
235 gobject_class->finalize = modest_folder_view_finalize;
237 g_type_class_add_private (gobject_class,
238 sizeof(ModestFolderViewPrivate));
240 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
241 g_signal_new ("folder_selection_changed",
242 G_TYPE_FROM_CLASS (gobject_class),
244 G_STRUCT_OFFSET (ModestFolderViewClass,
245 folder_selection_changed),
247 modest_marshal_VOID__POINTER_BOOLEAN,
248 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
251 * This signal is emitted whenever the currently selected
252 * folder display name is computed. Note that the name could
253 * be different to the folder name, because we could append
254 * the unread messages count to the folder name to build the
255 * folder display name
257 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
258 g_signal_new ("folder-display-name-changed",
259 G_TYPE_FROM_CLASS (gobject_class),
261 G_STRUCT_OFFSET (ModestFolderViewClass,
262 folder_display_name_changed),
264 g_cclosure_marshal_VOID__STRING,
265 G_TYPE_NONE, 1, G_TYPE_STRING);
268 /* Simplify checks for NULLs: */
270 strings_are_equal (const gchar *a, const gchar *b)
276 return (strcmp (a, b) == 0);
283 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
285 GObject *instance = NULL;
287 gtk_tree_model_get (model, iter,
288 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
292 return FALSE; /* keep walking */
294 if (!TNY_IS_ACCOUNT (instance)) {
295 g_object_unref (instance);
296 return FALSE; /* keep walking */
299 /* Check if this is the looked-for account: */
300 TnyAccount *this_account = TNY_ACCOUNT (instance);
301 TnyAccount *account = TNY_ACCOUNT (data);
303 const gchar *this_account_id = tny_account_get_id(this_account);
304 const gchar *account_id = tny_account_get_id(account);
305 g_object_unref (instance);
308 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
309 if (strings_are_equal(this_account_id, account_id)) {
310 /* Tell the model that the data has changed, so that
311 * it calls the cell_data_func callbacks again: */
312 /* TODO: This does not seem to actually cause the new string to be shown: */
313 gtk_tree_model_row_changed (model, path, iter);
315 return TRUE; /* stop walking */
318 return FALSE; /* keep walking */
323 ModestFolderView *self;
324 gchar *previous_name;
325 } GetMmcAccountNameData;
328 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
330 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
332 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
334 if (!strings_are_equal (
335 tny_account_get_name(TNY_ACCOUNT(account)),
336 data->previous_name)) {
338 /* Tell the model that the data has changed, so that
339 * it calls the cell_data_func callbacks again: */
340 ModestFolderView *self = data->self;
341 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
343 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
346 g_free (data->previous_name);
347 g_slice_free (GetMmcAccountNameData, data);
351 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
352 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
354 ModestFolderViewPrivate *priv;
357 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
358 GObject *instance = NULL;
360 g_return_if_fail (column);
361 g_return_if_fail (tree_model);
362 g_return_if_fail (iter != NULL);
364 gtk_tree_model_get (tree_model, iter,
365 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
366 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
367 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
369 rendobj = G_OBJECT(renderer);
379 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
380 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
382 gchar *item_name = NULL;
383 gint item_weight = 400;
385 if (type != TNY_FOLDER_TYPE_ROOT) {
388 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
389 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
390 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
391 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
393 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
397 /* note: we cannot reliably get the counts from the tree model, we need
398 * to use explicit calls on tny_folder for some reason.
400 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
401 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
402 (type == TNY_FOLDER_TYPE_OUTBOX) ||
403 (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
404 number = tny_folder_get_all_count (TNY_FOLDER(instance));
406 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
408 /* Use bold font style if there are unread or unset messages */
410 item_name = g_strdup_printf ("%s (%d)", fname, number);
413 item_name = g_strdup (fname);
417 } else if (TNY_IS_ACCOUNT (instance)) {
418 /* If it's a server account */
419 if (modest_tny_account_is_virtual_local_folders (
420 TNY_ACCOUNT (instance))) {
421 item_name = g_strdup (priv->local_account_name);
423 } else if (modest_tny_account_is_memory_card_account (
424 TNY_ACCOUNT (instance))) {
425 /* fname is only correct when the items are first
426 * added to the model, not when the account is
427 * changed later, so get the name from the account
429 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
432 item_name = g_strdup (fname);
438 item_name = g_strdup ("unknown");
440 if (item_name && item_weight) {
441 /* Set the name in the treeview cell: */
442 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
444 /* Notify display name observers */
445 /* TODO: What listens for this signal, and how can it use only the new name? */
446 if (G_OBJECT (priv->cur_folder_store) == instance) {
447 g_signal_emit (G_OBJECT(self),
448 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
455 /* If it is a Memory card account, make sure that we have the correct name.
456 * This function will be trigerred again when the name has been retrieved: */
457 if (TNY_IS_STORE_ACCOUNT (instance) &&
458 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
460 /* Get the account name asynchronously: */
461 GetMmcAccountNameData *callback_data =
462 g_slice_new0(GetMmcAccountNameData);
463 callback_data->self = self;
465 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
467 callback_data->previous_name = g_strdup (name);
469 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
470 on_get_mmc_account_name, callback_data);
473 g_object_unref (G_OBJECT (instance));
478 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
479 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
481 GObject *rendobj = NULL, *instance = NULL;
482 GdkPixbuf *pixbuf = NULL;
483 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
484 const gchar *account_id = NULL;
485 gboolean has_children;
487 rendobj = G_OBJECT(renderer);
488 gtk_tree_model_get (tree_model, iter,
489 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
490 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
492 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
497 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
498 /* We include the MERGE type here because it's used to create
499 the local OUTBOX folder */
500 if (type == TNY_FOLDER_TYPE_NORMAL ||
501 type == TNY_FOLDER_TYPE_UNKNOWN) {
502 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
506 case TNY_FOLDER_TYPE_ROOT:
507 if (TNY_IS_ACCOUNT (instance)) {
509 if (modest_tny_account_is_virtual_local_folders (
510 TNY_ACCOUNT (instance))) {
511 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
514 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
516 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
517 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
519 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
523 case TNY_FOLDER_TYPE_INBOX:
524 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
526 case TNY_FOLDER_TYPE_OUTBOX:
527 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
529 case TNY_FOLDER_TYPE_JUNK:
530 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
532 case TNY_FOLDER_TYPE_SENT:
533 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
535 case TNY_FOLDER_TYPE_TRASH:
536 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
538 case TNY_FOLDER_TYPE_DRAFTS:
539 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
541 case TNY_FOLDER_TYPE_NORMAL:
543 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
547 g_object_unref (G_OBJECT (instance));
550 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
551 if (has_children && (pixbuf != NULL)) {
552 GdkPixbuf *open_pixbuf, *closed_pixbuf;
553 GdkPixbuf *open_emblem, *closed_emblem;
554 open_pixbuf = gdk_pixbuf_copy (pixbuf);
555 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
556 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
557 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
560 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
561 MIN (gdk_pixbuf_get_width (open_emblem),
562 gdk_pixbuf_get_width (open_pixbuf)),
563 MIN (gdk_pixbuf_get_height (open_emblem),
564 gdk_pixbuf_get_height (open_pixbuf)),
565 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
566 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
567 g_object_unref (open_emblem);
570 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
571 MIN (gdk_pixbuf_get_width (closed_emblem),
572 gdk_pixbuf_get_width (closed_pixbuf)),
573 MIN (gdk_pixbuf_get_height (closed_emblem),
574 gdk_pixbuf_get_height (closed_pixbuf)),
575 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
576 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
577 g_object_unref (closed_emblem);
580 g_object_unref (closed_pixbuf);
582 g_object_unref (open_pixbuf);
586 g_object_unref (pixbuf);
590 add_columns (GtkWidget *treeview)
592 GtkTreeViewColumn *column;
593 GtkCellRenderer *renderer;
594 GtkTreeSelection *sel;
597 column = gtk_tree_view_column_new ();
599 /* Set icon and text render function */
600 renderer = gtk_cell_renderer_pixbuf_new();
601 gtk_tree_view_column_pack_start (column, renderer, FALSE);
602 gtk_tree_view_column_set_cell_data_func(column, renderer,
603 icon_cell_data, treeview, NULL);
605 renderer = gtk_cell_renderer_text_new();
606 gtk_tree_view_column_pack_start (column, renderer, FALSE);
607 gtk_tree_view_column_set_cell_data_func(column, renderer,
608 text_cell_data, treeview, NULL);
610 /* Set selection mode */
611 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
612 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
614 /* Set treeview appearance */
615 gtk_tree_view_column_set_spacing (column, 2);
616 gtk_tree_view_column_set_resizable (column, TRUE);
617 gtk_tree_view_column_set_fixed_width (column, TRUE);
618 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
619 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
622 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
626 modest_folder_view_init (ModestFolderView *obj)
628 ModestFolderViewPrivate *priv;
631 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
633 priv->timer_expander = 0;
634 priv->account_store = NULL;
636 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
637 priv->cur_folder_store = NULL;
638 priv->visible_account_id = NULL;
639 priv->folder_to_select = NULL;
641 priv->reexpand = TRUE;
643 /* Initialize the local account name */
644 conf = modest_runtime_get_conf();
645 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
647 /* Init email clipboard */
648 priv->clipboard = modest_runtime_get_email_clipboard ();
649 priv->hidding_ids = NULL;
650 priv->n_selected = 0;
651 priv->reselect = FALSE;
652 priv->show_non_move = TRUE;
655 add_columns (GTK_WIDGET (obj));
657 /* Setup drag and drop */
658 setup_drag_and_drop (GTK_TREE_VIEW(obj));
660 /* Connect signals */
661 g_signal_connect (G_OBJECT (obj),
663 G_CALLBACK (on_key_pressed), NULL);
666 * Track changes in the local account name (in the device it
667 * will be the device name)
669 priv->notification_id = modest_conf_listen_to_namespace (conf,
670 MODEST_CONF_NAMESPACE);
671 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
673 G_CALLBACK(on_configuration_key_changed),
678 tny_account_store_view_init (gpointer g, gpointer iface_data)
680 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
682 klass->set_account_store_func = modest_folder_view_set_account_store;
688 modest_folder_view_finalize (GObject *obj)
690 ModestFolderViewPrivate *priv;
691 GtkTreeSelection *sel;
693 g_return_if_fail (obj);
695 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
697 if (priv->notification_id) {
698 modest_conf_forget_namespace (modest_runtime_get_conf (),
699 MODEST_CONF_NAMESPACE,
700 priv->notification_id);
703 if (priv->timer_expander != 0) {
704 g_source_remove (priv->timer_expander);
705 priv->timer_expander = 0;
708 if (priv->account_store) {
709 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
710 priv->account_inserted_signal);
711 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
712 priv->account_removed_signal);
713 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
714 priv->account_changed_signal);
715 g_object_unref (G_OBJECT(priv->account_store));
716 priv->account_store = NULL;
720 g_object_unref (G_OBJECT (priv->query));
724 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
725 if (priv->folder_to_select) {
726 g_object_unref (G_OBJECT(priv->folder_to_select));
727 priv->folder_to_select = NULL;
730 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
732 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
734 g_free (priv->local_account_name);
735 g_free (priv->visible_account_id);
737 if (priv->conf_key_signal) {
738 g_signal_handler_disconnect (modest_runtime_get_conf (),
739 priv->conf_key_signal);
740 priv->conf_key_signal = 0;
743 if (priv->cur_folder_store) {
744 if (TNY_IS_FOLDER(priv->cur_folder_store))
745 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
746 /* FALSE --> expunge the message */
748 g_object_unref (priv->cur_folder_store);
749 priv->cur_folder_store = NULL;
752 /* Clear hidding array created by cut operation */
753 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
755 G_OBJECT_CLASS(parent_class)->finalize (obj);
760 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
762 ModestFolderViewPrivate *priv;
765 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
766 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
768 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
769 device = tny_account_store_get_device (account_store);
771 if (G_UNLIKELY (priv->account_store)) {
773 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
774 priv->account_inserted_signal))
775 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
776 priv->account_inserted_signal);
777 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
778 priv->account_removed_signal))
779 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
780 priv->account_removed_signal);
781 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
782 priv->account_changed_signal))
783 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
784 priv->account_changed_signal);
785 g_object_unref (G_OBJECT (priv->account_store));
788 priv->account_store = g_object_ref (G_OBJECT (account_store));
790 priv->account_removed_signal =
791 g_signal_connect (G_OBJECT(account_store), "account_removed",
792 G_CALLBACK (on_account_removed), self);
794 priv->account_inserted_signal =
795 g_signal_connect (G_OBJECT(account_store), "account_inserted",
796 G_CALLBACK (on_account_inserted), self);
798 priv->account_changed_signal =
799 g_signal_connect (G_OBJECT(account_store), "account_changed",
800 G_CALLBACK (on_account_changed), self);
802 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
804 g_object_unref (G_OBJECT (device));
808 on_account_inserted (TnyAccountStore *account_store,
812 ModestFolderViewPrivate *priv;
813 GtkTreeModel *sort_model, *filter_model;
815 /* Ignore transport account insertions, we're not showing them
816 in the folder view */
817 if (TNY_IS_TRANSPORT_ACCOUNT (account))
820 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
822 /* If we're adding a new account, and there is no previous
823 one, we need to select the visible server account */
824 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
825 !priv->visible_account_id)
826 modest_widget_memory_restore (modest_runtime_get_conf(),
827 G_OBJECT (user_data),
828 MODEST_CONF_FOLDER_VIEW_KEY);
830 /* Get the inner model */
831 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
832 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
834 /* Insert the account in the model */
835 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
842 on_account_changed (TnyAccountStore *account_store,
843 TnyAccount *tny_account,
847 ModestFolderViewPrivate *priv;
848 GtkTreeModel *sort_model, *filter_model;
850 /* Ignore transport account insertions, we're not showing them
851 in the folder view */
852 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
855 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
857 /* Get the inner model */
858 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
859 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
861 /* Remove the account from the model */
862 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
863 G_OBJECT (tny_account));
865 /* Insert the account in the model */
866 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
867 G_OBJECT (tny_account));
873 on_account_removed (TnyAccountStore *account_store,
877 ModestFolderView *self = NULL;
878 ModestFolderViewPrivate *priv;
879 GtkTreeModel *sort_model, *filter_model;
880 GtkTreeSelection *sel = NULL;
882 /* Ignore transport account removals, we're not showing them
883 in the folder view */
884 if (TNY_IS_TRANSPORT_ACCOUNT (account))
887 self = MODEST_FOLDER_VIEW (user_data);
888 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
890 /* Invalidate the cur_folder_store only if the selected folder
891 belongs to the account that is being removed */
892 if (priv->cur_folder_store) {
893 TnyAccount *selected_folder_account = NULL;
895 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
896 selected_folder_account =
897 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
899 selected_folder_account =
900 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
903 if (selected_folder_account == account) {
904 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
905 gtk_tree_selection_unselect_all (sel);
907 g_object_unref (selected_folder_account);
910 /* Invalidate row to select only if the folder to select
911 belongs to the account that is being removed*/
912 if (priv->folder_to_select) {
913 TnyAccount *folder_to_select_account = NULL;
915 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
916 if (folder_to_select_account == account) {
917 /* modest_folder_view_disable_next_folder_selection (self); */
918 g_object_unref (priv->folder_to_select);
919 priv->folder_to_select = NULL;
921 g_object_unref (folder_to_select_account);
924 /* Remove the account from the model */
925 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
926 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
927 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
930 /* If the removed account is the currently viewed one then
931 clear the configuration value. The new visible account will be the default account */
932 if (priv->visible_account_id &&
933 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
935 /* Clear the current visible account_id */
936 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
938 /* Call the restore method, this will set the new visible account */
939 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
940 MODEST_CONF_FOLDER_VIEW_KEY);
943 /* Select the INBOX */
944 modest_folder_view_select_first_inbox_or_local (self);
948 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
950 GtkTreeViewColumn *col;
952 g_return_if_fail (self);
954 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
956 g_printerr ("modest: failed get column for title\n");
960 gtk_tree_view_column_set_title (col, title);
961 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
966 modest_folder_view_on_map (ModestFolderView *self,
967 GdkEventExpose *event,
970 ModestFolderViewPrivate *priv;
972 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
974 /* This won't happen often */
975 if (G_UNLIKELY (priv->reselect)) {
976 /* Select the first inbox or the local account if not found */
978 /* TODO: this could cause a lock at startup, so we
979 comment it for the moment. We know that this will
980 be a bug, because the INBOX is not selected, but we
981 need to rewrite some parts of Modest to avoid the
982 deathlock situation */
983 /* TODO: check if this is still the case */
984 priv->reselect = FALSE;
985 modest_folder_view_select_first_inbox_or_local (self);
986 /* Notify the display name observers */
987 g_signal_emit (G_OBJECT(self),
988 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
992 if (priv->reexpand) {
993 expand_root_items (self);
994 priv->reexpand = FALSE;
1001 modest_folder_view_new (TnyFolderStoreQuery *query)
1004 ModestFolderViewPrivate *priv;
1005 GtkTreeSelection *sel;
1007 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1008 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1011 priv->query = g_object_ref (query);
1013 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1014 priv->changed_signal = g_signal_connect (sel, "changed",
1015 G_CALLBACK (on_selection_changed), self);
1017 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1019 return GTK_WIDGET(self);
1022 /* this feels dirty; any other way to expand all the root items? */
1024 expand_root_items (ModestFolderView *self)
1027 GtkTreeModel *model;
1030 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1031 path = gtk_tree_path_new_first ();
1033 /* all folders should have child items, so.. */
1035 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1036 gtk_tree_path_next (path);
1037 } while (gtk_tree_model_get_iter (model, &iter, path));
1039 gtk_tree_path_free (path);
1043 * We use this function to implement the
1044 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1045 * account in this case, and the local folders.
1048 filter_row (GtkTreeModel *model,
1052 ModestFolderViewPrivate *priv;
1053 gboolean retval = TRUE;
1054 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1055 GObject *instance = NULL;
1056 const gchar *id = NULL;
1058 gboolean found = FALSE;
1059 gboolean cleared = FALSE;
1061 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1062 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1064 gtk_tree_model_get (model, iter,
1065 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1066 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1069 /* Do not show if there is no instance, this could indeed
1070 happen when the model is being modified while it's being
1071 drawn. This could occur for example when moving folders
1076 if (type == TNY_FOLDER_TYPE_ROOT) {
1077 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1078 account instead of a folder. */
1079 if (TNY_IS_ACCOUNT (instance)) {
1080 TnyAccount *acc = TNY_ACCOUNT (instance);
1081 const gchar *account_id = tny_account_get_id (acc);
1083 /* If it isn't a special folder,
1084 * don't show it unless it is the visible account: */
1085 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1086 !modest_tny_account_is_virtual_local_folders (acc) &&
1087 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1089 /* Show only the visible account id */
1090 if (priv->visible_account_id) {
1091 if (strcmp (account_id, priv->visible_account_id))
1098 /* Never show these to the user. They are merged into one folder
1099 * in the local-folders account instead: */
1100 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1105 /* Check hiding (if necessary) */
1106 cleared = modest_email_clipboard_cleared (priv->clipboard);
1107 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1108 id = tny_folder_get_id (TNY_FOLDER(instance));
1109 if (priv->hidding_ids != NULL)
1110 for (i=0; i < priv->n_selected && !found; i++)
1111 if (priv->hidding_ids[i] != NULL && id != NULL)
1112 found = (!strcmp (priv->hidding_ids[i], id));
1118 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1119 folder as no message can be move there according to UI specs */
1120 if (!priv->show_non_move)
1124 case TNY_FOLDER_TYPE_OUTBOX:
1125 case TNY_FOLDER_TYPE_SENT:
1126 case TNY_FOLDER_TYPE_DRAFTS:
1129 case TNY_FOLDER_TYPE_UNKNOWN:
1130 case TNY_FOLDER_TYPE_NORMAL:
1131 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1132 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1133 || type == TNY_FOLDER_TYPE_DRAFTS)
1144 g_object_unref (instance);
1151 modest_folder_view_update_model (ModestFolderView *self,
1152 TnyAccountStore *account_store)
1154 ModestFolderViewPrivate *priv;
1155 GtkTreeModel *model /* , *old_model */;
1156 /* TnyAccount *local_account; */
1157 TnyList *model_as_list;
1159 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1160 g_return_val_if_fail (account_store, FALSE);
1162 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1164 /* Notify that there is no folder selected */
1165 g_signal_emit (G_OBJECT(self),
1166 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1168 if (priv->cur_folder_store) {
1169 g_object_unref (priv->cur_folder_store);
1170 priv->cur_folder_store = NULL;
1173 /* FIXME: the local accounts are not shown when the query
1174 selects only the subscribed folders. */
1175 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1176 model = tny_gtk_folder_store_tree_model_new (NULL);
1178 /* Deal with the model via its TnyList Interface,
1179 * filling the TnyList via a get_accounts() call: */
1180 model_as_list = TNY_LIST(model);
1182 /* Get the accounts: */
1183 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1185 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1186 g_object_unref (model_as_list);
1187 model_as_list = NULL;
1189 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1191 sortable = gtk_tree_model_sort_new_with_model (model);
1192 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1193 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1194 GTK_SORT_ASCENDING);
1195 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1196 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1197 cmp_rows, NULL, NULL);
1199 /* Create filter model */
1200 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1201 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1207 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1208 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1209 (GCallback) on_row_inserted_maybe_select_folder, self);
1212 g_object_unref (model);
1213 g_object_unref (filter_model);
1214 g_object_unref (sortable);
1216 /* Force a reselection of the INBOX next time the widget is shown */
1217 priv->reselect = TRUE;
1224 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1226 GtkTreeModel *model = NULL;
1227 TnyFolderStore *folder = NULL;
1229 ModestFolderView *tree_view = NULL;
1230 ModestFolderViewPrivate *priv = NULL;
1231 gboolean selected = FALSE;
1233 g_return_if_fail (sel);
1234 g_return_if_fail (user_data);
1236 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1238 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1240 /* Notify the display name observers */
1241 g_signal_emit (G_OBJECT(user_data),
1242 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1245 tree_view = MODEST_FOLDER_VIEW (user_data);
1248 gtk_tree_model_get (model, &iter,
1249 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1252 /* If the folder is the same do not notify */
1253 if (priv->cur_folder_store == folder && folder) {
1254 g_object_unref (folder);
1259 /* Current folder was unselected */
1260 if (priv->cur_folder_store) {
1261 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1262 priv->cur_folder_store, FALSE);
1264 if (TNY_IS_FOLDER(priv->cur_folder_store))
1265 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1266 FALSE, NULL, NULL, NULL);
1268 /* FALSE --> don't expunge the messages */
1270 g_object_unref (priv->cur_folder_store);
1271 priv->cur_folder_store = NULL;
1274 /* New current references */
1275 priv->cur_folder_store = folder;
1277 /* New folder has been selected */
1278 g_signal_emit (G_OBJECT(tree_view),
1279 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1280 0, priv->cur_folder_store, TRUE);
1284 modest_folder_view_get_selected (ModestFolderView *self)
1286 ModestFolderViewPrivate *priv;
1288 g_return_val_if_fail (self, NULL);
1290 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1291 if (priv->cur_folder_store)
1292 g_object_ref (priv->cur_folder_store);
1294 return priv->cur_folder_store;
1298 get_cmp_rows_type_pos (GObject *folder)
1300 /* Remote accounts -> Local account -> MMC account .*/
1303 if (TNY_IS_ACCOUNT (folder) &&
1304 modest_tny_account_is_virtual_local_folders (
1305 TNY_ACCOUNT (folder))) {
1307 } else if (TNY_IS_ACCOUNT (folder)) {
1308 TnyAccount *account = TNY_ACCOUNT (folder);
1309 const gchar *account_id = tny_account_get_id (account);
1310 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1316 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1317 return -1; /* Should never happen */
1322 get_cmp_subfolder_type_pos (TnyFolderType t)
1324 /* Inbox, Outbox, Drafts, Sent, User */
1328 case TNY_FOLDER_TYPE_INBOX:
1331 case TNY_FOLDER_TYPE_OUTBOX:
1334 case TNY_FOLDER_TYPE_DRAFTS:
1337 case TNY_FOLDER_TYPE_SENT:
1346 * This function orders the mail accounts according to these rules:
1347 * 1st - remote accounts
1348 * 2nd - local account
1352 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1356 gchar *name1 = NULL;
1357 gchar *name2 = NULL;
1358 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1359 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1360 GObject *folder1 = NULL;
1361 GObject *folder2 = NULL;
1363 gtk_tree_model_get (tree_model, iter1,
1364 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1365 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1366 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1368 gtk_tree_model_get (tree_model, iter2,
1369 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1370 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1371 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1374 /* Return if we get no folder. This could happen when folder
1375 operations are happening. The model is updated after the
1376 folder copy/move actually occurs, so there could be
1377 situations where the model to be drawn is not correct */
1378 if (!folder1 || !folder2)
1381 if (type == TNY_FOLDER_TYPE_ROOT) {
1382 /* Compare the types, so that
1383 * Remote accounts -> Local account -> MMC account .*/
1384 const gint pos1 = get_cmp_rows_type_pos (folder1);
1385 const gint pos2 = get_cmp_rows_type_pos (folder2);
1386 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1387 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1390 else if (pos1 > pos2)
1393 /* Compare items of the same type: */
1395 TnyAccount *account1 = NULL;
1396 if (TNY_IS_ACCOUNT (folder1))
1397 account1 = TNY_ACCOUNT (folder1);
1399 TnyAccount *account2 = NULL;
1400 if (TNY_IS_ACCOUNT (folder2))
1401 account2 = TNY_ACCOUNT (folder2);
1403 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1404 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1406 if (!account_id && !account_id2) {
1408 } else if (!account_id) {
1410 } else if (!account_id2) {
1412 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1415 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1419 gint cmp1 = 0, cmp2 = 0;
1420 /* get the parent to know if it's a local folder */
1423 gboolean has_parent;
1424 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1426 GObject *parent_folder;
1427 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1428 gtk_tree_model_get (tree_model, &parent,
1429 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1430 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1432 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1433 TNY_IS_ACCOUNT (parent_folder) &&
1434 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1435 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1436 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1438 g_object_unref (parent_folder);
1441 /* if they are not local folders */
1443 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1444 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1448 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1450 cmp = (cmp1 - cmp2);
1455 g_object_unref(G_OBJECT(folder1));
1457 g_object_unref(G_OBJECT(folder2));
1465 /*****************************************************************************/
1466 /* DRAG and DROP stuff */
1467 /*****************************************************************************/
1470 * This function fills the #GtkSelectionData with the row and the
1471 * model that has been dragged. It's called when this widget is a
1472 * source for dnd after the event drop happened
1475 on_drag_data_get (GtkWidget *widget,
1476 GdkDragContext *context,
1477 GtkSelectionData *selection_data,
1482 GtkTreeSelection *selection;
1483 GtkTreeModel *model;
1485 GtkTreePath *source_row;
1487 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1488 gtk_tree_selection_get_selected (selection, &model, &iter);
1489 source_row = gtk_tree_model_get_path (model, &iter);
1491 gtk_tree_set_row_drag_data (selection_data,
1495 gtk_tree_path_free (source_row);
1498 typedef struct _DndHelper {
1499 gboolean delete_source;
1500 GtkTreePath *source_row;
1501 GdkDragContext *context;
1507 * This function is the callback of the
1508 * modest_mail_operation_xfer_msgs () and
1509 * modest_mail_operation_xfer_folder() calls. We check here if the
1510 * message/folder was correctly asynchronously transferred. The reason
1511 * to use the same callback is that the code is the same, it only has
1512 * to check that the operation went fine and then finalize the drag
1516 on_progress_changed (ModestMailOperation *mail_op,
1517 ModestMailOperationState *state,
1523 helper = (DndHelper *) user_data;
1525 if (!state->finished)
1528 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1534 /* Notify the drag source. Never call delete, the monitor will
1535 do the job if needed */
1536 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1538 /* Free the helper */
1539 gtk_tree_path_free (helper->source_row);
1540 g_slice_free (DndHelper, helper);
1544 /* get the folder for the row the treepath refers to. */
1545 /* folder must be unref'd */
1547 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1550 TnyFolder *folder = NULL;
1552 if (gtk_tree_model_get_iter (model,&iter, path))
1553 gtk_tree_model_get (model, &iter,
1554 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1560 show_banner_move_target_error ()
1562 ModestWindow *main_window;
1564 main_window = modest_window_mgr_get_main_window(
1565 modest_runtime_get_window_mgr());
1567 modest_platform_information_banner(GTK_WIDGET(main_window),
1568 NULL, _("mail_in_ui_folder_move_target_error"));
1572 * This function is used by drag_data_received_cb to manage drag and
1573 * drop of a header, i.e, and drag from the header view to the folder
1577 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1578 GtkTreeModel *dest_model,
1579 GtkTreePath *dest_row,
1582 TnyList *headers = NULL;
1583 TnyHeader *header = NULL;
1584 TnyFolder *folder = NULL;
1585 ModestMailOperation *mail_op = NULL;
1586 GtkTreeIter source_iter;
1587 ModestWindowMgr *mgr = NULL; /*no need for unref*/
1588 ModestWindow *main_win = NULL; /*no need for unref*/
1590 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1591 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1592 g_return_if_fail (dest_row);
1593 g_return_if_fail (helper);
1596 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1597 gtk_tree_model_get (source_model, &source_iter,
1598 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1600 if (!TNY_IS_HEADER(header)) {
1601 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1605 /* Check if the selected message is in msg-view. If it is than
1606 * do not enable drag&drop on that. */
1607 mgr = modest_runtime_get_window_mgr ();
1608 if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1612 folder = tree_path_to_folder (dest_model, dest_row);
1613 if (!TNY_IS_FOLDER(folder)) {
1614 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1615 show_banner_move_target_error();
1618 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1619 g_debug ("folder rules: cannot write to that folder");
1623 headers = tny_simple_list_new ();
1624 tny_list_append (headers, G_OBJECT (header));
1626 main_win = modest_window_mgr_get_main_window(mgr);
1627 if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1628 == GTK_RESPONSE_CANCEL)
1631 /* Transfer message */
1632 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1634 modest_ui_actions_move_folder_error_handler,
1636 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1638 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1639 G_CALLBACK (on_progress_changed), helper);
1641 modest_mail_operation_xfer_msgs (mail_op,
1644 helper->delete_source,
1649 if (G_IS_OBJECT(mail_op))
1650 g_object_unref (G_OBJECT (mail_op));
1651 if (G_IS_OBJECT(header))
1652 g_object_unref (G_OBJECT (header));
1653 if (G_IS_OBJECT(folder))
1654 g_object_unref (G_OBJECT (folder));
1655 if (G_IS_OBJECT(headers))
1656 g_object_unref (headers);
1660 * This function is used by drag_data_received_cb to manage drag and
1661 * drop of a folder, i.e, and drag from the folder view to the same
1665 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1666 GtkTreeModel *dest_model,
1667 GtkTreePath *dest_row,
1668 GtkSelectionData *selection_data,
1671 ModestMailOperation *mail_op = NULL;
1672 GtkTreeIter dest_iter, iter;
1673 TnyFolderStore *dest_folder = NULL;
1674 TnyFolder *folder = NULL;
1675 gboolean forbidden = FALSE;
1678 /* check the folder rules for the destination */
1679 folder = tree_path_to_folder (dest_model, dest_row);
1680 if (TNY_IS_FOLDER(folder)) {
1681 ModestTnyFolderRules rules =
1682 modest_tny_folder_get_rules (folder);
1683 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1686 g_debug ("folder rules: cannot write to that folder");
1687 } else if (TNY_IS_FOLDER_STORE(folder)){
1688 /* enable local root as destination for folders */
1689 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1690 && TNY_IS_ACCOUNT (folder))
1693 g_object_unref (folder);
1696 /* check the folder rules for the source */
1697 folder = tree_path_to_folder (source_model, helper->source_row);
1698 if (TNY_IS_FOLDER(folder)) {
1699 ModestTnyFolderRules rules =
1700 modest_tny_folder_get_rules (folder);
1701 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1703 g_debug ("folder rules: cannot move that folder");
1706 g_object_unref (folder);
1710 /* Check if the drag is possible */
1711 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1712 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1713 gtk_tree_path_free (helper->source_row);
1714 g_slice_free (DndHelper, helper);
1719 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1720 gtk_tree_model_get (dest_model, &dest_iter,
1721 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1723 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1724 gtk_tree_model_get (source_model, &iter,
1725 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1728 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1729 if (modest_platform_connect_and_wait_if_network_folderstore (
1730 NULL, dest_folder) &&
1731 modest_platform_connect_and_wait_if_network_folderstore (
1732 NULL, TNY_FOLDER_STORE (folder))) {
1733 /* Do the mail operation */
1734 mail_op = modest_mail_operation_new_with_error_handling (
1735 MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1737 modest_ui_actions_move_folder_error_handler,
1739 modest_mail_operation_queue_add (
1740 modest_runtime_get_mail_operation_queue (),
1745 G_CALLBACK (on_progress_changed),
1748 modest_mail_operation_xfer_folder (mail_op,
1751 helper->delete_source,
1755 g_object_unref (G_OBJECT (mail_op));
1759 g_object_unref (G_OBJECT (dest_folder));
1760 g_object_unref (G_OBJECT (folder));
1764 * This function receives the data set by the "drag-data-get" signal
1765 * handler. This information comes within the #GtkSelectionData. This
1766 * function will manage both the drags of folders of the treeview and
1767 * drags of headers of the header view widget.
1770 on_drag_data_received (GtkWidget *widget,
1771 GdkDragContext *context,
1774 GtkSelectionData *selection_data,
1779 GtkWidget *source_widget;
1780 GtkTreeModel *dest_model, *source_model;
1781 GtkTreePath *source_row, *dest_row;
1782 GtkTreeViewDropPosition pos;
1783 gboolean success = FALSE, delete_source = FALSE;
1784 DndHelper *helper = NULL;
1786 /* Do not allow further process */
1787 g_signal_stop_emission_by_name (widget, "drag-data-received");
1788 source_widget = gtk_drag_get_source_widget (context);
1790 /* Get the action */
1791 if (context->action == GDK_ACTION_MOVE) {
1792 delete_source = TRUE;
1794 /* Notify that there is no folder selected. We need to
1795 do this in order to update the headers view (and
1796 its monitors, because when moving, the old folder
1797 won't longer exist. We can not wait for the end of
1798 the operation, because the operation won't start if
1799 the folder is in use */
1800 if (source_widget == widget) {
1801 ModestFolderViewPrivate *priv;
1803 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1804 if (priv->cur_folder_store) {
1805 g_object_unref (priv->cur_folder_store);
1806 priv->cur_folder_store = NULL;
1809 g_signal_emit (G_OBJECT (widget),
1810 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1814 /* Check if the get_data failed */
1815 if (selection_data == NULL || selection_data->length < 0)
1816 gtk_drag_finish (context, success, FALSE, time);
1818 /* Get the models */
1819 gtk_tree_get_row_drag_data (selection_data,
1823 /* Select the destination model */
1824 if (source_widget == widget) {
1825 dest_model = source_model;
1827 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1830 /* Get the path to the destination row. Can not call
1831 gtk_tree_view_get_drag_dest_row() because the source row
1832 is not selected anymore */
1833 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1836 /* Only allow drops IN other rows */
1837 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1838 gtk_drag_finish (context, success, FALSE, time);
1840 /* Create the helper */
1841 helper = g_slice_new0 (DndHelper);
1842 helper->delete_source = delete_source;
1843 helper->source_row = gtk_tree_path_copy (source_row);
1844 helper->context = context;
1845 helper->time = time;
1847 /* Drags from the header view */
1848 if (source_widget != widget) {
1850 drag_and_drop_from_header_view (source_model,
1857 drag_and_drop_from_folder_view (source_model,
1865 gtk_tree_path_free (source_row);
1866 gtk_tree_path_free (dest_row);
1870 * We define a "drag-drop" signal handler because we do not want to
1871 * use the default one, because the default one always calls
1872 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1873 * signal handler, because there we have all the information available
1874 * to know if the dnd was a success or not.
1877 drag_drop_cb (GtkWidget *widget,
1878 GdkDragContext *context,
1886 if (!context->targets)
1889 /* Check if we're dragging a folder row */
1890 target = gtk_drag_dest_find_target (widget, context, NULL);
1892 /* Request the data from the source. */
1893 gtk_drag_get_data(widget, context, target, time);
1899 * This function expands a node of a tree view if it's not expanded
1900 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1901 * does that, so that's why they're here.
1904 expand_row_timeout (gpointer data)
1906 GtkTreeView *tree_view = data;
1907 GtkTreePath *dest_path = NULL;
1908 GtkTreeViewDropPosition pos;
1909 gboolean result = FALSE;
1911 GDK_THREADS_ENTER ();
1913 gtk_tree_view_get_drag_dest_row (tree_view,
1918 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1919 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1920 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1921 gtk_tree_path_free (dest_path);
1925 gtk_tree_path_free (dest_path);
1930 GDK_THREADS_LEAVE ();
1936 * This function is called whenever the pointer is moved over a widget
1937 * while dragging some data. It installs a timeout that will expand a
1938 * node of the treeview if not expanded yet. This function also calls
1939 * gdk_drag_status in order to set the suggested action that will be
1940 * used by the "drag-data-received" signal handler to know if we
1941 * should do a move or just a copy of the data.
1944 on_drag_motion (GtkWidget *widget,
1945 GdkDragContext *context,
1951 GtkTreeViewDropPosition pos;
1952 GtkTreePath *dest_row;
1953 ModestFolderViewPrivate *priv;
1954 GdkDragAction suggested_action;
1955 gboolean valid_location = FALSE;
1957 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1959 if (priv->timer_expander != 0) {
1960 g_source_remove (priv->timer_expander);
1961 priv->timer_expander = 0;
1964 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1969 /* Do not allow drops between folders */
1971 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1972 pos == GTK_TREE_VIEW_DROP_AFTER) {
1973 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1974 gdk_drag_status(context, 0, time);
1975 valid_location = FALSE;
1978 valid_location = TRUE;
1981 /* Expand the selected row after 1/2 second */
1982 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1983 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1984 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1987 /* Select the desired action. By default we pick MOVE */
1988 suggested_action = GDK_ACTION_MOVE;
1990 if (context->actions == GDK_ACTION_COPY)
1991 gdk_drag_status(context, GDK_ACTION_COPY, time);
1992 else if (context->actions == GDK_ACTION_MOVE)
1993 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1994 else if (context->actions & suggested_action)
1995 gdk_drag_status(context, suggested_action, time);
1997 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2001 gtk_tree_path_free (dest_row);
2002 g_signal_stop_emission_by_name (widget, "drag-motion");
2003 return valid_location;
2007 /* Folder view drag types */
2008 const GtkTargetEntry folder_view_drag_types[] =
2010 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
2011 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
2015 * This function sets the treeview as a source and a target for dnd
2016 * events. It also connects all the requirede signals.
2019 setup_drag_and_drop (GtkTreeView *self)
2021 /* Set up the folder view as a dnd destination. Set only the
2022 highlight flag, otherwise gtk will have a different
2024 gtk_drag_dest_set (GTK_WIDGET (self),
2025 GTK_DEST_DEFAULT_HIGHLIGHT,
2026 folder_view_drag_types,
2027 G_N_ELEMENTS (folder_view_drag_types),
2028 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2030 g_signal_connect (G_OBJECT (self),
2031 "drag_data_received",
2032 G_CALLBACK (on_drag_data_received),
2036 /* Set up the treeview as a dnd source */
2037 gtk_drag_source_set (GTK_WIDGET (self),
2039 folder_view_drag_types,
2040 G_N_ELEMENTS (folder_view_drag_types),
2041 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2043 g_signal_connect (G_OBJECT (self),
2045 G_CALLBACK (on_drag_motion),
2048 g_signal_connect (G_OBJECT (self),
2050 G_CALLBACK (on_drag_data_get),
2053 g_signal_connect (G_OBJECT (self),
2055 G_CALLBACK (drag_drop_cb),
2060 * This function manages the navigation through the folders using the
2061 * keyboard or the hardware keys in the device
2064 on_key_pressed (GtkWidget *self,
2068 GtkTreeSelection *selection;
2070 GtkTreeModel *model;
2071 gboolean retval = FALSE;
2073 /* Up and Down are automatically managed by the treeview */
2074 if (event->keyval == GDK_Return) {
2075 /* Expand/Collapse the selected row */
2076 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2077 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2080 path = gtk_tree_model_get_path (model, &iter);
2082 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2083 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2085 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2086 gtk_tree_path_free (path);
2088 /* No further processing */
2096 * We listen to the changes in the local folder account name key,
2097 * because we want to show the right name in the view. The local
2098 * folder account name corresponds to the device name in the Maemo
2099 * version. We do this because we do not want to query gconf on each
2100 * tree view refresh. It's better to cache it and change whenever
2104 on_configuration_key_changed (ModestConf* conf,
2106 ModestConfEvent event,
2107 ModestConfNotificationId id,
2108 ModestFolderView *self)
2110 ModestFolderViewPrivate *priv;
2113 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2114 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2116 /* Do not listen for changes in other namespaces */
2117 if (priv->notification_id != id)
2120 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2121 g_free (priv->local_account_name);
2123 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2124 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2126 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2127 MODEST_CONF_DEVICE_NAME, NULL);
2129 /* Force a redraw */
2130 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2131 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2132 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2133 gtk_tree_view_column_queue_resize (tree_column);
2139 modest_folder_view_set_style (ModestFolderView *self,
2140 ModestFolderViewStyle style)
2142 ModestFolderViewPrivate *priv;
2144 g_return_if_fail (self);
2146 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2148 priv->style = style;
2152 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2153 const gchar *account_id)
2155 ModestFolderViewPrivate *priv;
2156 GtkTreeModel *model;
2158 g_return_if_fail (self);
2160 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2162 /* This will be used by the filter_row callback,
2163 * to decided which rows to show: */
2164 if (priv->visible_account_id) {
2165 g_free (priv->visible_account_id);
2166 priv->visible_account_id = NULL;
2169 priv->visible_account_id = g_strdup (account_id);
2172 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2173 if (GTK_IS_TREE_MODEL_FILTER (model))
2174 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2176 /* Save settings to gconf */
2177 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2178 MODEST_CONF_FOLDER_VIEW_KEY);
2182 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2184 ModestFolderViewPrivate *priv;
2186 g_return_val_if_fail (self, NULL);
2188 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2190 return (const gchar *) priv->visible_account_id;
2194 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2198 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2200 gtk_tree_model_get (model, iter,
2201 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2204 gboolean result = FALSE;
2205 if (type == TNY_FOLDER_TYPE_INBOX) {
2209 *inbox_iter = *iter;
2213 if (gtk_tree_model_iter_children (model, &child, iter)) {
2214 if (find_inbox_iter (model, &child, inbox_iter))
2218 } while (gtk_tree_model_iter_next (model, iter));
2227 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2229 GtkTreeModel *model;
2230 GtkTreeIter iter, inbox_iter;
2231 GtkTreeSelection *sel;
2232 GtkTreePath *path = NULL;
2234 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2238 expand_root_items (self);
2239 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2241 gtk_tree_model_get_iter_first (model, &iter);
2243 if (find_inbox_iter (model, &iter, &inbox_iter))
2244 path = gtk_tree_model_get_path (model, &inbox_iter);
2246 path = gtk_tree_path_new_first ();
2248 /* Select the row and free */
2249 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2250 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2251 gtk_tree_path_free (path);
2254 gtk_widget_grab_focus (GTK_WIDGET(self));
2260 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2265 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2266 TnyFolder* a_folder;
2269 gtk_tree_model_get (model, iter,
2270 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2271 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2272 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2276 if (folder == a_folder) {
2277 g_object_unref (a_folder);
2278 *folder_iter = *iter;
2281 g_object_unref (a_folder);
2283 if (gtk_tree_model_iter_children (model, &child, iter)) {
2284 if (find_folder_iter (model, &child, folder_iter, folder))
2288 } while (gtk_tree_model_iter_next (model, iter));
2295 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2296 ModestFolderView *self)
2298 ModestFolderViewPrivate *priv = NULL;
2299 GtkTreeSelection *sel;
2300 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2301 GObject *instance = NULL;
2303 if (!MODEST_IS_FOLDER_VIEW(self))
2306 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2308 priv->reexpand = TRUE;
2310 gtk_tree_model_get (tree_model, iter,
2311 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2312 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2314 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2315 priv->folder_to_select = g_object_ref (instance);
2317 g_object_unref (instance);
2320 if (priv->folder_to_select) {
2322 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2325 path = gtk_tree_model_get_path (tree_model, iter);
2326 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2328 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2330 gtk_tree_selection_select_iter (sel, iter);
2331 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2333 gtk_tree_path_free (path);
2338 modest_folder_view_disable_next_folder_selection (self);
2339 /* g_object_unref (priv->folder_to_select); */
2340 /* priv->folder_to_select = NULL; */
2346 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2348 ModestFolderViewPrivate *priv = NULL;
2350 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2351 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2353 if (priv->folder_to_select)
2354 g_object_unref(priv->folder_to_select);
2356 priv->folder_to_select = NULL;
2360 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2361 gboolean after_change)
2363 GtkTreeModel *model;
2364 GtkTreeIter iter, folder_iter;
2365 GtkTreeSelection *sel;
2366 ModestFolderViewPrivate *priv = NULL;
2368 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2369 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2371 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2375 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2376 gtk_tree_selection_unselect_all (sel);
2378 if (priv->folder_to_select)
2379 g_object_unref(priv->folder_to_select);
2380 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2384 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2389 gtk_tree_model_get_iter_first (model, &iter);
2390 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2393 path = gtk_tree_model_get_path (model, &folder_iter);
2394 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2396 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2397 gtk_tree_selection_select_iter (sel, &folder_iter);
2398 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2400 gtk_tree_path_free (path);
2408 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2410 /* Copy selection */
2411 _clipboard_set_selected_data (folder_view, FALSE);
2415 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2417 ModestFolderViewPrivate *priv = NULL;
2418 GtkTreeModel *model = NULL;
2419 const gchar **hidding = NULL;
2420 guint i, n_selected;
2422 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2423 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2425 /* Copy selection */
2426 if (!_clipboard_set_selected_data (folder_view, TRUE))
2429 /* Get hidding ids */
2430 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2432 /* Clear hidding array created by previous cut operation */
2433 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2435 /* Copy hidding array */
2436 priv->n_selected = n_selected;
2437 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2438 for (i=0; i < n_selected; i++)
2439 priv->hidding_ids[i] = g_strdup(hidding[i]);
2441 /* Hide cut folders */
2442 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2443 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2447 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2448 ModestFolderView *folder_view_dst)
2450 GtkTreeModel *filter_model = NULL;
2451 GtkTreeModel *model = NULL;
2452 GtkTreeModel *new_filter_model = NULL;
2454 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2455 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2458 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2459 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2461 /* Build new filter model */
2462 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2463 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2467 /* Set copied model */
2468 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2471 g_object_unref (new_filter_model);
2475 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2478 GtkTreeModel *model = NULL;
2479 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2480 priv->show_non_move = show;
2481 /* modest_folder_view_update_model(folder_view, */
2482 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2484 /* Hide special folders */
2485 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2486 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2487 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2491 /* Returns FALSE if it did not selected anything */
2493 _clipboard_set_selected_data (ModestFolderView *folder_view,
2496 ModestFolderViewPrivate *priv = NULL;
2497 TnyFolderStore *folder = NULL;
2498 gboolean retval = FALSE;
2500 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2501 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2503 /* Set selected data on clipboard */
2504 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2505 folder = modest_folder_view_get_selected (folder_view);
2507 /* Do not allow to select an account */
2508 if (TNY_IS_FOLDER (folder)) {
2509 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2514 g_object_unref (folder);
2520 _clear_hidding_filter (ModestFolderView *folder_view)
2522 ModestFolderViewPrivate *priv;
2525 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2526 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2528 if (priv->hidding_ids != NULL) {
2529 for (i=0; i < priv->n_selected; i++)
2530 g_free (priv->hidding_ids[i]);
2531 g_free(priv->hidding_ids);