1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2008 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <string.h>
24 #include <glib/gi18n.h>
25 #include <gtk/gtk.h>
26 #include "gth-file-data.h"
27 #include "glib-utils.h"
28 #include "gtk-utils.h"
29 #include "gth-file-source.h"
30 #include "gth-folder-tree.h"
31 #include "gth-icon-cache.h"
32 #include "gth-main.h"
33 #include "gth-marshal.h"
34 #include "gth-request-dialog.h"
35
36
37 #define DEFAULT_URI "gthumb-vfs:///"
38 #define EMPTY_URI "..."
39 #define LOADING_URI "."
40 #define PARENT_URI ".."
41 #define UPDATE_MONITORED_LOCATIONS_DELAY 500
42
43
44 typedef enum {
45 ENTRY_TYPE_FILE,
46 ENTRY_TYPE_PARENT,
47 ENTRY_TYPE_LOADING,
48 ENTRY_TYPE_EMPTY
49 } EntryType;
50
51
52 enum {
53 COLUMN_STYLE,
54 COLUMN_WEIGHT,
55 COLUMN_ICON,
56 COLUMN_TYPE,
57 COLUMN_FILE_DATA,
58 COLUMN_SORT_KEY,
59 COLUMN_SORT_ORDER,
60 COLUMN_SECONDARY_SORT_ORDER,
61 COLUMN_NAME,
62 COLUMN_NO_CHILD,
63 COLUMN_LOADED,
64 NUM_COLUMNS
65 };
66
67
68 enum {
69 PROP_0,
70 PROP_ROOT_URI
71 };
72
73
74 enum {
75 FOLDER_POPUP,
76 LIST_CHILDREN,
77 LOAD,
78 OPEN,
79 OPEN_PARENT,
80 RENAME,
81 LAST_SIGNAL
82 };
83
84
85 typedef struct {
86 GHashTable *locations;
87 GList *sources;
88 guint update_id;
89 } MonitorData;
90
91
92 struct _GthFolderTreePrivate {
93 GFile *root;
94 GHashTable *entry_points; /* An entry point is a root child */
95 gboolean recalc_entry_points;
96 GtkTreeStore *tree_store;
97 GtkCellRenderer *text_renderer;
98 GtkTreePath *hover_path;
99
100 /* drag-and-drop */
101
102 gboolean drag_source_enabled;
103 GdkModifierType drag_start_button_mask;
104 GtkTargetList *drag_target_list;
105 GdkDragAction drag_actions;
106
107 gboolean dragging : 1; /* Whether the user is dragging items. */
108 gboolean drag_started : 1; /* Whether the drag has started. */
109 int drag_start_x; /* The position where the drag started. */
110 int drag_start_y;
111
112 /* monitored locations */
113
114 MonitorData monitor;
115 };
116
117
118 static guint gth_folder_tree_signals[LAST_SIGNAL] = { 0 };
119
120
G_DEFINE_TYPE_WITH_CODE(GthFolderTree,gth_folder_tree,GTK_TYPE_TREE_VIEW,G_ADD_PRIVATE (GthFolderTree))121 G_DEFINE_TYPE_WITH_CODE (GthFolderTree,
122 gth_folder_tree,
123 GTK_TYPE_TREE_VIEW,
124 G_ADD_PRIVATE (GthFolderTree))
125
126
127 static void
128 gth_folder_tree_set_property (GObject *object,
129 guint property_id,
130 const GValue *value,
131 GParamSpec *pspec)
132 {
133 GthFolderTree *self;
134 const char *uri;
135
136 self = GTH_FOLDER_TREE (object);
137
138 switch (property_id) {
139 case PROP_ROOT_URI:
140 uri = g_value_get_string (value);
141 if (uri != NULL) {
142 GFile *new_root;
143
144 new_root = g_file_new_for_uri (uri);
145 if (new_root != NULL) {
146 _g_object_unref (self->priv->root);
147 self->priv->root = _g_object_ref (new_root);
148 }
149
150 _g_object_unref (new_root);
151 }
152 break;
153
154 default:
155 break;
156 }
157 }
158
159
160 static void
gth_folder_tree_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)161 gth_folder_tree_get_property (GObject *object,
162 guint property_id,
163 GValue *value,
164 GParamSpec *pspec)
165 {
166 GthFolderTree *self;
167 char *uri;
168
169 self = GTH_FOLDER_TREE (object);
170
171 switch (property_id) {
172 case PROP_ROOT_URI:
173 uri = g_file_get_uri (self->priv->root);
174 g_value_set_string (value, uri);
175 g_free (uri);
176 break;
177
178 default:
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
180 break;
181 }
182 }
183
184
185 static void remove_all_locations_from_the_monitor (GthFolderTree *folder_tree);
186
187
188 static void
gth_folder_tree_finalize(GObject * object)189 gth_folder_tree_finalize (GObject *object)
190 {
191 GthFolderTree *folder_tree;
192
193 folder_tree = GTH_FOLDER_TREE (object);
194
195 if (folder_tree->priv->drag_target_list != NULL) {
196 gtk_target_list_unref (folder_tree->priv->drag_target_list);
197 folder_tree->priv->drag_target_list = NULL;
198 }
199 if (folder_tree->priv->monitor.update_id != 0) {
200 g_source_remove (folder_tree->priv->monitor.update_id);
201 folder_tree->priv->monitor.update_id = 0;
202 }
203 g_hash_table_unref (folder_tree->priv->entry_points);
204 remove_all_locations_from_the_monitor (folder_tree);
205 g_hash_table_unref (folder_tree->priv->monitor.locations);
206 _g_object_list_unref (folder_tree->priv->monitor.sources);
207 if (folder_tree->priv->root != NULL)
208 g_object_unref (folder_tree->priv->root);
209 g_object_unref (folder_tree->priv->tree_store);
210
211 G_OBJECT_CLASS (gth_folder_tree_parent_class)->finalize (object);
212 }
213
214
215 static void
gth_folder_tree_class_init(GthFolderTreeClass * class)216 gth_folder_tree_class_init (GthFolderTreeClass *class)
217 {
218 GObjectClass *object_class;
219 GtkWidgetClass *widget_class;
220
221 object_class = (GObjectClass*) class;
222 object_class->set_property = gth_folder_tree_set_property;
223 object_class->get_property = gth_folder_tree_get_property;
224 object_class->finalize = gth_folder_tree_finalize;
225
226 widget_class = (GtkWidgetClass*) class;
227 widget_class->drag_begin = NULL;
228
229 /* properties */
230
231 g_object_class_install_property (object_class,
232 PROP_ROOT_URI,
233 g_param_spec_string ("root-uri",
234 "Root uri",
235 "The root of the folder tree as an uri",
236 NULL,
237 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
238
239 /* signals */
240
241 gth_folder_tree_signals[FOLDER_POPUP] =
242 g_signal_new ("folder_popup",
243 G_TYPE_FROM_CLASS (class),
244 G_SIGNAL_RUN_LAST,
245 G_STRUCT_OFFSET (GthFolderTreeClass, folder_popup),
246 NULL, NULL,
247 gth_marshal_VOID__OBJECT_UINT,
248 G_TYPE_NONE,
249 2,
250 G_TYPE_OBJECT,
251 G_TYPE_UINT);
252 gth_folder_tree_signals[LIST_CHILDREN] =
253 g_signal_new ("list_children",
254 G_TYPE_FROM_CLASS (class),
255 G_SIGNAL_RUN_LAST,
256 G_STRUCT_OFFSET (GthFolderTreeClass, list_children),
257 NULL, NULL,
258 g_cclosure_marshal_VOID__OBJECT,
259 G_TYPE_NONE,
260 1,
261 G_TYPE_OBJECT);
262 gth_folder_tree_signals[LOAD] =
263 g_signal_new ("load",
264 G_TYPE_FROM_CLASS (class),
265 G_SIGNAL_RUN_LAST,
266 G_STRUCT_OFFSET (GthFolderTreeClass, load),
267 NULL, NULL,
268 g_cclosure_marshal_VOID__OBJECT,
269 G_TYPE_NONE,
270 1,
271 G_TYPE_OBJECT);
272 gth_folder_tree_signals[OPEN] =
273 g_signal_new ("open",
274 G_TYPE_FROM_CLASS (class),
275 G_SIGNAL_RUN_LAST,
276 G_STRUCT_OFFSET (GthFolderTreeClass, open),
277 NULL, NULL,
278 g_cclosure_marshal_VOID__OBJECT,
279 G_TYPE_NONE,
280 1,
281 G_TYPE_OBJECT);
282 gth_folder_tree_signals[OPEN_PARENT] =
283 g_signal_new ("open_parent",
284 G_TYPE_FROM_CLASS (class),
285 G_SIGNAL_RUN_LAST,
286 G_STRUCT_OFFSET (GthFolderTreeClass, open_parent),
287 NULL, NULL,
288 g_cclosure_marshal_VOID__VOID,
289 G_TYPE_NONE,
290 0);
291 gth_folder_tree_signals[RENAME] =
292 g_signal_new ("rename",
293 G_TYPE_FROM_CLASS (class),
294 G_SIGNAL_RUN_LAST,
295 G_STRUCT_OFFSET (GthFolderTreeClass, rename),
296 NULL, NULL,
297 gth_marshal_VOID__OBJECT_STRING,
298 G_TYPE_NONE,
299 2,
300 G_TYPE_OBJECT,
301 G_TYPE_STRING);
302 }
303
304
305 static void
text_renderer_edited_cb(GtkCellRendererText * renderer,char * path,char * new_text,gpointer user_data)306 text_renderer_edited_cb (GtkCellRendererText *renderer,
307 char *path,
308 char *new_text,
309 gpointer user_data)
310 {
311 GthFolderTree *folder_tree = user_data;
312 GtkTreePath *tree_path;
313 GtkTreeIter iter;
314 EntryType entry_type;
315 GthFileData *file_data;
316 char *name;
317
318 g_object_set (folder_tree->priv->text_renderer,
319 "editable", FALSE,
320 NULL);
321
322 tree_path = gtk_tree_path_new_from_string (path);
323 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (folder_tree->priv->tree_store),
324 &iter,
325 tree_path))
326 {
327 gtk_tree_path_free (tree_path);
328 return;
329 }
330 gtk_tree_path_free (tree_path);
331
332 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store),
333 &iter,
334 COLUMN_TYPE, &entry_type,
335 COLUMN_FILE_DATA, &file_data,
336 COLUMN_NAME, &name,
337 -1);
338
339 if ((entry_type == ENTRY_TYPE_FILE) && (g_utf8_collate (name, new_text) != 0))
340 g_signal_emit (folder_tree, gth_folder_tree_signals[RENAME], 0, file_data->file, new_text);
341
342 _g_object_unref (file_data);
343 g_free (name);
344 }
345
346
347 static void
text_renderer_editing_started_cb(GtkCellRenderer * cell,GtkCellEditable * editable,const char * path,gpointer user_data)348 text_renderer_editing_started_cb (GtkCellRenderer *cell,
349 GtkCellEditable *editable,
350 const char *path,
351 gpointer user_data)
352 {
353 GthFolderTree *folder_tree = user_data;
354 GtkTreePath *tree_path;
355 GtkTreeIter iter;
356 GthFileData *file_data;
357
358 tree_path = gtk_tree_path_new_from_string (path);
359 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (folder_tree->priv->tree_store),
360 &iter,
361 tree_path))
362 {
363 gtk_tree_path_free (tree_path);
364 return;
365 }
366 gtk_tree_path_free (tree_path);
367
368 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store),
369 &iter,
370 COLUMN_FILE_DATA, &file_data,
371 -1);
372
373 if (GTK_IS_ENTRY (editable))
374 gtk_entry_set_text (GTK_ENTRY (editable), g_file_info_get_edit_name (file_data->info));
375
376 _g_object_unref (file_data);
377 }
378
379
380 static void
text_renderer_editing_canceled_cb(GtkCellRenderer * renderer,gpointer user_data)381 text_renderer_editing_canceled_cb (GtkCellRenderer *renderer,
382 gpointer user_data)
383 {
384 GthFolderTree *folder_tree = user_data;
385
386 g_object_set (folder_tree->priv->text_renderer,
387 "editable", FALSE,
388 NULL);
389 }
390
391
392 static void
add_columns(GthFolderTree * folder_tree,GtkTreeView * treeview)393 add_columns (GthFolderTree *folder_tree,
394 GtkTreeView *treeview)
395 {
396 GtkCellRenderer *renderer;
397 GtkTreeViewColumn *column;
398
399 column = gtk_tree_view_column_new ();
400
401 renderer = gtk_cell_renderer_pixbuf_new ();
402 g_object_set (renderer,
403 "follow-state", TRUE,
404 NULL);
405 gtk_tree_view_column_pack_start (column, renderer, FALSE);
406 gtk_tree_view_column_set_attributes (column, renderer,
407 "gicon", COLUMN_ICON,
408 NULL);
409
410 folder_tree->priv->text_renderer = renderer = gtk_cell_renderer_text_new ();
411 g_object_set (renderer,
412 "ellipsize", PANGO_ELLIPSIZE_END,
413 NULL);
414 g_signal_connect (folder_tree->priv->text_renderer,
415 "edited",
416 G_CALLBACK (text_renderer_edited_cb),
417 folder_tree);
418 g_signal_connect (folder_tree->priv->text_renderer,
419 "editing-started",
420 G_CALLBACK (text_renderer_editing_started_cb),
421 folder_tree);
422 g_signal_connect (folder_tree->priv->text_renderer,
423 "editing-canceled",
424 G_CALLBACK (text_renderer_editing_canceled_cb),
425 folder_tree);
426
427 gtk_tree_view_column_pack_start (column, renderer, TRUE);
428 gtk_tree_view_column_set_attributes (column, renderer,
429 "text", COLUMN_NAME,
430 "style", COLUMN_STYLE,
431 "weight", COLUMN_WEIGHT,
432 NULL);
433 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
434 gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME);
435
436 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
437 }
438
439
440 static void
open_uri(GthFolderTree * folder_tree,GthFileData * file_data,EntryType entry_type)441 open_uri (GthFolderTree *folder_tree,
442 GthFileData *file_data,
443 EntryType entry_type)
444 {
445 if (entry_type == ENTRY_TYPE_PARENT)
446 g_signal_emit (folder_tree, gth_folder_tree_signals[OPEN_PARENT], 0);
447 else if (entry_type == ENTRY_TYPE_FILE)
448 g_signal_emit (folder_tree, gth_folder_tree_signals[OPEN], 0, file_data->file);
449 }
450
451
452 static gboolean
row_activated_cb(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)453 row_activated_cb (GtkTreeView *tree_view,
454 GtkTreePath *path,
455 GtkTreeViewColumn *column,
456 gpointer user_data)
457 {
458 GthFolderTree *folder_tree = user_data;
459 GtkTreeIter iter;
460 EntryType entry_type;
461 GthFileData *file_data;
462
463 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (folder_tree->priv->tree_store),
464 &iter,
465 path))
466 {
467 return FALSE;
468 }
469
470 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store),
471 &iter,
472 COLUMN_TYPE, &entry_type,
473 COLUMN_FILE_DATA, &file_data,
474 -1);
475 open_uri (folder_tree, file_data, entry_type);
476
477 _g_object_unref (file_data);
478
479 return TRUE;
480 }
481
482
483 /* -- update_monitored_locations -- */
484
485
486 static GthFileSource *
get_monitor_file_source_for_file(GthFolderTree * folder_tree,GFile * file)487 get_monitor_file_source_for_file (GthFolderTree *folder_tree,
488 GFile *file)
489 {
490 GList *scan;
491 char *uri;
492
493 uri = g_file_get_uri (file);
494 for (scan = folder_tree->priv->monitor.sources; scan; scan = scan->next) {
495 GthFileSource *file_source = scan->data;
496
497 if (gth_file_source_supports_scheme (file_source, uri)) {
498 g_free (uri);
499 return g_object_ref (file_source);
500 }
501 }
502
503 g_free (uri);
504
505 return NULL;
506 }
507
508
509 static void
_gth_folder_tree_remove_from_monitor(GthFolderTree * folder_tree,GFile * file)510 _gth_folder_tree_remove_from_monitor (GthFolderTree *folder_tree,
511 GFile *file)
512 {
513 GthFileSource *file_source;
514
515 file_source = get_monitor_file_source_for_file (folder_tree, file);
516 if (file_source == NULL)
517 return;
518
519 gth_file_source_monitor_directory (file_source, file, FALSE);
520
521 g_object_unref (file_source);
522 }
523
524
525 static void
remove_all_locations_from_the_monitor(GthFolderTree * folder_tree)526 remove_all_locations_from_the_monitor (GthFolderTree *folder_tree)
527 {
528 GList *locations;
529 GList *scan;
530
531 locations = g_hash_table_get_keys (folder_tree->priv->monitor.locations);
532 for (scan = locations; scan; scan = scan->next)
533 _gth_folder_tree_remove_from_monitor (folder_tree, G_FILE (scan->data));
534 g_hash_table_remove_all (folder_tree->priv->monitor.locations);
535
536 g_list_free (locations);
537 }
538
539
540 static void
_gth_folder_tree_add_to_monitor(GthFolderTree * folder_tree,GFile * file)541 _gth_folder_tree_add_to_monitor (GthFolderTree *folder_tree,
542 GFile *file)
543 {
544 GthFileSource *file_source;
545
546 file_source = get_monitor_file_source_for_file (folder_tree, file);
547 if (file_source == NULL) {
548 file_source = gth_main_get_file_source (file);
549 if (file_source == NULL)
550 return;
551 folder_tree->priv->monitor.sources = g_list_prepend (folder_tree->priv->monitor.sources, g_object_ref (file_source));
552 }
553
554 gth_file_source_monitor_directory (file_source, file, TRUE);
555 g_hash_table_add (folder_tree->priv->monitor.locations, g_file_dup (file));
556
557 g_object_unref (file_source);
558 }
559
560
561 static void
add_to_open_locations(GtkTreeView * tree_view,GtkTreePath * path,gpointer user_data)562 add_to_open_locations (GtkTreeView *tree_view,
563 GtkTreePath *path,
564 gpointer user_data)
565 {
566 GHashTable *open_locations = user_data;
567 GthFileData *file_data;
568
569 file_data = gth_folder_tree_get_file (GTH_FOLDER_TREE (tree_view), path);
570 if (file_data != NULL) {
571 g_hash_table_add (open_locations, g_object_ref (file_data->file));
572 g_object_unref (file_data);
573 }
574 }
575
576
577 static gboolean
update_monitored_locations(gpointer user_data)578 update_monitored_locations (gpointer user_data)
579 {
580 GthFolderTree *folder_tree = user_data;
581 GHashTable *open_locations;
582 GList *locations;
583 GList *locations_to_remove;
584 GList *scan;
585
586 if (folder_tree->priv->monitor.update_id != 0) {
587 g_source_remove (folder_tree->priv->monitor.update_id);
588 folder_tree->priv->monitor.update_id = 0;
589 }
590
591 open_locations = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
592 gtk_tree_view_map_expanded_rows (GTK_TREE_VIEW (folder_tree),
593 add_to_open_locations,
594 open_locations);
595
596 #if 0
597 {
598 g_print ("** expanded locations **\n");
599
600 locations = g_hash_table_get_keys (open_locations);
601 for (scan = locations; scan; scan = scan->next) {
602 GFile *file = scan->data;
603
604 g_print ("\t%s\n", g_file_get_uri (file));
605 }
606 g_list_free (locations);
607 }
608 #endif
609
610 /* remove the old locations */
611
612 locations_to_remove = NULL;
613 locations = g_hash_table_get_keys (folder_tree->priv->monitor.locations);
614 for (scan = locations; scan; scan = scan->next) {
615 GFile *file = scan->data;
616
617 if (! g_hash_table_contains (open_locations, file)) {
618 _gth_folder_tree_remove_from_monitor (folder_tree, file);
619 locations_to_remove = g_list_prepend (locations_to_remove, g_object_ref (file));
620 }
621 }
622
623 for (scan = locations_to_remove; scan; scan = scan->next)
624 g_hash_table_remove (folder_tree->priv->monitor.locations, G_FILE (scan->data));
625
626 g_list_free (locations);
627 g_list_free (locations_to_remove);
628
629 /* add the new locations */
630
631 locations = g_hash_table_get_keys (open_locations);
632 for (scan = locations; scan; scan = scan->next) {
633 GFile *file = scan->data;
634
635 if (! g_hash_table_contains (folder_tree->priv->monitor.locations, file))
636 _gth_folder_tree_add_to_monitor (folder_tree, file);
637 }
638
639 g_list_free (locations);
640 g_hash_table_unref (open_locations);
641
642 return FALSE;
643 }
644
645
646 static void
queue_update_monitored_locations(GthFolderTree * folder_tree)647 queue_update_monitored_locations (GthFolderTree *folder_tree)
648 {
649 if (folder_tree->priv->monitor.update_id != 0)
650 g_source_remove (folder_tree->priv->monitor.update_id);
651 folder_tree->priv->monitor.update_id = g_timeout_add (UPDATE_MONITORED_LOCATIONS_DELAY, update_monitored_locations, folder_tree);
652 }
653
654
655 static gboolean
row_expanded_cb(GtkTreeView * tree_view,GtkTreeIter * expanded_iter,GtkTreePath * expanded_path,gpointer user_data)656 row_expanded_cb (GtkTreeView *tree_view,
657 GtkTreeIter *expanded_iter,
658 GtkTreePath *expanded_path,
659 gpointer user_data)
660 {
661 GthFolderTree *folder_tree = user_data;
662 EntryType entry_type;
663 GthFileData *file_data;
664 gboolean loaded;
665
666 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store),
667 expanded_iter,
668 COLUMN_TYPE, &entry_type,
669 COLUMN_FILE_DATA, &file_data,
670 COLUMN_LOADED, &loaded,
671 -1);
672
673 if ((entry_type == ENTRY_TYPE_FILE) && ! loaded)
674 g_signal_emit (folder_tree, gth_folder_tree_signals[LIST_CHILDREN], 0, file_data->file);
675
676 queue_update_monitored_locations (folder_tree);
677
678 _g_object_unref (file_data);
679
680 return FALSE;
681 }
682
683
684 static gboolean
row_collapsed_cb(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path,gpointer user_data)685 row_collapsed_cb (GtkTreeView *tree_view,
686 GtkTreeIter *iter,
687 GtkTreePath *path,
688 gpointer user_data)
689 {
690 queue_update_monitored_locations (GTH_FOLDER_TREE (user_data));
691 return FALSE;
692 }
693
694
695 static gboolean
popup_menu_cb(GtkWidget * widget,gpointer user_data)696 popup_menu_cb (GtkWidget *widget,
697 gpointer user_data)
698 {
699 GthFolderTree *folder_tree = user_data;
700 GtkTreeStore *tree_store = folder_tree->priv->tree_store;
701 GthFileData *file_data = NULL;
702 GtkTreeIter iter;
703
704 if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree)), NULL, &iter)) {
705 EntryType entry_type;
706
707 gtk_tree_model_get (GTK_TREE_MODEL (tree_store),
708 &iter,
709 COLUMN_TYPE, &entry_type,
710 COLUMN_FILE_DATA, &file_data,
711 -1);
712 if (entry_type != ENTRY_TYPE_FILE) {
713 _g_object_unref (file_data);
714 return FALSE;
715 }
716 }
717
718 g_signal_emit (folder_tree,
719 gth_folder_tree_signals[FOLDER_POPUP],
720 0,
721 file_data,
722 gtk_get_current_event_time ());
723
724 _g_object_unref (file_data);
725
726 return TRUE;
727 }
728
729
730 static int
button_press_cb(GtkWidget * widget,GdkEventButton * event,gpointer user_data)731 button_press_cb (GtkWidget *widget,
732 GdkEventButton *event,
733 gpointer user_data)
734 {
735 GthFolderTree *folder_tree = user_data;
736 GtkTreeStore *tree_store = folder_tree->priv->tree_store;
737 GtkTreePath *path;
738 GtkTreeIter iter;
739 gboolean retval;
740 GtkTreeViewColumn *column;
741 int cell_x;
742 int cell_y;
743
744 retval = FALSE;
745
746 gtk_widget_grab_focus (widget);
747
748 if ((event->state & GDK_SHIFT_MASK) || (event->state & GDK_CONTROL_MASK))
749 return retval;
750
751 if ((event->button != 1) && (event->button != 3))
752 return retval;
753
754 if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (folder_tree),
755 event->x, event->y,
756 &path,
757 &column,
758 &cell_x,
759 &cell_y))
760 {
761 if (event->button == 3) {
762 g_signal_emit (folder_tree,
763 gth_folder_tree_signals[FOLDER_POPUP],
764 0,
765 NULL,
766 event->time);
767 retval = TRUE;
768 }
769
770 return retval;
771 }
772
773 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store),
774 &iter,
775 path))
776 {
777 gtk_tree_path_free (path);
778 return retval;
779 }
780
781 if (event->button == 3) {
782 EntryType entry_type;
783 GthFileData *file_data;
784
785 gtk_tree_model_get (GTK_TREE_MODEL (tree_store),
786 &iter,
787 COLUMN_TYPE, &entry_type,
788 COLUMN_FILE_DATA, &file_data,
789 -1);
790
791 if (entry_type == ENTRY_TYPE_FILE) {
792 g_signal_emit (folder_tree,
793 gth_folder_tree_signals[FOLDER_POPUP],
794 0,
795 file_data,
796 event->time);
797 retval = TRUE;
798 }
799
800 _g_object_unref (file_data);
801 }
802 else if ((event->button == 1) && (event->type == GDK_BUTTON_PRESS)) {
803 /* This can be the start of a dragging action. */
804
805 if (! (event->state & GDK_CONTROL_MASK)
806 && ! (event->state & GDK_SHIFT_MASK)
807 && folder_tree->priv->drag_source_enabled)
808 {
809 folder_tree->priv->dragging = TRUE;
810 folder_tree->priv->drag_start_x = event->x;
811 folder_tree->priv->drag_start_y = event->y;
812 }
813 }
814 else if ((event->button == 1) && (event->type == GDK_2BUTTON_PRESS)) {
815 if (! gtk_tree_view_row_expanded (GTK_TREE_VIEW (folder_tree), path))
816 gtk_tree_view_expand_row (GTK_TREE_VIEW (folder_tree), path, FALSE);
817 else
818 gtk_tree_view_collapse_row (GTK_TREE_VIEW (folder_tree), path);
819 retval = TRUE;
820 }
821
822 gtk_tree_path_free (path);
823
824 return retval;
825 }
826
827
828 static gboolean
motion_notify_event_cb(GtkWidget * widget,GdkEventButton * event,gpointer user_data)829 motion_notify_event_cb (GtkWidget *widget,
830 GdkEventButton *event,
831 gpointer user_data)
832 {
833 GthFolderTree *folder_tree = user_data;
834
835 if (! folder_tree->priv->drag_source_enabled)
836 return FALSE;
837
838 if (folder_tree->priv->dragging) {
839 if (! folder_tree->priv->drag_started
840 && gtk_drag_check_threshold (widget,
841 folder_tree->priv->drag_start_x,
842 folder_tree->priv->drag_start_y,
843 event->x,
844 event->y))
845 {
846 GtkTreePath *path = NULL;
847 GdkDragContext *context;
848 int cell_x;
849 int cell_y;
850 cairo_surface_t *dnd_surface;
851
852 if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (folder_tree),
853 event->x,
854 event->y,
855 &path,
856 NULL,
857 &cell_x,
858 &cell_y))
859 {
860 return FALSE;
861 }
862
863 gtk_tree_view_set_cursor (GTK_TREE_VIEW (folder_tree), path, NULL, FALSE);
864 folder_tree->priv->drag_started = TRUE;
865
866 /**/
867
868 context = gtk_drag_begin_with_coordinates (widget,
869 folder_tree->priv->drag_target_list,
870 folder_tree->priv->drag_actions,
871 1,
872 (GdkEvent *) event,
873 -1,
874 -1);
875
876 dnd_surface = gtk_tree_view_create_row_drag_icon (GTK_TREE_VIEW (folder_tree), path);
877 gtk_drag_set_icon_surface (context, dnd_surface);
878
879 cairo_surface_destroy (dnd_surface);
880 gtk_tree_path_free (path);
881 }
882
883 return TRUE;
884 }
885
886 return FALSE;
887 }
888
889
890 static gboolean
button_release_event_cb(GtkWidget * widget,GdkEventButton * event,gpointer user_data)891 button_release_event_cb (GtkWidget *widget,
892 GdkEventButton *event,
893 gpointer user_data)
894 {
895 GthFolderTree *folder_tree = user_data;
896
897 if (folder_tree->priv->dragging) {
898 folder_tree->priv->dragging = FALSE;
899 folder_tree->priv->drag_started = FALSE;
900 }
901
902 return FALSE;
903 }
904
905
906 static void
load_uri(GthFolderTree * folder_tree,EntryType entry_type,GthFileData * file_data)907 load_uri (GthFolderTree *folder_tree,
908 EntryType entry_type,
909 GthFileData *file_data)
910 {
911 if (entry_type == ENTRY_TYPE_FILE)
912 g_signal_emit (folder_tree, gth_folder_tree_signals[LOAD], 0, file_data->file);
913 }
914
915
916 static gboolean
selection_changed_cb(GtkTreeSelection * selection,gpointer user_data)917 selection_changed_cb (GtkTreeSelection *selection,
918 gpointer user_data)
919 {
920 GthFolderTree *folder_tree = user_data;
921 GtkTreeIter iter;
922 GtkTreePath *selected_path;
923 EntryType entry_type;
924 GthFileData *file_data;
925
926 if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
927 return FALSE;
928
929 selected_path = gtk_tree_model_get_path (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter);
930
931 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store),
932 &iter,
933 COLUMN_TYPE, &entry_type,
934 COLUMN_FILE_DATA, &file_data,
935 -1);
936
937 load_uri (folder_tree, entry_type, file_data);
938
939 _g_object_unref (file_data);
940 gtk_tree_path_free (selected_path);
941
942 return FALSE;
943 }
944
945
946 static gint
column_name_compare_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)947 column_name_compare_func (GtkTreeModel *model,
948 GtkTreeIter *a,
949 GtkTreeIter *b,
950 gpointer user_data)
951 {
952 char *key_a, *key_b;
953 int order_a, order_b;
954 int sec_order_a, sec_order_b;
955 PangoStyle style_a, style_b;
956 gboolean result;
957
958 gtk_tree_model_get (model, a,
959 COLUMN_SORT_KEY, &key_a,
960 COLUMN_SORT_ORDER, &order_a,
961 COLUMN_SECONDARY_SORT_ORDER, &sec_order_a,
962 COLUMN_STYLE, &style_a,
963 -1);
964 gtk_tree_model_get (model, b,
965 COLUMN_SORT_KEY, &key_b,
966 COLUMN_SORT_ORDER, &order_b,
967 COLUMN_SECONDARY_SORT_ORDER, &sec_order_b,
968 COLUMN_STYLE, &style_b,
969 -1);
970
971 if (order_a == order_b) {
972 if (style_a == style_b) {
973 result = strcmp (key_a, key_b);
974 if (result == 0) {
975 if (sec_order_a < sec_order_b)
976 result = -1;
977 else if (sec_order_a > sec_order_b)
978 result = 1;
979 }
980 }
981 else if (style_a == PANGO_STYLE_ITALIC)
982 result = -1;
983 else
984 result = 1;
985 }
986 else if (order_a < order_b)
987 result = -1;
988 else
989 result = 1;
990
991 g_free (key_a);
992 g_free (key_b);
993
994 return result;
995 }
996
997
998 static gboolean
iter_stores_file(GtkTreeModel * tree_model,GtkTreeIter * iter,GFile * file,GtkTreeIter * file_iter)999 iter_stores_file (GtkTreeModel *tree_model,
1000 GtkTreeIter *iter,
1001 GFile *file,
1002 GtkTreeIter *file_iter)
1003 {
1004 GthFileData *iter_file_data;
1005 EntryType iter_type;
1006 gboolean found;
1007
1008 gtk_tree_model_get (tree_model, iter,
1009 COLUMN_FILE_DATA, &iter_file_data,
1010 COLUMN_TYPE, &iter_type,
1011 -1);
1012 found = (iter_type == ENTRY_TYPE_FILE) && (iter_file_data != NULL) && g_file_equal (file, iter_file_data->file);
1013
1014 _g_object_unref (iter_file_data);
1015
1016 if (found)
1017 *file_iter = *iter;
1018
1019 return found;
1020 }
1021
1022
1023 static gboolean
_gth_folder_tree_find_file_in_children(GtkTreeModel * tree_model,GFile * file,GtkTreeIter * file_iter,GtkTreeIter * root)1024 _gth_folder_tree_find_file_in_children (GtkTreeModel *tree_model,
1025 GFile *file,
1026 GtkTreeIter *file_iter,
1027 GtkTreeIter *root)
1028 {
1029 GtkTreeIter iter;
1030
1031 /* check the children... */
1032
1033 if (! gtk_tree_model_iter_children (tree_model, &iter, root))
1034 return FALSE;
1035
1036 do {
1037 if (iter_stores_file (tree_model, &iter, file, file_iter))
1038 return TRUE;
1039 }
1040 while (gtk_tree_model_iter_next (tree_model, &iter));
1041
1042 /* ...if no child stores the file, search recursively */
1043
1044 if (gtk_tree_model_iter_children (tree_model, &iter, root)) {
1045 do {
1046 if (_gth_folder_tree_find_file_in_children (tree_model, file, file_iter, &iter))
1047 return TRUE;
1048 }
1049 while (gtk_tree_model_iter_next (tree_model, &iter));
1050 }
1051
1052 return FALSE;
1053 }
1054
1055
1056 static gboolean
gth_folder_tree_get_iter(GthFolderTree * folder_tree,GFile * file,GtkTreeIter * file_iter,GtkTreeIter * root)1057 gth_folder_tree_get_iter (GthFolderTree *folder_tree,
1058 GFile *file,
1059 GtkTreeIter *file_iter,
1060 GtkTreeIter *root)
1061 {
1062 GtkTreeModel *tree_model = GTK_TREE_MODEL (folder_tree->priv->tree_store);
1063
1064 if (file == NULL)
1065 return FALSE;
1066
1067 if ((root != NULL) && iter_stores_file (tree_model, root, file, file_iter))
1068 return TRUE;
1069
1070 /* This type of search is useful to give priority to the first level
1071 * of entries which contains all the entry points.
1072 * For example if file is "file:///media/usb-disk" this function must
1073 * return the entry point corresponding to the device instead of
1074 * returing the usb-disk folder located in "file:///media". */
1075
1076 if (_gth_folder_tree_find_file_in_children (tree_model, file, file_iter, root))
1077 return TRUE;
1078
1079 return FALSE;
1080 }
1081
1082
1083 static gboolean
_gth_folder_tree_get_child(GthFolderTree * folder_tree,GFile * file,GtkTreeIter * file_iter,GtkTreeIter * parent)1084 _gth_folder_tree_get_child (GthFolderTree *folder_tree,
1085 GFile *file,
1086 GtkTreeIter *file_iter,
1087 GtkTreeIter *parent)
1088 {
1089 GtkTreeModel *tree_model = GTK_TREE_MODEL (folder_tree->priv->tree_store);
1090 GtkTreeIter iter;
1091
1092 if (! gtk_tree_model_iter_children (tree_model, &iter, parent))
1093 return FALSE;
1094
1095 do {
1096 GthFileData *test_file_data;
1097 EntryType file_entry_type;
1098
1099 gtk_tree_model_get (tree_model, &iter,
1100 COLUMN_FILE_DATA, &test_file_data,
1101 COLUMN_TYPE, &file_entry_type,
1102 -1);
1103 if ((file_entry_type == ENTRY_TYPE_FILE) && (test_file_data != NULL) && g_file_equal (file, test_file_data->file)) {
1104 _g_object_unref (test_file_data);
1105 *file_iter = iter;
1106 return TRUE;
1107 }
1108
1109 _g_object_unref (test_file_data);
1110 }
1111 while (gtk_tree_model_iter_next (tree_model, &iter));
1112
1113 return FALSE;
1114 }
1115
1116
1117 static gboolean
_gth_folder_tree_child_type_present(GthFolderTree * folder_tree,GtkTreeIter * parent,EntryType entry_type)1118 _gth_folder_tree_child_type_present (GthFolderTree *folder_tree,
1119 GtkTreeIter *parent,
1120 EntryType entry_type)
1121 {
1122 GtkTreeIter iter;
1123
1124 if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter, parent))
1125 return FALSE;
1126
1127 do {
1128 EntryType file_entry_type;
1129
1130 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter,
1131 COLUMN_TYPE, &file_entry_type,
1132 -1);
1133
1134 if (entry_type == file_entry_type)
1135 return TRUE;
1136 }
1137 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter));
1138
1139 return FALSE;
1140 }
1141
1142
1143 static void
_gth_folder_tree_add_loading_item(GthFolderTree * folder_tree,GtkTreeIter * parent,gboolean forced)1144 _gth_folder_tree_add_loading_item (GthFolderTree *folder_tree,
1145 GtkTreeIter *parent,
1146 gboolean forced)
1147 {
1148 char *sort_key;
1149 GtkTreeIter iter;
1150
1151 if (! forced && _gth_folder_tree_child_type_present (folder_tree, parent, ENTRY_TYPE_LOADING))
1152 return;
1153
1154 sort_key = g_utf8_collate_key_for_filename (LOADING_URI, -1);
1155
1156 gtk_tree_store_append (folder_tree->priv->tree_store, &iter, parent);
1157 gtk_tree_store_set (folder_tree->priv->tree_store, &iter,
1158 COLUMN_STYLE, PANGO_STYLE_ITALIC,
1159 COLUMN_TYPE, ENTRY_TYPE_LOADING,
1160 COLUMN_NAME, _("Loading…"),
1161 COLUMN_SORT_KEY, sort_key,
1162 COLUMN_SORT_ORDER, 0,
1163 COLUMN_SECONDARY_SORT_ORDER, 0,
1164 -1);
1165
1166 g_free (sort_key);
1167 }
1168
1169
1170 static void
_gth_folder_tree_add_empty_item(GthFolderTree * folder_tree,GtkTreeIter * parent)1171 _gth_folder_tree_add_empty_item (GthFolderTree *folder_tree,
1172 GtkTreeIter *parent)
1173 {
1174 char *sort_key;
1175 GtkTreeIter iter;
1176
1177 if (_gth_folder_tree_child_type_present (folder_tree, parent, ENTRY_TYPE_EMPTY))
1178 return;
1179
1180 sort_key = g_utf8_collate_key_for_filename (EMPTY_URI, -1);
1181
1182 gtk_tree_store_append (folder_tree->priv->tree_store, &iter, parent);
1183 gtk_tree_store_set (folder_tree->priv->tree_store, &iter,
1184 COLUMN_STYLE, PANGO_STYLE_ITALIC,
1185 COLUMN_TYPE, ENTRY_TYPE_EMPTY,
1186 COLUMN_NAME, _("(Empty)"),
1187 COLUMN_SORT_KEY, sort_key,
1188 COLUMN_SORT_ORDER, 0,
1189 COLUMN_SECONDARY_SORT_ORDER, 0,
1190 -1);
1191
1192 g_free (sort_key);
1193 }
1194
1195
1196 static gboolean
_gth_folder_tree_set_file_data(GthFolderTree * folder_tree,GtkTreeIter * iter,GthFileData * file_data)1197 _gth_folder_tree_set_file_data (GthFolderTree *folder_tree,
1198 GtkTreeIter *iter,
1199 GthFileData *file_data)
1200 {
1201 const char *display_name;
1202 const char *name_for_sorting;
1203 char *sort_key;
1204
1205 display_name = g_file_info_get_display_name (file_data->info);
1206 if (display_name == NULL)
1207 return FALSE;
1208
1209 name_for_sorting = g_file_info_get_edit_name (file_data->info);
1210 if (name_for_sorting == NULL)
1211 name_for_sorting = display_name;
1212
1213 sort_key = g_utf8_collate_key_for_filename (name_for_sorting, -1);
1214 gtk_tree_store_set (folder_tree->priv->tree_store, iter,
1215 COLUMN_STYLE, PANGO_STYLE_NORMAL,
1216 COLUMN_ICON, g_file_info_get_symbolic_icon (file_data->info),
1217 COLUMN_TYPE, ENTRY_TYPE_FILE,
1218 COLUMN_FILE_DATA, file_data,
1219 COLUMN_NAME, display_name,
1220 COLUMN_SORT_KEY, sort_key,
1221 COLUMN_SORT_ORDER, g_file_info_get_sort_order (file_data->info),
1222 COLUMN_SECONDARY_SORT_ORDER, _g_file_info_get_secondary_sort_order (file_data->info),
1223 COLUMN_NO_CHILD, g_file_info_get_attribute_boolean (file_data->info, "gthumb::no-child"),
1224 COLUMN_LOADED, FALSE,
1225 -1);
1226
1227 g_free (sort_key);
1228
1229 return TRUE;
1230 }
1231
1232
1233 static gboolean
_gth_folder_tree_iter_get_no_child(GthFolderTree * folder_tree,GtkTreeIter * iter)1234 _gth_folder_tree_iter_get_no_child (GthFolderTree *folder_tree,
1235 GtkTreeIter *iter)
1236 {
1237 gboolean no_child;
1238
1239 if (iter == NULL)
1240 return FALSE;
1241
1242 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store), iter,
1243 COLUMN_NO_CHILD, &no_child,
1244 -1);
1245
1246 return no_child;
1247 }
1248
1249
1250 static void
_gth_folder_tree_update_entry_points(GthFolderTree * folder_tree)1251 _gth_folder_tree_update_entry_points (GthFolderTree *folder_tree)
1252 {
1253 GtkTreeIter iter;
1254
1255 if (! folder_tree->priv->recalc_entry_points)
1256 return;
1257
1258 folder_tree->priv->recalc_entry_points = FALSE;
1259
1260 g_hash_table_remove_all (folder_tree->priv->entry_points);
1261
1262 if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter, NULL))
1263 return;
1264
1265 do {
1266 GthFileData *file_data;
1267 EntryType file_entry_type;
1268
1269 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter,
1270 COLUMN_FILE_DATA, &file_data,
1271 COLUMN_TYPE, &file_entry_type,
1272 -1);
1273 if ((file_entry_type == ENTRY_TYPE_FILE) && (file_data != NULL))
1274 g_hash_table_add (folder_tree->priv->entry_points, g_object_ref (file_data->file));
1275
1276 _g_object_unref (file_data);
1277 }
1278 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter));
1279 }
1280
1281
1282 /*
1283 * Returns TRUE if file_data points to a folder contained in the entry point
1284 * list but it's not real entry point, for example it returns TRUE for
1285 * '/home/user/Images' or '/home/user/Documents'.
1286 * This entries are duplicates of the entry points and are treated in a special
1287 * way to avoid confusion.
1288 * */
1289 static gboolean
_gth_folder_tree_is_entry_point_dup(GthFolderTree * folder_tree,GtkTreeIter * iter,GthFileData * file_data)1290 _gth_folder_tree_is_entry_point_dup (GthFolderTree *folder_tree,
1291 GtkTreeIter *iter,
1292 GthFileData *file_data)
1293 {
1294 _gth_folder_tree_update_entry_points (folder_tree);
1295
1296 if (g_hash_table_lookup (folder_tree->priv->entry_points, file_data->file) == NULL)
1297 return FALSE;
1298
1299 return ! g_file_info_get_attribute_boolean (file_data->info, "gthumb::entry-point");
1300 }
1301
1302
1303 static gboolean
_gth_folder_tree_add_file(GthFolderTree * folder_tree,GtkTreeIter * parent,GthFileData * fd)1304 _gth_folder_tree_add_file (GthFolderTree *folder_tree,
1305 GtkTreeIter *parent,
1306 GthFileData *fd)
1307 {
1308 GtkTreeIter iter;
1309
1310 if (g_file_info_get_file_type (fd->info) != G_FILE_TYPE_DIRECTORY)
1311 return FALSE;
1312
1313 /* add the folder */
1314
1315 gtk_tree_store_append (folder_tree->priv->tree_store, &iter, parent);
1316 if (! _gth_folder_tree_set_file_data (folder_tree, &iter, fd)) {
1317 gtk_tree_store_remove (folder_tree->priv->tree_store, &iter);
1318 return FALSE;
1319 }
1320
1321 if (g_file_info_get_attribute_boolean (fd->info, "gthumb::entry-point"))
1322 gtk_tree_store_set (folder_tree->priv->tree_store, &iter,
1323 COLUMN_WEIGHT, PANGO_WEIGHT_BOLD,
1324 -1);
1325 else
1326 gtk_tree_store_set (folder_tree->priv->tree_store, &iter,
1327 COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL,
1328 -1);
1329
1330 if (! g_file_info_get_attribute_boolean (fd->info, "gthumb::no-child")
1331 && ! _gth_folder_tree_is_entry_point_dup (folder_tree, &iter, fd))
1332 {
1333 _gth_folder_tree_add_loading_item (folder_tree, &iter, TRUE);
1334 }
1335
1336 return TRUE;
1337 }
1338
1339
1340 static void
gth_folder_tree_init(GthFolderTree * folder_tree)1341 gth_folder_tree_init (GthFolderTree *folder_tree)
1342 {
1343 GtkTreeSelection *selection;
1344
1345 folder_tree->priv = gth_folder_tree_get_instance_private (folder_tree);
1346 folder_tree->priv->root = g_file_new_for_uri (DEFAULT_URI);
1347 folder_tree->priv->entry_points = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
1348 folder_tree->priv->recalc_entry_points = FALSE;
1349 folder_tree->priv->tree_store = gtk_tree_store_new (NUM_COLUMNS,
1350 PANGO_TYPE_STYLE,
1351 PANGO_TYPE_WEIGHT,
1352 G_TYPE_ICON,
1353 G_TYPE_INT,
1354 G_TYPE_OBJECT,
1355 G_TYPE_STRING,
1356 G_TYPE_INT,
1357 G_TYPE_INT,
1358 G_TYPE_STRING,
1359 G_TYPE_BOOLEAN,
1360 G_TYPE_BOOLEAN);
1361 folder_tree->priv->text_renderer = NULL;
1362 folder_tree->priv->hover_path = NULL;
1363
1364 folder_tree->priv->drag_source_enabled = FALSE;
1365 folder_tree->priv->drag_start_button_mask = 0;
1366 folder_tree->priv->drag_target_list = NULL;
1367 folder_tree->priv->drag_actions = 0;
1368 folder_tree->priv->dragging = FALSE;
1369 folder_tree->priv->drag_started = FALSE;
1370 folder_tree->priv->drag_start_x = 0;
1371 folder_tree->priv->drag_start_y = 0;
1372
1373 folder_tree->priv->monitor.locations = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
1374 folder_tree->priv->monitor.sources = NULL;
1375 folder_tree->priv->monitor.update_id = 0;
1376
1377 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_tree), GTK_TREE_MODEL (folder_tree->priv->tree_store));
1378
1379 add_columns (folder_tree, GTK_TREE_VIEW (folder_tree));
1380
1381 gtk_tree_view_set_activate_on_single_click (GTK_TREE_VIEW (folder_tree), TRUE);
1382 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (folder_tree), FALSE);
1383 gtk_tree_view_set_enable_search (GTK_TREE_VIEW (folder_tree), TRUE);
1384 gtk_tree_view_set_search_column (GTK_TREE_VIEW (folder_tree), COLUMN_NAME);
1385 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (folder_tree->priv->tree_store), COLUMN_NAME, column_name_compare_func, folder_tree, NULL);
1386 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (folder_tree->priv->tree_store), COLUMN_NAME, GTK_SORT_ASCENDING);
1387
1388 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
1389 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1390 g_signal_connect (selection,
1391 "changed",
1392 G_CALLBACK (selection_changed_cb),
1393 folder_tree);
1394
1395 /**/
1396
1397 g_signal_connect (folder_tree,
1398 "popup-menu",
1399 G_CALLBACK (popup_menu_cb),
1400 folder_tree);
1401 g_signal_connect (folder_tree,
1402 "button-press-event",
1403 G_CALLBACK (button_press_cb),
1404 folder_tree);
1405 g_signal_connect (folder_tree,
1406 "motion-notify-event",
1407 G_CALLBACK (motion_notify_event_cb),
1408 folder_tree);
1409 g_signal_connect (folder_tree,
1410 "button-release-event",
1411 G_CALLBACK (button_release_event_cb),
1412 folder_tree);
1413 g_signal_connect (folder_tree,
1414 "row-activated",
1415 G_CALLBACK (row_activated_cb),
1416 folder_tree);
1417 g_signal_connect (folder_tree,
1418 "row-expanded",
1419 G_CALLBACK (row_expanded_cb),
1420 folder_tree);
1421 g_signal_connect (folder_tree,
1422 "row-collapsed",
1423 G_CALLBACK (row_collapsed_cb),
1424 folder_tree);
1425 }
1426
1427
1428 GtkWidget *
gth_folder_tree_new(const char * root)1429 gth_folder_tree_new (const char *root)
1430 {
1431 return g_object_new (GTH_TYPE_FOLDER_TREE, "root-uri", root, NULL);
1432 }
1433
1434
1435 void
gth_folder_tree_set_list(GthFolderTree * folder_tree,GFile * root,GList * files,gboolean open_parent)1436 gth_folder_tree_set_list (GthFolderTree *folder_tree,
1437 GFile *root,
1438 GList *files,
1439 gboolean open_parent)
1440 {
1441 gtk_tree_store_clear (folder_tree->priv->tree_store);
1442
1443 if (folder_tree->priv->root != NULL) {
1444 g_object_unref (folder_tree->priv->root);
1445 folder_tree->priv->root = NULL;
1446 }
1447 if (root != NULL)
1448 folder_tree->priv->root = g_file_dup (root);
1449
1450 /* add the parent folder item */
1451
1452 if (open_parent) {
1453 char *sort_key;
1454 GIcon *icon;
1455 GtkTreeIter iter;
1456
1457 sort_key = g_utf8_collate_key_for_filename (PARENT_URI, -1);
1458 icon = g_themed_icon_new ("go-up-symbolic");
1459
1460 gtk_tree_store_append (folder_tree->priv->tree_store, &iter, NULL);
1461 gtk_tree_store_set (folder_tree->priv->tree_store, &iter,
1462 COLUMN_STYLE, PANGO_STYLE_ITALIC,
1463 COLUMN_ICON, icon,
1464 COLUMN_TYPE, ENTRY_TYPE_PARENT,
1465 COLUMN_NAME, _("(Open Parent)"),
1466 COLUMN_SORT_KEY, sort_key,
1467 COLUMN_SORT_ORDER, 0,
1468 COLUMN_SECONDARY_SORT_ORDER, 0,
1469 -1);
1470
1471 g_object_unref (icon);
1472 g_free (sort_key);
1473 }
1474
1475 /* add the folder list */
1476
1477 gth_folder_tree_set_children (folder_tree, root, files);
1478 }
1479
1480
1481 /* After changing the children list, the node expander is not highlighted
1482 * anymore, this prevents the user to close the expander without moving the
1483 * mouse pointer. The problem can be fixed emitting a fake motion notify
1484 * event, this way the expander gets highlighted again and a click on the
1485 * expander will correctly collapse the node. */
1486 static void
emit_fake_motion_notify_event(GthFolderTree * folder_tree)1487 emit_fake_motion_notify_event (GthFolderTree *folder_tree)
1488 {
1489 GtkWidget *widget = GTK_WIDGET (folder_tree);
1490 GdkDevice *device;
1491 GdkWindow *window;
1492 GdkEventMotion event;
1493 int x, y;
1494
1495 if (! gtk_widget_get_realized (widget))
1496 return;
1497
1498 device = _gtk_widget_get_client_pointer (widget);
1499 if (device == NULL)
1500 return;
1501 window = gdk_window_get_device_position (gtk_widget_get_window (widget),
1502 device,
1503 &x,
1504 &y,
1505 NULL);
1506
1507 event.type = GDK_MOTION_NOTIFY;
1508 event.window = (window != NULL) ? window : gtk_tree_view_get_bin_window (GTK_TREE_VIEW (folder_tree));
1509 event.send_event = TRUE;
1510 event.time = GDK_CURRENT_TIME;
1511 event.x = x;
1512 event.y = y;
1513 event.axes = NULL;
1514 event.state = 0;
1515 event.is_hint = FALSE;
1516 event.device = device;
1517
1518 GTK_WIDGET_GET_CLASS (folder_tree)->motion_notify_event (widget, &event);
1519 }
1520
1521
1522 G_GNUC_UNUSED
1523 static GList *
_gth_folder_tree_get_children(GthFolderTree * folder_tree,GtkTreeIter * parent)1524 _gth_folder_tree_get_children (GthFolderTree *folder_tree,
1525 GtkTreeIter *parent)
1526 {
1527 GtkTreeModel *tree_model = GTK_TREE_MODEL (folder_tree->priv->tree_store);
1528 GtkTreeIter iter;
1529 GList *list;
1530
1531 if (! gtk_tree_model_iter_children (tree_model, &iter, parent))
1532 return NULL;
1533
1534 list = NULL;
1535 do {
1536 GthFileData *file_data;
1537 EntryType file_type;
1538
1539 gtk_tree_model_get (tree_model, &iter,
1540 COLUMN_FILE_DATA, &file_data,
1541 COLUMN_TYPE, &file_type,
1542 -1);
1543 if ((file_type == ENTRY_TYPE_FILE) && (file_data != NULL))
1544 list = g_list_prepend (list, g_object_ref (file_data));
1545
1546 _g_object_unref (file_data);
1547 }
1548 while (gtk_tree_model_iter_next (tree_model, &iter));
1549
1550 return g_list_reverse (list);
1551 }
1552
1553
1554 static void
_gth_folder_tree_remove_child_type(GthFolderTree * folder_tree,GtkTreeIter * parent,EntryType entry_type)1555 _gth_folder_tree_remove_child_type (GthFolderTree *folder_tree,
1556 GtkTreeIter *parent,
1557 EntryType entry_type)
1558 {
1559 GtkTreeIter iter;
1560
1561 if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter, parent))
1562 return;
1563
1564 do {
1565 EntryType file_entry_type;
1566
1567 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter,
1568 COLUMN_TYPE, &file_entry_type,
1569 -1);
1570
1571 if (entry_type == file_entry_type) {
1572 gtk_tree_store_remove (folder_tree->priv->tree_store, &iter);
1573 break;
1574 }
1575 }
1576 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter));
1577 }
1578
1579
1580 void
gth_folder_tree_set_children(GthFolderTree * folder_tree,GFile * parent,GList * files)1581 gth_folder_tree_set_children (GthFolderTree *folder_tree,
1582 GFile *parent,
1583 GList *files)
1584 {
1585 GtkTreeIter parent_iter;
1586 GtkTreeIter *p_parent_iter;
1587 GHashTable *file_hash;
1588 GList *scan;
1589 GList *old_files;
1590 GtkTreeModel *tree_model;
1591 GtkTreeIter iter;
1592
1593 if (g_file_equal (parent, folder_tree->priv->root))
1594 p_parent_iter = NULL;
1595 else if (gth_folder_tree_get_iter (folder_tree, parent, &parent_iter, NULL))
1596 p_parent_iter = &parent_iter;
1597 else
1598 return;
1599
1600 if (_gth_folder_tree_iter_get_no_child (folder_tree, p_parent_iter))
1601 return;
1602
1603 tree_model = GTK_TREE_MODEL (folder_tree->priv->tree_store);
1604 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_model), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, 0);
1605
1606 /* add the empty item first to not allow the folder to collapse. */
1607 _gth_folder_tree_add_empty_item (folder_tree, p_parent_iter);
1608
1609 /* delete the children not present in the new file list, update the
1610 * already existing files */
1611
1612 file_hash = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
1613 for (scan = files; scan; scan = scan->next) {
1614 GthFileData *file_data = scan->data;
1615 g_hash_table_insert (file_hash, g_object_ref (file_data->file), GINT_TO_POINTER (1));
1616 }
1617
1618 old_files = NULL;
1619 if (gtk_tree_model_iter_children (tree_model, &iter, p_parent_iter)) {
1620 gboolean valid = TRUE;
1621
1622 do {
1623 GthFileData *file_data = NULL;
1624 EntryType file_type;
1625
1626 gtk_tree_model_get (tree_model, &iter,
1627 COLUMN_FILE_DATA, &file_data,
1628 COLUMN_TYPE, &file_type,
1629 -1);
1630
1631 if (file_type == ENTRY_TYPE_LOADING) {
1632 valid = gtk_tree_store_remove (folder_tree->priv->tree_store, &iter);
1633 }
1634 else if (file_type == ENTRY_TYPE_FILE) {
1635 /* save the old files list to compute the new files list below */
1636 old_files = g_list_prepend (old_files, g_object_ref (file_data));
1637
1638 if (g_hash_table_lookup (file_hash, file_data->file)) {
1639 /* file_data is already present in the list, just update it */
1640 _gth_folder_tree_set_file_data (folder_tree, &iter, file_data);
1641 valid = gtk_tree_model_iter_next (tree_model, &iter);
1642 }
1643 else {
1644 /* file_data is not present anymore, remove it from the tree */
1645 valid = gtk_tree_store_remove (folder_tree->priv->tree_store, &iter);
1646 }
1647 }
1648 else
1649 valid = gtk_tree_model_iter_next (tree_model, &iter);
1650
1651 _g_object_unref (file_data);
1652 }
1653 while (valid);
1654 }
1655
1656 g_hash_table_unref (file_hash);
1657
1658 /* add the new files */
1659
1660 file_hash = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
1661 for (scan = old_files; scan; scan = scan->next) {
1662 GthFileData *file_data = scan->data;
1663 g_hash_table_insert (file_hash, g_object_ref (file_data->file), GINT_TO_POINTER (1));
1664 }
1665
1666 for (scan = files; scan; scan = scan->next) {
1667 GthFileData *file_data = scan->data;
1668
1669 if (! g_hash_table_lookup (file_hash, file_data->file))
1670 _gth_folder_tree_add_file (folder_tree, p_parent_iter, file_data);
1671 }
1672
1673 _g_object_list_unref (old_files);
1674 g_hash_table_unref (file_hash);
1675
1676 /**/
1677
1678 _gth_folder_tree_remove_child_type (folder_tree, p_parent_iter, ENTRY_TYPE_EMPTY);
1679
1680 if (p_parent_iter != NULL)
1681 gtk_tree_store_set (folder_tree->priv->tree_store, p_parent_iter,
1682 COLUMN_LOADED, TRUE,
1683 -1);
1684
1685 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (folder_tree->priv->tree_store), COLUMN_NAME, GTK_SORT_ASCENDING);
1686 folder_tree->priv->recalc_entry_points = TRUE;
1687
1688 emit_fake_motion_notify_event (folder_tree);
1689 }
1690
1691
1692 void
gth_folder_tree_loading_children(GthFolderTree * folder_tree,GFile * parent)1693 gth_folder_tree_loading_children (GthFolderTree *folder_tree,
1694 GFile *parent)
1695 {
1696 GtkTreeIter parent_iter;
1697 GtkTreeIter *p_parent_iter;
1698 GtkTreeIter iter;
1699 gboolean valid_iter;
1700
1701 if (g_file_equal (parent, folder_tree->priv->root))
1702 p_parent_iter = NULL;
1703 else if (gth_folder_tree_get_iter (folder_tree, parent, &parent_iter, NULL))
1704 p_parent_iter = &parent_iter;
1705 else
1706 return;
1707
1708 if (_gth_folder_tree_iter_get_no_child (folder_tree, p_parent_iter))
1709 return;
1710
1711 _gth_folder_tree_add_loading_item (folder_tree, p_parent_iter, FALSE);
1712
1713 /* remove anything but the loading item */
1714 gtk_tree_model_iter_children (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter, p_parent_iter);
1715 do {
1716 EntryType file_entry_type;
1717
1718 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter,
1719 COLUMN_TYPE, &file_entry_type,
1720 -1);
1721
1722 if (file_entry_type != ENTRY_TYPE_LOADING)
1723 valid_iter = gtk_tree_store_remove (folder_tree->priv->tree_store, &iter);
1724 else
1725 valid_iter = gtk_tree_model_iter_next (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter);
1726 }
1727 while (valid_iter);
1728
1729 emit_fake_motion_notify_event (folder_tree);
1730 }
1731
1732
1733 static gboolean
_gth_folder_tree_file_is_in_children(GthFolderTree * folder_tree,GtkTreeIter * parent_iter,GFile * file)1734 _gth_folder_tree_file_is_in_children (GthFolderTree *folder_tree,
1735 GtkTreeIter *parent_iter,
1736 GFile *file)
1737 {
1738 GtkTreeIter iter;
1739 gboolean found = FALSE;
1740
1741 if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter, parent_iter))
1742 return FALSE;
1743
1744 do {
1745 GthFileData *test_file_data;
1746 EntryType file_entry_type;
1747
1748 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter,
1749 COLUMN_FILE_DATA, &test_file_data,
1750 COLUMN_TYPE, &file_entry_type,
1751 -1);
1752 if ((file_entry_type == ENTRY_TYPE_FILE) && (test_file_data != NULL) && g_file_equal (file, test_file_data->file))
1753 found = TRUE;
1754
1755 _g_object_unref (test_file_data);
1756 }
1757 while (! found && gtk_tree_model_iter_next (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter));
1758
1759 return found;
1760 }
1761
1762
1763 void
gth_folder_tree_add_children(GthFolderTree * folder_tree,GFile * parent,GList * files)1764 gth_folder_tree_add_children (GthFolderTree *folder_tree,
1765 GFile *parent,
1766 GList *files)
1767 {
1768 GtkTreeIter parent_iter;
1769 GtkTreeIter *p_parent_iter;
1770 GList *scan;
1771
1772 if (g_file_equal (parent, folder_tree->priv->root))
1773 p_parent_iter = NULL;
1774 else if (gth_folder_tree_get_iter (folder_tree, parent, &parent_iter, NULL))
1775 p_parent_iter = &parent_iter;
1776 else
1777 return;
1778
1779 for (scan = files; scan; scan = scan->next) {
1780 GthFileData *file_data = scan->data;
1781
1782 if (_gth_folder_tree_file_is_in_children (folder_tree, p_parent_iter, file_data->file))
1783 continue;
1784 _gth_folder_tree_add_file (folder_tree, p_parent_iter, file_data);
1785 }
1786
1787 folder_tree->priv->recalc_entry_points = TRUE;
1788 }
1789
1790
1791 void
gth_folder_tree_update_children(GthFolderTree * folder_tree,GFile * parent,GList * files)1792 gth_folder_tree_update_children (GthFolderTree *folder_tree,
1793 GFile *parent,
1794 GList *files)
1795 {
1796 GtkTreeIter parent_iter;
1797 GtkTreeIter *p_parent_iter;
1798 GList *scan;
1799 GtkTreeIter iter;
1800
1801 if (g_file_equal (parent, folder_tree->priv->root))
1802 p_parent_iter = NULL;
1803 else if (gth_folder_tree_get_iter (folder_tree, parent, &parent_iter, NULL))
1804 p_parent_iter = &parent_iter;
1805 else
1806 return;
1807
1808 /* update each file if already present */
1809
1810 for (scan = files; scan; scan = scan->next) {
1811 GthFileData *file_data = scan->data;
1812
1813 if (_gth_folder_tree_get_child (folder_tree, file_data->file, &iter, p_parent_iter))
1814 _gth_folder_tree_set_file_data (folder_tree, &iter, file_data);
1815 }
1816 }
1817
1818
1819 void
gth_folder_tree_update_child(GthFolderTree * folder_tree,GFile * old_file,GthFileData * file_data)1820 gth_folder_tree_update_child (GthFolderTree *folder_tree,
1821 GFile *old_file,
1822 GthFileData *file_data)
1823 {
1824 GtkTreeIter old_file_iter;
1825 GtkTreeIter new_file_iter;
1826
1827 if (! gth_folder_tree_get_iter (folder_tree, old_file, &old_file_iter, NULL))
1828 return;
1829
1830 if (gth_folder_tree_get_iter (folder_tree, file_data->file, &new_file_iter, NULL)) {
1831 if (! _g_file_equal (old_file, file_data->file)) {
1832 GFile *parent;
1833 GList *files;
1834
1835 /* the new file is already present, remove the old file */
1836
1837 parent = g_file_get_parent (old_file);
1838 files = g_list_prepend (NULL, g_object_ref (old_file));
1839 gth_folder_tree_delete_children (folder_tree, parent, files);
1840 _g_object_list_unref (files);
1841 g_object_unref (parent);
1842 }
1843
1844 /* update the data of the new file */
1845
1846 _gth_folder_tree_set_file_data (folder_tree, &new_file_iter, file_data);
1847 }
1848 else
1849 _gth_folder_tree_set_file_data (folder_tree, &old_file_iter, file_data);
1850 }
1851
1852
1853 void
gth_folder_tree_delete_children(GthFolderTree * folder_tree,GFile * parent,GList * files)1854 gth_folder_tree_delete_children (GthFolderTree *folder_tree,
1855 GFile *parent,
1856 GList *files)
1857 {
1858 GtkTreeIter parent_iter;
1859 GtkTreeIter *p_parent_iter;
1860 GList *scan;
1861
1862 if (g_file_equal (parent, folder_tree->priv->root))
1863 p_parent_iter = NULL;
1864 else if (gth_folder_tree_get_iter (folder_tree, parent, &parent_iter, NULL))
1865 p_parent_iter = &parent_iter;
1866 else
1867 return;
1868
1869 if (_gth_folder_tree_iter_get_no_child (folder_tree, p_parent_iter))
1870 return;
1871
1872 /* add the empty item first to not allow the folder to collapse. */
1873 _gth_folder_tree_add_empty_item (folder_tree, p_parent_iter);
1874
1875 for (scan = files; scan; scan = scan->next) {
1876 GFile *file = scan->data;
1877 GtkTreeIter iter;
1878
1879 if (_gth_folder_tree_get_child (folder_tree, file, &iter, p_parent_iter))
1880 gtk_tree_store_remove (folder_tree->priv->tree_store, &iter);
1881 }
1882
1883 _gth_folder_tree_remove_child_type (folder_tree, p_parent_iter, ENTRY_TYPE_EMPTY);
1884
1885 folder_tree->priv->recalc_entry_points = TRUE;
1886 }
1887
1888
1889 /* -- gth_folder_tree_start_editing -- */
1890
1891
1892 typedef struct {
1893 GthFolderTree *folder_tree;
1894 GFile *file;
1895 } RenameData;
1896
1897
1898 static void
rename_data_free(RenameData * data)1899 rename_data_free (RenameData *data)
1900 {
1901 g_object_unref (data->folder_tree);
1902 g_object_unref (data->file);
1903 g_free (data);
1904 }
1905
1906
1907 static void
rename_dialog_response_cb(GtkWidget * dialog,int response_id,gpointer user_data)1908 rename_dialog_response_cb (GtkWidget *dialog,
1909 int response_id,
1910 gpointer user_data)
1911 {
1912 RenameData *data = user_data;
1913 char *name;
1914
1915 if (response_id != GTK_RESPONSE_OK) {
1916 rename_data_free (data);
1917 gtk_widget_destroy (dialog);
1918 return;
1919 }
1920
1921 name = gth_request_dialog_get_normalized_text (GTH_REQUEST_DIALOG (dialog));
1922 if (_g_utf8_all_spaces (name)) {
1923 g_free (name);
1924 gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, _("No name specified"));
1925 return;
1926 }
1927
1928 if (g_regex_match_simple ("/", name, 0, 0)) {
1929 char *message;
1930
1931 message = g_strdup_printf (_("Invalid name. The following characters are not allowed: %s"), "/");
1932 gth_request_dialog_set_info_text (GTH_REQUEST_DIALOG (dialog), GTK_MESSAGE_ERROR, message);
1933
1934 g_free (message);
1935 g_free (name);
1936
1937 return;
1938 }
1939
1940 g_signal_emit (data->folder_tree,
1941 gth_folder_tree_signals[RENAME],
1942 0,
1943 data->file,
1944 name);
1945
1946 g_free (name);
1947 rename_data_free (data);
1948 gtk_widget_destroy (dialog);
1949 }
1950
1951
1952 void
gth_folder_tree_start_editing(GthFolderTree * folder_tree,GFile * file)1953 gth_folder_tree_start_editing (GthFolderTree *folder_tree,
1954 GFile *file)
1955 {
1956 GtkTreeIter iter;
1957 GthFileData *file_data;
1958 RenameData *data;
1959 GtkWidget *dialog;
1960 const char *edit_name;
1961
1962 if (! gth_folder_tree_get_iter (folder_tree, file, &iter, NULL))
1963 return;
1964
1965 data = g_new0 (RenameData, 1);
1966 data->folder_tree = g_object_ref (folder_tree);
1967 data->file = g_object_ref (file);
1968
1969 dialog = gth_request_dialog_new (_gtk_widget_get_toplevel_if_window (GTK_WIDGET (folder_tree)),
1970 GTK_DIALOG_MODAL,
1971 _("Rename"),
1972 _("Enter the new name:"),
1973 _GTK_LABEL_CANCEL,
1974 _("_Rename"));
1975 g_signal_connect (dialog,
1976 "response",
1977 G_CALLBACK (rename_dialog_response_cb),
1978 data);
1979
1980 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store),
1981 &iter,
1982 COLUMN_FILE_DATA, &file_data,
1983 -1);
1984 edit_name = g_file_info_get_edit_name (file_data->info);
1985 if (edit_name == NULL)
1986 edit_name = g_file_info_get_display_name (file_data->info);
1987 if (edit_name != NULL)
1988 gtk_entry_set_text (GTK_ENTRY (gth_request_dialog_get_entry (GTH_REQUEST_DIALOG (dialog))), edit_name);
1989
1990 gtk_widget_show (dialog);
1991
1992 _g_object_unref (file_data);
1993
1994 #if 0
1995 GtkTreeIter iter;
1996 GtkTreePath *tree_path;
1997 GtkTreeViewColumn *tree_column;
1998
1999 if (! gth_folder_tree_get_iter (folder_tree, file, &iter, NULL))
2000 return;
2001
2002 g_object_set (folder_tree->priv->text_renderer,
2003 "editable", TRUE,
2004 NULL);
2005
2006 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter);
2007 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (folder_tree), 0);
2008 gtk_tree_view_set_cursor (GTK_TREE_VIEW (folder_tree),
2009 tree_path,
2010 tree_column,
2011 TRUE);
2012
2013 gtk_tree_path_free (tree_path);
2014 #endif
2015 }
2016
2017
2018 GtkTreePath *
gth_folder_tree_get_path(GthFolderTree * folder_tree,GFile * file)2019 gth_folder_tree_get_path (GthFolderTree *folder_tree,
2020 GFile *file)
2021 {
2022 GtkTreeIter iter;
2023
2024 if (! gth_folder_tree_get_iter (folder_tree, file, &iter, NULL))
2025 return NULL;
2026 else
2027 return gtk_tree_model_get_path (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter);
2028 }
2029
2030
2031 gboolean
gth_folder_tree_is_loaded(GthFolderTree * folder_tree,GtkTreePath * path)2032 gth_folder_tree_is_loaded (GthFolderTree *folder_tree,
2033 GtkTreePath *path)
2034 {
2035 GtkTreeIter iter;
2036 gboolean loaded;
2037
2038 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter, path))
2039 return FALSE;
2040
2041 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter,
2042 COLUMN_LOADED, &loaded,
2043 -1);
2044
2045 return loaded;
2046 }
2047
2048
2049 gboolean
gth_folder_tree_has_no_child(GthFolderTree * folder_tree,GtkTreePath * path)2050 gth_folder_tree_has_no_child (GthFolderTree *folder_tree,
2051 GtkTreePath *path)
2052 {
2053 GtkTreeIter iter;
2054 gboolean no_child;
2055
2056 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter, path))
2057 return FALSE;
2058
2059 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store), &iter,
2060 COLUMN_NO_CHILD, &no_child,
2061 -1);
2062
2063 return no_child;
2064 }
2065
2066
2067 static void
_gth_folder_tree_reset_loaded(GthFolderTree * folder_tree,GtkTreeIter * root)2068 _gth_folder_tree_reset_loaded (GthFolderTree *folder_tree,
2069 GtkTreeIter *root)
2070 {
2071 GtkTreeModel *tree_model = GTK_TREE_MODEL (folder_tree->priv->tree_store);
2072 GtkTreeIter iter;
2073
2074 if (root != NULL)
2075 gtk_tree_store_set (folder_tree->priv->tree_store, root,
2076 COLUMN_LOADED, FALSE,
2077 -1);
2078
2079 if (gtk_tree_model_iter_children (tree_model, &iter, root)) {
2080 do {
2081 _gth_folder_tree_reset_loaded (folder_tree, &iter);
2082 }
2083 while (gtk_tree_model_iter_next (tree_model, &iter));
2084 }
2085 }
2086
2087
2088 void
gth_folder_tree_reset_loaded(GthFolderTree * folder_tree)2089 gth_folder_tree_reset_loaded (GthFolderTree *folder_tree)
2090 {
2091 _gth_folder_tree_reset_loaded (folder_tree, NULL);
2092 }
2093
2094
2095 void
gth_folder_tree_expand_row(GthFolderTree * folder_tree,GtkTreePath * path,gboolean open_all)2096 gth_folder_tree_expand_row (GthFolderTree *folder_tree,
2097 GtkTreePath *path,
2098 gboolean open_all)
2099 {
2100 g_signal_handlers_block_by_func (folder_tree, row_expanded_cb, folder_tree);
2101 gtk_tree_view_expand_row (GTK_TREE_VIEW (folder_tree), path, open_all);
2102 g_signal_handlers_unblock_by_func (folder_tree, row_expanded_cb, folder_tree);
2103 }
2104
2105
2106 void
gth_folder_tree_collapse_all(GthFolderTree * folder_tree)2107 gth_folder_tree_collapse_all (GthFolderTree *folder_tree)
2108 {
2109 GtkTreeSelection *selection;
2110
2111 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
2112 g_signal_handlers_block_by_func (selection, selection_changed_cb, folder_tree);
2113 gtk_tree_view_collapse_all (GTK_TREE_VIEW (folder_tree));
2114 g_signal_handlers_unblock_by_func (selection, selection_changed_cb, folder_tree);
2115 }
2116
2117
2118 GthFileData *
gth_folder_tree_get_file(GthFolderTree * folder_tree,GtkTreePath * path)2119 gth_folder_tree_get_file (GthFolderTree *folder_tree,
2120 GtkTreePath *path)
2121 {
2122 GtkTreeModel *tree_model;
2123 GtkTreeIter iter;
2124 EntryType entry_type;
2125 GthFileData *file_data;
2126
2127 tree_model = GTK_TREE_MODEL (folder_tree->priv->tree_store);
2128 if (! gtk_tree_model_get_iter (tree_model, &iter, path))
2129 return NULL;
2130
2131 file_data = NULL;
2132 gtk_tree_model_get (tree_model,
2133 &iter,
2134 COLUMN_TYPE, &entry_type,
2135 COLUMN_FILE_DATA, &file_data,
2136 -1);
2137 if (entry_type != ENTRY_TYPE_FILE) {
2138 _g_object_unref (file_data);
2139 file_data = NULL;
2140 }
2141
2142 return file_data;
2143 }
2144
2145
2146 void
gth_folder_tree_select_path(GthFolderTree * folder_tree,GtkTreePath * path)2147 gth_folder_tree_select_path (GthFolderTree *folder_tree,
2148 GtkTreePath *path)
2149 {
2150 GtkTreeSelection *selection;
2151
2152 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
2153 g_signal_handlers_block_by_func (selection, selection_changed_cb, folder_tree);
2154 gtk_tree_selection_select_path (selection, path);
2155 g_signal_handlers_unblock_by_func (selection, selection_changed_cb, folder_tree);
2156 }
2157
2158
2159 GFile *
gth_folder_tree_get_root(GthFolderTree * folder_tree)2160 gth_folder_tree_get_root (GthFolderTree *folder_tree)
2161 {
2162 return folder_tree->priv->root;
2163 }
2164
2165
2166 gboolean
gth_folder_tree_is_root(GthFolderTree * folder_tree,GFile * folder)2167 gth_folder_tree_is_root (GthFolderTree *folder_tree,
2168 GFile *folder)
2169 {
2170 return _g_file_equal (folder_tree->priv->root, folder);
2171 }
2172
2173
2174 GthFileData *
gth_folder_tree_get_selected(GthFolderTree * folder_tree)2175 gth_folder_tree_get_selected (GthFolderTree *folder_tree)
2176 {
2177 GtkTreeSelection *selection;
2178 GtkTreeModel *tree_model;
2179 GtkTreeIter iter;
2180 EntryType entry_type;
2181 GthFileData *file_data;
2182
2183 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
2184 if (selection == NULL)
2185 return NULL;
2186
2187 tree_model = GTK_TREE_MODEL (folder_tree->priv->tree_store);
2188 if (! gtk_tree_selection_get_selected (selection, &tree_model, &iter))
2189 return NULL;
2190
2191 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store),
2192 &iter,
2193 COLUMN_TYPE, &entry_type,
2194 COLUMN_FILE_DATA, &file_data,
2195 -1);
2196
2197 if (entry_type != ENTRY_TYPE_FILE) {
2198 _g_object_unref (file_data);
2199 file_data = NULL;
2200 }
2201
2202 return file_data;
2203 }
2204
2205
2206 GthFileData *
gth_folder_tree_get_selected_or_parent(GthFolderTree * folder_tree)2207 gth_folder_tree_get_selected_or_parent (GthFolderTree *folder_tree)
2208 {
2209 GtkTreeSelection *selection;
2210 GtkTreeModel *tree_model;
2211 GtkTreeIter iter;
2212 GtkTreeIter parent;
2213 EntryType entry_type;
2214 GthFileData *file_data;
2215
2216 file_data = gth_folder_tree_get_selected (folder_tree);
2217 if (file_data != NULL)
2218 return file_data;
2219
2220 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
2221 if (selection == NULL)
2222 return NULL;
2223
2224 tree_model = GTK_TREE_MODEL (folder_tree->priv->tree_store);
2225 if (! gtk_tree_selection_get_selected (selection, &tree_model, &iter))
2226 return NULL;
2227
2228 if (! gtk_tree_model_iter_parent (tree_model, &parent, &iter))
2229 return NULL;
2230
2231 gtk_tree_model_get (GTK_TREE_MODEL (folder_tree->priv->tree_store),
2232 &parent,
2233 COLUMN_TYPE, &entry_type,
2234 COLUMN_FILE_DATA, &file_data,
2235 -1);
2236
2237 if (entry_type != ENTRY_TYPE_FILE) {
2238 _g_object_unref (file_data);
2239 file_data = NULL;
2240 }
2241
2242 return file_data;
2243 }
2244
2245
2246 void
gth_folder_tree_enable_drag_source(GthFolderTree * self,GdkModifierType start_button_mask,const GtkTargetEntry * targets,int n_targets,GdkDragAction actions)2247 gth_folder_tree_enable_drag_source (GthFolderTree *self,
2248 GdkModifierType start_button_mask,
2249 const GtkTargetEntry *targets,
2250 int n_targets,
2251 GdkDragAction actions)
2252 {
2253 if (self->priv->drag_target_list != NULL)
2254 gtk_target_list_unref (self->priv->drag_target_list);
2255
2256 self->priv->drag_source_enabled = TRUE;
2257 self->priv->drag_start_button_mask = start_button_mask;
2258 self->priv->drag_target_list = gtk_target_list_new (targets, n_targets);
2259 self->priv->drag_actions = actions;
2260 }
2261
2262
2263 void
gth_folder_tree_unset_drag_source(GthFolderTree * self)2264 gth_folder_tree_unset_drag_source (GthFolderTree *self)
2265 {
2266 self->priv->drag_source_enabled = FALSE;
2267 }
2268