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 <modest-tny-account.h>
45 #include <modest-tny-folder.h>
46 #include <modest-tny-local-folders-account.h>
47 #include <modest-tny-outbox-account.h>
48 #include <modest-marshal.h>
49 #include <modest-icon-names.h>
50 #include <modest-tny-account-store.h>
51 #include <modest-text-utils.h>
52 #include <modest-runtime.h>
53 #include "modest-folder-view.h"
54 #include <modest-dnd.h>
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
59 /* 'private'/'protected' functions */
60 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
61 static void modest_folder_view_init (ModestFolderView *obj);
62 static void modest_folder_view_finalize (GObject *obj);
64 static void tny_account_store_view_init (gpointer g,
67 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
68 TnyAccountStore *account_store);
70 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
72 static void on_account_removed (TnyAccountStore *self,
76 static void on_account_inserted (TnyAccountStore *self,
80 static void on_account_changed (TnyAccountStore *self,
84 static gint cmp_rows (GtkTreeModel *tree_model,
89 static gboolean filter_row (GtkTreeModel *model,
93 static gboolean on_key_pressed (GtkWidget *self,
97 static void on_configuration_key_changed (ModestConf* conf,
99 ModestConfEvent event,
100 ModestConfNotificationId notification_id,
101 ModestFolderView *self);
104 static void on_drag_data_get (GtkWidget *widget,
105 GdkDragContext *context,
106 GtkSelectionData *selection_data,
111 static void on_drag_data_received (GtkWidget *widget,
112 GdkDragContext *context,
115 GtkSelectionData *selection_data,
120 static gboolean on_drag_motion (GtkWidget *widget,
121 GdkDragContext *context,
127 static void expand_root_items (ModestFolderView *self);
129 static gint expand_row_timeout (gpointer data);
131 static void setup_drag_and_drop (GtkTreeView *self);
133 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
136 static void _clear_hidding_filter (ModestFolderView *folder_view);
138 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
141 ModestFolderView *self);
144 FOLDER_SELECTION_CHANGED_SIGNAL,
145 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
149 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
150 struct _ModestFolderViewPrivate {
151 TnyAccountStore *account_store;
152 TnyFolderStore *cur_folder_store;
154 TnyFolder *folder_to_select; /* folder to select after the next update */
156 ModestConfNotificationId notification_id;
158 gulong changed_signal;
159 gulong account_inserted_signal;
160 gulong account_removed_signal;
161 gulong account_changed_signal;
162 gulong conf_key_signal;
164 /* not unref this object, its a singlenton */
165 ModestEmailClipboard *clipboard;
167 /* Filter tree model */
171 TnyFolderStoreQuery *query;
172 guint timer_expander;
174 gchar *local_account_name;
175 gchar *visible_account_id;
176 ModestFolderViewStyle style;
178 gboolean reselect; /* we use this to force a reselection of the INBOX */
179 gboolean show_non_move;
181 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
182 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
183 MODEST_TYPE_FOLDER_VIEW, \
184 ModestFolderViewPrivate))
186 static GObjectClass *parent_class = NULL;
188 static guint signals[LAST_SIGNAL] = {0};
191 modest_folder_view_get_type (void)
193 static GType my_type = 0;
195 static const GTypeInfo my_info = {
196 sizeof(ModestFolderViewClass),
197 NULL, /* base init */
198 NULL, /* base finalize */
199 (GClassInitFunc) modest_folder_view_class_init,
200 NULL, /* class finalize */
201 NULL, /* class data */
202 sizeof(ModestFolderView),
204 (GInstanceInitFunc) modest_folder_view_init,
208 static const GInterfaceInfo tny_account_store_view_info = {
209 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
210 NULL, /* interface_finalize */
211 NULL /* interface_data */
215 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
219 g_type_add_interface_static (my_type,
220 TNY_TYPE_ACCOUNT_STORE_VIEW,
221 &tny_account_store_view_info);
227 modest_folder_view_class_init (ModestFolderViewClass *klass)
229 GObjectClass *gobject_class;
230 gobject_class = (GObjectClass*) klass;
232 parent_class = g_type_class_peek_parent (klass);
233 gobject_class->finalize = modest_folder_view_finalize;
235 g_type_class_add_private (gobject_class,
236 sizeof(ModestFolderViewPrivate));
238 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
239 g_signal_new ("folder_selection_changed",
240 G_TYPE_FROM_CLASS (gobject_class),
242 G_STRUCT_OFFSET (ModestFolderViewClass,
243 folder_selection_changed),
245 modest_marshal_VOID__POINTER_BOOLEAN,
246 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
249 * This signal is emitted whenever the currently selected
250 * folder display name is computed. Note that the name could
251 * be different to the folder name, because we could append
252 * the unread messages count to the folder name to build the
253 * folder display name
255 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
256 g_signal_new ("folder-display-name-changed",
257 G_TYPE_FROM_CLASS (gobject_class),
259 G_STRUCT_OFFSET (ModestFolderViewClass,
260 folder_display_name_changed),
262 g_cclosure_marshal_VOID__STRING,
263 G_TYPE_NONE, 1, G_TYPE_STRING);
266 /* Simplify checks for NULLs: */
268 strings_are_equal (const gchar *a, const gchar *b)
274 return (strcmp (a, b) == 0);
281 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
283 GObject *instance = NULL;
285 gtk_tree_model_get (model, iter,
286 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
290 return FALSE; /* keep walking */
292 if (!TNY_IS_ACCOUNT (instance)) {
293 g_object_unref (instance);
294 return FALSE; /* keep walking */
297 /* Check if this is the looked-for account: */
298 TnyAccount *this_account = TNY_ACCOUNT (instance);
299 TnyAccount *account = TNY_ACCOUNT (data);
301 const gchar *this_account_id = tny_account_get_id(this_account);
302 const gchar *account_id = tny_account_get_id(account);
303 g_object_unref (instance);
306 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
307 if (strings_are_equal(this_account_id, account_id)) {
308 /* Tell the model that the data has changed, so that
309 * it calls the cell_data_func callbacks again: */
310 /* TODO: This does not seem to actually cause the new string to be shown: */
311 gtk_tree_model_row_changed (model, path, iter);
313 return TRUE; /* stop walking */
316 return FALSE; /* keep walking */
321 ModestFolderView *self;
322 gchar *previous_name;
323 } GetMmcAccountNameData;
326 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
328 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
330 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
332 if (!strings_are_equal (
333 tny_account_get_name(TNY_ACCOUNT(account)),
334 data->previous_name)) {
336 /* Tell the model that the data has changed, so that
337 * it calls the cell_data_func callbacks again: */
338 ModestFolderView *self = data->self;
339 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
341 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
344 g_free (data->previous_name);
345 g_slice_free (GetMmcAccountNameData, data);
349 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
350 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
352 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_ALL_COLUMN, &all,
367 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
368 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
369 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
371 rendobj = G_OBJECT(renderer);
381 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
382 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
384 gchar *item_name = NULL;
385 gint item_weight = 400;
387 if (type != TNY_FOLDER_TYPE_ROOT) {
390 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
391 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
392 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
393 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
395 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
399 /* Select the number to show: the unread or unsent messages */
400 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
405 /* Use bold font style if there are unread or unset messages */
407 item_name = g_strdup_printf ("%s (%d)", fname, number);
410 item_name = g_strdup (fname);
414 } else if (TNY_IS_ACCOUNT (instance)) {
415 /* If it's a server account */
416 if (modest_tny_account_is_virtual_local_folders (
417 TNY_ACCOUNT (instance))) {
418 item_name = g_strdup (priv->local_account_name);
420 } else if (modest_tny_account_is_memory_card_account (
421 TNY_ACCOUNT (instance))) {
422 /* fname is only correct when the items are first
423 * added to the model, not when the account is
424 * changed later, so get the name from the account
426 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
429 item_name = g_strdup (fname);
435 item_name = g_strdup ("unknown");
437 if (item_name && item_weight) {
438 /* Set the name in the treeview cell: */
439 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
441 /* Notify display name observers */
442 /* TODO: What listens for this signal, and how can it use only the new name? */
443 if (G_OBJECT (priv->cur_folder_store) == instance) {
444 g_signal_emit (G_OBJECT(self),
445 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
452 /* If it is a Memory card account, make sure that we have the correct name.
453 * This function will be trigerred again when the name has been retrieved: */
454 if (TNY_IS_STORE_ACCOUNT (instance) &&
455 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
457 /* Get the account name asynchronously: */
458 GetMmcAccountNameData *callback_data =
459 g_slice_new0(GetMmcAccountNameData);
460 callback_data->self = self;
462 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
464 callback_data->previous_name = g_strdup (name);
466 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
467 on_get_mmc_account_name, callback_data);
470 g_object_unref (G_OBJECT (instance));
475 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
476 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
478 GObject *rendobj = NULL, *instance = NULL;
479 GdkPixbuf *pixbuf = NULL;
480 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
481 const gchar *account_id = NULL;
482 gboolean has_children;
484 rendobj = G_OBJECT(renderer);
485 gtk_tree_model_get (tree_model, iter,
486 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
487 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
489 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
494 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
495 /* We include the MERGE type here because it's used to create
496 the local OUTBOX folder */
497 if (type == TNY_FOLDER_TYPE_NORMAL ||
498 type == TNY_FOLDER_TYPE_UNKNOWN) {
499 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
503 case TNY_FOLDER_TYPE_ROOT:
504 if (TNY_IS_ACCOUNT (instance)) {
506 if (modest_tny_account_is_virtual_local_folders (
507 TNY_ACCOUNT (instance))) {
508 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
511 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
513 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
514 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
516 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
520 case TNY_FOLDER_TYPE_INBOX:
521 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
523 case TNY_FOLDER_TYPE_OUTBOX:
524 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
526 case TNY_FOLDER_TYPE_JUNK:
527 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
529 case TNY_FOLDER_TYPE_SENT:
530 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
532 case TNY_FOLDER_TYPE_TRASH:
533 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
535 case TNY_FOLDER_TYPE_DRAFTS:
536 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
538 case TNY_FOLDER_TYPE_NORMAL:
540 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
544 g_object_unref (G_OBJECT (instance));
547 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
548 if (has_children && (pixbuf != NULL)) {
549 GdkPixbuf *open_pixbuf, *closed_pixbuf;
550 GdkPixbuf *open_emblem, *closed_emblem;
551 open_pixbuf = gdk_pixbuf_copy (pixbuf);
552 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
553 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
554 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
557 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
558 MIN (gdk_pixbuf_get_width (open_emblem),
559 gdk_pixbuf_get_width (open_pixbuf)),
560 MIN (gdk_pixbuf_get_height (open_emblem),
561 gdk_pixbuf_get_height (open_pixbuf)),
562 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
563 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
564 g_object_unref (open_emblem);
567 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
568 MIN (gdk_pixbuf_get_width (closed_emblem),
569 gdk_pixbuf_get_width (closed_pixbuf)),
570 MIN (gdk_pixbuf_get_height (closed_emblem),
571 gdk_pixbuf_get_height (closed_pixbuf)),
572 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
573 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
574 g_object_unref (closed_emblem);
577 g_object_unref (closed_pixbuf);
579 g_object_unref (open_pixbuf);
583 g_object_unref (pixbuf);
587 add_columns (GtkWidget *treeview)
589 GtkTreeViewColumn *column;
590 GtkCellRenderer *renderer;
591 GtkTreeSelection *sel;
594 column = gtk_tree_view_column_new ();
596 /* Set icon and text render function */
597 renderer = gtk_cell_renderer_pixbuf_new();
598 gtk_tree_view_column_pack_start (column, renderer, FALSE);
599 gtk_tree_view_column_set_cell_data_func(column, renderer,
600 icon_cell_data, treeview, NULL);
602 renderer = gtk_cell_renderer_text_new();
603 gtk_tree_view_column_pack_start (column, renderer, FALSE);
604 gtk_tree_view_column_set_cell_data_func(column, renderer,
605 text_cell_data, treeview, NULL);
607 /* Set selection mode */
608 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
609 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
611 /* Set treeview appearance */
612 gtk_tree_view_column_set_spacing (column, 2);
613 gtk_tree_view_column_set_resizable (column, TRUE);
614 gtk_tree_view_column_set_fixed_width (column, TRUE);
615 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
616 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
619 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
623 modest_folder_view_init (ModestFolderView *obj)
625 ModestFolderViewPrivate *priv;
628 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
630 priv->timer_expander = 0;
631 priv->account_store = NULL;
633 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
634 priv->cur_folder_store = NULL;
635 priv->visible_account_id = NULL;
636 priv->folder_to_select = NULL;
638 /* Initialize the local account name */
639 conf = modest_runtime_get_conf();
640 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
642 /* Init email clipboard */
643 priv->clipboard = modest_runtime_get_email_clipboard ();
644 priv->hidding_ids = NULL;
645 priv->n_selected = 0;
646 priv->reselect = FALSE;
647 priv->show_non_move = TRUE;
650 add_columns (GTK_WIDGET (obj));
652 /* Setup drag and drop */
653 setup_drag_and_drop (GTK_TREE_VIEW(obj));
655 /* Connect signals */
656 g_signal_connect (G_OBJECT (obj),
658 G_CALLBACK (on_key_pressed), NULL);
661 * Track changes in the local account name (in the device it
662 * will be the device name)
664 priv->notification_id = modest_conf_listen_to_namespace (conf,
665 MODEST_CONF_NAMESPACE);
666 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
668 G_CALLBACK(on_configuration_key_changed),
673 tny_account_store_view_init (gpointer g, gpointer iface_data)
675 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
677 klass->set_account_store_func = modest_folder_view_set_account_store;
683 modest_folder_view_finalize (GObject *obj)
685 ModestFolderViewPrivate *priv;
686 GtkTreeSelection *sel;
688 g_return_if_fail (obj);
690 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
692 if (priv->notification_id) {
693 modest_conf_forget_namespace (modest_runtime_get_conf (),
694 MODEST_CONF_NAMESPACE,
695 priv->notification_id);
698 if (priv->timer_expander != 0) {
699 g_source_remove (priv->timer_expander);
700 priv->timer_expander = 0;
703 if (priv->account_store) {
704 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
705 priv->account_inserted_signal);
706 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
707 priv->account_removed_signal);
708 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
709 priv->account_changed_signal);
710 g_object_unref (G_OBJECT(priv->account_store));
711 priv->account_store = NULL;
715 g_object_unref (G_OBJECT (priv->query));
719 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
720 if (priv->folder_to_select) {
721 g_object_unref (G_OBJECT(priv->folder_to_select));
722 priv->folder_to_select = NULL;
725 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
727 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
729 g_free (priv->local_account_name);
730 g_free (priv->visible_account_id);
732 if (priv->conf_key_signal) {
733 g_signal_handler_disconnect (modest_runtime_get_conf (),
734 priv->conf_key_signal);
735 priv->conf_key_signal = 0;
738 if (priv->cur_folder_store) {
739 if (TNY_IS_FOLDER(priv->cur_folder_store))
740 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
741 /* FALSE --> expunge the message */
743 g_object_unref (priv->cur_folder_store);
744 priv->cur_folder_store = NULL;
747 /* Clear hidding array created by cut operation */
748 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
750 G_OBJECT_CLASS(parent_class)->finalize (obj);
755 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
757 ModestFolderViewPrivate *priv;
760 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
761 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
763 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
764 device = tny_account_store_get_device (account_store);
766 if (G_UNLIKELY (priv->account_store)) {
768 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
769 priv->account_inserted_signal))
770 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
771 priv->account_inserted_signal);
772 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
773 priv->account_removed_signal))
774 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
775 priv->account_removed_signal);
776 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
777 priv->account_changed_signal))
778 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
779 priv->account_changed_signal);
780 g_object_unref (G_OBJECT (priv->account_store));
783 priv->account_store = g_object_ref (G_OBJECT (account_store));
785 priv->account_removed_signal =
786 g_signal_connect (G_OBJECT(account_store), "account_removed",
787 G_CALLBACK (on_account_removed), self);
789 priv->account_inserted_signal =
790 g_signal_connect (G_OBJECT(account_store), "account_inserted",
791 G_CALLBACK (on_account_inserted), self);
793 priv->account_changed_signal =
794 g_signal_connect (G_OBJECT(account_store), "account_changed",
795 G_CALLBACK (on_account_changed), self);
797 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
799 g_object_unref (G_OBJECT (device));
803 on_account_inserted (TnyAccountStore *account_store,
807 ModestFolderViewPrivate *priv;
808 GtkTreeModel *sort_model, *filter_model;
810 /* Ignore transport account insertions, we're not showing them
811 in the folder view */
812 if (TNY_IS_TRANSPORT_ACCOUNT (account))
815 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
817 /* If we're adding a new account, and there is no previous
818 one, we need to select the visible server account */
819 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
820 !priv->visible_account_id)
821 modest_widget_memory_restore (modest_runtime_get_conf(),
822 G_OBJECT (user_data),
823 MODEST_CONF_FOLDER_VIEW_KEY);
825 /* Get the inner model */
826 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
827 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
829 /* Insert the account in the model */
830 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
836 on_account_changed (TnyAccountStore *account_store, TnyAccount *tny_account,
845 on_account_removed (TnyAccountStore *account_store,
849 ModestFolderView *self = NULL;
850 ModestFolderViewPrivate *priv;
851 GtkTreeModel *sort_model, *filter_model;
852 GtkTreeSelection *sel = NULL;
854 /* Ignore transport account removals, we're not showing them
855 in the folder view */
856 if (TNY_IS_TRANSPORT_ACCOUNT (account))
859 self = MODEST_FOLDER_VIEW (user_data);
860 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
862 /* Invalidate the cur_folder_store only if the selected folder
863 belongs to the account that is being removed */
864 if (priv->cur_folder_store) {
865 TnyAccount *selected_folder_account = NULL;
867 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
868 selected_folder_account =
869 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
871 selected_folder_account =
872 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
875 if (selected_folder_account == account) {
876 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
877 gtk_tree_selection_unselect_all (sel);
879 g_object_unref (selected_folder_account);
882 /* Invalidate row to select only if the folder to select
883 belongs to the account that is being removed*/
884 if (priv->folder_to_select) {
885 TnyAccount *folder_to_select_account = NULL;
887 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
888 if (folder_to_select_account == account) {
889 /* modest_folder_view_disable_next_folder_selection (self); */
890 g_object_unref (priv->folder_to_select);
891 priv->folder_to_select = NULL;
893 g_object_unref (folder_to_select_account);
896 /* Remove the account from the model */
897 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
898 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
899 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
902 /* If the removed account is the currently viewed one then
903 clear the configuration value. The new visible account will be the default account */
904 if (priv->visible_account_id &&
905 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
907 /* Clear the current visible account_id */
908 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
910 /* Call the restore method, this will set the new visible account */
911 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
912 MODEST_CONF_FOLDER_VIEW_KEY);
915 /* Select the INBOX */
916 modest_folder_view_select_first_inbox_or_local (self);
920 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
922 GtkTreeViewColumn *col;
924 g_return_if_fail (self);
926 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
928 g_printerr ("modest: failed get column for title\n");
932 gtk_tree_view_column_set_title (col, title);
933 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
938 modest_folder_view_on_map (ModestFolderView *self,
939 GdkEventExpose *event,
942 ModestFolderViewPrivate *priv;
944 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
946 /* This won't happen often */
947 if (G_UNLIKELY (priv->reselect)) {
948 /* Select the first inbox or the local account if not found */
950 /* TODO: this could cause a lock at startup, so we
951 comment it for the moment. We know that this will
952 be a bug, because the INBOX is not selected, but we
953 need to rewrite some parts of Modest to avoid the
954 deathlock situation */
955 /* TODO: check if this is still the case */
956 priv->reselect = FALSE;
957 modest_folder_view_select_first_inbox_or_local (self);
958 /* Notify the display name observers */
959 g_signal_emit (G_OBJECT(self),
960 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
964 expand_root_items (self);
970 modest_folder_view_new (TnyFolderStoreQuery *query)
973 ModestFolderViewPrivate *priv;
974 GtkTreeSelection *sel;
976 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
977 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
980 priv->query = g_object_ref (query);
982 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
983 priv->changed_signal = g_signal_connect (sel, "changed",
984 G_CALLBACK (on_selection_changed), self);
986 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
988 return GTK_WIDGET(self);
991 /* this feels dirty; any other way to expand all the root items? */
993 expand_root_items (ModestFolderView *self)
996 path = gtk_tree_path_new_first ();
998 /* all folders should have child items, so.. */
999 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
1000 gtk_tree_path_next (path);
1002 gtk_tree_path_free (path);
1006 * We use this function to implement the
1007 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1008 * account in this case, and the local folders.
1011 filter_row (GtkTreeModel *model,
1015 ModestFolderViewPrivate *priv;
1016 gboolean retval = TRUE;
1017 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1018 GObject *instance = NULL;
1019 const gchar *id = NULL;
1021 gboolean found = FALSE;
1022 gboolean cleared = FALSE;
1024 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1025 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1027 gtk_tree_model_get (model, iter,
1028 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1029 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1032 /* Do not show if there is no instance, this could indeed
1033 happen when the model is being modified while it's being
1034 drawn. This could occur for example when moving folders
1039 if (type == TNY_FOLDER_TYPE_ROOT) {
1040 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1041 account instead of a folder. */
1042 if (TNY_IS_ACCOUNT (instance)) {
1043 TnyAccount *acc = TNY_ACCOUNT (instance);
1044 const gchar *account_id = tny_account_get_id (acc);
1046 /* If it isn't a special folder,
1047 * don't show it unless it is the visible account: */
1048 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1049 !modest_tny_account_is_virtual_local_folders (acc) &&
1050 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1052 /* Show only the visible account id */
1053 if (priv->visible_account_id) {
1054 if (strcmp (account_id, priv->visible_account_id))
1061 /* Never show these to the user. They are merged into one folder
1062 * in the local-folders account instead: */
1063 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1068 /* Check hiding (if necessary) */
1069 cleared = modest_email_clipboard_cleared (priv->clipboard);
1070 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1071 id = tny_folder_get_id (TNY_FOLDER(instance));
1072 if (priv->hidding_ids != NULL)
1073 for (i=0; i < priv->n_selected && !found; i++)
1074 if (priv->hidding_ids[i] != NULL && id != NULL)
1075 found = (!strcmp (priv->hidding_ids[i], id));
1081 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1082 folder as no message can be move there according to UI specs */
1083 if (!priv->show_non_move)
1087 case TNY_FOLDER_TYPE_OUTBOX:
1088 case TNY_FOLDER_TYPE_SENT:
1089 case TNY_FOLDER_TYPE_DRAFTS:
1092 case TNY_FOLDER_TYPE_UNKNOWN:
1093 case TNY_FOLDER_TYPE_NORMAL:
1094 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1095 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1096 || type == TNY_FOLDER_TYPE_DRAFTS)
1107 g_object_unref (instance);
1114 modest_folder_view_update_model (ModestFolderView *self,
1115 TnyAccountStore *account_store)
1117 ModestFolderViewPrivate *priv;
1118 GtkTreeModel *model /* , *old_model */;
1119 /* TnyAccount *local_account; */
1120 TnyList *model_as_list;
1122 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1123 g_return_val_if_fail (account_store, FALSE);
1125 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1127 /* Notify that there is no folder selected */
1128 g_signal_emit (G_OBJECT(self),
1129 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1131 if (priv->cur_folder_store) {
1132 g_object_unref (priv->cur_folder_store);
1133 priv->cur_folder_store = NULL;
1136 /* FIXME: the local accounts are not shown when the query
1137 selects only the subscribed folders. */
1138 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1139 model = tny_gtk_folder_store_tree_model_new (NULL);
1141 /* Deal with the model via its TnyList Interface,
1142 * filling the TnyList via a get_accounts() call: */
1143 model_as_list = TNY_LIST(model);
1145 /* Get the accounts: */
1146 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1148 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1149 g_object_unref (model_as_list);
1150 model_as_list = NULL;
1152 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1154 sortable = gtk_tree_model_sort_new_with_model (model);
1155 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1156 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1157 GTK_SORT_ASCENDING);
1158 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1159 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1160 cmp_rows, NULL, NULL);
1162 /* Create filter model */
1163 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1164 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1170 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1171 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1172 (GCallback) on_row_inserted_maybe_select_folder, self);
1175 g_object_unref (model);
1176 g_object_unref (filter_model);
1177 g_object_unref (sortable);
1179 /* Force a reselection of the INBOX next time the widget is shown */
1180 priv->reselect = TRUE;
1187 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1189 GtkTreeModel *model = NULL;
1190 TnyFolderStore *folder = NULL;
1192 ModestFolderView *tree_view = NULL;
1193 ModestFolderViewPrivate *priv = NULL;
1194 gboolean selected = FALSE;
1196 g_return_if_fail (sel);
1197 g_return_if_fail (user_data);
1199 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1201 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1202 /* if(!gtk_tree_selection_get_selected (sel, &model, &iter)) */
1205 /* Notify the display name observers */
1206 g_signal_emit (G_OBJECT(user_data),
1207 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1210 tree_view = MODEST_FOLDER_VIEW (user_data);
1213 gtk_tree_model_get (model, &iter,
1214 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1217 /* If the folder is the same do not notify */
1218 if (priv->cur_folder_store == folder && folder) {
1219 g_object_unref (folder);
1224 /* Current folder was unselected */
1225 if (priv->cur_folder_store) {
1226 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1227 priv->cur_folder_store, FALSE);
1229 if (TNY_IS_FOLDER(priv->cur_folder_store))
1230 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1231 FALSE, NULL, NULL, NULL);
1233 /* FALSE --> don't expunge the messages */
1235 g_object_unref (priv->cur_folder_store);
1236 priv->cur_folder_store = NULL;
1239 /* New current references */
1240 priv->cur_folder_store = folder;
1242 /* New folder has been selected */
1243 g_signal_emit (G_OBJECT(tree_view),
1244 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1245 0, priv->cur_folder_store, TRUE);
1249 modest_folder_view_get_selected (ModestFolderView *self)
1251 ModestFolderViewPrivate *priv;
1253 g_return_val_if_fail (self, NULL);
1255 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1256 if (priv->cur_folder_store)
1257 g_object_ref (priv->cur_folder_store);
1259 return priv->cur_folder_store;
1263 get_cmp_rows_type_pos (GObject *folder)
1265 /* Remote accounts -> Local account -> MMC account .*/
1268 if (TNY_IS_ACCOUNT (folder) &&
1269 modest_tny_account_is_virtual_local_folders (
1270 TNY_ACCOUNT (folder))) {
1272 } else if (TNY_IS_ACCOUNT (folder)) {
1273 TnyAccount *account = TNY_ACCOUNT (folder);
1274 const gchar *account_id = tny_account_get_id (account);
1275 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1281 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1282 return -1; /* Should never happen */
1287 get_cmp_subfolder_type_pos (TnyFolderType t)
1289 /* Inbox, Outbox, Drafts, Sent, User */
1293 case TNY_FOLDER_TYPE_INBOX:
1296 case TNY_FOLDER_TYPE_OUTBOX:
1299 case TNY_FOLDER_TYPE_DRAFTS:
1302 case TNY_FOLDER_TYPE_SENT:
1311 * This function orders the mail accounts according to these rules:
1312 * 1st - remote accounts
1313 * 2nd - local account
1317 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1321 gchar *name1 = NULL;
1322 gchar *name2 = NULL;
1323 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1324 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1325 GObject *folder1 = NULL;
1326 GObject *folder2 = NULL;
1328 gtk_tree_model_get (tree_model, iter1,
1329 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1330 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1331 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1333 gtk_tree_model_get (tree_model, iter2,
1334 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1335 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1336 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1339 /* Return if we get no folder. This could happen when folder
1340 operations are happening. The model is updated after the
1341 folder copy/move actually occurs, so there could be
1342 situations where the model to be drawn is not correct */
1343 if (!folder1 || !folder2)
1346 if (type == TNY_FOLDER_TYPE_ROOT) {
1347 /* Compare the types, so that
1348 * Remote accounts -> Local account -> MMC account .*/
1349 const gint pos1 = get_cmp_rows_type_pos (folder1);
1350 const gint pos2 = get_cmp_rows_type_pos (folder2);
1351 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1352 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1355 else if (pos1 > pos2)
1358 /* Compare items of the same type: */
1360 TnyAccount *account1 = NULL;
1361 if (TNY_IS_ACCOUNT (folder1))
1362 account1 = TNY_ACCOUNT (folder1);
1364 TnyAccount *account2 = NULL;
1365 if (TNY_IS_ACCOUNT (folder2))
1366 account2 = TNY_ACCOUNT (folder2);
1368 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1369 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1371 if (!account_id && !account_id2) {
1373 } else if (!account_id) {
1375 } else if (!account_id2) {
1377 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1380 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1384 gint cmp1 = 0, cmp2 = 0;
1385 /* get the parent to know if it's a local folder */
1388 gboolean has_parent;
1389 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1391 GObject *parent_folder;
1392 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1393 gtk_tree_model_get (tree_model, &parent,
1394 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1395 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1397 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1398 TNY_IS_ACCOUNT (parent_folder) &&
1399 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1400 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1401 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1403 g_object_unref (parent_folder);
1406 /* if they are not local folders */
1408 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1409 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1413 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1415 cmp = (cmp1 - cmp2);
1420 g_object_unref(G_OBJECT(folder1));
1422 g_object_unref(G_OBJECT(folder2));
1430 /*****************************************************************************/
1431 /* DRAG and DROP stuff */
1432 /*****************************************************************************/
1435 * This function fills the #GtkSelectionData with the row and the
1436 * model that has been dragged. It's called when this widget is a
1437 * source for dnd after the event drop happened
1440 on_drag_data_get (GtkWidget *widget,
1441 GdkDragContext *context,
1442 GtkSelectionData *selection_data,
1447 GtkTreeSelection *selection;
1448 GtkTreeModel *model;
1450 GtkTreePath *source_row;
1452 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1453 gtk_tree_selection_get_selected (selection, &model, &iter);
1454 source_row = gtk_tree_model_get_path (model, &iter);
1456 gtk_tree_set_row_drag_data (selection_data,
1460 gtk_tree_path_free (source_row);
1463 typedef struct _DndHelper {
1464 gboolean delete_source;
1465 GtkTreePath *source_row;
1466 GdkDragContext *context;
1472 * This function is the callback of the
1473 * modest_mail_operation_xfer_msgs () and
1474 * modest_mail_operation_xfer_folder() calls. We check here if the
1475 * message/folder was correctly asynchronously transferred. The reason
1476 * to use the same callback is that the code is the same, it only has
1477 * to check that the operation went fine and then finalize the drag
1481 on_progress_changed (ModestMailOperation *mail_op,
1482 ModestMailOperationState *state,
1488 helper = (DndHelper *) user_data;
1490 if (!state->finished)
1493 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1499 /* Notify the drag source. Never call delete, the monitor will
1500 do the job if needed */
1501 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1503 /* Free the helper */
1504 gtk_tree_path_free (helper->source_row);
1505 g_slice_free (DndHelper, helper);
1509 /* get the folder for the row the treepath refers to. */
1510 /* folder must be unref'd */
1512 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1515 TnyFolder *folder = NULL;
1517 if (gtk_tree_model_get_iter (model,&iter, path))
1518 gtk_tree_model_get (model, &iter,
1519 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1525 show_banner_move_target_error ()
1527 ModestWindow *main_window;
1529 main_window = modest_window_mgr_get_main_window(
1530 modest_runtime_get_window_mgr());
1532 modest_platform_information_banner(GTK_WIDGET(main_window),
1533 NULL, _("mail_in_ui_folder_move_target_error"));
1537 * This function is used by drag_data_received_cb to manage drag and
1538 * drop of a header, i.e, and drag from the header view to the folder
1542 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1543 GtkTreeModel *dest_model,
1544 GtkTreePath *dest_row,
1547 TnyList *headers = NULL;
1548 TnyHeader *header = NULL;
1549 TnyFolder *folder = NULL;
1550 ModestMailOperation *mail_op = NULL;
1551 GtkTreeIter source_iter;
1552 ModestWindowMgr *mgr = NULL; /*no need for unref*/
1553 ModestWindow *main_win = NULL; /*no need for unref*/
1555 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1556 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1557 g_return_if_fail (dest_row);
1558 g_return_if_fail (helper);
1561 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1562 gtk_tree_model_get (source_model, &source_iter,
1563 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1565 if (!TNY_IS_HEADER(header)) {
1566 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1570 /* Check if the selected message is in msg-view. If it is than
1571 * do not enable drag&drop on that. */
1572 mgr = modest_runtime_get_window_mgr ();
1573 if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1577 folder = tree_path_to_folder (dest_model, dest_row);
1578 if (!TNY_IS_FOLDER(folder)) {
1579 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1580 show_banner_move_target_error();
1583 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1584 g_debug ("folder rules: cannot write to that folder");
1588 headers = tny_simple_list_new ();
1589 tny_list_append (headers, G_OBJECT (header));
1591 main_win = modest_window_mgr_get_main_window(mgr);
1592 if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1593 == GTK_RESPONSE_CANCEL)
1596 /* Transfer message */
1597 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1599 modest_ui_actions_move_folder_error_handler,
1601 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1603 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1604 G_CALLBACK (on_progress_changed), helper);
1606 modest_mail_operation_xfer_msgs (mail_op,
1609 helper->delete_source,
1614 if (G_IS_OBJECT(mail_op))
1615 g_object_unref (G_OBJECT (mail_op));
1616 if (G_IS_OBJECT(header))
1617 g_object_unref (G_OBJECT (header));
1618 if (G_IS_OBJECT(folder))
1619 g_object_unref (G_OBJECT (folder));
1620 if (G_IS_OBJECT(headers))
1621 g_object_unref (headers);
1625 * This function is used by drag_data_received_cb to manage drag and
1626 * drop of a folder, i.e, and drag from the folder view to the same
1630 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1631 GtkTreeModel *dest_model,
1632 GtkTreePath *dest_row,
1633 GtkSelectionData *selection_data,
1636 ModestMailOperation *mail_op = NULL;
1637 GtkTreeIter dest_iter, iter;
1638 TnyFolderStore *dest_folder = NULL;
1639 TnyFolder *folder = NULL;
1640 gboolean forbidden = FALSE;
1643 /* check the folder rules for the destination */
1644 folder = tree_path_to_folder (dest_model, dest_row);
1645 if (TNY_IS_FOLDER(folder)) {
1646 ModestTnyFolderRules rules =
1647 modest_tny_folder_get_rules (folder);
1648 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1651 g_debug ("folder rules: cannot write to that folder");
1652 } else if (TNY_IS_FOLDER_STORE(folder)){
1653 /* enable local root as destination for folders */
1654 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1655 && TNY_IS_ACCOUNT (folder))
1658 g_object_unref (folder);
1661 /* check the folder rules for the source */
1662 folder = tree_path_to_folder (source_model, helper->source_row);
1663 if (TNY_IS_FOLDER(folder)) {
1664 ModestTnyFolderRules rules =
1665 modest_tny_folder_get_rules (folder);
1666 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1668 g_debug ("folder rules: cannot move that folder");
1671 g_object_unref (folder);
1675 /* Check if the drag is possible */
1676 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1677 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1678 gtk_tree_path_free (helper->source_row);
1679 g_slice_free (DndHelper, helper);
1684 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1685 gtk_tree_model_get (dest_model, &dest_iter,
1686 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1688 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1689 gtk_tree_model_get (source_model, &iter,
1690 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1693 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1694 if (modest_platform_connect_and_wait_if_network_folderstore (
1695 NULL, dest_folder) &&
1696 modest_platform_connect_and_wait_if_network_folderstore (
1697 NULL, TNY_FOLDER_STORE (folder))) {
1698 /* Do the mail operation */
1699 mail_op = modest_mail_operation_new_with_error_handling (
1700 MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1702 modest_ui_actions_move_folder_error_handler,
1704 modest_mail_operation_queue_add (
1705 modest_runtime_get_mail_operation_queue (),
1710 G_CALLBACK (on_progress_changed),
1713 modest_mail_operation_xfer_folder (mail_op,
1716 helper->delete_source,
1720 g_object_unref (G_OBJECT (mail_op));
1724 g_object_unref (G_OBJECT (dest_folder));
1725 g_object_unref (G_OBJECT (folder));
1729 * This function receives the data set by the "drag-data-get" signal
1730 * handler. This information comes within the #GtkSelectionData. This
1731 * function will manage both the drags of folders of the treeview and
1732 * drags of headers of the header view widget.
1735 on_drag_data_received (GtkWidget *widget,
1736 GdkDragContext *context,
1739 GtkSelectionData *selection_data,
1744 GtkWidget *source_widget;
1745 GtkTreeModel *dest_model, *source_model;
1746 GtkTreePath *source_row, *dest_row;
1747 GtkTreeViewDropPosition pos;
1748 gboolean success = FALSE, delete_source = FALSE;
1749 DndHelper *helper = NULL;
1751 /* Do not allow further process */
1752 g_signal_stop_emission_by_name (widget, "drag-data-received");
1753 source_widget = gtk_drag_get_source_widget (context);
1755 /* Get the action */
1756 if (context->action == GDK_ACTION_MOVE) {
1757 delete_source = TRUE;
1759 /* Notify that there is no folder selected. We need to
1760 do this in order to update the headers view (and
1761 its monitors, because when moving, the old folder
1762 won't longer exist. We can not wait for the end of
1763 the operation, because the operation won't start if
1764 the folder is in use */
1765 if (source_widget == widget) {
1766 ModestFolderViewPrivate *priv;
1768 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1769 if (priv->cur_folder_store) {
1770 g_object_unref (priv->cur_folder_store);
1771 priv->cur_folder_store = NULL;
1774 g_signal_emit (G_OBJECT (widget),
1775 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1779 /* Check if the get_data failed */
1780 if (selection_data == NULL || selection_data->length < 0)
1781 gtk_drag_finish (context, success, FALSE, time);
1783 /* Get the models */
1784 gtk_tree_get_row_drag_data (selection_data,
1788 /* Select the destination model */
1789 if (source_widget == widget) {
1790 dest_model = source_model;
1792 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1795 /* Get the path to the destination row. Can not call
1796 gtk_tree_view_get_drag_dest_row() because the source row
1797 is not selected anymore */
1798 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1801 /* Only allow drops IN other rows */
1802 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1803 gtk_drag_finish (context, success, FALSE, time);
1805 /* Create the helper */
1806 helper = g_slice_new0 (DndHelper);
1807 helper->delete_source = delete_source;
1808 helper->source_row = gtk_tree_path_copy (source_row);
1809 helper->context = context;
1810 helper->time = time;
1812 /* Drags from the header view */
1813 if (source_widget != widget) {
1815 drag_and_drop_from_header_view (source_model,
1822 drag_and_drop_from_folder_view (source_model,
1830 gtk_tree_path_free (source_row);
1831 gtk_tree_path_free (dest_row);
1835 * We define a "drag-drop" signal handler because we do not want to
1836 * use the default one, because the default one always calls
1837 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1838 * signal handler, because there we have all the information available
1839 * to know if the dnd was a success or not.
1842 drag_drop_cb (GtkWidget *widget,
1843 GdkDragContext *context,
1851 if (!context->targets)
1854 /* Check if we're dragging a folder row */
1855 target = gtk_drag_dest_find_target (widget, context, NULL);
1857 /* Request the data from the source. */
1858 gtk_drag_get_data(widget, context, target, time);
1864 * This function expands a node of a tree view if it's not expanded
1865 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1866 * does that, so that's why they're here.
1869 expand_row_timeout (gpointer data)
1871 GtkTreeView *tree_view = data;
1872 GtkTreePath *dest_path = NULL;
1873 GtkTreeViewDropPosition pos;
1874 gboolean result = FALSE;
1876 GDK_THREADS_ENTER ();
1878 gtk_tree_view_get_drag_dest_row (tree_view,
1883 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1884 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1885 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1886 gtk_tree_path_free (dest_path);
1890 gtk_tree_path_free (dest_path);
1895 GDK_THREADS_LEAVE ();
1901 * This function is called whenever the pointer is moved over a widget
1902 * while dragging some data. It installs a timeout that will expand a
1903 * node of the treeview if not expanded yet. This function also calls
1904 * gdk_drag_status in order to set the suggested action that will be
1905 * used by the "drag-data-received" signal handler to know if we
1906 * should do a move or just a copy of the data.
1909 on_drag_motion (GtkWidget *widget,
1910 GdkDragContext *context,
1916 GtkTreeViewDropPosition pos;
1917 GtkTreePath *dest_row;
1918 ModestFolderViewPrivate *priv;
1919 GdkDragAction suggested_action;
1920 gboolean valid_location = FALSE;
1922 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1924 if (priv->timer_expander != 0) {
1925 g_source_remove (priv->timer_expander);
1926 priv->timer_expander = 0;
1929 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1934 /* Do not allow drops between folders */
1936 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1937 pos == GTK_TREE_VIEW_DROP_AFTER) {
1938 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1939 gdk_drag_status(context, 0, time);
1940 valid_location = FALSE;
1943 valid_location = TRUE;
1946 /* Expand the selected row after 1/2 second */
1947 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1948 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1949 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1952 /* Select the desired action. By default we pick MOVE */
1953 suggested_action = GDK_ACTION_MOVE;
1955 if (context->actions == GDK_ACTION_COPY)
1956 gdk_drag_status(context, GDK_ACTION_COPY, time);
1957 else if (context->actions == GDK_ACTION_MOVE)
1958 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1959 else if (context->actions & suggested_action)
1960 gdk_drag_status(context, suggested_action, time);
1962 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1966 gtk_tree_path_free (dest_row);
1967 g_signal_stop_emission_by_name (widget, "drag-motion");
1968 return valid_location;
1972 /* Folder view drag types */
1973 const GtkTargetEntry folder_view_drag_types[] =
1975 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1976 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1980 * This function sets the treeview as a source and a target for dnd
1981 * events. It also connects all the requirede signals.
1984 setup_drag_and_drop (GtkTreeView *self)
1986 /* Set up the folder view as a dnd destination. Set only the
1987 highlight flag, otherwise gtk will have a different
1989 gtk_drag_dest_set (GTK_WIDGET (self),
1990 GTK_DEST_DEFAULT_HIGHLIGHT,
1991 folder_view_drag_types,
1992 G_N_ELEMENTS (folder_view_drag_types),
1993 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1995 g_signal_connect (G_OBJECT (self),
1996 "drag_data_received",
1997 G_CALLBACK (on_drag_data_received),
2001 /* Set up the treeview as a dnd source */
2002 gtk_drag_source_set (GTK_WIDGET (self),
2004 folder_view_drag_types,
2005 G_N_ELEMENTS (folder_view_drag_types),
2006 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2008 g_signal_connect (G_OBJECT (self),
2010 G_CALLBACK (on_drag_motion),
2013 g_signal_connect (G_OBJECT (self),
2015 G_CALLBACK (on_drag_data_get),
2018 g_signal_connect (G_OBJECT (self),
2020 G_CALLBACK (drag_drop_cb),
2025 * This function manages the navigation through the folders using the
2026 * keyboard or the hardware keys in the device
2029 on_key_pressed (GtkWidget *self,
2033 GtkTreeSelection *selection;
2035 GtkTreeModel *model;
2036 gboolean retval = FALSE;
2038 /* Up and Down are automatically managed by the treeview */
2039 if (event->keyval == GDK_Return) {
2040 /* Expand/Collapse the selected row */
2041 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2042 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2045 path = gtk_tree_model_get_path (model, &iter);
2047 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2048 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2050 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2051 gtk_tree_path_free (path);
2053 /* No further processing */
2061 * We listen to the changes in the local folder account name key,
2062 * because we want to show the right name in the view. The local
2063 * folder account name corresponds to the device name in the Maemo
2064 * version. We do this because we do not want to query gconf on each
2065 * tree view refresh. It's better to cache it and change whenever
2069 on_configuration_key_changed (ModestConf* conf,
2071 ModestConfEvent event,
2072 ModestConfNotificationId id,
2073 ModestFolderView *self)
2075 ModestFolderViewPrivate *priv;
2078 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2079 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2081 /* Do not listen for changes in other namespaces */
2082 if (priv->notification_id != id)
2085 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2086 g_free (priv->local_account_name);
2088 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2089 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2091 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2092 MODEST_CONF_DEVICE_NAME, NULL);
2094 /* Force a redraw */
2095 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2096 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2097 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2098 gtk_tree_view_column_queue_resize (tree_column);
2104 modest_folder_view_set_style (ModestFolderView *self,
2105 ModestFolderViewStyle style)
2107 ModestFolderViewPrivate *priv;
2109 g_return_if_fail (self);
2111 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2113 priv->style = style;
2117 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2118 const gchar *account_id)
2120 ModestFolderViewPrivate *priv;
2121 GtkTreeModel *model;
2123 g_return_if_fail (self);
2125 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2127 /* This will be used by the filter_row callback,
2128 * to decided which rows to show: */
2129 if (priv->visible_account_id) {
2130 g_free (priv->visible_account_id);
2131 priv->visible_account_id = NULL;
2134 priv->visible_account_id = g_strdup (account_id);
2137 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2138 if (GTK_IS_TREE_MODEL_FILTER (model))
2139 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2141 /* Save settings to gconf */
2142 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2143 MODEST_CONF_FOLDER_VIEW_KEY);
2147 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2149 ModestFolderViewPrivate *priv;
2151 g_return_val_if_fail (self, NULL);
2153 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2155 return (const gchar *) priv->visible_account_id;
2159 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2163 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2165 gtk_tree_model_get (model, iter,
2166 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2169 gboolean result = FALSE;
2170 if (type == TNY_FOLDER_TYPE_INBOX) {
2174 *inbox_iter = *iter;
2178 if (gtk_tree_model_iter_children (model, &child, iter)) {
2179 if (find_inbox_iter (model, &child, inbox_iter))
2183 } while (gtk_tree_model_iter_next (model, iter));
2192 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2194 GtkTreeModel *model;
2195 GtkTreeIter iter, inbox_iter;
2196 GtkTreeSelection *sel;
2197 GtkTreePath *path = NULL;
2199 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2203 expand_root_items (self);
2204 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2206 gtk_tree_model_get_iter_first (model, &iter);
2208 if (find_inbox_iter (model, &iter, &inbox_iter))
2209 path = gtk_tree_model_get_path (model, &inbox_iter);
2211 path = gtk_tree_path_new_first ();
2213 /* Select the row and free */
2214 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2215 gtk_tree_path_free (path);
2218 gtk_widget_grab_focus (GTK_WIDGET(self));
2224 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2229 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2230 TnyFolder* a_folder;
2233 gtk_tree_model_get (model, iter,
2234 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2235 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2236 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2240 if (folder == a_folder) {
2241 g_object_unref (a_folder);
2242 *folder_iter = *iter;
2245 g_object_unref (a_folder);
2247 if (gtk_tree_model_iter_children (model, &child, iter)) {
2248 if (find_folder_iter (model, &child, folder_iter, folder))
2252 } while (gtk_tree_model_iter_next (model, iter));
2259 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2260 ModestFolderView *self)
2262 ModestFolderViewPrivate *priv = NULL;
2263 GtkTreeSelection *sel;
2265 if (!MODEST_IS_FOLDER_VIEW(self))
2268 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2270 if (priv->folder_to_select) {
2272 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2275 path = gtk_tree_model_get_path (tree_model, iter);
2276 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2278 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2280 gtk_tree_selection_select_iter (sel, iter);
2281 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2283 gtk_tree_path_free (path);
2288 modest_folder_view_disable_next_folder_selection (self);
2289 /* g_object_unref (priv->folder_to_select); */
2290 /* priv->folder_to_select = NULL; */
2296 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2298 ModestFolderViewPrivate *priv = NULL;
2300 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2301 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2303 if (priv->folder_to_select)
2304 g_object_unref(priv->folder_to_select);
2306 priv->folder_to_select = NULL;
2310 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2311 gboolean after_change)
2313 GtkTreeModel *model;
2314 GtkTreeIter iter, folder_iter;
2315 GtkTreeSelection *sel;
2316 ModestFolderViewPrivate *priv = NULL;
2318 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2319 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2321 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2325 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2326 gtk_tree_selection_unselect_all (sel);
2328 if (priv->folder_to_select)
2329 g_object_unref(priv->folder_to_select);
2330 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2334 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2339 gtk_tree_model_get_iter_first (model, &iter);
2340 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2343 path = gtk_tree_model_get_path (model, &folder_iter);
2344 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2346 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2347 gtk_tree_selection_select_iter (sel, &folder_iter);
2348 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2350 gtk_tree_path_free (path);
2358 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2360 /* Copy selection */
2361 _clipboard_set_selected_data (folder_view, FALSE);
2365 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2367 ModestFolderViewPrivate *priv = NULL;
2368 GtkTreeModel *model = NULL;
2369 const gchar **hidding = NULL;
2370 guint i, n_selected;
2372 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2373 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2375 /* Copy selection */
2376 if (!_clipboard_set_selected_data (folder_view, TRUE))
2379 /* Get hidding ids */
2380 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2382 /* Clear hidding array created by previous cut operation */
2383 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2385 /* Copy hidding array */
2386 priv->n_selected = n_selected;
2387 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2388 for (i=0; i < n_selected; i++)
2389 priv->hidding_ids[i] = g_strdup(hidding[i]);
2391 /* Hide cut folders */
2392 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2393 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2397 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2398 ModestFolderView *folder_view_dst)
2400 GtkTreeModel *filter_model = NULL;
2401 GtkTreeModel *model = NULL;
2402 GtkTreeModel *new_filter_model = NULL;
2404 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2405 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2408 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2409 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2411 /* Build new filter model */
2412 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2413 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2417 /* Set copied model */
2418 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2421 g_object_unref (new_filter_model);
2425 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2428 GtkTreeModel *model = NULL;
2429 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2430 priv->show_non_move = show;
2431 /* modest_folder_view_update_model(folder_view, */
2432 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2434 /* Hide special folders */
2435 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2436 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2437 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2441 /* Returns FALSE if it did not selected anything */
2443 _clipboard_set_selected_data (ModestFolderView *folder_view,
2446 ModestFolderViewPrivate *priv = NULL;
2447 TnyFolderStore *folder = NULL;
2448 gboolean retval = FALSE;
2450 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2451 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2453 /* Set selected data on clipboard */
2454 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2455 folder = modest_folder_view_get_selected (folder_view);
2457 /* Do not allow to select an account */
2458 if (TNY_IS_FOLDER (folder)) {
2459 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2464 g_object_unref (folder);
2470 _clear_hidding_filter (ModestFolderView *folder_view)
2472 ModestFolderViewPrivate *priv;
2475 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2476 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2478 if (priv->hidding_ids != NULL) {
2479 for (i=0; i < priv->n_selected; i++)
2480 g_free (priv->hidding_ids[i]);
2481 g_free(priv->hidding_ids);