1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2008-2009 Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <glib/gi18n.h>
24 #include "cairo-utils.h"
25 #include "glib-utils.h"
26 #include "gth-file-store.h"
27 #include "gth-marshal.h"
28 #include "gth-string-list.h"
29 
30 
31 #undef  DEBUG_FILE_STORE
32 #define VALID_ITER(iter, store) (((iter) != NULL) && ((iter)->stamp == (store)->priv->stamp) && ((iter)->user_data != NULL))
33 #define REALLOC_STEP 32
34 
35 
36 enum {
37 	VISIBILITY_CHANGED,
38 	THUMBNAIL_CHANGED,
39 	LAST_SIGNAL
40 };
41 
42 
43 static GType column_type[GTH_FILE_STORE_N_COLUMNS] = { G_TYPE_INVALID, };
44 static guint gth_file_store_signals[LAST_SIGNAL] = { 0 };
45 
46 
47 typedef struct {
48 	int              ref_count;
49 	GthFileData     *file_data;
50 	cairo_surface_t *thumbnail;
51 	gboolean         is_icon : 1;
52 
53 	/*< private >*/
54 
55 	guint        pos;
56 	guint        abs_pos;
57 	gboolean     visible : 1;
58 	gboolean     changed : 1;
59 } GthFileRow;
60 
61 
62 struct _GthFileStorePrivate {
63 	GthFileRow         **all_rows;
64 	GthFileRow         **rows;
65 	guint                size;
66 	guint                tot_rows;
67 	guint                num_rows;
68 	int                  stamp;
69 	gboolean             load_thumbs;
70 	GthTest             *filter;
71 	GList               *queue;
72 	GthFileDataCompFunc  cmp_func;
73 	gboolean             inverse_sort : 1;
74 	gboolean             update_filter : 1;
75 };
76 
77 
78 static void gtk_tree_model_interface_init (GtkTreeModelIface *iface);
79 static void gtk_tree_drag_source_interface_init (GtkTreeDragSourceIface *iface);
80 
81 
G_DEFINE_TYPE_WITH_CODE(GthFileStore,gth_file_store,G_TYPE_OBJECT,G_ADD_PRIVATE (GthFileStore)G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,gtk_tree_model_interface_init)G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,gtk_tree_drag_source_interface_init))82 G_DEFINE_TYPE_WITH_CODE (GthFileStore,
83 			 gth_file_store,
84 			 G_TYPE_OBJECT,
85 			 G_ADD_PRIVATE (GthFileStore)
86 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
87 						gtk_tree_model_interface_init)
88 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
89 						gtk_tree_drag_source_interface_init))
90 
91 
92 static GthFileRow *
93 _gth_file_row_new (void)
94 {
95 	GthFileRow *row;
96 
97 	row = g_new0 (GthFileRow, 1);
98 	row->ref_count = 1;
99 
100 	return row;
101 }
102 
103 
104 static void
_gth_file_row_set_file(GthFileRow * row,GthFileData * file)105 _gth_file_row_set_file (GthFileRow  *row,
106 			GthFileData *file)
107 {
108 	if (file != NULL) {
109 		g_object_ref (file);
110 		if (row->file_data != NULL)
111 			g_object_unref (row->file_data);
112 		row->file_data = file;
113 	}
114 }
115 
116 
117 static void
_gth_file_row_set_thumbnail(GthFileRow * row,cairo_surface_t * thumbnail)118 _gth_file_row_set_thumbnail (GthFileRow      *row,
119 			     cairo_surface_t *thumbnail)
120 {
121 	if (thumbnail != NULL) {
122 		cairo_surface_reference (thumbnail);
123 		if (row->thumbnail != NULL)
124 			cairo_surface_destroy (row->thumbnail);
125 		row->thumbnail = thumbnail;
126 	}
127 }
128 
129 
130 static GthFileRow *
_gth_file_row_copy(GthFileRow * row)131 _gth_file_row_copy (GthFileRow *row)
132 {
133 	GthFileRow *row2;
134 
135 	row2 = _gth_file_row_new ();
136 	_gth_file_row_set_file (row2, row->file_data);
137 	_gth_file_row_set_thumbnail (row2, row->thumbnail);
138 	row2->is_icon = row->is_icon;
139 	row2->pos = row->pos;
140 	row2->abs_pos = row->abs_pos;
141 	row2->visible = row->visible;
142 	row2->changed = row->changed;
143 
144 	return row2;
145 }
146 
147 
148 static GthFileRow *
_gth_file_row_ref(GthFileRow * row)149 _gth_file_row_ref (GthFileRow *row)
150 {
151 	row->ref_count++;
152 	return row;
153 }
154 
155 
156 static void
_gth_file_row_unref(GthFileRow * row)157 _gth_file_row_unref (GthFileRow *row)
158 {
159 	if (--row->ref_count > 0)
160 		return;
161 
162 	if (row->file_data != NULL)
163 		g_object_unref (row->file_data);
164 	if (row->thumbnail != NULL)
165 		cairo_surface_destroy (row->thumbnail);
166 	g_free (row);
167 }
168 
169 
170 static void
_gth_file_store_clear_queue(GthFileStore * file_store)171 _gth_file_store_clear_queue (GthFileStore *file_store)
172 {
173 	g_list_foreach (file_store->priv->queue, (GFunc) _gth_file_row_unref, NULL);
174 	g_list_free (file_store->priv->queue);
175 	file_store->priv->queue = NULL;
176 }
177 
178 
179 static void
_gth_file_store_free_rows(GthFileStore * file_store)180 _gth_file_store_free_rows (GthFileStore *file_store)
181 {
182 	int i;
183 
184 	for (i = 0; i < file_store->priv->tot_rows; i++)
185 		_gth_file_row_unref (file_store->priv->all_rows[i]);
186 	g_free (file_store->priv->all_rows);
187 	file_store->priv->all_rows = NULL;
188 	file_store->priv->tot_rows = 0;
189 
190 	g_free (file_store->priv->rows);
191 	file_store->priv->rows = NULL;
192 	file_store->priv->num_rows = 0;
193 
194 	file_store->priv->size = 0;
195 
196 	_gth_file_store_clear_queue (file_store);
197 }
198 
199 
200 #ifdef DEBUG_FILE_STORE
201 
202 static void
_print_rows(GthFileRow ** rows,int n,char * info)203 _print_rows (GthFileRow **rows, int n, char *info)
204 {
205 	int i;
206 
207 	g_print ("%s\n", info);
208 	for (i = 0; i < n; i++) {
209 		GthFileRow *row = rows[i];
210 		g_print ("(%d) [%d] %s\n", i, row->pos, g_file_get_uri (row->file_data->file));
211 	}
212 }
213 
214 
215 static void
_gth_file_store_print(GthFileStore * file_store,char * info)216 _gth_file_store_print (GthFileStore *file_store, char *info)
217 {
218 	_print_rows (file_store->priv->rows, file_store->priv->num_rows, info);
219 }
220 
221 #endif
222 
223 
224 static void
gth_file_store_finalize(GObject * object)225 gth_file_store_finalize (GObject *object)
226 {
227 	GthFileStore *file_store;
228 
229 	file_store = GTH_FILE_STORE (object);
230 
231 	_gth_file_store_free_rows (file_store);
232 	_g_object_unref (file_store->priv->filter);
233 
234 	G_OBJECT_CLASS (gth_file_store_parent_class)->finalize (object);
235 }
236 
237 
238 static void
gth_file_store_class_init(GthFileStoreClass * klass)239 gth_file_store_class_init (GthFileStoreClass *klass)
240 {
241 	GObjectClass *object_class;
242 
243 	object_class = (GObjectClass*) klass;
244 	object_class->finalize = gth_file_store_finalize;
245 
246 	gth_file_store_signals[VISIBILITY_CHANGED] =
247 		g_signal_new ("visibility_changed",
248 			      G_TYPE_FROM_CLASS (klass),
249 			      G_SIGNAL_RUN_LAST,
250 			      G_STRUCT_OFFSET (GthFileStoreClass, visibility_changed),
251 			      NULL, NULL,
252 			      g_cclosure_marshal_VOID__VOID,
253 			      G_TYPE_NONE,
254 			      0);
255 	gth_file_store_signals[THUMBNAIL_CHANGED] =
256 			g_signal_new ("thumbnail-changed",
257 				      G_TYPE_FROM_CLASS (klass),
258 				      G_SIGNAL_RUN_LAST,
259 				      G_STRUCT_OFFSET (GthFileStoreClass, thumbnail_changed),
260 				      NULL, NULL,
261 				      gth_marshal_VOID__BOXED_BOXED,
262 				      G_TYPE_NONE,
263 				      2,
264 				      GTK_TYPE_TREE_PATH,
265 				      GTK_TYPE_TREE_ITER);
266 }
267 
268 
269 static void
gth_file_store_init(GthFileStore * file_store)270 gth_file_store_init (GthFileStore *file_store)
271 {
272 	file_store->priv = gth_file_store_get_instance_private (file_store);
273 	file_store->priv->all_rows = NULL;
274 	file_store->priv->rows = NULL;
275 	file_store->priv->size = 0;
276 	file_store->priv->tot_rows = 0;
277 	file_store->priv->num_rows = 0;
278 	file_store->priv->stamp = g_random_int ();
279 	file_store->priv->load_thumbs = FALSE;
280 	file_store->priv->filter = gth_test_new ();
281 	file_store->priv->queue = NULL;
282 	file_store->priv->cmp_func = NULL;
283 	file_store->priv->inverse_sort = FALSE;
284 	file_store->priv->update_filter = FALSE;
285 
286 	if (column_type[0] == G_TYPE_INVALID) {
287 		column_type[GTH_FILE_STORE_FILE_DATA_COLUMN] = GTH_TYPE_FILE_DATA;
288 		column_type[GTH_FILE_STORE_THUMBNAIL_COLUMN] = GTH_TYPE_CAIRO_SURFACE;
289 		column_type[GTH_FILE_STORE_IS_ICON_COLUMN] = G_TYPE_BOOLEAN;
290 		column_type[GTH_FILE_STORE_EMBLEMS_COLUMN] = GTH_TYPE_STRING_LIST;
291 	}
292 }
293 
294 
295 static GtkTreeModelFlags
gth_file_store_get_flags(GtkTreeModel * tree_model)296 gth_file_store_get_flags (GtkTreeModel *tree_model)
297 {
298 	return GTK_TREE_MODEL_LIST_ONLY;
299 }
300 
301 
302 static gint
gth_file_store_get_n_columns(GtkTreeModel * tree_model)303 gth_file_store_get_n_columns (GtkTreeModel *tree_model)
304 {
305 	return GTH_FILE_STORE_N_COLUMNS;
306 }
307 
308 
309 static GType
gth_file_store_get_column_type(GtkTreeModel * tree_model,int index)310 gth_file_store_get_column_type (GtkTreeModel *tree_model,
311 				int           index)
312 {
313 	g_return_val_if_fail ((index >= 0) && (index < GTH_FILE_STORE_N_COLUMNS), G_TYPE_INVALID);
314 
315 	return column_type[index];
316 }
317 
318 
319 static gboolean
gth_file_store_get_iter(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreePath * path)320 gth_file_store_get_iter (GtkTreeModel *tree_model,
321 			 GtkTreeIter  *iter,
322 			 GtkTreePath  *path)
323 {
324 	GthFileStore *file_store;
325 	GthFileRow   *row;
326 	int          *indices, n;
327 
328 	g_return_val_if_fail (path != NULL, FALSE);
329 
330 	file_store = (GthFileStore *) tree_model;
331 
332 	indices = gtk_tree_path_get_indices (path);
333 	n = indices[0];
334 	if ((n < 0) || (n >= file_store->priv->num_rows))
335 		return FALSE;
336 
337 	row = file_store->priv->rows[n];
338 	g_return_val_if_fail (row != NULL, FALSE);
339 	g_return_val_if_fail (row->pos == n, FALSE);
340 
341 	iter->stamp = file_store->priv->stamp;
342 	iter->user_data = row;
343 
344 	return TRUE;
345 }
346 
347 
348 static GtkTreePath *
gth_file_store_get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)349 gth_file_store_get_path (GtkTreeModel *tree_model,
350 			 GtkTreeIter  *iter)
351 {
352 	GthFileStore *file_store;
353 	GthFileRow   *row;
354 	GtkTreePath  *path;
355 
356 	g_return_val_if_fail (iter != NULL, NULL);
357 	g_return_val_if_fail (iter->user_data != NULL, NULL);
358 
359 	file_store = (GthFileStore *) tree_model;
360 
361 	g_return_val_if_fail (VALID_ITER (iter, file_store), NULL);
362 
363 	row = (GthFileRow*) iter->user_data;
364 
365 	path = gtk_tree_path_new ();
366 	gtk_tree_path_append_index (path, row->pos);
367 
368 	return path;
369 }
370 
371 
372 static void
gth_file_store_get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,int column,GValue * value)373 gth_file_store_get_value (GtkTreeModel *tree_model,
374 			  GtkTreeIter  *iter,
375 			  int           column,
376 			  GValue       *value)
377 {
378 	GthFileStore *file_store;
379 	GthFileRow   *row;
380 
381 	g_return_if_fail ((column >= 0) && (column < GTH_FILE_STORE_N_COLUMNS));
382 
383 	file_store = (GthFileStore *) tree_model;
384 
385 	g_return_if_fail (VALID_ITER (iter, file_store));
386 
387 	row = (GthFileRow*) iter->user_data;
388 
389 	switch (column) {
390 	case GTH_FILE_STORE_FILE_DATA_COLUMN:
391 		g_value_init (value, GTH_TYPE_FILE_DATA);
392 		g_value_set_object (value, row->file_data);
393 		break;
394 	case GTH_FILE_STORE_THUMBNAIL_COLUMN:
395 		g_value_init (value, GTH_TYPE_CAIRO_SURFACE);
396 		g_value_set_boxed (value, row->thumbnail);
397 		break;
398 	case GTH_FILE_STORE_IS_ICON_COLUMN:
399 		g_value_init (value, G_TYPE_BOOLEAN);
400 		g_value_set_boolean (value, row->is_icon);
401 		break;
402 	case GTH_FILE_STORE_EMBLEMS_COLUMN:
403 		g_value_init (value, G_TYPE_STRING);
404 		g_value_set_object (value, g_file_info_get_attribute_object (row->file_data->info, GTH_FILE_ATTRIBUTE_EMBLEMS));
405 		break;
406 	}
407 }
408 
409 
410 static gboolean
gth_file_store_iter_next(GtkTreeModel * tree_model,GtkTreeIter * iter)411 gth_file_store_iter_next (GtkTreeModel  *tree_model,
412 			  GtkTreeIter   *iter)
413 {
414 	GthFileStore *file_store;
415 	GthFileRow   *row;
416 
417 	if ((iter == NULL) || (iter->user_data == NULL))
418 		return FALSE;
419 
420 	file_store = (GthFileStore *) tree_model;
421 
422 	g_return_val_if_fail (VALID_ITER (iter, file_store), FALSE);
423 
424   	row = (GthFileRow*) iter->user_data;
425 	if ((row->pos + 1) >= file_store->priv->num_rows)
426 		return FALSE;
427 
428 	iter->stamp = file_store->priv->stamp;
429 	iter->user_data = file_store->priv->rows[row->pos + 1];
430 
431 	return TRUE;
432 }
433 
434 
435 static gboolean
gth_file_store_iter_previous(GtkTreeModel * tree_model,GtkTreeIter * iter)436 gth_file_store_iter_previous (GtkTreeModel *tree_model,
437 			      GtkTreeIter  *iter)
438 {
439 	GthFileStore *file_store;
440 	GthFileRow   *row;
441 
442 	if ((iter == NULL) || (iter->user_data == NULL))
443 		return FALSE;
444 
445 	file_store = (GthFileStore *) tree_model;
446 
447 	g_return_val_if_fail (VALID_ITER (iter, file_store), FALSE);
448 
449 	row = (GthFileRow*) iter->user_data;
450 	if (row->pos == 0)
451 		return FALSE;
452 
453 	iter->stamp = file_store->priv->stamp;
454 	iter->user_data = file_store->priv->rows[row->pos - 1];
455 
456 	return TRUE;
457 }
458 
459 
460 static gboolean
gth_file_store_iter_children(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent)461 gth_file_store_iter_children (GtkTreeModel *tree_model,
462 			      GtkTreeIter  *iter,
463 			      GtkTreeIter  *parent)
464 {
465 	GthFileStore *file_store;
466 
467 	if (parent != NULL)
468 		return FALSE;
469 
470 	g_return_val_if_fail (parent->user_data != NULL, FALSE);
471 
472 	file_store = (GthFileStore *) tree_model;
473 
474 	if (file_store->priv->num_rows == 0)
475 		return FALSE;
476 
477 	iter->stamp = file_store->priv->stamp;
478 	iter->user_data = file_store->priv->rows[0];
479 
480 	return TRUE;
481 }
482 
483 
484 static gboolean
gth_file_store_iter_has_child(GtkTreeModel * tree_model,GtkTreeIter * iter)485 gth_file_store_iter_has_child (GtkTreeModel *tree_model,
486 			       GtkTreeIter  *iter)
487 {
488 	return FALSE;
489 }
490 
491 
492 static gint
gth_file_store_iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)493 gth_file_store_iter_n_children (GtkTreeModel *tree_model,
494 				GtkTreeIter  *iter)
495 {
496 	GthFileStore *file_store;
497 
498 	file_store = (GthFileStore *) tree_model;
499 
500 	if (iter == NULL)
501 		return file_store->priv->num_rows;
502 
503 	g_return_val_if_fail (VALID_ITER (iter, file_store), 0);
504 
505   	return 0;
506 }
507 
508 
509 static gboolean
gth_file_store_iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,int n)510 gth_file_store_iter_nth_child (GtkTreeModel *tree_model,
511 			       GtkTreeIter  *iter,
512 			       GtkTreeIter  *parent,
513 			       int           n)
514 {
515 	GthFileStore *file_store;
516 
517   	file_store = (GthFileStore *) tree_model;
518 
519 	if (parent != NULL) {
520 		g_return_val_if_fail (VALID_ITER (parent, file_store), FALSE);
521 		return FALSE;
522 	}
523 
524 	if (n >= file_store->priv->num_rows)
525 		return FALSE;
526 
527 	iter->stamp = file_store->priv->stamp;
528 	iter->user_data = file_store->priv->rows[n];
529 
530 	return TRUE;
531 }
532 
533 
534 static gboolean
gth_file_store_iter_parent(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * child)535 gth_file_store_iter_parent (GtkTreeModel *tree_model,
536 			    GtkTreeIter  *iter,
537 			    GtkTreeIter  *child)
538 {
539 	GthFileStore *file_store;
540 
541 	g_return_val_if_fail (child == NULL, FALSE);
542 
543 	file_store = (GthFileStore *) tree_model;
544 
545 	g_return_val_if_fail (VALID_ITER (child, file_store), FALSE);
546 
547 	return FALSE;
548 }
549 
550 
551 static gboolean
gth_file_store_row_draggable(GtkTreeDragSource * drag_source,GtkTreePath * path)552 gth_file_store_row_draggable (GtkTreeDragSource *drag_source,
553                               GtkTreePath       *path)
554 {
555 	return TRUE;
556 }
557 
558 
559 static gboolean
gth_file_store_drag_data_get(GtkTreeDragSource * drag_source,GtkTreePath * path,GtkSelectionData * selection_data)560 gth_file_store_drag_data_get (GtkTreeDragSource *drag_source,
561                               GtkTreePath       *path,
562                               GtkSelectionData  *selection_data)
563 {
564 	gboolean      retval = FALSE;
565 	GthFileStore *file_store;
566 	int          *indices, n;
567 	GthFileRow   *row;
568 
569 	g_return_val_if_fail (path != NULL, FALSE);
570 
571 	file_store = (GthFileStore *) drag_source;
572 
573 	indices = gtk_tree_path_get_indices (path);
574 	n = indices[0];
575 	if ((n < 0) || (n >= file_store->priv->num_rows))
576 		return FALSE;
577 
578 	row = file_store->priv->rows[n];
579 	g_return_val_if_fail (row != NULL, FALSE);
580 	g_return_val_if_fail (row->pos == n, FALSE);
581 
582 	if (gtk_selection_data_targets_include_uri (selection_data)) {
583 		char **uris;
584 
585 		uris = g_new (char *, 2);
586 		uris[0] = g_file_get_uri (row->file_data->file);
587 		uris[1] = NULL;
588 		gtk_selection_data_set_uris (selection_data, uris);
589 		retval = TRUE;
590 
591 		g_strfreev (uris);
592 	}
593 	else if (gtk_selection_data_targets_include_text (selection_data)) {
594 		char *parse_name;
595 
596 		parse_name = g_file_get_parse_name (row->file_data->file);
597 		gtk_selection_data_set_text (selection_data, parse_name, -1);
598 		retval = TRUE;
599 
600 		g_free (parse_name);
601 	}
602 
603 	return retval;
604 }
605 
606 static gboolean
gth_file_store_drag_data_delete(GtkTreeDragSource * drag_source,GtkTreePath * path)607 gth_file_store_drag_data_delete (GtkTreeDragSource *drag_source,
608                                  GtkTreePath       *path)
609 {
610 	GthFileStore *file_store;
611 	GtkTreeIter   iter;
612 
613 	g_return_val_if_fail (path != NULL, FALSE);
614 
615 	file_store = (GthFileStore *) drag_source;
616 	if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (file_store), &iter, path))
617 		return FALSE;
618 	gth_file_store_remove (file_store, &iter);
619 
620 	return TRUE;
621 }
622 
623 
624 static void
gtk_tree_model_interface_init(GtkTreeModelIface * iface)625 gtk_tree_model_interface_init (GtkTreeModelIface *iface)
626 {
627 	iface->get_flags       = gth_file_store_get_flags;
628 	iface->get_n_columns   = gth_file_store_get_n_columns;
629 	iface->get_column_type = gth_file_store_get_column_type;
630 	iface->get_iter        = gth_file_store_get_iter;
631 	iface->get_path        = gth_file_store_get_path;
632 	iface->get_value       = gth_file_store_get_value;
633 	iface->iter_next       = gth_file_store_iter_next;
634 	iface->iter_previous   = gth_file_store_iter_previous;
635 	iface->iter_children   = gth_file_store_iter_children;
636 	iface->iter_has_child  = gth_file_store_iter_has_child;
637 	iface->iter_n_children = gth_file_store_iter_n_children;
638 	iface->iter_nth_child  = gth_file_store_iter_nth_child;
639 	iface->iter_parent     = gth_file_store_iter_parent;
640 }
641 
642 
643 static void
gtk_tree_drag_source_interface_init(GtkTreeDragSourceIface * iface)644 gtk_tree_drag_source_interface_init (GtkTreeDragSourceIface *iface)
645 {
646 	iface->row_draggable = gth_file_store_row_draggable;
647 	iface->drag_data_get = gth_file_store_drag_data_get;
648 	iface->drag_data_delete = gth_file_store_drag_data_delete;
649 }
650 
651 
652 GthFileStore *
gth_file_store_new(void)653 gth_file_store_new (void)
654 {
655 	return (GthFileStore*) g_object_new (GTH_TYPE_FILE_STORE, NULL);
656 }
657 
658 
659 static void
_gth_file_store_increment_stamp(GthFileStore * file_store)660 _gth_file_store_increment_stamp (GthFileStore *file_store)
661 {
662 	do {
663 		file_store->priv->stamp++;
664 	}
665 	while (file_store->priv->stamp == 0);
666 }
667 
668 
669 G_GNUC_UNUSED
670 static GList *
_gth_file_store_get_files(GthFileStore * file_store)671 _gth_file_store_get_files (GthFileStore *file_store)
672 {
673 	GList *files = NULL;
674 	int    i;
675 
676 	for (i = 0; i < file_store->priv->tot_rows; i++) {
677 		GthFileRow *row = file_store->priv->all_rows[i];
678 		files = g_list_prepend (files, g_object_ref (row->file_data));
679 	}
680 
681 	return  g_list_reverse (files);
682 }
683 
684 
685 static int
compare_row_func(gconstpointer a,gconstpointer b,gpointer user_data)686 compare_row_func (gconstpointer a,
687 		  gconstpointer b,
688 		  gpointer      user_data)
689 {
690 	GthFileStore *file_store = user_data;
691 	GthFileRow   *row_a = *((GthFileRow **) a);
692 	GthFileRow   *row_b = *((GthFileRow **) b);
693 	int           result;
694 
695 	if (file_store->priv->cmp_func == NULL)
696 		return -1;
697 
698 	result = file_store->priv->cmp_func (row_a->file_data, row_b->file_data);
699 	if (file_store->priv->inverse_sort)
700 		result = result * -1;
701 
702 	return result;
703 }
704 
705 
706 static int
compare_by_pos(gconstpointer a,gconstpointer b,gpointer user_data)707 compare_by_pos (gconstpointer a,
708 		gconstpointer b,
709 		gpointer      user_data)
710 {
711 	GthFileRow *row_a = *((GthFileRow **) a);
712 	GthFileRow *row_b = *((GthFileRow **) b);
713 
714 	if (row_a->pos == row_b->pos)
715 		return 0;
716 	else if (row_a->pos > row_b->pos)
717 		return 1;
718 	else
719 		return -1;
720 }
721 
722 
723 static void
_gth_file_store_sort(GthFileStore * file_store,gconstpointer pbase,gint total_elems)724 _gth_file_store_sort (GthFileStore *file_store,
725 		      gconstpointer pbase,
726 		      gint          total_elems)
727 {
728 	g_qsort_with_data (pbase,
729 			   total_elems,
730 			   (gsize) sizeof (GthFileRow *),
731 			   (file_store->priv->cmp_func != NULL) ? compare_row_func : compare_by_pos,
732 			   file_store);
733 }
734 
735 
736 static void
_gth_file_store_compact_rows(GthFileStore * file_store)737 _gth_file_store_compact_rows (GthFileStore *file_store)
738 {
739 	int i, j;
740 
741 	for (i = 0, j = 0; i < file_store->priv->tot_rows; i++)
742 		if (file_store->priv->all_rows[i] != NULL) {
743 			file_store->priv->all_rows[j] = file_store->priv->all_rows[i];
744 			file_store->priv->all_rows[j]->abs_pos = j;
745 			j++;
746 		}
747 	file_store->priv->tot_rows = j;
748 }
749 
750 
751 static void
_gth_file_store_hide_row(GthFileStore * file_store,GthFileRow * row)752 _gth_file_store_hide_row (GthFileStore *file_store,
753 			  GthFileRow   *row)
754 {
755 	int          k;
756 	GtkTreePath *path;
757 
758 	file_store->priv->rows[row->pos] = NULL;
759 	for (k = row->pos; k < file_store->priv->num_rows - 1;  k++) {
760 		file_store->priv->rows[k] = file_store->priv->rows[k + 1];
761 		file_store->priv->rows[k]->pos--;
762 	}
763 	file_store->priv->num_rows--;
764 
765 	path = gtk_tree_path_new_from_indices (row->pos, -1);
766 	gtk_tree_model_row_deleted (GTK_TREE_MODEL (file_store), path);
767 	gtk_tree_path_free (path);
768 }
769 
770 
771 static void
_gth_file_store_update_visibility(GthFileStore * file_store,GList * add_queue,int position)772 _gth_file_store_update_visibility (GthFileStore *file_store,
773 				   GList        *add_queue,
774 				   int           position)
775 {
776 	GthFileRow **all_rows = NULL;
777 	guint        all_rows_n = 0;
778 	int          add_queue_size;
779 	GthFileRow **old_rows = NULL;
780 	guint        old_rows_n = 0;
781 	GthFileRow **new_rows = NULL;
782 	guint        new_rows_n = 0;
783 	int          i;
784 	GList       *files;
785 	GHashTable  *files_index;
786 	GList       *scan;
787 	GthFileData *file;
788 	int          j, k;
789 	gboolean     row_deleted;
790 	GHashTable  *new_rows_index;
791 
792 #ifdef DEBUG_FILE_STORE
793 g_print ("UPDATE VISIBILITY\n");
794 #endif
795 
796 	/* store the current state */
797 
798 	add_queue_size = g_list_length (add_queue);
799 	all_rows_n = file_store->priv->tot_rows + add_queue_size;
800 	all_rows = g_new (GthFileRow *, all_rows_n);
801 
802 	/* append to the end if position is -1 */
803 
804 	if (position == -1)
805 		position = file_store->priv->tot_rows;
806 
807 	/* insert the new rows at position */
808 
809 	j = 0;
810 	for (i = 0; i < file_store->priv->tot_rows; i++) {
811 		all_rows[j] = _gth_file_row_copy (file_store->priv->all_rows[i]);
812 		if (all_rows[j]->visible && (all_rows[j]->pos >= position))
813 			all_rows[j]->pos += add_queue_size;
814 		j++;
815 	}
816 
817 	for (scan = add_queue; scan; scan = scan->next) {
818 		all_rows[j] = _gth_file_row_ref ((GthFileRow *) scan->data);
819 		all_rows[j]->pos = position++;
820 		j++;
821 	}
822 
823 	/* old_rows is equal to file_store->priv->rows but points to
824 	 * the rows of all_rows */
825 
826 	old_rows_n = file_store->priv->num_rows;
827 	old_rows = g_new (GthFileRow *, old_rows_n);
828 	for (i = 0, j = 0; i < all_rows_n; i++) {
829 		if (all_rows[i]->visible) {
830 			old_rows[j] = all_rows[i];
831 			old_rows[j]->visible = FALSE;
832 			j++;
833 		}
834 	}
835 
836 	/* make sure old_rows preserve the order of the visible rows, sorting by
837 	 * position (compare_by_pos) */
838 
839 	g_qsort_with_data (old_rows,
840 			   old_rows_n,
841 			   (gsize) sizeof (GthFileRow *),
842 			   compare_by_pos,
843 			   file_store);
844 
845 	/* sort */
846 
847 	g_qsort_with_data (all_rows,
848 			   all_rows_n,
849 			   (gsize) sizeof (GthFileRow *),
850 			   (file_store->priv->cmp_func != NULL) ? compare_row_func : compare_by_pos,
851 			   file_store);
852 
853 	/* filter */
854 
855 	/* store the new_rows file positions in an hash table for
856 	 * faster searching. */
857 	files_index = g_hash_table_new (g_direct_hash, g_direct_equal);
858 	files = NULL;
859 	for (i = 0; i < all_rows_n; i++) {
860 		GthFileRow *row = all_rows[i];
861 
862 		row->abs_pos = i;
863 		/* store i + 1 instead of i to distinguish when a file is
864 		 * present and when the file is in the first position.
865 		 * (g_hash_table_lookup returns NULL if the file is not present) */
866 		g_hash_table_insert (files_index, row->file_data, GINT_TO_POINTER (i + 1));
867 		files = g_list_prepend (files, g_object_ref (row->file_data));
868 	}
869 	files = g_list_reverse (files);
870 
871 	new_rows_n = 0;
872 	gth_test_set_file_list (file_store->priv->filter, files);
873 	while ((file = gth_test_get_next (file_store->priv->filter)) != NULL) {
874 		i = GPOINTER_TO_INT (g_hash_table_lookup (files_index, file));
875 		g_assert (i != 0);
876 		i--; /* in files_index (i + 1) was stored, see above. */
877 
878 		all_rows[i]->visible = TRUE;
879 		new_rows_n++;
880 	}
881 
882 	_g_object_list_unref (files);
883 	g_hash_table_unref (files_index);
884 
885 	/* create the new visible rows array */
886 
887 	new_rows = g_new (GthFileRow *, new_rows_n);
888 	for (i = 0, j = 0; i < all_rows_n; i++) {
889 		GthFileRow *row = all_rows[i];
890 
891 		if (! row->visible)
892 			continue;
893 
894 		row->pos = j++;
895 		new_rows[row->pos] = row;
896 	}
897 
898 	/* emit the signals required to go from the old state to the new state */
899 
900 	/* hide filtered out files */
901 
902 	/* store the new_rows file positions in an hash table for
903 	 * faster searching. */
904 	new_rows_index = g_hash_table_new (g_direct_hash, g_direct_equal);
905 	for (j = 0; j < new_rows_n; j++)
906 		g_hash_table_insert (new_rows_index, new_rows[j]->file_data, GINT_TO_POINTER (j + 1));
907 
908 	row_deleted = FALSE;
909 	for (i = old_rows_n - 1; i >= 0; i--) {
910 		/* search old_rows[i] in new_rows */
911 
912 		j = GPOINTER_TO_INT (g_hash_table_lookup (new_rows_index, old_rows[i]->file_data));
913 		if (j > 0)
914 			continue;
915 
916 		/* old_rows[i] is not present in new_rows, emit a deleted
917 		 * signal. */
918 
919 #ifdef DEBUG_FILE_STORE
920 g_print ("  DELETE: %d\n", old_rows[i]->pos);
921 #endif
922 
923 		_gth_file_store_hide_row (file_store, old_rows[i]);
924 
925 		old_rows[i] = NULL;
926 		row_deleted = TRUE;
927 	}
928 
929 	g_hash_table_unref (new_rows_index);
930 
931 	/* compact the old visible rows array if needed */
932 
933 	if (row_deleted) {
934 		for (i = 0, j = 0; i < old_rows_n; i++) {
935 			if (old_rows[i] != NULL) {
936 				old_rows[j] = old_rows[i];
937 				/*old_rows[j]->abs_pos = j; FIXME: check if this is correct */
938 				j++;
939 			}
940 		}
941 		old_rows_n = j;
942 	}
943 
944 	/* Both old_rows and file_store->priv->rows now contain the files
945 	 * visible before and after the filtering.
946 	 * Reorder the two arrays according to the new_rows order */
947 
948 	if (old_rows_n > 0) {
949 		GHashTable *old_rows_index;
950 		gboolean    order_changed;
951 		int        *new_order;
952 		GthFileRow *tmp_row;
953 
954 		/* store the old_rows file positions in an hash table for
955 		 * faster searching. */
956 		old_rows_index = g_hash_table_new (g_direct_hash, g_direct_equal);
957 		for (j = 0; j < old_rows_n; j++)
958 			g_hash_table_insert (old_rows_index, old_rows[j]->file_data, GINT_TO_POINTER (j + 1));
959 
960 		order_changed = FALSE;
961 		new_order = g_new0 (int, old_rows_n);
962 		for (i = 0, k = 0; i < new_rows_n; i++) {
963 			/* search new_rows[i] in old_rows */
964 
965 			j = GPOINTER_TO_INT (g_hash_table_lookup (old_rows_index, new_rows[i]->file_data));
966 			if (j == 0)
967 				continue;
968 			j--; /* in old_rows_index we stored j+1, decrement to get the real position */
969 
970 			/* old_rows[j] == new_rows[i] */
971 
972 			/* k is the new position of old_rows[j] in new_rows
973 			 * without considering the new elements, that is
974 			 * the elements in new_rows not present in old_rows */
975 
976 			new_order[k] = j;
977 			old_rows[j]->pos = k;
978 
979 			/* swap the position of j and k in file_store->priv->rows
980 			 * old_rows can't be reordered now because we need
981 			 * to know the old positions, it will be ordered
982 			 * later with g_qsort_with_data */
983 
984 			if (k != j) {
985 				tmp_row = file_store->priv->rows[j];
986 				file_store->priv->rows[j] = file_store->priv->rows[k];
987 				file_store->priv->rows[j]->pos = j;
988 				file_store->priv->rows[k] = tmp_row;
989 				file_store->priv->rows[k]->pos = k;
990 
991 				order_changed = TRUE;
992 			}
993 
994 			k++;
995 		}
996 		if (order_changed) {
997 
998 #ifdef DEBUG_FILE_STORE
999 g_print ("  REORDER: ");
1000 for (i = 0; i < old_rows_n; i++)
1001 	g_print ("%d ", new_order[i]);
1002 g_print ("\n");
1003 #endif
1004 
1005 			gtk_tree_model_rows_reordered (GTK_TREE_MODEL (file_store),
1006 						       NULL,
1007 						       NULL,
1008 						       new_order);
1009 		}
1010 
1011 		g_free (new_order);
1012 		g_hash_table_unref (old_rows_index);
1013 	}
1014 
1015 	/* set the new state */
1016 
1017 	_gth_file_store_increment_stamp (file_store);
1018 
1019 	for (i = 0; i < file_store->priv->tot_rows; i++)
1020 		_gth_file_row_unref (file_store->priv->all_rows[i]);
1021 	g_free (file_store->priv->all_rows);
1022 	file_store->priv->all_rows = all_rows;
1023 	file_store->priv->tot_rows = all_rows_n;
1024 
1025 	g_qsort_with_data (old_rows,
1026 			   old_rows_n,
1027 			   (gsize) sizeof (GthFileRow *),
1028 			   compare_by_pos,
1029 			   NULL);
1030 
1031 	g_free (file_store->priv->rows);
1032 	file_store->priv->rows = g_new (GthFileRow *, new_rows_n);
1033 	for (i = 0; i < old_rows_n; i++)
1034 		file_store->priv->rows[i] = old_rows[i];
1035 
1036 	g_assert (file_store->priv->num_rows == old_rows_n);
1037 
1038 	/* add the new files */
1039 
1040 	for (i = 0; i < new_rows_n; i++) {
1041 		GtkTreePath *path;
1042 		GtkTreeIter  iter;
1043 
1044 		if ((i < file_store->priv->num_rows) && (new_rows[i]->file_data == file_store->priv->rows[i]->file_data))
1045 			continue;
1046 
1047 #ifdef DEBUG_FILE_STORE
1048 g_print ("  INSERT: %d\n", i);
1049 #endif
1050 
1051 		/* add the file to the visible rows */
1052 
1053 		for (k = file_store->priv->num_rows; k > i; k--) {
1054 			file_store->priv->rows[k] = file_store->priv->rows[k - 1];
1055 			file_store->priv->rows[k]->pos++;
1056 		}
1057 		file_store->priv->rows[i] = new_rows[i];
1058 		file_store->priv->rows[i]->pos = i;
1059 		file_store->priv->num_rows++;
1060 
1061 		path = gtk_tree_path_new ();
1062 		gtk_tree_path_append_index (path, i);
1063 		gth_file_store_get_iter (GTK_TREE_MODEL (file_store), &iter, path);
1064 		gtk_tree_model_row_inserted (GTK_TREE_MODEL (file_store), path, &iter);
1065 		gtk_tree_path_free (path);
1066 	}
1067 
1068 	g_signal_emit (file_store, gth_file_store_signals[VISIBILITY_CHANGED], 0);
1069 
1070 	g_free (new_rows);
1071 	g_free (old_rows);
1072 }
1073 
1074 
1075 void
gth_file_store_set_filter(GthFileStore * file_store,GthTest * filter)1076 gth_file_store_set_filter (GthFileStore *file_store,
1077 			   GthTest      *filter)
1078 {
1079 	if (file_store->priv->filter != NULL) {
1080 		g_object_unref (file_store->priv->filter);
1081 		file_store->priv->filter = NULL;
1082 	}
1083 
1084 	if (filter != NULL)
1085 		file_store->priv->filter = g_object_ref (filter);
1086 	else
1087 		file_store->priv->filter = gth_test_new ();
1088 
1089 	_gth_file_store_update_visibility (file_store, NULL, -1);
1090 }
1091 
1092 
1093 static void
_gth_file_store_reorder(GthFileStore * file_store)1094 _gth_file_store_reorder (GthFileStore *file_store)
1095 {
1096 	int *new_order;
1097 	int  i, j;
1098 
1099 	_gth_file_store_sort (file_store, file_store->priv->all_rows, file_store->priv->tot_rows);
1100 
1101 	/* compute the new position for each row */
1102 
1103 	new_order = g_new (int, file_store->priv->num_rows);
1104 	for (i = 0, j = -1; i < file_store->priv->tot_rows; i++) {
1105 		GthFileRow *row = file_store->priv->all_rows[i];
1106 		if (row->visible) {
1107 			j = j + 1;
1108 			file_store->priv->rows[j] = row;
1109 			new_order[j] = row->pos;
1110 			row->pos = j;
1111 		}
1112 		row->abs_pos = i;
1113 	}
1114 	if (new_order != NULL) {
1115 		gtk_tree_model_rows_reordered (GTK_TREE_MODEL (file_store),
1116 					       NULL,
1117 					       NULL,
1118 					       new_order);
1119 	}
1120 	g_free (new_order);
1121 }
1122 
1123 
1124 static void
_gth_file_store_set_sort_func(GthFileStore * file_store,GthFileDataCompFunc cmp_func,gboolean inverse_sort)1125 _gth_file_store_set_sort_func (GthFileStore        *file_store,
1126 			       GthFileDataCompFunc  cmp_func,
1127 			       gboolean             inverse_sort)
1128 {
1129 	file_store->priv->cmp_func = cmp_func;
1130 	file_store->priv->inverse_sort = inverse_sort;
1131 }
1132 
1133 void
gth_file_store_set_sort_func(GthFileStore * file_store,GthFileDataCompFunc cmp_func,gboolean inverse_sort)1134 gth_file_store_set_sort_func (GthFileStore        *file_store,
1135 			      GthFileDataCompFunc  cmp_func,
1136 			      gboolean             inverse_sort)
1137 {
1138 	_gth_file_store_set_sort_func (file_store, cmp_func, inverse_sort);
1139 	_gth_file_store_reorder (file_store);
1140 }
1141 
1142 
1143 GList *
gth_file_store_get_all(GthFileStore * file_store)1144 gth_file_store_get_all (GthFileStore *file_store)
1145 {
1146 	GList *list = NULL;
1147 	int    i;
1148 
1149 	for (i = 0; i < file_store->priv->tot_rows; i++)
1150 		list = g_list_prepend (list, g_object_ref (file_store->priv->all_rows[i]->file_data));
1151 
1152 	return g_list_reverse (list);
1153 }
1154 
1155 
1156 int
gth_file_store_n_files(GthFileStore * file_store)1157 gth_file_store_n_files (GthFileStore *file_store)
1158 {
1159 	return file_store->priv->tot_rows;
1160 }
1161 
1162 
1163 GList *
gth_file_store_get_visibles(GthFileStore * file_store)1164 gth_file_store_get_visibles (GthFileStore *file_store)
1165 {
1166 	GList *list = NULL;
1167 	int    i;
1168 
1169 	for (i = 0; i < file_store->priv->num_rows; i++)
1170 		list = g_list_prepend (list, g_object_ref (file_store->priv->rows[i]->file_data));
1171 
1172 	return g_list_reverse (list);
1173 }
1174 
1175 
1176 int
gth_file_store_n_visibles(GthFileStore * file_store)1177 gth_file_store_n_visibles (GthFileStore *file_store)
1178 {
1179 	return file_store->priv->num_rows;
1180 }
1181 
1182 
1183 GthFileData *
gth_file_store_get_file(GthFileStore * file_store,GtkTreeIter * iter)1184 gth_file_store_get_file (GthFileStore *file_store,
1185 			 GtkTreeIter  *iter)
1186 {
1187 	g_return_val_if_fail (VALID_ITER (iter, file_store), NULL);
1188 
1189 	return ((GthFileRow *) iter->user_data)->file_data;
1190 }
1191 
1192 
1193 gboolean
gth_file_store_find(GthFileStore * file_store,GFile * file,GtkTreeIter * iter)1194 gth_file_store_find (GthFileStore *file_store,
1195 		     GFile        *file,
1196 		     GtkTreeIter  *iter)
1197 {
1198 	gboolean found = FALSE;
1199 	int      i;
1200 
1201 	for (i = 0; i < file_store->priv->tot_rows; i++) {
1202 		GthFileRow *row = file_store->priv->all_rows[i];
1203 
1204 		if (row == NULL)
1205 			continue;
1206 
1207 		if (g_file_equal (row->file_data->file, file)) {
1208 			if (iter != NULL) {
1209 				iter->stamp = file_store->priv->stamp;
1210 				iter->user_data = row;
1211 			}
1212 			found = TRUE;
1213 			break;
1214 		}
1215 	}
1216 
1217 	return found;
1218 }
1219 
1220 
1221 gboolean
gth_file_store_find_visible(GthFileStore * file_store,GFile * file,GtkTreeIter * iter)1222 gth_file_store_find_visible (GthFileStore *file_store,
1223 			     GFile        *file,
1224 			     GtkTreeIter  *iter)
1225 {
1226 	gboolean found = FALSE;
1227 	int      i;
1228 
1229 	for (i = 0; i < file_store->priv->num_rows; i++) {
1230 		GthFileRow *row = file_store->priv->rows[i];
1231 
1232 		if (row == NULL)
1233 			continue;
1234 
1235 		if (g_file_equal (row->file_data->file, file)) {
1236 			if (iter != NULL) {
1237 				iter->stamp = file_store->priv->stamp;
1238 				iter->user_data = row;
1239 			}
1240 			found = TRUE;
1241 			break;
1242 		}
1243 	}
1244 
1245 	return found;
1246 }
1247 
1248 
1249 int
gth_file_store_get_pos(GthFileStore * file_store,GFile * file)1250 gth_file_store_get_pos (GthFileStore *file_store,
1251 			GFile        *file)
1252 {
1253 	GtkTreeIter iter;
1254 	GthFileRow *row;
1255 
1256 	if (! gth_file_store_find_visible (file_store, file, &iter))
1257 		return -1;
1258 
1259 	row = iter.user_data;
1260 
1261 	return row->pos;
1262 }
1263 
1264 
1265 gboolean
gth_file_store_get_nth(GthFileStore * file_store,int n,GtkTreeIter * iter)1266 gth_file_store_get_nth (GthFileStore *file_store,
1267 		        int           n,
1268 		        GtkTreeIter  *iter)
1269 {
1270 	GthFileRow *row;
1271 
1272 	if (file_store->priv->tot_rows <= n)
1273 		return FALSE;
1274 
1275 	row = file_store->priv->all_rows[n];
1276 	g_return_val_if_fail (row != NULL, FALSE);
1277 
1278 	if (iter != NULL) {
1279 		iter->stamp = file_store->priv->stamp;
1280 		iter->user_data = row;
1281 	}
1282 
1283 	return TRUE;
1284 }
1285 
1286 
1287 gboolean
gth_file_store_get_next(GthFileStore * file_store,GtkTreeIter * iter)1288 gth_file_store_get_next (GthFileStore *file_store,
1289 			 GtkTreeIter  *iter)
1290 {
1291 	GthFileRow *row;
1292 
1293 	if ((iter == NULL) || (iter->user_data == NULL))
1294 		return FALSE;
1295 
1296 	g_return_val_if_fail (VALID_ITER (iter, file_store), FALSE);
1297 
1298   	row = (GthFileRow*) iter->user_data;
1299 	if (row->abs_pos + 1 >= file_store->priv->tot_rows)
1300 		return FALSE;
1301 
1302 	if (iter != NULL) {
1303 		iter->stamp = file_store->priv->stamp;
1304 		iter->user_data = file_store->priv->all_rows[row->abs_pos + 1];
1305 	}
1306 
1307 	return TRUE;
1308 }
1309 
1310 
1311 gboolean
gth_file_store_get_nth_visible(GthFileStore * file_store,int n,GtkTreeIter * iter)1312 gth_file_store_get_nth_visible (GthFileStore *file_store,
1313 				int           n,
1314 				GtkTreeIter  *iter)
1315 {
1316 	GthFileRow *row;
1317 
1318 	if (file_store->priv->num_rows <= n)
1319 		return FALSE;
1320 
1321 	row = file_store->priv->rows[n];
1322 	g_return_val_if_fail (row != NULL, FALSE);
1323 
1324 	if (iter != NULL) {
1325 		iter->stamp = file_store->priv->stamp;
1326 		iter->user_data = row;
1327 	}
1328 
1329 	return TRUE;
1330 }
1331 
1332 
1333 gboolean
gth_file_store_get_next_visible(GthFileStore * file_store,GtkTreeIter * iter)1334 gth_file_store_get_next_visible (GthFileStore *file_store,
1335 				 GtkTreeIter  *iter)
1336 {
1337 	GthFileRow *row;
1338 
1339 	if ((iter == NULL) || (iter->user_data == NULL))
1340 		return FALSE;
1341 
1342 	g_return_val_if_fail (VALID_ITER (iter, file_store), FALSE);
1343 
1344   	row = (GthFileRow*) iter->user_data;
1345 	if (row->pos + 1 >= file_store->priv->num_rows)
1346 		return FALSE;
1347 
1348 	if (iter != NULL) {
1349 		iter->stamp = file_store->priv->stamp;
1350 		iter->user_data = file_store->priv->rows[row->pos + 1];
1351 	}
1352 
1353 	return TRUE;
1354 }
1355 
1356 
1357 gboolean
gth_file_store_get_prev_visible(GthFileStore * file_store,GtkTreeIter * iter)1358 gth_file_store_get_prev_visible (GthFileStore *file_store,
1359 				 GtkTreeIter  *iter)
1360 {
1361 	GthFileRow *row;
1362 
1363 	if ((iter == NULL) || (iter->user_data == NULL))
1364 		return FALSE;
1365 
1366 	g_return_val_if_fail (VALID_ITER (iter, file_store), FALSE);
1367 
1368   	row = (GthFileRow*) iter->user_data;
1369 	if (row->pos == 0)
1370 		return FALSE;
1371 
1372 	if (iter != NULL) {
1373 		iter->stamp = file_store->priv->stamp;
1374 		iter->user_data = file_store->priv->rows[row->pos - 1];
1375 	}
1376 
1377 	return TRUE;
1378 }
1379 
1380 
1381 void
gth_file_store_add(GthFileStore * file_store,GthFileData * file,cairo_surface_t * thumbnail,gboolean is_icon,int position)1382 gth_file_store_add (GthFileStore    *file_store,
1383 		    GthFileData     *file,
1384 		    cairo_surface_t *thumbnail,
1385 		    gboolean         is_icon,
1386 		    int              position)
1387 {
1388 	gth_file_store_queue_add (file_store, file, thumbnail, is_icon);
1389 	gth_file_store_exec_add (file_store, position);
1390 }
1391 
1392 
1393 void
gth_file_store_queue_add(GthFileStore * file_store,GthFileData * file,cairo_surface_t * thumbnail,gboolean is_icon)1394 gth_file_store_queue_add (GthFileStore    *file_store,
1395 			  GthFileData     *file,
1396 			  cairo_surface_t *thumbnail,
1397 			  gboolean         is_icon)
1398 {
1399 	GthFileRow *row;
1400 
1401 	g_return_if_fail (file != NULL);
1402 
1403 	row = _gth_file_row_new ();
1404 	_gth_file_row_set_file (row, file);
1405 	_gth_file_row_set_thumbnail (row, thumbnail);
1406 	row->is_icon = is_icon;
1407 
1408 	file_store->priv->queue = g_list_prepend (file_store->priv->queue, row);
1409 }
1410 
1411 
1412 void
gth_file_store_exec_add(GthFileStore * file_store,int position)1413 gth_file_store_exec_add (GthFileStore *file_store,
1414 			 int           position)
1415 {
1416 	file_store->priv->queue = g_list_reverse (file_store->priv->queue);
1417 	_gth_file_store_update_visibility (file_store, file_store->priv->queue, position);
1418 	_gth_file_store_clear_queue (file_store);
1419 }
1420 
1421 
1422 static void
gth_file_store_queue_set_valist(GthFileStore * file_store,GtkTreeIter * iter,va_list var_args)1423 gth_file_store_queue_set_valist (GthFileStore *file_store,
1424 			         GtkTreeIter  *iter,
1425 			         va_list       var_args)
1426 {
1427 	GthFileRow  *row;
1428 	int          column;
1429 
1430 	g_return_if_fail (VALID_ITER (iter, file_store));
1431 
1432   	row = (GthFileRow*) iter->user_data;
1433 
1434  	column = va_arg (var_args, int);
1435   	while (column != -1) {
1436   		GthFileData     *file_data;
1437   		cairo_surface_t *thumbnail;
1438   		GthStringList   *string_list;
1439 
1440   		switch (column) {
1441   		case GTH_FILE_STORE_FILE_DATA_COLUMN:
1442   			file_data = va_arg (var_args, GthFileData *);
1443   			g_return_if_fail (GTH_IS_FILE_DATA (file_data));
1444   			_gth_file_row_set_file (row, file_data);
1445   			row->changed = TRUE;
1446   			file_store->priv->update_filter = TRUE;
1447   			break;
1448   		case GTH_FILE_STORE_THUMBNAIL_COLUMN:
1449   			thumbnail = va_arg (var_args, cairo_surface_t *);
1450   			_gth_file_row_set_thumbnail (row, thumbnail);
1451   			row->changed = TRUE;
1452   			break;
1453   		case GTH_FILE_STORE_IS_ICON_COLUMN:
1454   			row->is_icon = va_arg (var_args, gboolean);
1455   			row->changed = TRUE;
1456   			break;
1457   		case GTH_FILE_STORE_EMBLEMS_COLUMN:
1458   			string_list = va_arg (var_args, GthStringList *);
1459   			g_file_info_set_attribute_object (row->file_data->info, GTH_FILE_ATTRIBUTE_EMBLEMS, G_OBJECT (string_list));
1460   			row->changed = TRUE;
1461   			break;
1462   		default:
1463   			g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column);
1464   			break;
1465   		}
1466 
1467   		column = va_arg (var_args, int);
1468   	}
1469 }
1470 
1471 
1472 void
gth_file_store_set(GthFileStore * file_store,GtkTreeIter * iter,...)1473 gth_file_store_set (GthFileStore *file_store,
1474 		    GtkTreeIter  *iter,
1475 		    ...)
1476 {
1477 	va_list var_args;
1478 
1479 	va_start (var_args, iter);
1480 	gth_file_store_queue_set_valist (file_store, iter, var_args);
1481 	va_end (var_args);
1482 
1483 	gth_file_store_exec_set (file_store);
1484 }
1485 
1486 
1487 void
gth_file_store_queue_set(GthFileStore * file_store,GtkTreeIter * iter,...)1488 gth_file_store_queue_set (GthFileStore *file_store,
1489 			  GtkTreeIter  *iter,
1490 			  ...)
1491 {
1492 	va_list var_args;
1493 
1494 	va_start (var_args, iter);
1495 	gth_file_store_queue_set_valist (file_store, iter, var_args);
1496 	va_end (var_args);
1497 }
1498 
1499 
1500 static void
_gth_file_store_row_changed(GthFileStore * file_store,GthFileRow * row)1501 _gth_file_store_row_changed (GthFileStore *file_store,
1502 			     GthFileRow   *row)
1503 {
1504 	GtkTreePath *path;
1505 	GtkTreeIter  iter;
1506 
1507 	path = gtk_tree_path_new ();
1508 	gtk_tree_path_append_index (path, row->pos);
1509 
1510 	iter.stamp = file_store->priv->stamp;
1511 	iter.user_data = row;
1512 
1513 	gtk_tree_model_row_changed (GTK_TREE_MODEL (file_store), path, &iter);
1514 
1515 	gtk_tree_path_free (path);
1516 }
1517 
1518 
1519 static void
_gth_file_store_list_changed(GthFileStore * file_store)1520 _gth_file_store_list_changed (GthFileStore *file_store)
1521 {
1522 	int i;
1523 
1524 	for (i = 0; i < file_store->priv->num_rows; i++) {
1525 		GthFileRow *row = file_store->priv->rows[i];
1526 
1527 		if (row->visible && row->changed)
1528 			_gth_file_store_row_changed (file_store, row);
1529 		row->changed = FALSE;
1530 	}
1531 }
1532 
1533 
1534 static void
_gth_file_store_thumbnail_changed(GthFileStore * file_store,GthFileRow * row)1535 _gth_file_store_thumbnail_changed (GthFileStore *file_store,
1536 				   GthFileRow   *row)
1537 {
1538 	GtkTreePath *path;
1539 	GtkTreeIter  iter;
1540 
1541 	path = gtk_tree_path_new ();
1542 	gtk_tree_path_append_index (path, row->pos);
1543 
1544 	iter.stamp = file_store->priv->stamp;
1545 	iter.user_data = row;
1546 
1547 	g_signal_emit (file_store, gth_file_store_signals[THUMBNAIL_CHANGED], 0, path, &iter);
1548 
1549 	gtk_tree_path_free (path);
1550 }
1551 
1552 
1553 static void
_gth_file_store_thumbnails_changed(GthFileStore * file_store)1554 _gth_file_store_thumbnails_changed (GthFileStore *file_store)
1555 {
1556 	int i;
1557 
1558 	for (i = 0; i < file_store->priv->num_rows; i++) {
1559 		GthFileRow *row = file_store->priv->rows[i];
1560 
1561 		if (row->visible && row->changed)
1562 			_gth_file_store_thumbnail_changed (file_store, row);
1563 		row->changed = FALSE;
1564 	}
1565 }
1566 
1567 
1568 void
gth_file_store_exec_set(GthFileStore * file_store)1569 gth_file_store_exec_set (GthFileStore *file_store)
1570 {
1571 	/* This is an speed optimization.  When the thumbnails are updated in
1572 	 * the store the visibility doesn't need to be updated, so we avoid to
1573 	 * emit the 'row-changed' signal for each row, which causes the
1574 	 * GtkIconView to invalidate the size of all the items, and instead
1575 	 * emit a single 'thumbnail-changed' signal that can be used to just
1576 	 * redraw the GthFileView. */
1577 	if (file_store->priv->update_filter)
1578 		_gth_file_store_list_changed (file_store);
1579 	else
1580 		_gth_file_store_thumbnails_changed (file_store);
1581 
1582 	_gth_file_store_clear_queue (file_store);
1583 
1584 	if (file_store->priv->update_filter) {
1585 		_gth_file_store_update_visibility (file_store, NULL, -1);
1586 		file_store->priv->update_filter = FALSE;
1587 	}
1588 }
1589 
1590 
1591 void
gth_file_store_remove(GthFileStore * file_store,GtkTreeIter * iter)1592 gth_file_store_remove (GthFileStore *file_store,
1593 		       GtkTreeIter  *iter)
1594 {
1595 	gth_file_store_queue_remove (file_store, iter);
1596 	gth_file_store_exec_remove (file_store);
1597 }
1598 
1599 
1600 void
gth_file_store_queue_remove(GthFileStore * file_store,GtkTreeIter * iter)1601 gth_file_store_queue_remove (GthFileStore *file_store,
1602 			     GtkTreeIter  *iter)
1603 {
1604 	GthFileRow  *row;
1605 
1606 	g_return_if_fail (VALID_ITER (iter, file_store));
1607 
1608   	row = (GthFileRow*) iter->user_data;
1609 
1610 	file_store->priv->queue = g_list_prepend (file_store->priv->queue, _gth_file_row_ref (file_store->priv->all_rows[row->abs_pos]));
1611 }
1612 
1613 
1614 void
gth_file_store_exec_remove(GthFileStore * file_store)1615 gth_file_store_exec_remove (GthFileStore *file_store)
1616 {
1617 	GList *scan;
1618 
1619 	if (file_store->priv->queue == NULL)
1620 		return;
1621 
1622 	file_store->priv->queue = g_list_reverse (file_store->priv->queue);
1623 	for (scan = file_store->priv->queue; scan; scan = scan->next) {
1624 		GthFileRow *row = scan->data;
1625 
1626 		if (row->visible)
1627 			_gth_file_store_hide_row (file_store, row);
1628 
1629 		file_store->priv->all_rows[row->abs_pos] = NULL;
1630 		_gth_file_row_unref (row);
1631 	}
1632 	_gth_file_store_compact_rows (file_store);
1633 	_gth_file_store_clear_queue (file_store);
1634 
1635 	_gth_file_store_update_visibility (file_store, NULL, -1);
1636 }
1637 
1638 
1639 void
gth_file_store_clear(GthFileStore * file_store)1640 gth_file_store_clear (GthFileStore *file_store)
1641 {
1642 	int i;
1643 
1644 	for (i = file_store->priv->num_rows - 1; i >= 0; i--) {
1645 		GthFileRow  *row = file_store->priv->rows[i];
1646 		GtkTreePath *path;
1647 
1648 		path = gtk_tree_path_new ();
1649 		gtk_tree_path_append_index (path, row->pos);
1650 		gtk_tree_model_row_deleted (GTK_TREE_MODEL (file_store), path);
1651 		gtk_tree_path_free (path);
1652 	}
1653 
1654 	_gth_file_store_free_rows (file_store);
1655 	_gth_file_store_increment_stamp (file_store);
1656 }
1657 
1658 
1659 void
gth_file_store_reorder(GthFileStore * file_store,int * new_order)1660 gth_file_store_reorder (GthFileStore *file_store,
1661 			int          *new_order)
1662 {
1663 	int i;
1664 
1665 	_gth_file_store_set_sort_func (file_store, NULL, FALSE);
1666 
1667 	for (i = 0; i < file_store->priv->num_rows; i++)
1668 		file_store->priv->rows[new_order[i]]->pos = i;
1669 
1670 	g_qsort_with_data (file_store->priv->rows,
1671 			   file_store->priv->num_rows,
1672 			   (gsize) sizeof (GthFileRow *),
1673 			   compare_by_pos,
1674 			   NULL);
1675 
1676 	gtk_tree_model_rows_reordered (GTK_TREE_MODEL (file_store), NULL, NULL, new_order);
1677 }
1678