1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
30 #include <glib/gi18n.h>
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-folder.h>
37 #include <tny-account-store.h>
38 #include <tny-account.h>
39 #include <tny-folder.h>
40 #include <tny-camel-folder.h>
41 #include <tny-simple-list.h>
42 #include <modest-tny-folder.h>
43 #include <modest-marshal.h>
44 #include <modest-icon-names.h>
45 #include <modest-tny-account-store.h>
46 #include <modest-text-utils.h>
47 #include <modest-runtime.h>
48 #include "modest-folder-view.h"
50 /* 'private'/'protected' functions */
51 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
52 static void modest_folder_view_init (ModestFolderView *obj);
53 static void modest_folder_view_finalize (GObject *obj);
55 static gboolean update_model (ModestFolderView *self,
56 ModestTnyAccountStore *account_store);
58 static gboolean update_model_empty (ModestFolderView *self);
60 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
62 static gint cmp_rows (GtkTreeModel *tree_model,
68 static void drag_data_get_cb (GtkWidget *widget,
69 GdkDragContext *context,
70 GtkSelectionData *selection_data,
75 static void drag_data_received_cb (GtkWidget *widget,
76 GdkDragContext *context,
79 GtkSelectionData *selection_data,
84 static gboolean drag_motion_cb (GtkWidget *widget,
85 GdkDragContext *context,
91 static gint expand_row_timeout (gpointer data);
93 static void setup_drag_and_drop (GtkTreeView *self);
96 static const GtkTargetEntry drag_types[] =
98 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 2 }
103 FOLDER_SELECTION_CHANGED_SIGNAL,
108 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
109 struct _ModestFolderViewPrivate {
110 TnyAccountStore *account_store;
111 TnyFolder *cur_folder;
112 GtkTreeRowReference *cur_row;
116 GtkTreeSelection *cur_selection;
117 TnyFolderStoreQuery *query;
118 guint timer_expander;
120 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
121 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
122 MODEST_TYPE_FOLDER_VIEW, \
123 ModestFolderViewPrivate))
125 static GObjectClass *parent_class = NULL;
127 static guint signals[LAST_SIGNAL] = {0};
130 modest_folder_view_get_type (void)
132 static GType my_type = 0;
134 static const GTypeInfo my_info = {
135 sizeof(ModestFolderViewClass),
136 NULL, /* base init */
137 NULL, /* base finalize */
138 (GClassInitFunc) modest_folder_view_class_init,
139 NULL, /* class finalize */
140 NULL, /* class data */
141 sizeof(ModestFolderView),
143 (GInstanceInitFunc) modest_folder_view_init,
147 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
155 modest_folder_view_class_init (ModestFolderViewClass *klass)
157 GObjectClass *gobject_class;
158 gobject_class = (GObjectClass*) klass;
160 parent_class = g_type_class_peek_parent (klass);
161 gobject_class->finalize = modest_folder_view_finalize;
163 klass->update_model = modest_folder_view_update_model;
165 g_type_class_add_private (gobject_class,
166 sizeof(ModestFolderViewPrivate));
168 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
169 g_signal_new ("folder_selection_changed",
170 G_TYPE_FROM_CLASS (gobject_class),
172 G_STRUCT_OFFSET (ModestFolderViewClass,
173 folder_selection_changed),
175 modest_marshal_VOID__POINTER_BOOLEAN,
176 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
179 * ModestFolderView::folder-moved
180 * @self: the #MailOperation that emits the signal
181 * @folder: the #TnyFolder that is going to be moved
182 * @parent: then #TnyFolderStore that is going to be the new parent
183 * @done: indicates if the folder move was correctly completed or not
184 * @user_data: user data set when the signal handler was connected
186 * Emitted when a the user wants to move a folder through a
187 * drag and drop action
189 signals[FOLDER_MOVED_SIGNAL] =
190 g_signal_new ("folder_moved",
191 G_TYPE_FROM_CLASS (gobject_class),
193 G_STRUCT_OFFSET (ModestFolderViewClass, folder_moved),
195 modest_marshal_VOID__POINTER_POINTER_POINTER,
196 G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
202 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
203 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
211 g_return_if_fail (column);
212 g_return_if_fail (tree_model);
214 gtk_tree_model_get (tree_model, iter,
215 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
216 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
217 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
218 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
220 rendobj = G_OBJECT(renderer);
225 if (folder && type != TNY_FOLDER_TYPE_ROOT) { /* FIXME: tnymail bug? crashes with root folders */
226 if (modest_tny_folder_is_local_folder (folder)) {
228 type = modest_tny_folder_get_local_folder_type (folder);
229 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
231 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
234 } else if (folder && type == TNY_FOLDER_TYPE_ROOT) {
235 if (strcmp (fname, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0) {/* FIXME: hack */
237 fname = g_strdup (MODEST_LOCAL_FOLDERS_DISPLAY_NAME);
242 gchar *folder_title = g_strdup_printf ("%s (%d)", fname, unread);
243 g_object_set (rendobj,"text", folder_title, "weight", 800, NULL);
244 g_free (folder_title);
246 g_object_set (rendobj,"text", fname, "weight", 400, NULL);
253 get_cached_icon (const gchar *name)
258 static GHashTable *icon_cache = NULL;
260 g_return_val_if_fail (name, NULL);
262 if (G_UNLIKELY(!icon_cache))
263 icon_cache = modest_cache_mgr_get_cache (modest_runtime_get_cache_mgr(),
264 MODEST_CACHE_MGR_CACHE_TYPE_PIXBUF);
266 if (!icon_cache || !g_hash_table_lookup_extended (icon_cache, name, &orig_key, &pixbuf)) {
267 pixbuf = (gpointer)gdk_pixbuf_new_from_file (name, &err);
269 g_printerr ("modest: error in icon factory while loading '%s': %s\n",
273 /* if we cannot find it, we still insert (if we have a cache), so we get the error
276 g_hash_table_insert (icon_cache, g_strdup(name),(gpointer)pixbuf);
278 return GDK_PIXBUF(pixbuf);
283 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
284 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
292 rendobj = G_OBJECT(renderer);
293 gtk_tree_model_get (tree_model, iter,
294 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
295 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
296 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread, -1);
297 rendobj = G_OBJECT(renderer);
299 if (type == TNY_FOLDER_TYPE_NORMAL || type == TNY_FOLDER_TYPE_UNKNOWN) {
300 type = modest_tny_folder_guess_folder_type_from_name (fname);
305 case TNY_FOLDER_TYPE_ROOT:
306 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_ACCOUNT);
308 case TNY_FOLDER_TYPE_INBOX:
309 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_INBOX);
311 case TNY_FOLDER_TYPE_OUTBOX:
312 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_OUTBOX);
314 case TNY_FOLDER_TYPE_JUNK:
315 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_JUNK);
317 case TNY_FOLDER_TYPE_SENT:
318 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_SENT);
320 case TNY_FOLDER_TYPE_TRASH:
321 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_TRASH);
323 case TNY_FOLDER_TYPE_DRAFTS:
324 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_DRAFTS);
326 case TNY_FOLDER_TYPE_NOTES:
327 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_NOTES);
329 case TNY_FOLDER_TYPE_CALENDAR:
330 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_CALENDAR);
332 case TNY_FOLDER_TYPE_CONTACTS:
333 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_CONTACTS);
335 case TNY_FOLDER_TYPE_NORMAL:
337 pixbuf = get_cached_icon (MODEST_FOLDER_ICON_NORMAL);
340 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
344 modest_folder_view_init (ModestFolderView *obj)
346 ModestFolderViewPrivate *priv;
347 GtkTreeViewColumn *column;
348 GtkCellRenderer *renderer;
349 GtkTreeSelection *sel;
351 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
353 priv->timer_expander = 0;
354 priv->account_store = NULL;
355 priv->cur_folder = NULL;
356 priv->cur_row = NULL;
358 priv->lock = g_mutex_new ();
360 column = gtk_tree_view_column_new ();
361 gtk_tree_view_append_column (GTK_TREE_VIEW(obj),column);
363 renderer = gtk_cell_renderer_pixbuf_new();
364 gtk_tree_view_column_pack_start (column, renderer, FALSE);
365 gtk_tree_view_column_set_cell_data_func(column, renderer,
366 icon_cell_data, NULL, NULL);
368 renderer = gtk_cell_renderer_text_new();
369 gtk_tree_view_column_pack_start (column, renderer, FALSE);
370 gtk_tree_view_column_set_cell_data_func(column, renderer,
371 text_cell_data, NULL, NULL);
373 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
374 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
376 gtk_tree_view_column_set_spacing (column, 2);
377 gtk_tree_view_column_set_resizable (column, TRUE);
378 gtk_tree_view_column_set_fixed_width (column, TRUE);
379 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), FALSE);
380 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), FALSE);
382 setup_drag_and_drop (GTK_TREE_VIEW(obj));
386 modest_folder_view_finalize (GObject *obj)
388 ModestFolderViewPrivate *priv;
389 GtkTreeSelection *sel;
391 g_return_if_fail (obj);
393 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
395 if (priv->timer_expander != 0) {
396 g_source_remove (priv->timer_expander);
397 priv->timer_expander = 0;
400 if (priv->account_store) {
401 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
403 g_object_unref (G_OBJECT(priv->account_store));
404 priv->account_store = NULL;
408 g_mutex_free (priv->lock);
413 g_object_unref (G_OBJECT (priv->query));
417 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
419 g_signal_handler_disconnect (G_OBJECT(sel), priv->sig2);
421 G_OBJECT_CLASS(parent_class)->finalize (obj);
426 on_account_update (TnyAccountStore *account_store, const gchar *account,
429 if (!update_model (MODEST_FOLDER_VIEW(user_data),
430 MODEST_TNY_ACCOUNT_STORE(account_store)))
431 g_printerr ("modest: failed to update model for changes in '%s'",
436 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
438 GtkTreeViewColumn *col;
440 g_return_if_fail (self);
442 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
444 g_printerr ("modest: failed get column for title\n");
448 gtk_tree_view_column_set_title (col, title);
449 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
454 modest_folder_view_new (ModestTnyAccountStore *account_store,
455 TnyFolderStoreQuery *query)
458 ModestFolderViewPrivate *priv;
459 GtkTreeSelection *sel;
461 g_return_val_if_fail (account_store, NULL);
463 self = G_OBJECT(g_object_new(MODEST_TYPE_FOLDER_VIEW, NULL));
464 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
466 priv->account_store = g_object_ref (G_OBJECT (account_store));
468 priv->query = g_object_ref (G_OBJECT (query));
470 if (!update_model (MODEST_FOLDER_VIEW(self),
471 MODEST_TNY_ACCOUNT_STORE(account_store)))
472 g_printerr ("modest: failed to update model\n");
474 priv->sig1 = g_signal_connect (G_OBJECT(account_store), "account_update",
475 G_CALLBACK (on_account_update), self);
477 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
478 priv->sig2 = g_signal_connect (sel, "changed",
479 G_CALLBACK(on_selection_changed), self);
480 return GTK_WIDGET(self);
485 update_model_empty (ModestFolderView *self)
487 ModestFolderViewPrivate *priv;
489 g_return_val_if_fail (self, FALSE);
490 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
492 g_signal_emit (G_OBJECT(self), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
498 /* this feels dirty; any other way to expand all the root items? */
500 expand_root_items (ModestFolderView *self)
503 path = gtk_tree_path_new_first ();
505 /* all folders should have child items, so.. */
506 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
507 gtk_tree_path_next (path);
509 gtk_tree_path_free (path);
513 update_model (ModestFolderView *self, ModestTnyAccountStore *account_store)
515 ModestFolderViewPrivate *priv;
517 TnyList *account_list;
518 GtkTreeModel *model, *sortable;
520 g_return_val_if_fail (account_store, FALSE);
522 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
524 /* Notify that there is no folder selected */
525 update_model_empty (self);
527 model = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
528 account_list = TNY_LIST(model);
530 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
532 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
534 sortable = gtk_tree_model_sort_new_with_model (model);
535 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
536 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
538 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
539 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
540 cmp_rows, NULL, NULL);
543 gtk_tree_view_set_model (GTK_TREE_VIEW(self), sortable);
544 expand_root_items (self); /* expand all account folders */
547 g_object_unref (model);
553 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
555 GtkTreeModel *model_sort, *model;
556 TnyFolder *folder = NULL;
557 GtkTreeIter iter, iter_sort;
559 ModestFolderView *tree_view;
560 ModestFolderViewPrivate *priv;
563 g_return_if_fail (sel);
564 g_return_if_fail (user_data);
566 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
567 priv->cur_selection = sel;
569 /* folder was _un_selected if true */
570 if (!gtk_tree_selection_get_selected (sel, &model_sort, &iter_sort)) {
571 priv->cur_folder = NULL; /* FIXME: need this? */
572 priv->cur_row = NULL; /* FIXME: need this? */
576 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model_sort));
577 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model_sort),
581 tree_view = MODEST_FOLDER_VIEW (user_data);
583 gtk_tree_model_get (model, &iter,
584 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
585 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
588 if (type == TNY_FOLDER_TYPE_ROOT)
591 /* emit 2 signals: one for the unselection of the old one,
592 * and one for the selection of the new on */
593 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
594 priv->cur_folder, FALSE);
595 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
597 if (priv->cur_folder) {
598 tny_folder_sync (priv->cur_folder, TRUE, NULL); /* FIXME */
599 gtk_tree_row_reference_free (priv->cur_row);
602 priv->cur_folder = folder;
603 path = gtk_tree_model_get_path (model_sort, &iter_sort);
604 priv->cur_row = gtk_tree_row_reference_new (model_sort, path);
605 gtk_tree_path_free (path);
609 modest_folder_view_update_model (ModestFolderView *self, TnyAccountStore *account_store)
611 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
613 g_signal_emit (G_OBJECT(self), signals[FOLDER_SELECTION_CHANGED_SIGNAL],
616 return update_model (self, MODEST_TNY_ACCOUNT_STORE(account_store)); /* ugly */
620 modest_folder_view_get_selected (ModestFolderView *self)
622 ModestFolderViewPrivate *priv;
624 g_return_val_if_fail (self, NULL);
626 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
627 if (priv->cur_folder)
628 g_object_ref (priv->cur_folder);
630 return priv->cur_folder;
634 get_model_iter (ModestFolderView *self,
635 GtkTreeModel **model,
638 GtkTreeModel *model_sort;
639 GtkTreeIter iter_sort;
641 ModestFolderViewPrivate *priv;
643 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
645 if (!priv->cur_folder)
648 if (!gtk_tree_row_reference_valid (priv->cur_row))
651 model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
652 *model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model_sort));
654 /* Get path to retrieve iter */
655 path = gtk_tree_row_reference_get_path (priv->cur_row);
656 if (!gtk_tree_model_get_iter (model_sort, &iter_sort, path))
659 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model_sort),
666 modest_folder_view_rename (ModestFolderView *self)
670 ModestFolderViewPrivate *priv;
673 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
675 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
677 if (!get_model_iter (self, &model, &iter))
680 /* Remove old name */
681 gtk_tree_model_get (model, &iter,
682 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
687 gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
688 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
689 tny_folder_get_name (priv->cur_folder), -1);
691 /* Invalidate selection */
692 g_signal_emit (G_OBJECT(self), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
693 priv->cur_folder, TRUE);
699 modest_folder_view_add_subfolder (ModestFolderView *self, TnyFolder *folder)
702 GtkTreeIter iter, child;
704 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
706 if (!get_model_iter (self, &model, &iter))
709 /* Append a new child to the folder */
710 gtk_tree_store_append (GTK_TREE_STORE (model), &child, &iter);
711 gtk_tree_store_set (GTK_TREE_STORE (model), &child,
712 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
713 tny_folder_get_name (TNY_FOLDER (folder)),
714 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN,
715 tny_folder_get_unread_count (TNY_FOLDER (folder)),
716 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
717 tny_folder_get_folder_type (TNY_FOLDER (folder)),
718 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
725 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
729 gchar *name1, *name2;
731 TnyFolder *folder1, *folder2;
733 gtk_tree_model_get (tree_model, iter1,
734 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
735 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
736 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
738 gtk_tree_model_get (tree_model, iter2,
739 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
740 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
743 /* local_folders should be the last one */
744 if (type == TNY_FOLDER_TYPE_ROOT) {
745 /* the account name is also the name of the root folder
746 * in case of local folders */
747 if (name1 && strcmp (name1, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0)
749 else if (name2 && strcmp (name2, MODEST_LOCAL_FOLDERS_ACCOUNT_NAME) == 0)
752 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
754 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
762 /*****************************************************************************/
763 /* DRAG and DROP stuff */
764 /*****************************************************************************/
766 drag_data_get_cb (GtkWidget *widget,
767 GdkDragContext *context,
768 GtkSelectionData *selection_data,
773 GtkTreeSelection *selection;
774 GtkTreeModel *model_sort, *model;
776 GtkTreePath *source_row_sort;
777 GtkTreePath *source_row;
779 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
780 gtk_tree_selection_get_selected (selection, &model_sort, &iter);
781 source_row_sort = gtk_tree_model_get_path (model_sort, &iter);
783 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model_sort));
784 source_row = gtk_tree_model_sort_convert_path_to_child_path (GTK_TREE_MODEL_SORT (model_sort),
787 gtk_tree_set_row_drag_data (selection_data,
791 gtk_tree_path_free (source_row_sort);
792 gtk_tree_path_free (source_row);
796 drag_data_received_cb (GtkWidget *widget,
797 GdkDragContext *context,
800 GtkSelectionData *selection_data,
805 GtkTreeModel *model_sort, *model;
806 GtkTreeRowReference *source_row_reference;
807 GtkTreePath *source_row, *dest_row, *child_dest_row;
808 GtkTreeViewDropPosition pos;
809 GtkTreeIter parent_iter, iter;
811 TnyFolderStore *parent_folder;
814 g_signal_stop_emission_by_name (widget, "drag-data-received");
815 model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
817 /* Get the unsorted model, and the path to the source row */
818 gtk_tree_get_row_drag_data (selection_data,
822 /* Can not call gtk_tree_view_get_drag_dest_row() because it's
823 not selected anymore */
824 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
829 /* Only allow drops IN other rows */
831 pos == GTK_TREE_VIEW_DROP_BEFORE ||
832 pos == GTK_TREE_VIEW_DROP_AFTER)
836 gtk_tree_model_sort_convert_path_to_child_path (GTK_TREE_MODEL_SORT (model_sort),
838 gtk_tree_path_free (dest_row);
840 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
845 /* Do the mail operation */
846 gtk_tree_model_get_iter (model, &parent_iter, child_dest_row);
847 gtk_tree_model_get_iter (model, &iter, source_row);
848 gtk_tree_model_get (model, &parent_iter,
849 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder, -1);
850 gtk_tree_model_get (model, &iter,
851 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder, -1);
853 g_signal_emit (G_OBJECT (widget), signals[FOLDER_MOVED_SIGNAL], 0,
854 folder, parent_folder, &done);
858 /* Get a row reference to the source path because the path
859 could change after the insertion */
860 source_row_reference = gtk_tree_row_reference_new (model, source_row);
861 gtk_tree_path_free (source_row);
863 /* Insert the dragged row as a child of the dest row */
864 gtk_tree_path_down (child_dest_row);
865 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
870 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
872 GTK_TREE_VIEW_DROP_BEFORE);
874 /* Get the new path of the source row */
875 source_row = gtk_tree_row_reference_get_path (source_row_reference);
877 /* Delete the source row */
878 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
881 gtk_tree_path_free (source_row);
884 gtk_tree_row_reference_free (source_row_reference);
887 gtk_tree_path_free (child_dest_row);
891 expand_row_timeout (gpointer data)
893 GtkTreeView *tree_view = data;
894 GtkTreePath *dest_path = NULL;
895 GtkTreeViewDropPosition pos;
896 gboolean result = FALSE;
898 GDK_THREADS_ENTER ();
900 gtk_tree_view_get_drag_dest_row (tree_view,
905 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
906 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
907 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
908 gtk_tree_path_free (dest_path);
912 gtk_tree_path_free (dest_path);
917 GDK_THREADS_LEAVE ();
924 drag_motion_cb (GtkWidget *widget,
925 GdkDragContext *context,
931 GtkTreeViewDropPosition pos;
932 GtkTreePath *dest_row;
933 ModestFolderViewPrivate *priv;
935 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
937 if (priv->timer_expander != 0) {
938 g_source_remove (priv->timer_expander);
939 priv->timer_expander = 0;
942 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
950 /* Expand the selected row after 1/2 second */
951 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
952 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
953 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
955 gtk_tree_path_free (dest_row);
961 setup_drag_and_drop (GtkTreeView *self)
963 gtk_drag_dest_set (GTK_WIDGET (self),
964 GTK_DEST_DEFAULT_ALL,
966 G_N_ELEMENTS (drag_types),
969 gtk_signal_connect(GTK_OBJECT (self),
970 "drag_data_received",
971 GTK_SIGNAL_FUNC(drag_data_received_cb),
975 gtk_drag_source_set (GTK_WIDGET (self),
976 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
978 G_N_ELEMENTS (drag_types),
982 gtk_signal_connect(GTK_OBJECT (self),
984 GTK_SIGNAL_FUNC(drag_motion_cb),
988 gtk_signal_connect(GTK_OBJECT (self),
990 GTK_SIGNAL_FUNC(drag_data_get_cb),