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);
270 ModestFolderView *self;
271 gchar *previous_name;
272 } GetMmcAccountNameData;
276 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
277 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
279 ModestFolderViewPrivate *priv;
284 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
285 GObject *instance = NULL;
287 g_return_if_fail (column);
288 g_return_if_fail (tree_model);
289 g_return_if_fail (iter != NULL);
291 gtk_tree_model_get (tree_model, iter,
292 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
293 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
294 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
295 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
296 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
298 rendobj = G_OBJECT(renderer);
308 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
309 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
311 gchar *item_name = NULL;
312 gint item_weight = 400;
314 if (type != TNY_FOLDER_TYPE_ROOT) {
317 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
318 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
319 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
320 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
322 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
326 /* Select the number to show: the unread or unsent messages */
327 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
332 /* Use bold font style if there are unread or unset messages */
334 item_name = g_strdup_printf ("%s (%d)", fname, number);
337 item_name = g_strdup (fname);
341 } else if (TNY_IS_ACCOUNT (instance)) {
342 /* If it's a server account */
343 if (modest_tny_account_is_virtual_local_folders (
344 TNY_ACCOUNT (instance))) {
345 item_name = g_strdup (priv->local_account_name);
347 } else if (modest_tny_account_is_memory_card_account (
348 TNY_ACCOUNT (instance))) {
349 /* fname is only correct when the items are first
350 * added to the model, not when the account is
351 * changed later, so get the name from the account
353 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
356 item_name = g_strdup (fname);
362 item_name = g_strdup ("unknown");
364 if (item_name && item_weight) {
365 /* Set the name in the treeview cell: */
366 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
368 /* Notify display name observers */
369 /* TODO: What listens for this signal, and how can it use only the new name? */
370 if (G_OBJECT (priv->cur_folder_store) == instance) {
371 g_signal_emit (G_OBJECT(self),
372 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
379 g_object_unref (G_OBJECT (instance));
384 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
385 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
387 GObject *rendobj = NULL, *instance = NULL;
388 GdkPixbuf *pixbuf = NULL;
389 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
390 const gchar *account_id = NULL;
391 gboolean has_children;
393 rendobj = G_OBJECT(renderer);
394 gtk_tree_model_get (tree_model, iter,
395 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
396 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
398 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
403 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
404 /* We include the MERGE type here because it's used to create
405 the local OUTBOX folder */
406 if (type == TNY_FOLDER_TYPE_NORMAL ||
407 type == TNY_FOLDER_TYPE_UNKNOWN) {
408 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
412 case TNY_FOLDER_TYPE_ROOT:
413 if (TNY_IS_ACCOUNT (instance)) {
415 if (modest_tny_account_is_virtual_local_folders (
416 TNY_ACCOUNT (instance))) {
417 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
420 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
422 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
423 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
425 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
429 case TNY_FOLDER_TYPE_INBOX:
430 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
432 case TNY_FOLDER_TYPE_OUTBOX:
433 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
435 case TNY_FOLDER_TYPE_JUNK:
436 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
438 case TNY_FOLDER_TYPE_SENT:
439 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
441 case TNY_FOLDER_TYPE_TRASH:
442 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
444 case TNY_FOLDER_TYPE_DRAFTS:
445 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
447 case TNY_FOLDER_TYPE_NORMAL:
449 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
453 g_object_unref (G_OBJECT (instance));
456 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
457 if (has_children && (pixbuf != NULL)) {
458 GdkPixbuf *open_pixbuf, *closed_pixbuf;
459 GdkPixbuf *open_emblem, *closed_emblem;
460 open_pixbuf = gdk_pixbuf_copy (pixbuf);
461 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
462 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
463 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
466 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
467 MIN (gdk_pixbuf_get_width (open_emblem),
468 gdk_pixbuf_get_width (open_pixbuf)),
469 MIN (gdk_pixbuf_get_height (open_emblem),
470 gdk_pixbuf_get_height (open_pixbuf)),
471 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
472 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
473 g_object_unref (open_emblem);
476 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
477 MIN (gdk_pixbuf_get_width (closed_emblem),
478 gdk_pixbuf_get_width (closed_pixbuf)),
479 MIN (gdk_pixbuf_get_height (closed_emblem),
480 gdk_pixbuf_get_height (closed_pixbuf)),
481 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
482 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
483 g_object_unref (closed_emblem);
486 g_object_unref (closed_pixbuf);
488 g_object_unref (open_pixbuf);
492 g_object_unref (pixbuf);
496 add_columns (GtkWidget *treeview)
498 GtkTreeViewColumn *column;
499 GtkCellRenderer *renderer;
500 GtkTreeSelection *sel;
503 column = gtk_tree_view_column_new ();
505 /* Set icon and text render function */
506 renderer = gtk_cell_renderer_pixbuf_new();
507 gtk_tree_view_column_pack_start (column, renderer, FALSE);
508 gtk_tree_view_column_set_cell_data_func(column, renderer,
509 icon_cell_data, treeview, NULL);
511 renderer = gtk_cell_renderer_text_new();
512 gtk_tree_view_column_pack_start (column, renderer, FALSE);
513 gtk_tree_view_column_set_cell_data_func(column, renderer,
514 text_cell_data, treeview, NULL);
516 /* Set selection mode */
517 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
518 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
520 /* Set treeview appearance */
521 gtk_tree_view_column_set_spacing (column, 2);
522 gtk_tree_view_column_set_resizable (column, TRUE);
523 gtk_tree_view_column_set_fixed_width (column, TRUE);
524 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
525 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
528 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
532 modest_folder_view_init (ModestFolderView *obj)
534 ModestFolderViewPrivate *priv;
537 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
539 priv->timer_expander = 0;
540 priv->account_store = NULL;
542 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
543 priv->cur_folder_store = NULL;
544 priv->visible_account_id = NULL;
545 priv->folder_to_select = NULL;
547 /* Initialize the local account name */
548 conf = modest_runtime_get_conf();
549 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
551 /* Init email clipboard */
552 priv->clipboard = modest_runtime_get_email_clipboard ();
553 priv->hidding_ids = NULL;
554 priv->n_selected = 0;
555 priv->reselect = FALSE;
556 priv->show_non_move = TRUE;
559 add_columns (GTK_WIDGET (obj));
561 /* Setup drag and drop */
562 setup_drag_and_drop (GTK_TREE_VIEW(obj));
564 /* Connect signals */
565 g_signal_connect (G_OBJECT (obj),
567 G_CALLBACK (on_key_pressed), NULL);
570 * Track changes in the local account name (in the device it
571 * will be the device name)
573 priv->notification_id = modest_conf_listen_to_namespace (conf,
574 MODEST_CONF_NAMESPACE);
575 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
577 G_CALLBACK(on_configuration_key_changed),
582 tny_account_store_view_init (gpointer g, gpointer iface_data)
584 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
586 klass->set_account_store_func = modest_folder_view_set_account_store;
592 modest_folder_view_finalize (GObject *obj)
594 ModestFolderViewPrivate *priv;
595 GtkTreeSelection *sel;
597 g_return_if_fail (obj);
599 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
601 if (priv->notification_id) {
602 modest_conf_forget_namespace (modest_runtime_get_conf (),
603 MODEST_CONF_NAMESPACE,
604 priv->notification_id);
607 if (priv->timer_expander != 0) {
608 g_source_remove (priv->timer_expander);
609 priv->timer_expander = 0;
612 if (priv->account_store) {
613 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
614 priv->account_inserted_signal);
615 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
616 priv->account_removed_signal);
617 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
618 priv->account_changed_signal);
619 g_object_unref (G_OBJECT(priv->account_store));
620 priv->account_store = NULL;
624 g_object_unref (G_OBJECT (priv->query));
628 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
629 if (priv->folder_to_select) {
630 g_object_unref (G_OBJECT(priv->folder_to_select));
631 priv->folder_to_select = NULL;
634 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
636 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
638 g_free (priv->local_account_name);
639 g_free (priv->visible_account_id);
641 if (priv->conf_key_signal) {
642 g_signal_handler_disconnect (modest_runtime_get_conf (),
643 priv->conf_key_signal);
644 priv->conf_key_signal = 0;
647 if (priv->cur_folder_store) {
648 if (TNY_IS_FOLDER(priv->cur_folder_store))
649 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
650 /* FALSE --> expunge the message */
652 g_object_unref (priv->cur_folder_store);
653 priv->cur_folder_store = NULL;
656 /* Clear hidding array created by cut operation */
657 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
659 G_OBJECT_CLASS(parent_class)->finalize (obj);
664 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
666 ModestFolderViewPrivate *priv;
669 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
670 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
672 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
673 device = tny_account_store_get_device (account_store);
675 if (G_UNLIKELY (priv->account_store)) {
677 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
678 priv->account_inserted_signal))
679 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
680 priv->account_inserted_signal);
681 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
682 priv->account_removed_signal))
683 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
684 priv->account_removed_signal);
685 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
686 priv->account_changed_signal))
687 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
688 priv->account_changed_signal);
689 g_object_unref (G_OBJECT (priv->account_store));
692 priv->account_store = g_object_ref (G_OBJECT (account_store));
694 priv->account_removed_signal =
695 g_signal_connect (G_OBJECT(account_store), "account_removed",
696 G_CALLBACK (on_account_removed), self);
698 priv->account_inserted_signal =
699 g_signal_connect (G_OBJECT(account_store), "account_inserted",
700 G_CALLBACK (on_account_inserted), self);
702 priv->account_changed_signal =
703 g_signal_connect (G_OBJECT(account_store), "account_changed",
704 G_CALLBACK (on_account_changed), self);
706 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
708 g_object_unref (G_OBJECT (device));
712 on_account_inserted (TnyAccountStore *account_store,
716 ModestFolderViewPrivate *priv;
717 GtkTreeModel *sort_model, *filter_model;
719 /* Ignore transport account insertions, we're not showing them
720 in the folder view */
721 if (TNY_IS_TRANSPORT_ACCOUNT (account))
724 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
726 /* If we're adding a new account, and there is no previous
727 one, we need to select the visible server account */
728 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
729 !priv->visible_account_id)
730 modest_widget_memory_restore (modest_runtime_get_conf(),
731 G_OBJECT (user_data),
732 MODEST_CONF_FOLDER_VIEW_KEY);
734 /* Get the inner model */
735 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
736 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
738 /* Insert the account in the model */
739 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
745 on_account_changed (TnyAccountStore *account_store,
746 TnyAccount *tny_account,
750 ModestFolderViewPrivate *priv;
751 GtkTreeModel *sort_model, *filter_model;
753 /* Ignore transport account insertions, we're not showing them
754 in the folder view */
755 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
758 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
760 /* Get the inner model */
761 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
762 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
764 /* Remove the account from the model */
765 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
766 G_OBJECT (tny_account));
768 /* Insert the account in the model */
769 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
770 G_OBJECT (tny_account));
776 on_account_removed (TnyAccountStore *account_store,
780 ModestFolderView *self = NULL;
781 ModestFolderViewPrivate *priv;
782 GtkTreeModel *sort_model, *filter_model;
783 GtkTreeSelection *sel = NULL;
785 /* Ignore transport account removals, we're not showing them
786 in the folder view */
787 if (TNY_IS_TRANSPORT_ACCOUNT (account))
790 self = MODEST_FOLDER_VIEW (user_data);
791 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
793 /* Invalidate the cur_folder_store only if the selected folder
794 belongs to the account that is being removed */
795 if (priv->cur_folder_store) {
796 TnyAccount *selected_folder_account = NULL;
798 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
799 selected_folder_account =
800 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
802 selected_folder_account =
803 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
806 if (selected_folder_account == account) {
807 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
808 gtk_tree_selection_unselect_all (sel);
810 g_object_unref (selected_folder_account);
813 /* Invalidate row to select only if the folder to select
814 belongs to the account that is being removed*/
815 if (priv->folder_to_select) {
816 TnyAccount *folder_to_select_account = NULL;
818 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
819 if (folder_to_select_account == account) {
820 /* modest_folder_view_disable_next_folder_selection (self); */
821 g_object_unref (priv->folder_to_select);
822 priv->folder_to_select = NULL;
824 g_object_unref (folder_to_select_account);
827 /* Remove the account from the model */
828 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
829 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
830 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
833 /* If the removed account is the currently viewed one then
834 clear the configuration value. The new visible account will be the default account */
835 if (priv->visible_account_id &&
836 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
838 /* Clear the current visible account_id */
839 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
841 /* Call the restore method, this will set the new visible account */
842 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
843 MODEST_CONF_FOLDER_VIEW_KEY);
846 /* Select the INBOX */
847 modest_folder_view_select_first_inbox_or_local (self);
851 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
853 GtkTreeViewColumn *col;
855 g_return_if_fail (self);
857 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
859 g_printerr ("modest: failed get column for title\n");
863 gtk_tree_view_column_set_title (col, title);
864 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
869 modest_folder_view_on_map (ModestFolderView *self,
870 GdkEventExpose *event,
873 ModestFolderViewPrivate *priv;
875 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
877 /* This won't happen often */
878 if (G_UNLIKELY (priv->reselect)) {
879 /* Select the first inbox or the local account if not found */
881 /* TODO: this could cause a lock at startup, so we
882 comment it for the moment. We know that this will
883 be a bug, because the INBOX is not selected, but we
884 need to rewrite some parts of Modest to avoid the
885 deathlock situation */
886 /* TODO: check if this is still the case */
887 priv->reselect = FALSE;
888 modest_folder_view_select_first_inbox_or_local (self);
889 /* Notify the display name observers */
890 g_signal_emit (G_OBJECT(self),
891 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
895 expand_root_items (self);
901 modest_folder_view_new (TnyFolderStoreQuery *query)
904 ModestFolderViewPrivate *priv;
905 GtkTreeSelection *sel;
907 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
908 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
911 priv->query = g_object_ref (query);
913 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
914 priv->changed_signal = g_signal_connect (sel, "changed",
915 G_CALLBACK (on_selection_changed), self);
917 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
919 return GTK_WIDGET(self);
922 /* this feels dirty; any other way to expand all the root items? */
924 expand_root_items (ModestFolderView *self)
927 path = gtk_tree_path_new_first ();
929 /* all folders should have child items, so.. */
930 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
931 gtk_tree_path_next (path);
933 gtk_tree_path_free (path);
937 * We use this function to implement the
938 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
939 * account in this case, and the local folders.
942 filter_row (GtkTreeModel *model,
946 ModestFolderViewPrivate *priv;
947 gboolean retval = TRUE;
948 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
949 GObject *instance = NULL;
950 const gchar *id = NULL;
952 gboolean found = FALSE;
953 gboolean cleared = FALSE;
955 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
956 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
958 gtk_tree_model_get (model, iter,
959 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
960 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
963 /* Do not show if there is no instance, this could indeed
964 happen when the model is being modified while it's being
965 drawn. This could occur for example when moving folders
970 if (type == TNY_FOLDER_TYPE_ROOT) {
971 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
972 account instead of a folder. */
973 if (TNY_IS_ACCOUNT (instance)) {
974 TnyAccount *acc = TNY_ACCOUNT (instance);
975 const gchar *account_id = tny_account_get_id (acc);
977 /* If it isn't a special folder,
978 * don't show it unless it is the visible account: */
979 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
980 !modest_tny_account_is_virtual_local_folders (acc) &&
981 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
983 /* Show only the visible account id */
984 if (priv->visible_account_id) {
985 if (strcmp (account_id, priv->visible_account_id))
992 /* Never show these to the user. They are merged into one folder
993 * in the local-folders account instead: */
994 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
999 /* Check hiding (if necessary) */
1000 cleared = modest_email_clipboard_cleared (priv->clipboard);
1001 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1002 id = tny_folder_get_id (TNY_FOLDER(instance));
1003 if (priv->hidding_ids != NULL)
1004 for (i=0; i < priv->n_selected && !found; i++)
1005 if (priv->hidding_ids[i] != NULL && id != NULL)
1006 found = (!strcmp (priv->hidding_ids[i], id));
1012 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1013 folder as no message can be move there according to UI specs */
1014 if (!priv->show_non_move)
1018 case TNY_FOLDER_TYPE_OUTBOX:
1019 case TNY_FOLDER_TYPE_SENT:
1020 case TNY_FOLDER_TYPE_DRAFTS:
1023 case TNY_FOLDER_TYPE_UNKNOWN:
1024 case TNY_FOLDER_TYPE_NORMAL:
1025 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1026 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1027 || type == TNY_FOLDER_TYPE_DRAFTS)
1038 g_object_unref (instance);
1045 modest_folder_view_update_model (ModestFolderView *self,
1046 TnyAccountStore *account_store)
1048 ModestFolderViewPrivate *priv;
1049 GtkTreeModel *model /* , *old_model */;
1050 /* TnyAccount *local_account; */
1051 TnyList *model_as_list;
1053 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1054 g_return_val_if_fail (account_store, FALSE);
1056 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1058 /* Notify that there is no folder selected */
1059 g_signal_emit (G_OBJECT(self),
1060 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1062 if (priv->cur_folder_store) {
1063 g_object_unref (priv->cur_folder_store);
1064 priv->cur_folder_store = NULL;
1067 /* FIXME: the local accounts are not shown when the query
1068 selects only the subscribed folders. */
1069 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1070 model = tny_gtk_folder_store_tree_model_new (NULL);
1072 /* Deal with the model via its TnyList Interface,
1073 * filling the TnyList via a get_accounts() call: */
1074 model_as_list = TNY_LIST(model);
1076 /* Get the accounts: */
1077 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1079 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1080 g_object_unref (model_as_list);
1081 model_as_list = NULL;
1083 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1085 sortable = gtk_tree_model_sort_new_with_model (model);
1086 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1087 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1088 GTK_SORT_ASCENDING);
1089 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1090 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1091 cmp_rows, NULL, NULL);
1093 /* Create filter model */
1094 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1095 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1101 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1102 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1103 (GCallback) on_row_inserted_maybe_select_folder, self);
1106 g_object_unref (model);
1107 g_object_unref (filter_model);
1108 g_object_unref (sortable);
1110 /* Force a reselection of the INBOX next time the widget is shown */
1111 priv->reselect = TRUE;
1118 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1120 GtkTreeModel *model = NULL;
1121 TnyFolderStore *folder = NULL;
1123 ModestFolderView *tree_view = NULL;
1124 ModestFolderViewPrivate *priv = NULL;
1125 gboolean selected = FALSE;
1127 g_return_if_fail (sel);
1128 g_return_if_fail (user_data);
1130 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1132 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1133 /* if(!gtk_tree_selection_get_selected (sel, &model, &iter)) */
1136 /* Notify the display name observers */
1137 g_signal_emit (G_OBJECT(user_data),
1138 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1141 tree_view = MODEST_FOLDER_VIEW (user_data);
1144 gtk_tree_model_get (model, &iter,
1145 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1148 /* If the folder is the same do not notify */
1149 if (priv->cur_folder_store == folder && folder) {
1150 g_object_unref (folder);
1155 /* Current folder was unselected */
1156 if (priv->cur_folder_store) {
1157 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1158 priv->cur_folder_store, FALSE);
1160 if (TNY_IS_FOLDER(priv->cur_folder_store))
1161 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1162 FALSE, NULL, NULL, NULL);
1164 /* FALSE --> don't expunge the messages */
1166 g_object_unref (priv->cur_folder_store);
1167 priv->cur_folder_store = NULL;
1170 /* New current references */
1171 priv->cur_folder_store = folder;
1173 /* New folder has been selected */
1174 g_signal_emit (G_OBJECT(tree_view),
1175 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1176 0, priv->cur_folder_store, TRUE);
1180 modest_folder_view_get_selected (ModestFolderView *self)
1182 ModestFolderViewPrivate *priv;
1184 g_return_val_if_fail (self, NULL);
1186 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1187 if (priv->cur_folder_store)
1188 g_object_ref (priv->cur_folder_store);
1190 return priv->cur_folder_store;
1194 get_cmp_rows_type_pos (GObject *folder)
1196 /* Remote accounts -> Local account -> MMC account .*/
1199 if (TNY_IS_ACCOUNT (folder) &&
1200 modest_tny_account_is_virtual_local_folders (
1201 TNY_ACCOUNT (folder))) {
1203 } else if (TNY_IS_ACCOUNT (folder)) {
1204 TnyAccount *account = TNY_ACCOUNT (folder);
1205 const gchar *account_id = tny_account_get_id (account);
1206 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1212 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1213 return -1; /* Should never happen */
1218 get_cmp_subfolder_type_pos (TnyFolderType t)
1220 /* Inbox, Outbox, Drafts, Sent, User */
1224 case TNY_FOLDER_TYPE_INBOX:
1227 case TNY_FOLDER_TYPE_OUTBOX:
1230 case TNY_FOLDER_TYPE_DRAFTS:
1233 case TNY_FOLDER_TYPE_SENT:
1242 * This function orders the mail accounts according to these rules:
1243 * 1st - remote accounts
1244 * 2nd - local account
1248 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1252 gchar *name1 = NULL;
1253 gchar *name2 = NULL;
1254 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1255 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1256 GObject *folder1 = NULL;
1257 GObject *folder2 = NULL;
1259 gtk_tree_model_get (tree_model, iter1,
1260 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1261 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1262 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1264 gtk_tree_model_get (tree_model, iter2,
1265 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1266 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1267 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1270 /* Return if we get no folder. This could happen when folder
1271 operations are happening. The model is updated after the
1272 folder copy/move actually occurs, so there could be
1273 situations where the model to be drawn is not correct */
1274 if (!folder1 || !folder2)
1277 if (type == TNY_FOLDER_TYPE_ROOT) {
1278 /* Compare the types, so that
1279 * Remote accounts -> Local account -> MMC account .*/
1280 const gint pos1 = get_cmp_rows_type_pos (folder1);
1281 const gint pos2 = get_cmp_rows_type_pos (folder2);
1282 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1283 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1286 else if (pos1 > pos2)
1289 /* Compare items of the same type: */
1291 TnyAccount *account1 = NULL;
1292 if (TNY_IS_ACCOUNT (folder1))
1293 account1 = TNY_ACCOUNT (folder1);
1295 TnyAccount *account2 = NULL;
1296 if (TNY_IS_ACCOUNT (folder2))
1297 account2 = TNY_ACCOUNT (folder2);
1299 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1300 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1302 if (!account_id && !account_id2) {
1304 } else if (!account_id) {
1306 } else if (!account_id2) {
1308 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1311 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1315 gint cmp1 = 0, cmp2 = 0;
1316 /* get the parent to know if it's a local folder */
1319 gboolean has_parent;
1320 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1322 GObject *parent_folder;
1323 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1324 gtk_tree_model_get (tree_model, &parent,
1325 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1326 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1328 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1329 TNY_IS_ACCOUNT (parent_folder) &&
1330 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1331 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1332 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1334 g_object_unref (parent_folder);
1337 /* if they are not local folders */
1339 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1340 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1344 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1346 cmp = (cmp1 - cmp2);
1351 g_object_unref(G_OBJECT(folder1));
1353 g_object_unref(G_OBJECT(folder2));
1361 /*****************************************************************************/
1362 /* DRAG and DROP stuff */
1363 /*****************************************************************************/
1366 * This function fills the #GtkSelectionData with the row and the
1367 * model that has been dragged. It's called when this widget is a
1368 * source for dnd after the event drop happened
1371 on_drag_data_get (GtkWidget *widget,
1372 GdkDragContext *context,
1373 GtkSelectionData *selection_data,
1378 GtkTreeSelection *selection;
1379 GtkTreeModel *model;
1381 GtkTreePath *source_row;
1383 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1384 gtk_tree_selection_get_selected (selection, &model, &iter);
1385 source_row = gtk_tree_model_get_path (model, &iter);
1387 gtk_tree_set_row_drag_data (selection_data,
1391 gtk_tree_path_free (source_row);
1394 typedef struct _DndHelper {
1395 gboolean delete_source;
1396 GtkTreePath *source_row;
1397 GdkDragContext *context;
1403 * This function is the callback of the
1404 * modest_mail_operation_xfer_msgs () and
1405 * modest_mail_operation_xfer_folder() calls. We check here if the
1406 * message/folder was correctly asynchronously transferred. The reason
1407 * to use the same callback is that the code is the same, it only has
1408 * to check that the operation went fine and then finalize the drag
1412 on_progress_changed (ModestMailOperation *mail_op,
1413 ModestMailOperationState *state,
1419 helper = (DndHelper *) user_data;
1421 if (!state->finished)
1424 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1430 /* Notify the drag source. Never call delete, the monitor will
1431 do the job if needed */
1432 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1434 /* Free the helper */
1435 gtk_tree_path_free (helper->source_row);
1436 g_slice_free (DndHelper, helper);
1440 /* get the folder for the row the treepath refers to. */
1441 /* folder must be unref'd */
1443 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1446 TnyFolder *folder = NULL;
1448 if (gtk_tree_model_get_iter (model,&iter, path))
1449 gtk_tree_model_get (model, &iter,
1450 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1456 show_banner_move_target_error ()
1458 ModestWindow *main_window;
1460 main_window = modest_window_mgr_get_main_window(
1461 modest_runtime_get_window_mgr());
1463 modest_platform_information_banner(GTK_WIDGET(main_window),
1464 NULL, _("mail_in_ui_folder_move_target_error"));
1468 * This function is used by drag_data_received_cb to manage drag and
1469 * drop of a header, i.e, and drag from the header view to the folder
1473 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1474 GtkTreeModel *dest_model,
1475 GtkTreePath *dest_row,
1478 TnyList *headers = NULL;
1479 TnyHeader *header = NULL;
1480 TnyFolder *folder = NULL;
1481 ModestMailOperation *mail_op = NULL;
1482 GtkTreeIter source_iter;
1483 ModestWindowMgr *mgr = NULL; /*no need for unref*/
1484 ModestWindow *main_win = NULL; /*no need for unref*/
1486 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1487 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1488 g_return_if_fail (dest_row);
1489 g_return_if_fail (helper);
1492 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1493 gtk_tree_model_get (source_model, &source_iter,
1494 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1496 if (!TNY_IS_HEADER(header)) {
1497 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1501 /* Check if the selected message is in msg-view. If it is than
1502 * do not enable drag&drop on that. */
1503 mgr = modest_runtime_get_window_mgr ();
1504 if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1508 folder = tree_path_to_folder (dest_model, dest_row);
1509 if (!TNY_IS_FOLDER(folder)) {
1510 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1511 show_banner_move_target_error();
1514 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1515 g_debug ("folder rules: cannot write to that folder");
1519 headers = tny_simple_list_new ();
1520 tny_list_append (headers, G_OBJECT (header));
1522 main_win = modest_window_mgr_get_main_window(mgr);
1523 if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1524 == GTK_RESPONSE_CANCEL)
1527 /* Transfer message */
1528 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1530 modest_ui_actions_move_folder_error_handler,
1532 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1534 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1535 G_CALLBACK (on_progress_changed), helper);
1537 modest_mail_operation_xfer_msgs (mail_op,
1540 helper->delete_source,
1545 if (G_IS_OBJECT(mail_op))
1546 g_object_unref (G_OBJECT (mail_op));
1547 if (G_IS_OBJECT(header))
1548 g_object_unref (G_OBJECT (header));
1549 if (G_IS_OBJECT(folder))
1550 g_object_unref (G_OBJECT (folder));
1551 if (G_IS_OBJECT(headers))
1552 g_object_unref (headers);
1556 * This function is used by drag_data_received_cb to manage drag and
1557 * drop of a folder, i.e, and drag from the folder view to the same
1561 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1562 GtkTreeModel *dest_model,
1563 GtkTreePath *dest_row,
1564 GtkSelectionData *selection_data,
1567 ModestMailOperation *mail_op = NULL;
1568 GtkTreeIter dest_iter, iter;
1569 TnyFolderStore *dest_folder = NULL;
1570 TnyFolder *folder = NULL;
1571 gboolean forbidden = FALSE;
1574 /* check the folder rules for the destination */
1575 folder = tree_path_to_folder (dest_model, dest_row);
1576 if (TNY_IS_FOLDER(folder)) {
1577 ModestTnyFolderRules rules =
1578 modest_tny_folder_get_rules (folder);
1579 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1582 g_debug ("folder rules: cannot write to that folder");
1583 } else if (TNY_IS_FOLDER_STORE(folder)){
1584 /* enable local root as destination for folders */
1585 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1586 && TNY_IS_ACCOUNT (folder))
1589 g_object_unref (folder);
1592 /* check the folder rules for the source */
1593 folder = tree_path_to_folder (source_model, helper->source_row);
1594 if (TNY_IS_FOLDER(folder)) {
1595 ModestTnyFolderRules rules =
1596 modest_tny_folder_get_rules (folder);
1597 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1599 g_debug ("folder rules: cannot move that folder");
1602 g_object_unref (folder);
1606 /* Check if the drag is possible */
1607 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1608 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1609 gtk_tree_path_free (helper->source_row);
1610 g_slice_free (DndHelper, helper);
1615 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1616 gtk_tree_model_get (dest_model, &dest_iter,
1617 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1619 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1620 gtk_tree_model_get (source_model, &iter,
1621 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1624 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1625 if (modest_platform_connect_and_wait_if_network_folderstore (
1626 NULL, dest_folder) &&
1627 modest_platform_connect_and_wait_if_network_folderstore (
1628 NULL, TNY_FOLDER_STORE (folder))) {
1629 /* Do the mail operation */
1630 mail_op = modest_mail_operation_new_with_error_handling (
1631 MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1633 modest_ui_actions_move_folder_error_handler,
1635 modest_mail_operation_queue_add (
1636 modest_runtime_get_mail_operation_queue (),
1641 G_CALLBACK (on_progress_changed),
1644 modest_mail_operation_xfer_folder (mail_op,
1647 helper->delete_source,
1651 g_object_unref (G_OBJECT (mail_op));
1655 g_object_unref (G_OBJECT (dest_folder));
1656 g_object_unref (G_OBJECT (folder));
1660 * This function receives the data set by the "drag-data-get" signal
1661 * handler. This information comes within the #GtkSelectionData. This
1662 * function will manage both the drags of folders of the treeview and
1663 * drags of headers of the header view widget.
1666 on_drag_data_received (GtkWidget *widget,
1667 GdkDragContext *context,
1670 GtkSelectionData *selection_data,
1675 GtkWidget *source_widget;
1676 GtkTreeModel *dest_model, *source_model;
1677 GtkTreePath *source_row, *dest_row;
1678 GtkTreeViewDropPosition pos;
1679 gboolean success = FALSE, delete_source = FALSE;
1680 DndHelper *helper = NULL;
1682 /* Do not allow further process */
1683 g_signal_stop_emission_by_name (widget, "drag-data-received");
1684 source_widget = gtk_drag_get_source_widget (context);
1686 /* Get the action */
1687 if (context->action == GDK_ACTION_MOVE) {
1688 delete_source = TRUE;
1690 /* Notify that there is no folder selected. We need to
1691 do this in order to update the headers view (and
1692 its monitors, because when moving, the old folder
1693 won't longer exist. We can not wait for the end of
1694 the operation, because the operation won't start if
1695 the folder is in use */
1696 if (source_widget == widget) {
1697 ModestFolderViewPrivate *priv;
1699 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1700 if (priv->cur_folder_store) {
1701 g_object_unref (priv->cur_folder_store);
1702 priv->cur_folder_store = NULL;
1705 g_signal_emit (G_OBJECT (widget),
1706 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1710 /* Check if the get_data failed */
1711 if (selection_data == NULL || selection_data->length < 0)
1712 gtk_drag_finish (context, success, FALSE, time);
1714 /* Get the models */
1715 gtk_tree_get_row_drag_data (selection_data,
1719 /* Select the destination model */
1720 if (source_widget == widget) {
1721 dest_model = source_model;
1723 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1726 /* Get the path to the destination row. Can not call
1727 gtk_tree_view_get_drag_dest_row() because the source row
1728 is not selected anymore */
1729 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1732 /* Only allow drops IN other rows */
1733 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1734 gtk_drag_finish (context, success, FALSE, time);
1736 /* Create the helper */
1737 helper = g_slice_new0 (DndHelper);
1738 helper->delete_source = delete_source;
1739 helper->source_row = gtk_tree_path_copy (source_row);
1740 helper->context = context;
1741 helper->time = time;
1743 /* Drags from the header view */
1744 if (source_widget != widget) {
1746 drag_and_drop_from_header_view (source_model,
1753 drag_and_drop_from_folder_view (source_model,
1761 gtk_tree_path_free (source_row);
1762 gtk_tree_path_free (dest_row);
1766 * We define a "drag-drop" signal handler because we do not want to
1767 * use the default one, because the default one always calls
1768 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1769 * signal handler, because there we have all the information available
1770 * to know if the dnd was a success or not.
1773 drag_drop_cb (GtkWidget *widget,
1774 GdkDragContext *context,
1782 if (!context->targets)
1785 /* Check if we're dragging a folder row */
1786 target = gtk_drag_dest_find_target (widget, context, NULL);
1788 /* Request the data from the source. */
1789 gtk_drag_get_data(widget, context, target, time);
1795 * This function expands a node of a tree view if it's not expanded
1796 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1797 * does that, so that's why they're here.
1800 expand_row_timeout (gpointer data)
1802 GtkTreeView *tree_view = data;
1803 GtkTreePath *dest_path = NULL;
1804 GtkTreeViewDropPosition pos;
1805 gboolean result = FALSE;
1807 GDK_THREADS_ENTER ();
1809 gtk_tree_view_get_drag_dest_row (tree_view,
1814 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1815 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1816 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1817 gtk_tree_path_free (dest_path);
1821 gtk_tree_path_free (dest_path);
1826 GDK_THREADS_LEAVE ();
1832 * This function is called whenever the pointer is moved over a widget
1833 * while dragging some data. It installs a timeout that will expand a
1834 * node of the treeview if not expanded yet. This function also calls
1835 * gdk_drag_status in order to set the suggested action that will be
1836 * used by the "drag-data-received" signal handler to know if we
1837 * should do a move or just a copy of the data.
1840 on_drag_motion (GtkWidget *widget,
1841 GdkDragContext *context,
1847 GtkTreeViewDropPosition pos;
1848 GtkTreePath *dest_row;
1849 ModestFolderViewPrivate *priv;
1850 GdkDragAction suggested_action;
1851 gboolean valid_location = FALSE;
1853 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1855 if (priv->timer_expander != 0) {
1856 g_source_remove (priv->timer_expander);
1857 priv->timer_expander = 0;
1860 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1865 /* Do not allow drops between folders */
1867 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1868 pos == GTK_TREE_VIEW_DROP_AFTER) {
1869 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1870 gdk_drag_status(context, 0, time);
1871 valid_location = FALSE;
1874 valid_location = TRUE;
1877 /* Expand the selected row after 1/2 second */
1878 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1879 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1880 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1883 /* Select the desired action. By default we pick MOVE */
1884 suggested_action = GDK_ACTION_MOVE;
1886 if (context->actions == GDK_ACTION_COPY)
1887 gdk_drag_status(context, GDK_ACTION_COPY, time);
1888 else if (context->actions == GDK_ACTION_MOVE)
1889 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1890 else if (context->actions & suggested_action)
1891 gdk_drag_status(context, suggested_action, time);
1893 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1897 gtk_tree_path_free (dest_row);
1898 g_signal_stop_emission_by_name (widget, "drag-motion");
1899 return valid_location;
1903 /* Folder view drag types */
1904 const GtkTargetEntry folder_view_drag_types[] =
1906 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1907 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1911 * This function sets the treeview as a source and a target for dnd
1912 * events. It also connects all the requirede signals.
1915 setup_drag_and_drop (GtkTreeView *self)
1917 /* Set up the folder view as a dnd destination. Set only the
1918 highlight flag, otherwise gtk will have a different
1920 gtk_drag_dest_set (GTK_WIDGET (self),
1921 GTK_DEST_DEFAULT_HIGHLIGHT,
1922 folder_view_drag_types,
1923 G_N_ELEMENTS (folder_view_drag_types),
1924 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1926 g_signal_connect (G_OBJECT (self),
1927 "drag_data_received",
1928 G_CALLBACK (on_drag_data_received),
1932 /* Set up the treeview as a dnd source */
1933 gtk_drag_source_set (GTK_WIDGET (self),
1935 folder_view_drag_types,
1936 G_N_ELEMENTS (folder_view_drag_types),
1937 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1939 g_signal_connect (G_OBJECT (self),
1941 G_CALLBACK (on_drag_motion),
1944 g_signal_connect (G_OBJECT (self),
1946 G_CALLBACK (on_drag_data_get),
1949 g_signal_connect (G_OBJECT (self),
1951 G_CALLBACK (drag_drop_cb),
1956 * This function manages the navigation through the folders using the
1957 * keyboard or the hardware keys in the device
1960 on_key_pressed (GtkWidget *self,
1964 GtkTreeSelection *selection;
1966 GtkTreeModel *model;
1967 gboolean retval = FALSE;
1969 /* Up and Down are automatically managed by the treeview */
1970 if (event->keyval == GDK_Return) {
1971 /* Expand/Collapse the selected row */
1972 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1973 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1976 path = gtk_tree_model_get_path (model, &iter);
1978 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1979 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1981 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1982 gtk_tree_path_free (path);
1984 /* No further processing */
1992 * We listen to the changes in the local folder account name key,
1993 * because we want to show the right name in the view. The local
1994 * folder account name corresponds to the device name in the Maemo
1995 * version. We do this because we do not want to query gconf on each
1996 * tree view refresh. It's better to cache it and change whenever
2000 on_configuration_key_changed (ModestConf* conf,
2002 ModestConfEvent event,
2003 ModestConfNotificationId id,
2004 ModestFolderView *self)
2006 ModestFolderViewPrivate *priv;
2009 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2010 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2012 /* Do not listen for changes in other namespaces */
2013 if (priv->notification_id != id)
2016 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2017 g_free (priv->local_account_name);
2019 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2020 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2022 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2023 MODEST_CONF_DEVICE_NAME, NULL);
2025 /* Force a redraw */
2026 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2027 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2028 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2029 gtk_tree_view_column_queue_resize (tree_column);
2035 modest_folder_view_set_style (ModestFolderView *self,
2036 ModestFolderViewStyle style)
2038 ModestFolderViewPrivate *priv;
2040 g_return_if_fail (self);
2042 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2044 priv->style = style;
2048 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2049 const gchar *account_id)
2051 ModestFolderViewPrivate *priv;
2052 GtkTreeModel *model;
2054 g_return_if_fail (self);
2056 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2058 /* This will be used by the filter_row callback,
2059 * to decided which rows to show: */
2060 if (priv->visible_account_id) {
2061 g_free (priv->visible_account_id);
2062 priv->visible_account_id = NULL;
2065 priv->visible_account_id = g_strdup (account_id);
2068 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2069 if (GTK_IS_TREE_MODEL_FILTER (model))
2070 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2072 /* Save settings to gconf */
2073 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2074 MODEST_CONF_FOLDER_VIEW_KEY);
2078 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2080 ModestFolderViewPrivate *priv;
2082 g_return_val_if_fail (self, NULL);
2084 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2086 return (const gchar *) priv->visible_account_id;
2090 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2094 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2096 gtk_tree_model_get (model, iter,
2097 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2100 gboolean result = FALSE;
2101 if (type == TNY_FOLDER_TYPE_INBOX) {
2105 *inbox_iter = *iter;
2109 if (gtk_tree_model_iter_children (model, &child, iter)) {
2110 if (find_inbox_iter (model, &child, inbox_iter))
2114 } while (gtk_tree_model_iter_next (model, iter));
2123 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2125 GtkTreeModel *model;
2126 GtkTreeIter iter, inbox_iter;
2127 GtkTreeSelection *sel;
2128 GtkTreePath *path = NULL;
2130 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2134 expand_root_items (self);
2135 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2137 gtk_tree_model_get_iter_first (model, &iter);
2139 if (find_inbox_iter (model, &iter, &inbox_iter))
2140 path = gtk_tree_model_get_path (model, &inbox_iter);
2142 path = gtk_tree_path_new_first ();
2144 /* Select the row and free */
2145 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2146 gtk_tree_path_free (path);
2149 gtk_widget_grab_focus (GTK_WIDGET(self));
2155 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2160 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2161 TnyFolder* a_folder;
2164 gtk_tree_model_get (model, iter,
2165 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2166 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2167 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2171 if (folder == a_folder) {
2172 g_object_unref (a_folder);
2173 *folder_iter = *iter;
2176 g_object_unref (a_folder);
2178 if (gtk_tree_model_iter_children (model, &child, iter)) {
2179 if (find_folder_iter (model, &child, folder_iter, folder))
2183 } while (gtk_tree_model_iter_next (model, iter));
2190 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2191 ModestFolderView *self)
2193 ModestFolderViewPrivate *priv = NULL;
2194 GtkTreeSelection *sel;
2196 if (!MODEST_IS_FOLDER_VIEW(self))
2199 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2201 if (priv->folder_to_select) {
2203 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2206 path = gtk_tree_model_get_path (tree_model, iter);
2207 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2209 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2211 gtk_tree_selection_select_iter (sel, iter);
2212 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2214 gtk_tree_path_free (path);
2219 modest_folder_view_disable_next_folder_selection (self);
2220 /* g_object_unref (priv->folder_to_select); */
2221 /* priv->folder_to_select = NULL; */
2227 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2229 ModestFolderViewPrivate *priv = NULL;
2231 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2232 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2234 if (priv->folder_to_select)
2235 g_object_unref(priv->folder_to_select);
2237 priv->folder_to_select = NULL;
2241 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2242 gboolean after_change)
2244 GtkTreeModel *model;
2245 GtkTreeIter iter, folder_iter;
2246 GtkTreeSelection *sel;
2247 ModestFolderViewPrivate *priv = NULL;
2249 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2250 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2252 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2256 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2257 gtk_tree_selection_unselect_all (sel);
2259 if (priv->folder_to_select)
2260 g_object_unref(priv->folder_to_select);
2261 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2265 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2270 gtk_tree_model_get_iter_first (model, &iter);
2271 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2274 path = gtk_tree_model_get_path (model, &folder_iter);
2275 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2277 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2278 gtk_tree_selection_select_iter (sel, &folder_iter);
2279 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2281 gtk_tree_path_free (path);
2289 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2291 /* Copy selection */
2292 _clipboard_set_selected_data (folder_view, FALSE);
2296 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2298 ModestFolderViewPrivate *priv = NULL;
2299 GtkTreeModel *model = NULL;
2300 const gchar **hidding = NULL;
2301 guint i, n_selected;
2303 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2304 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2306 /* Copy selection */
2307 if (!_clipboard_set_selected_data (folder_view, TRUE))
2310 /* Get hidding ids */
2311 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2313 /* Clear hidding array created by previous cut operation */
2314 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2316 /* Copy hidding array */
2317 priv->n_selected = n_selected;
2318 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2319 for (i=0; i < n_selected; i++)
2320 priv->hidding_ids[i] = g_strdup(hidding[i]);
2322 /* Hide cut folders */
2323 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2324 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2328 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2329 ModestFolderView *folder_view_dst)
2331 GtkTreeModel *filter_model = NULL;
2332 GtkTreeModel *model = NULL;
2333 GtkTreeModel *new_filter_model = NULL;
2335 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2336 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2339 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2340 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2342 /* Build new filter model */
2343 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2344 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2348 /* Set copied model */
2349 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2352 g_object_unref (new_filter_model);
2356 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2359 GtkTreeModel *model = NULL;
2360 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2361 priv->show_non_move = show;
2362 /* modest_folder_view_update_model(folder_view, */
2363 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2365 /* Hide special folders */
2366 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2367 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2368 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2372 /* Returns FALSE if it did not selected anything */
2374 _clipboard_set_selected_data (ModestFolderView *folder_view,
2377 ModestFolderViewPrivate *priv = NULL;
2378 TnyFolderStore *folder = NULL;
2379 gboolean retval = FALSE;
2381 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2382 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2384 /* Set selected data on clipboard */
2385 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2386 folder = modest_folder_view_get_selected (folder_view);
2388 /* Do not allow to select an account */
2389 if (TNY_IS_FOLDER (folder)) {
2390 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2395 g_object_unref (folder);
2401 _clear_hidding_filter (ModestFolderView *folder_view)
2403 ModestFolderViewPrivate *priv;
2406 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2407 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2409 if (priv->hidding_ids != NULL) {
2410 for (i=0; i < priv->n_selected; i++)
2411 g_free (priv->hidding_ids[i]);
2412 g_free(priv->hidding_ids);