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