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