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,
837 TnyAccount *tny_account,
841 ModestFolderViewPrivate *priv;
842 GtkTreeModel *sort_model, *filter_model;
844 /* Ignore transport account insertions, we're not showing them
845 in the folder view */
846 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
849 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
851 /* Get the inner model */
852 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
853 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
855 /* Remove the account from the model */
856 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
857 G_OBJECT (tny_account));
859 /* Insert the account in the model */
860 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
861 G_OBJECT (tny_account));
867 on_account_removed (TnyAccountStore *account_store,
871 ModestFolderView *self = NULL;
872 ModestFolderViewPrivate *priv;
873 GtkTreeModel *sort_model, *filter_model;
874 GtkTreeSelection *sel = NULL;
876 /* Ignore transport account removals, we're not showing them
877 in the folder view */
878 if (TNY_IS_TRANSPORT_ACCOUNT (account))
881 self = MODEST_FOLDER_VIEW (user_data);
882 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
884 /* Invalidate the cur_folder_store only if the selected folder
885 belongs to the account that is being removed */
886 if (priv->cur_folder_store) {
887 TnyAccount *selected_folder_account = NULL;
889 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
890 selected_folder_account =
891 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
893 selected_folder_account =
894 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
897 if (selected_folder_account == account) {
898 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
899 gtk_tree_selection_unselect_all (sel);
901 g_object_unref (selected_folder_account);
904 /* Invalidate row to select only if the folder to select
905 belongs to the account that is being removed*/
906 if (priv->folder_to_select) {
907 TnyAccount *folder_to_select_account = NULL;
909 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
910 if (folder_to_select_account == account) {
911 /* modest_folder_view_disable_next_folder_selection (self); */
912 g_object_unref (priv->folder_to_select);
913 priv->folder_to_select = NULL;
915 g_object_unref (folder_to_select_account);
918 /* Remove the account from the model */
919 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
920 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
921 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
924 /* If the removed account is the currently viewed one then
925 clear the configuration value. The new visible account will be the default account */
926 if (priv->visible_account_id &&
927 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
929 /* Clear the current visible account_id */
930 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
932 /* Call the restore method, this will set the new visible account */
933 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
934 MODEST_CONF_FOLDER_VIEW_KEY);
937 /* Select the INBOX */
938 modest_folder_view_select_first_inbox_or_local (self);
942 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
944 GtkTreeViewColumn *col;
946 g_return_if_fail (self);
948 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
950 g_printerr ("modest: failed get column for title\n");
954 gtk_tree_view_column_set_title (col, title);
955 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
960 modest_folder_view_on_map (ModestFolderView *self,
961 GdkEventExpose *event,
964 ModestFolderViewPrivate *priv;
966 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
968 /* This won't happen often */
969 if (G_UNLIKELY (priv->reselect)) {
970 /* Select the first inbox or the local account if not found */
972 /* TODO: this could cause a lock at startup, so we
973 comment it for the moment. We know that this will
974 be a bug, because the INBOX is not selected, but we
975 need to rewrite some parts of Modest to avoid the
976 deathlock situation */
977 /* TODO: check if this is still the case */
978 priv->reselect = FALSE;
979 modest_folder_view_select_first_inbox_or_local (self);
980 /* Notify the display name observers */
981 g_signal_emit (G_OBJECT(self),
982 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
986 expand_root_items (self);
992 modest_folder_view_new (TnyFolderStoreQuery *query)
995 ModestFolderViewPrivate *priv;
996 GtkTreeSelection *sel;
998 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
999 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1002 priv->query = g_object_ref (query);
1004 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1005 priv->changed_signal = g_signal_connect (sel, "changed",
1006 G_CALLBACK (on_selection_changed), self);
1008 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1010 return GTK_WIDGET(self);
1013 /* this feels dirty; any other way to expand all the root items? */
1015 expand_root_items (ModestFolderView *self)
1018 path = gtk_tree_path_new_first ();
1020 /* all folders should have child items, so.. */
1021 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
1022 gtk_tree_path_next (path);
1024 gtk_tree_path_free (path);
1028 * We use this function to implement the
1029 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1030 * account in this case, and the local folders.
1033 filter_row (GtkTreeModel *model,
1037 ModestFolderViewPrivate *priv;
1038 gboolean retval = TRUE;
1039 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1040 GObject *instance = NULL;
1041 const gchar *id = NULL;
1043 gboolean found = FALSE;
1044 gboolean cleared = FALSE;
1046 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1047 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1049 gtk_tree_model_get (model, iter,
1050 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1051 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1054 /* Do not show if there is no instance, this could indeed
1055 happen when the model is being modified while it's being
1056 drawn. This could occur for example when moving folders
1061 if (type == TNY_FOLDER_TYPE_ROOT) {
1062 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1063 account instead of a folder. */
1064 if (TNY_IS_ACCOUNT (instance)) {
1065 TnyAccount *acc = TNY_ACCOUNT (instance);
1066 const gchar *account_id = tny_account_get_id (acc);
1068 /* If it isn't a special folder,
1069 * don't show it unless it is the visible account: */
1070 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1071 !modest_tny_account_is_virtual_local_folders (acc) &&
1072 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1074 /* Show only the visible account id */
1075 if (priv->visible_account_id) {
1076 if (strcmp (account_id, priv->visible_account_id))
1083 /* Never show these to the user. They are merged into one folder
1084 * in the local-folders account instead: */
1085 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1090 /* Check hiding (if necessary) */
1091 cleared = modest_email_clipboard_cleared (priv->clipboard);
1092 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1093 id = tny_folder_get_id (TNY_FOLDER(instance));
1094 if (priv->hidding_ids != NULL)
1095 for (i=0; i < priv->n_selected && !found; i++)
1096 if (priv->hidding_ids[i] != NULL && id != NULL)
1097 found = (!strcmp (priv->hidding_ids[i], id));
1103 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1104 folder as no message can be move there according to UI specs */
1105 if (!priv->show_non_move)
1109 case TNY_FOLDER_TYPE_OUTBOX:
1110 case TNY_FOLDER_TYPE_SENT:
1111 case TNY_FOLDER_TYPE_DRAFTS:
1114 case TNY_FOLDER_TYPE_UNKNOWN:
1115 case TNY_FOLDER_TYPE_NORMAL:
1116 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1117 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1118 || type == TNY_FOLDER_TYPE_DRAFTS)
1129 g_object_unref (instance);
1136 modest_folder_view_update_model (ModestFolderView *self,
1137 TnyAccountStore *account_store)
1139 ModestFolderViewPrivate *priv;
1140 GtkTreeModel *model /* , *old_model */;
1141 /* TnyAccount *local_account; */
1142 TnyList *model_as_list;
1144 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1145 g_return_val_if_fail (account_store, FALSE);
1147 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1149 /* Notify that there is no folder selected */
1150 g_signal_emit (G_OBJECT(self),
1151 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1153 if (priv->cur_folder_store) {
1154 g_object_unref (priv->cur_folder_store);
1155 priv->cur_folder_store = NULL;
1158 /* FIXME: the local accounts are not shown when the query
1159 selects only the subscribed folders. */
1160 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1161 model = tny_gtk_folder_store_tree_model_new (NULL);
1163 /* Deal with the model via its TnyList Interface,
1164 * filling the TnyList via a get_accounts() call: */
1165 model_as_list = TNY_LIST(model);
1167 /* Get the accounts: */
1168 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1170 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1171 g_object_unref (model_as_list);
1172 model_as_list = NULL;
1174 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1176 sortable = gtk_tree_model_sort_new_with_model (model);
1177 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1178 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1179 GTK_SORT_ASCENDING);
1180 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1181 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1182 cmp_rows, NULL, NULL);
1184 /* Create filter model */
1185 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1186 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1192 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1193 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1194 (GCallback) on_row_inserted_maybe_select_folder, self);
1197 g_object_unref (model);
1198 g_object_unref (filter_model);
1199 g_object_unref (sortable);
1201 /* Force a reselection of the INBOX next time the widget is shown */
1202 priv->reselect = TRUE;
1209 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1211 GtkTreeModel *model = NULL;
1212 TnyFolderStore *folder = NULL;
1214 ModestFolderView *tree_view = NULL;
1215 ModestFolderViewPrivate *priv = NULL;
1216 gboolean selected = FALSE;
1218 g_return_if_fail (sel);
1219 g_return_if_fail (user_data);
1221 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1223 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1225 /* Notify the display name observers */
1226 g_signal_emit (G_OBJECT(user_data),
1227 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1230 tree_view = MODEST_FOLDER_VIEW (user_data);
1233 gtk_tree_model_get (model, &iter,
1234 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1237 /* If the folder is the same do not notify */
1238 if (priv->cur_folder_store == folder && folder) {
1239 g_object_unref (folder);
1244 /* Current folder was unselected */
1245 if (priv->cur_folder_store) {
1246 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1247 priv->cur_folder_store, FALSE);
1249 if (TNY_IS_FOLDER(priv->cur_folder_store))
1250 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1251 FALSE, NULL, NULL, NULL);
1253 /* FALSE --> don't expunge the messages */
1255 g_object_unref (priv->cur_folder_store);
1256 priv->cur_folder_store = NULL;
1259 /* New current references */
1260 priv->cur_folder_store = folder;
1262 /* New folder has been selected */
1263 g_signal_emit (G_OBJECT(tree_view),
1264 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1265 0, priv->cur_folder_store, TRUE);
1269 modest_folder_view_get_selected (ModestFolderView *self)
1271 ModestFolderViewPrivate *priv;
1273 g_return_val_if_fail (self, NULL);
1275 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1276 if (priv->cur_folder_store)
1277 g_object_ref (priv->cur_folder_store);
1279 return priv->cur_folder_store;
1283 get_cmp_rows_type_pos (GObject *folder)
1285 /* Remote accounts -> Local account -> MMC account .*/
1288 if (TNY_IS_ACCOUNT (folder) &&
1289 modest_tny_account_is_virtual_local_folders (
1290 TNY_ACCOUNT (folder))) {
1292 } else if (TNY_IS_ACCOUNT (folder)) {
1293 TnyAccount *account = TNY_ACCOUNT (folder);
1294 const gchar *account_id = tny_account_get_id (account);
1295 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1301 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1302 return -1; /* Should never happen */
1307 get_cmp_subfolder_type_pos (TnyFolderType t)
1309 /* Inbox, Outbox, Drafts, Sent, User */
1313 case TNY_FOLDER_TYPE_INBOX:
1316 case TNY_FOLDER_TYPE_OUTBOX:
1319 case TNY_FOLDER_TYPE_DRAFTS:
1322 case TNY_FOLDER_TYPE_SENT:
1331 * This function orders the mail accounts according to these rules:
1332 * 1st - remote accounts
1333 * 2nd - local account
1337 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1341 gchar *name1 = NULL;
1342 gchar *name2 = NULL;
1343 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1344 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1345 GObject *folder1 = NULL;
1346 GObject *folder2 = NULL;
1348 gtk_tree_model_get (tree_model, iter1,
1349 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1350 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1351 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1353 gtk_tree_model_get (tree_model, iter2,
1354 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1355 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1356 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1359 /* Return if we get no folder. This could happen when folder
1360 operations are happening. The model is updated after the
1361 folder copy/move actually occurs, so there could be
1362 situations where the model to be drawn is not correct */
1363 if (!folder1 || !folder2)
1366 if (type == TNY_FOLDER_TYPE_ROOT) {
1367 /* Compare the types, so that
1368 * Remote accounts -> Local account -> MMC account .*/
1369 const gint pos1 = get_cmp_rows_type_pos (folder1);
1370 const gint pos2 = get_cmp_rows_type_pos (folder2);
1371 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1372 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1375 else if (pos1 > pos2)
1378 /* Compare items of the same type: */
1380 TnyAccount *account1 = NULL;
1381 if (TNY_IS_ACCOUNT (folder1))
1382 account1 = TNY_ACCOUNT (folder1);
1384 TnyAccount *account2 = NULL;
1385 if (TNY_IS_ACCOUNT (folder2))
1386 account2 = TNY_ACCOUNT (folder2);
1388 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1389 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1391 if (!account_id && !account_id2) {
1393 } else if (!account_id) {
1395 } else if (!account_id2) {
1397 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1400 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1404 gint cmp1 = 0, cmp2 = 0;
1405 /* get the parent to know if it's a local folder */
1408 gboolean has_parent;
1409 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1411 GObject *parent_folder;
1412 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1413 gtk_tree_model_get (tree_model, &parent,
1414 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1415 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1417 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1418 TNY_IS_ACCOUNT (parent_folder) &&
1419 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1420 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1421 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1423 g_object_unref (parent_folder);
1426 /* if they are not local folders */
1428 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1429 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1433 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1435 cmp = (cmp1 - cmp2);
1440 g_object_unref(G_OBJECT(folder1));
1442 g_object_unref(G_OBJECT(folder2));
1450 /*****************************************************************************/
1451 /* DRAG and DROP stuff */
1452 /*****************************************************************************/
1455 * This function fills the #GtkSelectionData with the row and the
1456 * model that has been dragged. It's called when this widget is a
1457 * source for dnd after the event drop happened
1460 on_drag_data_get (GtkWidget *widget,
1461 GdkDragContext *context,
1462 GtkSelectionData *selection_data,
1467 GtkTreeSelection *selection;
1468 GtkTreeModel *model;
1470 GtkTreePath *source_row;
1472 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1473 gtk_tree_selection_get_selected (selection, &model, &iter);
1474 source_row = gtk_tree_model_get_path (model, &iter);
1476 gtk_tree_set_row_drag_data (selection_data,
1480 gtk_tree_path_free (source_row);
1483 typedef struct _DndHelper {
1484 gboolean delete_source;
1485 GtkTreePath *source_row;
1486 GdkDragContext *context;
1492 * This function is the callback of the
1493 * modest_mail_operation_xfer_msgs () and
1494 * modest_mail_operation_xfer_folder() calls. We check here if the
1495 * message/folder was correctly asynchronously transferred. The reason
1496 * to use the same callback is that the code is the same, it only has
1497 * to check that the operation went fine and then finalize the drag
1501 on_progress_changed (ModestMailOperation *mail_op,
1502 ModestMailOperationState *state,
1508 helper = (DndHelper *) user_data;
1510 if (!state->finished)
1513 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1519 /* Notify the drag source. Never call delete, the monitor will
1520 do the job if needed */
1521 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1523 /* Free the helper */
1524 gtk_tree_path_free (helper->source_row);
1525 g_slice_free (DndHelper, helper);
1529 /* get the folder for the row the treepath refers to. */
1530 /* folder must be unref'd */
1532 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1535 TnyFolder *folder = NULL;
1537 if (gtk_tree_model_get_iter (model,&iter, path))
1538 gtk_tree_model_get (model, &iter,
1539 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1545 show_banner_move_target_error ()
1547 ModestWindow *main_window;
1549 main_window = modest_window_mgr_get_main_window(
1550 modest_runtime_get_window_mgr());
1552 modest_platform_information_banner(GTK_WIDGET(main_window),
1553 NULL, _("mail_in_ui_folder_move_target_error"));
1557 * This function is used by drag_data_received_cb to manage drag and
1558 * drop of a header, i.e, and drag from the header view to the folder
1562 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1563 GtkTreeModel *dest_model,
1564 GtkTreePath *dest_row,
1567 TnyList *headers = NULL;
1568 TnyHeader *header = NULL;
1569 TnyFolder *folder = NULL;
1570 ModestMailOperation *mail_op = NULL;
1571 GtkTreeIter source_iter;
1572 ModestWindowMgr *mgr = NULL; /*no need for unref*/
1573 ModestWindow *main_win = NULL; /*no need for unref*/
1575 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1576 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1577 g_return_if_fail (dest_row);
1578 g_return_if_fail (helper);
1581 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1582 gtk_tree_model_get (source_model, &source_iter,
1583 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1585 if (!TNY_IS_HEADER(header)) {
1586 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1590 /* Check if the selected message is in msg-view. If it is than
1591 * do not enable drag&drop on that. */
1592 mgr = modest_runtime_get_window_mgr ();
1593 if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1597 folder = tree_path_to_folder (dest_model, dest_row);
1598 if (!TNY_IS_FOLDER(folder)) {
1599 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1600 show_banner_move_target_error();
1603 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1604 g_debug ("folder rules: cannot write to that folder");
1608 headers = tny_simple_list_new ();
1609 tny_list_append (headers, G_OBJECT (header));
1611 main_win = modest_window_mgr_get_main_window(mgr);
1612 if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1613 == GTK_RESPONSE_CANCEL)
1616 /* Transfer message */
1617 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1619 modest_ui_actions_move_folder_error_handler,
1621 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1623 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1624 G_CALLBACK (on_progress_changed), helper);
1626 modest_mail_operation_xfer_msgs (mail_op,
1629 helper->delete_source,
1634 if (G_IS_OBJECT(mail_op))
1635 g_object_unref (G_OBJECT (mail_op));
1636 if (G_IS_OBJECT(header))
1637 g_object_unref (G_OBJECT (header));
1638 if (G_IS_OBJECT(folder))
1639 g_object_unref (G_OBJECT (folder));
1640 if (G_IS_OBJECT(headers))
1641 g_object_unref (headers);
1645 * This function is used by drag_data_received_cb to manage drag and
1646 * drop of a folder, i.e, and drag from the folder view to the same
1650 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1651 GtkTreeModel *dest_model,
1652 GtkTreePath *dest_row,
1653 GtkSelectionData *selection_data,
1656 ModestMailOperation *mail_op = NULL;
1657 GtkTreeIter dest_iter, iter;
1658 TnyFolderStore *dest_folder = NULL;
1659 TnyFolder *folder = NULL;
1660 gboolean forbidden = FALSE;
1663 /* check the folder rules for the destination */
1664 folder = tree_path_to_folder (dest_model, dest_row);
1665 if (TNY_IS_FOLDER(folder)) {
1666 ModestTnyFolderRules rules =
1667 modest_tny_folder_get_rules (folder);
1668 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1671 g_debug ("folder rules: cannot write to that folder");
1672 } else if (TNY_IS_FOLDER_STORE(folder)){
1673 /* enable local root as destination for folders */
1674 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1675 && TNY_IS_ACCOUNT (folder))
1678 g_object_unref (folder);
1681 /* check the folder rules for the source */
1682 folder = tree_path_to_folder (source_model, helper->source_row);
1683 if (TNY_IS_FOLDER(folder)) {
1684 ModestTnyFolderRules rules =
1685 modest_tny_folder_get_rules (folder);
1686 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1688 g_debug ("folder rules: cannot move that folder");
1691 g_object_unref (folder);
1695 /* Check if the drag is possible */
1696 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1697 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1698 gtk_tree_path_free (helper->source_row);
1699 g_slice_free (DndHelper, helper);
1704 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1705 gtk_tree_model_get (dest_model, &dest_iter,
1706 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1708 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1709 gtk_tree_model_get (source_model, &iter,
1710 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1713 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1714 if (modest_platform_connect_and_wait_if_network_folderstore (
1715 NULL, dest_folder) &&
1716 modest_platform_connect_and_wait_if_network_folderstore (
1717 NULL, TNY_FOLDER_STORE (folder))) {
1718 /* Do the mail operation */
1719 mail_op = modest_mail_operation_new_with_error_handling (
1720 MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1722 modest_ui_actions_move_folder_error_handler,
1724 modest_mail_operation_queue_add (
1725 modest_runtime_get_mail_operation_queue (),
1730 G_CALLBACK (on_progress_changed),
1733 modest_mail_operation_xfer_folder (mail_op,
1736 helper->delete_source,
1740 g_object_unref (G_OBJECT (mail_op));
1744 g_object_unref (G_OBJECT (dest_folder));
1745 g_object_unref (G_OBJECT (folder));
1749 * This function receives the data set by the "drag-data-get" signal
1750 * handler. This information comes within the #GtkSelectionData. This
1751 * function will manage both the drags of folders of the treeview and
1752 * drags of headers of the header view widget.
1755 on_drag_data_received (GtkWidget *widget,
1756 GdkDragContext *context,
1759 GtkSelectionData *selection_data,
1764 GtkWidget *source_widget;
1765 GtkTreeModel *dest_model, *source_model;
1766 GtkTreePath *source_row, *dest_row;
1767 GtkTreeViewDropPosition pos;
1768 gboolean success = FALSE, delete_source = FALSE;
1769 DndHelper *helper = NULL;
1771 /* Do not allow further process */
1772 g_signal_stop_emission_by_name (widget, "drag-data-received");
1773 source_widget = gtk_drag_get_source_widget (context);
1775 /* Get the action */
1776 if (context->action == GDK_ACTION_MOVE) {
1777 delete_source = TRUE;
1779 /* Notify that there is no folder selected. We need to
1780 do this in order to update the headers view (and
1781 its monitors, because when moving, the old folder
1782 won't longer exist. We can not wait for the end of
1783 the operation, because the operation won't start if
1784 the folder is in use */
1785 if (source_widget == widget) {
1786 ModestFolderViewPrivate *priv;
1788 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1789 if (priv->cur_folder_store) {
1790 g_object_unref (priv->cur_folder_store);
1791 priv->cur_folder_store = NULL;
1794 g_signal_emit (G_OBJECT (widget),
1795 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1799 /* Check if the get_data failed */
1800 if (selection_data == NULL || selection_data->length < 0)
1801 gtk_drag_finish (context, success, FALSE, time);
1803 /* Get the models */
1804 gtk_tree_get_row_drag_data (selection_data,
1808 /* Select the destination model */
1809 if (source_widget == widget) {
1810 dest_model = source_model;
1812 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1815 /* Get the path to the destination row. Can not call
1816 gtk_tree_view_get_drag_dest_row() because the source row
1817 is not selected anymore */
1818 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1821 /* Only allow drops IN other rows */
1822 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1823 gtk_drag_finish (context, success, FALSE, time);
1825 /* Create the helper */
1826 helper = g_slice_new0 (DndHelper);
1827 helper->delete_source = delete_source;
1828 helper->source_row = gtk_tree_path_copy (source_row);
1829 helper->context = context;
1830 helper->time = time;
1832 /* Drags from the header view */
1833 if (source_widget != widget) {
1835 drag_and_drop_from_header_view (source_model,
1842 drag_and_drop_from_folder_view (source_model,
1850 gtk_tree_path_free (source_row);
1851 gtk_tree_path_free (dest_row);
1855 * We define a "drag-drop" signal handler because we do not want to
1856 * use the default one, because the default one always calls
1857 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1858 * signal handler, because there we have all the information available
1859 * to know if the dnd was a success or not.
1862 drag_drop_cb (GtkWidget *widget,
1863 GdkDragContext *context,
1871 if (!context->targets)
1874 /* Check if we're dragging a folder row */
1875 target = gtk_drag_dest_find_target (widget, context, NULL);
1877 /* Request the data from the source. */
1878 gtk_drag_get_data(widget, context, target, time);
1884 * This function expands a node of a tree view if it's not expanded
1885 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1886 * does that, so that's why they're here.
1889 expand_row_timeout (gpointer data)
1891 GtkTreeView *tree_view = data;
1892 GtkTreePath *dest_path = NULL;
1893 GtkTreeViewDropPosition pos;
1894 gboolean result = FALSE;
1896 GDK_THREADS_ENTER ();
1898 gtk_tree_view_get_drag_dest_row (tree_view,
1903 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1904 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1905 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1906 gtk_tree_path_free (dest_path);
1910 gtk_tree_path_free (dest_path);
1915 GDK_THREADS_LEAVE ();
1921 * This function is called whenever the pointer is moved over a widget
1922 * while dragging some data. It installs a timeout that will expand a
1923 * node of the treeview if not expanded yet. This function also calls
1924 * gdk_drag_status in order to set the suggested action that will be
1925 * used by the "drag-data-received" signal handler to know if we
1926 * should do a move or just a copy of the data.
1929 on_drag_motion (GtkWidget *widget,
1930 GdkDragContext *context,
1936 GtkTreeViewDropPosition pos;
1937 GtkTreePath *dest_row;
1938 ModestFolderViewPrivate *priv;
1939 GdkDragAction suggested_action;
1940 gboolean valid_location = FALSE;
1942 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1944 if (priv->timer_expander != 0) {
1945 g_source_remove (priv->timer_expander);
1946 priv->timer_expander = 0;
1949 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1954 /* Do not allow drops between folders */
1956 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1957 pos == GTK_TREE_VIEW_DROP_AFTER) {
1958 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1959 gdk_drag_status(context, 0, time);
1960 valid_location = FALSE;
1963 valid_location = TRUE;
1966 /* Expand the selected row after 1/2 second */
1967 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1968 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1969 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1972 /* Select the desired action. By default we pick MOVE */
1973 suggested_action = GDK_ACTION_MOVE;
1975 if (context->actions == GDK_ACTION_COPY)
1976 gdk_drag_status(context, GDK_ACTION_COPY, time);
1977 else if (context->actions == GDK_ACTION_MOVE)
1978 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1979 else if (context->actions & suggested_action)
1980 gdk_drag_status(context, suggested_action, time);
1982 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1986 gtk_tree_path_free (dest_row);
1987 g_signal_stop_emission_by_name (widget, "drag-motion");
1988 return valid_location;
1992 /* Folder view drag types */
1993 const GtkTargetEntry folder_view_drag_types[] =
1995 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1996 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
2000 * This function sets the treeview as a source and a target for dnd
2001 * events. It also connects all the requirede signals.
2004 setup_drag_and_drop (GtkTreeView *self)
2006 /* Set up the folder view as a dnd destination. Set only the
2007 highlight flag, otherwise gtk will have a different
2009 gtk_drag_dest_set (GTK_WIDGET (self),
2010 GTK_DEST_DEFAULT_HIGHLIGHT,
2011 folder_view_drag_types,
2012 G_N_ELEMENTS (folder_view_drag_types),
2013 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2015 g_signal_connect (G_OBJECT (self),
2016 "drag_data_received",
2017 G_CALLBACK (on_drag_data_received),
2021 /* Set up the treeview as a dnd source */
2022 gtk_drag_source_set (GTK_WIDGET (self),
2024 folder_view_drag_types,
2025 G_N_ELEMENTS (folder_view_drag_types),
2026 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2028 g_signal_connect (G_OBJECT (self),
2030 G_CALLBACK (on_drag_motion),
2033 g_signal_connect (G_OBJECT (self),
2035 G_CALLBACK (on_drag_data_get),
2038 g_signal_connect (G_OBJECT (self),
2040 G_CALLBACK (drag_drop_cb),
2045 * This function manages the navigation through the folders using the
2046 * keyboard or the hardware keys in the device
2049 on_key_pressed (GtkWidget *self,
2053 GtkTreeSelection *selection;
2055 GtkTreeModel *model;
2056 gboolean retval = FALSE;
2058 /* Up and Down are automatically managed by the treeview */
2059 if (event->keyval == GDK_Return) {
2060 /* Expand/Collapse the selected row */
2061 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2062 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2065 path = gtk_tree_model_get_path (model, &iter);
2067 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2068 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2070 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2071 gtk_tree_path_free (path);
2073 /* No further processing */
2081 * We listen to the changes in the local folder account name key,
2082 * because we want to show the right name in the view. The local
2083 * folder account name corresponds to the device name in the Maemo
2084 * version. We do this because we do not want to query gconf on each
2085 * tree view refresh. It's better to cache it and change whenever
2089 on_configuration_key_changed (ModestConf* conf,
2091 ModestConfEvent event,
2092 ModestConfNotificationId id,
2093 ModestFolderView *self)
2095 ModestFolderViewPrivate *priv;
2098 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2099 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2101 /* Do not listen for changes in other namespaces */
2102 if (priv->notification_id != id)
2105 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2106 g_free (priv->local_account_name);
2108 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2109 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2111 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2112 MODEST_CONF_DEVICE_NAME, NULL);
2114 /* Force a redraw */
2115 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2116 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2117 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2118 gtk_tree_view_column_queue_resize (tree_column);
2124 modest_folder_view_set_style (ModestFolderView *self,
2125 ModestFolderViewStyle style)
2127 ModestFolderViewPrivate *priv;
2129 g_return_if_fail (self);
2131 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2133 priv->style = style;
2137 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2138 const gchar *account_id)
2140 ModestFolderViewPrivate *priv;
2141 GtkTreeModel *model;
2143 g_return_if_fail (self);
2145 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2147 /* This will be used by the filter_row callback,
2148 * to decided which rows to show: */
2149 if (priv->visible_account_id) {
2150 g_free (priv->visible_account_id);
2151 priv->visible_account_id = NULL;
2154 priv->visible_account_id = g_strdup (account_id);
2157 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2158 if (GTK_IS_TREE_MODEL_FILTER (model))
2159 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2161 /* Save settings to gconf */
2162 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2163 MODEST_CONF_FOLDER_VIEW_KEY);
2167 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2169 ModestFolderViewPrivate *priv;
2171 g_return_val_if_fail (self, NULL);
2173 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2175 return (const gchar *) priv->visible_account_id;
2179 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2183 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2185 gtk_tree_model_get (model, iter,
2186 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2189 gboolean result = FALSE;
2190 if (type == TNY_FOLDER_TYPE_INBOX) {
2194 *inbox_iter = *iter;
2198 if (gtk_tree_model_iter_children (model, &child, iter)) {
2199 if (find_inbox_iter (model, &child, inbox_iter))
2203 } while (gtk_tree_model_iter_next (model, iter));
2212 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2214 GtkTreeModel *model;
2215 GtkTreeIter iter, inbox_iter;
2216 GtkTreeSelection *sel;
2217 GtkTreePath *path = NULL;
2219 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2223 expand_root_items (self);
2224 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2226 gtk_tree_model_get_iter_first (model, &iter);
2228 if (find_inbox_iter (model, &iter, &inbox_iter))
2229 path = gtk_tree_model_get_path (model, &inbox_iter);
2231 path = gtk_tree_path_new_first ();
2233 /* Select the row and free */
2234 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2235 gtk_tree_path_free (path);
2238 gtk_widget_grab_focus (GTK_WIDGET(self));
2244 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2249 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2250 TnyFolder* a_folder;
2253 gtk_tree_model_get (model, iter,
2254 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2255 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2256 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2260 if (folder == a_folder) {
2261 g_object_unref (a_folder);
2262 *folder_iter = *iter;
2265 g_object_unref (a_folder);
2267 if (gtk_tree_model_iter_children (model, &child, iter)) {
2268 if (find_folder_iter (model, &child, folder_iter, folder))
2272 } while (gtk_tree_model_iter_next (model, iter));
2279 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2280 ModestFolderView *self)
2282 ModestFolderViewPrivate *priv = NULL;
2283 GtkTreeSelection *sel;
2285 if (!MODEST_IS_FOLDER_VIEW(self))
2288 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2290 if (priv->folder_to_select) {
2292 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2295 path = gtk_tree_model_get_path (tree_model, iter);
2296 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2298 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2300 gtk_tree_selection_select_iter (sel, iter);
2301 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2303 gtk_tree_path_free (path);
2308 modest_folder_view_disable_next_folder_selection (self);
2309 /* g_object_unref (priv->folder_to_select); */
2310 /* priv->folder_to_select = NULL; */
2316 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2318 ModestFolderViewPrivate *priv = NULL;
2320 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2321 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2323 if (priv->folder_to_select)
2324 g_object_unref(priv->folder_to_select);
2326 priv->folder_to_select = NULL;
2330 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2331 gboolean after_change)
2333 GtkTreeModel *model;
2334 GtkTreeIter iter, folder_iter;
2335 GtkTreeSelection *sel;
2336 ModestFolderViewPrivate *priv = NULL;
2338 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2339 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2341 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2345 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2346 gtk_tree_selection_unselect_all (sel);
2348 if (priv->folder_to_select)
2349 g_object_unref(priv->folder_to_select);
2350 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2354 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2359 gtk_tree_model_get_iter_first (model, &iter);
2360 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2363 path = gtk_tree_model_get_path (model, &folder_iter);
2364 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2366 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2367 gtk_tree_selection_select_iter (sel, &folder_iter);
2368 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2370 gtk_tree_path_free (path);
2378 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2380 /* Copy selection */
2381 _clipboard_set_selected_data (folder_view, FALSE);
2385 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2387 ModestFolderViewPrivate *priv = NULL;
2388 GtkTreeModel *model = NULL;
2389 const gchar **hidding = NULL;
2390 guint i, n_selected;
2392 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2393 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2395 /* Copy selection */
2396 if (!_clipboard_set_selected_data (folder_view, TRUE))
2399 /* Get hidding ids */
2400 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2402 /* Clear hidding array created by previous cut operation */
2403 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2405 /* Copy hidding array */
2406 priv->n_selected = n_selected;
2407 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2408 for (i=0; i < n_selected; i++)
2409 priv->hidding_ids[i] = g_strdup(hidding[i]);
2411 /* Hide cut folders */
2412 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2413 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2417 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2418 ModestFolderView *folder_view_dst)
2420 GtkTreeModel *filter_model = NULL;
2421 GtkTreeModel *model = NULL;
2422 GtkTreeModel *new_filter_model = NULL;
2424 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2425 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2428 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2429 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2431 /* Build new filter model */
2432 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2433 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2437 /* Set copied model */
2438 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2441 g_object_unref (new_filter_model);
2445 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2448 GtkTreeModel *model = NULL;
2449 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2450 priv->show_non_move = show;
2451 /* modest_folder_view_update_model(folder_view, */
2452 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2454 /* Hide special folders */
2455 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2456 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2457 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2461 /* Returns FALSE if it did not selected anything */
2463 _clipboard_set_selected_data (ModestFolderView *folder_view,
2466 ModestFolderViewPrivate *priv = NULL;
2467 TnyFolderStore *folder = NULL;
2468 gboolean retval = FALSE;
2470 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2471 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2473 /* Set selected data on clipboard */
2474 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2475 folder = modest_folder_view_get_selected (folder_view);
2477 /* Do not allow to select an account */
2478 if (TNY_IS_FOLDER (folder)) {
2479 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2484 g_object_unref (folder);
2490 _clear_hidding_filter (ModestFolderView *folder_view)
2492 ModestFolderViewPrivate *priv;
2495 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2496 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2498 if (priv->hidding_ids != NULL) {
2499 for (i=0; i < priv->n_selected; i++)
2500 g_free (priv->hidding_ids[i]);
2501 g_free(priv->hidding_ids);