1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* fm-list-model.h - a GtkTreeModel for file lists.
4
5 Copyright (C) 2001, 2002 Anders Carlsson
6 Copyright (C) 2003, Soeren Sandmann
7 Copyright (C) 2004, Novell, Inc.
8
9 The Gnome Library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
13
14 The Gnome Library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public
20 License along with the Gnome Library; see the file COPYING.LIB. If not,
21 write to the Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
22 Boston, MA 02110-1335, USA.
23
24 Authors: Anders Carlsson <andersca@gnu.org>, Soeren Sandmann (sandmann@daimi.au.dk), Dave Camp <dave@ximian.com>
25 */
26
27 #include <config.h>
28
29 #include "nemo-list-model.h"
30
31 #include <string.h>
32 #include <glib.h>
33 #include <glib/gi18n.h>
34 #include <gtk/gtk.h>
35 #include <cairo-gobject.h>
36
37 #include <libegg/eggtreemultidnd.h>
38 #include <eel/eel-graphic-effects.h>
39 #include <libnemo-private/nemo-dnd.h>
40 #include <libnemo-private/nemo-file-utilities.h>
41
42 enum {
43 SUBDIRECTORY_UNLOADED,
44 GET_ICON_SCALE,
45 LAST_SIGNAL
46 };
47
48 static GQuark attribute_name_q,
49 attribute_modification_date_q,
50 attribute_date_modified_q;
51
52 /* msec delay after Loading... dummy row turns into (empty) */
53 #define LOADING_TO_EMPTY_DELAY 100
54
55 static guint list_model_signals[LAST_SIGNAL] = { 0 };
56
57 static int nemo_list_model_file_entry_compare_func (gconstpointer a,
58 gconstpointer b,
59 gpointer user_data);
60 static void nemo_list_model_tree_model_init (GtkTreeModelIface *iface);
61 static void nemo_list_model_sortable_init (GtkTreeSortableIface *iface);
62 static void nemo_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface);
63
64 struct NemoListModelDetails {
65 GSequence *files;
66 GHashTable *directory_reverse_map; /* map from directory to GSequenceIter's */
67 GHashTable *top_reverse_map; /* map from files in top dir to GSequenceIter's */
68
69 int stamp;
70
71 GQuark sort_attribute;
72 GtkSortType order;
73
74 gboolean sort_directories_first;
75
76 GtkTreeView *drag_view;
77 int drag_begin_x;
78 int drag_begin_y;
79
80 GPtrArray *columns;
81
82 GList *highlight_files;
83 gboolean temp_unsorted;
84 };
85
86 typedef struct {
87 NemoListModel *model;
88
89 GList *path_list;
90 } DragDataGetInfo;
91
92 typedef struct FileEntry FileEntry;
93
94 struct FileEntry {
95 NemoFile *file;
96 GHashTable *reverse_map; /* map from files to GSequenceIter's */
97 NemoDirectory *subdirectory;
98 FileEntry *parent;
99 GSequence *files;
100 GSequenceIter *ptr;
101 guint loaded : 1;
102 guint expanding : 1;
103 guint ok_to_show_thumb : 1;
104 };
105
106 G_DEFINE_TYPE_WITH_CODE (NemoListModel, nemo_list_model, G_TYPE_OBJECT,
107 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
108 nemo_list_model_tree_model_init)
109 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
110 nemo_list_model_sortable_init)
111 G_IMPLEMENT_INTERFACE (EGG_TYPE_TREE_MULTI_DRAG_SOURCE,
112 nemo_list_model_multi_drag_source_init));
113
114 static const GtkTargetEntry drag_types [] = {
115 { (char *)NEMO_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NEMO_ICON_DND_GNOME_ICON_LIST },
116 { (char *)NEMO_ICON_DND_URI_LIST_TYPE, 0, NEMO_ICON_DND_URI_LIST },
117 };
118
119 static GtkTargetList *drag_target_list = NULL;
120
121 static void
file_entry_free(FileEntry * file_entry)122 file_entry_free (FileEntry *file_entry)
123 {
124 nemo_file_unref (file_entry->file);
125 if (file_entry->reverse_map) {
126 g_hash_table_destroy (file_entry->reverse_map);
127 file_entry->reverse_map = NULL;
128 }
129 if (file_entry->subdirectory != NULL) {
130 nemo_directory_unref (file_entry->subdirectory);
131 }
132 if (file_entry->files != NULL) {
133 g_sequence_free (file_entry->files);
134 }
135 g_free (file_entry);
136 }
137
138 static GtkTreeModelFlags
nemo_list_model_get_flags(GtkTreeModel * tree_model)139 nemo_list_model_get_flags (GtkTreeModel *tree_model)
140 {
141 return GTK_TREE_MODEL_ITERS_PERSIST;
142 }
143
144 static int
nemo_list_model_get_n_columns(GtkTreeModel * tree_model)145 nemo_list_model_get_n_columns (GtkTreeModel *tree_model)
146 {
147 return NEMO_LIST_MODEL_NUM_COLUMNS + NEMO_LIST_MODEL (tree_model)->details->columns->len;
148 }
149
150 static GType
nemo_list_model_get_column_type(GtkTreeModel * tree_model,int index)151 nemo_list_model_get_column_type (GtkTreeModel *tree_model, int index)
152 {
153 switch (index) {
154 case NEMO_LIST_MODEL_FILE_COLUMN:
155 return NEMO_TYPE_FILE;
156 case NEMO_LIST_MODEL_SUBDIRECTORY_COLUMN:
157 return NEMO_TYPE_DIRECTORY;
158 case NEMO_LIST_MODEL_SMALLEST_ICON_COLUMN:
159 case NEMO_LIST_MODEL_SMALLER_ICON_COLUMN:
160 case NEMO_LIST_MODEL_SMALL_ICON_COLUMN:
161 case NEMO_LIST_MODEL_STANDARD_ICON_COLUMN:
162 case NEMO_LIST_MODEL_LARGE_ICON_COLUMN:
163 case NEMO_LIST_MODEL_LARGER_ICON_COLUMN:
164 case NEMO_LIST_MODEL_LARGEST_ICON_COLUMN:
165 return CAIRO_GOBJECT_TYPE_SURFACE;
166 case NEMO_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN:
167 return G_TYPE_BOOLEAN;
168 case NEMO_LIST_MODEL_TEXT_WEIGHT_COLUMN:
169 return G_TYPE_INT;
170 default:
171 if (index < (int)(NEMO_LIST_MODEL_NUM_COLUMNS + NEMO_LIST_MODEL (tree_model)->details->columns->len)) {
172 return G_TYPE_STRING;
173 } else {
174 return G_TYPE_INVALID;
175 }
176 }
177 }
178
179 static void
nemo_list_model_ptr_to_iter(NemoListModel * model,GSequenceIter * ptr,GtkTreeIter * iter)180 nemo_list_model_ptr_to_iter (NemoListModel *model, GSequenceIter *ptr, GtkTreeIter *iter)
181 {
182 g_assert (!g_sequence_iter_is_end (ptr));
183 if (iter != NULL) {
184 iter->stamp = model->details->stamp;
185 iter->user_data = ptr;
186 }
187 }
188
189 static gboolean
nemo_list_model_get_iter(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreePath * path)190 nemo_list_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
191 {
192 NemoListModel *model;
193 GSequence *files;
194 GSequenceIter *ptr;
195 FileEntry *file_entry;
196 int i, d;
197
198 model = (NemoListModel *)tree_model;
199 ptr = NULL;
200
201 files = model->details->files;
202 for (d = 0; d < gtk_tree_path_get_depth (path); d++) {
203 i = gtk_tree_path_get_indices (path)[d];
204
205 if (files == NULL || i >= g_sequence_get_length (files)) {
206 return FALSE;
207 }
208
209 ptr = g_sequence_get_iter_at_pos (files, i);
210 file_entry = g_sequence_get (ptr);
211 files = file_entry->files;
212 }
213
214 nemo_list_model_ptr_to_iter (model, ptr, iter);
215
216 return TRUE;
217 }
218
219 static GtkTreePath *
nemo_list_model_get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)220 nemo_list_model_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter)
221 {
222 GtkTreePath *path;
223 NemoListModel *model;
224 GSequenceIter *ptr;
225 FileEntry *file_entry;
226
227
228 model = (NemoListModel *)tree_model;
229
230 g_return_val_if_fail (iter->stamp == model->details->stamp, NULL);
231
232 if (g_sequence_iter_is_end (iter->user_data)) {
233 /* FIXME is this right? */
234 return NULL;
235 }
236
237 path = gtk_tree_path_new ();
238 ptr = iter->user_data;
239 while (ptr != NULL) {
240 gtk_tree_path_prepend_index (path, g_sequence_iter_get_position (ptr));
241 file_entry = g_sequence_get (ptr);
242 if (file_entry->parent != NULL) {
243 ptr = file_entry->parent->ptr;
244 } else {
245 ptr = NULL;
246 }
247 }
248
249 return path;
250 }
251
252 static gint
nemo_list_model_get_icon_scale(NemoListModel * model)253 nemo_list_model_get_icon_scale (NemoListModel *model)
254 {
255 gint retval = -1;
256
257 g_signal_emit (model, list_model_signals[GET_ICON_SCALE], 0,
258 &retval);
259
260 if (retval == -1) {
261 retval = gdk_screen_get_monitor_scale_factor (gdk_screen_get_default (), 0);
262 }
263
264 return retval;
265 }
266
267 static void
nemo_list_model_get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,int column,GValue * value)268 nemo_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int column, GValue *value)
269 {
270 NemoListModel *model;
271 NemoFile *parent_file;
272 FileEntry *file_entry;
273 NemoFile *file;
274 char *str;
275
276 model = (NemoListModel *)tree_model;
277
278 g_return_if_fail (model->details->stamp == iter->stamp);
279 g_return_if_fail (!g_sequence_iter_is_end (iter->user_data));
280
281 file_entry = g_sequence_get (iter->user_data);
282 file = file_entry->file;
283 parent_file = file_entry->parent ? file_entry->parent->file : NULL;
284
285 switch (column) {
286 case NEMO_LIST_MODEL_FILE_COLUMN:
287 g_value_init (value, NEMO_TYPE_FILE);
288
289 g_value_set_object (value, file);
290 break;
291 case NEMO_LIST_MODEL_SUBDIRECTORY_COLUMN:
292 g_value_init (value, NEMO_TYPE_DIRECTORY);
293
294 g_value_set_object (value, file_entry->subdirectory);
295 break;
296 case NEMO_LIST_MODEL_SMALLEST_ICON_COLUMN:
297 case NEMO_LIST_MODEL_SMALLER_ICON_COLUMN:
298 case NEMO_LIST_MODEL_SMALL_ICON_COLUMN:
299 case NEMO_LIST_MODEL_STANDARD_ICON_COLUMN:
300 case NEMO_LIST_MODEL_LARGE_ICON_COLUMN:
301 case NEMO_LIST_MODEL_LARGER_ICON_COLUMN:
302 case NEMO_LIST_MODEL_LARGEST_ICON_COLUMN:
303 g_value_init (value, CAIRO_GOBJECT_TYPE_SURFACE);
304
305 if (file != NULL) {
306 NemoFileIconFlags flags;
307 cairo_surface_t *surface;
308 int icon_size, icon_scale;
309 NemoZoomLevel zoom_level;
310 GdkPixbuf *icon, *rendered_icon;
311 NemoIconInfo *icon_info;
312 GList *emblem_icons, *l;
313
314 zoom_level = nemo_list_model_get_zoom_level_from_column_id (column);
315 icon_size = nemo_get_list_icon_size_for_zoom_level (zoom_level);
316 icon_scale = nemo_list_model_get_icon_scale (model);
317
318 flags = NEMO_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE |
319 NEMO_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM;
320
321 if (file_entry->ok_to_show_thumb) {
322 flags |= NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS;
323 }
324
325 if (model->details->drag_view != NULL) {
326 GtkTreePath *path_a, *path_b;
327
328 gtk_tree_view_get_drag_dest_row (model->details->drag_view,
329 &path_a,
330 NULL);
331 if (path_a != NULL) {
332 path_b = gtk_tree_model_get_path (tree_model, iter);
333
334 if (gtk_tree_path_compare (path_a, path_b) == 0) {
335 flags |= NEMO_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT;
336 }
337
338 gtk_tree_path_free (path_a);
339 gtk_tree_path_free (path_b);
340 }
341 }
342
343 icon_info = nemo_file_get_icon (file, icon_size, 0, icon_scale, flags);
344 emblem_icons = nemo_file_get_emblem_icons (file, parent_file);
345
346 if (emblem_icons) {
347 GdkPixbuf *initial_pixbuf;
348 GIcon *gicon, *emblemed_icon, *emblem_icon;
349 GEmblem *emblem;
350 gint w, h, s;
351 gboolean bad_ratio;
352
353 initial_pixbuf = nemo_icon_info_get_pixbuf_at_size (icon_info, icon_size);
354
355 w = gdk_pixbuf_get_width (initial_pixbuf);
356 h = gdk_pixbuf_get_height (initial_pixbuf);
357
358 s = MAX (w, h);
359 if (s < icon_size)
360 icon_size = s;
361
362 bad_ratio = (int)(nemo_icon_get_emblem_size_for_icon_size (icon_size) * icon_scale) > w ||
363 (int)(nemo_icon_get_emblem_size_for_icon_size (icon_size) * icon_scale) > h;
364
365 gicon = G_ICON (initial_pixbuf);
366
367 /* pick only the first emblem we can render for the list view */
368 for (l = emblem_icons; !bad_ratio && l != NULL; l = l->next) {
369 emblem_icon = l->data;
370 emblem = g_emblem_new (emblem_icon);
371 emblemed_icon = g_emblemed_icon_new (gicon, emblem);
372
373 g_object_unref (gicon);
374 g_object_unref (emblem);
375 gicon = emblemed_icon;
376
377 break;
378 }
379
380 nemo_icon_info_clear (&icon_info);
381 icon_info = nemo_icon_info_lookup (gicon, icon_size, icon_scale);
382
383 g_list_free_full (emblem_icons, g_object_unref);
384 g_object_unref (gicon);
385 }
386
387 icon = nemo_icon_info_get_pixbuf_at_size (icon_info, icon_size);
388
389 nemo_icon_info_unref (icon_info);
390
391 if (model->details->highlight_files != NULL &&
392 g_list_find_custom (model->details->highlight_files,
393 file, (GCompareFunc) nemo_file_compare_location))
394 {
395 rendered_icon = eel_create_spotlight_pixbuf (icon);
396
397 if (rendered_icon != NULL) {
398 g_object_unref (icon);
399 icon = rendered_icon;
400 }
401 }
402
403 surface = gdk_cairo_surface_create_from_pixbuf (icon, icon_scale, NULL);
404 g_value_take_boxed (value, surface);
405 g_object_unref (icon);
406 }
407 break;
408 case NEMO_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN:
409 g_value_init (value, G_TYPE_BOOLEAN);
410
411 g_value_set_boolean (value, file != NULL && nemo_file_can_rename (file));
412 break;
413 case NEMO_LIST_MODEL_TEXT_WEIGHT_COLUMN:
414 g_value_init (value, G_TYPE_INT);
415
416 if (file != NULL) {
417 if (nemo_file_is_unavailable_favorite (file)) {
418 g_value_set_int (value, UNAVAILABLE_TEXT_WEIGHT);
419 }
420 else
421 if (nemo_file_get_pinning (file) && !nemo_file_is_in_favorites (file)) {
422 g_value_set_int (value, PINNED_TEXT_WEIGHT);
423 }
424 else
425 {
426 g_value_set_int (value, NORMAL_TEXT_WEIGHT);
427 }
428 }
429 break;
430 case NEMO_LIST_MODEL_ICON_SHOWN:
431 g_value_init (value, G_TYPE_BOOLEAN);
432
433 g_value_set_boolean (value, file_entry->ok_to_show_thumb);
434 file_entry->ok_to_show_thumb = TRUE;
435 break;
436 default:
437 if (column >= NEMO_LIST_MODEL_NUM_COLUMNS || column < (int)(NEMO_LIST_MODEL_NUM_COLUMNS + model->details->columns->len)) {
438 NemoColumn *nemo_column;
439 GQuark attribute;
440 nemo_column = model->details->columns->pdata[column - NEMO_LIST_MODEL_NUM_COLUMNS];
441
442 g_value_init (value, G_TYPE_STRING);
443 g_object_get (nemo_column,
444 "attribute_q", &attribute,
445 NULL);
446 if (file != NULL) {
447 str = nemo_file_get_string_attribute_with_default_q (file,
448 attribute);
449 g_value_take_string (value, str);
450 } else if (attribute == attribute_name_q) {
451 if (file_entry->parent->loaded) {
452 g_value_set_string (value, _("(Empty)"));
453 } else {
454 g_value_set_string (value, _("Loading..."));
455 }
456 }
457 } else {
458 g_assert_not_reached ();
459 }
460 }
461 }
462
463 static gboolean
nemo_list_model_iter_next(GtkTreeModel * tree_model,GtkTreeIter * iter)464 nemo_list_model_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter)
465 {
466 NemoListModel *model;
467
468 model = (NemoListModel *)tree_model;
469
470 g_return_val_if_fail (model->details->stamp == iter->stamp, FALSE);
471
472 iter->user_data = g_sequence_iter_next (iter->user_data);
473
474 return !g_sequence_iter_is_end (iter->user_data);
475 }
476
477 static gboolean
nemo_list_model_iter_children(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent)478 nemo_list_model_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent)
479 {
480 NemoListModel *model;
481 GSequence *files;
482 FileEntry *file_entry;
483
484 model = (NemoListModel *)tree_model;
485
486 if (parent == NULL) {
487 files = model->details->files;
488 } else {
489 file_entry = g_sequence_get (parent->user_data);
490 files = file_entry->files;
491 }
492
493 if (files == NULL || g_sequence_get_length (files) == 0) {
494 return FALSE;
495 }
496
497 iter->stamp = model->details->stamp;
498 iter->user_data = g_sequence_get_begin_iter (files);
499
500 return TRUE;
501 }
502
503 static gboolean
nemo_list_model_iter_has_child(GtkTreeModel * tree_model,GtkTreeIter * iter)504 nemo_list_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
505 {
506 FileEntry *file_entry;
507
508 if (iter == NULL) {
509 return !nemo_list_model_is_empty (NEMO_LIST_MODEL (tree_model));
510 }
511
512 file_entry = g_sequence_get (iter->user_data);
513
514 return (file_entry->files != NULL && g_sequence_get_length (file_entry->files) > 0);
515 }
516
517 static int
nemo_list_model_iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)518 nemo_list_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter)
519 {
520 NemoListModel *model;
521 GSequence *files;
522 FileEntry *file_entry;
523
524 model = (NemoListModel *)tree_model;
525
526 if (iter == NULL) {
527 files = model->details->files;
528 } else {
529 file_entry = g_sequence_get (iter->user_data);
530 files = file_entry->files;
531 }
532
533 return g_sequence_get_length (files);
534 }
535
536 static gboolean
nemo_list_model_iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,int n)537 nemo_list_model_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, int n)
538 {
539 NemoListModel *model;
540 GSequenceIter *child;
541 GSequence *files;
542 FileEntry *file_entry;
543
544 model = (NemoListModel *)tree_model;
545
546 if (parent != NULL) {
547 file_entry = g_sequence_get (parent->user_data);
548 files = file_entry->files;
549 } else {
550 files = model->details->files;
551 }
552
553 child = g_sequence_get_iter_at_pos (files, n);
554
555 if (g_sequence_iter_is_end (child)) {
556 return FALSE;
557 }
558
559 iter->stamp = model->details->stamp;
560 iter->user_data = child;
561
562 return TRUE;
563 }
564
565 static gboolean
nemo_list_model_iter_parent(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * child)566 nemo_list_model_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child)
567 {
568 NemoListModel *model;
569 FileEntry *file_entry;
570
571 model = (NemoListModel *)tree_model;
572
573 file_entry = g_sequence_get (child->user_data);
574
575 if (file_entry->parent == NULL) {
576 return FALSE;
577 }
578
579 iter->stamp = model->details->stamp;
580 iter->user_data = file_entry->parent->ptr;
581
582 return TRUE;
583 }
584
585 static GSequenceIter *
lookup_file(NemoListModel * model,NemoFile * file,NemoDirectory * directory)586 lookup_file (NemoListModel *model, NemoFile *file,
587 NemoDirectory *directory)
588 {
589 FileEntry *file_entry;
590 GSequenceIter *ptr, *parent_ptr;
591
592 parent_ptr = NULL;
593 if (directory) {
594 parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map,
595 directory);
596 }
597
598 if (parent_ptr) {
599 file_entry = g_sequence_get (parent_ptr);
600 ptr = g_hash_table_lookup (file_entry->reverse_map, file);
601 } else {
602 ptr = g_hash_table_lookup (model->details->top_reverse_map, file);
603 }
604
605 if (ptr) {
606 g_assert (((FileEntry *)g_sequence_get (ptr))->file == file);
607 }
608
609 return ptr;
610 }
611
612
613 struct GetIters {
614 NemoListModel *model;
615 NemoFile *file;
616 GList *iters;
617 };
618
619 static void
dir_to_iters(struct GetIters * data,GHashTable * reverse_map)620 dir_to_iters (struct GetIters *data,
621 GHashTable *reverse_map)
622 {
623 GSequenceIter *ptr;
624
625 ptr = g_hash_table_lookup (reverse_map, data->file);
626 if (ptr) {
627 GtkTreeIter *iter;
628 iter = g_new0 (GtkTreeIter, 1);
629 nemo_list_model_ptr_to_iter (data->model, ptr, iter);
630 data->iters = g_list_prepend (data->iters, iter);
631 }
632 }
633
634 static void
file_to_iter_cb(gpointer key,gpointer value,gpointer user_data)635 file_to_iter_cb (gpointer key,
636 gpointer value,
637 gpointer user_data)
638 {
639 struct GetIters *data;
640 FileEntry *dir_file_entry;
641
642 data = user_data;
643 dir_file_entry = g_sequence_get ((GSequenceIter *)value);
644 dir_to_iters (data, dir_file_entry->reverse_map);
645 }
646
647 GList *
nemo_list_model_get_all_iters_for_file(NemoListModel * model,NemoFile * file)648 nemo_list_model_get_all_iters_for_file (NemoListModel *model, NemoFile *file)
649 {
650 struct GetIters data;
651
652 data.file = file;
653 data.model = model;
654 data.iters = NULL;
655
656 dir_to_iters (&data, model->details->top_reverse_map);
657 g_hash_table_foreach (model->details->directory_reverse_map,
658 file_to_iter_cb, &data);
659
660 return g_list_reverse (data.iters);
661 }
662
663 gboolean
nemo_list_model_get_first_iter_for_file(NemoListModel * model,NemoFile * file,GtkTreeIter * iter)664 nemo_list_model_get_first_iter_for_file (NemoListModel *model,
665 NemoFile *file,
666 GtkTreeIter *iter)
667 {
668 GList *list;
669 gboolean res;
670
671 res = FALSE;
672
673 list = nemo_list_model_get_all_iters_for_file (model, file);
674 if (list != NULL) {
675 res = TRUE;
676 *iter = *(GtkTreeIter *)list->data;
677 }
678 g_list_free_full (list, g_free);
679
680 return res;
681 }
682
683
684 gboolean
nemo_list_model_get_tree_iter_from_file(NemoListModel * model,NemoFile * file,NemoDirectory * directory,GtkTreeIter * iter)685 nemo_list_model_get_tree_iter_from_file (NemoListModel *model, NemoFile *file,
686 NemoDirectory *directory,
687 GtkTreeIter *iter)
688 {
689 GSequenceIter *ptr;
690
691 ptr = lookup_file (model, file, directory);
692 if (!ptr) {
693 return FALSE;
694 }
695
696 nemo_list_model_ptr_to_iter (model, ptr, iter);
697
698 return TRUE;
699 }
700
701 static int
nemo_list_model_file_entry_compare_func(gconstpointer a,gconstpointer b,gpointer user_data)702 nemo_list_model_file_entry_compare_func (gconstpointer a,
703 gconstpointer b,
704 gpointer user_data)
705 {
706 FileEntry *file_entry1;
707 FileEntry *file_entry2;
708 NemoListModel *model;
709 int result;
710
711 model = (NemoListModel *)user_data;
712
713 file_entry1 = (FileEntry *)a;
714 file_entry2 = (FileEntry *)b;
715
716 if (file_entry1->file != NULL && file_entry2->file != NULL) {
717 result = nemo_file_compare_for_sort_by_attribute_q (file_entry1->file, file_entry2->file,
718 model->details->sort_attribute,
719 model->details->sort_directories_first,
720 (model->details->order == GTK_SORT_DESCENDING));
721 } else if (file_entry1->file == NULL) {
722 return -1;
723 } else {
724 return 1;
725 }
726
727 return result;
728 }
729
730 int
nemo_list_model_compare_func(NemoListModel * model,NemoFile * file1,NemoFile * file2)731 nemo_list_model_compare_func (NemoListModel *model,
732 NemoFile *file1,
733 NemoFile *file2)
734 {
735 int result;
736
737 result = nemo_file_compare_for_sort_by_attribute_q (file1, file2,
738 model->details->sort_attribute,
739 model->details->sort_directories_first,
740 (model->details->order == GTK_SORT_DESCENDING));
741
742 return result;
743 }
744
745 static void
nemo_list_model_sort_file_entries(NemoListModel * model,GSequence * files,GtkTreePath * path)746 nemo_list_model_sort_file_entries (NemoListModel *model, GSequence *files, GtkTreePath *path)
747 {
748 GSequenceIter **old_order;
749 GtkTreeIter iter;
750 int *new_order;
751 int length;
752 int i;
753 FileEntry *file_entry;
754 gboolean has_iter;
755
756 length = g_sequence_get_length (files);
757
758 if (length <= 1) {
759 return;
760 }
761
762 /* generate old order of GSequenceIter's */
763 old_order = g_new (GSequenceIter *, length);
764 for (i = 0; i < length; ++i) {
765 GSequenceIter *ptr = g_sequence_get_iter_at_pos (files, i);
766
767 file_entry = g_sequence_get (ptr);
768 if (file_entry->files != NULL) {
769 gtk_tree_path_append_index (path, i);
770 nemo_list_model_sort_file_entries (model, file_entry->files, path);
771 gtk_tree_path_up (path);
772 }
773
774 old_order[i] = ptr;
775 }
776
777 /* sort */
778 g_sequence_sort (files, nemo_list_model_file_entry_compare_func, model);
779
780 /* generate new order */
781 new_order = g_new (int, length);
782 /* Note: new_order[newpos] = oldpos */
783 for (i = 0; i < length; ++i) {
784 new_order[g_sequence_iter_get_position (old_order[i])] = i;
785 }
786
787 /* Let the world know about our new order */
788
789 g_assert (new_order != NULL);
790
791 has_iter = FALSE;
792 if (gtk_tree_path_get_depth (path) != 0) {
793 gboolean get_iter_result;
794 has_iter = TRUE;
795 get_iter_result = gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
796 g_assert (get_iter_result);
797 }
798
799 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
800 path, has_iter ? &iter : NULL, new_order);
801
802 g_free (old_order);
803 g_free (new_order);
804 }
805
806 static void
nemo_list_model_sort(NemoListModel * model)807 nemo_list_model_sort (NemoListModel *model)
808 {
809 GtkTreePath *path;
810
811 path = gtk_tree_path_new ();
812
813 nemo_list_model_sort_file_entries (model, model->details->files, path);
814
815 gtk_tree_path_free (path);
816 }
817
818 static gboolean
nemo_list_model_get_sort_column_id(GtkTreeSortable * sortable,gint * sort_column_id,GtkSortType * order)819 nemo_list_model_get_sort_column_id (GtkTreeSortable *sortable,
820 gint *sort_column_id,
821 GtkSortType *order)
822 {
823 NemoListModel *model;
824 int id;
825
826 model = (NemoListModel *)sortable;
827
828 id = nemo_list_model_get_sort_column_id_from_attribute
829 (model, model->details->sort_attribute);
830
831 if (id == -1) {
832 return FALSE;
833 }
834
835 if (sort_column_id != NULL) {
836 *sort_column_id = id;
837 }
838
839 if (order != NULL) {
840 *order = model->details->order;
841 }
842
843 return TRUE;
844 }
845
846 static void
nemo_list_model_set_sort_column_id(GtkTreeSortable * sortable,gint sort_column_id,GtkSortType order)847 nemo_list_model_set_sort_column_id (GtkTreeSortable *sortable, gint sort_column_id, GtkSortType order)
848 {
849 NemoListModel *model;
850
851 model = (NemoListModel *)sortable;
852
853 model->details->sort_attribute = nemo_list_model_get_attribute_from_sort_column_id (model, sort_column_id);
854
855 model->details->order = order;
856
857 nemo_list_model_sort (model);
858 gtk_tree_sortable_sort_column_changed (sortable);
859 }
860
861 static gboolean
nemo_list_model_has_default_sort_func(GtkTreeSortable * sortable)862 nemo_list_model_has_default_sort_func (GtkTreeSortable *sortable)
863 {
864 return FALSE;
865 }
866
867 static gboolean
nemo_list_model_multi_row_draggable(EggTreeMultiDragSource * drag_source,GList * path_list)868 nemo_list_model_multi_row_draggable (EggTreeMultiDragSource *drag_source, GList *path_list)
869 {
870 return TRUE;
871 }
872
873 static void
each_path_get_data_binder(NemoDragEachSelectedItemDataGet data_get,gpointer context,gpointer data)874 each_path_get_data_binder (NemoDragEachSelectedItemDataGet data_get,
875 gpointer context,
876 gpointer data)
877 {
878 DragDataGetInfo *info;
879 GList *l;
880 NemoFile *file;
881 GtkTreeRowReference *row;
882 GtkTreePath *path;
883 char *uri;
884 GdkRectangle cell_area;
885 GtkTreeViewColumn *column;
886
887 info = context;
888
889 g_return_if_fail (info->model->details->drag_view);
890
891 column = gtk_tree_view_get_column (info->model->details->drag_view, 0);
892
893 for (l = info->path_list; l != NULL; l = l->next) {
894 row = l->data;
895
896 path = gtk_tree_row_reference_get_path (row);
897 file = nemo_list_model_file_for_path (info->model, path);
898 if (file) {
899 gtk_tree_view_get_cell_area
900 (info->model->details->drag_view,
901 path,
902 column,
903 &cell_area);
904
905 uri = nemo_file_get_uri (file);
906
907 (*data_get) (uri,
908 0,
909 cell_area.y - info->model->details->drag_begin_y,
910 cell_area.width, cell_area.height,
911 data);
912
913 g_free (uri);
914
915 nemo_file_unref (file);
916 }
917
918 gtk_tree_path_free (path);
919 }
920 }
921
922 static gboolean
nemo_list_model_multi_drag_data_get(EggTreeMultiDragSource * drag_source,GList * path_list,GtkSelectionData * selection_data)923 nemo_list_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source,
924 GList *path_list,
925 GtkSelectionData *selection_data)
926 {
927 NemoListModel *model;
928 DragDataGetInfo context;
929 guint target_info;
930
931 model = NEMO_LIST_MODEL (drag_source);
932
933 context.model = model;
934 context.path_list = path_list;
935
936 if (!drag_target_list) {
937 drag_target_list = nemo_list_model_get_drag_target_list ();
938 }
939
940 if (gtk_target_list_find (drag_target_list,
941 gtk_selection_data_get_target (selection_data),
942 &target_info)) {
943 nemo_drag_drag_data_get (NULL,
944 NULL,
945 selection_data,
946 target_info,
947 GDK_CURRENT_TIME,
948 &context,
949 each_path_get_data_binder);
950 return TRUE;
951 } else {
952 return FALSE;
953 }
954 }
955
956 static gboolean
nemo_list_model_multi_drag_data_delete(EggTreeMultiDragSource * drag_source,GList * path_list)957 nemo_list_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source, GList *path_list)
958 {
959 return TRUE;
960 }
961
962 static void
add_dummy_row(NemoListModel * model,FileEntry * parent_entry)963 add_dummy_row (NemoListModel *model, FileEntry *parent_entry)
964 {
965 FileEntry *dummy_file_entry;
966 GtkTreeIter iter;
967 GtkTreePath *path;
968
969 dummy_file_entry = g_new0 (FileEntry, 1);
970 dummy_file_entry->parent = parent_entry;
971 dummy_file_entry->ptr = g_sequence_insert_sorted (parent_entry->files, dummy_file_entry,
972 nemo_list_model_file_entry_compare_func, model);
973 iter.stamp = model->details->stamp;
974 iter.user_data = dummy_file_entry->ptr;
975
976 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
977 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
978 gtk_tree_path_free (path);
979 }
980
981 gboolean
nemo_list_model_add_file(NemoListModel * model,NemoFile * file,NemoDirectory * directory)982 nemo_list_model_add_file (NemoListModel *model, NemoFile *file,
983 NemoDirectory *directory)
984 {
985 GtkTreeIter iter;
986 GtkTreePath *path;
987 FileEntry *file_entry;
988 GSequenceIter *ptr, *parent_ptr;
989 GSequence *files;
990 gboolean replace_dummy;
991 GHashTable *parent_hash;
992
993 parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map,
994 directory);
995 if (parent_ptr) {
996 file_entry = g_sequence_get (parent_ptr);
997 ptr = g_hash_table_lookup (file_entry->reverse_map, file);
998 } else {
999 file_entry = NULL;
1000 ptr = g_hash_table_lookup (model->details->top_reverse_map, file);
1001 }
1002
1003 if (ptr != NULL) {
1004 g_warning ("file already in tree (parent_ptr: %p)!!!\n", parent_ptr);
1005 return FALSE;
1006 }
1007
1008 file_entry = g_new0 (FileEntry, 1);
1009 file_entry->file = nemo_file_ref (file);
1010 file_entry->parent = NULL;
1011 file_entry->subdirectory = NULL;
1012 file_entry->files = NULL;
1013 file_entry->ok_to_show_thumb =
1014 nemo_file_get_load_deferred_attrs (file) == NEMO_FILE_LOAD_DEFERRED_ATTRS_PRELOAD;
1015 files = model->details->files;
1016 parent_hash = model->details->top_reverse_map;
1017
1018 replace_dummy = FALSE;
1019
1020 if (parent_ptr != NULL) {
1021 file_entry->parent = g_sequence_get (parent_ptr);
1022 /* At this point we set loaded. Either we saw
1023 * "done" and ignored it waiting for this, or we do this
1024 * earlier, but then we replace the dummy row anyway,
1025 * so it doesn't matter */
1026 file_entry->parent->loaded = 1;
1027 parent_hash = file_entry->parent->reverse_map;
1028 files = file_entry->parent->files;
1029 if (g_sequence_get_length (files) == 1) {
1030 GSequenceIter *dummy_ptr = g_sequence_get_iter_at_pos (files, 0);
1031 FileEntry *dummy_entry = g_sequence_get (dummy_ptr);
1032 if (dummy_entry->file == NULL) {
1033 /* replace the dummy loading entry */
1034 model->details->stamp++;
1035 g_sequence_remove (dummy_ptr);
1036
1037 replace_dummy = TRUE;
1038 }
1039 }
1040 }
1041
1042 if (model->details->temp_unsorted)
1043 file_entry->ptr = g_sequence_append (files, file_entry);
1044 else
1045 file_entry->ptr = g_sequence_insert_sorted (files, file_entry,
1046 nemo_list_model_file_entry_compare_func, model);
1047
1048 g_hash_table_insert (parent_hash, file, file_entry->ptr);
1049
1050 iter.stamp = model->details->stamp;
1051 iter.user_data = file_entry->ptr;
1052
1053 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1054 if (replace_dummy) {
1055 gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1056 } else {
1057 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
1058 }
1059
1060 if (nemo_file_is_directory (file)) {
1061 guint count;
1062 gboolean got_count, unreadable;
1063
1064 file_entry->files = g_sequence_new ((GDestroyNotify)file_entry_free);
1065
1066 got_count = nemo_file_get_directory_item_count (file, &count, &unreadable);
1067
1068 if ((!got_count && !unreadable) || count > 0) {
1069 add_dummy_row (model, file_entry);
1070 gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
1071 path, &iter);
1072 }
1073 }
1074
1075 gtk_tree_path_free (path);
1076
1077 return TRUE;
1078 }
1079
1080 static gboolean
update_dummy_row(NemoListModel * model,NemoFile * file,FileEntry * file_entry)1081 update_dummy_row (NemoListModel *model,
1082 NemoFile *file,
1083 FileEntry *file_entry)
1084 {
1085 GSequence *files;
1086 gboolean changed;
1087 guint count;
1088 gboolean got_count, unreadable;
1089
1090 changed = FALSE;
1091
1092 got_count = nemo_file_get_directory_item_count (file, &count, &unreadable);
1093
1094 if ((got_count && count == 0) || (!got_count && unreadable)) {
1095 files = file_entry->files;
1096 if (g_sequence_get_length (files) == 1) {
1097 GSequenceIter *dummy_ptr = g_sequence_get_iter_at_pos (files, 0);
1098 FileEntry *dummy_entry = g_sequence_get (dummy_ptr);
1099
1100 if (dummy_entry->file == NULL) {
1101 if (!file_entry->expanding) {
1102
1103 model->details->stamp++;
1104 g_sequence_remove (dummy_ptr);
1105 changed = TRUE;
1106 }
1107 }
1108 }
1109 }
1110
1111 file_entry->expanding = FALSE;
1112
1113 return changed;
1114 }
1115
1116 void
nemo_list_model_file_changed(NemoListModel * model,NemoFile * file,NemoDirectory * directory)1117 nemo_list_model_file_changed (NemoListModel *model, NemoFile *file,
1118 NemoDirectory *directory)
1119 {
1120 FileEntry *parent_file_entry;
1121 GtkTreeIter iter;
1122 GtkTreePath *path, *parent_path;
1123 GSequenceIter *ptr;
1124 int pos_before, pos_after, length, i, old;
1125 int *new_order;
1126 gboolean has_iter;
1127 GSequence *files;
1128
1129 ptr = lookup_file (model, file, directory);
1130 if (!ptr) {
1131 return;
1132 }
1133
1134 pos_before = g_sequence_iter_get_position (ptr);
1135
1136 if (!model->details->temp_unsorted)
1137 g_sequence_sort_changed (ptr, nemo_list_model_file_entry_compare_func, model);
1138
1139 pos_after = g_sequence_iter_get_position (ptr);
1140
1141 if (pos_before != pos_after) {
1142 /* The file moved, we need to send rows_reordered */
1143
1144 parent_file_entry = ((FileEntry *)g_sequence_get (ptr))->parent;
1145
1146 if (parent_file_entry == NULL) {
1147 has_iter = FALSE;
1148 parent_path = gtk_tree_path_new ();
1149 files = model->details->files;
1150 } else {
1151 has_iter = TRUE;
1152 nemo_list_model_ptr_to_iter (model, parent_file_entry->ptr, &iter);
1153 parent_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1154 files = parent_file_entry->files;
1155 }
1156
1157 length = g_sequence_get_length (files);
1158 new_order = g_new (int, length);
1159 /* Note: new_order[newpos] = oldpos */
1160 for (i = 0, old = 0; i < length; ++i) {
1161 if (i == pos_after) {
1162 new_order[i] = pos_before;
1163 } else {
1164 if (old == pos_before)
1165 old++;
1166 new_order[i] = old++;
1167 }
1168 }
1169
1170 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
1171 parent_path, has_iter ? &iter : NULL, new_order);
1172
1173 gtk_tree_path_free (parent_path);
1174 g_free (new_order);
1175 }
1176
1177 nemo_list_model_ptr_to_iter (model, ptr, &iter);
1178 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1179
1180 gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1181
1182 if (nemo_file_is_directory (file)) {
1183 if (update_dummy_row (model, file, g_sequence_get (ptr))) {
1184 gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
1185 path, &iter);
1186 }
1187 }
1188
1189 gtk_tree_path_free (path);
1190 }
1191
1192 gboolean
nemo_list_model_is_empty(NemoListModel * model)1193 nemo_list_model_is_empty (NemoListModel *model)
1194 {
1195 return (g_sequence_get_length (model->details->files) == 0);
1196 }
1197
1198 guint
nemo_list_model_get_length(NemoListModel * model)1199 nemo_list_model_get_length (NemoListModel *model)
1200 {
1201 return g_sequence_get_length (model->details->files);
1202 }
1203
1204 static void
nemo_list_model_remove(NemoListModel * model,GtkTreeIter * iter)1205 nemo_list_model_remove (NemoListModel *model, GtkTreeIter *iter)
1206 {
1207 GSequenceIter *ptr, *child_ptr;
1208 FileEntry *file_entry, *child_file_entry, *parent_file_entry;
1209 GtkTreePath *path;
1210 GtkTreeIter parent_iter;
1211
1212 ptr = iter->user_data;
1213 file_entry = g_sequence_get (ptr);
1214
1215 if (file_entry->files != NULL) {
1216 while (g_sequence_get_length (file_entry->files) > 0) {
1217 child_ptr = g_sequence_get_begin_iter (file_entry->files);
1218 child_file_entry = g_sequence_get (child_ptr);
1219 if (child_file_entry->file != NULL) {
1220 nemo_list_model_remove_file (model,
1221 child_file_entry->file,
1222 file_entry->subdirectory);
1223 } else {
1224 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
1225 gtk_tree_path_append_index (path, 0);
1226 model->details->stamp++;
1227 g_sequence_remove (child_ptr);
1228 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1229 gtk_tree_path_free (path);
1230 }
1231
1232 /* the parent iter didn't actually change */
1233 iter->stamp = model->details->stamp;
1234 }
1235
1236 }
1237
1238 if (file_entry->file != NULL) { /* Don't try to remove dummy row */
1239 if (file_entry->parent != NULL) {
1240 g_hash_table_remove (file_entry->parent->reverse_map, file_entry->file);
1241 } else {
1242 g_hash_table_remove (model->details->top_reverse_map, file_entry->file);
1243 }
1244 }
1245
1246 parent_file_entry = file_entry->parent;
1247 if (parent_file_entry && g_sequence_get_length (parent_file_entry->files) == 1 &&
1248 file_entry->file != NULL) {
1249 /* this is the last non-dummy child, add a dummy node */
1250 /* We need to do this before removing the last file to avoid
1251 * collapsing the row.
1252 */
1253
1254 guint count;
1255 gboolean got_count, unreadable;
1256
1257 got_count = nemo_file_get_directory_item_count (parent_file_entry->file, &count, &unreadable);
1258
1259 if ((!got_count && !unreadable) || count > 0) {
1260 add_dummy_row (model, parent_file_entry);
1261 }
1262 }
1263
1264 if (file_entry->subdirectory != NULL) {
1265 g_signal_emit (model,
1266 list_model_signals[SUBDIRECTORY_UNLOADED], 0,
1267 file_entry->subdirectory);
1268 g_hash_table_remove (model->details->directory_reverse_map,
1269 file_entry->subdirectory);
1270 }
1271
1272 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
1273
1274 g_sequence_remove (ptr);
1275 model->details->stamp++;
1276 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1277
1278 gtk_tree_path_free (path);
1279
1280 if (parent_file_entry && g_sequence_get_length (parent_file_entry->files) == 0) {
1281 parent_iter.stamp = model->details->stamp;
1282 parent_iter.user_data = parent_file_entry->ptr;
1283 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &parent_iter);
1284 gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
1285 path, &parent_iter);
1286 gtk_tree_path_free (path);
1287 }
1288 }
1289
1290 void
nemo_list_model_remove_file(NemoListModel * model,NemoFile * file,NemoDirectory * directory)1291 nemo_list_model_remove_file (NemoListModel *model, NemoFile *file,
1292 NemoDirectory *directory)
1293 {
1294 GtkTreeIter iter;
1295
1296 if (nemo_list_model_get_tree_iter_from_file (model, file, directory, &iter)) {
1297 nemo_list_model_remove (model, &iter);
1298 }
1299 }
1300
1301 static void
nemo_list_model_clear_directory(NemoListModel * model,GSequence * files)1302 nemo_list_model_clear_directory (NemoListModel *model, GSequence *files)
1303 {
1304 GtkTreeIter iter;
1305 FileEntry *file_entry;
1306
1307 while (g_sequence_get_length (files) > 0) {
1308 iter.user_data = g_sequence_get_begin_iter (files);
1309
1310 file_entry = g_sequence_get (iter.user_data);
1311 if (file_entry->files != NULL) {
1312 nemo_list_model_clear_directory (model, file_entry->files);
1313 }
1314
1315 iter.stamp = model->details->stamp;
1316 nemo_list_model_remove (model, &iter);
1317 }
1318 }
1319
1320 void
nemo_list_model_clear(NemoListModel * model)1321 nemo_list_model_clear (NemoListModel *model)
1322 {
1323 g_return_if_fail (model != NULL);
1324
1325 nemo_list_model_clear_directory (model, model->details->files);
1326 }
1327
1328 NemoFile *
nemo_list_model_file_for_path(NemoListModel * model,GtkTreePath * path)1329 nemo_list_model_file_for_path (NemoListModel *model, GtkTreePath *path)
1330 {
1331 NemoFile *file;
1332 GtkTreeIter iter;
1333
1334 file = NULL;
1335 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model),
1336 &iter, path)) {
1337 gtk_tree_model_get (GTK_TREE_MODEL (model),
1338 &iter,
1339 NEMO_LIST_MODEL_FILE_COLUMN, &file,
1340 -1);
1341 }
1342 return file;
1343 }
1344
1345 gboolean
nemo_list_model_load_subdirectory(NemoListModel * model,GtkTreePath * path,NemoDirectory ** directory)1346 nemo_list_model_load_subdirectory (NemoListModel *model, GtkTreePath *path, NemoDirectory **directory)
1347 {
1348 GtkTreeIter iter;
1349 FileEntry *file_entry;
1350 NemoDirectory *subdirectory;
1351
1352 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) {
1353 return FALSE;
1354 }
1355
1356 file_entry = g_sequence_get (iter.user_data);
1357 if (file_entry->file == NULL ||
1358 file_entry->subdirectory != NULL) {
1359 return FALSE;
1360 }
1361
1362 subdirectory = nemo_directory_get_for_file (file_entry->file);
1363
1364 if (g_hash_table_lookup (model->details->directory_reverse_map,
1365 subdirectory) != NULL) {
1366 nemo_directory_unref (subdirectory);
1367 g_warning ("Already in directory_reverse_map, failing\n");
1368 return FALSE;
1369 }
1370
1371 file_entry->subdirectory = subdirectory,
1372 g_hash_table_insert (model->details->directory_reverse_map,
1373 subdirectory, file_entry->ptr);
1374 file_entry->reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1375
1376 /* Return a ref too */
1377 nemo_directory_ref (subdirectory);
1378 *directory = subdirectory;
1379
1380 return TRUE;
1381 }
1382
1383 /* removes all children of the subfolder and unloads the subdirectory */
1384 void
nemo_list_model_unload_subdirectory(NemoListModel * model,GtkTreeIter * iter)1385 nemo_list_model_unload_subdirectory (NemoListModel *model, GtkTreeIter *iter)
1386 {
1387 GSequenceIter *child_ptr;
1388 FileEntry *file_entry, *child_file_entry;
1389 GtkTreeIter child_iter;
1390
1391 file_entry = g_sequence_get (iter->user_data);
1392 if (file_entry->file == NULL ||
1393 file_entry->subdirectory == NULL) {
1394 return;
1395 }
1396
1397 file_entry->loaded = 0;
1398
1399 /* Remove all children */
1400 while (g_sequence_get_length (file_entry->files) > 0) {
1401 child_ptr = g_sequence_get_begin_iter (file_entry->files);
1402 child_file_entry = g_sequence_get (child_ptr);
1403 if (child_file_entry->file == NULL) {
1404 /* Don't delete the dummy node */
1405 break;
1406 } else {
1407 nemo_list_model_ptr_to_iter (model, child_ptr, &child_iter);
1408 nemo_list_model_remove (model, &child_iter);
1409 }
1410 }
1411
1412 /* Emit unload signal */
1413 g_signal_emit (model,
1414 list_model_signals[SUBDIRECTORY_UNLOADED], 0,
1415 file_entry->subdirectory);
1416
1417 /* actually unload */
1418 g_hash_table_remove (model->details->directory_reverse_map,
1419 file_entry->subdirectory);
1420 nemo_directory_unref (file_entry->subdirectory);
1421 file_entry->subdirectory = NULL;
1422
1423 g_assert (g_hash_table_size (file_entry->reverse_map) == 0);
1424 g_hash_table_destroy (file_entry->reverse_map);
1425 file_entry->reverse_map = NULL;
1426 }
1427
1428
1429
1430 void
nemo_list_model_set_should_sort_directories_first(NemoListModel * model,gboolean sort_directories_first)1431 nemo_list_model_set_should_sort_directories_first (NemoListModel *model, gboolean sort_directories_first)
1432 {
1433 if (model->details->sort_directories_first == sort_directories_first) {
1434 return;
1435 }
1436
1437 model->details->sort_directories_first = sort_directories_first;
1438 nemo_list_model_sort (model);
1439 }
1440
1441 int
nemo_list_model_get_sort_column_id_from_attribute(NemoListModel * model,GQuark attribute)1442 nemo_list_model_get_sort_column_id_from_attribute (NemoListModel *model,
1443 GQuark attribute)
1444 {
1445 guint i;
1446
1447 if (attribute == 0) {
1448 return -1;
1449 }
1450
1451 /* Hack - the preferences dialog sets modification_date for some
1452 * rather than date_modified for some reason. Make sure that
1453 * works. */
1454 if (attribute == attribute_modification_date_q) {
1455 attribute = attribute_date_modified_q;
1456 }
1457
1458 for (i = 0; i < model->details->columns->len; i++) {
1459 NemoColumn *column;
1460 GQuark column_attribute;
1461
1462 column =
1463 NEMO_COLUMN (model->details->columns->pdata[i]);
1464 g_object_get (G_OBJECT (column),
1465 "attribute_q", &column_attribute,
1466 NULL);
1467 if (column_attribute == attribute) {
1468 return NEMO_LIST_MODEL_NUM_COLUMNS + i;
1469 }
1470 }
1471
1472 return -1;
1473 }
1474
1475 GQuark
nemo_list_model_get_attribute_from_sort_column_id(NemoListModel * model,int sort_column_id)1476 nemo_list_model_get_attribute_from_sort_column_id (NemoListModel *model,
1477 int sort_column_id)
1478 {
1479 NemoColumn *column;
1480 int index;
1481 GQuark attribute;
1482
1483 index = sort_column_id - NEMO_LIST_MODEL_NUM_COLUMNS;
1484
1485 if (index < 0 || index >= (int)model->details->columns->len) {
1486 g_warning ("unknown sort column id: %d", sort_column_id);
1487 return 0;
1488 }
1489
1490 column = NEMO_COLUMN (model->details->columns->pdata[index]);
1491 g_object_get (G_OBJECT (column), "attribute_q", &attribute, NULL);
1492
1493 return attribute;
1494 }
1495
1496 NemoZoomLevel
nemo_list_model_get_zoom_level_from_column_id(int column)1497 nemo_list_model_get_zoom_level_from_column_id (int column)
1498 {
1499 switch (column) {
1500 case NEMO_LIST_MODEL_SMALLEST_ICON_COLUMN:
1501 return NEMO_ZOOM_LEVEL_SMALLEST;
1502 case NEMO_LIST_MODEL_SMALLER_ICON_COLUMN:
1503 return NEMO_ZOOM_LEVEL_SMALLER;
1504 case NEMO_LIST_MODEL_SMALL_ICON_COLUMN:
1505 return NEMO_ZOOM_LEVEL_SMALL;
1506 case NEMO_LIST_MODEL_STANDARD_ICON_COLUMN:
1507 return NEMO_ZOOM_LEVEL_STANDARD;
1508 case NEMO_LIST_MODEL_LARGE_ICON_COLUMN:
1509 return NEMO_ZOOM_LEVEL_LARGE;
1510 case NEMO_LIST_MODEL_LARGER_ICON_COLUMN:
1511 return NEMO_ZOOM_LEVEL_LARGER;
1512 case NEMO_LIST_MODEL_LARGEST_ICON_COLUMN:
1513 return NEMO_ZOOM_LEVEL_LARGEST;
1514 default:
1515 break;
1516 }
1517
1518 g_return_val_if_reached (NEMO_ZOOM_LEVEL_STANDARD);
1519 }
1520
1521 int
nemo_list_model_get_column_id_from_zoom_level(NemoZoomLevel zoom_level)1522 nemo_list_model_get_column_id_from_zoom_level (NemoZoomLevel zoom_level)
1523 {
1524 switch (zoom_level) {
1525 case NEMO_ZOOM_LEVEL_SMALLEST:
1526 return NEMO_LIST_MODEL_SMALLEST_ICON_COLUMN;
1527 case NEMO_ZOOM_LEVEL_SMALLER:
1528 return NEMO_LIST_MODEL_SMALLER_ICON_COLUMN;
1529 case NEMO_ZOOM_LEVEL_SMALL:
1530 return NEMO_LIST_MODEL_SMALL_ICON_COLUMN;
1531 case NEMO_ZOOM_LEVEL_STANDARD:
1532 return NEMO_LIST_MODEL_STANDARD_ICON_COLUMN;
1533 case NEMO_ZOOM_LEVEL_LARGE:
1534 return NEMO_LIST_MODEL_LARGE_ICON_COLUMN;
1535 case NEMO_ZOOM_LEVEL_LARGER:
1536 return NEMO_LIST_MODEL_LARGER_ICON_COLUMN;
1537 case NEMO_ZOOM_LEVEL_LARGEST:
1538 return NEMO_LIST_MODEL_LARGEST_ICON_COLUMN;
1539 case NEMO_ZOOM_LEVEL_NULL:
1540 default:
1541 g_return_val_if_reached (NEMO_LIST_MODEL_STANDARD_ICON_COLUMN);
1542 }
1543 }
1544
1545 void
nemo_list_model_set_drag_view(NemoListModel * model,GtkTreeView * view,int drag_begin_x,int drag_begin_y)1546 nemo_list_model_set_drag_view (NemoListModel *model,
1547 GtkTreeView *view,
1548 int drag_begin_x,
1549 int drag_begin_y)
1550 {
1551 g_return_if_fail (model != NULL);
1552 g_return_if_fail (NEMO_IS_LIST_MODEL (model));
1553 g_return_if_fail (!view || GTK_IS_TREE_VIEW (view));
1554
1555 model->details->drag_view = view;
1556 model->details->drag_begin_x = drag_begin_x;
1557 model->details->drag_begin_y = drag_begin_y;
1558 }
1559
1560 GtkTargetList *
nemo_list_model_get_drag_target_list(void)1561 nemo_list_model_get_drag_target_list (void)
1562 {
1563 GtkTargetList *target_list;
1564
1565 target_list = gtk_target_list_new (drag_types, G_N_ELEMENTS (drag_types));
1566 gtk_target_list_add_text_targets (target_list, NEMO_ICON_DND_TEXT);
1567
1568 return target_list;
1569 }
1570
1571 int
nemo_list_model_add_column(NemoListModel * model,NemoColumn * column)1572 nemo_list_model_add_column (NemoListModel *model,
1573 NemoColumn *column)
1574 {
1575 g_ptr_array_add (model->details->columns, column);
1576 g_object_ref (column);
1577
1578 return NEMO_LIST_MODEL_NUM_COLUMNS + (model->details->columns->len - 1);
1579 }
1580
1581 int
nemo_list_model_get_column_number(NemoListModel * model,const char * column_name)1582 nemo_list_model_get_column_number (NemoListModel *model,
1583 const char *column_name)
1584 {
1585 guint i;
1586
1587 for (i = 0; i < model->details->columns->len; i++) {
1588 NemoColumn *column;
1589 char *name;
1590
1591 column = model->details->columns->pdata[i];
1592
1593 g_object_get (G_OBJECT (column), "name", &name, NULL);
1594
1595 if (!strcmp (name, column_name)) {
1596 g_free (name);
1597 return NEMO_LIST_MODEL_NUM_COLUMNS + i;
1598 }
1599 g_free (name);
1600 }
1601
1602 return -1;
1603 }
1604
1605 static void
nemo_list_model_dispose(GObject * object)1606 nemo_list_model_dispose (GObject *object)
1607 {
1608 NemoListModel *model;
1609 guint i;
1610
1611 model = NEMO_LIST_MODEL (object);
1612
1613 if (model->details->columns) {
1614 for (i = 0; i < model->details->columns->len; i++) {
1615 g_object_unref (model->details->columns->pdata[i]);
1616 }
1617 g_ptr_array_free (model->details->columns, TRUE);
1618 model->details->columns = NULL;
1619 }
1620
1621 if (model->details->files) {
1622 g_sequence_free (model->details->files);
1623 model->details->files = NULL;
1624 }
1625
1626 if (model->details->top_reverse_map) {
1627 g_hash_table_destroy (model->details->top_reverse_map);
1628 model->details->top_reverse_map = NULL;
1629 }
1630 if (model->details->directory_reverse_map) {
1631 g_hash_table_destroy (model->details->directory_reverse_map);
1632 model->details->directory_reverse_map = NULL;
1633 }
1634
1635 G_OBJECT_CLASS (nemo_list_model_parent_class)->dispose (object);
1636 }
1637
1638 static void
nemo_list_model_finalize(GObject * object)1639 nemo_list_model_finalize (GObject *object)
1640 {
1641 NemoListModel *model;
1642
1643 model = NEMO_LIST_MODEL (object);
1644
1645 if (model->details->highlight_files != NULL) {
1646 nemo_file_list_free (model->details->highlight_files);
1647 model->details->highlight_files = NULL;
1648 }
1649
1650 g_free (model->details);
1651
1652 G_OBJECT_CLASS (nemo_list_model_parent_class)->finalize (object);
1653 }
1654
1655 static void
nemo_list_model_init(NemoListModel * model)1656 nemo_list_model_init (NemoListModel *model)
1657 {
1658 model->details = g_new0 (NemoListModelDetails, 1);
1659 model->details->files = g_sequence_new ((GDestroyNotify)file_entry_free);
1660 model->details->top_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1661 model->details->directory_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1662 model->details->stamp = g_random_int ();
1663 model->details->sort_attribute = 0;
1664 model->details->columns = g_ptr_array_new ();
1665 }
1666
1667 static void
nemo_list_model_class_init(NemoListModelClass * klass)1668 nemo_list_model_class_init (NemoListModelClass *klass)
1669 {
1670 GObjectClass *object_class;
1671
1672 attribute_name_q = g_quark_from_static_string ("name");
1673 attribute_modification_date_q = g_quark_from_static_string ("modification_date");
1674 attribute_date_modified_q = g_quark_from_static_string ("date_modified");
1675
1676 object_class = (GObjectClass *)klass;
1677 object_class->finalize = nemo_list_model_finalize;
1678 object_class->dispose = nemo_list_model_dispose;
1679
1680 list_model_signals[SUBDIRECTORY_UNLOADED] =
1681 g_signal_new ("subdirectory_unloaded",
1682 NEMO_TYPE_LIST_MODEL,
1683 G_SIGNAL_RUN_FIRST,
1684 G_STRUCT_OFFSET (NemoListModelClass, subdirectory_unloaded),
1685 NULL, NULL,
1686 g_cclosure_marshal_VOID__OBJECT,
1687 G_TYPE_NONE, 1,
1688 NEMO_TYPE_DIRECTORY);
1689
1690 list_model_signals[GET_ICON_SCALE] =
1691 g_signal_new ("get-icon-scale",
1692 NEMO_TYPE_LIST_MODEL,
1693 G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
1694 0, NULL, NULL,
1695 NULL,
1696 G_TYPE_INT, 0);
1697 }
1698
1699 static void
nemo_list_model_tree_model_init(GtkTreeModelIface * iface)1700 nemo_list_model_tree_model_init (GtkTreeModelIface *iface)
1701 {
1702 iface->get_flags = nemo_list_model_get_flags;
1703 iface->get_n_columns = nemo_list_model_get_n_columns;
1704 iface->get_column_type = nemo_list_model_get_column_type;
1705 iface->get_iter = nemo_list_model_get_iter;
1706 iface->get_path = nemo_list_model_get_path;
1707 iface->get_value = nemo_list_model_get_value;
1708 iface->iter_next = nemo_list_model_iter_next;
1709 iface->iter_children = nemo_list_model_iter_children;
1710 iface->iter_has_child = nemo_list_model_iter_has_child;
1711 iface->iter_n_children = nemo_list_model_iter_n_children;
1712 iface->iter_nth_child = nemo_list_model_iter_nth_child;
1713 iface->iter_parent = nemo_list_model_iter_parent;
1714 }
1715
1716 static void
nemo_list_model_sortable_init(GtkTreeSortableIface * iface)1717 nemo_list_model_sortable_init (GtkTreeSortableIface *iface)
1718 {
1719 iface->get_sort_column_id = nemo_list_model_get_sort_column_id;
1720 iface->set_sort_column_id = nemo_list_model_set_sort_column_id;
1721 iface->has_default_sort_func = nemo_list_model_has_default_sort_func;
1722 }
1723
1724 static void
nemo_list_model_multi_drag_source_init(EggTreeMultiDragSourceIface * iface)1725 nemo_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface)
1726 {
1727 iface->row_draggable = nemo_list_model_multi_row_draggable;
1728 iface->drag_data_get = nemo_list_model_multi_drag_data_get;
1729 iface->drag_data_delete = nemo_list_model_multi_drag_data_delete;
1730 }
1731
1732 void
nemo_list_model_subdirectory_done_loading(NemoListModel * model,NemoDirectory * directory)1733 nemo_list_model_subdirectory_done_loading (NemoListModel *model, NemoDirectory *directory)
1734 {
1735 GtkTreeIter iter;
1736 GtkTreePath *path;
1737 FileEntry *file_entry, *dummy_entry;
1738 GSequenceIter *parent_ptr, *dummy_ptr;
1739 GSequence *files;
1740
1741 if (model == NULL || model->details->directory_reverse_map == NULL) {
1742 return;
1743 }
1744 parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map,
1745 directory);
1746 if (parent_ptr == NULL) {
1747 return;
1748 }
1749
1750
1751
1752 file_entry = g_sequence_get (parent_ptr);
1753 files = file_entry->files;
1754
1755 /* Only swap loading -> empty if we saw no files yet at "done",
1756 * otherwise, toggle loading at first added file to the model.
1757 */
1758 if (!nemo_directory_is_not_empty (directory) &&
1759 g_sequence_get_length (files) == 1) {
1760 dummy_ptr = g_sequence_get_iter_at_pos (file_entry->files, 0);
1761 dummy_entry = g_sequence_get (dummy_ptr);
1762 if (dummy_entry->file == NULL) {
1763 /* was the dummy file */
1764 file_entry->loaded = 1;
1765
1766 iter.stamp = model->details->stamp;
1767 iter.user_data = dummy_ptr;
1768
1769 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1770 gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
1771 gtk_tree_path_free (path);
1772 }
1773 }
1774 }
1775
1776 static void
refresh_row(gpointer data,gpointer user_data)1777 refresh_row (gpointer data,
1778 gpointer user_data)
1779 {
1780 NemoFile *file;
1781 NemoListModel *model;
1782 GList *iters, *l;
1783 GtkTreePath *path;
1784
1785 model = user_data;
1786 file = data;
1787
1788 iters = nemo_list_model_get_all_iters_for_file (model, file);
1789 for (l = iters; l != NULL; l = l->next) {
1790 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), l->data);
1791 gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, l->data);
1792
1793 gtk_tree_path_free (path);
1794 }
1795
1796 g_list_free_full (iters, g_free);
1797 }
1798
1799 void
nemo_list_model_set_highlight_for_files(NemoListModel * model,GList * files)1800 nemo_list_model_set_highlight_for_files (NemoListModel *model,
1801 GList *files)
1802 {
1803 if (model->details->highlight_files != NULL) {
1804 g_list_foreach (model->details->highlight_files,
1805 refresh_row, model);
1806 nemo_file_list_free (model->details->highlight_files);
1807 model->details->highlight_files = NULL;
1808 }
1809
1810 if (files != NULL) {
1811 model->details->highlight_files = nemo_file_list_copy (files);
1812 g_list_foreach (model->details->highlight_files,
1813 refresh_row, model);
1814
1815 }
1816 }
1817
1818 void
nemo_list_model_set_temporarily_disable_sort(NemoListModel * model,gboolean disable)1819 nemo_list_model_set_temporarily_disable_sort (NemoListModel *model, gboolean disable)
1820 {
1821 model->details->temp_unsorted = disable;
1822
1823 if (!disable)
1824 nemo_list_model_sort (model);
1825 }
1826
1827 gboolean
nemo_list_model_get_temporarily_disable_sort(NemoListModel * model)1828 nemo_list_model_get_temporarily_disable_sort (NemoListModel *model)
1829 {
1830 return model->details->temp_unsorted;
1831 }
1832
1833 void
nemo_list_model_set_expanding(NemoListModel * model,NemoDirectory * directory)1834 nemo_list_model_set_expanding (NemoListModel *model, NemoDirectory *directory)
1835 {
1836 FileEntry *entry;
1837 GSequenceIter *ptr;
1838
1839 ptr = NULL;
1840
1841 if (directory) {
1842 ptr = g_hash_table_lookup (model->details->directory_reverse_map,
1843 directory);
1844 }
1845
1846 if (!ptr) {
1847 g_warning ("nemo_list_model_set_expanding: No pointer found, something's wrong?");
1848 return;
1849 }
1850
1851 entry = g_sequence_get (ptr);
1852 entry->expanding = TRUE;
1853 }
1854