r4716: Fixes for the filter directories option: Initialize properly, inherit from
[rox-filer.git] / ROX-Filer / src / pixmaps.c
blobcd7e4752a4baed00241007a3e6d18b4ac62f1f64
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 dir = g_path_get_dirname(path);
298 /* If the image itself is in ~/.thumbnails, load it now
299 * (ie, don't create thumbnails for thumbnails!).
301 if (mc_stat(dir, &info1) != 0)
303 callback(data, NULL);
304 g_free(dir);
305 return;
307 g_free(dir);
309 if (mc_stat(make_path(home_dir, ".thumbnails/normal"),
310 &info2) == 0 &&
311 info1.st_dev == info2.st_dev &&
312 info1.st_ino == info2.st_ino)
314 pixbuf = rox_pixbuf_new_from_file_at_scale(path,
315 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
316 if (!pixbuf)
318 g_fscache_insert(pixmap_cache,
319 path, NULL, TRUE);
320 callback(data, NULL);
321 return;
326 if (pixbuf)
328 MaskedPixmap *image;
330 image = masked_pixmap_new(pixbuf);
331 gdk_pixbuf_unref(pixbuf);
332 g_fscache_insert(pixmap_cache, path, image, TRUE);
333 callback(data, (gchar *) path);
334 g_object_unref(G_OBJECT(image));
335 return;
338 type = type_from_path(path);
339 if (!type)
340 type = text_plain;
342 /* Add an entry, set to NULL, so no-one else tries to load this
343 * image.
345 g_fscache_insert(pixmap_cache, path, NULL, TRUE);
347 thumb_prog = thumbnail_program(type);
349 /* Only attempt to load 'images' types ourselves */
350 if (thumb_prog == NULL && strcmp(type->media_type, "image") != 0)
352 callback(data, NULL);
353 return; /* Don't know how to handle this type */
356 child = fork();
358 if (child == -1)
360 g_free(thumb_prog);
361 delayed_error("fork(): %s", g_strerror(errno));
362 callback(data, NULL);
363 return;
366 if (child == 0)
368 /* We are the child process. (We are sloppy with freeing
369 memory, but since we go away very quickly, that's ok.) */
370 if (thumb_prog)
372 DirItem *item;
374 item = diritem_new(g_basename(thumb_prog));
376 diritem_restat(thumb_prog, item, NULL);
377 if (item->flags & ITEM_FLAG_APPDIR)
378 thumb_prog = g_strconcat(thumb_prog, "/AppRun",
379 NULL);
381 execl(thumb_prog, thumb_prog, path,
382 thumbnail_path(path),
383 g_strdup_printf("%d", PIXMAP_THUMB_SIZE),
384 NULL);
385 _exit(1);
388 child_create_thumbnail(path);
389 _exit(0);
392 g_free(thumb_prog);
394 info = g_new(ChildThumbnail, 1);
395 info->path = g_strdup(path);
396 info->callback = callback;
397 info->data = data;
398 on_child_death(child, (CallbackFn) thumbnail_child_done, info);
401 /****************************************************************
402 * INTERNAL FUNCTIONS *
403 ****************************************************************/
405 /* Create a thumbnail file for this image */
406 static void save_thumbnail(const char *pathname, GdkPixbuf *full)
408 struct stat info;
409 gchar *path;
410 int original_width, original_height;
411 GString *to;
412 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
413 mode_t old_mask;
414 int name_len;
415 GdkPixbuf *thumb;
417 thumb = scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
419 original_width = gdk_pixbuf_get_width(full);
420 original_height = gdk_pixbuf_get_height(full);
422 if (mc_stat(pathname, &info) != 0)
423 return;
425 swidth = g_strdup_printf("%d", original_width);
426 sheight = g_strdup_printf("%d", original_height);
427 ssize = g_strdup_printf("%" SIZE_FMT, info.st_size);
428 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
430 path = pathdup(pathname);
431 uri = g_filename_to_uri(path, NULL, NULL);
432 if (!uri)
433 uri = g_strconcat("file://", path, NULL);
434 md5 = md5_hash(uri);
435 g_free(path);
437 to = g_string_new(home_dir);
438 g_string_append(to, "/.thumbnails");
439 mkdir(to->str, 0700);
440 g_string_append(to, "/normal/");
441 mkdir(to->str, 0700);
442 g_string_append(to, md5);
443 name_len = to->len + 4; /* Truncate to this length when renaming */
444 g_string_append_printf(to, ".png.ROX-Filer-%ld", (long) getpid());
446 g_free(md5);
448 old_mask = umask(0077);
449 gdk_pixbuf_save(thumb, to->str, "png", NULL,
450 "tEXt::Thumb::Image::Width", swidth,
451 "tEXt::Thumb::Image::Height", sheight,
452 "tEXt::Thumb::Size", ssize,
453 "tEXt::Thumb::MTime", smtime,
454 "tEXt::Thumb::URI", uri,
455 "tEXt::Software", PROJECT,
456 NULL);
457 umask(old_mask);
459 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
460 * a race condition if two programs create the same thumb at
461 * once.
464 gchar *final;
466 final = g_strndup(to->str, name_len);
467 if (rename(to->str, final))
468 g_warning("Failed to rename '%s' to '%s': %s",
469 to->str, final, g_strerror(errno));
470 g_free(final);
473 g_string_free(to, TRUE);
474 g_free(swidth);
475 g_free(sheight);
476 g_free(ssize);
477 g_free(smtime);
478 g_free(uri);
481 static gchar *thumbnail_path(const char *path)
483 gchar *uri, *md5;
484 GString *to;
485 gchar *ans;
487 uri = g_filename_to_uri(path, NULL, NULL);
488 if(!uri)
489 uri = g_strconcat("file://", path, NULL);
490 md5 = md5_hash(uri);
492 to = g_string_new(home_dir);
493 g_string_append(to, "/.thumbnails");
494 mkdir(to->str, 0700);
495 g_string_append(to, "/normal/");
496 mkdir(to->str, 0700);
497 g_string_append(to, md5);
498 g_string_append(to, ".png");
500 g_free(md5);
501 g_free(uri);
503 ans=to->str;
504 g_string_free(to, FALSE);
506 return ans;
509 /* Return a program to create thumbnails for files of this type.
510 * NULL to try to make it ourself (using gdk).
511 * g_free the result.
513 static gchar *thumbnail_program(MIME_type *type)
515 gchar *leaf;
516 gchar *path;
518 if (!type)
519 return NULL;
521 leaf = g_strconcat(type->media_type, "_", type->subtype, NULL);
522 path = choices_find_xdg_path_load(leaf, "MIME-thumb", SITE);
523 g_free(leaf);
524 if (path)
526 return path;
529 path = choices_find_xdg_path_load(type->media_type, "MIME-thumb",
530 SITE);
532 return path;
535 /* Called in a subprocess. Load path and create the thumbnail
536 * file. Parent will notice when we die.
538 static void child_create_thumbnail(const gchar *path)
540 GdkPixbuf *image;
542 image = rox_pixbuf_new_from_file_at_scale(path,
543 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
545 if (image)
546 save_thumbnail(path, image);
548 /* (no need to unref, as we're about to exit) */
551 /* Called when the child process exits */
552 static void thumbnail_child_done(ChildThumbnail *info)
554 GdkPixbuf *thumb;
556 thumb = get_thumbnail_for(info->path);
558 if (thumb)
560 MaskedPixmap *image;
562 image = masked_pixmap_new(thumb);
563 g_object_unref(thumb);
565 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
566 g_object_unref(image);
568 info->callback(info->data, info->path);
570 else
571 info->callback(info->data, NULL);
573 g_free(info->path);
574 g_free(info);
577 /* Check if we have an up-to-date thumbnail for this image.
578 * If so, return it. Otherwise, returns NULL.
580 static GdkPixbuf *get_thumbnail_for(const char *pathname)
582 GdkPixbuf *thumb = NULL;
583 char *thumb_path, *md5, *uri, *path;
584 const char *ssize, *smtime;
585 struct stat info;
586 time_t ttime, now;
588 path = pathdup(pathname);
589 uri = g_filename_to_uri(path, NULL, NULL);
590 if(!uri)
591 uri = g_strconcat("file://", path, NULL);
592 md5 = md5_hash(uri);
593 g_free(uri);
595 thumb_path = g_strdup_printf("%s/.thumbnails/normal/%s.png",
596 home_dir, md5);
597 g_free(md5);
599 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
600 if (!thumb)
601 goto err;
603 /* Note that these don't need freeing... */
604 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
605 if (!ssize)
606 goto err;
608 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
609 if (!smtime)
610 goto err;
612 if (mc_stat(path, &info) != 0)
613 goto err;
615 ttime=(time_t) atol(smtime);
616 time(&now);
617 if (info.st_mtime != ttime && now>ttime+PIXMAP_THUMB_TOO_OLD_TIME)
618 goto err;
620 if (info.st_size < atol(ssize))
621 goto err;
623 goto out;
624 err:
625 if (thumb)
626 gdk_pixbuf_unref(thumb);
627 thumb = NULL;
628 out:
629 g_free(path);
630 g_free(thumb_path);
631 return thumb;
634 /* Load the image 'path' and return a pointer to the resulting
635 * MaskedPixmap. NULL on failure.
636 * Doesn't check for thumbnails (this is for small icons).
638 static MaskedPixmap *image_from_file(const char *path)
640 GdkPixbuf *pixbuf;
641 MaskedPixmap *image;
642 GError *error = NULL;
644 pixbuf = gdk_pixbuf_new_from_file(path, &error);
645 if (!pixbuf)
647 g_warning("%s\n", error->message);
648 g_error_free(error);
649 return NULL;
652 image = masked_pixmap_new(pixbuf);
654 gdk_pixbuf_unref(pixbuf);
656 return image;
659 /* Load this icon named by this .desktop file from the current theme.
660 * NULL on failure.
662 static MaskedPixmap *image_from_desktop_file(const char *path)
664 GError *error = NULL;
665 MaskedPixmap *image = NULL;
666 char *icon = NULL;
668 icon = get_value_from_desktop_file(path,
669 "Desktop Entry", "Icon", &error);
670 if (error)
672 g_warning("Failed to parse .desktop file '%s':\n%s",
673 path, error->message);
674 goto err;
676 if (!icon)
677 goto err;
679 if (icon[0] == '/')
680 image = image_from_file(icon);
681 else
683 GdkPixbuf *pixbuf;
684 int tmp_fd;
685 char *extension;
687 /* For some unknown reason, some icon names have extensions.
688 * Remove them.
690 extension = strrchr(icon, '.');
691 if (extension && strcmp(extension, ".png") == 0)
693 *extension = '\0';
696 /* SVG reader is very noisy, so redirect stderr to stdout */
697 tmp_fd = dup(2);
698 dup2(1, 2);
699 pixbuf = gtk_icon_theme_load_icon(icon_theme, icon, HUGE_WIDTH,
700 0, NULL);
701 dup2(tmp_fd, 2);
702 close(tmp_fd);
704 if (pixbuf == NULL)
705 goto err; /* Might just not be in the theme */
707 image = masked_pixmap_new(pixbuf);
708 g_object_unref(pixbuf);
710 err:
711 if (error != NULL)
712 g_error_free(error);
713 if (icon != NULL)
714 g_free(icon);
715 return image;
718 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
719 * If src is small enough, then ref it and return that.
721 GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
723 int w, h;
725 w = gdk_pixbuf_get_width(src);
726 h = gdk_pixbuf_get_height(src);
728 if (w <= max_w && h <= max_h)
730 gdk_pixbuf_ref(src);
731 return src;
733 else
735 float scale_x = ((float) w) / max_w;
736 float scale_y = ((float) h) / max_h;
737 float scale = MAX(scale_x, scale_y);
738 int dest_w = w / scale;
739 int dest_h = h / scale;
741 return gdk_pixbuf_scale_simple(src,
742 MAX(dest_w, 1),
743 MAX(dest_h, 1),
744 GDK_INTERP_BILINEAR);
748 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
749 * If src is that size or bigger, then ref it and return that.
751 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
753 int w, h;
755 w = gdk_pixbuf_get_width(src);
756 h = gdk_pixbuf_get_height(src);
758 if (w == 0 || h == 0 || w >= max_w || h >= max_h)
760 gdk_pixbuf_ref(src);
761 return src;
763 else
765 float scale_x = max_w / ((float) w);
766 float scale_y = max_h / ((float) h);
767 float scale = MIN(scale_x, scale_y);
769 return gdk_pixbuf_scale_simple(src,
770 w * scale,
771 h * scale,
772 GDK_INTERP_BILINEAR);
776 /* Return a pointer to the (static) bad image. The ref counter will ensure
777 * that the image is never freed.
779 static MaskedPixmap *get_bad_image(void)
781 GdkPixbuf *bad;
782 MaskedPixmap *mp;
784 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
785 mp = masked_pixmap_new(bad);
786 gdk_pixbuf_unref(bad);
788 return mp;
791 /* Called now and then to clear out old pixmaps */
792 static gint purge(gpointer data)
794 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
796 return TRUE;
799 static gpointer parent_class;
801 static void masked_pixmap_finialize(GObject *object)
803 MaskedPixmap *mp = (MaskedPixmap *) object;
805 if (mp->src_pixbuf)
807 g_object_unref(mp->src_pixbuf);
808 mp->src_pixbuf = NULL;
811 if (mp->huge_pixbuf)
813 g_object_unref(mp->huge_pixbuf);
814 mp->huge_pixbuf = NULL;
816 if (mp->pixbuf)
818 g_object_unref(mp->pixbuf);
819 mp->pixbuf = NULL;
822 if (mp->sm_pixbuf)
824 g_object_unref(mp->sm_pixbuf);
825 mp->sm_pixbuf = NULL;
828 G_OBJECT_CLASS(parent_class)->finalize(object);
831 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
833 GObjectClass *object = (GObjectClass *) gclass;
835 parent_class = g_type_class_peek_parent(gclass);
837 object->finalize = masked_pixmap_finialize;
840 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
842 MaskedPixmap *mp = (MaskedPixmap *) object;
844 mp->src_pixbuf = NULL;
846 mp->huge_pixbuf = NULL;
847 mp->huge_width = -1;
848 mp->huge_height = -1;
850 mp->pixbuf = NULL;
851 mp->width = -1;
852 mp->height = -1;
854 mp->sm_pixbuf = NULL;
855 mp->sm_width = -1;
856 mp->sm_height = -1;
859 static GType masked_pixmap_get_type(void)
861 static GType type = 0;
863 if (!type)
865 static const GTypeInfo info =
867 sizeof (MaskedPixmapClass),
868 NULL, /* base_init */
869 NULL, /* base_finalise */
870 masked_pixmap_class_init,
871 NULL, /* class_finalise */
872 NULL, /* class_data */
873 sizeof(MaskedPixmap),
874 0, /* n_preallocs */
875 masked_pixmap_init
878 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
879 &info, 0);
882 return type;
885 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
887 MaskedPixmap *mp;
888 GdkPixbuf *src_pixbuf, *normal_pixbuf;
890 g_return_val_if_fail(full_size != NULL, NULL);
892 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
893 g_return_val_if_fail(src_pixbuf != NULL, NULL);
895 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
896 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
898 mp = g_object_new(masked_pixmap_get_type(), NULL);
900 mp->src_pixbuf = src_pixbuf;
902 mp->pixbuf = normal_pixbuf;
903 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
904 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
906 return mp;
909 /* Load all the standard pixmaps. Also sets the default window icon. */
910 static void load_default_pixmaps(void)
912 GdkPixbuf *pixbuf;
913 GError *error = NULL;
915 im_error = mp_from_stock(GTK_STOCK_DIALOG_WARNING,
916 GTK_ICON_SIZE_DIALOG);
917 im_unknown = mp_from_stock(GTK_STOCK_DIALOG_QUESTION,
918 GTK_ICON_SIZE_DIALOG);
919 im_symlink = load_pixmap("symlink");
921 im_unmounted = mp_from_stock(ROX_STOCK_MOUNT, mount_icon_size);
922 im_mounted = mp_from_stock(ROX_STOCK_MOUNTED, mount_icon_size);
923 im_appdir = load_pixmap("application");
924 im_xattr = load_pixmap("rox-xattr");
926 im_dirs = load_pixmap("dirs");
928 pixbuf = gdk_pixbuf_new_from_file(
929 make_path(app_dir, ".DirIcon"), &error);
930 if (pixbuf)
932 GList *icon_list;
934 icon_list = g_list_append(NULL, pixbuf);
935 gtk_window_set_default_icon_list(icon_list);
936 g_list_free(icon_list);
938 g_object_unref(G_OBJECT(pixbuf));
940 else
942 g_warning("%s\n", error->message);
943 g_error_free(error);
947 /* Also purges memory cache */
948 static void purge_disk_cache(GtkWidget *button, gpointer data)
950 char *path;
951 GList *list = NULL;
952 DIR *dir;
953 struct dirent *ent;
955 g_fscache_purge(pixmap_cache, 0);
957 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
959 dir = opendir(path);
960 if (!dir)
962 report_error(_("Can't delete thumbnails in %s:\n%s"),
963 path, g_strerror(errno));
964 goto out;
967 while ((ent = readdir(dir)))
969 if (ent->d_name[0] == '.')
970 continue;
971 list = g_list_prepend(list,
972 g_strconcat(path, ent->d_name, NULL));
975 closedir(dir);
977 if (list)
979 action_delete(list);
980 destroy_glist(&list);
982 else
983 info_message(_("There are no thumbnails to delete"));
984 out:
985 g_free(path);
988 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
990 GtkWidget *button, *align;
992 g_return_val_if_fail(option == NULL, NULL);
994 align = gtk_alignment_new(0, 0.5, 0, 0);
995 button = button_new_mixed(GTK_STOCK_CLEAR,
996 _("Purge thumbnails disk cache"));
997 gtk_container_add(GTK_CONTAINER(align), button);
998 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
1000 return g_list_append(NULL, align);