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 (folder && priv->cur_folder_store == 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 xfer_cb (ModestMailOperation *mail_op,
1522 helper = (DndHelper *) user_data;
1524 if (modest_mail_operation_get_status (mail_op) ==
1525 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1531 /* Notify the drag source. Never call delete, the monitor will
1532 do the job if needed */
1533 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1535 /* Free the helper */
1536 gtk_tree_path_free (helper->source_row);
1537 g_slice_free (DndHelper, helper);
1540 /* get the folder for the row the treepath refers to. */
1541 /* folder must be unref'd */
1543 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1546 TnyFolder *folder = NULL;
1548 if (gtk_tree_model_get_iter (model,&iter, path))
1549 gtk_tree_model_get (model, &iter,
1550 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1556 show_banner_move_target_error ()
1558 ModestWindow *main_window;
1560 main_window = modest_window_mgr_get_main_window(
1561 modest_runtime_get_window_mgr());
1563 modest_platform_information_banner(GTK_WIDGET(main_window),
1564 NULL, _("mail_in_ui_folder_move_target_error"));
1568 * This function is used by drag_data_received_cb to manage drag and
1569 * drop of a header, i.e, and drag from the header view to the folder
1573 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1574 GtkTreeModel *dest_model,
1575 GtkTreePath *dest_row,
1578 TnyList *headers = NULL;
1579 TnyHeader *header = NULL;
1580 TnyFolder *folder = NULL;
1581 ModestMailOperation *mail_op = NULL;
1582 GtkTreeIter source_iter;
1583 ModestWindowMgr *mgr = NULL; /*no need for unref*/
1584 ModestWindow *main_win = NULL; /*no need for unref*/
1586 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1587 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1588 g_return_if_fail (dest_row);
1589 g_return_if_fail (helper);
1592 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1593 gtk_tree_model_get (source_model, &source_iter,
1594 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1596 if (!TNY_IS_HEADER(header)) {
1597 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1601 /* Check if the selected message is in msg-view. If it is than
1602 * do not enable drag&drop on that. */
1603 mgr = modest_runtime_get_window_mgr ();
1604 if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1608 folder = tree_path_to_folder (dest_model, dest_row);
1609 if (!TNY_IS_FOLDER(folder)) {
1610 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1611 show_banner_move_target_error();
1614 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1615 g_debug ("folder rules: cannot write to that folder");
1619 headers = tny_simple_list_new ();
1620 tny_list_append (headers, G_OBJECT (header));
1622 main_win = modest_window_mgr_get_main_window(mgr);
1623 if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1624 == GTK_RESPONSE_CANCEL)
1627 /* Transfer message */
1628 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1630 modest_ui_actions_move_folder_error_handler,
1632 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1635 modest_mail_operation_xfer_msgs (mail_op,
1638 helper->delete_source,
1643 if (G_IS_OBJECT(mail_op))
1644 g_object_unref (G_OBJECT (mail_op));
1645 if (G_IS_OBJECT(header))
1646 g_object_unref (G_OBJECT (header));
1647 if (G_IS_OBJECT(folder))
1648 g_object_unref (G_OBJECT (folder));
1649 if (G_IS_OBJECT(headers))
1650 g_object_unref (headers);
1654 * This function is used by drag_data_received_cb to manage drag and
1655 * drop of a folder, i.e, and drag from the folder view to the same
1659 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1660 GtkTreeModel *dest_model,
1661 GtkTreePath *dest_row,
1662 GtkSelectionData *selection_data,
1665 ModestMailOperation *mail_op = NULL;
1666 GtkTreeIter dest_iter, iter;
1667 TnyFolderStore *dest_folder = NULL;
1668 TnyFolder *folder = NULL;
1669 gboolean forbidden = FALSE;
1672 /* check the folder rules for the destination */
1673 folder = tree_path_to_folder (dest_model, dest_row);
1674 if (TNY_IS_FOLDER(folder)) {
1675 ModestTnyFolderRules rules =
1676 modest_tny_folder_get_rules (folder);
1677 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1678 } else if (TNY_IS_FOLDER_STORE(folder)) {
1679 /* enable local root as destination for folders */
1680 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1681 && TNY_IS_ACCOUNT (folder))
1684 g_object_unref (folder);
1687 /* check the folder rules for the source */
1688 folder = tree_path_to_folder (source_model, helper->source_row);
1689 if (TNY_IS_FOLDER(folder)) {
1690 ModestTnyFolderRules rules =
1691 modest_tny_folder_get_rules (folder);
1692 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1695 g_object_unref (folder);
1699 /* Check if the drag is possible */
1700 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1701 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1702 gtk_tree_path_free (helper->source_row);
1703 g_slice_free (DndHelper, helper);
1708 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1709 gtk_tree_model_get (dest_model, &dest_iter,
1710 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1712 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1713 gtk_tree_model_get (source_model, &iter,
1714 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1717 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1718 if (modest_platform_connect_and_wait_if_network_folderstore (NULL, dest_folder) &&
1719 modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1720 ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1722 /* Do the mail operation */
1724 modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1725 G_OBJECT (modest_window_mgr_get_main_window (mgr)),
1726 modest_ui_actions_move_folder_error_handler,
1729 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1732 modest_mail_operation_xfer_folder (mail_op,
1735 helper->delete_source,
1739 g_object_unref (G_OBJECT (mail_op));
1743 g_object_unref (G_OBJECT (dest_folder));
1744 g_object_unref (G_OBJECT (folder));
1748 * This function receives the data set by the "drag-data-get" signal
1749 * handler. This information comes within the #GtkSelectionData. This
1750 * function will manage both the drags of folders of the treeview and
1751 * drags of headers of the header view widget.
1754 on_drag_data_received (GtkWidget *widget,
1755 GdkDragContext *context,
1758 GtkSelectionData *selection_data,
1763 GtkWidget *source_widget;
1764 GtkTreeModel *dest_model, *source_model;
1765 GtkTreePath *source_row, *dest_row;
1766 GtkTreeViewDropPosition pos;
1767 gboolean success = FALSE, delete_source = FALSE;
1768 DndHelper *helper = NULL;
1770 /* Do not allow further process */
1771 g_signal_stop_emission_by_name (widget, "drag-data-received");
1772 source_widget = gtk_drag_get_source_widget (context);
1774 /* Get the action */
1775 if (context->action == GDK_ACTION_MOVE) {
1776 delete_source = TRUE;
1778 /* Notify that there is no folder selected. We need to
1779 do this in order to update the headers view (and
1780 its monitors, because when moving, the old folder
1781 won't longer exist. We can not wait for the end of
1782 the operation, because the operation won't start if
1783 the folder is in use */
1784 if (source_widget == widget) {
1785 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1786 gtk_tree_selection_unselect_all (sel);
1790 /* Check if the get_data failed */
1791 if (selection_data == NULL || selection_data->length < 0)
1792 gtk_drag_finish (context, success, FALSE, time);
1794 /* Get the models */
1795 gtk_tree_get_row_drag_data (selection_data,
1799 /* Select the destination model */
1800 if (source_widget == widget) {
1801 dest_model = source_model;
1803 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1806 /* Get the path to the destination row. Can not call
1807 gtk_tree_view_get_drag_dest_row() because the source row
1808 is not selected anymore */
1809 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1812 /* Only allow drops IN other rows */
1813 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1814 gtk_drag_finish (context, success, FALSE, time);
1816 /* Create the helper */
1817 helper = g_slice_new0 (DndHelper);
1818 helper->delete_source = delete_source;
1819 helper->source_row = gtk_tree_path_copy (source_row);
1820 helper->context = context;
1821 helper->time = time;
1823 /* Drags from the header view */
1824 if (source_widget != widget) {
1826 drag_and_drop_from_header_view (source_model,
1833 drag_and_drop_from_folder_view (source_model,
1841 gtk_tree_path_free (source_row);
1842 gtk_tree_path_free (dest_row);
1846 * We define a "drag-drop" signal handler because we do not want to
1847 * use the default one, because the default one always calls
1848 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1849 * signal handler, because there we have all the information available
1850 * to know if the dnd was a success or not.
1853 drag_drop_cb (GtkWidget *widget,
1854 GdkDragContext *context,
1862 if (!context->targets)
1865 /* Check if we're dragging a folder row */
1866 target = gtk_drag_dest_find_target (widget, context, NULL);
1868 /* Request the data from the source. */
1869 gtk_drag_get_data(widget, context, target, time);
1875 * This function expands a node of a tree view if it's not expanded
1876 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1877 * does that, so that's why they're here.
1880 expand_row_timeout (gpointer data)
1882 GtkTreeView *tree_view = data;
1883 GtkTreePath *dest_path = NULL;
1884 GtkTreeViewDropPosition pos;
1885 gboolean result = FALSE;
1887 GDK_THREADS_ENTER ();
1889 gtk_tree_view_get_drag_dest_row (tree_view,
1894 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1895 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1896 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1897 gtk_tree_path_free (dest_path);
1901 gtk_tree_path_free (dest_path);
1906 GDK_THREADS_LEAVE ();
1912 * This function is called whenever the pointer is moved over a widget
1913 * while dragging some data. It installs a timeout that will expand a
1914 * node of the treeview if not expanded yet. This function also calls
1915 * gdk_drag_status in order to set the suggested action that will be
1916 * used by the "drag-data-received" signal handler to know if we
1917 * should do a move or just a copy of the data.
1920 on_drag_motion (GtkWidget *widget,
1921 GdkDragContext *context,
1927 GtkTreeViewDropPosition pos;
1928 GtkTreePath *dest_row;
1929 ModestFolderViewPrivate *priv;
1930 GdkDragAction suggested_action;
1931 gboolean valid_location = FALSE;
1933 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1935 if (priv->timer_expander != 0) {
1936 g_source_remove (priv->timer_expander);
1937 priv->timer_expander = 0;
1940 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1945 /* Do not allow drops between folders */
1947 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1948 pos == GTK_TREE_VIEW_DROP_AFTER) {
1949 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1950 gdk_drag_status(context, 0, time);
1951 valid_location = FALSE;
1954 valid_location = TRUE;
1957 /* Expand the selected row after 1/2 second */
1958 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1959 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1960 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1963 /* Select the desired action. By default we pick MOVE */
1964 suggested_action = GDK_ACTION_MOVE;
1966 if (context->actions == GDK_ACTION_COPY)
1967 gdk_drag_status(context, GDK_ACTION_COPY, time);
1968 else if (context->actions == GDK_ACTION_MOVE)
1969 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1970 else if (context->actions & suggested_action)
1971 gdk_drag_status(context, suggested_action, time);
1973 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1977 gtk_tree_path_free (dest_row);
1978 g_signal_stop_emission_by_name (widget, "drag-motion");
1979 return valid_location;
1983 /* Folder view drag types */
1984 const GtkTargetEntry folder_view_drag_types[] =
1986 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1987 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1991 * This function sets the treeview as a source and a target for dnd
1992 * events. It also connects all the requirede signals.
1995 setup_drag_and_drop (GtkTreeView *self)
1997 /* Set up the folder view as a dnd destination. Set only the
1998 highlight flag, otherwise gtk will have a different
2000 gtk_drag_dest_set (GTK_WIDGET (self),
2001 GTK_DEST_DEFAULT_HIGHLIGHT,
2002 folder_view_drag_types,
2003 G_N_ELEMENTS (folder_view_drag_types),
2004 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2006 g_signal_connect (G_OBJECT (self),
2007 "drag_data_received",
2008 G_CALLBACK (on_drag_data_received),
2012 /* Set up the treeview as a dnd source */
2013 gtk_drag_source_set (GTK_WIDGET (self),
2015 folder_view_drag_types,
2016 G_N_ELEMENTS (folder_view_drag_types),
2017 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2019 g_signal_connect (G_OBJECT (self),
2021 G_CALLBACK (on_drag_motion),
2024 g_signal_connect (G_OBJECT (self),
2026 G_CALLBACK (on_drag_data_get),
2029 g_signal_connect (G_OBJECT (self),
2031 G_CALLBACK (drag_drop_cb),
2036 * This function manages the navigation through the folders using the
2037 * keyboard or the hardware keys in the device
2040 on_key_pressed (GtkWidget *self,
2044 GtkTreeSelection *selection;
2046 GtkTreeModel *model;
2047 gboolean retval = FALSE;
2049 /* Up and Down are automatically managed by the treeview */
2050 if (event->keyval == GDK_Return) {
2051 /* Expand/Collapse the selected row */
2052 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2053 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2056 path = gtk_tree_model_get_path (model, &iter);
2058 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2059 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2061 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2062 gtk_tree_path_free (path);
2064 /* No further processing */
2072 * We listen to the changes in the local folder account name key,
2073 * because we want to show the right name in the view. The local
2074 * folder account name corresponds to the device name in the Maemo
2075 * version. We do this because we do not want to query gconf on each
2076 * tree view refresh. It's better to cache it and change whenever
2080 on_configuration_key_changed (ModestConf* conf,
2082 ModestConfEvent event,
2083 ModestConfNotificationId id,
2084 ModestFolderView *self)
2086 ModestFolderViewPrivate *priv;
2089 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2090 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2092 /* Do not listen for changes in other namespaces */
2093 if (priv->notification_id != id)
2096 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2097 g_free (priv->local_account_name);
2099 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2100 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2102 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2103 MODEST_CONF_DEVICE_NAME, NULL);
2105 /* Force a redraw */
2106 #if GTK_CHECK_VERSION(2, 8, 0)
2107 GtkTreeViewColumn * tree_column;
2109 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2110 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2111 gtk_tree_view_column_queue_resize (tree_column);
2113 gtk_widget_queue_draw (GTK_WIDGET (self));
2119 modest_folder_view_set_style (ModestFolderView *self,
2120 ModestFolderViewStyle style)
2122 ModestFolderViewPrivate *priv;
2124 g_return_if_fail (self);
2126 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2128 priv->style = style;
2132 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2133 const gchar *account_id)
2135 ModestFolderViewPrivate *priv;
2136 GtkTreeModel *model;
2138 g_return_if_fail (self);
2140 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2142 /* This will be used by the filter_row callback,
2143 * to decided which rows to show: */
2144 if (priv->visible_account_id) {
2145 g_free (priv->visible_account_id);
2146 priv->visible_account_id = NULL;
2149 priv->visible_account_id = g_strdup (account_id);
2152 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2153 if (GTK_IS_TREE_MODEL_FILTER (model))
2154 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2156 /* Save settings to gconf */
2157 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2158 MODEST_CONF_FOLDER_VIEW_KEY);
2162 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2164 ModestFolderViewPrivate *priv;
2166 g_return_val_if_fail (self, NULL);
2168 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2170 return (const gchar *) priv->visible_account_id;
2174 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2178 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2180 gtk_tree_model_get (model, iter,
2181 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2184 gboolean result = FALSE;
2185 if (type == TNY_FOLDER_TYPE_INBOX) {
2189 *inbox_iter = *iter;
2193 if (gtk_tree_model_iter_children (model, &child, iter)) {
2194 if (find_inbox_iter (model, &child, inbox_iter))
2198 } while (gtk_tree_model_iter_next (model, iter));
2207 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2209 GtkTreeModel *model;
2210 GtkTreeIter iter, inbox_iter;
2211 GtkTreeSelection *sel;
2212 GtkTreePath *path = NULL;
2214 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2218 expand_root_items (self);
2219 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2221 gtk_tree_model_get_iter_first (model, &iter);
2223 if (find_inbox_iter (model, &iter, &inbox_iter))
2224 path = gtk_tree_model_get_path (model, &inbox_iter);
2226 path = gtk_tree_path_new_first ();
2228 /* Select the row and free */
2229 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2230 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2231 gtk_tree_path_free (path);
2234 gtk_widget_grab_focus (GTK_WIDGET(self));
2240 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2245 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2246 TnyFolder* a_folder;
2249 gtk_tree_model_get (model, iter,
2250 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2251 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2252 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2256 if (folder == a_folder) {
2257 g_object_unref (a_folder);
2258 *folder_iter = *iter;
2261 g_object_unref (a_folder);
2263 if (gtk_tree_model_iter_children (model, &child, iter)) {
2264 if (find_folder_iter (model, &child, folder_iter, folder))
2268 } while (gtk_tree_model_iter_next (model, iter));
2275 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2276 ModestFolderView *self)
2278 ModestFolderViewPrivate *priv = NULL;
2279 GtkTreeSelection *sel;
2280 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2281 GObject *instance = NULL;
2283 if (!MODEST_IS_FOLDER_VIEW(self))
2286 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2288 priv->reexpand = TRUE;
2290 gtk_tree_model_get (tree_model, iter,
2291 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2292 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2294 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2295 priv->folder_to_select = g_object_ref (instance);
2297 g_object_unref (instance);
2300 if (priv->folder_to_select) {
2302 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2305 path = gtk_tree_model_get_path (tree_model, iter);
2306 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2308 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2310 gtk_tree_selection_select_iter (sel, iter);
2311 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2313 gtk_tree_path_free (path);
2318 modest_folder_view_disable_next_folder_selection (self);
2319 /* g_object_unref (priv->folder_to_select); */
2320 /* priv->folder_to_select = NULL; */
2326 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2328 ModestFolderViewPrivate *priv = NULL;
2330 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2331 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2333 if (priv->folder_to_select)
2334 g_object_unref(priv->folder_to_select);
2336 priv->folder_to_select = NULL;
2340 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2341 gboolean after_change)
2343 GtkTreeModel *model;
2344 GtkTreeIter iter, folder_iter;
2345 GtkTreeSelection *sel;
2346 ModestFolderViewPrivate *priv = NULL;
2348 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2349 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2351 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2355 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2356 gtk_tree_selection_unselect_all (sel);
2358 if (priv->folder_to_select)
2359 g_object_unref(priv->folder_to_select);
2360 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2364 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2369 gtk_tree_model_get_iter_first (model, &iter);
2370 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2373 path = gtk_tree_model_get_path (model, &folder_iter);
2374 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2376 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2377 gtk_tree_selection_select_iter (sel, &folder_iter);
2378 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2380 gtk_tree_path_free (path);
2388 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2390 /* Copy selection */
2391 _clipboard_set_selected_data (folder_view, FALSE);
2395 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2397 ModestFolderViewPrivate *priv = NULL;
2398 GtkTreeModel *model = NULL;
2399 const gchar **hidding = NULL;
2400 guint i, n_selected;
2402 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2403 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2405 /* Copy selection */
2406 if (!_clipboard_set_selected_data (folder_view, TRUE))
2409 /* Get hidding ids */
2410 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2412 /* Clear hidding array created by previous cut operation */
2413 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2415 /* Copy hidding array */
2416 priv->n_selected = n_selected;
2417 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2418 for (i=0; i < n_selected; i++)
2419 priv->hidding_ids[i] = g_strdup(hidding[i]);
2421 /* Hide cut folders */
2422 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2423 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2427 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2428 ModestFolderView *folder_view_dst)
2430 GtkTreeModel *filter_model = NULL;
2431 GtkTreeModel *model = NULL;
2432 GtkTreeModel *new_filter_model = NULL;
2434 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2435 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2438 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2439 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2441 /* Build new filter model */
2442 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2443 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2447 /* Set copied model */
2448 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2451 g_object_unref (new_filter_model);
2455 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2458 GtkTreeModel *model = NULL;
2459 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2460 priv->show_non_move = show;
2461 /* modest_folder_view_update_model(folder_view, */
2462 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2464 /* Hide special folders */
2465 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2466 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2467 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2471 /* Returns FALSE if it did not selected anything */
2473 _clipboard_set_selected_data (ModestFolderView *folder_view,
2476 ModestFolderViewPrivate *priv = NULL;
2477 TnyFolderStore *folder = NULL;
2478 gboolean retval = FALSE;
2480 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2481 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2483 /* Set selected data on clipboard */
2484 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2485 folder = modest_folder_view_get_selected (folder_view);
2487 /* Do not allow to select an account */
2488 if (TNY_IS_FOLDER (folder)) {
2489 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2494 g_object_unref (folder);
2500 _clear_hidding_filter (ModestFolderView *folder_view)
2502 ModestFolderViewPrivate *priv;
2505 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2506 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2508 if (priv->hidding_ids != NULL) {
2509 for (i=0; i < priv->n_selected; i++)
2510 g_free (priv->hidding_ids[i]);
2511 g_free(priv->hidding_ids);