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