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