Added support to handle accounts-reloaded signal
[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
33 #include <tny-gtk-account-list-model.h>
34 #include <tny-gtk-folder-store-tree-model.h>
35 #include <tny-gtk-header-list-model.h>
36 #include <tny-account-store.h>
37 #include <tny-account.h>
38 #include <tny-folder.h>
39 #include <tny-camel-folder.h>
40 #include <modest-tny-folder.h>
41 #include <modest-marshal.h>
42 #include <modest-icon-names.h>
43 #include <modest-icon-factory.h>
44 #include <modest-tny-account-store.h>
45 #include <modest-text-utils.h>
46
47 #include "modest-folder-view.h"
48
49 /* 'private'/'protected' functions */
50 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
51 static void modest_folder_view_init        (ModestFolderView *obj);
52 static void modest_folder_view_finalize    (GObject *obj);
53
54 static gboolean     update_model             (ModestFolderView *self,
55                                               ModestTnyAccountStore *account_store);
56 static gboolean     update_model_empty       (ModestFolderView *self);
57
58 static void         on_selection_changed     (GtkTreeSelection *sel, gpointer data);
59 static void         on_subscription_changed  (TnyStoreAccount *store_account, TnyFolder *folder,
60                                               ModestFolderView *self);
61
62 static void         modest_folder_view_disconnect_store_account_handlers (GtkTreeView *self);
63
64 static gint         cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
65                               gpointer user_data);
66
67
68 enum {
69         FOLDER_SELECTION_CHANGED_SIGNAL,
70         LAST_SIGNAL
71 };
72
73 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
74 struct _ModestFolderViewPrivate {
75
76         TnyAccountStore     *account_store;
77         TnyFolder           *cur_folder;
78
79         gulong               sig1, sig2;
80         gulong              *store_accounts_handlers;
81         GMutex              *lock;
82         GtkTreeSelection    *cur_selection;
83         TnyFolderStoreQuery *query;
84
85 };
86 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                               \
87         (G_TYPE_INSTANCE_GET_PRIVATE((o),                               \
88                                      MODEST_TYPE_FOLDER_VIEW,           \
89                                      ModestFolderViewPrivate))
90 /* globals */
91 static GObjectClass *parent_class = NULL;
92
93 static guint signals[LAST_SIGNAL] = {0}; 
94
95 GType
96 modest_folder_view_get_type (void)
97 {
98         static GType my_type = 0;
99         if (!my_type) {
100                 static const GTypeInfo my_info = {
101                         sizeof(ModestFolderViewClass),
102                         NULL,           /* base init */
103                         NULL,           /* base finalize */
104                         (GClassInitFunc) modest_folder_view_class_init,
105                         NULL,           /* class finalize */
106                         NULL,           /* class data */
107                         sizeof(ModestFolderView),
108                         1,              /* n_preallocs */
109                         (GInstanceInitFunc) modest_folder_view_init,
110                         NULL
111                 };
112                                 
113                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
114                                                   "ModestFolderView",
115                                                   &my_info, 0);         
116         }
117         return my_type;
118 }
119
120 static void
121 modest_folder_view_class_init (ModestFolderViewClass *klass)
122 {
123         GObjectClass *gobject_class;
124         gobject_class = (GObjectClass*) klass;
125
126         parent_class            = g_type_class_peek_parent (klass);
127         gobject_class->finalize = modest_folder_view_finalize;
128         
129         klass->update_model = modest_folder_view_update_model;
130
131         g_type_class_add_private (gobject_class,
132                                   sizeof(ModestFolderViewPrivate));
133         
134         signals[FOLDER_SELECTION_CHANGED_SIGNAL] = 
135                 g_signal_new ("folder_selection_changed",
136                               G_TYPE_FROM_CLASS (gobject_class),
137                               G_SIGNAL_RUN_FIRST,
138                               G_STRUCT_OFFSET (ModestFolderViewClass,
139                                                folder_selection_changed),
140                               NULL, NULL,
141                               modest_marshal_VOID__POINTER_BOOLEAN,
142                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
143 }
144
145
146
147 static void
148 text_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
149                  GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer data)
150 {
151         GObject *rendobj;
152         gchar *fname;
153         gint unread;
154         TnyFolderType type;
155         TnyFolder *folder;
156         
157         g_return_if_fail (column);
158         g_return_if_fail (tree_model);
159
160         gtk_tree_model_get (tree_model, iter,
161                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
162                             TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
163                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
164                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
165                             -1);
166         rendobj = G_OBJECT(renderer);
167
168         if (!fname)
169                 return;
170         
171         if (folder && type != TNY_FOLDER_TYPE_ROOT) { /* FIXME: tnymail bug? crashes with root folders */
172                 if (modest_tny_folder_is_local_folder (folder)) {
173                         TnyFolderType type;
174                         type = modest_tny_folder_get_local_folder_type (folder);
175                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
176                                 g_free (fname);
177                                 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
178                         }
179                 }
180         } else if (folder && type == TNY_FOLDER_TYPE_ROOT) {
181                 if (strcmp (fname, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0) {/* FIXME: hack */
182                         g_free (fname);
183                         fname = g_strdup (MODEST_LOCAL_FOLDERS_DISPLAY_NAME);
184                 }
185         }
186                         
187         if (unread > 0) {
188                 gchar *folder_title = g_strdup_printf ("%s (%d)", fname, unread);
189                 g_object_set (rendobj,"text", folder_title,  "weight", 800, NULL);
190                 g_free (folder_title);
191         } else 
192                 g_object_set (rendobj,"text", fname, "weight", 400, NULL);
193                 
194         g_free (fname);
195 }
196
197
198
199 static void
200 icon_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
201                  GtkTreeModel *tree_model,  GtkTreeIter *iter, gpointer data)
202 {
203         GObject *rendobj;
204         GdkPixbuf *pixbuf;
205         TnyFolderType type;
206         gchar *fname = NULL;
207         gint unread;
208         
209         rendobj = G_OBJECT(renderer);
210         gtk_tree_model_get (tree_model, iter,
211                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
212                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
213                             TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread, -1);
214         rendobj = G_OBJECT(renderer);
215         
216         if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN)
217                 type = modest_tny_folder_guess_folder_type_from_name (fname);
218         g_free (fname);
219
220         switch (type) {
221         case TNY_FOLDER_TYPE_ROOT:
222                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
223                 break;
224         case TNY_FOLDER_TYPE_INBOX:
225                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_INBOX);
226                 break;
227         case TNY_FOLDER_TYPE_OUTBOX:
228                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_OUTBOX);
229                 break;
230         case TNY_FOLDER_TYPE_JUNK:
231                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_JUNK);
232                 break;
233         case TNY_FOLDER_TYPE_SENT:
234                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_SENT);
235                 break;
236         case TNY_FOLDER_TYPE_DRAFTS:
237                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_DRAFTS);
238                 break;
239         case TNY_FOLDER_TYPE_NOTES:
240                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_NOTES);
241                 break;
242         case TNY_FOLDER_TYPE_CALENDAR:
243                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_CALENDAR);
244                 break;
245         case TNY_FOLDER_TYPE_CONTACTS:
246                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_CONTACTS);
247                 break;
248         case TNY_FOLDER_TYPE_NORMAL:
249         default:
250                 pixbuf = modest_icon_factory_get_icon (MODEST_FOLDER_ICON_NORMAL);
251                 break;
252         }
253
254         g_object_set (rendobj,
255                       "pixbuf-expander-open",
256                       modest_icon_factory_get_icon (MODEST_FOLDER_ICON_OPEN),
257                       "pixbuf-expander-closed",
258                       modest_icon_factory_get_icon (MODEST_FOLDER_ICON_CLOSED),
259                       "pixbuf", pixbuf,
260                       NULL);
261 }
262
263 static void
264 modest_folder_view_init (ModestFolderView *obj)
265 {
266         ModestFolderViewPrivate *priv;
267         GtkTreeViewColumn *column;
268         GtkCellRenderer *renderer;
269         GtkTreeSelection *sel;
270         
271         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
272         
273         priv->account_store  = NULL;
274         priv->cur_folder     = NULL;
275         priv->query          = NULL;
276         priv->lock           = g_mutex_new ();
277         
278         column = gtk_tree_view_column_new ();   
279         gtk_tree_view_append_column (GTK_TREE_VIEW(obj),column);
280         
281         renderer = gtk_cell_renderer_pixbuf_new();
282         gtk_tree_view_column_pack_start (column, renderer, FALSE);
283         gtk_tree_view_column_set_cell_data_func(column, renderer,
284                                                 icon_cell_data, NULL, NULL);
285         
286         renderer = gtk_cell_renderer_text_new();
287         gtk_tree_view_column_pack_start (column, renderer, FALSE);
288         gtk_tree_view_column_set_cell_data_func(column, renderer,
289                                                 text_cell_data, NULL, NULL);
290         
291         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
292         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
293
294         gtk_tree_view_column_set_spacing (column, 2);
295         gtk_tree_view_column_set_resizable (column, TRUE);
296         gtk_tree_view_column_set_fixed_width (column, TRUE);            
297         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), FALSE);
298         gtk_tree_view_set_enable_search     (GTK_TREE_VIEW(obj), FALSE);
299
300 }
301
302 static void
303 modest_folder_view_disconnect_store_account_handlers (GtkTreeView *self)
304 {
305         TnyIterator *iter;
306         ModestFolderViewPrivate *priv;
307         GtkTreeModel *model;
308         GtkTreeModelSort *sortable;
309         gint i = 0;
310
311         sortable = GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (self));
312         if (!sortable)
313                 return; 
314
315         model = gtk_tree_model_sort_get_model (sortable);
316         if (!model)
317                 return; 
318
319         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);  
320         iter = tny_list_create_iterator (TNY_LIST (model));
321         while (!tny_iterator_is_done (iter)) {
322                 g_signal_handler_disconnect (G_OBJECT (tny_iterator_get_current (iter)),
323                                              priv->store_accounts_handlers [i++]);
324                 tny_iterator_next (iter);
325         }
326         g_object_unref (G_OBJECT (iter));
327 }
328
329
330 static void
331 modest_folder_view_finalize (GObject *obj)
332 {
333         ModestFolderViewPrivate *priv;
334         GtkTreeSelection    *sel;
335         
336         g_return_if_fail (obj);
337         
338         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
339         if (priv->account_store) {
340                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
341                                              priv->sig1);
342                 g_object_unref (G_OBJECT(priv->account_store));
343                 priv->account_store = NULL;
344         }
345
346         if (priv->lock) {
347                 g_mutex_free (priv->lock);
348                 priv->lock = NULL;
349         }
350
351         if (priv->store_accounts_handlers) {
352                 modest_folder_view_disconnect_store_account_handlers (GTK_TREE_VIEW (obj));
353                 g_free (priv->store_accounts_handlers);
354                 priv->store_accounts_handlers = NULL;
355         }
356
357         if (priv->query) {
358                 g_object_unref (G_OBJECT (priv->query));
359                 priv->query = NULL;
360         }
361
362         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
363         if (sel)
364                 g_signal_handler_disconnect (G_OBJECT(sel), priv->sig2);
365         
366         G_OBJECT_CLASS(parent_class)->finalize (obj);
367 }
368
369
370 static void
371 on_account_update (TnyAccountStore *account_store, const gchar *account,
372                    gpointer user_data)
373 {
374         if (!update_model (MODEST_FOLDER_VIEW(user_data), 
375                            MODEST_TNY_ACCOUNT_STORE(account_store)))
376                 g_printerr ("modest: failed to update model for changes in '%s'",
377                             account);
378 }
379
380 void
381 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
382 {
383         GtkTreeViewColumn *col;
384         
385         g_return_if_fail (self);
386
387         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
388         if (!col) {
389                 g_printerr ("modest: failed get column for title\n");
390                 return;
391         }
392
393         gtk_tree_view_column_set_title (col, title);
394         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
395                                            title != NULL);
396 }
397
398 GtkWidget*
399 modest_folder_view_new (ModestTnyAccountStore *account_store, 
400                         TnyFolderStoreQuery *query)
401 {
402         GObject *self;
403         ModestFolderViewPrivate *priv;
404         GtkTreeSelection *sel;
405         
406         g_return_val_if_fail (account_store, NULL);
407         
408         self = G_OBJECT(g_object_new(MODEST_TYPE_FOLDER_VIEW, NULL));
409         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
410         
411         priv->account_store = g_object_ref (G_OBJECT (account_store));
412         if (query)
413                 priv->query = g_object_ref (G_OBJECT (query));
414         
415         if (!update_model (MODEST_FOLDER_VIEW(self),
416                            MODEST_TNY_ACCOUNT_STORE(account_store)))
417                 g_printerr ("modest: failed to update model\n");
418         
419         priv->sig1 = g_signal_connect (G_OBJECT(account_store), "account_update",
420                                        G_CALLBACK (on_account_update), self);   
421         
422         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
423         priv->sig2 = g_signal_connect (sel, "changed",
424                                        G_CALLBACK(on_selection_changed), self);
425         return GTK_WIDGET(self);
426 }
427
428
429 static gboolean
430 update_model_empty (ModestFolderView *self)
431 {
432         ModestFolderViewPrivate *priv;
433         
434         g_return_val_if_fail (self, FALSE);
435         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
436
437         /* Disconnect old handlers */
438         if (priv->store_accounts_handlers) {
439                 modest_folder_view_disconnect_store_account_handlers (GTK_TREE_VIEW (self));
440                 g_free (priv->store_accounts_handlers);
441                 priv->store_accounts_handlers = NULL;
442         }
443
444         g_signal_emit (G_OBJECT(self), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
445                        NULL, TRUE);
446         return TRUE;
447 }
448
449
450 static void
451 update_store_account_handlers (ModestFolderView *self, TnyList *account_list)
452 {
453         ModestFolderViewPrivate *priv;
454         TnyIterator *iter;
455         guint len;
456         
457         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
458
459         /* Listen to subscription changes */
460         len = tny_list_get_length (TNY_LIST (account_list));
461
462         g_assert (priv->store_accounts_handlers == NULL); /* don't leak */
463         priv->store_accounts_handlers = g_malloc0 (sizeof (guint) * len);
464         iter = tny_list_create_iterator (account_list);
465         
466         if (!tny_iterator_is_done (iter)) {
467                 gint i = 0;
468
469                 do  {
470                         
471                         priv->store_accounts_handlers [i++] =
472                                 g_signal_connect (G_OBJECT (tny_iterator_get_current (iter)),
473                                                   "subscription_changed",
474                                                   G_CALLBACK (on_subscription_changed),
475                                                   self);
476                         tny_iterator_next (iter);
477                 } while (!tny_iterator_is_done (iter));
478         }
479         g_object_unref (G_OBJECT (iter));       
480 }
481
482
483
484 static gboolean
485 update_model (ModestFolderView *self, ModestTnyAccountStore *account_store)
486 {
487         ModestFolderViewPrivate *priv;
488
489         TnyList          *account_list;
490         GtkTreeModel     *model, *sortable;
491
492         g_return_val_if_fail (account_store, FALSE);
493
494         update_model_empty (self);
495
496         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
497         
498         model        = tny_gtk_folder_store_tree_model_new (FALSE, NULL);
499         account_list = TNY_LIST(model);
500
501         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
502                                         account_list,
503                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
504
505         
506         if (account_list) {
507                 sortable = gtk_tree_model_sort_new_with_model (model);
508                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
509                                                       TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
510                                                       GTK_SORT_ASCENDING);
511                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
512                                                  TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
513                                                  cmp_rows, NULL, NULL);
514                 gtk_tree_view_set_model (GTK_TREE_VIEW(self), sortable);
515                 update_store_account_handlers (self, account_list);
516         }
517         
518         g_object_unref (model);
519         return TRUE;
520 }
521
522
523 static void
524 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
525 {
526         GtkTreeModel            *model;
527         TnyFolder               *folder = NULL;
528         GtkTreeIter             iter;
529         ModestFolderView        *tree_view;
530         ModestFolderViewPrivate *priv;
531         gint                    type;
532
533         g_return_if_fail (sel);
534         g_return_if_fail (user_data);
535         
536         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
537         priv->cur_selection = sel;
538         
539         /* folder was _un_selected if true */
540         if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
541                 priv->cur_folder = NULL; /* FIXME: need this? */
542                return; 
543         }
544         
545         tree_view = MODEST_FOLDER_VIEW (user_data);
546         gtk_tree_model_get (model, &iter,
547                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
548                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
549                             -1);
550
551         if (type == TNY_FOLDER_TYPE_ROOT)
552                 return;
553         
554         /* emit 2 signals: one for the unselection of the old one,
555          * and one for the selection of the new on */
556         g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
557                        priv->cur_folder, FALSE);
558         g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
559                        folder, TRUE);
560         if (priv->cur_folder)
561                 tny_folder_sync (priv->cur_folder, TRUE, NULL); /* FIXME */
562         priv->cur_folder = folder;
563
564 }
565
566 static void 
567 on_subscription_changed  (TnyStoreAccount *store_account, 
568                           TnyFolder *folder,
569                           ModestFolderView *self)
570 {
571         /* TODO: probably we won't need a full reload, just the store
572            account or even the parent of the folder */
573
574         ModestFolderViewPrivate *priv;
575
576         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
577         update_model (self, MODEST_TNY_ACCOUNT_STORE (priv->account_store));
578 }
579
580
581 gboolean
582 modest_folder_view_update_model (ModestFolderView *self, TnyAccountStore *account_store)
583 {
584         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
585
586         g_signal_emit (G_OBJECT(self), signals[FOLDER_SELECTION_CHANGED_SIGNAL],
587                        0, NULL, TRUE);
588         
589         return update_model (self, MODEST_TNY_ACCOUNT_STORE(account_store)); /* ugly */
590 }
591
592 TnyFolder *
593 modest_folder_view_get_selected (ModestFolderView *self)
594 {
595         ModestFolderViewPrivate *priv;
596
597         g_return_val_if_fail (self, NULL);
598         
599         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
600         if (priv->cur_folder)
601                 g_object_ref (priv->cur_folder);
602
603         return priv->cur_folder;
604 }
605
606 static gint
607 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
608           gpointer user_data)
609 {
610         gint cmp;
611         gchar         *name1, *name2;
612         TnyFolderType type;
613         TnyFolder     *folder1, *folder2;
614         
615         gtk_tree_model_get (tree_model, iter1,
616                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
617                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
618                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
619                             -1);
620         gtk_tree_model_get (tree_model, iter2,
621                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
622                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
623                             -1);
624
625         /* local_folders should be the last one */
626         if (type == TNY_FOLDER_TYPE_ROOT) {
627                 /* the account name is also the name of the root folder
628                  * in case of local folders */
629                 if (strcmp (name1, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0)
630                         cmp = +1;
631                 else if (strcmp (name2, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0)
632                         cmp = -1;
633                 else 
634                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
635         } else {
636                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
637         }
638         g_free (name1);
639         g_free (name2);
640
641         return cmp;     
642 }