1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3 nemo-thumbnail-cache.h: Thumbnail code for icon factory.
4
5 Copyright (C) 2000, 2001 Eazel, Inc.
6 Copyright (C) 2002, 2003 Red Hat, Inc.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (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 GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public
19 License along with this program; if not, write to the
20 Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
21 Boston, MA 02110-1335, USA.
22
23 Author: Andy Hertzfeld <andy@eazel.com>
24 */
25
26 #include <config.h>
27 #include "nemo-thumbnails.h"
28
29 #define GNOME_DESKTOP_USE_UNSTABLE_API
30
31 #include "nemo-directory-notify.h"
32 #include "nemo-global-preferences.h"
33 #include "nemo-file-utilities.h"
34 #include <math.h>
35 #include <eel/eel-graphic-effects.h>
36 #include <eel/eel-string.h>
37 #include <eel/eel-debug.h>
38 #include <eel/eel-vfs-extensions.h>
39 #include <gtk/gtk.h>
40 #include <errno.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <sys/wait.h>
44 #include <unistd.h>
45 #include <signal.h>
46 #include <libcinnamon-desktop/gnome-desktop-thumbnail.h>
47
48 #define DEBUG_FLAG NEMO_DEBUG_THUMBNAILS
49 #include <libnemo-private/nemo-debug.h>
50
51 #include "nemo-file-private.h"
52
53 /* Should never be a reasonable actual mtime */
54 #define INVALID_MTIME 0
55
56 /* Cool-off period between last file modification time and thumbnail creation */
57 #define THUMBNAIL_CREATION_DELAY_SECS 3
58
59 #define NEMO_THUMBNAIL_FRAME_LEFT 3
60 #define NEMO_THUMBNAIL_FRAME_TOP 3
61 #define NEMO_THUMBNAIL_FRAME_RIGHT 3
62 #define NEMO_THUMBNAIL_FRAME_BOTTOM 3
63
64 /* structure used for making thumbnails, associating a uri with where the thumbnail is to be stored */
65
66 typedef struct {
67 char *image_uri;
68 char *mime_type;
69 time_t original_file_mtime;
70 gint throttle_count;
71 } NemoThumbnailInfo;
72
73 /*
74 * Thumbnail thread state.
75 */
76
77 /* The id of the idle handler used to start the thumbnail thread, or 0 if no
78 idle handler is currently registered. */
79 static guint thumbnail_thread_starter_id = 0;
80
81 /* Our mutex used when accessing data shared between the main thread and the
82 thumbnail thread, i.e. the thumbnail_thread_is_running flag and the
83 thumbnails_to_make list. */
84 static GMutex thumbnails_mutex;
85 static GCancellable *thumbnails_cancellable;
86
87 /* A flag to indicate whether a thumbnail thread is running, so we don't
88 start more than one. Lock thumbnails_mutex when accessing this. */
89 static volatile gboolean thumbnail_thread_is_running = FALSE;
90
91 /* The list of NemoThumbnailInfo structs containing information about the
92 thumbnails we are making. Lock thumbnails_mutex when accessing this. */
93 static volatile GQueue thumbnails_to_make = G_QUEUE_INIT;
94
95 /* Quickly check if uri is in thumbnails_to_make list */
96 static GHashTable *thumbnails_to_make_hash = NULL;
97
98 /* The currently thumbnailed icon. it also exists in the thumbnails_to_make list
99 * to avoid adding it again. Lock thumbnails_mutex when accessing this. */
100 static NemoThumbnailInfo *currently_thumbnailing = NULL;
101
102 static GnomeDesktopThumbnailFactory *thumbnail_factory = NULL;
103
104 static gboolean
get_file_mtime(const char * file_uri,time_t * mtime)105 get_file_mtime (const char *file_uri, time_t* mtime)
106 {
107 GFile *file;
108 GFileInfo *info;
109 gboolean ret;
110
111 ret = FALSE;
112 *mtime = INVALID_MTIME;
113
114 file = g_file_new_for_uri (file_uri);
115 info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, 0, NULL, NULL);
116 if (info) {
117 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED)) {
118 *mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
119 ret = TRUE;
120 }
121
122 g_object_unref (info);
123 }
124 g_object_unref (file);
125
126 return ret;
127 }
128
129 static void
free_thumbnail_info(NemoThumbnailInfo * info)130 free_thumbnail_info (NemoThumbnailInfo *info)
131 {
132 g_free (info->image_uri);
133 g_free (info->mime_type);
134 g_free (info);
135 }
136
137 static GnomeDesktopThumbnailFactory *
get_thumbnail_factory(void)138 get_thumbnail_factory (void)
139 {
140 if (thumbnail_factory == NULL) {
141 thumbnail_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE);
142 }
143
144 return thumbnail_factory;
145 }
146
147 static GdkPixbuf *
nemo_get_thumbnail_frame(void)148 nemo_get_thumbnail_frame (void)
149 {
150 static GdkPixbuf *thumbnail_frame = NULL;
151
152 if (thumbnail_frame == NULL) {
153 GInputStream *stream = g_resources_open_stream ("/org/nemo/icons/thumbnail_frame.png", 0, NULL);
154 if (stream != NULL) {
155 thumbnail_frame = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
156 g_object_unref (stream);
157 }
158 }
159
160 return thumbnail_frame;
161 }
162
163 void
nemo_thumbnail_frame_image(GdkPixbuf ** pixbuf)164 nemo_thumbnail_frame_image (GdkPixbuf **pixbuf)
165 {
166 GdkPixbuf *pixbuf_with_frame, *frame;
167 int left_offset, top_offset, right_offset, bottom_offset;
168
169 /* The pixbuf isn't already framed (i.e., it was not made by
170 * an old Nemo), so we must embed it in a frame.
171 */
172
173 frame = nemo_get_thumbnail_frame ();
174 if (frame == NULL) {
175 return;
176 }
177
178 left_offset = NEMO_THUMBNAIL_FRAME_LEFT;
179 top_offset = NEMO_THUMBNAIL_FRAME_TOP;
180 right_offset = NEMO_THUMBNAIL_FRAME_RIGHT;
181 bottom_offset = NEMO_THUMBNAIL_FRAME_BOTTOM;
182
183 pixbuf_with_frame = eel_embed_image_in_frame
184 (*pixbuf, frame,
185 left_offset, top_offset, right_offset, bottom_offset);
186 g_object_unref (*pixbuf);
187
188 *pixbuf = pixbuf_with_frame;
189 }
190
191 void
nemo_thumbnail_pad_top_and_bottom(GdkPixbuf ** pixbuf,gint extra_height)192 nemo_thumbnail_pad_top_and_bottom (GdkPixbuf **pixbuf,
193 gint extra_height)
194 {
195 GdkPixbuf *pixbuf_with_padding;
196 GdkRectangle rect;
197 GdkRGBA transparent = { 0, 0, 0, 0.0 };
198 cairo_surface_t *surface;
199 cairo_t *cr;
200 gint width, height;
201
202 width = gdk_pixbuf_get_width (*pixbuf);
203 height = gdk_pixbuf_get_height (*pixbuf);
204
205 surface = gdk_window_create_similar_image_surface (NULL,
206 CAIRO_FORMAT_ARGB32,
207 width,
208 height + extra_height,
209 0);
210
211 cr = cairo_create (surface);
212
213 rect.x = 0;
214 rect.y = 0;
215 rect.width = width;
216 rect.height = height + extra_height;
217
218 gdk_cairo_rectangle (cr, &rect);
219 gdk_cairo_set_source_rgba (cr, &transparent);
220 cairo_fill (cr);
221
222 gdk_cairo_set_source_pixbuf (cr,
223 *pixbuf,
224 0,
225 extra_height / 2);
226 cairo_paint (cr);
227
228 pixbuf_with_padding = gdk_pixbuf_get_from_surface (surface,
229 0,
230 0,
231 width,
232 height + extra_height);
233
234 g_object_unref (*pixbuf);
235 cairo_surface_destroy (surface);
236 cairo_destroy (cr);
237
238 *pixbuf = pixbuf_with_padding;
239 }
240
241 static GHashTable *
get_types_table(void)242 get_types_table (void)
243 {
244 static GHashTable *image_mime_types = NULL;
245 GSList *format_list, *l;
246 char **types;
247 int i;
248
249 if (image_mime_types == NULL) {
250 image_mime_types =
251 g_hash_table_new_full (g_str_hash, g_str_equal,
252 g_free, NULL);
253
254 format_list = gdk_pixbuf_get_formats ();
255 for (l = format_list; l; l = l->next) {
256 types = gdk_pixbuf_format_get_mime_types (l->data);
257
258 for (i = 0; types[i] != NULL; i++) {
259 g_hash_table_insert (image_mime_types,
260 types [i],
261 GUINT_TO_POINTER (1));
262 }
263
264 g_free (types);
265 }
266
267 g_slist_free (format_list);
268 }
269
270 return image_mime_types;
271 }
272
273 static gboolean
pixbuf_can_load_type(const char * mime_type)274 pixbuf_can_load_type (const char *mime_type)
275 {
276 GHashTable *image_mime_types;
277
278 image_mime_types = get_types_table ();
279 if (g_hash_table_lookup (image_mime_types, mime_type)) {
280 return TRUE;
281 }
282
283 return FALSE;
284 }
285
286 gboolean
nemo_can_thumbnail_internally(NemoFile * file)287 nemo_can_thumbnail_internally (NemoFile *file)
288 {
289 char *mime_type;
290 gboolean res;
291
292 mime_type = nemo_file_get_mime_type (file);
293 res = pixbuf_can_load_type (mime_type);
294 g_free (mime_type);
295 return res;
296 }
297
298 gboolean
nemo_thumbnail_is_mimetype_limited_by_size(const char * mime_type)299 nemo_thumbnail_is_mimetype_limited_by_size (const char *mime_type)
300 {
301 return pixbuf_can_load_type (mime_type);
302 }
303
304 gboolean
nemo_can_thumbnail(NemoFile * file)305 nemo_can_thumbnail (NemoFile *file)
306 {
307 GnomeDesktopThumbnailFactory *factory;
308 gboolean res;
309 char *uri;
310 time_t mtime;
311 char *mime_type;
312
313 uri = nemo_file_get_uri (file);
314 mime_type = nemo_file_get_mime_type (file);
315 mtime = nemo_file_get_mtime (file);
316
317 factory = get_thumbnail_factory ();
318 res = gnome_desktop_thumbnail_factory_can_thumbnail (factory,
319 uri,
320 mime_type,
321 mtime);
322 g_free (mime_type);
323 g_free (uri);
324
325 return res;
326 }
327
328 /***************************************************************************
329 * Thumbnail Thread Functions.
330 ***************************************************************************/
331
332 void
nemo_thumbnail_remove_from_queue(const char * file_uri)333 nemo_thumbnail_remove_from_queue (const char *file_uri)
334 {
335 GList *node;
336
337 if (DEBUGGING) {
338 g_message ("(Remove from queue) Locking mutex");
339 }
340
341 g_mutex_lock (&thumbnails_mutex);
342
343 /*********************************
344 * MUTEX LOCKED
345 *********************************/
346
347 if (thumbnails_to_make_hash) {
348 node = g_hash_table_lookup (thumbnails_to_make_hash, file_uri);
349
350 if (node && node->data != currently_thumbnailing) {
351 g_hash_table_remove (thumbnails_to_make_hash, file_uri);
352 free_thumbnail_info (node->data);
353 g_queue_delete_link ((GQueue *)&thumbnails_to_make, node);
354 }
355 }
356
357 /*********************************
358 * MUTEX UNLOCKED
359 *********************************/
360
361 if (DEBUGGING) {
362 g_message ("(Remove from queue) Unlocking mutex");
363 }
364
365 g_mutex_unlock (&thumbnails_mutex);
366 }
367
368 void
nemo_thumbnail_prioritize(const char * file_uri)369 nemo_thumbnail_prioritize (const char *file_uri)
370 {
371 GList *node;
372
373 if (DEBUGGING) {
374 g_message ("(Prioritize) Locking mutex");
375 }
376
377 g_mutex_lock (&thumbnails_mutex);
378
379 /*********************************
380 * MUTEX LOCKED
381 *********************************/
382
383 if (thumbnails_to_make_hash) {
384 node = g_hash_table_lookup (thumbnails_to_make_hash, file_uri);
385
386 if (node && node->data != currently_thumbnailing) {
387 g_queue_unlink ((GQueue *)&thumbnails_to_make, node);
388 g_queue_push_head_link ((GQueue *)&thumbnails_to_make, node);
389 }
390 }
391
392 /*********************************
393 * MUTEX UNLOCKED
394 *********************************/
395
396 if (DEBUGGING) {
397 g_message ("(Prioritize) Unlocking mutex");
398 }
399
400 g_mutex_unlock (&thumbnails_mutex);
401 }
402
403 /* This is a one-shot idle callback called from the main loop to call
404 notify_file_changed() for a thumbnail. It frees the uri afterwards.
405 We do this in an idle callback as I don't think nemo_file_changed() is
406 thread-safe. */
407 static gboolean
thumbnail_thread_notify_file_changed(gpointer image_uri)408 thumbnail_thread_notify_file_changed (gpointer image_uri)
409 {
410 NemoFile *file;
411
412 file = nemo_file_get_by_uri ((char *) image_uri);
413
414 if (DEBUGGING) {
415 g_message ("(Thumbnail Thread) Notifying file changed file:%p uri: %s", file, (char*) image_uri);
416 }
417
418 if (file != NULL) {
419 nemo_file_set_is_thumbnailing (file, FALSE);
420 nemo_file_invalidate_attributes (file,
421 NEMO_FILE_ATTRIBUTE_THUMBNAIL |
422 NEMO_FILE_ATTRIBUTE_INFO);
423 nemo_file_unref (file);
424 }
425 g_free (image_uri);
426 // g_printerr ("length: %d REMOVE\n" , g_hash_table_size (thumbnails_to_make_hash));
427
428 return FALSE;
429 }
430
431 static void
on_thumbnail_thread_finished(GObject * source,GAsyncResult * res,gpointer user_data)432 on_thumbnail_thread_finished (GObject *source,
433 GAsyncResult *res,
434 gpointer user_data)
435 {
436 GError *error;
437
438 error = NULL;
439 g_task_propagate_boolean (G_TASK (res), &error);
440
441 if (error != NULL) {
442 g_warning ("Error thumbnailing: %s", error->message);
443 g_error_free (error);
444 }
445
446 if (DEBUGGING) {
447 g_message ("(Main Thread) Thumbnail thread finished");
448 }
449
450 /* Thread is no longer running, no need to lock mutex */
451 thumbnail_thread_is_running = FALSE;
452 }
453
454 /* thumbnail_thread is invoked as a separate thread to to make thumbnails. */
455 static void
thumbnail_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)456 thumbnail_thread (GTask *task,
457 gpointer source_object,
458 gpointer task_data,
459 GCancellable *cancellable)
460 {
461 NemoThumbnailInfo *info = NULL;
462 GdkPixbuf *pixbuf;
463 time_t current_orig_mtime = 0;
464 time_t current_time;
465 GList *node;
466
467 /* We loop until there are no more thumbails to make, at which point
468 we exit the thread. */
469 for (;;) {
470 if (DEBUGGING) {
471 g_message ("(Thumbnail Thread) Locking mutex");
472 }
473
474 g_mutex_lock (&thumbnails_mutex);
475
476 /*********************************
477 * MUTEX LOCKED
478 *********************************/
479
480 /* Pop the last thumbnail we just made off the head of the
481 list and free it. I did this here so we only have to lock
482 the mutex once per thumbnail, rather than once before
483 creating it and once after.
484 Don't pop the thumbnail off the queue if the original file
485 mtime of the request changed. Then we need to redo the thumbnail.
486 */
487 if (currently_thumbnailing &&
488 currently_thumbnailing->original_file_mtime == current_orig_mtime) {
489 g_assert (info == currently_thumbnailing);
490
491 node = g_hash_table_lookup (thumbnails_to_make_hash, info->image_uri);
492
493 g_assert (node != NULL);
494
495 g_hash_table_remove (thumbnails_to_make_hash, info->image_uri);
496 free_thumbnail_info (info);
497 g_queue_delete_link ((GQueue *)&thumbnails_to_make, node);
498 }
499
500 currently_thumbnailing = NULL;
501
502 /* If there are no more thumbnails to make, reset the
503 thumbnail_thread_is_running flag, unlock the mutex, and
504 exit the thread. */
505 if (g_queue_is_empty ((GQueue *)&thumbnails_to_make)) {
506 if (DEBUGGING) {
507 g_message ("(Thumbnail Thread) Exiting");
508 }
509
510 g_mutex_unlock (&thumbnails_mutex);
511 g_task_return_boolean (task, TRUE);
512 return;
513 }
514
515 /* Get the next one to make. We leave it on the list until it
516 is created so the main thread doesn't add it again while we
517 are creating it. */
518 info = g_queue_peek_head ((GQueue *)&thumbnails_to_make);
519 currently_thumbnailing = info;
520 current_orig_mtime = info->original_file_mtime;
521
522 /*********************************
523 * MUTEX UNLOCKED
524 *********************************/
525
526 if (DEBUGGING) {
527 g_message ("(Thumbnail Thread) Unlocking mutex");
528 }
529
530 g_mutex_unlock (&thumbnails_mutex);
531
532 time (¤t_time);
533
534 /* Don't try to create a thumbnail if the file was modified recently.
535 This prevents constant re-thumbnailing of changing files. */
536 if (current_time < current_orig_mtime + (THUMBNAIL_CREATION_DELAY_SECS * info->throttle_count) &&
537 current_time >= current_orig_mtime) {
538 if (DEBUGGING) {
539 g_message ("(Thumbnail Thread) Skipping for %d seconds: %s",
540 THUMBNAIL_CREATION_DELAY_SECS * info->throttle_count,
541 info->image_uri);
542 }
543
544 /* Reschedule thumbnailing via a change notification */
545 g_timeout_add_seconds (THUMBNAIL_CREATION_DELAY_SECS * info->throttle_count, thumbnail_thread_notify_file_changed,
546 g_strdup (info->image_uri));
547 continue;
548 }
549
550 /* Create the thumbnail. */
551 if (DEBUGGING) {
552 g_message ("(Thumbnail Thread) Creating thumbnail: %s",
553 info->image_uri);
554 }
555
556 pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (thumbnail_factory,
557 info->image_uri,
558 info->mime_type);
559
560 if (pixbuf) {
561 gnome_desktop_thumbnail_factory_save_thumbnail (thumbnail_factory,
562 pixbuf,
563 info->image_uri,
564 current_orig_mtime);
565 g_object_unref (pixbuf);
566 } else {
567 gnome_desktop_thumbnail_factory_create_failed_thumbnail (thumbnail_factory,
568 info->image_uri,
569 current_orig_mtime);
570 }
571
572 /* We need to call nemo_file_changed(), but I don't think that is
573 thread safe. So add an idle handler and do it from the main loop. */
574 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
575 thumbnail_thread_notify_file_changed,
576 g_strdup (info->image_uri), NULL);
577 }
578
579 g_task_return_boolean (task, TRUE);
580 }
581
582 /* This function is added as a very low priority idle function to start the
583 thread to create any needed thumbnails. It is added with a very low priority
584 so that it doesn't delay showing the directory in the icon/list views.
585 We want to show the files in the directory as quickly as possible. */
586 static gboolean
thumbnail_thread_starter_cb(gpointer data)587 thumbnail_thread_starter_cb (gpointer data)
588 {
589 GTask *thumbnail_task;
590
591 /* Don't do this in thread, since g_object_ref is not threadsafe */
592 if (thumbnail_factory == NULL) {
593 thumbnail_factory = get_thumbnail_factory ();
594 }
595
596 thumbnails_cancellable = g_cancellable_new ();
597
598 if (DEBUGGING) {
599 g_message ("(Main Thread) Creating thumbnails thread");
600 }
601
602 thumbnail_task = g_task_new (thumbnail_factory,
603 thumbnails_cancellable,
604 on_thumbnail_thread_finished,
605 NULL);
606
607 /* We set a flag to indicate the thread is running, so we don't create
608 a new one. We don't need to lock a mutex here, as the thumbnail
609 thread isn't running yet. And we know we won't create the thread
610 twice, as we also check thumbnail_thread_starter_id before
611 scheduling this idle function. */
612 thumbnail_thread_is_running = TRUE;
613
614 g_task_run_in_thread (thumbnail_task, thumbnail_thread);
615 g_object_unref (thumbnail_task);
616
617 thumbnail_thread_starter_id = 0;
618 return FALSE;
619 }
620
621 void
nemo_create_thumbnail(NemoFile * file,gint throttle_count,gboolean prioritize)622 nemo_create_thumbnail (NemoFile *file,
623 gint throttle_count,
624 gboolean prioritize)
625 {
626 time_t file_mtime = 0;
627 NemoThumbnailInfo *info;
628 NemoThumbnailInfo *existing_info;
629 GList *existing, *node;
630
631 /* The gdk-pixbuf-thumbnailer tool has special hardcoded handling for recent: and trash: uris.
632 * we need to find the activation uri here instead */
633 if (nemo_file_is_in_favorites (file)) {
634 NemoFile *real_file;
635 gchar *uri;
636
637 uri = nemo_file_get_symbolic_link_target_uri (file);
638
639 real_file = nemo_file_get_by_uri (uri);
640 nemo_create_thumbnail (real_file, 0, FALSE);
641
642 nemo_file_unref (real_file);
643 return;
644 }
645
646 nemo_file_set_is_thumbnailing (file, TRUE);
647
648 info = g_new0 (NemoThumbnailInfo, 1);
649 info->image_uri = nemo_file_get_uri (file);
650
651 info->mime_type = nemo_file_get_mime_type (file); // info->image_uri = nemo_file_is_in_favorites (file) ? nemo_file_get_activation_uri (file) :
652 info->throttle_count = MIN (10, throttle_count);
653
654 /* Hopefully the NemoFile will already have the image file mtime,
655 so we can just use that. Otherwise we have to get it ourselves. */
656 if (file->details->got_file_info &&
657 file->details->file_info_is_up_to_date &&
658 file->details->mtime != 0) {
659 file_mtime = file->details->mtime;
660 } else {
661 get_file_mtime (info->image_uri, &file_mtime);
662 }
663
664 info->original_file_mtime = file_mtime;
665
666 if (DEBUGGING) {
667 g_message ("(Main Thread) Locking mutex");
668 }
669
670 g_mutex_lock (&thumbnails_mutex);
671
672 /*********************************
673 * MUTEX LOCKED
674 *********************************/
675
676 if (thumbnails_to_make_hash == NULL) {
677 thumbnails_to_make_hash = g_hash_table_new (g_str_hash,
678 g_str_equal);
679 }
680 // g_printerr ("length: %d ADD\n" , g_hash_table_size (thumbnails_to_make_hash));
681 /* Check if it is already in the list of thumbnails to make. */
682 existing = g_hash_table_lookup (thumbnails_to_make_hash, info->image_uri);
683
684 if (existing == NULL) {
685 /* Add the thumbnail to the list. */
686
687 if (DEBUGGING) {
688 g_message ("(Main Thread) Adding thumbnail: %s",
689 info->image_uri);
690 }
691
692 if (prioritize) {
693 g_queue_push_head ((GQueue *)&thumbnails_to_make, info);
694 node = g_queue_peek_head_link ((GQueue *)&thumbnails_to_make);
695 } else {
696 g_queue_push_tail ((GQueue *)&thumbnails_to_make, info);
697 node = g_queue_peek_tail_link ((GQueue *)&thumbnails_to_make);
698 }
699
700 g_hash_table_insert (thumbnails_to_make_hash,
701 info->image_uri,
702 node);
703
704 /* If the thumbnail thread isn't running, and we haven't
705 scheduled an idle function to start it up, do that now.
706 We don't want to start it until all the other work is done,
707 so the GUI will be updated as quickly as possible.*/
708
709 if (thumbnail_thread_is_running == FALSE &&
710 thumbnail_thread_starter_id == 0) {
711 thumbnail_thread_starter_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
712 thumbnail_thread_starter_cb,
713 NULL, NULL);
714 }
715 } else {
716 if (DEBUGGING) {
717 g_message ("(Main Thread) Updating non-current mtime: %s",
718 info->image_uri);
719 }
720
721 /* The file in the queue might need a new original mtime */
722 existing_info = existing->data;
723 existing_info->original_file_mtime = info->original_file_mtime;
724 free_thumbnail_info (info);
725
726 if (existing && existing->data != currently_thumbnailing) {
727 g_queue_unlink ((GQueue *)&thumbnails_to_make, existing);
728 g_queue_push_head_link ((GQueue *)&thumbnails_to_make, existing);
729 }
730 }
731
732 /*********************************
733 * MUTEX UNLOCKED
734 *********************************/
735
736 if (DEBUGGING) {
737 g_message ("(Main Thread) Unlocking mutex");
738 }
739
740 g_mutex_unlock (&thumbnails_mutex);
741 }
742
743 gboolean
nemo_thumbnail_factory_check_status(void)744 nemo_thumbnail_factory_check_status (void)
745 {
746 return gnome_desktop_thumbnail_cache_check_permissions (get_thumbnail_factory (), TRUE);
747 }
748