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-folder.h>
45 #include <modest-tny-local-folders-account.h>
46 #include <modest-tny-outbox-account.h>
47 #include <modest-marshal.h>
48 #include <modest-icon-names.h>
49 #include <modest-tny-account-store.h>
50 #include <modest-text-utils.h>
51 #include <modest-runtime.h>
52 #include "modest-folder-view.h"
53 #include <modest-dnd.h>
54 #include <modest-platform.h>
55 #include <modest-widget-memory.h>
56 #include <modest-ui-actions.h>
58 /* 'private'/'protected' functions */
59 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
60 static void modest_folder_view_init (ModestFolderView *obj);
61 static void modest_folder_view_finalize (GObject *obj);
63 static void tny_account_store_view_init (gpointer g,
66 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
67 TnyAccountStore *account_store);
69 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
71 static void on_account_update (TnyAccountStore *account_store,
75 static void on_account_removed (TnyAccountStore *self,
79 static void on_accounts_reloaded (TnyAccountStore *store,
82 static gint cmp_rows (GtkTreeModel *tree_model,
87 static gboolean filter_row (GtkTreeModel *model,
91 static gboolean on_key_pressed (GtkWidget *self,
95 static void on_configuration_key_changed (ModestConf* conf,
97 ModestConfEvent event,
98 ModestFolderView *self);
101 static void on_drag_data_get (GtkWidget *widget,
102 GdkDragContext *context,
103 GtkSelectionData *selection_data,
108 static void on_drag_data_received (GtkWidget *widget,
109 GdkDragContext *context,
112 GtkSelectionData *selection_data,
117 static gboolean on_drag_motion (GtkWidget *widget,
118 GdkDragContext *context,
124 static gint expand_row_timeout (gpointer data);
126 static void setup_drag_and_drop (GtkTreeView *self);
128 static void _clipboard_set_selected_data (ModestFolderView *folder_view, gboolean delete);
130 static void _clear_hidding_filter (ModestFolderView *folder_view);
135 FOLDER_SELECTION_CHANGED_SIGNAL,
136 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
140 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
141 struct _ModestFolderViewPrivate {
142 TnyAccountStore *account_store;
143 TnyFolderStore *cur_folder_store;
145 gulong account_update_signal;
146 gulong changed_signal;
147 gulong accounts_reloaded_signal;
148 gulong account_removed_signal;
149 gulong conf_key_signal;
151 /* not unref this object, its a singlenton */
152 ModestEmailClipboard *clipboard;
154 /* Filter tree model */
158 TnyFolderStoreQuery *query;
159 guint timer_expander;
161 gchar *local_account_name;
162 gchar *visible_account_id;
163 ModestFolderViewStyle style;
165 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
166 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
167 MODEST_TYPE_FOLDER_VIEW, \
168 ModestFolderViewPrivate))
170 static GObjectClass *parent_class = NULL;
172 static guint signals[LAST_SIGNAL] = {0};
175 modest_folder_view_get_type (void)
177 static GType my_type = 0;
179 static const GTypeInfo my_info = {
180 sizeof(ModestFolderViewClass),
181 NULL, /* base init */
182 NULL, /* base finalize */
183 (GClassInitFunc) modest_folder_view_class_init,
184 NULL, /* class finalize */
185 NULL, /* class data */
186 sizeof(ModestFolderView),
188 (GInstanceInitFunc) modest_folder_view_init,
192 static const GInterfaceInfo tny_account_store_view_info = {
193 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
194 NULL, /* interface_finalize */
195 NULL /* interface_data */
199 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
203 g_type_add_interface_static (my_type,
204 TNY_TYPE_ACCOUNT_STORE_VIEW,
205 &tny_account_store_view_info);
211 modest_folder_view_class_init (ModestFolderViewClass *klass)
213 GObjectClass *gobject_class;
214 gobject_class = (GObjectClass*) klass;
216 parent_class = g_type_class_peek_parent (klass);
217 gobject_class->finalize = modest_folder_view_finalize;
219 g_type_class_add_private (gobject_class,
220 sizeof(ModestFolderViewPrivate));
222 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
223 g_signal_new ("folder_selection_changed",
224 G_TYPE_FROM_CLASS (gobject_class),
226 G_STRUCT_OFFSET (ModestFolderViewClass,
227 folder_selection_changed),
229 modest_marshal_VOID__POINTER_BOOLEAN,
230 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
233 * This signal is emitted whenever the currently selected
234 * folder display name is computed. Note that the name could
235 * be different to the folder name, because we could append
236 * the unread messages count to the folder name to build the
237 * folder display name
239 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
240 g_signal_new ("folder-display-name-changed",
241 G_TYPE_FROM_CLASS (gobject_class),
243 G_STRUCT_OFFSET (ModestFolderViewClass,
244 folder_display_name_changed),
246 g_cclosure_marshal_VOID__STRING,
247 G_TYPE_NONE, 1, G_TYPE_STRING);
251 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
252 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
254 ModestFolderViewPrivate *priv;
259 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
260 GObject *instance = NULL;
262 g_return_if_fail (column);
263 g_return_if_fail (tree_model);
265 gtk_tree_model_get (tree_model, iter,
266 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
267 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
268 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
269 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
270 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
272 rendobj = G_OBJECT(renderer);
283 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
285 gchar *item_name = NULL;
286 gint item_weight = 400;
288 if (type != TNY_FOLDER_TYPE_ROOT) {
291 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
292 type = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
293 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
295 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
299 /* Select the number to show: the unread or unsent messages */
300 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
305 /* Use bold font style if there are unread or unset messages */
307 item_name = g_strdup_printf ("%s (%d)", fname, number);
310 item_name = g_strdup (fname);
314 } else if (TNY_IS_ACCOUNT (instance)) {
315 /* If it's a server account */
316 if (modest_tny_account_is_virtual_local_folders (
317 TNY_ACCOUNT (instance))) {
318 item_name = g_strdup (priv->local_account_name);
321 item_name = g_strdup (fname);
327 item_name = g_strdup ("unknown");
329 if (item_name && item_weight) {
330 /* Set the name in the treeview cell: */
331 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
333 /* Notify display name observers */
334 if (G_OBJECT (priv->cur_folder_store) == instance) {
335 g_signal_emit (G_OBJECT(data),
336 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
344 g_object_unref (G_OBJECT (instance));
351 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
352 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
354 GObject *rendobj = NULL, *instance = NULL;
355 GdkPixbuf *pixbuf = NULL;
356 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
358 const gchar *account_id = NULL;
361 rendobj = G_OBJECT(renderer);
362 gtk_tree_model_get (tree_model, iter,
363 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
364 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
365 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
366 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
377 /* We include the MERGE type here because it's used to create
378 the local OUTBOX folder */
379 if (type == TNY_FOLDER_TYPE_NORMAL ||
380 type == TNY_FOLDER_TYPE_UNKNOWN ||
381 type == TNY_FOLDER_TYPE_MERGE) {
382 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
386 case TNY_FOLDER_TYPE_ROOT:
387 if (TNY_IS_ACCOUNT (instance)) {
389 if (modest_tny_account_is_virtual_local_folders (
390 TNY_ACCOUNT (instance))) {
391 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
394 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
396 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
397 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
399 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
403 case TNY_FOLDER_TYPE_INBOX:
404 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
406 case TNY_FOLDER_TYPE_OUTBOX:
407 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
409 case TNY_FOLDER_TYPE_JUNK:
410 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
412 case TNY_FOLDER_TYPE_SENT:
413 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
415 case TNY_FOLDER_TYPE_TRASH:
416 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
418 case TNY_FOLDER_TYPE_DRAFTS:
419 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
421 case TNY_FOLDER_TYPE_NORMAL:
423 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
427 g_object_unref (G_OBJECT (instance));
431 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
434 g_object_unref (pixbuf);
438 add_columns (GtkWidget *treeview)
440 GtkTreeViewColumn *column;
441 GtkCellRenderer *renderer;
442 GtkTreeSelection *sel;
445 column = gtk_tree_view_column_new ();
447 /* Set icon and text render function */
448 renderer = gtk_cell_renderer_pixbuf_new();
449 gtk_tree_view_column_pack_start (column, renderer, FALSE);
450 gtk_tree_view_column_set_cell_data_func(column, renderer,
451 icon_cell_data, treeview, NULL);
453 renderer = gtk_cell_renderer_text_new();
454 gtk_tree_view_column_pack_start (column, renderer, FALSE);
455 gtk_tree_view_column_set_cell_data_func(column, renderer,
456 text_cell_data, treeview, NULL);
458 /* Set selection mode */
459 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
460 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
462 /* Set treeview appearance */
463 gtk_tree_view_column_set_spacing (column, 2);
464 gtk_tree_view_column_set_resizable (column, TRUE);
465 gtk_tree_view_column_set_fixed_width (column, TRUE);
466 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
467 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
470 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
474 modest_folder_view_init (ModestFolderView *obj)
476 ModestFolderViewPrivate *priv;
479 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
481 priv->timer_expander = 0;
482 priv->account_store = NULL;
484 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
485 priv->cur_folder_store = NULL;
486 priv->visible_account_id = NULL;
488 /* Initialize the local account name */
489 conf = modest_runtime_get_conf();
490 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
492 /* Init email clipboard */
493 priv->clipboard = modest_runtime_get_email_clipboard ();
494 priv->hidding_ids = NULL;
495 priv->n_selected = 0;
498 add_columns (GTK_WIDGET (obj));
500 /* Setup drag and drop */
501 setup_drag_and_drop (GTK_TREE_VIEW(obj));
503 /* Connect signals */
504 g_signal_connect (G_OBJECT (obj),
506 G_CALLBACK (on_key_pressed), NULL);
509 * Track changes in the local account name (in the device it
510 * will be the device name)
512 priv->conf_key_signal =
513 g_signal_connect (G_OBJECT(conf),
515 G_CALLBACK(on_configuration_key_changed), obj);
519 tny_account_store_view_init (gpointer g, gpointer iface_data)
521 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
523 klass->set_account_store_func = modest_folder_view_set_account_store;
529 modest_folder_view_finalize (GObject *obj)
531 ModestFolderViewPrivate *priv;
532 GtkTreeSelection *sel;
534 g_return_if_fail (obj);
536 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
538 if (priv->timer_expander != 0) {
539 g_source_remove (priv->timer_expander);
540 priv->timer_expander = 0;
543 if (priv->account_store) {
544 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
545 priv->account_update_signal);
546 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
547 priv->accounts_reloaded_signal);
548 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
549 priv->account_removed_signal);
550 g_object_unref (G_OBJECT(priv->account_store));
551 priv->account_store = NULL;
555 g_object_unref (G_OBJECT (priv->query));
559 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
561 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
563 g_free (priv->local_account_name);
564 g_free (priv->visible_account_id);
566 if (priv->conf_key_signal) {
567 g_signal_handler_disconnect (modest_runtime_get_conf (),
568 priv->conf_key_signal);
569 priv->conf_key_signal = 0;
572 /* Clear hidding array created by cut operation */
573 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
575 G_OBJECT_CLASS(parent_class)->finalize (obj);
580 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
582 ModestFolderViewPrivate *priv;
585 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
586 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
588 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
589 device = tny_account_store_get_device (account_store);
591 if (G_UNLIKELY (priv->account_store)) {
593 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
594 priv->account_update_signal))
595 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
596 priv->account_update_signal);
597 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
598 priv->accounts_reloaded_signal))
599 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
600 priv->accounts_reloaded_signal);
601 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
602 priv->account_removed_signal))
603 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
604 priv->account_removed_signal);
606 g_object_unref (G_OBJECT (priv->account_store));
609 priv->account_store = g_object_ref (G_OBJECT (account_store));
611 priv->account_update_signal =
612 g_signal_connect (G_OBJECT(account_store), "account_update",
613 G_CALLBACK (on_account_update), self);
615 priv->account_removed_signal =
616 g_signal_connect (G_OBJECT(account_store), "account_removed",
617 G_CALLBACK (on_account_removed), self);
619 priv->accounts_reloaded_signal =
620 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
621 G_CALLBACK (on_accounts_reloaded), self);
623 on_accounts_reloaded (account_store, (gpointer ) self);
625 g_object_unref (G_OBJECT (device));
629 on_account_removed (TnyAccountStore *account_store,
633 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
634 ModestFolderViewPrivate *priv;
636 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
638 /* If the removed account is the currently viewed one then
639 clear the configuration value. The new visible account will be the default account */
640 if (!strcmp (priv->visible_account_id, tny_account_get_id (account))) {
641 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
646 on_account_update (TnyAccountStore *account_store,
647 const gchar *account,
650 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
651 ModestFolderViewPrivate *priv;
653 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
654 if (!priv->visible_account_id)
655 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
656 MODEST_CONF_FOLDER_VIEW_KEY);
658 if (!modest_folder_view_update_model (self, account_store))
659 g_printerr ("modest: failed to update model for changes in '%s'",
664 on_accounts_reloaded (TnyAccountStore *account_store,
667 modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
671 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
673 GtkTreeViewColumn *col;
675 g_return_if_fail (self);
677 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
679 g_printerr ("modest: failed get column for title\n");
683 gtk_tree_view_column_set_title (col, title);
684 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
689 modest_folder_view_new (TnyFolderStoreQuery *query)
692 ModestFolderViewPrivate *priv;
693 GtkTreeSelection *sel;
695 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
696 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
699 priv->query = g_object_ref (query);
701 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
702 priv->changed_signal = g_signal_connect (sel, "changed",
703 G_CALLBACK (on_selection_changed), self);
705 return GTK_WIDGET(self);
708 /* this feels dirty; any other way to expand all the root items? */
710 expand_root_items (ModestFolderView *self)
713 path = gtk_tree_path_new_first ();
715 /* all folders should have child items, so.. */
716 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
717 gtk_tree_path_next (path);
719 gtk_tree_path_free (path);
723 * We use this function to implement the
724 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
725 * account in this case, and the local folders.
728 filter_row (GtkTreeModel *model,
732 ModestFolderViewPrivate *priv;
733 gboolean retval = TRUE;
734 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
735 GObject *instance = NULL;
736 const gchar *id = NULL;
738 gboolean found = FALSE;
739 gboolean cleared = FALSE;
741 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
742 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
744 gtk_tree_model_get (model, iter,
745 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
746 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
749 /* Do not show if there is no instance, this could indeed
750 happen when the model is being modified while it's being
751 drawn. This could occur for example when moving folders
756 if (type == TNY_FOLDER_TYPE_ROOT) {
757 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
758 if (TNY_IS_ACCOUNT (instance)) {
759 TnyAccount *acc = TNY_ACCOUNT (instance);
760 const gchar *account_id = tny_account_get_id (acc);
762 /* If it isn't a special folder,
763 * don't show it unless it is the visible account: */
764 if (!modest_tny_account_is_virtual_local_folders (acc) &&
765 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
766 /* Show only the visible account id */
767 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
771 /* Never show these to the user. They are merged into one folder
772 * in the local-folders account instead: */
773 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
778 /* The virtual local-folders folder store is also shown by default. */
780 /* Check hiding (if necessary) */
781 cleared = modest_email_clipboard_cleared (priv->clipboard);
782 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
783 id = tny_folder_get_id (TNY_FOLDER(instance));
784 if (priv->hidding_ids != NULL)
785 for (i=0; i < priv->n_selected && !found; i++)
786 if (priv->hidding_ids[i] != NULL && id != NULL)
787 found = (!strcmp (priv->hidding_ids[i], id));
793 g_object_unref (instance);
799 modest_folder_view_update_model (ModestFolderView *self,
800 TnyAccountStore *account_store)
802 ModestFolderViewPrivate *priv;
803 GtkTreeModel *model /* , *old_model */;
804 /* TnyAccount *local_account; */
805 TnyList *model_as_list;
807 g_return_val_if_fail (account_store, FALSE);
809 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
811 /* Notify that there is no folder selected */
812 g_signal_emit (G_OBJECT(self),
813 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
816 /* FIXME: the local accounts are not shown when the query
817 selects only the subscribed folders. */
818 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
819 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
821 /* Deal with the model via its TnyList Interface,
822 * filling the TnyList via a get_accounts() call: */
823 model_as_list = TNY_LIST(model);
825 /* Get the accounts: */
826 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
828 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
829 g_object_unref (model_as_list);
830 model_as_list = NULL;
832 GtkTreeModel *filter_model = NULL, *sortable = NULL;
834 sortable = gtk_tree_model_sort_new_with_model (model);
835 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
836 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
838 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
839 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
840 cmp_rows, NULL, NULL);
842 /* Create filter model */
843 filter_model = gtk_tree_model_filter_new (sortable, NULL);
844 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
848 /* if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) { */
849 /* filter_model = gtk_tree_model_filter_new (sortable, NULL); */
850 /* gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model), */
857 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
858 /* gtk_tree_view_set_model (GTK_TREE_VIEW(self), */
859 /* (filter_model) ? filter_model : sortable); */
860 expand_root_items (self); /* expand all account folders */
862 g_object_unref (model);
864 g_object_unref (filter_model);
865 /* if (filter_model) */
866 /* g_object_unref (filter_model); */
868 g_object_unref (sortable);
870 /* Select the first inbox or the local account if not found */
871 modest_folder_view_select_first_inbox_or_local (self);
878 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
881 TnyFolderStore *folder = NULL;
883 ModestFolderView *tree_view;
884 ModestFolderViewPrivate *priv;
886 g_return_if_fail (sel);
887 g_return_if_fail (user_data);
889 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
891 /* folder was _un_selected if true */
892 if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
893 if (priv->cur_folder_store)
894 g_object_unref (priv->cur_folder_store);
895 priv->cur_folder_store = NULL;
897 /* Notify the display name observers */
898 g_signal_emit (G_OBJECT(user_data),
899 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
904 tree_view = MODEST_FOLDER_VIEW (user_data);
906 gtk_tree_model_get (model, &iter,
907 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
910 /* If the folder is the same do not notify */
911 if (priv->cur_folder_store == folder) {
912 g_object_unref (folder);
916 /* Current folder was unselected */
917 if (priv->cur_folder_store) {
918 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
919 priv->cur_folder_store, FALSE);
920 g_object_unref (priv->cur_folder_store);
923 /* New current references */
924 priv->cur_folder_store = folder;
926 /* New folder has been selected */
927 g_signal_emit (G_OBJECT(tree_view),
928 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
933 modest_folder_view_get_selected (ModestFolderView *self)
935 ModestFolderViewPrivate *priv;
937 g_return_val_if_fail (self, NULL);
939 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
940 if (priv->cur_folder_store)
941 g_object_ref (priv->cur_folder_store);
943 return priv->cur_folder_store;
947 get_cmp_rows_type_pos (GObject *folder)
949 /* Remote accounts -> Local account -> MMC account .*/
952 if (TNY_IS_ACCOUNT (folder) &&
953 modest_tny_account_is_virtual_local_folders (
954 TNY_ACCOUNT (folder))) {
956 } else if (TNY_IS_ACCOUNT (folder)) {
957 TnyAccount *account = TNY_ACCOUNT (folder);
958 const gchar *account_id = tny_account_get_id (account);
959 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
965 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
966 return -1; /* Should never happen */
971 * This function orders the mail accounts according to these rules:
972 * 1st - remote accounts
973 * 2nd - local account
977 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
983 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
984 GObject *folder1 = NULL;
985 GObject *folder2 = NULL;
987 gtk_tree_model_get (tree_model, iter1,
988 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
989 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
990 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
992 gtk_tree_model_get (tree_model, iter2,
993 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
994 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
997 if (type == TNY_FOLDER_TYPE_ROOT) {
998 /* Compare the types, so that
999 * Remote accounts -> Local account -> MMC account .*/
1000 const gint pos1 = get_cmp_rows_type_pos (folder1);
1001 const gint pos2 = get_cmp_rows_type_pos (folder2);
1002 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1003 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1006 else if (pos1 > pos2)
1009 /* Compare items of the same type: */
1011 TnyAccount *account1 = NULL;
1012 if (TNY_IS_ACCOUNT (folder1))
1013 account1 = TNY_ACCOUNT (folder1);
1015 TnyAccount *account2 = NULL;
1016 if (TNY_IS_ACCOUNT (folder2))
1017 account2 = TNY_ACCOUNT (folder2);
1019 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1020 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1022 if (!account_id && !account_id2)
1024 else if (!account_id)
1026 else if (!account_id2)
1028 else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1031 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1034 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1038 g_object_unref(G_OBJECT(folder1));
1040 g_object_unref(G_OBJECT(folder2));
1048 /*****************************************************************************/
1049 /* DRAG and DROP stuff */
1050 /*****************************************************************************/
1053 * This function fills the #GtkSelectionData with the row and the
1054 * model that has been dragged. It's called when this widget is a
1055 * source for dnd after the event drop happened
1058 on_drag_data_get (GtkWidget *widget,
1059 GdkDragContext *context,
1060 GtkSelectionData *selection_data,
1065 GtkTreeSelection *selection;
1066 GtkTreeModel *model;
1068 GtkTreePath *source_row;
1070 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1071 gtk_tree_selection_get_selected (selection, &model, &iter);
1072 source_row = gtk_tree_model_get_path (model, &iter);
1074 gtk_tree_set_row_drag_data (selection_data,
1078 gtk_tree_path_free (source_row);
1081 typedef struct _DndHelper {
1082 gboolean delete_source;
1083 GtkTreePath *source_row;
1084 GdkDragContext *context;
1090 * This function is the callback of the
1091 * modest_mail_operation_xfer_msgs () and
1092 * modest_mail_operation_xfer_folder() calls. We check here if the
1093 * message/folder was correctly asynchronously transferred. The reason
1094 * to use the same callback is that the code is the same, it only has
1095 * to check that the operation went fine and then finalize the drag
1099 on_progress_changed (ModestMailOperation *mail_op,
1100 ModestMailOperationState *state,
1106 helper = (DndHelper *) user_data;
1108 if (!state->finished)
1111 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1117 /* Notify the drag source. Never call delete, the monitor will
1118 do the job if needed */
1119 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1121 /* Free the helper */
1122 gtk_tree_path_free (helper->source_row);
1123 g_slice_free (DndHelper, helper);
1127 * This function is used by drag_data_received_cb to manage drag and
1128 * drop of a header, i.e, and drag from the header view to the folder
1132 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1133 GtkTreeModel *dest_model,
1134 GtkTreePath *dest_row,
1137 TnyList *headers = NULL;
1138 TnyHeader *header = NULL;
1139 TnyFolder *folder = NULL;
1140 ModestMailOperation *mail_op = NULL;
1141 GtkTreeIter source_iter, dest_iter;
1143 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1144 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1145 g_return_if_fail (dest_row);
1146 g_return_if_fail (helper);
1149 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1150 gtk_tree_model_get (source_model, &source_iter,
1151 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1153 if (!TNY_IS_HEADER(header)) {
1154 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1159 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1160 gtk_tree_model_get (dest_model, &dest_iter,
1161 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1164 if (!TNY_IS_FOLDER(folder)) {
1165 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1169 /* Transfer message */
1170 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1171 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1173 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1174 G_CALLBACK (on_progress_changed), helper);
1176 headers = tny_simple_list_new ();
1177 tny_list_append (headers, G_OBJECT (header));
1178 modest_mail_operation_xfer_msgs (mail_op,
1181 helper->delete_source,
1186 if (G_IS_OBJECT(mail_op))
1187 g_object_unref (G_OBJECT (mail_op));
1188 if (G_IS_OBJECT(header))
1189 g_object_unref (G_OBJECT (header));
1190 if (G_IS_OBJECT(folder))
1191 g_object_unref (G_OBJECT (folder));
1192 if (G_IS_OBJECT(headers))
1193 g_object_unref (headers);
1197 * This function is used by drag_data_received_cb to manage drag and
1198 * drop of a folder, i.e, and drag from the folder view to the same
1202 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1203 GtkTreeModel *dest_model,
1204 GtkTreePath *dest_row,
1205 GtkSelectionData *selection_data,
1208 ModestMailOperation *mail_op;
1209 GtkTreeIter parent_iter, iter;
1210 TnyFolderStore *parent_folder;
1213 /* Check if the drag is possible */
1214 /* if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1215 /* !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1217 /* selection_data)) { */
1218 if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1220 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1221 gtk_tree_path_free (helper->source_row);
1222 g_slice_free (DndHelper, helper);
1227 gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1228 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1229 gtk_tree_model_get (source_model, &parent_iter,
1230 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1231 &parent_folder, -1);
1232 gtk_tree_model_get (source_model, &iter,
1233 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1236 /* Do the mail operation */
1237 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1239 modest_ui_actions_move_folder_error_handler,
1241 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1243 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1244 G_CALLBACK (on_progress_changed), helper);
1246 modest_mail_operation_xfer_folder (mail_op,
1249 helper->delete_source);
1252 g_object_unref (G_OBJECT (parent_folder));
1253 g_object_unref (G_OBJECT (folder));
1254 g_object_unref (G_OBJECT (mail_op));
1258 * This function receives the data set by the "drag-data-get" signal
1259 * handler. This information comes within the #GtkSelectionData. This
1260 * function will manage both the drags of folders of the treeview and
1261 * drags of headers of the header view widget.
1264 on_drag_data_received (GtkWidget *widget,
1265 GdkDragContext *context,
1268 GtkSelectionData *selection_data,
1273 GtkWidget *source_widget;
1274 GtkTreeModel *dest_model, *source_model;
1275 GtkTreePath *source_row, *dest_row;
1276 GtkTreeViewDropPosition pos;
1277 gboolean success = FALSE, delete_source = FALSE;
1278 DndHelper *helper = NULL;
1280 /* Do not allow further process */
1281 g_signal_stop_emission_by_name (widget, "drag-data-received");
1282 source_widget = gtk_drag_get_source_widget (context);
1284 /* Get the action */
1285 if (context->action == GDK_ACTION_MOVE) {
1286 delete_source = TRUE;
1288 /* Notify that there is no folder selected. We need to
1289 do this in order to update the headers view (and
1290 its monitors, because when moving, the old folder
1291 won't longer exist. We can not wait for the end of
1292 the operation, because the operation won't start if
1293 the folder is in use */
1294 if (source_widget == widget)
1295 g_signal_emit (G_OBJECT (widget),
1296 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1299 /* Check if the get_data failed */
1300 if (selection_data == NULL || selection_data->length < 0)
1301 gtk_drag_finish (context, success, FALSE, time);
1303 /* Get the models */
1304 gtk_tree_get_row_drag_data (selection_data,
1308 /* Select the destination model */
1309 if (source_widget == widget) {
1310 dest_model = source_model;
1312 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1315 /* Get the path to the destination row. Can not call
1316 gtk_tree_view_get_drag_dest_row() because the source row
1317 is not selected anymore */
1318 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1321 /* Only allow drops IN other rows */
1322 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1323 gtk_drag_finish (context, success, FALSE, time);
1325 /* Create the helper */
1326 helper = g_slice_new0 (DndHelper);
1327 helper->delete_source = delete_source;
1328 helper->source_row = gtk_tree_path_copy (source_row);
1329 helper->context = context;
1330 helper->time = time;
1332 /* Drags from the header view */
1333 if (source_widget != widget) {
1335 drag_and_drop_from_header_view (source_model,
1342 drag_and_drop_from_folder_view (source_model,
1350 gtk_tree_path_free (source_row);
1351 gtk_tree_path_free (dest_row);
1355 * We define a "drag-drop" signal handler because we do not want to
1356 * use the default one, because the default one always calls
1357 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1358 * signal handler, because there we have all the information available
1359 * to know if the dnd was a success or not.
1362 drag_drop_cb (GtkWidget *widget,
1363 GdkDragContext *context,
1371 if (!context->targets)
1374 /* Check if we're dragging a folder row */
1375 target = gtk_drag_dest_find_target (widget, context, NULL);
1377 /* Request the data from the source. */
1378 gtk_drag_get_data(widget, context, target, time);
1384 * This function expands a node of a tree view if it's not expanded
1385 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1386 * does that, so that's why they're here.
1389 expand_row_timeout (gpointer data)
1391 GtkTreeView *tree_view = data;
1392 GtkTreePath *dest_path = NULL;
1393 GtkTreeViewDropPosition pos;
1394 gboolean result = FALSE;
1396 GDK_THREADS_ENTER ();
1398 gtk_tree_view_get_drag_dest_row (tree_view,
1403 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1404 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1405 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1406 gtk_tree_path_free (dest_path);
1410 gtk_tree_path_free (dest_path);
1415 GDK_THREADS_LEAVE ();
1421 * This function is called whenever the pointer is moved over a widget
1422 * while dragging some data. It installs a timeout that will expand a
1423 * node of the treeview if not expanded yet. This function also calls
1424 * gdk_drag_status in order to set the suggested action that will be
1425 * used by the "drag-data-received" signal handler to know if we
1426 * should do a move or just a copy of the data.
1429 on_drag_motion (GtkWidget *widget,
1430 GdkDragContext *context,
1436 GtkTreeViewDropPosition pos;
1437 GtkTreePath *dest_row;
1438 ModestFolderViewPrivate *priv;
1439 GdkDragAction suggested_action;
1440 gboolean valid_location = FALSE;
1442 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1444 if (priv->timer_expander != 0) {
1445 g_source_remove (priv->timer_expander);
1446 priv->timer_expander = 0;
1449 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1454 /* Do not allow drops between folders */
1456 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1457 pos == GTK_TREE_VIEW_DROP_AFTER) {
1458 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1459 gdk_drag_status(context, 0, time);
1460 valid_location = FALSE;
1463 valid_location = TRUE;
1466 /* Expand the selected row after 1/2 second */
1467 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1468 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1469 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1472 /* Select the desired action. By default we pick MOVE */
1473 suggested_action = GDK_ACTION_MOVE;
1475 if (context->actions == GDK_ACTION_COPY)
1476 gdk_drag_status(context, GDK_ACTION_COPY, time);
1477 else if (context->actions == GDK_ACTION_MOVE)
1478 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1479 else if (context->actions & suggested_action)
1480 gdk_drag_status(context, suggested_action, time);
1482 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1486 gtk_tree_path_free (dest_row);
1487 g_signal_stop_emission_by_name (widget, "drag-motion");
1488 return valid_location;
1492 /* Folder view drag types */
1493 const GtkTargetEntry folder_view_drag_types[] =
1495 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1496 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1500 * This function sets the treeview as a source and a target for dnd
1501 * events. It also connects all the requirede signals.
1504 setup_drag_and_drop (GtkTreeView *self)
1506 /* Set up the folder view as a dnd destination. Set only the
1507 highlight flag, otherwise gtk will have a different
1509 gtk_drag_dest_set (GTK_WIDGET (self),
1510 GTK_DEST_DEFAULT_HIGHLIGHT,
1511 folder_view_drag_types,
1512 G_N_ELEMENTS (folder_view_drag_types),
1513 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1515 g_signal_connect (G_OBJECT (self),
1516 "drag_data_received",
1517 G_CALLBACK (on_drag_data_received),
1521 /* Set up the treeview as a dnd source */
1522 gtk_drag_source_set (GTK_WIDGET (self),
1524 folder_view_drag_types,
1525 G_N_ELEMENTS (folder_view_drag_types),
1526 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1528 g_signal_connect (G_OBJECT (self),
1530 G_CALLBACK (on_drag_motion),
1533 g_signal_connect (G_OBJECT (self),
1535 G_CALLBACK (on_drag_data_get),
1538 g_signal_connect (G_OBJECT (self),
1540 G_CALLBACK (drag_drop_cb),
1545 * This function manages the navigation through the folders using the
1546 * keyboard or the hardware keys in the device
1549 on_key_pressed (GtkWidget *self,
1553 GtkTreeSelection *selection;
1555 GtkTreeModel *model;
1556 gboolean retval = FALSE;
1558 /* Up and Down are automatically managed by the treeview */
1559 if (event->keyval == GDK_Return) {
1560 /* Expand/Collapse the selected row */
1561 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1562 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1565 path = gtk_tree_model_get_path (model, &iter);
1567 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1568 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1570 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1571 gtk_tree_path_free (path);
1573 /* No further processing */
1581 * We listen to the changes in the local folder account name key,
1582 * because we want to show the right name in the view. The local
1583 * folder account name corresponds to the device name in the Maemo
1584 * version. We do this because we do not want to query gconf on each
1585 * tree view refresh. It's better to cache it and change whenever
1589 on_configuration_key_changed (ModestConf* conf,
1591 ModestConfEvent event,
1592 ModestFolderView *self)
1594 ModestFolderViewPrivate *priv;
1599 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1600 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1602 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1603 g_free (priv->local_account_name);
1605 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1606 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1608 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1609 MODEST_CONF_DEVICE_NAME, NULL);
1611 /* Force a redraw */
1612 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1613 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
1614 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1615 gtk_tree_view_column_queue_resize (tree_column);
1621 modest_folder_view_set_style (ModestFolderView *self,
1622 ModestFolderViewStyle style)
1624 ModestFolderViewPrivate *priv;
1626 g_return_if_fail (self);
1628 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1630 priv->style = style;
1634 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1635 const gchar *account_id)
1637 ModestFolderViewPrivate *priv;
1638 GtkTreeModel *model;
1640 g_return_if_fail (self);
1642 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1644 /* This will be used by the filter_row callback,
1645 * to decided which rows to show: */
1646 if (priv->visible_account_id) {
1647 g_free (priv->visible_account_id);
1648 priv->visible_account_id = NULL;
1651 priv->visible_account_id = g_strdup (account_id);
1654 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1655 if (GTK_IS_TREE_MODEL_FILTER (model))
1656 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1658 /* Save settings to gconf */
1659 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1660 MODEST_CONF_FOLDER_VIEW_KEY);
1664 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1666 ModestFolderViewPrivate *priv;
1668 g_return_val_if_fail (self, NULL);
1670 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1672 return (const gchar *) priv->visible_account_id;
1676 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1680 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1683 gtk_tree_model_get (model, iter,
1684 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1685 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
1689 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n",
1690 __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1693 gboolean result = FALSE;
1694 if (type == TNY_FOLDER_TYPE_INBOX) {
1696 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1697 /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1698 * when getting folders from the cache, before connectin, so we do
1699 * an extra check. We could fix this in tinymail, but it's easier
1702 if (strcmp (name, "Inbox") == 0)
1709 *inbox_iter = *iter;
1713 if (gtk_tree_model_iter_children (model, &child, iter)) {
1714 if (find_inbox_iter (model, &child, inbox_iter))
1718 } while (gtk_tree_model_iter_next (model, iter));
1724 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1726 GtkTreeModel *model;
1727 GtkTreeIter iter, inbox_iter;
1728 GtkTreeSelection *sel;
1730 /* Do not set it if the folder view was not painted */
1731 if (!GTK_WIDGET_MAPPED (self))
1734 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1738 expand_root_items (self);
1739 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1741 gtk_tree_model_get_iter_first (model, &iter);
1742 if (find_inbox_iter (model, &iter, &inbox_iter)) {
1743 gtk_tree_selection_select_iter (sel, &inbox_iter);
1746 gtk_tree_model_get_iter_first (model, &iter);
1747 gtk_tree_selection_select_iter (sel, &iter);
1752 modest_folder_view_copy_selection (ModestFolderView *folder_view)
1754 /* Copy selection */
1755 _clipboard_set_selected_data (folder_view, FALSE);
1759 modest_folder_view_cut_selection (ModestFolderView *folder_view)
1761 ModestFolderViewPrivate *priv = NULL;
1762 GtkTreeModel *model = NULL;
1763 const gchar **hidding = NULL;
1764 guint i, n_selected;
1766 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1767 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1769 /* Copy selection */
1770 _clipboard_set_selected_data (folder_view, TRUE);
1772 /* Get hidding ids */
1773 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1775 /* Clear hidding array created by previous cut operation */
1776 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
1778 /* Copy hidding array */
1779 priv->n_selected = n_selected;
1780 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1781 for (i=0; i < n_selected; i++)
1782 priv->hidding_ids[i] = g_strdup(hidding[i]);
1784 /* Hide cut folders */
1785 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
1786 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1790 _clipboard_set_selected_data (ModestFolderView *folder_view,
1793 ModestFolderViewPrivate *priv = NULL;
1794 TnyFolderStore *folder = NULL;
1796 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1797 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1799 /* Set selected data on clipboard */
1800 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1801 folder = modest_folder_view_get_selected (folder_view);
1802 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
1805 g_object_unref (folder);
1809 _clear_hidding_filter (ModestFolderView *folder_view)
1811 ModestFolderViewPrivate *priv;
1814 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1815 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
1817 if (priv->hidding_ids != NULL) {
1818 for (i=0; i < priv->n_selected; i++)
1819 g_free (priv->hidding_ids[i]);
1820 g_free(priv->hidding_ids);