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