r4888: Don't try to thumbnail zero-byte files. They're either empty, or special and
[rox-filer.git] / ROX-Filer / src / pixmaps.c
blob830af19c869152beadeafbf7fb465b95edfc928e
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* pixmaps.c - code for handling pixbufs (despite the name!) */
22 #include "config.h"
23 #define PIXMAPS_C
25 /* Remove pixmaps from the cache when they haven't been accessed for
26 * this period of time (seconds).
29 #define PIXMAP_PURGE_TIME 1200
30 #define PIXMAP_THUMB_SIZE 128
31 #define PIXMAP_THUMB_TOO_OLD_TIME 5
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <string.h>
41 #include <gtk/gtk.h>
43 #include "global.h"
45 #include "fscache.h"
46 #include "support.h"
47 #include "gui_support.h"
48 #include "pixmaps.h"
49 #include "main.h"
50 #include "filer.h"
51 #include "dir.h"
52 #include "diritem.h"
53 #include "choices.h"
54 #include "options.h"
55 #include "action.h"
56 #include "type.h"
58 GFSCache *pixmap_cache = NULL;
59 GFSCache *desktop_icon_cache = NULL;
61 static const char * bad_xpm[] = {
62 "12 12 3 1",
63 " c #000000000000",
64 ". c #FFFF00000000",
65 "x c #FFFFFFFFFFFF",
66 " ",
67 " ..xxxxxx.. ",
68 " ...xxxx... ",
69 " x...xx...x ",
70 " xx......xx ",
71 " xxx....xxx ",
72 " xxx....xxx ",
73 " xx......xx ",
74 " x...xx...x ",
75 " ...xxxx... ",
76 " ..xxxxxx.. ",
77 " "};
79 MaskedPixmap *im_error;
80 MaskedPixmap *im_unknown;
81 MaskedPixmap *im_symlink;
83 MaskedPixmap *im_unmounted;
84 MaskedPixmap *im_mounted;
85 MaskedPixmap *im_appdir;
86 MaskedPixmap *im_xattr;
88 MaskedPixmap *im_dirs;
90 typedef struct _ChildThumbnail ChildThumbnail;
92 /* There is one of these for each active child process */
93 struct _ChildThumbnail {
94 gchar *path;
95 GFunc callback;
96 gpointer data;
99 static const char *stocks[] = {
100 ROX_STOCK_SHOW_DETAILS,
101 ROX_STOCK_SHOW_HIDDEN,
102 ROX_STOCK_SELECT,
103 ROX_STOCK_MOUNT,
104 ROX_STOCK_MOUNTED,
107 static GtkIconSize mount_icon_size = -1;
109 /* Static prototypes */
111 static void load_default_pixmaps(void);
112 static gint purge(gpointer data);
113 static MaskedPixmap *image_from_file(const char *path);
114 static MaskedPixmap *image_from_desktop_file(const char *path);
115 static MaskedPixmap *get_bad_image(void);
116 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h);
117 static GdkPixbuf *get_thumbnail_for(const char *path);
118 static void thumbnail_child_done(ChildThumbnail *info);
119 static void child_create_thumbnail(const gchar *path);
120 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label);
121 static gchar *thumbnail_path(const gchar *path);
122 static gchar *thumbnail_program(MIME_type *type);
124 /****************************************************************
125 * EXTERNAL INTERFACE *
126 ****************************************************************/
128 void pixmaps_init(void)
130 GtkIconFactory *factory;
131 int i;
133 gtk_widget_push_colormap(gdk_rgb_get_colormap());
135 pixmap_cache = g_fscache_new((GFSLoadFunc) image_from_file, NULL, NULL);
136 desktop_icon_cache = g_fscache_new((GFSLoadFunc) image_from_desktop_file, NULL, NULL);
138 g_timeout_add(10000, purge, NULL);
140 factory = gtk_icon_factory_new();
141 for (i = 0; i < G_N_ELEMENTS(stocks); i++)
143 GdkPixbuf *pixbuf;
144 GError *error = NULL;
145 gchar *path;
146 GtkIconSet *iset;
147 const gchar *name = stocks[i];
149 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
150 pixbuf = gdk_pixbuf_new_from_file(path, &error);
151 if (!pixbuf)
153 g_warning("%s", error->message);
154 g_error_free(error);
155 pixbuf = gdk_pixbuf_new_from_xpm_data(bad_xpm);
157 g_free(path);
159 iset = gtk_icon_set_new_from_pixbuf(pixbuf);
160 g_object_unref(G_OBJECT(pixbuf));
161 gtk_icon_factory_add(factory, name, iset);
162 gtk_icon_set_unref(iset);
164 gtk_icon_factory_add_default(factory);
166 mount_icon_size = gtk_icon_size_register("rox-mount-size", 14, 14);
168 load_default_pixmaps();
170 option_register_widget("thumbs-purge-cache", thumbs_purge_cache);
173 /* Load image <appdir>/images/name.png.
174 * Always returns with a valid image.
176 MaskedPixmap *load_pixmap(const char *name)
178 guchar *path;
179 MaskedPixmap *retval;
181 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
182 retval = image_from_file(path);
183 g_free(path);
185 if (!retval)
186 retval = get_bad_image();
188 return retval;
191 /* Create a MaskedPixmap from a GTK stock ID. Always returns
192 * a valid image.
194 static MaskedPixmap *mp_from_stock(const char *stock_id, int size)
196 GtkIconSet *icon_set;
197 GdkPixbuf *pixbuf;
198 MaskedPixmap *retval;
200 icon_set = gtk_icon_factory_lookup_default(stock_id);
201 if (!icon_set)
202 return get_bad_image();
204 pixbuf = gtk_icon_set_render_icon(icon_set,
205 gtk_widget_get_default_style(), /* Gtk bug */
206 GTK_TEXT_DIR_LTR,
207 GTK_STATE_NORMAL,
208 size,
209 NULL,
210 NULL);
211 retval = masked_pixmap_new(pixbuf);
212 gdk_pixbuf_unref(pixbuf);
214 return retval;
217 void pixmap_make_huge(MaskedPixmap *mp)
219 if (mp->huge_pixbuf)
220 return;
222 g_return_if_fail(mp->src_pixbuf != NULL);
224 /* Limit to small size now, otherwise they get scaled up in mixed mode.
225 * Also looked ugly.
227 mp->huge_pixbuf = scale_pixbuf_up(mp->src_pixbuf,
228 SMALL_WIDTH, SMALL_HEIGHT);
230 if (!mp->huge_pixbuf)
232 mp->huge_pixbuf = mp->src_pixbuf;
233 g_object_ref(mp->huge_pixbuf);
236 mp->huge_width = gdk_pixbuf_get_width(mp->huge_pixbuf);
237 mp->huge_height = gdk_pixbuf_get_height(mp->huge_pixbuf);
240 void pixmap_make_small(MaskedPixmap *mp)
242 if (mp->sm_pixbuf)
243 return;
245 g_return_if_fail(mp->src_pixbuf != NULL);
247 mp->sm_pixbuf = scale_pixbuf(mp->src_pixbuf, SMALL_WIDTH, SMALL_HEIGHT);
249 if (!mp->sm_pixbuf)
251 mp->sm_pixbuf = mp->src_pixbuf;
252 g_object_ref(mp->sm_pixbuf);
255 mp->sm_width = gdk_pixbuf_get_width(mp->sm_pixbuf);
256 mp->sm_height = gdk_pixbuf_get_height(mp->sm_pixbuf);
259 /* Load image 'path' in the background and insert into pixmap_cache.
260 * Call callback(data, path) when done (path is NULL => error).
261 * If the image is already uptodate, or being created already, calls the
262 * callback right away.
264 void pixmap_background_thumb(const gchar *path, GFunc callback, gpointer data)
266 gboolean found;
267 MaskedPixmap *image;
268 GdkPixbuf *pixbuf;
269 pid_t child;
270 ChildThumbnail *info;
271 MIME_type *type;
272 gchar *thumb_prog;
275 image = g_fscache_lookup_full(pixmap_cache, path,
276 FSCACHE_LOOKUP_ONLY_NEW, &found);
278 if (found)
280 /* Thumbnail is known, or being created */
281 if (image)
282 g_object_unref(image);
283 callback(data, NULL);
284 return;
287 g_return_if_fail(image == NULL);
289 pixbuf = get_thumbnail_for(path);
291 if (!pixbuf)
293 struct stat info1, info2;
294 char *dir;
296 // Skip zero-byte files. They're either empty, or special (may cause
297 // us to hang, e.g. /proc/kmsg).
298 if (mc_stat(path, &info1) == 0 && info1.st_size == 0) {
299 callback(data, NULL);
300 return;
303 dir = g_path_get_dirname(path);
305 /* If the image itself is in ~/.thumbnails, load it now
306 * (ie, don't create thumbnails for thumbnails!).
308 if (mc_stat(dir, &info1) != 0)
310 callback(data, NULL);
311 g_free(dir);
312 return;
314 g_free(dir);
316 if (mc_stat(make_path(home_dir, ".thumbnails/normal"),
317 &info2) == 0 &&
318 info1.st_dev == info2.st_dev &&
319 info1.st_ino == info2.st_ino)
321 pixbuf = rox_pixbuf_new_from_file_at_scale(path,
322 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
323 if (!pixbuf)
325 g_fscache_insert(pixmap_cache,
326 path, NULL, TRUE);
327 callback(data, NULL);
328 return;
333 if (pixbuf)
335 MaskedPixmap *image;
337 image = masked_pixmap_new(pixbuf);
338 gdk_pixbuf_unref(pixbuf);
339 g_fscache_insert(pixmap_cache, path, image, TRUE);
340 callback(data, (gchar *) path);
341 g_object_unref(G_OBJECT(image));
342 return;
345 type = type_from_path(path);
346 if (!type)
347 type = text_plain;
349 /* Add an entry, set to NULL, so no-one else tries to load this
350 * image.
352 g_fscache_insert(pixmap_cache, path, NULL, TRUE);
354 thumb_prog = thumbnail_program(type);
356 /* Only attempt to load 'images' types ourselves */
357 if (thumb_prog == NULL && strcmp(type->media_type, "image") != 0)
359 callback(data, NULL);
360 return; /* Don't know how to handle this type */
363 child = fork();
365 if (child == -1)
367 g_free(thumb_prog);
368 delayed_error("fork(): %s", g_strerror(errno));
369 callback(data, NULL);
370 return;
373 if (child == 0)
375 /* We are the child process. (We are sloppy with freeing
376 memory, but since we go away very quickly, that's ok.) */
377 if (thumb_prog)
379 DirItem *item;
381 item = diritem_new(g_basename(thumb_prog));
383 diritem_restat(thumb_prog, item, NULL);
384 if (item->flags & ITEM_FLAG_APPDIR)
385 thumb_prog = g_strconcat(thumb_prog, "/AppRun",
386 NULL);
388 execl(thumb_prog, thumb_prog, path,
389 thumbnail_path(path),
390 g_strdup_printf("%d", PIXMAP_THUMB_SIZE),
391 NULL);
392 _exit(1);
395 child_create_thumbnail(path);
396 _exit(0);
399 g_free(thumb_prog);
401 info = g_new(ChildThumbnail, 1);
402 info->path = g_strdup(path);
403 info->callback = callback;
404 info->data = data;
405 on_child_death(child, (CallbackFn) thumbnail_child_done, info);
408 /****************************************************************
409 * INTERNAL FUNCTIONS *
410 ****************************************************************/
412 /* Create a thumbnail file for this image */
413 static void save_thumbnail(const char *pathname, GdkPixbuf *full)
415 struct stat info;
416 gchar *path;
417 int original_width, original_height;
418 GString *to;
419 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
420 mode_t old_mask;
421 int name_len;
422 GdkPixbuf *thumb;
424 thumb = scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
426 original_width = gdk_pixbuf_get_width(full);
427 original_height = gdk_pixbuf_get_height(full);
429 if (mc_stat(pathname, &info) != 0)
430 return;
432 swidth = g_strdup_printf("%d", original_width);
433 sheight = g_strdup_printf("%d", original_height);
434 ssize = g_strdup_printf("%" SIZE_FMT, info.st_size);
435 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
437 path = pathdup(pathname);
438 uri = g_filename_to_uri(path, NULL, NULL);
439 if (!uri)
440 uri = g_strconcat("file://", path, NULL);
441 md5 = md5_hash(uri);
442 g_free(path);
444 to = g_string_new(home_dir);
445 g_string_append(to, "/.thumbnails");
446 mkdir(to->str, 0700);
447 g_string_append(to, "/normal/");
448 mkdir(to->str, 0700);
449 g_string_append(to, md5);
450 name_len = to->len + 4; /* Truncate to this length when renaming */
451 g_string_append_printf(to, ".png.ROX-Filer-%ld", (long) getpid());
453 g_free(md5);
455 old_mask = umask(0077);
456 gdk_pixbuf_save(thumb, to->str, "png", NULL,
457 "tEXt::Thumb::Image::Width", swidth,
458 "tEXt::Thumb::Image::Height", sheight,
459 "tEXt::Thumb::Size", ssize,
460 "tEXt::Thumb::MTime", smtime,
461 "tEXt::Thumb::URI", uri,
462 "tEXt::Software", PROJECT,
463 NULL);
464 umask(old_mask);
466 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
467 * a race condition if two programs create the same thumb at
468 * once.
471 gchar *final;
473 final = g_strndup(to->str, name_len);
474 if (rename(to->str, final))
475 g_warning("Failed to rename '%s' to '%s': %s",
476 to->str, final, g_strerror(errno));
477 g_free(final);
480 g_string_free(to, TRUE);
481 g_free(swidth);
482 g_free(sheight);
483 g_free(ssize);
484 g_free(smtime);
485 g_free(uri);
488 static gchar *thumbnail_path(const char *path)
490 gchar *uri, *md5;
491 GString *to;
492 gchar *ans;
494 uri = g_filename_to_uri(path, NULL, NULL);
495 if(!uri)
496 uri = g_strconcat("file://", path, NULL);
497 md5 = md5_hash(uri);
499 to = g_string_new(home_dir);
500 g_string_append(to, "/.thumbnails");
501 mkdir(to->str, 0700);
502 g_string_append(to, "/normal/");
503 mkdir(to->str, 0700);
504 g_string_append(to, md5);
505 g_string_append(to, ".png");
507 g_free(md5);
508 g_free(uri);
510 ans=to->str;
511 g_string_free(to, FALSE);
513 return ans;
516 /* Return a program to create thumbnails for files of this type.
517 * NULL to try to make it ourself (using gdk).
518 * g_free the result.
520 static gchar *thumbnail_program(MIME_type *type)
522 gchar *leaf;
523 gchar *path;
525 if (!type)
526 return NULL;
528 leaf = g_strconcat(type->media_type, "_", type->subtype, NULL);
529 path = choices_find_xdg_path_load(leaf, "MIME-thumb", SITE);
530 g_free(leaf);
531 if (path)
533 return path;
536 path = choices_find_xdg_path_load(type->media_type, "MIME-thumb",
537 SITE);
539 return path;
542 /* Called in a subprocess. Load path and create the thumbnail
543 * file. Parent will notice when we die.
545 static void child_create_thumbnail(const gchar *path)
547 GdkPixbuf *image;
549 image = rox_pixbuf_new_from_file_at_scale(path,
550 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
552 if (image)
553 save_thumbnail(path, image);
555 /* (no need to unref, as we're about to exit) */
558 /* Called when the child process exits */
559 static void thumbnail_child_done(ChildThumbnail *info)
561 GdkPixbuf *thumb;
563 thumb = get_thumbnail_for(info->path);
565 if (thumb)
567 MaskedPixmap *image;
569 image = masked_pixmap_new(thumb);
570 g_object_unref(thumb);
572 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
573 g_object_unref(image);
575 info->callback(info->data, info->path);
577 else
578 info->callback(info->data, NULL);
580 g_free(info->path);
581 g_free(info);
584 /* Check if we have an up-to-date thumbnail for this image.
585 * If so, return it. Otherwise, returns NULL.
587 static GdkPixbuf *get_thumbnail_for(const char *pathname)
589 GdkPixbuf *thumb = NULL;
590 char *thumb_path, *md5, *uri, *path;
591 const char *ssize, *smtime;
592 struct stat info;
593 time_t ttime, now;
595 path = pathdup(pathname);
596 uri = g_filename_to_uri(path, NULL, NULL);
597 if(!uri)
598 uri = g_strconcat("file://", path, NULL);
599 md5 = md5_hash(uri);
600 g_free(uri);
602 thumb_path = g_strdup_printf("%s/.thumbnails/normal/%s.png",
603 home_dir, md5);
604 g_free(md5);
606 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
607 if (!thumb)
608 goto err;
610 /* Note that these don't need freeing... */
611 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
612 if (!ssize)
613 goto err;
615 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
616 if (!smtime)
617 goto err;
619 if (mc_stat(path, &info) != 0)
620 goto err;
622 ttime=(time_t) atol(smtime);
623 time(&now);
624 if (info.st_mtime != ttime && now>ttime+PIXMAP_THUMB_TOO_OLD_TIME)
625 goto err;
627 if (info.st_size < atol(ssize))
628 goto err;
630 goto out;
631 err:
632 if (thumb)
633 gdk_pixbuf_unref(thumb);
634 thumb = NULL;
635 out:
636 g_free(path);
637 g_free(thumb_path);
638 return thumb;
641 /* Load the image 'path' and return a pointer to the resulting
642 * MaskedPixmap. NULL on failure.
643 * Doesn't check for thumbnails (this is for small icons).
645 static MaskedPixmap *image_from_file(const char *path)
647 GdkPixbuf *pixbuf;
648 MaskedPixmap *image;
649 GError *error = NULL;
651 pixbuf = gdk_pixbuf_new_from_file(path, &error);
652 if (!pixbuf)
654 g_warning("%s\n", error->message);
655 g_error_free(error);
656 return NULL;
659 image = masked_pixmap_new(pixbuf);
661 gdk_pixbuf_unref(pixbuf);
663 return image;
666 /* Load this icon named by this .desktop file from the current theme.
667 * NULL on failure.
669 static MaskedPixmap *image_from_desktop_file(const char *path)
671 GError *error = NULL;
672 MaskedPixmap *image = NULL;
673 char *icon = NULL;
675 icon = get_value_from_desktop_file(path,
676 "Desktop Entry", "Icon", &error);
677 if (error)
679 g_warning("Failed to parse .desktop file '%s':\n%s",
680 path, error->message);
681 goto err;
683 if (!icon)
684 goto err;
686 if (icon[0] == '/')
687 image = image_from_file(icon);
688 else
690 GdkPixbuf *pixbuf;
691 int tmp_fd;
692 char *extension;
694 /* For some unknown reason, some icon names have extensions.
695 * Remove them.
697 extension = strrchr(icon, '.');
698 if (extension && strcmp(extension, ".png") == 0)
700 *extension = '\0';
703 /* SVG reader is very noisy, so redirect stderr to stdout */
704 tmp_fd = dup(2);
705 dup2(1, 2);
706 pixbuf = gtk_icon_theme_load_icon(icon_theme, icon, HUGE_WIDTH,
707 0, NULL);
708 dup2(tmp_fd, 2);
709 close(tmp_fd);
711 if (pixbuf == NULL)
712 goto err; /* Might just not be in the theme */
714 image = masked_pixmap_new(pixbuf);
715 g_object_unref(pixbuf);
717 err:
718 if (error != NULL)
719 g_error_free(error);
720 if (icon != NULL)
721 g_free(icon);
722 return image;
725 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
726 * If src is small enough, then ref it and return that.
728 GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
730 int w, h;
732 w = gdk_pixbuf_get_width(src);
733 h = gdk_pixbuf_get_height(src);
735 if (w <= max_w && h <= max_h)
737 gdk_pixbuf_ref(src);
738 return src;
740 else
742 float scale_x = ((float) w) / max_w;
743 float scale_y = ((float) h) / max_h;
744 float scale = MAX(scale_x, scale_y);
745 int dest_w = w / scale;
746 int dest_h = h / scale;
748 return gdk_pixbuf_scale_simple(src,
749 MAX(dest_w, 1),
750 MAX(dest_h, 1),
751 GDK_INTERP_BILINEAR);
755 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
756 * If src is that size or bigger, then ref it and return that.
758 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
760 int w, h;
762 w = gdk_pixbuf_get_width(src);
763 h = gdk_pixbuf_get_height(src);
765 if (w == 0 || h == 0 || w >= max_w || h >= max_h)
767 gdk_pixbuf_ref(src);
768 return src;
770 else
772 float scale_x = max_w / ((float) w);
773 float scale_y = max_h / ((float) h);
774 float scale = MIN(scale_x, scale_y);
776 return gdk_pixbuf_scale_simple(src,
777 w * scale,
778 h * scale,
779 GDK_INTERP_BILINEAR);
783 /* Return a pointer to the (static) bad image. The ref counter will ensure
784 * that the image is never freed.
786 static MaskedPixmap *get_bad_image(void)
788 GdkPixbuf *bad;
789 MaskedPixmap *mp;
791 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
792 mp = masked_pixmap_new(bad);
793 gdk_pixbuf_unref(bad);
795 return mp;
798 /* Called now and then to clear out old pixmaps */
799 static gint purge(gpointer data)
801 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
803 return TRUE;
806 static gpointer parent_class;
808 static void masked_pixmap_finialize(GObject *object)
810 MaskedPixmap *mp = (MaskedPixmap *) object;
812 if (mp->src_pixbuf)
814 g_object_unref(mp->src_pixbuf);
815 mp->src_pixbuf = NULL;
818 if (mp->huge_pixbuf)
820 g_object_unref(mp->huge_pixbuf);
821 mp->huge_pixbuf = NULL;
823 if (mp->pixbuf)
825 g_object_unref(mp->pixbuf);
826 mp->pixbuf = NULL;
829 if (mp->sm_pixbuf)
831 g_object_unref(mp->sm_pixbuf);
832 mp->sm_pixbuf = NULL;
835 G_OBJECT_CLASS(parent_class)->finalize(object);
838 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
840 GObjectClass *object = (GObjectClass *) gclass;
842 parent_class = g_type_class_peek_parent(gclass);
844 object->finalize = masked_pixmap_finialize;
847 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
849 MaskedPixmap *mp = (MaskedPixmap *) object;
851 mp->src_pixbuf = NULL;
853 mp->huge_pixbuf = NULL;
854 mp->huge_width = -1;
855 mp->huge_height = -1;
857 mp->pixbuf = NULL;
858 mp->width = -1;
859 mp->height = -1;
861 mp->sm_pixbuf = NULL;
862 mp->sm_width = -1;
863 mp->sm_height = -1;
866 static GType masked_pixmap_get_type(void)
868 static GType type = 0;
870 if (!type)
872 static const GTypeInfo info =
874 sizeof (MaskedPixmapClass),
875 NULL, /* base_init */
876 NULL, /* base_finalise */
877 masked_pixmap_class_init,
878 NULL, /* class_finalise */
879 NULL, /* class_data */
880 sizeof(MaskedPixmap),
881 0, /* n_preallocs */
882 masked_pixmap_init
885 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
886 &info, 0);
889 return type;
892 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
894 MaskedPixmap *mp;
895 GdkPixbuf *src_pixbuf, *normal_pixbuf;
897 g_return_val_if_fail(full_size != NULL, NULL);
899 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
900 g_return_val_if_fail(src_pixbuf != NULL, NULL);
902 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
903 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
905 mp = g_object_new(masked_pixmap_get_type(), NULL);
907 mp->src_pixbuf = src_pixbuf;
909 mp->pixbuf = normal_pixbuf;
910 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
911 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
913 return mp;
916 /* Load all the standard pixmaps. Also sets the default window icon. */
917 static void load_default_pixmaps(void)
919 GdkPixbuf *pixbuf;
920 GError *error = NULL;
922 im_error = mp_from_stock(GTK_STOCK_DIALOG_WARNING,
923 GTK_ICON_SIZE_DIALOG);
924 im_unknown = mp_from_stock(GTK_STOCK_DIALOG_QUESTION,
925 GTK_ICON_SIZE_DIALOG);
926 im_symlink = load_pixmap("symlink");
928 im_unmounted = mp_from_stock(ROX_STOCK_MOUNT, mount_icon_size);
929 im_mounted = mp_from_stock(ROX_STOCK_MOUNTED, mount_icon_size);
930 im_appdir = load_pixmap("application");
931 im_xattr = load_pixmap("rox-xattr");
933 im_dirs = load_pixmap("dirs");
935 pixbuf = gdk_pixbuf_new_from_file(
936 make_path(app_dir, ".DirIcon"), &error);
937 if (pixbuf)
939 GList *icon_list;
941 icon_list = g_list_append(NULL, pixbuf);
942 gtk_window_set_default_icon_list(icon_list);
943 g_list_free(icon_list);
945 g_object_unref(G_OBJECT(pixbuf));
947 else
949 g_warning("%s\n", error->message);
950 g_error_free(error);
954 /* Also purges memory cache */
955 static void purge_disk_cache(GtkWidget *button, gpointer data)
957 char *path;
958 GList *list = NULL;
959 DIR *dir;
960 struct dirent *ent;
962 g_fscache_purge(pixmap_cache, 0);
964 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
966 dir = opendir(path);
967 if (!dir)
969 report_error(_("Can't delete thumbnails in %s:\n%s"),
970 path, g_strerror(errno));
971 goto out;
974 while ((ent = readdir(dir)))
976 if (ent->d_name[0] == '.')
977 continue;
978 list = g_list_prepend(list,
979 g_strconcat(path, ent->d_name, NULL));
982 closedir(dir);
984 if (list)
986 action_delete(list);
987 destroy_glist(&list);
989 else
990 info_message(_("There are no thumbnails to delete"));
991 out:
992 g_free(path);
995 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
997 GtkWidget *button, *align;
999 g_return_val_if_fail(option == NULL, NULL);
1001 align = gtk_alignment_new(0, 0.5, 0, 0);
1002 button = button_new_mixed(GTK_STOCK_CLEAR,
1003 _("Purge thumbnails disk cache"));
1004 gtk_container_add(GTK_CONTAINER(align), button);
1005 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
1007 return g_list_append(NULL, align);