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