1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2008 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 <string.h>
24 #include <glib/gi18n.h>
25 #include <gtk/gtk.h>
26 #include "glib-utils.h"
27 #include "gth-empty-list.h"
28 #include "gth-file-list.h"
29 #include "gth-file-selection.h"
30 #include "gth-file-store.h"
31 #include "gth-icon-cache.h"
32 #include "gth-preferences.h"
33 #include "gth-thumb-loader.h"
34 #include "gtk-utils.h"
35
36
37 #define FLASH_THUMBNAIL_QUEUE_TIMEOUT 100
38 #define UPDATE_THUMBNAILS_AFTER_SCROLL_TIMEOUT 125
39 #define RESTART_LOADING_THUMBS_DELAY 1500
40 #define N_VIEWAHEAD 50
41 #define N_CREATEAHEAD 50000
42 #define EMPTY (N_("(Empty)"))
43 #define CHECK_JOBS_INTERVAL 50
44 #define _FILE_VIEW "file-view"
45 #define _EMPTY_VIEW "empty-view"
46
47
48 typedef enum {
49 GTH_FILE_LIST_OP_TYPE_SET_FILES,
50 GTH_FILE_LIST_OP_TYPE_CLEAR_FILES,
51 GTH_FILE_LIST_OP_TYPE_ADD_FILES,
52 GTH_FILE_LIST_OP_TYPE_UPDATE_FILES,
53 GTH_FILE_LIST_OP_TYPE_UPDATE_EMBLEMS,
54 GTH_FILE_LIST_OP_TYPE_DELETE_FILES,
55 GTH_FILE_LIST_OP_TYPE_SET_FILTER,
56 GTH_FILE_LIST_OP_TYPE_SET_SORT_FUNC,
57 GTH_FILE_LIST_OP_TYPE_ENABLE_THUMBS,
58 GTH_FILE_LIST_OP_TYPE_RENAME_FILE,
59 GTH_FILE_LIST_OP_TYPE_MAKE_FILE_VISIBLE,
60 GTH_FILE_LIST_OP_TYPE_RESTORE_STATE
61 } GthFileListOpType;
62
63
64 typedef struct {
65 GthFileListOpType type;
66 GtkTreeModel *model;
67 GthTest *filter;
68 GList *file_list; /* GthFileData */
69 GList *files; /* GFile */
70 GthFileDataCompFunc cmp_func;
71 gboolean inverse_sort;
72 char *sval;
73 int ival;
74 GFile *file;
75 GthFileData *file_data;
76 int position;
77 GList *selected;
78 double vscroll;
79 } GthFileListOp;
80
81
82 typedef struct {
83 int ref;
84 guint error : 1; /* Whether an error occurred loading
85 * this file. */
86 guint thumb_loaded : 1; /* Whether we have a thumb of this
87 * image. */
88 guint thumb_created : 1; /* Whether a thumb has been
89 * created for this image. */
90 cairo_surface_t *image;
91 } ThumbData;
92
93
94 typedef struct {
95 GthFileList *file_list;
96 GthThumbLoader *loader;
97 GCancellable *cancellable;
98 GthFileData *file_data;
99 gboolean update_in_view;
100 int pos;
101 gboolean started;
102 } ThumbnailJob;
103
104
105 typedef enum {
106 THUMBNAILER_PHASE_INITIALIZE,
107 THUMBNAILER_PHASE_UPDATE_VISIBLE,
108 THUMBNAILER_PHASE_UPDATE_DOWNWARD,
109 THUMBNAILER_PHASE_UPDATE_UPWARD,
110 THUMBNAILER_PHASE_COMPLETED
111 } ThumbnailerPhase;
112
113
114 typedef struct {
115 ThumbnailerPhase phase;
116 int first_visibile;
117 int last_visible;
118 int current_pos;
119 GList *current_item;
120 } ThumbnailerState;
121
122
123 struct _GthFileListPrivate {
124 GSettings *settings;
125 GthFileListMode type;
126 GtkAdjustment *vadj;
127 GtkWidget *notebook;
128 GtkWidget *view;
129 GtkWidget *message;
130 GtkWidget *scrolled_window;
131 GthIconCache *icon_cache;
132 GthFileSource *file_source;
133 gboolean load_thumbs;
134 int thumb_size;
135 gboolean ignore_hidden_thumbs;
136 GHashTable *thumb_data;
137 GthThumbLoader *thumb_loader;
138 gboolean loading_thumbs;
139 gboolean dirty;
140 guint dirty_event;
141 guint restart_thumb_update;
142 GList *queue; /* list of GthFileListOp */
143 GList *jobs; /* list of ThumbnailJob */
144 gboolean cancelling;
145 guint update_event;
146 gboolean visibility_changed;
147 GList *visibles;
148 ThumbnailerState thumbnailer_state;
149 };
150
151
152 G_DEFINE_TYPE_WITH_CODE (GthFileList,
153 gth_file_list,
154 GTK_TYPE_BOX,
155 G_ADD_PRIVATE (GthFileList))
156
157
158 /* OPs */
159
160
161 static void _gth_file_list_exec_next_op (GthFileList *file_list);
162
163
164 static GthFileListOp *
gth_file_list_op_new(GthFileListOpType op_type)165 gth_file_list_op_new (GthFileListOpType op_type)
166 {
167 GthFileListOp *op;
168
169 op = g_new0 (GthFileListOp, 1);
170 op->type = op_type;
171
172 return op;
173 }
174
175
176 static void
gth_file_list_op_free(GthFileListOp * op)177 gth_file_list_op_free (GthFileListOp *op)
178 {
179 switch (op->type) {
180 case GTH_FILE_LIST_OP_TYPE_SET_FILES:
181 _g_object_list_unref (op->file_list);
182 break;
183 case GTH_FILE_LIST_OP_TYPE_CLEAR_FILES:
184 g_free (op->sval);
185 break;
186 case GTH_FILE_LIST_OP_TYPE_ADD_FILES:
187 case GTH_FILE_LIST_OP_TYPE_UPDATE_FILES:
188 case GTH_FILE_LIST_OP_TYPE_UPDATE_EMBLEMS:
189 _g_object_list_unref (op->file_list);
190 break;
191 case GTH_FILE_LIST_OP_TYPE_DELETE_FILES:
192 _g_object_list_unref (op->files);
193 break;
194 case GTH_FILE_LIST_OP_TYPE_SET_FILTER:
195 g_object_unref (op->filter);
196 break;
197 case GTH_FILE_LIST_OP_TYPE_RENAME_FILE:
198 g_object_unref (op->file);
199 g_object_unref (op->file_data);
200 break;
201 case GTH_FILE_LIST_OP_TYPE_MAKE_FILE_VISIBLE:
202 g_object_unref (op->file);
203 break;
204 case GTH_FILE_LIST_OP_TYPE_RESTORE_STATE:
205 g_list_free_full (op->selected, (GDestroyNotify) gtk_tree_path_free);
206 break;
207 default:
208 break;
209 }
210 g_free (op);
211 }
212
213
214 static void
_gth_file_list_clear_queue(GthFileList * file_list)215 _gth_file_list_clear_queue (GthFileList *file_list)
216 {
217 if (file_list->priv->dirty_event != 0) {
218 g_source_remove (file_list->priv->dirty_event);
219 file_list->priv->dirty_event = 0;
220 file_list->priv->dirty = FALSE;
221 }
222
223 if (file_list->priv->update_event != 0) {
224 g_source_remove (file_list->priv->update_event);
225 file_list->priv->update_event = 0;
226 file_list->priv->dirty = FALSE;
227 }
228
229 if (file_list->priv->restart_thumb_update != 0) {
230 g_source_remove (file_list->priv->restart_thumb_update);
231 file_list->priv->restart_thumb_update = 0;
232 }
233
234 g_list_foreach (file_list->priv->queue, (GFunc) gth_file_list_op_free, NULL);
235 g_list_free (file_list->priv->queue);
236 file_list->priv->queue = NULL;
237 }
238
239
240 static void
_gth_file_list_remove_op(GthFileList * file_list,GthFileListOpType op_type)241 _gth_file_list_remove_op (GthFileList *file_list,
242 GthFileListOpType op_type)
243 {
244 GList *scan;
245
246 scan = file_list->priv->queue;
247 while (scan != NULL) {
248 GthFileListOp *op = scan->data;
249
250 if (op->type != op_type) {
251 scan = scan->next;
252 continue;
253 }
254
255 file_list->priv->queue = g_list_remove_link (file_list->priv->queue, scan);
256 gth_file_list_op_free (op);
257 g_list_free (scan);
258
259 scan = file_list->priv->queue;
260 }
261 }
262
263
264 static void
_gth_file_list_queue_op(GthFileList * file_list,GthFileListOp * op)265 _gth_file_list_queue_op (GthFileList *file_list,
266 GthFileListOp *op)
267 {
268 if ((op->type == GTH_FILE_LIST_OP_TYPE_SET_FILES) || (op->type == GTH_FILE_LIST_OP_TYPE_CLEAR_FILES))
269 _gth_file_list_clear_queue (file_list);
270 if (op->type == GTH_FILE_LIST_OP_TYPE_SET_FILTER)
271 _gth_file_list_remove_op (file_list, GTH_FILE_LIST_OP_TYPE_SET_FILTER);
272 file_list->priv->queue = g_list_append (file_list->priv->queue, op);
273
274 if (! file_list->priv->loading_thumbs)
275 _gth_file_list_exec_next_op (file_list);
276 }
277
278
279 /* -- gth_file_list -- */
280
281
282 static void
gth_file_list_finalize(GObject * object)283 gth_file_list_finalize (GObject *object)
284 {
285 GthFileList *file_list;
286
287 file_list = GTH_FILE_LIST (object);
288
289 _gth_file_list_clear_queue (file_list);
290 _g_object_unref (file_list->priv->thumb_loader);
291 _g_object_list_unref (file_list->priv->visibles);
292 g_hash_table_unref (file_list->priv->thumb_data);
293 if (file_list->priv->icon_cache != NULL)
294 gth_icon_cache_free (file_list->priv->icon_cache);
295 g_object_unref (file_list->priv->settings);
296
297 G_OBJECT_CLASS (gth_file_list_parent_class)->finalize (object);
298 }
299
300
301 static GtkSizeRequestMode
gth_file_list_get_request_mode(GtkWidget * widget)302 gth_file_list_get_request_mode (GtkWidget *widget)
303 {
304 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
305 }
306
307
308 static void
gth_file_list_get_preferred_width(GtkWidget * widget,int * minimum_width,int * natural_width)309 gth_file_list_get_preferred_width (GtkWidget *widget,
310 int *minimum_width,
311 int *natural_width)
312 {
313 GthFileList *file_list = GTH_FILE_LIST (widget);
314 GtkWidget *vscrollbar;
315 const int border = 1;
316
317 gtk_widget_get_preferred_width (file_list->priv->view, minimum_width, natural_width);
318 if (minimum_width)
319 *minimum_width += border * 2;
320 if (natural_width)
321 *natural_width += border * 2;
322
323 vscrollbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (file_list->priv->scrolled_window));
324 if (gtk_widget_get_visible (vscrollbar) || (file_list->priv->type == GTH_FILE_LIST_MODE_V_SIDEBAR)) {
325 int vscrollbar_minimum_width;
326 int vscrollbar_natural_width;
327 int scrollbar_spacing;
328
329 gtk_widget_get_preferred_width (vscrollbar, &vscrollbar_minimum_width, &vscrollbar_natural_width);
330 gtk_widget_style_get (file_list->priv->scrolled_window,
331 "scrollbar-spacing", &scrollbar_spacing,
332 NULL);
333
334 *minimum_width += vscrollbar_minimum_width + scrollbar_spacing;
335 *natural_width += vscrollbar_natural_width + scrollbar_spacing;
336 }
337 }
338
339
340 static void
gth_file_list_get_preferred_height(GtkWidget * widget,int * minimum_height,int * natural_height)341 gth_file_list_get_preferred_height (GtkWidget *widget,
342 int *minimum_height,
343 int *natural_height)
344 {
345 GthFileList *file_list = GTH_FILE_LIST (widget);
346 const int border = 1;
347
348 gtk_widget_get_preferred_height (file_list->priv->view, minimum_height, natural_height);
349 if (minimum_height)
350 *minimum_height += border * 2;
351 if (natural_height)
352 *natural_height += border * 2;
353 }
354
355
356 static void
gth_file_list_class_init(GthFileListClass * class)357 gth_file_list_class_init (GthFileListClass *class)
358 {
359 GObjectClass *object_class;
360 GtkWidgetClass *widget_class;
361
362 object_class = (GObjectClass*) class;
363 object_class->finalize = gth_file_list_finalize;
364
365 widget_class = (GtkWidgetClass*) class;
366 widget_class->get_request_mode = gth_file_list_get_request_mode;
367 widget_class->get_preferred_width = gth_file_list_get_preferred_width;
368 widget_class->get_preferred_height = gth_file_list_get_preferred_height;
369 }
370
371
372 /* -- ThumbData -- */
373
374
375 static ThumbData *
thumb_data_new(void)376 thumb_data_new (void)
377 {
378 ThumbData *data;
379
380 data = g_new0 (ThumbData, 1);
381 data->ref = 1;
382
383 return data;
384 }
385
386
387 static ThumbData *
thumb_data_ref(ThumbData * data)388 thumb_data_ref (ThumbData *data)
389 {
390 data->ref++;
391 return data;
392 }
393
394
395 static void
thumb_data_unref(ThumbData * data)396 thumb_data_unref (ThumbData *data)
397 {
398 data->ref--;
399 if (data->ref > 0)
400 return;
401 cairo_surface_destroy (data->image);
402 g_free (data);
403 }
404
405
406 /* --- */
407
408
409 static void
gth_file_list_init(GthFileList * file_list)410 gth_file_list_init (GthFileList *file_list)
411 {
412 gtk_widget_set_can_focus (GTK_WIDGET (file_list), FALSE);
413
414 file_list->priv = gth_file_list_get_instance_private (file_list);
415 file_list->priv->settings = g_settings_new (GTHUMB_BROWSER_SCHEMA);
416 file_list->priv->type = GTH_FILE_LIST_MODE_NORMAL;
417 file_list->priv->vadj = NULL;
418 file_list->priv->notebook = NULL;
419 file_list->priv->view = NULL;
420 file_list->priv->message = NULL;
421 file_list->priv->scrolled_window = NULL;
422 file_list->priv->icon_cache = NULL;
423 file_list->priv->file_source = NULL;
424 file_list->priv->load_thumbs = TRUE;
425 file_list->priv->thumb_size = g_settings_get_int (file_list->priv->settings, PREF_BROWSER_THUMBNAIL_SIZE);
426 file_list->priv->ignore_hidden_thumbs = FALSE;
427 file_list->priv->thumb_data = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, (GDestroyNotify) thumb_data_unref);
428 file_list->priv->thumb_loader = NULL;
429 file_list->priv->loading_thumbs = FALSE;
430 file_list->priv->dirty = FALSE;
431 file_list->priv->dirty_event = 0;
432 file_list->priv->restart_thumb_update = 0;
433 file_list->priv->queue = NULL;
434 file_list->priv->jobs = NULL;
435 file_list->priv->cancelling = FALSE;
436 file_list->priv->update_event = 0;
437 file_list->priv->visibility_changed = FALSE;
438 file_list->priv->visibles = NULL;
439 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_INITIALIZE;
440 }
441
442
443 static void _gth_file_list_update_next_thumb (GthFileList *file_list);
444
445
446 static gboolean
restart_thumb_update_cb(gpointer data)447 restart_thumb_update_cb (gpointer data)
448 {
449 GthFileList *file_list = data;
450
451 if (file_list->priv->update_event != 0) {
452 g_source_remove (file_list->priv->update_event);
453 file_list->priv->update_event = 0;
454 }
455
456 if (file_list->priv->restart_thumb_update != 0) {
457 g_source_remove (file_list->priv->restart_thumb_update);
458 file_list->priv->restart_thumb_update = 0;
459 }
460
461 file_list->priv->loading_thumbs = TRUE;
462 _gth_file_list_update_next_thumb (file_list);
463
464 return FALSE;
465 }
466
467
468 static void
start_update_next_thumb(GthFileList * file_list)469 start_update_next_thumb (GthFileList *file_list)
470 {
471 if (file_list->priv->cancelling)
472 return;
473
474 if (file_list->priv->loading_thumbs)
475 return;
476
477 if (! file_list->priv->load_thumbs) {
478 file_list->priv->loading_thumbs = FALSE;
479 return;
480 }
481
482 if (file_list->priv->restart_thumb_update != 0)
483 g_source_remove (file_list->priv->restart_thumb_update);
484 file_list->priv->restart_thumb_update = g_timeout_add (UPDATE_THUMBNAILS_AFTER_SCROLL_TIMEOUT, restart_thumb_update_cb, file_list);
485 }
486
487
488 static void
_gth_file_list_done(GthFileList * file_list)489 _gth_file_list_done (GthFileList *file_list)
490 {
491 file_list->priv->loading_thumbs = FALSE;
492 }
493
494
495 /* ThumbnailJob */
496
497
498 static void
thumbnail_job_free(ThumbnailJob * job)499 thumbnail_job_free (ThumbnailJob *job)
500 {
501 job->file_list->priv->jobs = g_list_remove (job->file_list->priv->jobs, job);
502 if (job->file_list->priv->jobs == NULL)
503 _gth_file_list_done (job->file_list);
504
505 _g_object_unref (job->file_data);
506 _g_object_unref (job->cancellable);
507 _g_object_unref (job->loader);
508 _g_object_unref (job->file_list);
509 g_free (job);
510 }
511
512
513 static void
thumbnail_job_cancel(ThumbnailJob * job)514 thumbnail_job_cancel (ThumbnailJob *job)
515 {
516 if (job->started)
517 g_cancellable_cancel (job->cancellable);
518 else
519 thumbnail_job_free (job);
520 }
521
522
523 /* --- */
524
525
526 static gboolean
vadj_changed_cb(GtkAdjustment * adjustment,gpointer user_data)527 vadj_changed_cb (GtkAdjustment *adjustment,
528 gpointer user_data)
529 {
530 GthFileList *file_list = user_data;
531
532 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_INITIALIZE;
533 start_update_next_thumb (GTH_FILE_LIST (user_data));
534
535 return FALSE;
536 }
537
538
539 static void
file_view_drag_data_get_cb(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * data,guint info,guint time,gpointer user_data)540 file_view_drag_data_get_cb (GtkWidget *widget,
541 GdkDragContext *drag_context,
542 GtkSelectionData *data,
543 guint info,
544 guint time,
545 gpointer user_data)
546 {
547 GthFileList *file_list = user_data;
548 GList *items;
549 GList *files;
550 GList *scan;
551 int n_uris;
552 char **uris;
553 int i;
554
555 items = gth_file_selection_get_selected (GTH_FILE_SELECTION (file_list->priv->view));
556 files = gth_file_list_get_files (file_list, items);
557 n_uris = g_list_length (files);
558 uris = g_new (char *, n_uris + 1);
559 for (scan = files, i = 0; scan; scan = scan->next, i++) {
560 GthFileData *file_data = scan->data;
561 uris[i] = g_file_get_uri (file_data->file);
562 }
563 uris[i] = NULL;
564
565 gtk_selection_data_set_uris (data, uris);
566
567 g_strfreev (uris);
568 _g_object_list_unref (files);
569 _gtk_tree_path_list_free (items);
570 }
571
572
573 static void
_gth_file_list_update_orientation(GthFileList * file_list)574 _gth_file_list_update_orientation (GthFileList *file_list)
575 {
576 GtkPolicyType hscrollbar_policy;
577 GtkPolicyType vscrollbar_policy;
578
579 hscrollbar_policy = GTK_POLICY_AUTOMATIC;
580 vscrollbar_policy = GTK_POLICY_AUTOMATIC;
581
582 if (file_list->priv->type == GTH_FILE_LIST_MODE_V_SIDEBAR) {
583 gtk_orientable_set_orientation (GTK_ORIENTABLE (file_list), GTK_ORIENTATION_VERTICAL);
584 vscrollbar_policy = GTK_POLICY_ALWAYS;
585 }
586 else if (file_list->priv->type == GTH_FILE_LIST_MODE_H_SIDEBAR)
587 gtk_orientable_set_orientation (GTK_ORIENTABLE (file_list), GTK_ORIENTATION_HORIZONTAL);
588
589 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (file_list->priv->scrolled_window),
590 hscrollbar_policy,
591 vscrollbar_policy);
592 }
593
594
595 static void
file_store_visibility_changed_cb(GthFileStore * file_store,GthFileList * file_list)596 file_store_visibility_changed_cb (GthFileStore *file_store,
597 GthFileList *file_list)
598 {
599 file_list->priv->visibility_changed = TRUE;
600 }
601
602
603 static void
file_store_rows_reordered_cb(GtkTreeModel * tree_model,GtkTreePath * path,GtkTreeIter * iter,gpointer new_order,gpointer user_data)604 file_store_rows_reordered_cb (GtkTreeModel *tree_model,
605 GtkTreePath *path,
606 GtkTreeIter *iter,
607 gpointer new_order,
608 gpointer user_data)
609 {
610 GthFileList *file_list = user_data;
611
612 file_list->priv->visibility_changed = TRUE;
613 }
614
615
616 static void
gth_file_list_construct(GthFileList * file_list,GtkWidget * file_view,GthFileListMode list_type,gboolean enable_drag_drop)617 gth_file_list_construct (GthFileList *file_list,
618 GtkWidget *file_view,
619 GthFileListMode list_type,
620 gboolean enable_drag_drop)
621 {
622 GthFileStore *model;
623
624 file_list->priv->thumb_loader = gth_thumb_loader_new (file_list->priv->thumb_size);
625 file_list->priv->icon_cache = gth_icon_cache_new (gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (file_list))), file_list->priv->thumb_size / 2);
626
627 /* the main notebook */
628
629 file_list->priv->notebook = gtk_stack_new ();
630 gtk_widget_set_can_focus (file_list->priv->notebook, FALSE);
631
632 /* the message pane */
633
634 file_list->priv->message = gth_empty_list_new (_(EMPTY));
635
636 /* the file view */
637
638 file_list->priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
639 gtk_widget_set_can_focus (file_list->priv->scrolled_window, FALSE);
640 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (file_list->priv->scrolled_window), GTK_SHADOW_ETCHED_IN);
641
642 file_list->priv->vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (file_list->priv->scrolled_window));
643 g_signal_connect (G_OBJECT (file_list->priv->vadj),
644 "changed",
645 G_CALLBACK (vadj_changed_cb),
646 file_list);
647 g_signal_connect (G_OBJECT (file_list->priv->vadj),
648 "value-changed",
649 G_CALLBACK (vadj_changed_cb),
650 file_list);
651
652 file_list->priv->view = file_view;
653 model = gth_file_store_new ();
654 gth_file_view_set_model (GTH_FILE_VIEW (file_list->priv->view), GTK_TREE_MODEL (model));
655 g_object_unref (model);
656
657 g_signal_connect (model,
658 "visibility-changed",
659 G_CALLBACK (file_store_visibility_changed_cb),
660 file_list);
661 g_signal_connect (model,
662 "rows-reordered",
663 G_CALLBACK (file_store_rows_reordered_cb),
664 file_list);
665 g_signal_connect (G_OBJECT (file_list->priv->view),
666 "drag-data-get",
667 G_CALLBACK (file_view_drag_data_get_cb),
668 file_list);
669
670 if (enable_drag_drop)
671 gth_file_list_enable_drag_source (file_list, GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
672
673 gth_file_list_set_mode (file_list, list_type);
674
675 /* pack the widgets together */
676
677 gtk_widget_show (file_list->priv->view);
678 gtk_container_add (GTK_CONTAINER (file_list->priv->scrolled_window), file_list->priv->view);
679
680 gtk_widget_show (file_list->priv->scrolled_window);
681 gtk_stack_add_named (GTK_STACK (file_list->priv->notebook), file_list->priv->scrolled_window, _FILE_VIEW);
682
683 gtk_widget_show (file_list->priv->message);
684 gtk_stack_add_named (GTK_STACK (file_list->priv->notebook), file_list->priv->message, _EMPTY_VIEW);
685
686 gtk_widget_show (file_list->priv->notebook);
687 gtk_box_pack_start (GTK_BOX (file_list), file_list->priv->notebook, TRUE, TRUE, 0);
688
689 gtk_stack_set_visible_child_name (GTK_STACK (file_list->priv->notebook), _EMPTY_VIEW);
690 }
691
692
693 GtkWidget *
gth_file_list_new(GtkWidget * file_view,GthFileListMode list_type,gboolean enable_drag_drop)694 gth_file_list_new (GtkWidget *file_view,
695 GthFileListMode list_type,
696 gboolean enable_drag_drop)
697 {
698 GtkWidget *widget;
699
700 widget = GTK_WIDGET (g_object_new (GTH_TYPE_FILE_LIST, NULL));
701 gth_file_list_construct (GTH_FILE_LIST (widget), file_view, list_type, enable_drag_drop);
702
703 return widget;
704 }
705
706
707 void
gth_file_list_set_mode(GthFileList * file_list,GthFileListMode list_type)708 gth_file_list_set_mode (GthFileList *file_list,
709 GthFileListMode list_type)
710 {
711 g_return_if_fail (GTH_IS_FILE_LIST (file_list));
712
713 file_list->priv->type = list_type;
714
715 if ((file_list->priv->type == GTH_FILE_LIST_MODE_SELECTOR) || (file_list->priv->type == GTH_FILE_LIST_MODE_NO_SELECTION))
716 gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_NONE);
717 else if ((file_list->priv->type == GTH_FILE_LIST_MODE_H_SIDEBAR) || (file_list->priv->type == GTH_FILE_LIST_MODE_V_SIDEBAR))
718 gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_SINGLE);
719 else
720 gth_file_selection_set_selection_mode (GTH_FILE_SELECTION (file_list->priv->view), GTK_SELECTION_MULTIPLE);
721
722 _gth_file_list_update_orientation (file_list);
723 }
724
725
726 GthFileListMode
gth_file_list_get_mode(GthFileList * file_list)727 gth_file_list_get_mode (GthFileList *file_list)
728 {
729 return file_list->priv->type;
730 }
731
732
733 typedef struct {
734 GthFileList *file_list;
735 DataFunc done_func;
736 gpointer user_data;
737 guint check_id;
738 } CancelData;
739
740
741 static void
cancel_data_free(CancelData * cancel_data)742 cancel_data_free (CancelData *cancel_data)
743 {
744 if (cancel_data->check_id != 0)
745 g_source_remove (cancel_data->check_id);
746 g_object_unref (cancel_data->file_list);
747 g_free (cancel_data);
748 }
749
750
751 static gboolean
wait_for_jobs_to_finish(gpointer user_data)752 wait_for_jobs_to_finish (gpointer user_data)
753 {
754 CancelData *cancel_data = user_data;
755
756 if (cancel_data->file_list->priv->jobs == NULL) {
757 if (cancel_data->check_id != 0) {
758 g_source_remove (cancel_data->check_id);
759 cancel_data->check_id = 0;
760 }
761 cancel_data->file_list->priv->cancelling = FALSE;
762 if (cancel_data->done_func != NULL)
763 cancel_data->done_func (cancel_data->user_data);
764 cancel_data_free (cancel_data);
765 return FALSE;
766 }
767
768 return TRUE;
769 }
770
771
772 static void
_gth_file_list_cancel_jobs(GthFileList * file_list,DataFunc done_func,gpointer user_data)773 _gth_file_list_cancel_jobs (GthFileList *file_list,
774 DataFunc done_func,
775 gpointer user_data)
776 {
777 CancelData *cancel_data;
778 GList *list;
779 GList *scan;
780
781 cancel_data = g_new0 (CancelData, 1);
782 cancel_data->file_list = g_object_ref (file_list);
783 cancel_data->done_func = done_func;
784 cancel_data->user_data = user_data;
785
786 if (file_list->priv->jobs == NULL) {
787 cancel_data->check_id = g_idle_add (wait_for_jobs_to_finish, cancel_data);
788 return;
789 }
790
791 list = g_list_copy (file_list->priv->jobs);
792 for (scan = list; scan; scan = scan->next) {
793 ThumbnailJob *job = scan->data;
794 thumbnail_job_cancel (job);
795 }
796 g_list_free (list);
797
798 cancel_data->check_id = g_timeout_add (CHECK_JOBS_INTERVAL,
799 wait_for_jobs_to_finish,
800 cancel_data);
801 }
802
803
804 void
gth_file_list_cancel(GthFileList * file_list,DataFunc done_func,gpointer user_data)805 gth_file_list_cancel (GthFileList *file_list,
806 DataFunc done_func,
807 gpointer user_data)
808 {
809 file_list->priv->cancelling = TRUE;
810 _gth_file_list_clear_queue (file_list);
811 _gth_file_list_cancel_jobs (file_list, done_func, user_data);
812 }
813
814
815 GthThumbLoader *
gth_file_list_get_thumb_loader(GthFileList * file_list)816 gth_file_list_get_thumb_loader (GthFileList *file_list)
817 {
818 return file_list->priv->thumb_loader;
819 }
820
821
822 static void
gfl_clear_list(GthFileList * file_list,const char * message)823 gfl_clear_list (GthFileList *file_list,
824 const char *message)
825 {
826 GthFileStore *file_store;
827
828 gth_file_selection_unselect_all (GTH_FILE_SELECTION (file_list->priv->view));
829
830 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
831 gth_file_store_clear (file_store);
832 g_hash_table_remove_all (file_list->priv->thumb_data);
833
834 gth_empty_list_set_text (GTH_EMPTY_LIST (file_list->priv->message), message);
835 gtk_stack_set_visible_child_name (GTK_STACK (file_list->priv->notebook), _EMPTY_VIEW);
836 }
837
838
839 void
gth_file_list_clear(GthFileList * file_list,const char * message)840 gth_file_list_clear (GthFileList *file_list,
841 const char *message)
842 {
843 GthFileListOp *op;
844
845 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_CLEAR_FILES);
846 op->sval = g_strdup (message != NULL ? message : _(EMPTY));
847 _gth_file_list_queue_op (file_list, op);
848 }
849
850
851 static void
_gth_file_list_update_pane(GthFileList * file_list)852 _gth_file_list_update_pane (GthFileList *file_list)
853 {
854 GthFileStore *file_store;
855
856 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
857
858 if (gth_file_store_n_visibles (file_store) > 0) {
859 gtk_stack_set_visible_child_name (GTK_STACK (file_list->priv->notebook), _FILE_VIEW);
860 }
861 else {
862 gth_empty_list_set_text (GTH_EMPTY_LIST (file_list->priv->message), _(EMPTY));
863 gtk_stack_set_visible_child_name (GTK_STACK (file_list->priv->notebook), _EMPTY_VIEW);
864 }
865 }
866
867
868 static void
gfl_add_files(GthFileList * file_list,GList * files,int position)869 gfl_add_files (GthFileList *file_list,
870 GList *files,
871 int position)
872 {
873 GthFileStore *file_store;
874 GList *scan;
875 char *cache_base_uri;
876
877 performance (DEBUG_INFO, "gfl_add_files start");
878
879 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
880
881 cache_base_uri = g_strconcat (_g_uri_get_home (), "/.thumbnails", NULL);
882 for (scan = files; scan; scan = scan->next) {
883 GthFileData *file_data = scan->data;
884 char *uri;
885 ThumbData *thumb_data;
886 GIcon *icon;
887 cairo_surface_t *image = NULL;
888
889 if (g_file_info_get_file_type (file_data->info) != G_FILE_TYPE_REGULAR)
890 continue;
891
892 if (g_hash_table_lookup (file_list->priv->thumb_data, file_data->file) != NULL)
893 continue;
894
895 uri = g_file_get_uri (file_data->file);
896
897 thumb_data = thumb_data_new ();
898 /* files in the .thumbnails directory are already thumbnails,
899 * set them as created. */
900 thumb_data->thumb_created = _g_uri_is_parent (cache_base_uri, uri);
901
902 g_hash_table_insert (file_list->priv->thumb_data,
903 g_object_ref (file_data->file),
904 thumb_data);
905
906 icon = g_file_info_get_symbolic_icon (file_data->info);
907 image = gth_icon_cache_get_surface (file_list->priv->icon_cache, icon);
908 gth_file_store_queue_add (file_store,
909 file_data,
910 image,
911 TRUE);
912
913 cairo_surface_destroy (image);
914 g_free (uri);
915 }
916 g_free (cache_base_uri);
917
918 gth_file_store_exec_add (file_store, position);
919 _gth_file_list_update_pane (file_list);
920
921 performance (DEBUG_INFO, "gfl_add_files end");
922 }
923
924
925 void
gth_file_list_add_files(GthFileList * file_list,GList * files,int position)926 gth_file_list_add_files (GthFileList *file_list,
927 GList *files,
928 int position)
929 {
930 GthFileListOp *op;
931
932 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_ADD_FILES);
933 op->file_list = _g_object_list_ref (files);
934 op->position = position;
935 _gth_file_list_queue_op (file_list, op);
936 }
937
938
939 static void
gfl_delete_files(GthFileList * file_list,GList * files)940 gfl_delete_files (GthFileList *file_list,
941 GList *files)
942 {
943 GthFileStore *file_store;
944 GList *scan;
945
946 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
947
948 for (scan = files; scan; scan = scan->next) {
949 GFile *file = scan->data;
950 GtkTreeIter iter;
951
952 if (g_hash_table_lookup (file_list->priv->thumb_data, file) == NULL)
953 continue;
954
955 g_hash_table_remove (file_list->priv->thumb_data, file);
956
957 if (gth_file_store_find (file_store, file, &iter))
958 gth_file_store_queue_remove (file_store, &iter);
959 }
960 gth_file_store_exec_remove (file_store);
961 _gth_file_list_update_pane (file_list);
962 }
963
964
965 void
gth_file_list_delete_files(GthFileList * file_list,GList * files)966 gth_file_list_delete_files (GthFileList *file_list,
967 GList *files)
968 {
969 GthFileListOp *op;
970
971 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_DELETE_FILES);
972 op->files = _g_object_list_ref (files);
973 _gth_file_list_queue_op (file_list, op);
974 }
975
976
977 static void
gfl_update_files(GthFileList * file_list,GList * files)978 gfl_update_files (GthFileList *file_list,
979 GList *files)
980 {
981 GthFileStore *file_store;
982 GList *scan;
983
984 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
985 for (scan = files; scan; scan = scan->next) {
986 GthFileData *file_data = scan->data;
987 ThumbData *thumb_data;
988 GtkTreeIter iter;
989
990 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
991 if (thumb_data == NULL)
992 continue;
993
994 thumb_data->error = FALSE;
995 thumb_data->thumb_loaded = FALSE;
996 thumb_data->thumb_created = FALSE;
997
998 if (gth_file_store_find (file_store, file_data->file, &iter))
999 gth_file_store_queue_set (file_store,
1000 &iter,
1001 GTH_FILE_STORE_FILE_DATA_COLUMN, file_data,
1002 -1);
1003 }
1004 gth_file_store_exec_set (file_store);
1005 _gth_file_list_update_pane (file_list);
1006 }
1007
1008
1009 void
gth_file_list_update_files(GthFileList * file_list,GList * files)1010 gth_file_list_update_files (GthFileList *file_list,
1011 GList *files)
1012 {
1013 GthFileListOp *op;
1014
1015 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_UPDATE_FILES);
1016 op->file_list = _g_object_list_ref (files);
1017 _gth_file_list_queue_op (file_list, op);
1018 }
1019
1020
1021 static void
gfl_update_emblems(GthFileList * file_list,GList * files)1022 gfl_update_emblems (GthFileList *file_list,
1023 GList *files)
1024 {
1025 GthFileStore *file_store;
1026 GList *scan;
1027
1028 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1029 for (scan = files; scan; scan = scan->next) {
1030 GthFileData *file_data = scan->data;
1031 ThumbData *thumb_data;
1032 GtkTreeIter iter;
1033
1034 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
1035 if (thumb_data == NULL)
1036 continue;
1037
1038 if (gth_file_store_find (file_store, file_data->file, &iter))
1039 gth_file_store_queue_set (file_store,
1040 &iter,
1041 GTH_FILE_STORE_EMBLEMS_COLUMN, g_file_info_get_attribute_object (file_data->info, GTH_FILE_ATTRIBUTE_EMBLEMS),
1042 -1);
1043 }
1044 gth_file_store_exec_set (file_store);
1045 }
1046
1047
1048 void
gth_file_list_update_emblems(GthFileList * file_list,GList * files)1049 gth_file_list_update_emblems (GthFileList *file_list,
1050 GList *files /* GthFileData */)
1051 {
1052 GthFileListOp *op;
1053
1054 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_UPDATE_EMBLEMS);
1055 op->file_list = _g_object_list_ref (files);
1056 _gth_file_list_queue_op (file_list, op);
1057 }
1058
1059
1060 static void
gfl_rename_file(GthFileList * file_list,GFile * file,GthFileData * file_data)1061 gfl_rename_file (GthFileList *file_list,
1062 GFile *file,
1063 GthFileData *file_data)
1064 {
1065 GthFileStore *file_store;
1066 GtkTreeIter iter;
1067
1068 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1069 if (gth_file_store_find (file_store, file, &iter)) {
1070 ThumbData *thumb_data;
1071
1072 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file);
1073 g_assert (thumb_data != NULL);
1074
1075 g_hash_table_insert (file_list->priv->thumb_data,
1076 g_object_ref (file_data->file),
1077 thumb_data_ref (thumb_data));
1078 g_hash_table_remove (file_list->priv->thumb_data, file);
1079
1080 gth_file_store_set (file_store,
1081 &iter,
1082 GTH_FILE_STORE_FILE_DATA_COLUMN, file_data,
1083 -1);
1084 }
1085 _gth_file_list_update_pane (file_list);
1086 }
1087
1088
1089 void
gth_file_list_rename_file(GthFileList * file_list,GFile * file,GthFileData * file_data)1090 gth_file_list_rename_file (GthFileList *file_list,
1091 GFile *file,
1092 GthFileData *file_data)
1093 {
1094 GthFileListOp *op;
1095
1096 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_RENAME_FILE);
1097 op->file = g_object_ref (file);
1098 op->file_data = g_object_ref (file_data);
1099 _gth_file_list_queue_op (file_list, op);
1100 }
1101
1102
1103 static void
gfl_set_files(GthFileList * file_list,GList * files)1104 gfl_set_files (GthFileList *file_list,
1105 GList *files)
1106 {
1107 gth_thumb_loader_set_save_thumbnails (file_list->priv->thumb_loader, g_settings_get_boolean (file_list->priv->settings, PREF_BROWSER_SAVE_THUMBNAILS));
1108 gth_thumb_loader_set_max_file_size (file_list->priv->thumb_loader, g_settings_get_int (file_list->priv->settings, PREF_BROWSER_THUMBNAIL_LIMIT));
1109 gth_file_selection_unselect_all (GTH_FILE_SELECTION (file_list->priv->view));
1110
1111 gth_file_view_set_vscroll (GTH_FILE_VIEW (file_list->priv->view), 0);
1112 gth_file_store_clear ((GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view)));
1113 g_hash_table_remove_all (file_list->priv->thumb_data);
1114 gfl_add_files (file_list, files, -1);
1115 }
1116
1117
1118 void
gth_file_list_set_files(GthFileList * file_list,GList * files)1119 gth_file_list_set_files (GthFileList *file_list,
1120 GList *files)
1121 {
1122 GthFileListOp *op;
1123
1124 if (files != NULL) {
1125 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_SET_FILES);
1126 op->file_list = _g_object_list_ref (files);
1127 _gth_file_list_queue_op (file_list, op);
1128 }
1129 else {
1130 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_CLEAR_FILES);
1131 op->sval = g_strdup (_(EMPTY));
1132 _gth_file_list_queue_op (file_list, op);
1133 }
1134 }
1135
1136
1137 GList *
gth_file_list_get_files(GthFileList * file_list,GList * items)1138 gth_file_list_get_files (GthFileList *file_list,
1139 GList *items)
1140 {
1141 GList *list = NULL;
1142 GthFileStore *file_store;
1143 GList *scan;
1144
1145 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1146 for (scan = items; scan; scan = scan->next) {
1147 GtkTreePath *tree_path = scan->data;
1148 GtkTreeIter iter;
1149 GthFileData *file_data;
1150
1151 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (file_store), &iter, tree_path))
1152 continue;
1153 file_data = gth_file_store_get_file (file_store, &iter);
1154 if (file_data != NULL)
1155 list = g_list_prepend (list, g_object_ref (file_data));
1156 }
1157
1158 return g_list_reverse (list);
1159 }
1160
1161
1162 static void
gfl_set_filter(GthFileList * file_list,GthTest * filter)1163 gfl_set_filter (GthFileList *file_list,
1164 GthTest *filter)
1165 {
1166 GthFileStore *file_store;
1167
1168 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1169 if (file_store != NULL)
1170 gth_file_store_set_filter (file_store, filter);
1171 _gth_file_list_update_pane (file_list);
1172 }
1173
1174
1175 void
gth_file_list_set_filter(GthFileList * file_list,GthTest * filter)1176 gth_file_list_set_filter (GthFileList *file_list,
1177 GthTest *filter)
1178 {
1179 GthFileListOp *op;
1180
1181 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_SET_FILTER);
1182 if (filter != NULL)
1183 op->filter = g_object_ref (filter);
1184 else
1185 op->filter = gth_test_new ();
1186 _gth_file_list_queue_op (file_list, op);
1187 }
1188
1189
1190 static void
gfl_set_sort_func(GthFileList * file_list,GthFileDataCompFunc cmp_func,gboolean inverse_sort)1191 gfl_set_sort_func (GthFileList *file_list,
1192 GthFileDataCompFunc cmp_func,
1193 gboolean inverse_sort)
1194 {
1195 GthFileStore *file_store;
1196
1197 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1198 if (file_store != NULL)
1199 gth_file_store_set_sort_func (file_store, cmp_func, inverse_sort);
1200 }
1201
1202
1203 void
gth_file_list_set_sort_func(GthFileList * file_list,GthFileDataCompFunc cmp_func,gboolean inverse_sort)1204 gth_file_list_set_sort_func (GthFileList *file_list,
1205 GthFileDataCompFunc cmp_func,
1206 gboolean inverse_sort)
1207 {
1208 GthFileListOp *op;
1209
1210 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_SET_SORT_FUNC);
1211 op->cmp_func = cmp_func;
1212 op->inverse_sort = inverse_sort;
1213 _gth_file_list_queue_op (file_list, op);
1214 }
1215
1216
1217 static void
gfl_enable_thumbs(GthFileList * file_list,gboolean enable)1218 gfl_enable_thumbs (GthFileList *file_list,
1219 gboolean enable)
1220 {
1221 GthFileStore *file_store;
1222 GtkTreeIter iter;
1223
1224 gth_thumb_loader_set_save_thumbnails (file_list->priv->thumb_loader,
1225 g_settings_get_boolean (file_list->priv->settings, PREF_BROWSER_SAVE_THUMBNAILS));
1226 gth_thumb_loader_set_max_file_size (file_list->priv->thumb_loader,
1227 g_settings_get_int (file_list->priv->settings, PREF_BROWSER_THUMBNAIL_LIMIT));
1228
1229 file_list->priv->load_thumbs = enable;
1230
1231 file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1232 if (gth_file_store_get_first (file_store, &iter)) {
1233 do {
1234 GthFileData *file_data;
1235 ThumbData *thumb_data;
1236 GIcon *icon;
1237 cairo_surface_t *image;
1238
1239 file_data = gth_file_store_get_file (file_store, &iter);
1240
1241 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
1242 g_assert (thumb_data != NULL);
1243 thumb_data->thumb_loaded = FALSE;
1244
1245 icon = g_file_info_get_symbolic_icon (file_data->info);
1246 image = gth_icon_cache_get_surface (file_list->priv->icon_cache, icon);
1247 gth_file_store_queue_set (file_store,
1248 &iter,
1249 GTH_FILE_STORE_THUMBNAIL_COLUMN, image,
1250 GTH_FILE_STORE_IS_ICON_COLUMN, TRUE,
1251 -1);
1252
1253 cairo_surface_destroy (image);
1254 }
1255 while (gth_file_store_get_next (file_store, &iter));
1256
1257 gth_file_store_exec_set (file_store);
1258 }
1259
1260 if (enable)
1261 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_INITIALIZE;
1262 start_update_next_thumb (file_list);
1263 }
1264
1265
1266 void
gth_file_list_enable_thumbs(GthFileList * file_list,gboolean enable)1267 gth_file_list_enable_thumbs (GthFileList *file_list,
1268 gboolean enable)
1269 {
1270 GthFileListOp *op;
1271
1272 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_ENABLE_THUMBS);
1273 op->ival = enable;
1274 _gth_file_list_queue_op (file_list, op);
1275 }
1276
1277
1278 void
gth_file_list_set_ignore_hidden(GthFileList * file_list,gboolean value)1279 gth_file_list_set_ignore_hidden (GthFileList *file_list,
1280 gboolean value)
1281 {
1282 file_list->priv->ignore_hidden_thumbs = value;
1283 }
1284
1285
1286 void
gth_file_list_set_thumb_size(GthFileList * file_list,int size)1287 gth_file_list_set_thumb_size (GthFileList *file_list,
1288 int size)
1289 {
1290 file_list->priv->thumb_size = size;
1291
1292 gth_thumb_loader_set_requested_size (file_list->priv->thumb_loader, size);
1293 gth_thumb_loader_set_save_thumbnails (file_list->priv->thumb_loader, g_settings_get_boolean (file_list->priv->settings, PREF_BROWSER_SAVE_THUMBNAILS));
1294 gth_thumb_loader_set_max_file_size (file_list->priv->thumb_loader, g_settings_get_int (file_list->priv->settings, PREF_BROWSER_THUMBNAIL_LIMIT));
1295
1296 gth_icon_cache_free (file_list->priv->icon_cache);
1297 file_list->priv->icon_cache = gth_icon_cache_new (gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (file_list))), size / 2);
1298 gth_icon_cache_set_fallback (file_list->priv->icon_cache, g_themed_icon_new ("text-x-generic-symbolic"));
1299
1300 gth_file_view_set_thumbnail_size (GTH_FILE_VIEW (file_list->priv->view), file_list->priv->thumb_size);
1301
1302 _gth_file_list_update_orientation (file_list);
1303 }
1304
1305
1306 void
gth_file_list_set_caption(GthFileList * file_list,const char * attributes)1307 gth_file_list_set_caption (GthFileList *file_list,
1308 const char *attributes)
1309 {
1310 gth_file_view_set_caption (GTH_FILE_VIEW (file_list->priv->view), attributes);
1311 }
1312
1313
1314 static void
gfl_make_file_visible(GthFileList * file_list,GFile * file)1315 gfl_make_file_visible (GthFileList *file_list,
1316 GFile *file)
1317 {
1318 int pos;
1319
1320 pos = gth_file_store_get_pos ((GthFileStore *) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view)), file);
1321 if (pos >= 0) {
1322 gth_file_selection_unselect_all (GTH_FILE_SELECTION (file_list->priv->view));
1323 gth_file_selection_select (GTH_FILE_SELECTION (file_list->priv->view), pos);
1324 gth_file_view_set_cursor (GTH_FILE_VIEW (file_list->priv->view), pos);
1325 }
1326 }
1327
1328
1329 void
gth_file_list_make_file_visible(GthFileList * file_list,GFile * file)1330 gth_file_list_make_file_visible (GthFileList *file_list,
1331 GFile *file)
1332 {
1333 GthFileListOp *op;
1334
1335 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_MAKE_FILE_VISIBLE);
1336 op->file = g_object_ref (file);
1337 _gth_file_list_queue_op (file_list, op);
1338 }
1339
1340
1341 static void
gfl_restore_state(GthFileList * file_list,GList * selected,double vscroll)1342 gfl_restore_state (GthFileList *file_list,
1343 GList *selected,
1344 double vscroll)
1345 {
1346 GList *scan;
1347
1348 for (scan = selected; scan; scan = scan->next) {
1349 GtkTreePath *path = scan->data;
1350 int pos;
1351
1352 pos = gtk_tree_path_get_indices (path)[0];
1353 gth_file_selection_select (GTH_FILE_SELECTION (file_list->priv->view), pos);
1354 }
1355
1356 gth_file_view_set_vscroll (GTH_FILE_VIEW (file_list->priv->view), vscroll);
1357 }
1358
1359
1360 void
gth_file_list_restore_state(GthFileList * file_list,GList * selected,double vscroll)1361 gth_file_list_restore_state (GthFileList *file_list,
1362 GList *selected,
1363 double vscroll)
1364 {
1365 GthFileListOp *op;
1366
1367 op = gth_file_list_op_new (GTH_FILE_LIST_OP_TYPE_RESTORE_STATE);
1368 op->selected = g_list_copy_deep (selected, (GCopyFunc) gtk_tree_path_copy, NULL);
1369 op->vscroll = vscroll;
1370 _gth_file_list_queue_op (file_list, op);
1371 }
1372
1373
1374 GtkWidget *
gth_file_list_get_view(GthFileList * file_list)1375 gth_file_list_get_view (GthFileList *file_list)
1376 {
1377 return file_list->priv->view;
1378 }
1379
1380
1381 GtkWidget *
gth_file_list_get_empty_view(GthFileList * file_list)1382 gth_file_list_get_empty_view (GthFileList *file_list)
1383 {
1384 return file_list->priv->message;
1385 }
1386
1387
1388 GtkAdjustment *
gth_file_list_get_vadjustment(GthFileList * file_list)1389 gth_file_list_get_vadjustment (GthFileList *file_list)
1390 {
1391 return file_list->priv->vadj;
1392 }
1393
1394
1395 /* thumbs */
1396
1397
1398 static void
flash_queue(GthFileList * file_list)1399 flash_queue (GthFileList *file_list)
1400 {
1401 if (file_list->priv->dirty) {
1402 GthFileStore *file_store;
1403
1404 file_store = (GthFileStore *) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1405 gth_file_store_exec_set (file_store);
1406 file_list->priv->dirty = FALSE;
1407 }
1408
1409 if (file_list->priv->dirty_event != 0) {
1410 g_source_remove (file_list->priv->dirty_event);
1411 file_list->priv->dirty_event = 0;
1412 }
1413 }
1414
1415
1416 static gboolean
flash_queue_cb(gpointer data)1417 flash_queue_cb (gpointer data)
1418 {
1419 flash_queue ((GthFileList *) data);
1420 return FALSE;
1421 }
1422
1423
1424 static void
queue_flash_updates(GthFileList * file_list)1425 queue_flash_updates (GthFileList *file_list)
1426 {
1427 file_list->priv->dirty = TRUE;
1428 if (file_list->priv->dirty_event == 0)
1429 file_list->priv->dirty_event = g_timeout_add (FLASH_THUMBNAIL_QUEUE_TIMEOUT, flash_queue_cb, file_list);
1430 }
1431
1432
1433 static gboolean
get_file_data_iter_with_suggested_pos(GthFileStore * file_store,GthFileData * file_data,int try_pos,GtkTreeIter * iter_p)1434 get_file_data_iter_with_suggested_pos (GthFileStore *file_store,
1435 GthFileData *file_data,
1436 int try_pos,
1437 GtkTreeIter *iter_p)
1438 {
1439 if (gth_file_store_get_nth_visible (file_store, try_pos, iter_p)) {
1440 GthFileData *nth_file_data;
1441
1442 nth_file_data = gth_file_store_get_file (file_store, iter_p);
1443 if (g_file_equal (file_data->file, nth_file_data->file))
1444 return TRUE;
1445
1446 if (gth_file_store_find (file_store, file_data->file, iter_p))
1447 return TRUE;
1448 }
1449
1450 return FALSE;
1451 }
1452
1453
1454 static void
update_thumb_in_file_view(GthFileList * file_list,GthFileData * file_data,int try_pos)1455 update_thumb_in_file_view (GthFileList *file_list,
1456 GthFileData *file_data,
1457 int try_pos)
1458 {
1459 GthFileStore *file_store;
1460 GtkTreeIter iter;
1461 ThumbData *thumb_data;
1462
1463 file_store = (GthFileStore *) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1464 if (! get_file_data_iter_with_suggested_pos (file_store, file_data, try_pos, &iter))
1465 return;
1466
1467 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
1468 if (thumb_data == NULL)
1469 return;
1470
1471 if (thumb_data->image == NULL)
1472 return;
1473
1474 gth_file_store_queue_set (file_store,
1475 &iter,
1476 GTH_FILE_STORE_THUMBNAIL_COLUMN, thumb_data->image,
1477 GTH_FILE_STORE_IS_ICON_COLUMN, FALSE,
1478 -1);
1479 queue_flash_updates (file_list);
1480 }
1481
1482
1483 static void
set_mime_type_icon(GthFileList * file_list,GthFileData * file_data,int try_pos)1484 set_mime_type_icon (GthFileList *file_list,
1485 GthFileData *file_data,
1486 int try_pos)
1487 {
1488 GthFileStore *file_store;
1489 GtkTreeIter iter;
1490 GIcon *icon;
1491 cairo_surface_t *image;
1492
1493 file_store = (GthFileStore *) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1494 if (! get_file_data_iter_with_suggested_pos (file_store, file_data, try_pos, &iter))
1495 return;
1496
1497 icon = g_file_info_get_symbolic_icon (file_data->info);
1498 image = gth_icon_cache_get_surface (file_list->priv->icon_cache, icon);
1499 gth_file_store_queue_set (file_store,
1500 &iter,
1501 GTH_FILE_STORE_THUMBNAIL_COLUMN, image,
1502 GTH_FILE_STORE_IS_ICON_COLUMN, TRUE,
1503 -1);
1504 queue_flash_updates (file_list);
1505
1506 cairo_surface_destroy (image);
1507 }
1508
1509
1510 static void
thumbnail_job_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1511 thumbnail_job_ready_cb (GObject *source_object,
1512 GAsyncResult *result,
1513 gpointer user_data)
1514 {
1515 ThumbnailJob *job = user_data;
1516 GthFileList *file_list = job->file_list;
1517 gboolean success;
1518 cairo_surface_t *image = NULL;
1519 GError *error = NULL;
1520 ThumbData *thumb_data;
1521
1522 success = gth_thumb_loader_load_finish (GTH_THUMB_LOADER (source_object),
1523 result,
1524 &image,
1525 &error);
1526 job->started = FALSE;
1527
1528 if ((! success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1529 || file_list->priv->cancelling)
1530 {
1531 cairo_surface_destroy (image);
1532 thumbnail_job_free (job);
1533 return;
1534 }
1535
1536 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, job->file_data->file);
1537 if (thumb_data == NULL) {
1538 cairo_surface_destroy (image);
1539 thumbnail_job_free (job);
1540 _gth_file_list_update_next_thumb (file_list);
1541 return;
1542 }
1543
1544 cairo_surface_destroy (thumb_data->image);
1545 thumb_data->image = NULL;
1546
1547 if (! success) {
1548 thumb_data->thumb_created = FALSE;
1549 thumb_data->thumb_loaded = FALSE;
1550 if (job->update_in_view)
1551 set_mime_type_icon (file_list, job->file_data, job->pos);
1552
1553 thumb_data->error = TRUE;
1554 }
1555 else {
1556 thumb_data->image = cairo_surface_reference (image);
1557 thumb_data->thumb_created = TRUE;
1558 thumb_data->error = FALSE;
1559 if (job->update_in_view) {
1560 thumb_data->thumb_loaded = TRUE;
1561 update_thumb_in_file_view (file_list, job->file_data, job->pos);
1562 }
1563 }
1564
1565 cairo_surface_destroy (image);
1566 thumbnail_job_free (job);
1567
1568 _gth_file_list_update_next_thumb (file_list);
1569 }
1570
1571
1572 static void
set_loading_icon(GthFileList * file_list,GthFileData * file_data,int try_pos)1573 set_loading_icon (GthFileList *file_list,
1574 GthFileData *file_data,
1575 int try_pos)
1576 {
1577 GthFileStore *file_store;
1578 GtkTreeIter iter;
1579 GIcon *icon;
1580 cairo_surface_t *image;
1581
1582 file_store = (GthFileStore *) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
1583 if (! get_file_data_iter_with_suggested_pos (file_store, file_data, try_pos, &iter))
1584 return;
1585
1586 icon = g_themed_icon_new ("content-loading-symbolic");
1587 image = gth_icon_cache_get_surface (file_list->priv->icon_cache, icon);
1588 gth_file_store_queue_set (file_store,
1589 &iter,
1590 GTH_FILE_STORE_THUMBNAIL_COLUMN, image,
1591 GTH_FILE_STORE_IS_ICON_COLUMN, TRUE,
1592 -1);
1593 queue_flash_updates (file_list);
1594
1595 cairo_surface_destroy (image);
1596 g_object_unref (icon);
1597 }
1598
1599
1600 static gboolean
start_thumbnail_job(gpointer user_data)1601 start_thumbnail_job (gpointer user_data)
1602 {
1603 ThumbnailJob *job = user_data;
1604 GthFileList *file_list = job->file_list;
1605
1606 if (file_list->priv->update_event != 0) {
1607 g_source_remove (file_list->priv->update_event);
1608 file_list->priv->update_event = 0;
1609 }
1610
1611 job->started = TRUE;
1612 gth_thumb_loader_load (job->loader,
1613 job->file_data,
1614 job->cancellable,
1615 thumbnail_job_ready_cb,
1616 job);
1617
1618 return FALSE;
1619 }
1620
1621
1622 static void
_gth_file_list_update_thumb(GthFileList * file_list,ThumbnailJob * job)1623 _gth_file_list_update_thumb (GthFileList *file_list,
1624 ThumbnailJob *job)
1625 {
1626 GList *list;
1627 GList *scan;
1628
1629 if (file_list->priv->update_event != 0) {
1630 g_source_remove (file_list->priv->update_event);
1631 file_list->priv->update_event = 0;
1632 }
1633
1634 if (! job->update_in_view) {
1635 ThumbData *thumb_data;
1636
1637 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, job->file_data->file);
1638
1639 if (gth_thumb_loader_has_valid_thumbnail (file_list->priv->thumb_loader, job->file_data)) {
1640 thumb_data->thumb_created = TRUE;
1641 thumb_data->error = FALSE;
1642 thumbnail_job_free (job);
1643 job = NULL;
1644 }
1645 else if (gth_thumb_loader_has_failed_thumbnail (file_list->priv->thumb_loader, job->file_data)) {
1646 thumb_data->thumb_created = TRUE;
1647 thumb_data->error = TRUE;
1648 thumbnail_job_free (job);
1649 job = NULL;
1650 }
1651
1652 if (job == NULL) {
1653 file_list->priv->update_event = g_idle_add (restart_thumb_update_cb, file_list);
1654 return;
1655 }
1656 }
1657
1658 list = g_list_copy (file_list->priv->jobs);
1659 for (scan = list; scan; scan = scan->next) {
1660 ThumbnailJob *other_job = scan->data;
1661 thumbnail_job_cancel (other_job);
1662 }
1663 g_list_free (list);
1664
1665 file_list->priv->jobs = g_list_prepend (file_list->priv->jobs, job);
1666
1667 if (job->update_in_view)
1668 set_loading_icon (job->file_list, job->file_data, job->pos);
1669 file_list->priv->update_event = g_idle_add (start_thumbnail_job, job);
1670 }
1671
1672
1673 static void
_gth_file_list_thumbs_completed(GthFileList * file_list)1674 _gth_file_list_thumbs_completed (GthFileList *file_list)
1675 {
1676 flash_queue (file_list);
1677 _gth_file_list_done (file_list);
1678 }
1679
1680
1681 static gboolean
update_thumbs_stopped(gpointer callback_data)1682 update_thumbs_stopped (gpointer callback_data)
1683 {
1684 GthFileList *file_list = callback_data;
1685
1686 if (file_list->priv->update_event != 0) {
1687 g_source_remove (file_list->priv->update_event);
1688 file_list->priv->update_event = 0;
1689 }
1690
1691 file_list->priv->loading_thumbs = FALSE;
1692 _gth_file_list_exec_next_op (file_list);
1693
1694 return FALSE;
1695 }
1696
1697
1698 static gboolean
can_create_file_thumbnail(GthFileData * file_data,ThumbData * thumb_data,GTimeVal * current_time,gboolean * young_file_found)1699 can_create_file_thumbnail (GthFileData *file_data,
1700 ThumbData *thumb_data,
1701 GTimeVal *current_time,
1702 gboolean *young_file_found)
1703 {
1704
1705 time_t time_diff;
1706 gboolean young_file;
1707
1708 /* Check for files that are exactly 0 or 1 seconds old; they may still be changing. */
1709 time_diff = current_time->tv_sec - gth_file_data_get_mtime (file_data);
1710 young_file = (time_diff <= 1) && (time_diff >= 0);
1711
1712 if (young_file)
1713 *young_file_found = TRUE;
1714
1715 return ! thumb_data->error && ! young_file;
1716 }
1717
1718
1719 static GList *
_gth_file_list_get_visibles(GthFileList * file_list)1720 _gth_file_list_get_visibles (GthFileList *file_list)
1721 {
1722 if (file_list->priv->visibility_changed) {
1723 _g_object_list_unref (file_list->priv->visibles);
1724 file_list->priv->visibles = gth_file_store_get_visibles ((GthFileStore *) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view)));
1725 file_list->priv->visibility_changed = FALSE;
1726 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_INITIALIZE;
1727 }
1728
1729 return file_list->priv->visibles;
1730 }
1731
1732
1733 static gboolean
_gth_file_list_thumbnailer_iterate(GthFileList * file_list,int * new_pos,GTimeVal * current_time,gboolean * young_file_found)1734 _gth_file_list_thumbnailer_iterate (GthFileList *file_list,
1735 int *new_pos,
1736 GTimeVal *current_time,
1737 gboolean *young_file_found)
1738 {
1739 gboolean iterate_again = TRUE;
1740 GList *list;
1741 GList *scan;
1742 int pos;
1743 GthFileData *file_data;
1744 ThumbData *thumb_data;
1745
1746 list = _gth_file_list_get_visibles (file_list);
1747
1748 switch (file_list->priv->thumbnailer_state.phase) {
1749 case THUMBNAILER_PHASE_INITIALIZE:
1750 file_list->priv->thumbnailer_state.first_visibile = gth_file_view_get_first_visible (GTH_FILE_VIEW (file_list->priv->view));
1751 file_list->priv->thumbnailer_state.last_visible = gth_file_view_get_last_visible (GTH_FILE_VIEW (file_list->priv->view));
1752
1753 /* pass to the 'update visible files' phase. */
1754 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_UPDATE_VISIBLE;
1755 file_list->priv->thumbnailer_state.current_pos = file_list->priv->thumbnailer_state.first_visibile;
1756 file_list->priv->thumbnailer_state.current_item = g_list_nth (list, file_list->priv->thumbnailer_state.current_pos);
1757 if (file_list->priv->thumbnailer_state.current_item == NULL) {
1758 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_COMPLETED;
1759 return FALSE;
1760 }
1761 break;
1762
1763 case THUMBNAILER_PHASE_UPDATE_VISIBLE:
1764 /* Find a non-loaded thumbnail among the visible files. */
1765
1766 scan = file_list->priv->thumbnailer_state.current_item;
1767 pos = file_list->priv->thumbnailer_state.current_pos;
1768 while (scan && (pos <= file_list->priv->thumbnailer_state.last_visible)) {
1769 file_data = scan->data;
1770 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
1771 if (thumb_data == NULL) {
1772 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_COMPLETED;
1773 return FALSE;
1774 }
1775
1776 if (! thumb_data->thumb_loaded && can_create_file_thumbnail (file_data, thumb_data, current_time, young_file_found)) {
1777 /* found a thumbnail to load */
1778 file_list->priv->thumbnailer_state.current_item = scan;
1779 file_list->priv->thumbnailer_state.current_pos = pos;
1780 *new_pos = pos;
1781 return FALSE;
1782 }
1783
1784 pos++;
1785 scan = scan->next;
1786 }
1787
1788 /* No thumbnail to load among the visible images, pass to the
1789 * next phase. Start from the one after the last visible image. */
1790 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_UPDATE_DOWNWARD;
1791 file_list->priv->thumbnailer_state.current_pos = file_list->priv->thumbnailer_state.last_visible + 1;
1792 file_list->priv->thumbnailer_state.current_item = g_list_nth (list, file_list->priv->thumbnailer_state.current_pos);
1793 break;
1794
1795 case THUMBNAILER_PHASE_UPDATE_DOWNWARD:
1796 scan = file_list->priv->thumbnailer_state.current_item;
1797 pos = file_list->priv->thumbnailer_state.current_pos;
1798 while (scan && (pos <= file_list->priv->thumbnailer_state.last_visible + N_CREATEAHEAD)) {
1799 gboolean requested_action_performed;
1800
1801 file_data = scan->data;
1802 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
1803 if (thumb_data == NULL) {
1804 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_COMPLETED;
1805 return FALSE;
1806 }
1807
1808 if (pos <= file_list->priv->thumbnailer_state.last_visible + N_VIEWAHEAD)
1809 requested_action_performed = thumb_data->thumb_loaded;
1810 else
1811 requested_action_performed = thumb_data->thumb_created;
1812
1813 if (! requested_action_performed && can_create_file_thumbnail (file_data, thumb_data, current_time, young_file_found)) {
1814 /* found a thumbnail to load */
1815 file_list->priv->thumbnailer_state.current_item = scan;
1816 file_list->priv->thumbnailer_state.current_pos = pos;
1817 *new_pos = pos;
1818 return FALSE;
1819 }
1820
1821 pos++;
1822 scan = scan->next;
1823 }
1824
1825 /* No thumbnail to load, pass to the next phase. Start from the
1826 * one before the first visible upward to the first one. */
1827 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_UPDATE_UPWARD;
1828 file_list->priv->thumbnailer_state.current_pos = file_list->priv->thumbnailer_state.first_visibile - 1;
1829 file_list->priv->thumbnailer_state.current_item = g_list_nth (list, file_list->priv->thumbnailer_state.current_pos);
1830 break;
1831
1832 case THUMBNAILER_PHASE_UPDATE_UPWARD:
1833 scan = file_list->priv->thumbnailer_state.current_item;
1834 pos = file_list->priv->thumbnailer_state.current_pos;
1835 while (scan && (pos >= file_list->priv->thumbnailer_state.first_visibile - N_CREATEAHEAD)) {
1836 gboolean requested_action_performed;
1837
1838 file_data = scan->data;
1839 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
1840 if (thumb_data == NULL) {
1841 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_COMPLETED;
1842 return FALSE;
1843 }
1844
1845 if (pos >= file_list->priv->thumbnailer_state.first_visibile - N_VIEWAHEAD)
1846 requested_action_performed = thumb_data->thumb_loaded;
1847 else
1848 requested_action_performed = thumb_data->thumb_created;
1849
1850 if (! requested_action_performed && can_create_file_thumbnail (file_data, thumb_data, current_time, young_file_found)) {
1851 /* found a thumbnail to load */
1852 file_list->priv->thumbnailer_state.current_item = scan;
1853 file_list->priv->thumbnailer_state.current_pos = pos;
1854 *new_pos = pos;
1855 return FALSE;
1856 }
1857
1858 pos--;
1859 scan = scan->prev;
1860 }
1861
1862 /* No thumbnail to load, terminate the process. */
1863 file_list->priv->thumbnailer_state.phase = THUMBNAILER_PHASE_COMPLETED;
1864 break;
1865
1866 case THUMBNAILER_PHASE_COMPLETED:
1867 return FALSE;
1868 }
1869
1870 return iterate_again;
1871 }
1872
1873
1874 static void
_gth_file_list_update_next_thumb(GthFileList * file_list)1875 _gth_file_list_update_next_thumb (GthFileList *file_list)
1876 {
1877 int new_pos;
1878 GTimeVal current_time;
1879 gboolean young_file_found;
1880 ThumbnailJob *job;
1881
1882 /* give priority to any other operation, the thumbnailer will restart
1883 * again soon after the operation terminates. */
1884 if (file_list->priv->queue != NULL) {
1885 if (file_list->priv->update_event != 0)
1886 g_source_remove (file_list->priv->update_event);
1887 file_list->priv->update_event = g_idle_add (update_thumbs_stopped, file_list);
1888 return;
1889 }
1890
1891 if (file_list->priv->cancelling)
1892 return;
1893
1894 if (! file_list->priv->load_thumbs) {
1895 _gth_file_list_thumbs_completed (file_list);
1896 return;
1897 }
1898
1899 /* find the new thumbnail to load */
1900
1901 new_pos = -1;
1902 g_get_current_time (¤t_time);
1903 young_file_found = FALSE;
1904 while (_gth_file_list_thumbnailer_iterate (file_list, &new_pos, ¤t_time, &young_file_found))
1905 /* void */;
1906
1907 if (file_list->priv->thumbnailer_state.phase == THUMBNAILER_PHASE_COMPLETED) {
1908 _gth_file_list_thumbs_completed (file_list);
1909 if (young_file_found && (file_list->priv->restart_thumb_update == 0))
1910 file_list->priv->restart_thumb_update = g_timeout_add (RESTART_LOADING_THUMBS_DELAY, restart_thumb_update_cb, file_list);
1911 return;
1912 }
1913
1914 g_assert (file_list->priv->thumbnailer_state.current_item != NULL);
1915
1916 job = g_new0 (ThumbnailJob, 1);
1917 job->file_list = g_object_ref (file_list);
1918 job->loader = g_object_ref (file_list->priv->thumb_loader);
1919 job->cancellable = g_cancellable_new ();
1920 job->file_data = g_object_ref (file_list->priv->thumbnailer_state.current_item->data);
1921 job->pos = file_list->priv->thumbnailer_state.current_pos;
1922 job->update_in_view = (job->pos >= (file_list->priv->thumbnailer_state.first_visibile - N_VIEWAHEAD)) && (job->pos <= (file_list->priv->thumbnailer_state.last_visible + N_VIEWAHEAD));
1923
1924 #if 0
1925 g_print ("%d in [%d, %d] => %d\n",
1926 job->pos,
1927 (file_list->priv->thumbnailer_state.first_visibile - N_VIEWAHEAD),
1928 (file_list->priv->thumbnailer_state.last_visible + N_VIEWAHEAD),
1929 job->update_in_view);
1930 #endif
1931
1932 _gth_file_list_update_thumb (file_list, job);
1933 }
1934
1935
1936 static void
_gth_file_list_exec_next_op(GthFileList * file_list)1937 _gth_file_list_exec_next_op (GthFileList *file_list)
1938 {
1939 GList *first;
1940 GthFileListOp *op;
1941 gboolean exec_next_op = TRUE;
1942
1943 if (file_list->priv->queue == NULL) {
1944 start_update_next_thumb (file_list);
1945 return;
1946 }
1947
1948 first = file_list->priv->queue;
1949 file_list->priv->queue = g_list_remove_link (file_list->priv->queue, first);
1950
1951 op = first->data;
1952
1953 switch (op->type) {
1954 case GTH_FILE_LIST_OP_TYPE_SET_FILES:
1955 gfl_set_files (file_list, op->file_list);
1956 break;
1957 case GTH_FILE_LIST_OP_TYPE_ADD_FILES:
1958 gfl_add_files (file_list, op->file_list, op->position);
1959 break;
1960 case GTH_FILE_LIST_OP_TYPE_DELETE_FILES:
1961 gfl_delete_files (file_list, op->files);
1962 break;
1963 case GTH_FILE_LIST_OP_TYPE_UPDATE_FILES:
1964 gfl_update_files (file_list, op->file_list);
1965 break;
1966 case GTH_FILE_LIST_OP_TYPE_UPDATE_EMBLEMS:
1967 gfl_update_emblems (file_list, op->file_list);
1968 break;
1969 case GTH_FILE_LIST_OP_TYPE_ENABLE_THUMBS:
1970 gfl_enable_thumbs (file_list, op->ival);
1971 exec_next_op = FALSE;
1972 break;
1973 case GTH_FILE_LIST_OP_TYPE_CLEAR_FILES:
1974 gfl_clear_list (file_list, op->sval);
1975 break;
1976 case GTH_FILE_LIST_OP_TYPE_SET_FILTER:
1977 gfl_set_filter (file_list, op->filter);
1978 break;
1979 case GTH_FILE_LIST_OP_TYPE_SET_SORT_FUNC:
1980 gfl_set_sort_func (file_list, op->cmp_func, op->inverse_sort);
1981 break;
1982 case GTH_FILE_LIST_OP_TYPE_RENAME_FILE:
1983 gfl_rename_file (file_list, op->file, op->file_data);
1984 break;
1985 case GTH_FILE_LIST_OP_TYPE_MAKE_FILE_VISIBLE:
1986 gfl_make_file_visible (file_list, op->file);
1987 break;
1988 case GTH_FILE_LIST_OP_TYPE_RESTORE_STATE:
1989 gfl_restore_state (file_list, op->selected, op->vscroll);
1990 break;
1991 default:
1992 exec_next_op = FALSE;
1993 break;
1994 }
1995
1996 gth_file_list_op_free (op);
1997 g_list_free (first);
1998
1999 if (exec_next_op)
2000 _gth_file_list_exec_next_op (file_list);
2001 }
2002
2003
2004 int
gth_file_list_first_file(GthFileList * file_list,gboolean skip_broken,gboolean only_selected)2005 gth_file_list_first_file (GthFileList *file_list,
2006 gboolean skip_broken,
2007 gboolean only_selected)
2008 {
2009 GList *files;
2010 GList *scan;
2011 int pos;
2012
2013 files = _gth_file_list_get_visibles (file_list);
2014
2015 pos = 0;
2016 for (scan = files; scan; scan = scan->next, pos++) {
2017 GthFileData *file_data = scan->data;
2018 ThumbData *thumb_data;
2019
2020 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
2021 if (skip_broken && thumb_data->error)
2022 continue;
2023 if (only_selected && ! gth_file_selection_is_selected (GTH_FILE_SELECTION (file_list->priv->view), pos))
2024 continue;
2025
2026 return pos;
2027 }
2028
2029 return -1;
2030 }
2031
2032
2033 int
gth_file_list_last_file(GthFileList * file_list,gboolean skip_broken,gboolean only_selected)2034 gth_file_list_last_file (GthFileList *file_list,
2035 gboolean skip_broken,
2036 gboolean only_selected)
2037 {
2038 GList *files;
2039 GList *scan;
2040 int pos;
2041
2042 files = _gth_file_list_get_visibles (file_list);
2043
2044 pos = g_list_length (files) - 1;
2045 if (pos < 0)
2046 return -1;
2047
2048 for (scan = g_list_nth (files, pos); scan; scan = scan->prev, pos--) {
2049 GthFileData *file_data = scan->data;
2050 ThumbData *thumb_data;
2051
2052 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
2053 if (skip_broken && thumb_data->error)
2054 continue;
2055 if (only_selected && ! gth_file_selection_is_selected (GTH_FILE_SELECTION (file_list->priv->view), pos))
2056 continue;
2057
2058 return pos;
2059 }
2060
2061 return -1;
2062 }
2063
2064
2065 int
gth_file_list_next_file(GthFileList * file_list,int pos,gboolean skip_broken,gboolean only_selected,gboolean wrap)2066 gth_file_list_next_file (GthFileList *file_list,
2067 int pos,
2068 gboolean skip_broken,
2069 gboolean only_selected,
2070 gboolean wrap)
2071 {
2072 GList *files;
2073 GList *scan;
2074
2075 files = _gth_file_list_get_visibles (file_list);
2076
2077 pos++;
2078 if (pos >= 0)
2079 scan = g_list_nth (files, pos);
2080 else if (wrap)
2081 scan = g_list_first (files);
2082 else
2083 scan = NULL;
2084
2085 for (/* void */; scan; scan = scan->next, pos++) {
2086 GthFileData *file_data = scan->data;
2087 ThumbData *thumb_data;
2088
2089 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
2090 if (skip_broken && thumb_data->error)
2091 continue;
2092 if (only_selected && ! gth_file_selection_is_selected (GTH_FILE_SELECTION (file_list->priv->view), pos))
2093 continue;
2094
2095 break;
2096 }
2097
2098 return (scan != NULL) ? pos : -1;
2099 }
2100
2101
2102 int
gth_file_list_prev_file(GthFileList * file_list,int pos,gboolean skip_broken,gboolean only_selected,gboolean wrap)2103 gth_file_list_prev_file (GthFileList *file_list,
2104 int pos,
2105 gboolean skip_broken,
2106 gboolean only_selected,
2107 gboolean wrap)
2108 {
2109 GList *files;
2110 GList *scan;
2111
2112 files = _gth_file_list_get_visibles (file_list);
2113
2114 pos--;
2115 if (pos >= 0)
2116 scan = g_list_nth (files, pos);
2117 else if (wrap) {
2118 pos = g_list_length (files) - 1;
2119 scan = g_list_nth (files, pos);
2120 }
2121 else
2122 scan = NULL;
2123
2124 for (/* void */; scan; scan = scan->prev, pos--) {
2125 GthFileData *file_data = scan->data;
2126 ThumbData *thumb_data;
2127
2128 thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_data->file);
2129 if (skip_broken && thumb_data->error)
2130 continue;
2131 if (only_selected && ! gth_file_selection_is_selected (GTH_FILE_SELECTION (file_list->priv->view), pos))
2132 continue;
2133
2134 break;
2135 }
2136
2137 return (scan != NULL) ? pos : -1;
2138 }
2139
2140
2141 void
gth_file_list_enable_drag_source(GthFileList * file_list,GdkDragAction actions)2142 gth_file_list_enable_drag_source (GthFileList *file_list,
2143 GdkDragAction actions)
2144 {
2145 GtkTargetList *target_list;
2146 GtkTargetEntry *targets;
2147 int n_targets;
2148
2149 target_list = gtk_target_list_new (NULL, 0);
2150 gtk_target_list_add_uri_targets (target_list, 0);
2151 gtk_target_list_add_text_targets (target_list, 0);
2152 targets = gtk_target_table_new_from_list (target_list, &n_targets);
2153 gth_file_view_enable_drag_source (GTH_FILE_VIEW (file_list->priv->view),
2154 GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
2155 targets,
2156 n_targets,
2157 actions);
2158
2159 gtk_target_list_unref (target_list);
2160 gtk_target_table_free (targets, n_targets);
2161 }
2162
2163
2164 void
gth_file_list_unset_drag_source(GthFileList * file_list)2165 gth_file_list_unset_drag_source (GthFileList *file_list)
2166 {
2167 gth_file_view_unset_drag_source (GTH_FILE_VIEW (file_list->priv->view));
2168 }
2169
2170
2171 void
gth_file_list_focus(GthFileList * file_list)2172 gth_file_list_focus (GthFileList *file_list)
2173 {
2174 GtkWidget *child;
2175
2176 child = gtk_stack_get_visible_child (GTK_STACK (file_list->priv->notebook));
2177 if (GTK_IS_BIN (child))
2178 child = gtk_bin_get_child (GTK_BIN (child));
2179 gtk_widget_grab_focus ((child != NULL) ? child : GTK_WIDGET (file_list));
2180 }
2181