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