fixes NB#66958
[modest] / src / widgets / modest-folder-view.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
16  *
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.
28  */
29
30 #include <glib/gi18n.h>
31 #include <string.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-store-tree-model.h>
36 #include <tny-gtk-header-list-model.h>
37 #include <tny-folder.h>
38 #include <tny-folder-store-observer.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <tny-camel-account.h>
45 #include <modest-tny-account.h>
46 #include <modest-tny-folder.h>
47 #include <modest-tny-local-folders-account.h>
48 #include <modest-tny-outbox-account.h>
49 #include <modest-marshal.h>
50 #include <modest-icon-names.h>
51 #include <modest-tny-account-store.h>
52 #include <modest-text-utils.h>
53 #include <modest-runtime.h>
54 #include "modest-folder-view.h"
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
58 #include "modest-dnd.h"
59 #include "widgets/modest-window.h"
60
61 /* Folder view drag types */
62 const GtkTargetEntry folder_view_drag_types[] =
63 {
64         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
65         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
66 };
67
68 /* 'private'/'protected' functions */
69 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
70 static void modest_folder_view_init        (ModestFolderView *obj);
71 static void modest_folder_view_finalize    (GObject *obj);
72
73 static void         tny_account_store_view_init (gpointer g, 
74                                                  gpointer iface_data);
75
76 static void         modest_folder_view_set_account_store (TnyAccountStoreView *self, 
77                                                           TnyAccountStore     *account_store);
78
79 static void         on_selection_changed   (GtkTreeSelection *sel, gpointer data);
80
81 static void         on_account_removed     (TnyAccountStore *self, 
82                                             TnyAccount *account,
83                                             gpointer user_data);
84
85 static void         on_account_inserted    (TnyAccountStore *self, 
86                                             TnyAccount *account,
87                                             gpointer user_data);
88
89 static void         on_account_changed    (TnyAccountStore *self, 
90                                             TnyAccount *account,
91                                             gpointer user_data);
92
93 static gint         cmp_rows               (GtkTreeModel *tree_model, 
94                                             GtkTreeIter *iter1, 
95                                             GtkTreeIter *iter2,
96                                             gpointer user_data);
97
98 static gboolean     filter_row             (GtkTreeModel *model,
99                                             GtkTreeIter *iter,
100                                             gpointer data);
101
102 static gboolean     on_key_pressed         (GtkWidget *self,
103                                             GdkEventKey *event,
104                                             gpointer user_data);
105
106 static void         on_configuration_key_changed  (ModestConf* conf, 
107                                                    const gchar *key, 
108                                                    ModestConfEvent event,
109                                                    ModestConfNotificationId notification_id, 
110                                                    ModestFolderView *self);
111
112 /* DnD functions */
113 static void         on_drag_data_get       (GtkWidget *widget, 
114                                             GdkDragContext *context, 
115                                             GtkSelectionData *selection_data, 
116                                             guint info, 
117                                             guint time, 
118                                             gpointer data);
119
120 static void         on_drag_data_received  (GtkWidget *widget, 
121                                             GdkDragContext *context, 
122                                             gint x, 
123                                             gint y, 
124                                             GtkSelectionData *selection_data, 
125                                             guint info, 
126                                             guint time, 
127                                             gpointer data);
128
129 static gboolean     on_drag_motion         (GtkWidget      *widget,
130                                             GdkDragContext *context,
131                                             gint            x,
132                                             gint            y,
133                                             guint           time,
134                                             gpointer        user_data);
135
136 static void         expand_root_items (ModestFolderView *self);
137
138 static gint         expand_row_timeout     (gpointer data);
139
140 static void         setup_drag_and_drop    (GtkTreeView *self);
141
142 static gboolean     _clipboard_set_selected_data (ModestFolderView *folder_view, 
143                                                   gboolean delete);
144
145 static void         _clear_hidding_filter (ModestFolderView *folder_view);
146
147 static void         on_row_inserted_maybe_select_folder (GtkTreeModel     *tree_model, 
148                                                          GtkTreePath      *path, 
149                                                          GtkTreeIter      *iter,
150                                                          ModestFolderView *self);
151
152 static void         on_display_name_changed (ModestAccountMgr *self, 
153                                              const gchar *account,
154                                              gpointer user_data);
155
156 enum {
157         FOLDER_SELECTION_CHANGED_SIGNAL,
158         FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
159         LAST_SIGNAL
160 };
161
162 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
163 struct _ModestFolderViewPrivate {
164         TnyAccountStore      *account_store;
165         TnyFolderStore       *cur_folder_store;
166
167         TnyFolder            *folder_to_select; /* folder to select after the next update */
168
169         gulong                changed_signal;
170         gulong                account_inserted_signal;
171         gulong                account_removed_signal;
172         gulong                account_changed_signal;
173         gulong                conf_key_signal;
174         gulong                display_name_changed_signal;
175         
176         /* not unref this object, its a singlenton */
177         ModestEmailClipboard *clipboard;
178
179         /* Filter tree model */
180         gchar **hidding_ids;
181         guint n_selected;
182
183         TnyFolderStoreQuery  *query;
184         guint                 timer_expander;
185
186         gchar                *local_account_name;
187         gchar                *visible_account_id;
188         ModestFolderViewStyle style;
189
190         gboolean  reselect; /* we use this to force a reselection of the INBOX */
191         gboolean  show_non_move;
192         gboolean  reexpand; /* next time we expose, we'll expand all root folders */
193 };
194 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                       \
195         (G_TYPE_INSTANCE_GET_PRIVATE((o),                       \
196                                      MODEST_TYPE_FOLDER_VIEW,   \
197                                      ModestFolderViewPrivate))
198 /* globals */
199 static GObjectClass *parent_class = NULL;
200
201 static guint signals[LAST_SIGNAL] = {0}; 
202
203 GType
204 modest_folder_view_get_type (void)
205 {
206         static GType my_type = 0;
207         if (!my_type) {
208                 static const GTypeInfo my_info = {
209                         sizeof(ModestFolderViewClass),
210                         NULL,           /* base init */
211                         NULL,           /* base finalize */
212                         (GClassInitFunc) modest_folder_view_class_init,
213                         NULL,           /* class finalize */
214                         NULL,           /* class data */
215                         sizeof(ModestFolderView),
216                         1,              /* n_preallocs */
217                         (GInstanceInitFunc) modest_folder_view_init,
218                         NULL
219                 };
220
221                 static const GInterfaceInfo tny_account_store_view_info = {
222                         (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
223                         NULL,         /* interface_finalize */
224                         NULL          /* interface_data */
225                 };
226
227                                 
228                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
229                                                   "ModestFolderView",
230                                                   &my_info, 0);
231
232                 g_type_add_interface_static (my_type, 
233                                              TNY_TYPE_ACCOUNT_STORE_VIEW, 
234                                              &tny_account_store_view_info);
235         }
236         return my_type;
237 }
238
239 static void
240 modest_folder_view_class_init (ModestFolderViewClass *klass)
241 {
242         GObjectClass *gobject_class;
243         gobject_class = (GObjectClass*) klass;
244
245         parent_class            = g_type_class_peek_parent (klass);
246         gobject_class->finalize = modest_folder_view_finalize;
247
248         g_type_class_add_private (gobject_class,
249                                   sizeof(ModestFolderViewPrivate));
250         
251         signals[FOLDER_SELECTION_CHANGED_SIGNAL] = 
252                 g_signal_new ("folder_selection_changed",
253                               G_TYPE_FROM_CLASS (gobject_class),
254                               G_SIGNAL_RUN_FIRST,
255                               G_STRUCT_OFFSET (ModestFolderViewClass,
256                                                folder_selection_changed),
257                               NULL, NULL,
258                               modest_marshal_VOID__POINTER_BOOLEAN,
259                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
260
261         /*
262          * This signal is emitted whenever the currently selected
263          * folder display name is computed. Note that the name could
264          * be different to the folder name, because we could append
265          * the unread messages count to the folder name to build the
266          * folder display name
267          */
268         signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] = 
269                 g_signal_new ("folder-display-name-changed",
270                               G_TYPE_FROM_CLASS (gobject_class),
271                               G_SIGNAL_RUN_FIRST,
272                               G_STRUCT_OFFSET (ModestFolderViewClass,
273                                                folder_display_name_changed),
274                               NULL, NULL,
275                               g_cclosure_marshal_VOID__STRING,
276                               G_TYPE_NONE, 1, G_TYPE_STRING);
277 }
278
279 /* Simplify checks for NULLs: */
280 static gboolean
281 strings_are_equal (const gchar *a, const gchar *b)
282 {
283         if (!a && !b)
284                 return TRUE;
285         if (a && b)
286         {
287                 return (strcmp (a, b) == 0);
288         }
289         else
290                 return FALSE;
291 }
292
293 static gboolean
294 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path,  GtkTreeIter *iter, gpointer data)
295 {
296         GObject *instance = NULL;
297         
298         gtk_tree_model_get (model, iter,
299                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
300                             -1);
301                             
302         if (!instance)
303                 return FALSE; /* keep walking */
304                         
305         if (!TNY_IS_ACCOUNT (instance)) {
306                 g_object_unref (instance);
307                 return FALSE; /* keep walking */        
308         }    
309         
310         /* Check if this is the looked-for account: */
311         TnyAccount *this_account = TNY_ACCOUNT (instance);
312         TnyAccount *account = TNY_ACCOUNT (data);
313         
314         const gchar *this_account_id = tny_account_get_id(this_account);
315         const gchar *account_id = tny_account_get_id(account);
316         g_object_unref (instance);
317         instance = NULL;
318
319         /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
320         if (strings_are_equal(this_account_id, account_id)) {
321                 /* Tell the model that the data has changed, so that
322                  * it calls the cell_data_func callbacks again: */
323                 /* TODO: This does not seem to actually cause the new string to be shown: */
324                 gtk_tree_model_row_changed (model, path, iter);
325                 
326                 return TRUE; /* stop walking */
327         }
328         
329         return FALSE; /* keep walking */
330 }
331
332 typedef struct 
333 {
334         ModestFolderView *self;
335         gchar *previous_name;
336 } GetMmcAccountNameData;
337
338 static void
339 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
340 {
341         /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
342
343         GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
344         
345         if (!strings_are_equal (
346                 tny_account_get_name(TNY_ACCOUNT(account)), 
347                 data->previous_name)) {
348         
349                 /* Tell the model that the data has changed, so that 
350                  * it calls the cell_data_func callbacks again: */
351                 ModestFolderView *self = data->self;
352                 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
353                 if (model)
354                         gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
355         }
356
357         g_free (data->previous_name);
358         g_slice_free (GetMmcAccountNameData, data);
359 }
360
361 static void
362 text_cell_data  (GtkTreeViewColumn *column,  
363                  GtkCellRenderer *renderer,
364                  GtkTreeModel *tree_model,  
365                  GtkTreeIter *iter,  
366                  gpointer data)
367 {
368         ModestFolderViewPrivate *priv;
369         GObject *rendobj;
370         gchar *fname = NULL;
371         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
372         GObject *instance = NULL;
373
374         g_return_if_fail (column);
375         g_return_if_fail (tree_model);
376         g_return_if_fail (iter != NULL);
377
378         gtk_tree_model_get (tree_model, iter,
379                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
380                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
381                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
382                             -1);
383         rendobj = G_OBJECT(renderer);
384
385         if (!fname)
386                 return;
387
388         if (!instance) {
389                 g_free (fname);
390                 return;
391         }
392
393         ModestFolderView *self = MODEST_FOLDER_VIEW (data);
394         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
395         
396         gchar *item_name = NULL;
397         gint item_weight = 400;
398         
399         if (type != TNY_FOLDER_TYPE_ROOT) {
400                 gint number = 0;
401                 
402                 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
403                     modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
404                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
405                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
406                                 g_free (fname);
407                                 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
408                         }
409                 }
410
411                 /* note: we cannot reliably get the counts from the tree model, we need
412                  * to use explicit calls on tny_folder for some reason.
413                  */
414                 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
415                 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
416                     (type == TNY_FOLDER_TYPE_OUTBOX) ||
417                     (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
418                         number = tny_folder_get_all_count (TNY_FOLDER(instance));
419                 else
420                         number = tny_folder_get_unread_count (TNY_FOLDER(instance));
421                                                                 
422                 /* Use bold font style if there are unread or unset messages */
423                 if (number > 0) {
424                         item_name = g_strdup_printf ("%s (%d)", fname, number);
425                         item_weight = 800;
426                 } else {
427                         item_name = g_strdup (fname);
428                         item_weight = 400;
429                 }
430                 
431         } else if (TNY_IS_ACCOUNT (instance)) {
432                 /* If it's a server account */
433                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
434                         item_name = g_strdup (priv->local_account_name);
435                         item_weight = 800;
436                 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
437                         /* fname is only correct when the items are first 
438                          * added to the model, not when the account is 
439                          * changed later, so get the name from the account
440                          * instance: */
441                         item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
442                         item_weight = 800;
443                 } else {
444                         item_name = g_strdup (fname);
445                         item_weight = 800;
446                 }
447         }
448         
449         if (!item_name)
450                 item_name = g_strdup ("unknown");
451                         
452         if (item_name && item_weight) {
453                 /* Set the name in the treeview cell: */
454                 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
455                 
456                 /* Notify display name observers */
457                 /* TODO: What listens for this signal, and how can it use only the new name? */
458                 if (G_OBJECT (priv->cur_folder_store) == instance) {
459                         g_signal_emit (G_OBJECT(self),
460                                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
461                                                item_name);
462                 }
463                 g_free (item_name);
464                 
465         }
466         
467         /* If it is a Memory card account, make sure that we have the correct name.
468          * This function will be trigerred again when the name has been retrieved: */
469         if (TNY_IS_STORE_ACCOUNT (instance) && 
470                 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
471
472                 /* Get the account name asynchronously: */
473                 GetMmcAccountNameData *callback_data = 
474                         g_slice_new0(GetMmcAccountNameData);
475                 callback_data->self = self;
476
477                 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
478                 if (name)
479                         callback_data->previous_name = g_strdup (name); 
480
481                 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance), 
482                                                          on_get_mmc_account_name, callback_data);
483         }
484                         
485         g_object_unref (G_OBJECT (instance));
486         g_free (fname);
487 }
488
489 static void
490 icon_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
491                  GtkTreeModel *tree_model,  GtkTreeIter *iter, gpointer data)
492 {
493         GObject *rendobj = NULL, *instance = NULL;
494         GdkPixbuf *pixbuf = NULL;
495         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
496         const gchar *account_id = NULL;
497         gboolean has_children;
498
499         rendobj = G_OBJECT(renderer);
500         gtk_tree_model_get (tree_model, iter,
501                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
502                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
503                             -1);
504         has_children = gtk_tree_model_iter_has_child (tree_model, iter);
505
506         if (!instance) 
507                 return;
508
509         /* MERGE is not needed anymore as the folder now has the correct type jschmid */
510         /* We include the MERGE type here because it's used to create
511            the local OUTBOX folder */
512         if (type == TNY_FOLDER_TYPE_NORMAL || 
513             type == TNY_FOLDER_TYPE_UNKNOWN) {
514                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
515         }
516
517         switch (type) {
518         case TNY_FOLDER_TYPE_ROOT:
519                 if (TNY_IS_ACCOUNT (instance)) {
520                         
521                         if (modest_tny_account_is_virtual_local_folders (
522                                 TNY_ACCOUNT (instance))) {
523                                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
524                         }
525                         else {
526                                 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
527                                 
528                                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
529                                         pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
530                                 else
531                                         pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
532                         }
533                 }
534                 break;
535         case TNY_FOLDER_TYPE_INBOX:
536             pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
537             break;
538         case TNY_FOLDER_TYPE_OUTBOX:
539                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
540                 break;
541         case TNY_FOLDER_TYPE_JUNK:
542                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
543                 break;
544         case TNY_FOLDER_TYPE_SENT:
545                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
546                 break;
547         case TNY_FOLDER_TYPE_TRASH:
548                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
549                 break;
550         case TNY_FOLDER_TYPE_DRAFTS:
551                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
552                 break;
553         case TNY_FOLDER_TYPE_NORMAL:
554         default:
555                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
556                 break;
557         }
558         
559         g_object_unref (G_OBJECT (instance));
560
561         /* Set pixbuf */
562         g_object_set (rendobj, "pixbuf", pixbuf, NULL);
563         if (has_children && (pixbuf != NULL)) {
564                 GdkPixbuf *open_pixbuf, *closed_pixbuf;
565                 GdkPixbuf *open_emblem, *closed_emblem;
566                 open_pixbuf = gdk_pixbuf_copy (pixbuf);
567                 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
568                 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
569                 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
570
571                 if (open_emblem) {
572                         gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0, 
573                                               MIN (gdk_pixbuf_get_width (open_emblem), 
574                                                    gdk_pixbuf_get_width (open_pixbuf)),
575                                               MIN (gdk_pixbuf_get_height (open_emblem), 
576                                                    gdk_pixbuf_get_height (open_pixbuf)),
577                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
578                         g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
579                         g_object_unref (open_emblem);
580                 }
581                 if (closed_emblem) {
582                         gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0, 
583                                               MIN (gdk_pixbuf_get_width (closed_emblem), 
584                                                    gdk_pixbuf_get_width (closed_pixbuf)),
585                                               MIN (gdk_pixbuf_get_height (closed_emblem), 
586                                                    gdk_pixbuf_get_height (closed_pixbuf)),
587                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
588                         g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
589                         g_object_unref (closed_emblem);
590                 }
591                 if (closed_pixbuf)
592                         g_object_unref (closed_pixbuf);
593                 if (open_pixbuf)
594                         g_object_unref (open_pixbuf);
595         }
596
597         if (pixbuf != NULL)
598                 g_object_unref (pixbuf);
599 }
600
601 static void
602 add_columns (GtkWidget *treeview)
603 {
604         GtkTreeViewColumn *column;
605         GtkCellRenderer *renderer;
606         GtkTreeSelection *sel;
607
608         /* Create column */
609         column = gtk_tree_view_column_new ();   
610         
611         /* Set icon and text render function */
612         renderer = gtk_cell_renderer_pixbuf_new();
613         gtk_tree_view_column_pack_start (column, renderer, FALSE);
614         gtk_tree_view_column_set_cell_data_func(column, renderer,
615                                                 icon_cell_data, treeview, NULL);
616         
617         renderer = gtk_cell_renderer_text_new();
618         g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, 
619                         "ellipsize-set", TRUE, NULL);
620         gtk_tree_view_column_pack_start (column, renderer, TRUE);
621         gtk_tree_view_column_set_cell_data_func(column, renderer,
622                                                 text_cell_data, treeview, NULL);
623         
624         /* Set selection mode */
625         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
626         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
627
628         /* Set treeview appearance */
629         gtk_tree_view_column_set_spacing (column, 2);
630         gtk_tree_view_column_set_resizable (column, TRUE);
631         gtk_tree_view_column_set_fixed_width (column, TRUE);            
632         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
633         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
634
635         /* Add column */
636         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
637 }
638
639 static void
640 modest_folder_view_init (ModestFolderView *obj)
641 {
642         ModestFolderViewPrivate *priv;
643         ModestConf *conf;
644         
645         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
646         
647         priv->timer_expander = 0;
648         priv->account_store  = NULL;
649         priv->query          = NULL;
650         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
651         priv->cur_folder_store   = NULL;
652         priv->visible_account_id = NULL;
653         priv->folder_to_select = NULL;
654
655         priv->reexpand = TRUE;
656
657         /* Initialize the local account name */
658         conf = modest_runtime_get_conf();
659         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
660
661         /* Init email clipboard */
662         priv->clipboard = modest_runtime_get_email_clipboard ();
663         priv->hidding_ids = NULL;
664         priv->n_selected = 0;
665         priv->reselect = FALSE;
666         priv->show_non_move = TRUE;
667
668         /* Build treeview */
669         add_columns (GTK_WIDGET (obj));
670
671         /* Setup drag and drop */
672         setup_drag_and_drop (GTK_TREE_VIEW(obj));
673
674         /* Connect signals */
675         g_signal_connect (G_OBJECT (obj), 
676                           "key-press-event", 
677                           G_CALLBACK (on_key_pressed), NULL);
678
679         priv->display_name_changed_signal = 
680                 g_signal_connect (modest_runtime_get_account_mgr (),
681                                   "display_name_changed",
682                                   G_CALLBACK (on_display_name_changed),
683                                   obj);
684
685         /*
686          * Track changes in the local account name (in the device it
687          * will be the device name)
688          */
689         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf), 
690                                                   "key_changed",
691                                                   G_CALLBACK(on_configuration_key_changed), 
692                                                   obj);
693 }
694
695 static void
696 tny_account_store_view_init (gpointer g, gpointer iface_data)
697 {
698         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
699
700         klass->set_account_store_func = modest_folder_view_set_account_store;
701
702         return;
703 }
704
705 static void
706 modest_folder_view_finalize (GObject *obj)
707 {
708         ModestFolderViewPrivate *priv;
709         GtkTreeSelection    *sel;
710         
711         g_return_if_fail (obj);
712         
713         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
714
715         if (priv->timer_expander != 0) {
716                 g_source_remove (priv->timer_expander);
717                 priv->timer_expander = 0;
718         }
719
720         if (priv->account_store) {
721                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
722                                              priv->account_inserted_signal);
723                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
724                                              priv->account_removed_signal);
725                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
726                                              priv->account_changed_signal);
727                 g_object_unref (G_OBJECT(priv->account_store));
728                 priv->account_store = NULL;
729         }
730
731         if (priv->query) {
732                 g_object_unref (G_OBJECT (priv->query));
733                 priv->query = NULL;
734         }
735
736 /*      modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
737         if (priv->folder_to_select) {
738                 g_object_unref (G_OBJECT(priv->folder_to_select));
739                 priv->folder_to_select = NULL;
740         }
741    
742         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
743         if (sel)
744                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
745
746         g_free (priv->local_account_name);
747         g_free (priv->visible_account_id);
748         
749         if (priv->conf_key_signal) {
750                 g_signal_handler_disconnect (modest_runtime_get_conf (),
751                                              priv->conf_key_signal);
752                 priv->conf_key_signal = 0;
753         }
754
755         if (priv->cur_folder_store) {
756                 if (TNY_IS_FOLDER(priv->cur_folder_store))
757                         tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
758                         /* FALSE --> expunge the message */
759
760                 g_object_unref (priv->cur_folder_store);
761                 priv->cur_folder_store = NULL;
762         }
763
764         /* Clear hidding array created by cut operation */
765         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
766
767         G_OBJECT_CLASS(parent_class)->finalize (obj);
768 }
769
770
771 static void
772 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
773 {
774         ModestFolderViewPrivate *priv;
775         TnyDevice *device;
776
777         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
778         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
779
780         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
781         device = tny_account_store_get_device (account_store);
782
783         if (G_UNLIKELY (priv->account_store)) {
784
785                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
786                                                    priv->account_inserted_signal))
787                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
788                                                      priv->account_inserted_signal);
789                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
790                                                    priv->account_removed_signal))
791                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
792                                                      priv->account_removed_signal);
793                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
794                                                    priv->account_changed_signal))
795                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
796                                                      priv->account_changed_signal);
797                 g_object_unref (G_OBJECT (priv->account_store));
798         }
799
800         priv->account_store = g_object_ref (G_OBJECT (account_store));
801
802         priv->account_removed_signal = 
803                 g_signal_connect (G_OBJECT(account_store), "account_removed",
804                                   G_CALLBACK (on_account_removed), self);
805
806         priv->account_inserted_signal =
807                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
808                                   G_CALLBACK (on_account_inserted), self);
809
810         priv->account_changed_signal =
811                 g_signal_connect (G_OBJECT(account_store), "account_changed",
812                                   G_CALLBACK (on_account_changed), self);
813
814         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
815         
816         g_object_unref (G_OBJECT (device));
817 }
818
819 static void
820 on_connection_status_changed (TnyAccount *self, 
821                               TnyConnectionStatus status, 
822                               gpointer user_data)
823 {
824         /* If the account becomes online then refresh it */
825         if (status == TNY_CONNECTION_STATUS_CONNECTED) {
826                 const gchar *acc_name;
827                 GtkWidget *my_window;
828
829                 my_window = gtk_widget_get_ancestor (GTK_WIDGET (user_data), MODEST_TYPE_WINDOW);
830                 acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (self);
831                 modest_ui_actions_do_send_receive (acc_name, MODEST_WINDOW (my_window));
832         }
833 }
834
835 static void
836 on_account_inserted (TnyAccountStore *account_store, 
837                      TnyAccount *account,
838                      gpointer user_data)
839 {
840         ModestFolderViewPrivate *priv;
841         GtkTreeModel *sort_model, *filter_model;
842
843         /* Ignore transport account insertions, we're not showing them
844            in the folder view */
845         if (TNY_IS_TRANSPORT_ACCOUNT (account))
846                 return;
847
848         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
849
850         /* If we're adding a new account, and there is no previous
851            one, we need to select the visible server account */
852         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
853             !priv->visible_account_id)
854                 modest_widget_memory_restore (modest_runtime_get_conf(), 
855                                               G_OBJECT (user_data),
856                                               MODEST_CONF_FOLDER_VIEW_KEY);
857
858         /* Get the inner model */
859         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
860         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
861
862         /* Insert the account in the model */
863         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
864                          G_OBJECT (account));
865
866
867         /* When the store account gets online refresh it */
868         g_signal_connect (account, "connection_status_changed", 
869                           G_CALLBACK (on_connection_status_changed), 
870                           MODEST_FOLDER_VIEW (user_data));
871
872         /* Refilter the model */
873         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
874 }
875
876
877 static void
878 on_account_changed (TnyAccountStore *account_store, 
879                     TnyAccount *tny_account,
880                     gpointer user_data)
881 {
882         /* do nothing */
883         ModestFolderViewPrivate *priv;
884         GtkTreeModel *sort_model, *filter_model;
885
886         /* Ignore transport account insertions, we're not showing them
887            in the folder view */
888         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
889                 return;
890
891         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
892
893         /* Get the inner model */
894         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
895         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
896         
897         /* Remove the account from the model */
898         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
899                          G_OBJECT (tny_account));
900
901         /* Insert the account in the model */
902         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
903                          G_OBJECT (tny_account));
904
905         /* Refilter the model */
906         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
907 }
908
909
910
911 static void
912 on_account_removed (TnyAccountStore *account_store, 
913                     TnyAccount *account,
914                     gpointer user_data)
915 {
916         ModestFolderView *self = NULL;
917         ModestFolderViewPrivate *priv;
918         GtkTreeModel *sort_model, *filter_model;
919         GtkTreeSelection *sel = NULL;
920
921         /* Ignore transport account removals, we're not showing them
922            in the folder view */
923         if (TNY_IS_TRANSPORT_ACCOUNT (account))
924                 return;
925
926         self = MODEST_FOLDER_VIEW (user_data);
927         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
928
929         /* Invalidate the cur_folder_store only if the selected folder
930            belongs to the account that is being removed */
931         if (priv->cur_folder_store) {
932                 TnyAccount *selected_folder_account = NULL;
933
934                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
935                         selected_folder_account = 
936                                 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
937                 } else {
938                         selected_folder_account = 
939                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
940                 }
941
942                 if (selected_folder_account == account) {
943                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
944                         gtk_tree_selection_unselect_all (sel);
945                 }
946                 g_object_unref (selected_folder_account);
947         }
948
949         /* Invalidate row to select only if the folder to select
950            belongs to the account that is being removed*/
951         if (priv->folder_to_select) {
952                 TnyAccount *folder_to_select_account = NULL;
953
954                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
955                 if (folder_to_select_account == account) {
956 /*                      modest_folder_view_disable_next_folder_selection (self); */
957                         g_object_unref (priv->folder_to_select);
958                         priv->folder_to_select = NULL;
959                 }
960                 g_object_unref (folder_to_select_account);
961         }
962
963         /* Remove the account from the model */
964         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
965         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
966         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
967                          G_OBJECT (account));
968
969         /* If the removed account is the currently viewed one then
970            clear the configuration value. The new visible account will be the default account */
971         if (priv->visible_account_id &&
972             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
973
974                 /* Clear the current visible account_id */
975                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
976
977                 /* Call the restore method, this will set the new visible account */
978                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
979                                               MODEST_CONF_FOLDER_VIEW_KEY);
980         }
981
982         /* Refilter the model */
983         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
984
985         /* Select the first INBOX if the currently selected folder
986            belongs to the account that is being deleted */
987         if (priv->cur_folder_store) {
988                 TnyAccount *folder_selected_account;
989
990                 folder_selected_account = (TNY_IS_FOLDER (priv->cur_folder_store)) ?
991                         modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store)) :
992                         TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
993                 if (account == folder_selected_account)
994                         modest_folder_view_select_first_inbox_or_local (self);
995                 g_object_unref (folder_selected_account);
996         }
997 }
998
999 void
1000 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1001 {
1002         GtkTreeViewColumn *col;
1003         
1004         g_return_if_fail (self);
1005
1006         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1007         if (!col) {
1008                 g_printerr ("modest: failed get column for title\n");
1009                 return;
1010         }
1011
1012         gtk_tree_view_column_set_title (col, title);
1013         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1014                                            title != NULL);
1015 }
1016
1017 static gboolean
1018 modest_folder_view_on_map (ModestFolderView *self, 
1019                            GdkEventExpose *event,
1020                            gpointer data)
1021 {
1022         ModestFolderViewPrivate *priv;
1023
1024         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1025
1026         /* This won't happen often */
1027         if (G_UNLIKELY (priv->reselect)) {
1028                 /* Select the first inbox or the local account if not found */
1029
1030                 /* TODO: this could cause a lock at startup, so we
1031                    comment it for the moment. We know that this will
1032                    be a bug, because the INBOX is not selected, but we
1033                    need to rewrite some parts of Modest to avoid the
1034                    deathlock situation */
1035                 /* TODO: check if this is still the case */
1036                 priv->reselect = FALSE;
1037                 modest_folder_view_select_first_inbox_or_local (self);
1038                 /* Notify the display name observers */
1039                 g_signal_emit (G_OBJECT(self),
1040                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1041                                NULL);
1042         }
1043
1044         if (priv->reexpand) {
1045                 expand_root_items (self); 
1046                 priv->reexpand = FALSE;
1047         }
1048
1049         return FALSE;
1050 }
1051
1052 GtkWidget*
1053 modest_folder_view_new (TnyFolderStoreQuery *query)
1054 {
1055         GObject *self;
1056         ModestFolderViewPrivate *priv;
1057         GtkTreeSelection *sel;
1058         
1059         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1060         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1061
1062         if (query)
1063                 priv->query = g_object_ref (query);
1064         
1065         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1066         priv->changed_signal = g_signal_connect (sel, "changed",
1067                                                  G_CALLBACK (on_selection_changed), self);
1068
1069         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1070
1071         return GTK_WIDGET(self);
1072 }
1073
1074 /* this feels dirty; any other way to expand all the root items? */
1075 static void
1076 expand_root_items (ModestFolderView *self)
1077 {
1078         GtkTreePath *path;
1079         GtkTreeModel *model;
1080         GtkTreeIter iter;
1081
1082         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1083         path = gtk_tree_path_new_first ();
1084
1085         /* all folders should have child items, so.. */
1086         do {
1087                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1088                 gtk_tree_path_next (path);
1089         } while (gtk_tree_model_get_iter (model, &iter, path));
1090         
1091         gtk_tree_path_free (path);
1092 }
1093
1094 /*
1095  * We use this function to implement the
1096  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1097  * account in this case, and the local folders.
1098  */
1099 static gboolean 
1100 filter_row (GtkTreeModel *model,
1101             GtkTreeIter *iter,
1102             gpointer data)
1103 {
1104         ModestFolderViewPrivate *priv;
1105         gboolean retval = TRUE;
1106         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1107         GObject *instance = NULL;
1108         const gchar *id = NULL;
1109         guint i;
1110         gboolean found = FALSE;
1111         gboolean cleared = FALSE;
1112
1113         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1114         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1115
1116         gtk_tree_model_get (model, iter,
1117                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1118                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1119                             -1);
1120
1121         /* Do not show if there is no instance, this could indeed
1122            happen when the model is being modified while it's being
1123            drawn. This could occur for example when moving folders
1124            using drag&drop */
1125         if (!instance)
1126                 return FALSE;
1127
1128         if (type == TNY_FOLDER_TYPE_ROOT) {
1129                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1130                    account instead of a folder. */
1131                 if (TNY_IS_ACCOUNT (instance)) {
1132                         TnyAccount *acc = TNY_ACCOUNT (instance);
1133                         const gchar *account_id = tny_account_get_id (acc);
1134         
1135                         /* If it isn't a special folder, 
1136                          * don't show it unless it is the visible account: */
1137                         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1138                             !modest_tny_account_is_virtual_local_folders (acc) &&
1139                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1140                                 
1141                                 /* Show only the visible account id */
1142                                 if (priv->visible_account_id) {
1143                                         if (strcmp (account_id, priv->visible_account_id))
1144                                                 retval = FALSE;
1145                                 } else {
1146                                         retval = FALSE;
1147                                 }                               
1148                         }
1149                         
1150                         /* Never show these to the user. They are merged into one folder 
1151                          * in the local-folders account instead: */
1152                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1153                                 retval = FALSE;
1154                 }
1155         }
1156
1157         /* Check hiding (if necessary) */
1158         cleared = modest_email_clipboard_cleared (priv->clipboard);            
1159         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1160                 id = tny_folder_get_id (TNY_FOLDER(instance));
1161                 if (priv->hidding_ids != NULL)
1162                         for (i=0; i < priv->n_selected && !found; i++)
1163                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1164                                         found = (!strcmp (priv->hidding_ids[i], id));
1165                 
1166                 retval = !found;
1167         }
1168         
1169         
1170         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1171         folder as no message can be move there according to UI specs */
1172         if (!priv->show_non_move) {
1173                 switch (type) {
1174                         case TNY_FOLDER_TYPE_OUTBOX:
1175                         case TNY_FOLDER_TYPE_SENT:
1176                         case TNY_FOLDER_TYPE_DRAFTS:
1177                                 retval = FALSE;
1178                                 break;
1179                         case TNY_FOLDER_TYPE_UNKNOWN:
1180                         case TNY_FOLDER_TYPE_NORMAL:
1181                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1182                                 if (type == TNY_FOLDER_TYPE_OUTBOX || 
1183                                     type == TNY_FOLDER_TYPE_SENT
1184                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1185                                         retval = FALSE;
1186                                 break;
1187                         default:
1188                                 break;
1189                 }
1190         }
1191         
1192         /* Free */
1193         g_object_unref (instance);
1194
1195         return retval;
1196 }
1197
1198
1199 gboolean
1200 modest_folder_view_update_model (ModestFolderView *self,
1201                                  TnyAccountStore *account_store)
1202 {
1203         ModestFolderViewPrivate *priv;
1204         GtkTreeModel *model /* , *old_model */;                                                    
1205         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1206
1207         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1208         g_return_val_if_fail (account_store, FALSE);
1209
1210         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1211         
1212         /* Notify that there is no folder selected */
1213         g_signal_emit (G_OBJECT(self), 
1214                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1215                        NULL, FALSE);
1216         if (priv->cur_folder_store) {
1217                 g_object_unref (priv->cur_folder_store);
1218                 priv->cur_folder_store = NULL;
1219         }
1220
1221         /* FIXME: the local accounts are not shown when the query
1222            selects only the subscribed folders */
1223         model        = tny_gtk_folder_store_tree_model_new (NULL);
1224
1225         /* Get the accounts: */
1226         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1227                                         TNY_LIST (model),
1228                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1229
1230         sortable = gtk_tree_model_sort_new_with_model (model);
1231         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1232                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
1233                                               GTK_SORT_ASCENDING);
1234         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1235                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1236                                          cmp_rows, NULL, NULL);
1237
1238         /* Create filter model */
1239         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1240         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1241                                                 filter_row,
1242                                                 self,
1243                                                 NULL);
1244
1245         /* Set new model */
1246         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1247         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1248                           (GCallback) on_row_inserted_maybe_select_folder, self);
1249
1250
1251         g_object_unref (model);
1252         g_object_unref (filter_model);          
1253         g_object_unref (sortable);
1254         
1255         /* Force a reselection of the INBOX next time the widget is shown */
1256         priv->reselect = TRUE;
1257                         
1258         return TRUE;
1259 }
1260
1261
1262 static void
1263 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1264 {
1265         GtkTreeModel *model = NULL;
1266         TnyFolderStore *folder = NULL;
1267         GtkTreeIter iter;
1268         ModestFolderView *tree_view = NULL;
1269         ModestFolderViewPrivate *priv = NULL;
1270         gboolean selected = FALSE;
1271
1272         g_return_if_fail (sel);
1273         g_return_if_fail (user_data);
1274         
1275         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1276
1277         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1278
1279         /* Notify the display name observers */
1280         g_signal_emit (G_OBJECT(user_data),
1281                        signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1282                        NULL);
1283
1284         tree_view = MODEST_FOLDER_VIEW (user_data);
1285
1286         if (selected) {
1287                 gtk_tree_model_get (model, &iter,
1288                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1289                                     -1);
1290
1291                 /* If the folder is the same do not notify */
1292                 if (folder && priv->cur_folder_store == folder) {
1293                         g_object_unref (folder);
1294                         return;
1295                 }
1296         }
1297         
1298         /* Current folder was unselected */
1299         if (priv->cur_folder_store) {
1300                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1301                        priv->cur_folder_store, FALSE);
1302
1303                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1304                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1305                                                FALSE, NULL, NULL, NULL);
1306
1307                 /* FALSE --> don't expunge the messages */
1308
1309                 g_object_unref (priv->cur_folder_store);
1310                 priv->cur_folder_store = NULL;
1311         }
1312
1313         /* New current references */
1314         priv->cur_folder_store = folder;
1315
1316         /* New folder has been selected */
1317         g_signal_emit (G_OBJECT(tree_view),
1318                        signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1319                        0, priv->cur_folder_store, TRUE);
1320 }
1321
1322 TnyFolderStore *
1323 modest_folder_view_get_selected (ModestFolderView *self)
1324 {
1325         ModestFolderViewPrivate *priv;
1326
1327         g_return_val_if_fail (self, NULL);
1328         
1329         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1330         if (priv->cur_folder_store)
1331                 g_object_ref (priv->cur_folder_store);
1332
1333         return priv->cur_folder_store;
1334 }
1335
1336 static gint
1337 get_cmp_rows_type_pos (GObject *folder)
1338 {
1339         /* Remote accounts -> Local account -> MMC account .*/
1340         /* 0, 1, 2 */
1341         
1342         if (TNY_IS_ACCOUNT (folder) && 
1343                 modest_tny_account_is_virtual_local_folders (
1344                         TNY_ACCOUNT (folder))) {
1345                 return 1;
1346         } else if (TNY_IS_ACCOUNT (folder)) {
1347                 TnyAccount *account = TNY_ACCOUNT (folder);
1348                 const gchar *account_id = tny_account_get_id (account);
1349                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1350                         return 2;
1351                 else
1352                         return 0;
1353         }
1354         else {
1355                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1356                 return -1; /* Should never happen */
1357         }
1358 }
1359
1360 static gint
1361 get_cmp_subfolder_type_pos (TnyFolderType t)
1362 {
1363         /* Inbox, Outbox, Drafts, Sent, User */
1364         /* 0, 1, 2, 3, 4 */
1365
1366         switch (t) {
1367         case TNY_FOLDER_TYPE_INBOX:
1368                 return 0;
1369                 break;
1370         case TNY_FOLDER_TYPE_OUTBOX:
1371                 return 1;
1372                 break;
1373         case TNY_FOLDER_TYPE_DRAFTS:
1374                 return 2;
1375                 break;
1376         case TNY_FOLDER_TYPE_SENT:
1377                 return 3;
1378                 break;
1379         default:
1380                 return 4;
1381         }
1382 }
1383
1384 /*
1385  * This function orders the mail accounts according to these rules:
1386  * 1st - remote accounts
1387  * 2nd - local account
1388  * 3rd - MMC account
1389  */
1390 static gint
1391 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1392           gpointer user_data)
1393 {
1394         gint cmp = 0;
1395         gchar *name1 = NULL;
1396         gchar *name2 = NULL;
1397         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1398         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1399         GObject *folder1 = NULL;
1400         GObject *folder2 = NULL;
1401
1402         gtk_tree_model_get (tree_model, iter1,
1403                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1404                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1405                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1406                             -1);
1407         gtk_tree_model_get (tree_model, iter2,
1408                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1409                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1410                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1411                             -1);
1412
1413         /* Return if we get no folder. This could happen when folder
1414            operations are happening. The model is updated after the
1415            folder copy/move actually occurs, so there could be
1416            situations where the model to be drawn is not correct */
1417         if (!folder1 || !folder2)
1418                 goto finish;
1419
1420         if (type == TNY_FOLDER_TYPE_ROOT) {
1421                 /* Compare the types, so that 
1422                  * Remote accounts -> Local account -> MMC account .*/
1423                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1424                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1425                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1426                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1427                 if (pos1 <  pos2)
1428                         cmp = -1;
1429                 else if (pos1 > pos2)
1430                         cmp = 1;
1431                 else {
1432                         /* Compare items of the same type: */
1433                         
1434                         TnyAccount *account1 = NULL;
1435                         if (TNY_IS_ACCOUNT (folder1))
1436                                 account1 = TNY_ACCOUNT (folder1);
1437                                 
1438                         TnyAccount *account2 = NULL;
1439                         if (TNY_IS_ACCOUNT (folder2))
1440                                 account2 = TNY_ACCOUNT (folder2);
1441                                 
1442                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1443                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1444         
1445                         if (!account_id && !account_id2) {
1446                                 cmp = 0;
1447                         } else if (!account_id) {
1448                                 cmp = -1;
1449                         } else if (!account_id2) {
1450                                 cmp = +1;
1451                         } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1452                                 cmp = +1;
1453                         } else {
1454                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1455                         }
1456                 }
1457         } else {
1458                 gint cmp1 = 0, cmp2 = 0;
1459                 /* get the parent to know if it's a local folder */
1460
1461                 GtkTreeIter parent;
1462                 gboolean has_parent;
1463                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1464                 if (has_parent) {
1465                         GObject *parent_folder;
1466                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1467                         gtk_tree_model_get (tree_model, &parent, 
1468                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1469                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1470                                             -1);
1471                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1472                             TNY_IS_ACCOUNT (parent_folder) &&
1473                             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1474                                 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1475                                 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1476                         }
1477                         g_object_unref (parent_folder);
1478                 }
1479
1480                 /* if they are not local folders */
1481                 if (cmp1 == cmp2) {
1482                         cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1483                         cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1484                 }
1485
1486                 if (cmp1 == cmp2)
1487                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1488                 else 
1489                         cmp = (cmp1 - cmp2);
1490         }
1491
1492 finish: 
1493         if (folder1)
1494                 g_object_unref(G_OBJECT(folder1));
1495         if (folder2)
1496                 g_object_unref(G_OBJECT(folder2));
1497
1498         g_free (name1);
1499         g_free (name2);
1500
1501         return cmp;     
1502 }
1503
1504 /*****************************************************************************/
1505 /*                        DRAG and DROP stuff                                */
1506 /*****************************************************************************/
1507 /*
1508  * This function fills the #GtkSelectionData with the row and the
1509  * model that has been dragged. It's called when this widget is a
1510  * source for dnd after the event drop happened
1511  */
1512 static void
1513 on_drag_data_get (GtkWidget *widget, 
1514                   GdkDragContext *context, 
1515                   GtkSelectionData *selection_data, 
1516                   guint info, 
1517                   guint time, 
1518                   gpointer data)
1519 {
1520         GtkTreeSelection *selection;
1521         GtkTreeModel *model;
1522         GtkTreeIter iter;
1523         GtkTreePath *source_row;
1524
1525         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1526         gtk_tree_selection_get_selected (selection, &model, &iter);
1527         source_row = gtk_tree_model_get_path (model, &iter);
1528
1529         gtk_tree_set_row_drag_data (selection_data,
1530                                     model,
1531                                     source_row);
1532
1533         gtk_tree_path_free (source_row);
1534 }
1535
1536 typedef struct _DndHelper {
1537         gboolean delete_source;
1538         GtkTreePath *source_row;
1539         GdkDragContext *context;
1540         guint time;
1541 } DndHelper;
1542
1543
1544 /*
1545  * This function is the callback of the
1546  * modest_mail_operation_xfer_msgs () and
1547  * modest_mail_operation_xfer_folder() calls. We check here if the
1548  * message/folder was correctly asynchronously transferred. The reason
1549  * to use the same callback is that the code is the same, it only has
1550  * to check that the operation went fine and then finalize the drag
1551  * and drop action
1552  */
1553 static void
1554 xfer_cb (ModestMailOperation *mail_op, 
1555          gpointer user_data)
1556 {
1557         gboolean success;
1558         DndHelper *helper;
1559
1560         helper = (DndHelper *) user_data;
1561
1562         if (modest_mail_operation_get_status (mail_op) == 
1563             MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1564                 success = TRUE;
1565         } else {
1566                 success = FALSE;
1567         }
1568
1569         /* Notify the drag source. Never call delete, the monitor will
1570            do the job if needed */
1571         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1572
1573         /* Free the helper */
1574         gtk_tree_path_free (helper->source_row);
1575         g_slice_free (DndHelper, helper);
1576 }
1577
1578 /* get the folder for the row the treepath refers to. */
1579 /* folder must be unref'd */
1580 static TnyFolderStore *
1581 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1582 {
1583         GtkTreeIter iter;
1584         TnyFolderStore *folder = NULL;
1585         
1586         if (gtk_tree_model_get_iter (model,&iter, path))
1587                 gtk_tree_model_get (model, &iter,
1588                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1589                                     -1);
1590         return folder;
1591 }
1592
1593 /*
1594  * This function is used by drag_data_received_cb to manage drag and
1595  * drop of a header, i.e, and drag from the header view to the folder
1596  * view.
1597  */
1598 static void
1599 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1600                                 GtkTreeModel *dest_model,
1601                                 GtkTreePath  *dest_row,
1602                                 GtkSelectionData *selection_data,
1603                                 DndHelper    *helper)
1604 {
1605         TnyList *headers = NULL;
1606         TnyFolder *folder = NULL;
1607         ModestMailOperation *mail_op = NULL;
1608         GtkTreeIter source_iter, dest_iter;
1609         ModestWindowMgr *mgr = NULL;
1610         ModestWindow *main_win = NULL;
1611         gchar **uris, **tmp;
1612         gint response;
1613
1614         /* Build the list of headers */
1615         mgr = modest_runtime_get_window_mgr ();
1616         headers = tny_simple_list_new ();
1617         uris = modest_dnd_selection_data_get_paths (selection_data);
1618         tmp = uris;
1619
1620         while (*tmp != NULL) {
1621                 TnyHeader *header;
1622                 GtkTreePath *path;
1623
1624                 /* Get header */
1625                 path = gtk_tree_path_new_from_string (*tmp);
1626                 gtk_tree_model_get_iter (source_model, &source_iter, path);
1627                 gtk_tree_model_get (source_model, &source_iter, 
1628                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1629                                     &header, -1);
1630
1631                 /* Do not enable d&d of headers already opened */
1632                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
1633                         tny_list_append (headers, G_OBJECT (header));
1634
1635                 /* Free and go on */
1636                 gtk_tree_path_free (path);
1637                 g_object_unref (header);
1638                 tmp++;
1639         }
1640         g_strfreev (uris);
1641
1642         /* Get the target folder */
1643         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1644         gtk_tree_model_get (dest_model, &dest_iter, 
1645                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1646                             &folder, -1);
1647
1648         /* Ask for confirmation to move */
1649         main_win = modest_window_mgr_get_main_window (mgr);
1650         response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder, 
1651                                                                 TRUE, headers);
1652         if (response == GTK_RESPONSE_CANCEL)
1653                 goto cleanup;
1654
1655         /* Transfer messages */
1656         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
1657                                                                  modest_ui_actions_move_folder_error_handler,
1658                                                                  NULL, NULL);
1659
1660         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1661                                          mail_op);
1662
1663         modest_mail_operation_xfer_msgs (mail_op,
1664                                          headers, 
1665                                          folder, 
1666                                          helper->delete_source, 
1667                                          xfer_cb, helper);
1668         
1669         /* Frees */
1670 cleanup:
1671         if (G_IS_OBJECT(mail_op))
1672                 g_object_unref (G_OBJECT (mail_op));
1673         if (G_IS_OBJECT(folder))
1674                 g_object_unref (G_OBJECT (folder));
1675         if (G_IS_OBJECT(headers))
1676                 g_object_unref (headers);
1677 }
1678
1679 /*
1680  * This function is used by drag_data_received_cb to manage drag and
1681  * drop of a folder, i.e, and drag from the folder view to the same
1682  * folder view.
1683  */
1684 static void
1685 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
1686                                 GtkTreeModel     *dest_model,
1687                                 GtkTreePath      *dest_row,
1688                                 GtkSelectionData *selection_data,
1689                                 DndHelper        *helper)
1690 {
1691         ModestMailOperation *mail_op = NULL;
1692         GtkTreeIter dest_iter, iter;
1693         TnyFolderStore *dest_folder = NULL;
1694         TnyFolderStore *folder = NULL;
1695         gboolean forbidden = FALSE;
1696
1697         if (!forbidden) {
1698                 /* check the folder rules for the destination */
1699                 folder = tree_path_to_folder (dest_model, dest_row);
1700                 if (TNY_IS_FOLDER(folder)) {
1701                         ModestTnyFolderRules rules =
1702                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
1703                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1704                 } else if (TNY_IS_FOLDER_STORE(folder)) {
1705                         /* enable local root as destination for folders */
1706                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1707                                         && TNY_IS_ACCOUNT (folder))
1708                                 forbidden = TRUE;
1709                 }
1710                 g_object_unref (folder);
1711         }
1712         if (!forbidden) {
1713                 /* check the folder rules for the source */
1714                 folder = tree_path_to_folder (source_model, helper->source_row);
1715                 if (TNY_IS_FOLDER(folder)) {
1716                         ModestTnyFolderRules rules =
1717                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
1718                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1719                 } else
1720                         forbidden = TRUE;
1721                 g_object_unref (folder);
1722         }
1723
1724         
1725         /* Check if the drag is possible */
1726         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1727                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1728                 gtk_tree_path_free (helper->source_row);        
1729                 g_slice_free (DndHelper, helper);
1730                 return;
1731         }
1732
1733         /* Get data */
1734         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1735         gtk_tree_model_get (dest_model, &dest_iter, 
1736                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1737                             &dest_folder, -1);
1738         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1739         gtk_tree_model_get (source_model, &iter,
1740                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1741                             &folder, -1);
1742
1743         /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1744         if (modest_platform_connect_and_wait_if_network_folderstore (NULL, dest_folder) && 
1745             modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1746                 ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1747
1748                 /* Do the mail operation */
1749                 mail_op = 
1750                         modest_mail_operation_new_with_error_handling ((GObject *) modest_window_mgr_get_main_window (mgr),
1751                                                                        modest_ui_actions_move_folder_error_handler,
1752                                                                        folder, NULL);
1753
1754                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), 
1755                                                  mail_op);
1756
1757                 modest_mail_operation_xfer_folder (mail_op, 
1758                                                    TNY_FOLDER (folder), 
1759                                                    dest_folder,
1760                                                    helper->delete_source,
1761                                                    xfer_cb,
1762                                                    helper);
1763
1764                 g_object_unref (G_OBJECT (mail_op));
1765         }
1766         
1767         /* Frees */
1768         g_object_unref (G_OBJECT (dest_folder));
1769         g_object_unref (G_OBJECT (folder));
1770 }
1771
1772 /*
1773  * This function receives the data set by the "drag-data-get" signal
1774  * handler. This information comes within the #GtkSelectionData. This
1775  * function will manage both the drags of folders of the treeview and
1776  * drags of headers of the header view widget.
1777  */
1778 static void 
1779 on_drag_data_received (GtkWidget *widget, 
1780                        GdkDragContext *context, 
1781                        gint x, 
1782                        gint y, 
1783                        GtkSelectionData *selection_data, 
1784                        guint target_type, 
1785                        guint time, 
1786                        gpointer data)
1787 {
1788         GtkWidget *source_widget;
1789         GtkTreeModel *dest_model, *source_model;
1790         GtkTreePath *source_row, *dest_row;
1791         GtkTreeViewDropPosition pos;
1792         gboolean success = FALSE, delete_source = FALSE;
1793         DndHelper *helper = NULL; 
1794
1795         /* Do not allow further process */
1796         g_signal_stop_emission_by_name (widget, "drag-data-received");
1797         source_widget = gtk_drag_get_source_widget (context);
1798
1799         /* Get the action */
1800         if (context->action == GDK_ACTION_MOVE) {
1801                 delete_source = TRUE;
1802
1803                 /* Notify that there is no folder selected. We need to
1804                    do this in order to update the headers view (and
1805                    its monitors, because when moving, the old folder
1806                    won't longer exist. We can not wait for the end of
1807                    the operation, because the operation won't start if
1808                    the folder is in use */
1809                 if (source_widget == widget) {
1810                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1811                         gtk_tree_selection_unselect_all (sel);
1812                 }
1813         }
1814
1815         /* Check if the get_data failed */
1816         if (selection_data == NULL || selection_data->length < 0)
1817                 gtk_drag_finish (context, success, FALSE, time);
1818
1819         /* Select the destination model */
1820         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));  
1821
1822         /* Get the path to the destination row. Can not call
1823            gtk_tree_view_get_drag_dest_row() because the source row
1824            is not selected anymore */
1825         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1826                                            &dest_row, &pos);
1827
1828         /* Only allow drops IN other rows */
1829         if (!dest_row || 
1830             pos == GTK_TREE_VIEW_DROP_BEFORE || 
1831             pos == GTK_TREE_VIEW_DROP_AFTER)
1832                 gtk_drag_finish (context, success, FALSE, time);
1833
1834         /* Create the helper */
1835         helper = g_slice_new0 (DndHelper);
1836         helper->delete_source = delete_source;
1837         helper->context = context;
1838         helper->time = time;
1839
1840         /* Drags from the header view */
1841         if (source_widget != widget) {
1842                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
1843
1844                 drag_and_drop_from_header_view (source_model,
1845                                                 dest_model,
1846                                                 dest_row,
1847                                                 selection_data,
1848                                                 helper);
1849         } else {
1850                 /* Get the source model and row */
1851                 gtk_tree_get_row_drag_data (selection_data,
1852                                             &source_model,
1853                                             &source_row);
1854                 helper->source_row = gtk_tree_path_copy (source_row);
1855
1856                 drag_and_drop_from_folder_view (source_model,
1857                                                 dest_model,
1858                                                 dest_row,
1859                                                 selection_data, 
1860                                                 helper);
1861
1862                 gtk_tree_path_free (source_row);
1863         }
1864
1865         /* Frees */
1866         gtk_tree_path_free (dest_row);
1867 }
1868
1869 /*
1870  * We define a "drag-drop" signal handler because we do not want to
1871  * use the default one, because the default one always calls
1872  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1873  * signal handler, because there we have all the information available
1874  * to know if the dnd was a success or not.
1875  */
1876 static gboolean
1877 drag_drop_cb (GtkWidget      *widget,
1878               GdkDragContext *context,
1879               gint            x,
1880               gint            y,
1881               guint           time,
1882               gpointer        user_data) 
1883 {
1884         gpointer target;
1885
1886         if (!context->targets)
1887                 return FALSE;
1888
1889         /* Check if we're dragging a folder row */
1890         target = gtk_drag_dest_find_target (widget, context, NULL);
1891
1892         /* Request the data from the source. */
1893         gtk_drag_get_data(widget, context, target, time);
1894
1895     return TRUE;
1896 }
1897
1898 /*
1899  * This function expands a node of a tree view if it's not expanded
1900  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1901  * does that, so that's why they're here.
1902  */
1903 static gint
1904 expand_row_timeout (gpointer data)
1905 {
1906         GtkTreeView *tree_view = data;
1907         GtkTreePath *dest_path = NULL;
1908         GtkTreeViewDropPosition pos;
1909         gboolean result = FALSE;
1910         
1911         GDK_THREADS_ENTER ();
1912         
1913         gtk_tree_view_get_drag_dest_row (tree_view,
1914                                          &dest_path,
1915                                          &pos);
1916         
1917         if (dest_path &&
1918             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1919              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1920                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1921                 gtk_tree_path_free (dest_path);
1922         }
1923         else {
1924                 if (dest_path)
1925                         gtk_tree_path_free (dest_path);
1926                 
1927                 result = TRUE;
1928         }
1929         
1930         GDK_THREADS_LEAVE ();
1931
1932         return result;
1933 }
1934
1935 /*
1936  * This function is called whenever the pointer is moved over a widget
1937  * while dragging some data. It installs a timeout that will expand a
1938  * node of the treeview if not expanded yet. This function also calls
1939  * gdk_drag_status in order to set the suggested action that will be
1940  * used by the "drag-data-received" signal handler to know if we
1941  * should do a move or just a copy of the data.
1942  */
1943 static gboolean
1944 on_drag_motion (GtkWidget      *widget,
1945                 GdkDragContext *context,
1946                 gint            x,
1947                 gint            y,
1948                 guint           time,
1949                 gpointer        user_data)  
1950 {
1951         GtkTreeViewDropPosition pos;
1952         GtkTreePath *dest_row;
1953         GtkTreeModel *dest_model;
1954         ModestFolderViewPrivate *priv;
1955         GdkDragAction suggested_action;
1956         gboolean valid_location = FALSE;
1957         TnyFolderStore *folder;
1958
1959         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1960
1961         if (priv->timer_expander != 0) {
1962                 g_source_remove (priv->timer_expander);
1963                 priv->timer_expander = 0;
1964         }
1965
1966         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1967                                            x, y,
1968                                            &dest_row,
1969                                            &pos);
1970
1971         /* Do not allow drops between folders */
1972         if (!dest_row ||
1973             pos == GTK_TREE_VIEW_DROP_BEFORE ||
1974             pos == GTK_TREE_VIEW_DROP_AFTER) {
1975                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1976                 gdk_drag_status(context, 0, time);
1977                 valid_location = FALSE;
1978                 goto out;
1979         } else {
1980                 valid_location = TRUE;
1981         }
1982
1983         /* Check that the destination folder is writable */
1984         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1985         folder = tree_path_to_folder (dest_model, dest_row);
1986         if (folder && TNY_IS_FOLDER (folder)) {
1987                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
1988
1989                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1990                         valid_location = FALSE;
1991                         goto out;
1992                 }
1993         }
1994         g_object_unref (folder);
1995
1996         /* Expand the selected row after 1/2 second */
1997         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1998                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1999                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2000         }
2001
2002         /* Select the desired action. By default we pick MOVE */
2003         suggested_action = GDK_ACTION_MOVE;
2004
2005         if (context->actions == GDK_ACTION_COPY)
2006             gdk_drag_status(context, GDK_ACTION_COPY, time);
2007         else if (context->actions == GDK_ACTION_MOVE)
2008             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2009         else if (context->actions & suggested_action)
2010             gdk_drag_status(context, suggested_action, time);
2011         else
2012             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2013
2014  out:
2015         if (dest_row)
2016                 gtk_tree_path_free (dest_row);
2017         g_signal_stop_emission_by_name (widget, "drag-motion");
2018
2019         return valid_location;
2020 }
2021
2022 /*
2023  * This function sets the treeview as a source and a target for dnd
2024  * events. It also connects all the requirede signals.
2025  */
2026 static void
2027 setup_drag_and_drop (GtkTreeView *self)
2028 {
2029         /* Set up the folder view as a dnd destination. Set only the
2030            highlight flag, otherwise gtk will have a different
2031            behaviour */
2032         gtk_drag_dest_set (GTK_WIDGET (self),
2033                            GTK_DEST_DEFAULT_HIGHLIGHT,
2034                            folder_view_drag_types,
2035                            G_N_ELEMENTS (folder_view_drag_types),
2036                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2037
2038         g_signal_connect (G_OBJECT (self),
2039                           "drag_data_received",
2040                           G_CALLBACK (on_drag_data_received),
2041                           NULL);
2042
2043
2044         /* Set up the treeview as a dnd source */
2045         gtk_drag_source_set (GTK_WIDGET (self),
2046                              GDK_BUTTON1_MASK,
2047                              folder_view_drag_types,
2048                              G_N_ELEMENTS (folder_view_drag_types),
2049                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2050
2051         g_signal_connect (G_OBJECT (self),
2052                           "drag_motion",
2053                           G_CALLBACK (on_drag_motion),
2054                           NULL);
2055         
2056         g_signal_connect (G_OBJECT (self),
2057                           "drag_data_get",
2058                           G_CALLBACK (on_drag_data_get),
2059                           NULL);
2060
2061         g_signal_connect (G_OBJECT (self),
2062                           "drag_drop",
2063                           G_CALLBACK (drag_drop_cb),
2064                           NULL);
2065 }
2066
2067 /*
2068  * This function manages the navigation through the folders using the
2069  * keyboard or the hardware keys in the device
2070  */
2071 static gboolean
2072 on_key_pressed (GtkWidget *self,
2073                 GdkEventKey *event,
2074                 gpointer user_data)
2075 {
2076         GtkTreeSelection *selection;
2077         GtkTreeIter iter;
2078         GtkTreeModel *model;
2079         gboolean retval = FALSE;
2080
2081         /* Up and Down are automatically managed by the treeview */
2082         if (event->keyval == GDK_Return) {
2083                 /* Expand/Collapse the selected row */
2084                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2085                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2086                         GtkTreePath *path;
2087
2088                         path = gtk_tree_model_get_path (model, &iter);
2089
2090                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2091                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2092                         else
2093                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2094                         gtk_tree_path_free (path);
2095                 }
2096                 /* No further processing */
2097                 retval = TRUE;
2098         }
2099
2100         return retval;
2101 }
2102
2103 /*
2104  * We listen to the changes in the local folder account name key,
2105  * because we want to show the right name in the view. The local
2106  * folder account name corresponds to the device name in the Maemo
2107  * version. We do this because we do not want to query gconf on each
2108  * tree view refresh. It's better to cache it and change whenever
2109  * necessary.
2110  */
2111 static void 
2112 on_configuration_key_changed (ModestConf* conf, 
2113                               const gchar *key, 
2114                               ModestConfEvent event,
2115                               ModestConfNotificationId id, 
2116                               ModestFolderView *self)
2117 {
2118         ModestFolderViewPrivate *priv;
2119
2120
2121         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2122         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2123
2124         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2125                 g_free (priv->local_account_name);
2126
2127                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2128                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2129                 else
2130                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2131                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2132
2133                 /* Force a redraw */
2134 #if GTK_CHECK_VERSION(2, 8, 0)
2135                 GtkTreeViewColumn * tree_column;
2136
2137                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2138                                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2139                 gtk_tree_view_column_queue_resize (tree_column);
2140 #else
2141                 gtk_widget_queue_draw (GTK_WIDGET (self));
2142 #endif
2143         }
2144 }
2145
2146 void
2147 modest_folder_view_set_style (ModestFolderView *self,
2148                               ModestFolderViewStyle style)
2149 {
2150         ModestFolderViewPrivate *priv;
2151
2152         g_return_if_fail (self);
2153         
2154         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2155
2156         priv->style = style;
2157 }
2158
2159 void
2160 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2161                                                              const gchar *account_id)
2162 {
2163         ModestFolderViewPrivate *priv;
2164         GtkTreeModel *model;
2165
2166         g_return_if_fail (self);
2167         
2168         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2169
2170         /* This will be used by the filter_row callback,
2171          * to decided which rows to show: */
2172         if (priv->visible_account_id) {
2173                 g_free (priv->visible_account_id);
2174                 priv->visible_account_id = NULL;
2175         }
2176         if (account_id)
2177                 priv->visible_account_id = g_strdup (account_id);
2178
2179         /* Refilter */
2180         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2181         if (GTK_IS_TREE_MODEL_FILTER (model))
2182                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2183
2184         /* Save settings to gconf */
2185         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2186                                    MODEST_CONF_FOLDER_VIEW_KEY);
2187 }
2188
2189 const gchar *
2190 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2191 {
2192         ModestFolderViewPrivate *priv;
2193
2194         g_return_val_if_fail (self, NULL);
2195         
2196         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2197
2198         return (const gchar *) priv->visible_account_id;
2199 }
2200
2201 static gboolean
2202 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2203 {
2204         do {
2205                 GtkTreeIter child;
2206                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2207
2208                 gtk_tree_model_get (model, iter, 
2209                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
2210                                     &type, -1);
2211                         
2212                 gboolean result = FALSE;
2213                 if (type == TNY_FOLDER_TYPE_INBOX) {
2214                         result = TRUE;
2215                 }               
2216                 if (result) {
2217                         *inbox_iter = *iter;
2218                         return TRUE;
2219                 }
2220
2221                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2222                         if (find_inbox_iter (model, &child, inbox_iter))
2223                                 return TRUE;
2224                 }
2225
2226         } while (gtk_tree_model_iter_next (model, iter));
2227
2228         return FALSE;
2229 }
2230
2231
2232
2233
2234 void 
2235 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2236 {
2237         GtkTreeModel *model;
2238         GtkTreeIter iter, inbox_iter;
2239         GtkTreeSelection *sel;
2240         GtkTreePath *path = NULL;
2241
2242         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2243         if (!model)
2244                 return;
2245
2246         expand_root_items (self);
2247         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2248
2249         gtk_tree_model_get_iter_first (model, &iter);
2250
2251         if (find_inbox_iter (model, &iter, &inbox_iter))
2252                 path = gtk_tree_model_get_path (model, &inbox_iter);
2253         else
2254                 path = gtk_tree_path_new_first ();
2255
2256         /* Select the row and free */
2257         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2258         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2259         gtk_tree_path_free (path);
2260
2261         /* set focus */
2262         gtk_widget_grab_focus (GTK_WIDGET(self));
2263 }
2264
2265
2266 /* recursive */
2267 static gboolean
2268 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, 
2269                   TnyFolder* folder)
2270 {
2271         do {
2272                 GtkTreeIter child;
2273                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2274                 TnyFolder* a_folder;
2275                 gchar *name = NULL;
2276                 
2277                 gtk_tree_model_get (model, iter, 
2278                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2279                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2280                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type, 
2281                                     -1);                
2282                 g_free (name);
2283
2284                 if (folder == a_folder) {
2285                         g_object_unref (a_folder);
2286                         *folder_iter = *iter;
2287                         return TRUE;
2288                 }
2289                 g_object_unref (a_folder);
2290                 
2291                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2292                         if (find_folder_iter (model, &child, folder_iter, folder)) 
2293                                 return TRUE;
2294                 }
2295
2296         } while (gtk_tree_model_iter_next (model, iter));
2297
2298         return FALSE;
2299 }
2300
2301
2302 static void
2303 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath  *path, GtkTreeIter *iter,
2304                                      ModestFolderView *self)
2305 {
2306         ModestFolderViewPrivate *priv = NULL;
2307         GtkTreeSelection *sel;
2308         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2309         GObject *instance = NULL;
2310
2311         if (!MODEST_IS_FOLDER_VIEW(self))
2312                 return;
2313         
2314         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2315
2316         priv->reexpand = TRUE;
2317
2318         gtk_tree_model_get (tree_model, iter, 
2319                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2320                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2321                             -1);
2322         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2323                 priv->folder_to_select = g_object_ref (instance);
2324         }
2325         g_object_unref (instance);
2326
2327         
2328         if (priv->folder_to_select) {
2329                 
2330                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2331                                                        FALSE)) {
2332                         GtkTreePath *path;
2333                         path = gtk_tree_model_get_path (tree_model, iter);
2334                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2335                         
2336                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2337
2338                         gtk_tree_selection_select_iter (sel, iter);
2339                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2340
2341                         gtk_tree_path_free (path);
2342                 
2343                 }
2344
2345                 /* Disable next */
2346                 modest_folder_view_disable_next_folder_selection (self);
2347 /*              g_object_unref (priv->folder_to_select); */
2348 /*              priv->folder_to_select = NULL; */
2349         }
2350 }
2351
2352
2353 void
2354 modest_folder_view_disable_next_folder_selection (ModestFolderView *self) 
2355 {
2356         ModestFolderViewPrivate *priv = NULL;
2357
2358         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));        
2359         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2360
2361         if (priv->folder_to_select)
2362                 g_object_unref(priv->folder_to_select);
2363         
2364         priv->folder_to_select = NULL;
2365 }
2366
2367 gboolean
2368 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder, 
2369                                   gboolean after_change)
2370 {
2371         GtkTreeModel *model;
2372         GtkTreeIter iter, folder_iter;
2373         GtkTreeSelection *sel;
2374         ModestFolderViewPrivate *priv = NULL;
2375         
2376         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);     
2377         g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);   
2378                 
2379         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2380
2381         if (after_change) {
2382
2383                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2384                 gtk_tree_selection_unselect_all (sel);
2385
2386                 if (priv->folder_to_select)
2387                         g_object_unref(priv->folder_to_select);
2388                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2389                 return TRUE;
2390         }
2391                 
2392         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2393         if (!model)
2394                 return FALSE;
2395
2396                 
2397         gtk_tree_model_get_iter_first (model, &iter);
2398         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2399                 GtkTreePath *path;
2400
2401                 path = gtk_tree_model_get_path (model, &folder_iter);
2402                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2403
2404                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2405                 gtk_tree_selection_select_iter (sel, &folder_iter);
2406                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2407
2408                 gtk_tree_path_free (path);
2409                 return TRUE;
2410         }
2411         return FALSE;
2412 }
2413
2414
2415 void 
2416 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2417 {
2418         /* Copy selection */
2419         _clipboard_set_selected_data (folder_view, FALSE);
2420 }
2421
2422 void 
2423 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2424 {
2425         ModestFolderViewPrivate *priv = NULL;
2426         GtkTreeModel *model = NULL;
2427         const gchar **hidding = NULL;
2428         guint i, n_selected;
2429
2430         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2431         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2432
2433         /* Copy selection */
2434         if (!_clipboard_set_selected_data (folder_view, TRUE))
2435                 return;
2436
2437         /* Get hidding ids */
2438         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
2439         
2440         /* Clear hidding array created by previous cut operation */
2441         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2442
2443         /* Copy hidding array */
2444         priv->n_selected = n_selected;
2445         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2446         for (i=0; i < n_selected; i++) 
2447                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2448
2449         /* Hide cut folders */
2450         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2451         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2452 }
2453
2454 void
2455 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2456                                ModestFolderView *folder_view_dst)
2457 {
2458         GtkTreeModel *filter_model = NULL;
2459         GtkTreeModel *model = NULL;
2460         GtkTreeModel *new_filter_model = NULL;
2461         
2462         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2463         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2464
2465         /* Get src model*/
2466         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2467         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2468
2469         /* Build new filter model */
2470         new_filter_model = gtk_tree_model_filter_new (model, NULL);     
2471         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2472                                                 filter_row,
2473                                                 folder_view_dst,
2474                                                 NULL);
2475         /* Set copied model */
2476         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2477         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
2478                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
2479
2480         /* Free */
2481         g_object_unref (new_filter_model);
2482 }
2483
2484 void
2485 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2486                                           gboolean show)
2487 {
2488         GtkTreeModel *model = NULL;
2489         ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2490         priv->show_non_move = show;
2491 /*      modest_folder_view_update_model(folder_view, */
2492 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2493
2494         /* Hide special folders */
2495         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2496         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2497                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2498         }
2499 }
2500
2501 /* Returns FALSE if it did not selected anything */
2502 static gboolean
2503 _clipboard_set_selected_data (ModestFolderView *folder_view,
2504                               gboolean delete)
2505 {
2506         ModestFolderViewPrivate *priv = NULL;
2507         TnyFolderStore *folder = NULL;
2508         gboolean retval = FALSE;
2509
2510         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2511         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2512                 
2513         /* Set selected data on clipboard   */
2514         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2515         folder = modest_folder_view_get_selected (folder_view);
2516
2517         /* Do not allow to select an account */
2518         if (TNY_IS_FOLDER (folder)) {
2519                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2520                 retval = TRUE;
2521         }
2522
2523         /* Free */
2524         g_object_unref (folder);
2525
2526         return retval;
2527 }
2528
2529 static void
2530 _clear_hidding_filter (ModestFolderView *folder_view) 
2531 {
2532         ModestFolderViewPrivate *priv;
2533         guint i;
2534         
2535         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
2536         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2537
2538         if (priv->hidding_ids != NULL) {
2539                 for (i=0; i < priv->n_selected; i++) 
2540                         g_free (priv->hidding_ids[i]);
2541                 g_free(priv->hidding_ids);
2542         }       
2543 }
2544
2545
2546 static void 
2547 on_display_name_changed (ModestAccountMgr *mgr, 
2548                          const gchar *account,
2549                          gpointer user_data)
2550 {
2551         ModestFolderView *self;
2552
2553         self = MODEST_FOLDER_VIEW (user_data);
2554
2555         /* Force a redraw */
2556 #if GTK_CHECK_VERSION(2, 8, 0)
2557         GtkTreeViewColumn * tree_column;
2558         
2559         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2560                                                 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2561         gtk_tree_view_column_queue_resize (tree_column);
2562 #else
2563         gtk_widget_queue_draw (GTK_WIDGET (self));
2564 #endif
2565 }