Added modest-dnd.h
[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-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-monitor.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <modest-tny-folder.h>
45 #include <modest-marshal.h>
46 #include <modest-icon-names.h>
47 #include <modest-tny-account-store.h>
48 #include <modest-text-utils.h>
49 #include <modest-runtime.h>
50 #include "modest-folder-view.h"
51 #include <modest-dnd.h>
52
53 /* 'private'/'protected' functions */
54 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
55 static void modest_folder_view_init        (ModestFolderView *obj);
56 static void modest_folder_view_finalize    (GObject *obj);
57
58 static void         tny_account_store_view_init (gpointer g, 
59                                                  gpointer iface_data);
60
61 static void         modest_folder_view_set_account_store (TnyAccountStoreView *self, 
62                                                           TnyAccountStore     *account_store);
63
64 static gboolean     update_model           (ModestFolderView *self,
65                                             ModestTnyAccountStore *account_store);
66
67 static gboolean     update_model_empty     (ModestFolderView *self);
68
69 static void         on_selection_changed   (GtkTreeSelection *sel, gpointer data);
70
71 static void         on_account_update      (TnyAccountStore *account_store, 
72                                             const gchar *account,
73                                             gpointer user_data);
74
75 static void         on_accounts_reloaded   (TnyAccountStore *store, 
76                                             gpointer user_data);
77
78 static gint         cmp_rows               (GtkTreeModel *tree_model, 
79                                             GtkTreeIter *iter1, 
80                                             GtkTreeIter *iter2,
81                                             gpointer user_data);
82
83 /* DnD functions */
84 static void         on_drag_data_get       (GtkWidget *widget, 
85                                             GdkDragContext *context, 
86                                             GtkSelectionData *selection_data, 
87                                             guint info, 
88                                             guint time, 
89                                             gpointer data);
90
91 static void         on_drag_data_received  (GtkWidget *widget, 
92                                             GdkDragContext *context, 
93                                             gint x, 
94                                             gint y, 
95                                             GtkSelectionData *selection_data, 
96                                             guint info, 
97                                             guint time, 
98                                             gpointer data);
99
100 static gboolean     on_drag_motion         (GtkWidget      *widget,
101                                             GdkDragContext *context,
102                                             gint            x,
103                                             gint            y,
104                                             guint           time,
105                                             gpointer        user_data);
106
107 static gint         expand_row_timeout     (gpointer data);
108
109 static void         setup_drag_and_drop    (GtkTreeView *self);
110
111 enum {
112         FOLDER_SELECTION_CHANGED_SIGNAL,
113         FOLDER_XFER_SIGNAL,
114         LAST_SIGNAL
115 };
116
117 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
118 struct _ModestFolderViewPrivate {
119         TnyAccountStore     *account_store;
120         TnyFolder           *cur_folder;
121         GtkTreeRowReference *cur_row;
122
123         gulong               account_update_signal;
124         gulong               changed_signal;
125         gulong               accounts_reloaded_signal;
126         GMutex              *lock;
127         
128         GtkTreeSelection    *cur_selection;
129         TnyFolderStoreQuery *query;
130         guint                timer_expander;
131
132         TnyFolderMonitor    *monitor;
133 };
134 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                       \
135         (G_TYPE_INSTANCE_GET_PRIVATE((o),                       \
136                                      MODEST_TYPE_FOLDER_VIEW,   \
137                                      ModestFolderViewPrivate))
138 /* globals */
139 static GObjectClass *parent_class = NULL;
140
141 static guint signals[LAST_SIGNAL] = {0}; 
142
143 GType
144 modest_folder_view_get_type (void)
145 {
146         static GType my_type = 0;
147         if (!my_type) {
148                 static const GTypeInfo my_info = {
149                         sizeof(ModestFolderViewClass),
150                         NULL,           /* base init */
151                         NULL,           /* base finalize */
152                         (GClassInitFunc) modest_folder_view_class_init,
153                         NULL,           /* class finalize */
154                         NULL,           /* class data */
155                         sizeof(ModestFolderView),
156                         1,              /* n_preallocs */
157                         (GInstanceInitFunc) modest_folder_view_init,
158                         NULL
159                 };
160
161                 static const GInterfaceInfo tny_account_store_view_info = {
162                         (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
163                         NULL,         /* interface_finalize */
164                         NULL          /* interface_data */
165                 };
166
167                                 
168                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
169                                                   "ModestFolderView",
170                                                   &my_info, 0);
171
172                 g_type_add_interface_static (my_type, 
173                                              TNY_TYPE_ACCOUNT_STORE_VIEW, 
174                                              &tny_account_store_view_info);
175         }
176         return my_type;
177 }
178
179 static void
180 modest_folder_view_class_init (ModestFolderViewClass *klass)
181 {
182         GObjectClass *gobject_class;
183         gobject_class = (GObjectClass*) klass;
184
185         parent_class            = g_type_class_peek_parent (klass);
186         gobject_class->finalize = modest_folder_view_finalize;
187
188         g_type_class_add_private (gobject_class,
189                                   sizeof(ModestFolderViewPrivate));
190         
191         signals[FOLDER_SELECTION_CHANGED_SIGNAL] = 
192                 g_signal_new ("folder_selection_changed",
193                               G_TYPE_FROM_CLASS (gobject_class),
194                               G_SIGNAL_RUN_FIRST,
195                               G_STRUCT_OFFSET (ModestFolderViewClass,
196                                                folder_selection_changed),
197                               NULL, NULL,
198                               modest_marshal_VOID__POINTER_BOOLEAN,
199                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
200
201         /**
202          * ModestFolderView::folder-moved
203          * @self: the #MailOperation that emits the signal
204          * @folder: the #TnyFolder that is going to be moved
205          * @parent: then #TnyFolderStore that is going to be the new parent
206          * @done: indicates if the folder move was correctly completed or not
207          * @user_data: user data set when the signal handler was connected
208          *
209          * Emitted when a the user wants to move a folder through a
210          * drag and drop action
211          */
212         signals[FOLDER_XFER_SIGNAL] = 
213                 g_signal_new ("folder_xfer",
214                               G_TYPE_FROM_CLASS (gobject_class),
215                               G_SIGNAL_RUN_FIRST,
216                               G_STRUCT_OFFSET (ModestFolderViewClass, folder_xfer),
217                               NULL, NULL,
218                               modest_marshal_VOID__POINTER_POINTER_BOOL_POINTER,
219                               G_TYPE_NONE, 4, 
220                               G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_POINTER);
221 }
222
223
224
225 static void
226 text_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
227                  GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer data)
228 {
229         GObject *rendobj;
230         gchar *fname = NULL;
231         gint unread;
232         TnyFolderType type;
233         TnyFolder *folder = NULL;
234         
235         g_return_if_fail (column);
236         g_return_if_fail (tree_model);
237
238         gtk_tree_model_get (tree_model, iter,
239                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
240                             TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
241                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
242                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
243                             -1);
244         rendobj = G_OBJECT(renderer);
245  
246         if (!fname)
247                 return;
248         
249         if (folder && type != TNY_FOLDER_TYPE_ROOT) { /* FIXME: tnymail bug? crashes with root folders */
250                 if (modest_tny_folder_is_local_folder (folder)) {
251                         TnyFolderType type;
252                         type = modest_tny_folder_get_local_folder_type (folder);
253                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
254                                 g_free (fname);
255                                 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
256                         }
257                 }
258         } else if (folder && type == TNY_FOLDER_TYPE_ROOT) {
259                 if (strcmp (fname, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0) {/* FIXME: hack */
260                         g_free (fname);
261                         fname = g_strdup (MODEST_LOCAL_FOLDERS_DISPLAY_NAME);
262                 }
263         }
264                         
265         if (unread > 0) {
266                 gchar *folder_title = g_strdup_printf ("%s (%d)", fname, unread);
267                 g_object_set (rendobj,"text", folder_title,  "weight", 800, NULL);
268                 g_free (folder_title);
269         } else 
270                 g_object_set (rendobj,"text", fname, "weight", 400, NULL);
271                 
272         g_free (fname);
273         if (folder) g_object_unref (G_OBJECT (folder));
274 }
275
276
277 static GdkPixbuf*
278 get_cached_icon (const gchar *name)
279 {
280         GError *err = NULL;
281         gpointer pixbuf;
282         gpointer orig_key;
283         static GHashTable *icon_cache = NULL;
284         
285         g_return_val_if_fail (name, NULL);
286
287         if (G_UNLIKELY(!icon_cache))
288                 icon_cache = modest_cache_mgr_get_cache (modest_runtime_get_cache_mgr(),
289                                                          MODEST_CACHE_MGR_CACHE_TYPE_PIXBUF);
290         
291         if (!icon_cache || !g_hash_table_lookup_extended (icon_cache, name, &orig_key, &pixbuf)) {
292                 pixbuf = (gpointer)gdk_pixbuf_new_from_file (name, &err);
293                 if (!pixbuf) {
294                         g_printerr ("modest: error in icon factory while loading '%s': %s\n",
295                                     name, err->message);
296                         g_error_free (err);
297                 }
298                 /* if we cannot find it, we still insert (if we have a cache), so we get the error
299                  * only once */
300                 if (icon_cache)
301                         g_hash_table_insert (icon_cache, g_strdup(name),(gpointer)pixbuf);
302         }
303         return GDK_PIXBUF(pixbuf);
304 }
305
306
307 static void
308 icon_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
309                  GtkTreeModel *tree_model,  GtkTreeIter *iter, gpointer data)
310 {
311         GObject *rendobj;
312         GdkPixbuf *pixbuf;
313         TnyFolderType type;
314         gchar *fname = NULL;
315         gint unread;
316         
317         rendobj = G_OBJECT(renderer);
318         gtk_tree_model_get (tree_model, iter,
319                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
320                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
321                             TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread, 
322                             -1);
323         rendobj = G_OBJECT(renderer);
324         
325         if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
326                 type = modest_tny_folder_guess_folder_type_from_name (fname);
327         }
328         g_free (fname);
329
330         switch (type) {
331         case TNY_FOLDER_TYPE_ROOT:
332                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_ACCOUNT);
333                 break;
334         case TNY_FOLDER_TYPE_INBOX:
335                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_INBOX);
336                 break;
337         case TNY_FOLDER_TYPE_OUTBOX:
338                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_OUTBOX);
339                 break;
340         case TNY_FOLDER_TYPE_JUNK:
341                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_JUNK);
342                 break;
343         case TNY_FOLDER_TYPE_SENT:
344                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_SENT);
345                 break;
346         case TNY_FOLDER_TYPE_TRASH:
347                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_TRASH);
348                 break;
349         case TNY_FOLDER_TYPE_DRAFTS:
350                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_DRAFTS);
351                 break;
352         case TNY_FOLDER_TYPE_NOTES:
353                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_NOTES);
354                 break;
355         case TNY_FOLDER_TYPE_CALENDAR:
356                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_CALENDAR);
357                 break;
358         case TNY_FOLDER_TYPE_CONTACTS:
359                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_CONTACTS);
360                 break;
361         case TNY_FOLDER_TYPE_NORMAL:
362         default:
363                 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_NORMAL);
364                 break;
365         }
366         g_object_set (rendobj, "pixbuf", pixbuf, NULL);
367 }
368
369 static void
370 modest_folder_view_init (ModestFolderView *obj)
371 {
372         ModestFolderViewPrivate *priv;
373         GtkTreeViewColumn *column;
374         GtkCellRenderer *renderer;
375         GtkTreeSelection *sel;
376         
377         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
378         
379         priv->timer_expander = 0;
380         priv->account_store  = NULL;
381         priv->cur_folder     = NULL;
382         priv->cur_row        = NULL;
383         priv->query          = NULL;
384         priv->monitor        = NULL;
385
386         priv->lock           = g_mutex_new ();
387
388         column = gtk_tree_view_column_new ();   
389         gtk_tree_view_append_column (GTK_TREE_VIEW(obj),column);
390         
391         renderer = gtk_cell_renderer_pixbuf_new();
392         gtk_tree_view_column_pack_start (column, renderer, FALSE);
393         gtk_tree_view_column_set_cell_data_func(column, renderer,
394                                                 icon_cell_data, NULL, NULL);
395         
396         renderer = gtk_cell_renderer_text_new();
397         gtk_tree_view_column_pack_start (column, renderer, FALSE);
398         gtk_tree_view_column_set_cell_data_func(column, renderer,
399                                                 text_cell_data, NULL, NULL);
400         
401         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
402         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
403
404         gtk_tree_view_column_set_spacing (column, 2);
405         gtk_tree_view_column_set_resizable (column, TRUE);
406         gtk_tree_view_column_set_fixed_width (column, TRUE);            
407         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), FALSE);
408         gtk_tree_view_set_enable_search     (GTK_TREE_VIEW(obj), FALSE);
409
410         setup_drag_and_drop (GTK_TREE_VIEW(obj));
411 }
412
413 static void
414 tny_account_store_view_init (gpointer g, gpointer iface_data)
415 {
416         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
417
418         klass->set_account_store_func = modest_folder_view_set_account_store;
419
420         return;
421 }
422
423 static void
424 modest_folder_view_finalize (GObject *obj)
425 {
426         ModestFolderViewPrivate *priv;
427         GtkTreeSelection    *sel;
428         
429         g_return_if_fail (obj);
430         
431         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
432
433         if (priv->timer_expander != 0) {
434                 g_source_remove (priv->timer_expander);
435                 priv->timer_expander = 0;
436         }
437
438         if (priv->account_store) {
439                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
440                                              priv->account_update_signal);
441                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
442                                              priv->accounts_reloaded_signal);
443                 g_object_unref (G_OBJECT(priv->account_store));
444                 priv->account_store = NULL;
445         }
446
447         if (priv->lock) {
448                 g_mutex_free (priv->lock);
449                 priv->lock = NULL;
450         }
451
452         if (priv->query) {
453                 g_object_unref (G_OBJECT (priv->query));
454                 priv->query = NULL;
455         }
456
457         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
458         if (sel)
459                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
460         
461         G_OBJECT_CLASS(parent_class)->finalize (obj);
462 }
463
464
465 static void
466 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
467 {
468         ModestFolderViewPrivate *priv;
469         TnyDevice *device;
470
471         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
472         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
473
474         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
475         device = tny_account_store_get_device (account_store);
476
477         if (G_UNLIKELY (priv->account_store)) {
478
479                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
480                                                    priv->account_update_signal))
481                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
482                                                      priv->account_update_signal);
483                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
484                                                    priv->accounts_reloaded_signal))
485                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
486                                                      priv->accounts_reloaded_signal);
487
488                 g_object_unref (G_OBJECT (priv->account_store));
489         }
490
491         priv->account_store = g_object_ref (G_OBJECT (account_store));
492
493         priv->account_update_signal = 
494                 g_signal_connect (G_OBJECT(account_store), "account_update",
495                                   G_CALLBACK (on_account_update), self);
496
497         priv->accounts_reloaded_signal = 
498                 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
499                                   G_CALLBACK (on_accounts_reloaded), self);
500         
501         if (!update_model (MODEST_FOLDER_VIEW (self),
502                            MODEST_TNY_ACCOUNT_STORE (priv->account_store)))
503                 g_printerr ("modest: failed to update model\n");
504
505         g_object_unref (G_OBJECT (device));
506 }
507
508 static void
509 on_account_update (TnyAccountStore *account_store, const gchar *account,
510                    gpointer user_data)
511 {
512         if (!update_model (MODEST_FOLDER_VIEW(user_data), 
513                            MODEST_TNY_ACCOUNT_STORE(account_store)))
514                 g_printerr ("modest: failed to update model for changes in '%s'",
515                             account);
516 }
517
518 static void 
519 on_accounts_reloaded   (TnyAccountStore *account_store, 
520                         gpointer user_data)
521 {
522         update_model (MODEST_FOLDER_VIEW (user_data), 
523                       MODEST_TNY_ACCOUNT_STORE(account_store));
524 }
525
526 void
527 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
528 {
529         GtkTreeViewColumn *col;
530         
531         g_return_if_fail (self);
532
533         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
534         if (!col) {
535                 g_printerr ("modest: failed get column for title\n");
536                 return;
537         }
538
539         gtk_tree_view_column_set_title (col, title);
540         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
541                                            title != NULL);
542 }
543
544 GtkWidget*
545 modest_folder_view_new (TnyFolderStoreQuery *query)
546 {
547         GObject *self;
548         ModestFolderViewPrivate *priv;
549         GtkTreeSelection *sel;
550         
551         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
552         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
553
554         priv->query = g_object_ref (query);
555         
556         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
557         priv->changed_signal = g_signal_connect (sel, "changed",
558                                                  G_CALLBACK (on_selection_changed), self);
559         return GTK_WIDGET(self);
560 }
561
562
563 static gboolean
564 update_model_empty (ModestFolderView *self)
565 {
566         ModestFolderViewPrivate *priv;
567         
568         g_return_val_if_fail (self, FALSE);
569         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
570
571         g_mutex_lock (priv->lock);
572         {
573                 if (priv->monitor) {
574                         tny_folder_monitor_stop (priv->monitor);
575                         g_object_unref(G_OBJECT(priv->monitor));
576                         priv->monitor = NULL;
577                 }
578         }
579         g_mutex_unlock (priv->lock);
580         
581         g_signal_emit (G_OBJECT(self), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
582                        NULL, TRUE);
583         return TRUE;
584 }
585
586
587 /* this feels dirty; any other way to expand all the root items? */
588 static void
589 expand_root_items (ModestFolderView *self)
590 {
591         GtkTreePath *path;
592         path = gtk_tree_path_new_first ();
593
594         /* all folders should have child items, so.. */
595         while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
596                 gtk_tree_path_next (path);
597         
598         gtk_tree_path_free (path);
599 }
600
601 static gboolean
602 update_model (ModestFolderView *self, ModestTnyAccountStore *account_store)
603 {
604         ModestFolderViewPrivate *priv;
605
606         TnyList          *account_list;
607         GtkTreeModel     *model, *sortable;
608
609         g_return_val_if_fail (account_store, FALSE);
610
611         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
612         
613         /* Notify that there is no folder selected */
614         update_model_empty (self);
615         
616         /* FIXME: the local accounts are not shown when the query
617            selects only the subscribed folders. */
618 /*      model        = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
619         model        = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
620         account_list = TNY_LIST(model);
621
622         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
623                                         account_list,
624                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);      
625         if (account_list) {
626                 sortable = gtk_tree_model_sort_new_with_model (model);
627                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
628                                                       TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
629                                                       GTK_SORT_ASCENDING);
630                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
631                                                  TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
632                                                  cmp_rows, NULL, NULL);
633
634                 /* Set new model */
635                 gtk_tree_view_set_model (GTK_TREE_VIEW(self), sortable);
636                 expand_root_items (self); /* expand all account folders */
637                 g_object_unref (account_list);
638         }
639         
640         return TRUE;
641 }
642
643
644 static void
645 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
646 {
647         GtkTreeModel            *model_sort, *model;
648         TnyFolder               *folder = NULL;
649         GtkTreeIter             iter, iter_sort;
650         GtkTreePath            *path;
651         ModestFolderView        *tree_view;
652         ModestFolderViewPrivate *priv;
653         gint                    type;
654
655         g_return_if_fail (sel);
656         g_return_if_fail (user_data);
657         
658         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
659         priv->cur_selection = sel;
660         
661         /* folder was _un_selected if true */
662         if (!gtk_tree_selection_get_selected (sel, &model_sort, &iter_sort)) {
663                 priv->cur_folder = NULL; /* FIXME: need this? */
664                 gtk_tree_row_reference_free (priv->cur_row);
665                 priv->cur_row = NULL;
666                 return; 
667         }
668
669         model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model_sort));
670         gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model_sort),
671                                                         &iter,
672                                                         &iter_sort);
673
674         tree_view = MODEST_FOLDER_VIEW (user_data);
675
676         gtk_tree_model_get (model, &iter,
677                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
678                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
679                             -1);
680
681         if (type == TNY_FOLDER_TYPE_ROOT) {
682                 g_object_unref (folder);
683                 return;
684         }
685         
686         /* Current folder was unselected */
687         g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
688                        priv->cur_folder, FALSE);
689
690         if (priv->cur_row) {
691 /*              tny_folder_sync (priv->cur_folder, TRUE, NULL); /\* FIXME *\/ */
692                 gtk_tree_row_reference_free (priv->cur_row);
693         }
694
695         /* New current references */
696         path = gtk_tree_model_get_path (model_sort, &iter_sort);
697         priv->cur_folder = folder;
698         priv->cur_row = gtk_tree_row_reference_new (model_sort, path);
699
700         /* Frees */
701         gtk_tree_path_free (path);
702         g_object_unref (G_OBJECT (folder));
703
704         /* New folder has been selected */
705         g_signal_emit (G_OBJECT(tree_view), 
706                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 
707                        0, folder, TRUE); 
708 }
709
710 TnyFolder *
711 modest_folder_view_get_selected (ModestFolderView *self)
712 {
713         ModestFolderViewPrivate *priv;
714
715         g_return_val_if_fail (self, NULL);
716         
717         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
718         if (priv->cur_folder)
719                 g_object_ref (priv->cur_folder);
720
721         return priv->cur_folder;
722 }
723
724 static gboolean
725 get_model_iter (ModestFolderView *self, 
726                 GtkTreeModel **model, 
727                 GtkTreeIter *iter)
728 {
729         GtkTreeModel *model_sort;
730         GtkTreeIter iter_sort;
731         GtkTreePath *path;
732         ModestFolderViewPrivate *priv;
733
734         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
735
736         if (!priv->cur_folder)
737                 return FALSE;
738
739         if (!gtk_tree_row_reference_valid (priv->cur_row))
740                 return FALSE;
741
742         model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
743         *model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model_sort));
744
745         /* Get path to retrieve iter */
746         path = gtk_tree_row_reference_get_path (priv->cur_row);
747         if (!gtk_tree_model_get_iter (model_sort, &iter_sort, path))
748                 return FALSE;
749
750         gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model_sort),
751                                                         iter,
752                                                         &iter_sort);
753         return TRUE;
754 }
755
756 gboolean 
757 modest_folder_view_rename (ModestFolderView *self)
758 {
759         GtkTreeModel *model;
760         GtkTreeIter iter;
761         ModestFolderViewPrivate *priv;
762
763         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
764
765         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
766
767         if (!get_model_iter (self, &model, &iter))
768                 return FALSE;
769
770         /* Set new name */
771         gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
772                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
773                             tny_folder_get_name (priv->cur_folder), -1);
774
775         /* Invalidate selection */
776         g_signal_emit (G_OBJECT(self), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
777                        priv->cur_folder, TRUE);
778
779         return TRUE;
780 }
781
782 gboolean 
783 modest_folder_view_add_subfolder (ModestFolderView *self, TnyFolder *folder)
784 {
785         GtkTreeModel *model;
786         GtkTreeIter iter, child;
787
788         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
789
790         if (!get_model_iter (self, &model, &iter))
791                 return FALSE;
792
793         /* Append a new child to the folder */
794         gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter);
795         gtk_tree_store_set (GTK_TREE_STORE (model), &child,
796                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
797                             tny_folder_get_name (TNY_FOLDER (folder)),
798                             TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN,
799                             tny_folder_get_unread_count (TNY_FOLDER (folder)),
800                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
801                             tny_folder_get_folder_type (TNY_FOLDER (folder)),
802                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
803                             folder, -1);
804
805         return TRUE;
806 }
807
808 static gint
809 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
810           gpointer user_data)
811 {
812         gint cmp;
813         gchar         *name1, *name2;
814         TnyFolderType type;
815         TnyFolder     *folder1, *folder2;
816         
817         gtk_tree_model_get (tree_model, iter1,
818                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
819                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
820                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
821                             -1);
822         gtk_tree_model_get (tree_model, iter2,
823                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
824                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
825                             -1);
826
827         /* local_folders should be the last one */
828         if (type == TNY_FOLDER_TYPE_ROOT) {
829                 /* the account name is also the name of the root folder
830                  * in case of local folders */
831                 if (name1 && strcmp (name1, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0)
832                         cmp = +1;
833                 else if (name2 && strcmp (name2, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0)
834                         cmp = -1;
835                 else 
836                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
837         } else 
838                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
839
840         
841         if (folder1)
842                 g_object_unref(G_OBJECT(folder1));
843         if (folder2)
844                 g_object_unref(G_OBJECT(folder2));
845         
846         g_free (name1);
847         g_free (name2);
848
849         return cmp;     
850 }
851
852 /*****************************************************************************/
853 /*                        DRAG and DROP stuff                                */
854 /*****************************************************************************/
855
856 /*
857  * This function fills the #GtkSelectionData with the row and the
858  * model that has been dragged. It's called when this widget is a
859  * source for dnd after the event drop happened
860  */
861 static void
862 on_drag_data_get (GtkWidget *widget, 
863                   GdkDragContext *context, 
864                   GtkSelectionData *selection_data, 
865                   guint info, 
866                   guint time, 
867                   gpointer data)
868 {
869         GtkTreeSelection *selection;
870         GtkTreeModel *model_sort, *model;
871         GtkTreeIter iter;
872         GtkTreePath *source_row_sort;
873         GtkTreePath *source_row;
874
875         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
876         gtk_tree_selection_get_selected (selection, &model_sort, &iter);
877         source_row_sort = gtk_tree_model_get_path (model_sort, &iter);
878
879         /* Get the unsorted path and model */
880         model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model_sort));
881         source_row = gtk_tree_model_sort_convert_path_to_child_path (GTK_TREE_MODEL_SORT (model_sort),
882                                                                      source_row_sort);
883
884         gtk_tree_set_row_drag_data (selection_data,
885                                     model,
886                                     source_row);
887
888         gtk_tree_path_free (source_row_sort);
889         gtk_tree_path_free (source_row);
890 }
891
892
893 static void
894 on_progress_changed (ModestMailOperation *mail_op, gpointer user_data)
895 {
896         ModestMailOperationQueue *queue;
897
898         if (modest_mail_operation_get_status (mail_op) == 
899             MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
900                 g_print ("Bien bien");
901         }
902         queue = modest_runtime_get_mail_operation_queue ();
903
904         modest_mail_operation_queue_remove (queue, mail_op);
905         g_object_unref (G_OBJECT (mail_op));
906 }
907
908 /*
909  * This function receives the data set by the "drag-data-get" signal
910  * handler. This information comes within the #GtkSelectionData. This
911  * function will manage both the drags of folders of the treeview and
912  * drags of headers of the header view widget.
913  */
914 static void 
915 on_drag_data_received (GtkWidget *widget, 
916                        GdkDragContext *context, 
917                        gint x, 
918                        gint y, 
919                        GtkSelectionData *selection_data, 
920                        guint target_type, 
921                        guint time, 
922                        gpointer data)
923 {
924         GtkWidget *source_widget;
925         GtkTreeModel *model_sort, *dest_model, *source_model;
926         GtkTreePath *source_row, *dest_row, *child_dest_row;
927         GtkTreeViewDropPosition pos;
928         gboolean success = FALSE, delete_source = FALSE;
929
930         /* Do not allow further process */
931         g_signal_stop_emission_by_name (widget, "drag-data-received");
932
933         /* Check if the get_data failed */
934         if (selection_data == NULL || selection_data->length < 0)
935                 return;
936
937         /* Get the action */
938         if (context->action == GDK_ACTION_MOVE)
939                 delete_source = TRUE;
940
941         /* Get the models */
942         source_widget = gtk_drag_get_source_widget (context);
943         model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
944
945         gtk_tree_get_row_drag_data (selection_data,
946                                     &source_model,
947                                     &source_row);
948
949         /* Select the destination model */
950         if (source_widget == widget)
951                 dest_model = source_model;
952         else
953                 dest_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model_sort));
954
955
956         /* Get the path to the destination row. Can not call
957            gtk_tree_view_get_drag_dest_row() because the source row
958            it's not selected anymore */
959         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
960                                            x, y,
961                                            &dest_row,
962                                            &pos);
963
964         /* Only allow drops IN other rows */
965         if (!dest_row || 
966             pos == GTK_TREE_VIEW_DROP_BEFORE ||
967             pos == GTK_TREE_VIEW_DROP_AFTER)
968                 goto finish;
969
970         /* Get the destination row in the usorted model */
971         child_dest_row = 
972                 gtk_tree_model_sort_convert_path_to_child_path (GTK_TREE_MODEL_SORT (model_sort),
973                                                                 dest_row);
974
975         /* Drags from the header view */
976         if ((target_type == FOLDER_ROW) && (source_widget != widget)) {
977                 TnyHeader *header;
978                 TnyFolder *folder;
979                 ModestMailOperationQueue *queue;
980                 ModestMailOperation *mail_op;
981                 gboolean started;
982                 GtkTreeIter source_iter, dest_iter;
983
984                 /* Get header */
985                 gtk_tree_model_get_iter (source_model, &source_iter, source_row);
986                 gtk_tree_model_get (source_model, &source_iter, 
987                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
988                                     &header, -1);
989
990                 /* Get Folder */
991                 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
992                 gtk_tree_model_get (dest_model, &dest_iter, 
993                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
994                                     &folder, -1);
995
996                 /* Transfer message */
997                 queue = modest_runtime_get_mail_operation_queue ();
998                 mail_op = modest_mail_operation_new ();
999                 started = modest_mail_operation_xfer_msg (mail_op, header,
1000                                                           folder, delete_source);
1001                 if (started) {
1002                         g_signal_connect (G_OBJECT (mail_op), "progress_changed", 
1003                                           G_CALLBACK (on_progress_changed), queue);
1004                         modest_mail_operation_queue_add (queue, mail_op);
1005                 } else {
1006                         const GError *error;
1007                         error = modest_mail_operation_get_error (mail_op);
1008                         g_warning ("Error trying to transfer messages: %s\n",
1009                                    error->message);
1010                 }
1011                 g_object_unref (G_OBJECT (mail_op));
1012                 g_object_unref (G_OBJECT (header));
1013                 g_object_unref (G_OBJECT (folder));
1014
1015                 /* FIXME */
1016                 success = TRUE;
1017         } else {
1018                 GtkTreeRowReference *source_row_reference;
1019                 GtkTreeIter parent_iter, iter;
1020                 TnyFolder *folder, *new_folder;
1021                 TnyFolderStore *parent_folder;
1022
1023                 /* Check if the drag is possible */
1024                 if (!gtk_tree_path_compare (source_row, child_dest_row))
1025                         goto out;
1026
1027                 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model),
1028                                                            child_dest_row,
1029                                                            selection_data))
1030                         goto out;
1031
1032                 /* Do the mail operation */
1033                 gtk_tree_model_get_iter (source_model, &parent_iter, child_dest_row);
1034                 gtk_tree_model_get_iter (source_model, &iter, source_row);
1035                 gtk_tree_model_get (source_model, &parent_iter, 
1036                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1037                                     &parent_folder, -1);
1038                 gtk_tree_model_get (source_model, &iter,
1039                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1040                                     &folder, -1);
1041                 
1042                 g_signal_emit (G_OBJECT (widget), signals[FOLDER_XFER_SIGNAL], 0,
1043                                folder, parent_folder, delete_source, &new_folder);
1044
1045                 g_object_unref (G_OBJECT (parent_folder));
1046                 g_object_unref (G_OBJECT (folder));
1047
1048                 if (!new_folder)
1049                         goto out;
1050
1051                 /* Get a row reference to the source path because the path
1052                    could change after the insertion. The gtk_drag_finish() is
1053                    not able to delete the source because that, so we have to
1054                    do it manually */
1055                 source_row_reference = gtk_tree_row_reference_new (source_model, source_row);
1056                 gtk_tree_path_free (source_row);
1057
1058                 /* Insert the dragged row as a child of the dest row */
1059                 gtk_tree_path_down (child_dest_row);
1060                 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (dest_model),
1061                                                            child_dest_row,
1062                                                            selection_data)) {
1063
1064                         {
1065                                 GtkTreeIter iter;
1066
1067                                 gtk_tree_model_get_iter (dest_model, &iter, child_dest_row);
1068                                 gtk_tree_store_set (GTK_TREE_STORE (dest_model), &iter,
1069                                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, new_folder, -1);
1070                                 g_object_unref (G_OBJECT (new_folder));
1071                         }
1072
1073                         source_row = gtk_tree_row_reference_get_path (source_row_reference);
1074
1075                         success = TRUE;
1076                 }
1077                 gtk_tree_row_reference_free (source_row_reference);
1078                 
1079                 if (folder)
1080                         g_object_unref (G_OBJECT(folder));
1081                 if (parent_folder)
1082                         g_object_unref (G_OBJECT(parent_folder));
1083                 
1084         }
1085  out:
1086         gtk_tree_path_free (dest_row);
1087         gtk_tree_path_free (child_dest_row);
1088
1089         /* Save the new path, will be used by the
1090            drag-data-delete handler */
1091         if (success && delete_source)
1092                 g_object_set_data (G_OBJECT (source_widget),
1093                                    ROW_REF_DATA_NAME,
1094                                    gtk_tree_path_copy (source_row));
1095
1096         /* Clean dest row */
1097         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
1098                                          NULL,
1099                                          GTK_TREE_VIEW_DROP_BEFORE);
1100
1101  finish:
1102         gtk_drag_finish (context, success, (success && delete_source), time);
1103 }
1104
1105 /*
1106  * We define a "drag-drop" signal handler because we do not want to
1107  * use the default one, because the default one always calls
1108  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1109  * signal handler, because there we have all the information available
1110  * to know if the dnd was a success or not.
1111  */
1112 static gboolean
1113 drag_drop_cb (GtkWidget      *widget,
1114               GdkDragContext *context,
1115               gint            x,
1116               gint            y,
1117               guint           time,
1118               gpointer        user_data) 
1119 {
1120         if (!context->targets)
1121                 return FALSE;
1122
1123         /* Request the data from the source. */
1124         gtk_drag_get_data(widget, 
1125                           context, 
1126                           GDK_POINTER_TO_ATOM (context->targets->data), 
1127                           time);
1128
1129     return TRUE;
1130 }
1131
1132 /*
1133  * This function deletes the data that has been dragged from its
1134  * source widget. Since is a function received by the source of the
1135  * drag, this function only deletes rows of the folder view
1136  * widget. The header view widget will need to define its own one.
1137  */
1138 static void 
1139 drag_data_delete_cb (GtkWidget      *widget,
1140                      GdkDragContext *context,
1141                      gpointer        user_data)
1142 {
1143         GtkTreePath *source_row;
1144         GtkTreeModel *model_sort, *model;
1145
1146         model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1147         model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model_sort));
1148         source_row = g_object_steal_data (G_OBJECT (widget), ROW_REF_DATA_NAME);
1149
1150         /* Delete the source row */
1151         gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
1152                                                source_row);
1153
1154         gtk_tree_path_free (source_row);
1155 }
1156
1157 /*
1158  * This function expands a node of a tree view if it's not expanded
1159  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1160  * does that, so that's why they're here.
1161  */
1162 static gint
1163 expand_row_timeout (gpointer data)
1164 {
1165         GtkTreeView *tree_view = data;
1166         GtkTreePath *dest_path = NULL;
1167         GtkTreeViewDropPosition pos;
1168         gboolean result = FALSE;
1169         
1170         GDK_THREADS_ENTER ();
1171         
1172         gtk_tree_view_get_drag_dest_row (tree_view,
1173                                          &dest_path,
1174                                          &pos);
1175         
1176         if (dest_path &&
1177             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1178              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1179                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1180                 gtk_tree_path_free (dest_path);
1181         }
1182         else {
1183                 if (dest_path)
1184                         gtk_tree_path_free (dest_path);
1185                 
1186                 result = TRUE;
1187         }
1188         
1189         GDK_THREADS_LEAVE ();
1190
1191         return result;
1192 }
1193
1194 /*
1195  * This function is called whenever the pointer is moved over a widget
1196  * while dragging some data. It installs a timeout that will expand a
1197  * node of the treeview if not expanded yet. This function also calls
1198  * gdk_drag_status in order to set the suggested action that will be
1199  * used by the "drag-data-received" signal handler to know if we
1200  * should do a move or just a copy of the data.
1201  */
1202 static gboolean
1203 on_drag_motion (GtkWidget      *widget,
1204                 GdkDragContext *context,
1205                 gint            x,
1206                 gint            y,
1207                 guint           time,
1208                 gpointer        user_data)  
1209 {
1210         GtkTreeViewDropPosition pos;
1211         GtkTreePath *dest_row;
1212         ModestFolderViewPrivate *priv;
1213         GdkDragAction suggested_action;
1214
1215         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1216
1217         if (priv->timer_expander != 0) {
1218                 g_source_remove (priv->timer_expander);
1219                 priv->timer_expander = 0;
1220         }
1221
1222         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1223                                            x, y,
1224                                            &dest_row,
1225                                            &pos);
1226
1227         if (!dest_row)
1228                 return FALSE;
1229
1230         /* Expand the selected row after 1/2 second */
1231         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1232                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1233                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1234         }
1235         gtk_tree_path_free (dest_row);
1236
1237         /* Select the desired action. By default we pick MOVE */
1238         suggested_action = GDK_ACTION_MOVE;
1239
1240         if (context->actions == GDK_ACTION_COPY)
1241             gdk_drag_status(context, GDK_ACTION_COPY, time);
1242         else if (context->actions == GDK_ACTION_MOVE)
1243             gdk_drag_status(context, GDK_ACTION_MOVE, time);
1244         else if (context->actions & suggested_action)
1245             gdk_drag_status(context, suggested_action, time);
1246         else
1247             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1248
1249         return TRUE;
1250 }
1251
1252
1253 /* Folder view drag types */
1254 const GtkTargetEntry folder_view_drag_types[] =
1255 {
1256         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, FOLDER_ROW },
1257 };
1258
1259 /*
1260  * This function sets the treeview as a source and a target for dnd
1261  * events. It also connects all the requirede signals.
1262  */
1263 static void
1264 setup_drag_and_drop (GtkTreeView *self)
1265 {
1266         /* Set up the folder view as a dnd destination. Set only the
1267            highlight flag, otherwise gtk will have a different
1268            behaviour */
1269         gtk_drag_dest_set (GTK_WIDGET (self),
1270                            GTK_DEST_DEFAULT_HIGHLIGHT,
1271                            folder_view_drag_types,
1272                            G_N_ELEMENTS (folder_view_drag_types),
1273                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
1274
1275         gtk_signal_connect(GTK_OBJECT (self),
1276                            "drag_data_received",
1277                            GTK_SIGNAL_FUNC(on_drag_data_received),
1278                            NULL);
1279
1280
1281         /* Set up the treeview as a dnd source */
1282         gtk_drag_source_set (GTK_WIDGET (self),
1283                              GDK_BUTTON1_MASK,
1284                              folder_view_drag_types,
1285                              G_N_ELEMENTS (folder_view_drag_types),
1286                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1287
1288         gtk_signal_connect(GTK_OBJECT (self),
1289                            "drag_data_delete",
1290                            GTK_SIGNAL_FUNC(drag_data_delete_cb),
1291                            NULL);
1292
1293         gtk_signal_connect(GTK_OBJECT (self),
1294                            "drag_motion",
1295                            GTK_SIGNAL_FUNC(on_drag_motion),
1296                            NULL);
1297
1298
1299         gtk_signal_connect(GTK_OBJECT (self),
1300                            "drag_data_get",
1301                            GTK_SIGNAL_FUNC(on_drag_data_get),
1302                            NULL);
1303
1304         gtk_signal_connect(GTK_OBJECT (self),
1305                            "drag_drop",
1306                            GTK_SIGNAL_FUNC(drag_drop_cb),
1307                            NULL);
1308 }