r2313: 2002 -> 2003
[rox-filer.git] / ROX-Filer / src / pixmaps.c
blobeaced7456f680a40efc6ca1982daf53c40d757f5
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* pixmaps.c - code for handling pixbufs (despite the name!) */
24 #include "config.h"
25 #define PIXMAPS_C
27 /* Remove pixmaps from the cache when they haven't been accessed for
28 * this period of time (seconds).
31 #define PIXMAP_PURGE_TIME 1200
33 /* Image files smaller than this are loaded in the main process.
34 * Larger images are sent to a sub-process.
35 * If this is too small, then looking inside ~/.thumbnails will
36 * cause nasty effects ;-)
38 /* XXX: This is stupid! */
39 #define SMALL_IMAGE_THRESHOLD 50000
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
48 #include <gtk/gtk.h>
50 #include "global.h"
52 #include "fscache.h"
53 #include "support.h"
54 #include "gui_support.h"
55 #include "pixmaps.h"
56 #include "main.h"
57 #include "filer.h"
58 #include "dir.h"
59 #include "options.h"
60 #include "action.h"
62 GFSCache *pixmap_cache = NULL;
64 static const char * bad_xpm[] = {
65 "12 12 3 1",
66 " c #000000000000",
67 ". c #FFFF00000000",
68 "x c #FFFFFFFFFFFF",
69 " ",
70 " ..xxxxxx.. ",
71 " ...xxxx... ",
72 " x...xx...x ",
73 " xx......xx ",
74 " xxx....xxx ",
75 " xxx....xxx ",
76 " xx......xx ",
77 " x...xx...x ",
78 " ...xxxx... ",
79 " ..xxxxxx.. ",
80 " "};
82 MaskedPixmap *im_error;
83 MaskedPixmap *im_unknown;
84 MaskedPixmap *im_symlink;
86 MaskedPixmap *im_unmounted;
87 MaskedPixmap *im_mounted;
88 MaskedPixmap *im_appdir;
90 MaskedPixmap *im_dirs;
92 typedef struct _ChildThumbnail ChildThumbnail;
94 /* There is one of these for each active child process */
95 struct _ChildThumbnail {
96 gchar *path;
97 GFunc callback;
98 gpointer data;
101 static const char *stocks[] = {
102 ROX_STOCK_SHOW_DETAILS,
103 ROX_STOCK_SHOW_HIDDEN,
104 ROX_STOCK_MOUNT,
105 ROX_STOCK_MOUNTED,
108 static GtkIconSize mount_icon_size = -1;
110 /* Static prototypes */
112 static void load_default_pixmaps(void);
113 static gint purge(gpointer data);
114 static MaskedPixmap *image_from_file(const char *path);
115 static MaskedPixmap *get_bad_image(void);
116 static GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h);
117 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h);
118 static GdkPixbuf *get_thumbnail_for(const char *path);
119 static void thumbnail_child_done(ChildThumbnail *info);
120 static void child_create_thumbnail(const gchar *path);
121 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src, guint32 color,
122 guchar alpha);
123 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label);
125 /****************************************************************
126 * EXTERNAL INTERFACE *
127 ****************************************************************/
129 void pixmaps_init(void)
131 GtkIconFactory *factory;
132 int i;
134 gtk_widget_push_colormap(gdk_rgb_get_colormap());
136 pixmap_cache = g_fscache_new((GFSLoadFunc) image_from_file, NULL, NULL);
138 gtk_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_pixbuf_lit = create_spotlight_pixbuf(mp->huge_pixbuf,
237 0x000099, 128);
238 mp->huge_width = gdk_pixbuf_get_width(mp->huge_pixbuf);
239 mp->huge_height = gdk_pixbuf_get_height(mp->huge_pixbuf);
242 void pixmap_make_small(MaskedPixmap *mp)
244 if (mp->sm_pixbuf)
245 return;
247 g_return_if_fail(mp->src_pixbuf != NULL);
249 mp->sm_pixbuf = scale_pixbuf(mp->src_pixbuf, SMALL_WIDTH, SMALL_HEIGHT);
251 if (!mp->sm_pixbuf)
253 mp->sm_pixbuf = mp->src_pixbuf;
254 g_object_ref(mp->sm_pixbuf);
257 mp->sm_pixbuf_lit = create_spotlight_pixbuf(mp->sm_pixbuf,
258 0x000099, 128);
260 mp->sm_width = gdk_pixbuf_get_width(mp->sm_pixbuf);
261 mp->sm_height = gdk_pixbuf_get_height(mp->sm_pixbuf);
264 /* Load image 'path' in the background and insert into pixmap_cache.
265 * Call callback(data, path) when done (path is NULL => error).
266 * If the image is already uptodate, or being created already, calls the
267 * callback right away.
269 void pixmap_background_thumb(const gchar *path, GFunc callback, gpointer data)
271 gboolean found;
272 MaskedPixmap *image;
273 GdkPixbuf *pixbuf;
274 pid_t child;
275 ChildThumbnail *info;
277 image = g_fscache_lookup_full(pixmap_cache, path,
278 FSCACHE_LOOKUP_ONLY_NEW, &found);
280 if (found)
282 /* Thumbnail is known, or being created */
283 g_object_unref(image);
284 callback(data, NULL);
285 return;
288 g_return_if_fail(image == NULL);
290 pixbuf = get_thumbnail_for(path);
292 if (!pixbuf)
294 struct stat finfo;
296 /* If the image is small, load it now */
297 if (mc_stat(path, &finfo) != 0)
299 callback(data, NULL);
300 return;
303 if (finfo.st_size < SMALL_IMAGE_THRESHOLD)
305 pixbuf = gdk_pixbuf_new_from_file(path, NULL);
306 if (!pixbuf)
308 g_fscache_insert(pixmap_cache,
309 path, NULL, TRUE);
310 callback(data, NULL);
311 return;
316 if (pixbuf)
318 MaskedPixmap *image;
320 image = masked_pixmap_new(pixbuf);
321 gdk_pixbuf_unref(pixbuf);
322 g_fscache_insert(pixmap_cache, path, image, TRUE);
323 callback(data, (gchar *) path);
324 return;
327 /* Add an entry, set to NULL, so no-one else tries to load this
328 * image.
330 g_fscache_insert(pixmap_cache, path, NULL, TRUE);
332 child = fork();
334 if (child == -1)
336 delayed_error("fork(): %s", g_strerror(errno));
337 callback(data, NULL);
338 return;
341 if (child == 0)
343 /* We are the child process */
344 child_create_thumbnail(path);
345 _exit(0);
348 info = g_new(ChildThumbnail, 1);
349 info->path = g_strdup(path);
350 info->callback = callback;
351 info->data = data;
352 on_child_death(child, (CallbackFn) thumbnail_child_done, info);
355 /****************************************************************
356 * INTERNAL FUNCTIONS *
357 ****************************************************************/
359 /* Create a thumbnail file for this image.
360 * XXX: Thumbnails should be deleted somewhere!
362 static void save_thumbnail(const char *pathname, GdkPixbuf *full)
364 struct stat info;
365 gchar *path;
366 int original_width, original_height;
367 GString *to;
368 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
369 mode_t old_mask;
370 int name_len;
371 GdkPixbuf *thumb;
373 thumb = scale_pixbuf(full, 128, 128);
375 original_width = gdk_pixbuf_get_width(full);
376 original_height = gdk_pixbuf_get_height(full);
378 if (mc_stat(pathname, &info) != 0)
379 return;
381 swidth = g_strdup_printf("%d", original_width);
382 sheight = g_strdup_printf("%d", original_height);
383 ssize = g_strdup_printf("%" SIZE_FMT, info.st_size);
384 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
386 path = pathdup(pathname);
387 uri = g_strconcat("file://", path, NULL);
388 md5 = md5_hash(uri);
389 g_free(path);
391 to = g_string_new(home_dir);
392 g_string_append(to, "/.thumbnails");
393 mkdir(to->str, 0700);
394 g_string_append(to, "/normal/");
395 mkdir(to->str, 0700);
396 g_string_append(to, md5);
397 name_len = to->len + 4; /* Truncate to this length when renaming */
398 g_string_append_printf(to, ".png.ROX-Filer-%ld", (long) getpid());
400 g_free(md5);
402 old_mask = umask(0077);
403 gdk_pixbuf_save(thumb, to->str, "png", NULL,
404 "tEXt::Thumb::Image::Width", swidth,
405 "tEXt::Thumb::Image::Height", sheight,
406 "tEXt::Thumb::Size", ssize,
407 "tEXt::Thumb::MTime", smtime,
408 "tEXt::Thumb::URI", uri,
409 "tEXt::Software", PROJECT,
410 NULL);
411 umask(old_mask);
413 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
414 * a race condition if two programs create the same thumb at
415 * once.
418 gchar *final;
420 final = g_strndup(to->str, name_len);
421 if (rename(to->str, final))
422 g_warning("Failed to rename '%s' to '%s': %s",
423 to->str, final, g_strerror(errno));
424 g_free(final);
427 g_string_free(to, TRUE);
428 g_free(swidth);
429 g_free(sheight);
430 g_free(ssize);
431 g_free(smtime);
432 g_free(uri);
435 /* Called in a subprocess. Load path and create the thumbnail
436 * file. Parent will notice when we die.
438 static void child_create_thumbnail(const gchar *path)
440 GdkPixbuf *image;
442 image = gdk_pixbuf_new_from_file(path, NULL);
444 if (image)
445 save_thumbnail(path, image);
447 /* (no need to unref, as we're about to exit) */
450 /* Called when the child process exits */
451 static void thumbnail_child_done(ChildThumbnail *info)
453 GdkPixbuf *thumb;
455 thumb = get_thumbnail_for(info->path);
457 if (thumb)
459 MaskedPixmap *image;
461 image = masked_pixmap_new(thumb);
462 g_object_unref(thumb);
464 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
465 g_object_unref(image);
467 info->callback(info->data, info->path);
469 else
470 info->callback(info->data, NULL);
472 g_free(info->path);
473 g_free(info);
476 /* Check if we have an up-to-date thumbnail for this image.
477 * If so, return it. Otherwise, returns NULL.
479 static GdkPixbuf *get_thumbnail_for(const char *pathname)
481 GdkPixbuf *thumb = NULL;
482 char *thumb_path, *md5, *uri, *path;
483 const char *ssize, *smtime;
484 struct stat info;
486 path = pathdup(pathname);
487 uri = g_strconcat("file://", path, NULL);
488 md5 = md5_hash(uri);
489 g_free(uri);
491 thumb_path = g_strdup_printf("%s/.thumbnails/normal/%s.png",
492 home_dir, md5);
493 g_free(md5);
495 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
496 if (!thumb)
497 goto err;
499 /* Note that these don't need freeing... */
500 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
501 if (!ssize)
502 goto err;
504 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
505 if (!smtime)
506 goto err;
508 if (mc_stat(path, &info) != 0)
509 goto err;
511 if (info.st_mtime != atol(smtime) || info.st_size != atol(ssize))
512 goto err;
514 goto out;
515 err:
516 if (thumb)
517 gdk_pixbuf_unref(thumb);
518 thumb = NULL;
519 out:
520 g_free(path);
521 g_free(thumb_path);
522 return thumb;
525 /* Load the image 'path' and return a pointer to the resulting
526 * MaskedPixmap. NULL on failure.
527 * Doesn't check for thumbnails (this is for small icons).
529 static MaskedPixmap *image_from_file(const char *path)
531 GdkPixbuf *pixbuf;
532 MaskedPixmap *image;
533 GError *error = NULL;
535 pixbuf = gdk_pixbuf_new_from_file(path, &error);
536 if (!pixbuf)
538 g_warning("%s\n", error->message);
539 g_error_free(error);
540 return NULL;
543 image = masked_pixmap_new(pixbuf);
545 gdk_pixbuf_unref(pixbuf);
547 return image;
550 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
551 * If src is small enough, then ref it and return that.
553 static GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
555 int w, h;
557 w = gdk_pixbuf_get_width(src);
558 h = gdk_pixbuf_get_height(src);
560 if (w <= max_w && h <= max_h)
562 gdk_pixbuf_ref(src);
563 return src;
565 else
567 float scale_x = ((float) w) / max_w;
568 float scale_y = ((float) h) / max_h;
569 float scale = MAX(scale_x, scale_y);
570 int dest_w = w / scale;
571 int dest_h = h / scale;
573 return gdk_pixbuf_scale_simple(src,
574 MAX(dest_w, 1),
575 MAX(dest_h, 1),
576 GDK_INTERP_BILINEAR);
580 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
581 * If src is that size or bigger, then ref it and return that.
583 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
585 int w, h;
587 w = gdk_pixbuf_get_width(src);
588 h = gdk_pixbuf_get_height(src);
590 if (w == 0 || h == 0 || (w >= max_w && h >= max_h))
592 gdk_pixbuf_ref(src);
593 return src;
595 else
597 float scale_x = max_w / ((float) w);
598 float scale_y = max_h / ((float) h);
599 float scale = MIN(scale_x, scale_y);
601 return gdk_pixbuf_scale_simple(src,
602 w * scale,
603 h * scale,
604 GDK_INTERP_BILINEAR);
608 /* Return a pointer to the (static) bad image. The ref counter will ensure
609 * that the image is never freed.
611 static MaskedPixmap *get_bad_image(void)
613 GdkPixbuf *bad;
614 MaskedPixmap *mp;
616 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
617 mp = masked_pixmap_new(bad);
618 gdk_pixbuf_unref(bad);
620 return mp;
623 /* Called now and then to clear out old pixmaps */
624 static gint purge(gpointer data)
626 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
628 return TRUE;
631 static gpointer parent_class;
633 static void masked_pixmap_finialize(GObject *object)
635 MaskedPixmap *mp = (MaskedPixmap *) object;
637 if (mp->src_pixbuf)
639 g_object_unref(mp->src_pixbuf);
640 mp->src_pixbuf = NULL;
643 if (mp->huge_pixbuf)
645 g_object_unref(mp->huge_pixbuf);
646 mp->huge_pixbuf = NULL;
648 if (mp->huge_pixbuf_lit)
650 g_object_unref(mp->huge_pixbuf_lit);
651 mp->huge_pixbuf_lit = NULL;
654 if (mp->pixbuf)
656 g_object_unref(mp->pixbuf);
657 mp->pixbuf = NULL;
659 if (mp->pixbuf_lit)
661 g_object_unref(mp->pixbuf_lit);
662 mp->pixbuf_lit = NULL;
665 if (mp->sm_pixbuf)
667 g_object_unref(mp->sm_pixbuf);
668 mp->sm_pixbuf = NULL;
670 if (mp->sm_pixbuf_lit)
672 g_object_unref(mp->sm_pixbuf_lit);
673 mp->sm_pixbuf_lit = NULL;
676 G_OBJECT_CLASS(parent_class)->finalize(object);
679 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
681 GObjectClass *object = (GObjectClass *) gclass;
683 parent_class = g_type_class_peek_parent(gclass);
685 object->finalize = masked_pixmap_finialize;
688 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
690 MaskedPixmap *mp = (MaskedPixmap *) object;
692 mp->src_pixbuf = NULL;
694 mp->huge_pixbuf = NULL;
695 mp->huge_pixbuf_lit = NULL;
696 mp->huge_width = -1;
697 mp->huge_height = -1;
699 mp->pixbuf = NULL;
700 mp->pixbuf_lit = NULL;
701 mp->width = -1;
702 mp->height = -1;
704 mp->sm_pixbuf = NULL;
705 mp->sm_pixbuf_lit = NULL;
706 mp->sm_width = -1;
707 mp->sm_height = -1;
710 static GType masked_pixmap_get_type(void)
712 static GType type = 0;
714 if (!type)
716 static const GTypeInfo info =
718 sizeof (MaskedPixmapClass),
719 NULL, /* base_init */
720 NULL, /* base_finalise */
721 masked_pixmap_class_init,
722 NULL, /* class_finalise */
723 NULL, /* class_data */
724 sizeof(MaskedPixmap),
725 0, /* n_preallocs */
726 masked_pixmap_init
729 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
730 &info, 0);
733 return type;
736 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
738 MaskedPixmap *mp;
739 GdkPixbuf *src_pixbuf, *normal_pixbuf;
741 g_return_val_if_fail(full_size != NULL, NULL);
743 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
744 g_return_val_if_fail(src_pixbuf != NULL, NULL);
746 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
747 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
749 mp = g_object_new(masked_pixmap_get_type(), NULL);
751 mp->src_pixbuf = src_pixbuf;
753 mp->pixbuf = normal_pixbuf;
754 mp->pixbuf_lit = create_spotlight_pixbuf(normal_pixbuf, 0x000099, 128);
755 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
756 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
758 return mp;
761 /* Stolen from eel...and modified to colourize the pixbuf.
762 * 'alpha' is the transparency of 'color' (0xRRGGBB):
763 * 0 = fully opaque, 255 = fully transparent.
765 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src,
766 guint32 color,
767 guchar alpha)
769 GdkPixbuf *dest;
770 int i, j;
771 int width, height, has_alpha, src_row_stride, dst_row_stride;
772 guchar *target_pixels, *original_pixels;
773 guchar *pixsrc, *pixdest;
774 guchar r, g, b;
775 gint n_channels;
777 n_channels = gdk_pixbuf_get_n_channels(src);
778 has_alpha = gdk_pixbuf_get_has_alpha(src);
779 width = gdk_pixbuf_get_width(src);
780 height = gdk_pixbuf_get_height(src);
782 g_return_val_if_fail(gdk_pixbuf_get_colorspace(src) ==
783 GDK_COLORSPACE_RGB, NULL);
784 g_return_val_if_fail((!has_alpha && n_channels == 3) ||
785 (has_alpha && n_channels == 4), NULL);
786 g_return_val_if_fail(gdk_pixbuf_get_bits_per_sample(src) == 8, NULL);
788 dest = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), has_alpha,
789 gdk_pixbuf_get_bits_per_sample(src),
790 width, height);
792 dst_row_stride = gdk_pixbuf_get_rowstride(dest);
793 src_row_stride = gdk_pixbuf_get_rowstride(src);
794 target_pixels = gdk_pixbuf_get_pixels(dest);
795 original_pixels = gdk_pixbuf_get_pixels(src);
797 r = (color & 0xff0000) >> 16;
798 g = (color & 0xff00) >> 8;
799 b = color & 0xff;
801 for (i = 0; i < height; i++)
803 gint tmp;
805 pixdest = target_pixels + i * dst_row_stride;
806 pixsrc = original_pixels + i * src_row_stride;
807 for (j = 0; j < width; j++)
809 tmp = (*pixsrc++ * alpha + r * (255 - alpha)) / 255;
810 *pixdest++ = (guchar) MIN(255, tmp);
811 tmp = (*pixsrc++ * alpha + g * (255 - alpha)) / 255;
812 *pixdest++ = (guchar) MIN(255, tmp);
813 tmp = (*pixsrc++ * alpha + b * (255 - alpha)) / 255;
814 *pixdest++ = (guchar) MIN(255, tmp);
815 if (has_alpha)
816 *pixdest++ = *pixsrc++;
820 return dest;
823 /* Load all the standard pixmaps. Also sets the default window icon. */
824 static void load_default_pixmaps(void)
826 GdkPixbuf *pixbuf;
827 GError *error = NULL;
829 im_error = mp_from_stock(GTK_STOCK_DIALOG_WARNING,
830 GTK_ICON_SIZE_DIALOG);
831 im_unknown = mp_from_stock(GTK_STOCK_DIALOG_QUESTION,
832 GTK_ICON_SIZE_DIALOG);
833 im_symlink = load_pixmap("symlink");
835 im_unmounted = mp_from_stock(ROX_STOCK_MOUNT, mount_icon_size);
836 im_mounted = mp_from_stock(ROX_STOCK_MOUNTED, mount_icon_size);
837 im_appdir = load_pixmap("application");
839 im_dirs = load_pixmap("dirs");
841 pixbuf = gdk_pixbuf_new_from_file(
842 make_path(app_dir, ".DirIcon"), &error);
843 if (pixbuf)
845 GList *icon_list;
847 icon_list = g_list_append(NULL, pixbuf);
848 gtk_window_set_default_icon_list(icon_list);
849 g_list_free(icon_list);
851 g_object_unref(G_OBJECT(pixbuf));
853 else
855 g_warning("%s\n", error->message);
856 g_error_free(error);
860 /* Also purges memory cache */
861 static void purge_disk_cache(GtkWidget *button, gpointer data)
863 char *path;
864 GList *list = NULL;
865 DIR *dir;
866 struct dirent *ent;
868 g_fscache_purge(pixmap_cache, 0);
870 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
872 dir = opendir(path);
873 if (!dir)
875 report_error(_("Can't delete thumbnails in %s:\n%s"),
876 path, g_strerror(errno));
877 goto out;
880 while ((ent = readdir(dir)))
882 if (ent->d_name[0] == '.')
883 continue;
884 list = g_list_prepend(list,
885 g_strconcat(path, ent->d_name, NULL));
888 closedir(dir);
890 if (list)
892 action_delete(list);
893 destroy_glist(&list);
895 else
896 info_message(_("There are no thumbnails to delete"));
897 out:
898 g_free(path);
901 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
903 GtkWidget *button, *align;
905 g_return_val_if_fail(option == NULL, NULL);
907 align = gtk_alignment_new(0, 0.5, 0, 0);
908 button = button_new_mixed(GTK_STOCK_CLEAR,
909 _("Purge thumbnails disk cache"));
910 gtk_container_add(GTK_CONTAINER(align), button);
911 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
913 return g_list_append(NULL, align);