r2402: Changing the icon for a MIME type updates all filer windows.
[rox-filer.git] / ROX-Filer / src / pixmaps.c
blob16f73bec6e0731a8f40d82656e14074c1fcf1872
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 #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>
40 #include <gtk/gtk.h>
42 #include "global.h"
44 #include "fscache.h"
45 #include "support.h"
46 #include "gui_support.h"
47 #include "pixmaps.h"
48 #include "main.h"
49 #include "filer.h"
50 #include "dir.h"
51 #include "options.h"
52 #include "action.h"
54 GFSCache *pixmap_cache = NULL;
56 static const char * bad_xpm[] = {
57 "12 12 3 1",
58 " c #000000000000",
59 ". c #FFFF00000000",
60 "x c #FFFFFFFFFFFF",
61 " ",
62 " ..xxxxxx.. ",
63 " ...xxxx... ",
64 " x...xx...x ",
65 " xx......xx ",
66 " xxx....xxx ",
67 " xxx....xxx ",
68 " xx......xx ",
69 " x...xx...x ",
70 " ...xxxx... ",
71 " ..xxxxxx.. ",
72 " "};
74 MaskedPixmap *im_error;
75 MaskedPixmap *im_unknown;
76 MaskedPixmap *im_symlink;
78 MaskedPixmap *im_unmounted;
79 MaskedPixmap *im_mounted;
80 MaskedPixmap *im_appdir;
82 MaskedPixmap *im_dirs;
84 typedef struct _ChildThumbnail ChildThumbnail;
86 /* There is one of these for each active child process */
87 struct _ChildThumbnail {
88 gchar *path;
89 GFunc callback;
90 gpointer data;
93 static const char *stocks[] = {
94 ROX_STOCK_SHOW_DETAILS,
95 ROX_STOCK_SHOW_HIDDEN,
96 ROX_STOCK_MOUNT,
97 ROX_STOCK_MOUNTED,
100 static GtkIconSize mount_icon_size = -1;
102 /* Static prototypes */
104 static void load_default_pixmaps(void);
105 static gint purge(gpointer data);
106 static MaskedPixmap *image_from_file(const char *path);
107 static MaskedPixmap *get_bad_image(void);
108 static GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h);
109 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h);
110 static GdkPixbuf *get_thumbnail_for(const char *path);
111 static void thumbnail_child_done(ChildThumbnail *info);
112 static void child_create_thumbnail(const gchar *path);
113 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src, guint32 color,
114 guchar alpha);
115 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label);
117 /****************************************************************
118 * EXTERNAL INTERFACE *
119 ****************************************************************/
121 void pixmaps_init(void)
123 GtkIconFactory *factory;
124 int i;
126 gtk_widget_push_colormap(gdk_rgb_get_colormap());
128 pixmap_cache = g_fscache_new((GFSLoadFunc) image_from_file, NULL, NULL);
130 gtk_timeout_add(10000, purge, NULL);
132 factory = gtk_icon_factory_new();
133 for (i = 0; i < G_N_ELEMENTS(stocks); i++)
135 GdkPixbuf *pixbuf;
136 GError *error = NULL;
137 gchar *path;
138 GtkIconSet *iset;
139 const gchar *name = stocks[i];
141 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
142 pixbuf = gdk_pixbuf_new_from_file(path, &error);
143 if (!pixbuf)
145 g_warning("%s", error->message);
146 g_error_free(error);
147 pixbuf = gdk_pixbuf_new_from_xpm_data(bad_xpm);
149 g_free(path);
151 iset = gtk_icon_set_new_from_pixbuf(pixbuf);
152 g_object_unref(G_OBJECT(pixbuf));
153 gtk_icon_factory_add(factory, name, iset);
154 gtk_icon_set_unref(iset);
156 gtk_icon_factory_add_default(factory);
158 mount_icon_size = gtk_icon_size_register("rox-mount-size", 14, 14);
160 load_default_pixmaps();
162 option_register_widget("thumbs-purge-cache", thumbs_purge_cache);
165 /* Load image <appdir>/images/name.png.
166 * Always returns with a valid image.
168 MaskedPixmap *load_pixmap(const char *name)
170 guchar *path;
171 MaskedPixmap *retval;
173 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
174 retval = image_from_file(path);
175 g_free(path);
177 if (!retval)
178 retval = get_bad_image();
180 return retval;
183 /* Create a MaskedPixmap from a GTK stock ID. Always returns
184 * a valid image.
186 static MaskedPixmap *mp_from_stock(const char *stock_id, int size)
188 GtkIconSet *icon_set;
189 GdkPixbuf *pixbuf;
190 MaskedPixmap *retval;
192 icon_set = gtk_icon_factory_lookup_default(stock_id);
193 if (!icon_set)
194 return get_bad_image();
196 pixbuf = gtk_icon_set_render_icon(icon_set,
197 gtk_widget_get_default_style(), /* Gtk bug */
198 GTK_TEXT_DIR_LTR,
199 GTK_STATE_NORMAL,
200 size,
201 NULL,
202 NULL);
203 retval = masked_pixmap_new(pixbuf);
204 gdk_pixbuf_unref(pixbuf);
206 return retval;
209 void pixmap_make_huge(MaskedPixmap *mp)
211 if (mp->huge_pixbuf)
212 return;
214 g_return_if_fail(mp->src_pixbuf != NULL);
216 /* Limit to small size now, otherwise they get scaled up in mixed mode.
217 * Also looked ugly.
219 mp->huge_pixbuf = scale_pixbuf_up(mp->src_pixbuf,
220 SMALL_WIDTH, SMALL_HEIGHT);
222 if (!mp->huge_pixbuf)
224 mp->huge_pixbuf = mp->src_pixbuf;
225 g_object_ref(mp->huge_pixbuf);
228 mp->huge_pixbuf_lit = create_spotlight_pixbuf(mp->huge_pixbuf,
229 0x000099, 128);
230 mp->huge_width = gdk_pixbuf_get_width(mp->huge_pixbuf);
231 mp->huge_height = gdk_pixbuf_get_height(mp->huge_pixbuf);
234 void pixmap_make_small(MaskedPixmap *mp)
236 if (mp->sm_pixbuf)
237 return;
239 g_return_if_fail(mp->src_pixbuf != NULL);
241 mp->sm_pixbuf = scale_pixbuf(mp->src_pixbuf, SMALL_WIDTH, SMALL_HEIGHT);
243 if (!mp->sm_pixbuf)
245 mp->sm_pixbuf = mp->src_pixbuf;
246 g_object_ref(mp->sm_pixbuf);
249 mp->sm_pixbuf_lit = create_spotlight_pixbuf(mp->sm_pixbuf,
250 0x000099, 128);
252 mp->sm_width = gdk_pixbuf_get_width(mp->sm_pixbuf);
253 mp->sm_height = gdk_pixbuf_get_height(mp->sm_pixbuf);
256 /* Load image 'path' in the background and insert into pixmap_cache.
257 * Call callback(data, path) when done (path is NULL => error).
258 * If the image is already uptodate, or being created already, calls the
259 * callback right away.
261 void pixmap_background_thumb(const gchar *path, GFunc callback, gpointer data)
263 gboolean found;
264 MaskedPixmap *image;
265 GdkPixbuf *pixbuf;
266 pid_t child;
267 ChildThumbnail *info;
269 image = g_fscache_lookup_full(pixmap_cache, path,
270 FSCACHE_LOOKUP_ONLY_NEW, &found);
272 if (found)
274 /* Thumbnail is known, or being created */
275 g_object_unref(image);
276 callback(data, NULL);
277 return;
280 g_return_if_fail(image == NULL);
282 pixbuf = get_thumbnail_for(path);
284 if (!pixbuf)
286 struct stat info1, info2;
287 char *dir;
289 dir = g_path_get_dirname(path);
291 /* If the image itself is in ~/.thumbnails, load it now
292 * (ie, don't create thumbnails for thumbnails!).
294 if (mc_stat(dir, &info1) != 0)
296 callback(data, NULL);
297 g_free(dir);
298 return;
300 g_free(dir);
302 if (mc_stat(make_path(home_dir, ".thumbnails/normal"),
303 &info2) == 0 &&
304 info1.st_dev == info2.st_dev &&
305 info1.st_ino == info2.st_ino)
307 pixbuf = gdk_pixbuf_new_from_file(path, NULL);
308 if (!pixbuf)
310 g_fscache_insert(pixmap_cache,
311 path, NULL, TRUE);
312 callback(data, NULL);
313 return;
318 if (pixbuf)
320 MaskedPixmap *image;
322 image = masked_pixmap_new(pixbuf);
323 gdk_pixbuf_unref(pixbuf);
324 g_fscache_insert(pixmap_cache, path, image, TRUE);
325 callback(data, (gchar *) path);
326 return;
329 /* Add an entry, set to NULL, so no-one else tries to load this
330 * image.
332 g_fscache_insert(pixmap_cache, path, NULL, TRUE);
334 child = fork();
336 if (child == -1)
338 delayed_error("fork(): %s", g_strerror(errno));
339 callback(data, NULL);
340 return;
343 if (child == 0)
345 /* We are the child process */
346 child_create_thumbnail(path);
347 _exit(0);
350 info = g_new(ChildThumbnail, 1);
351 info->path = g_strdup(path);
352 info->callback = callback;
353 info->data = data;
354 on_child_death(child, (CallbackFn) thumbnail_child_done, info);
357 /****************************************************************
358 * INTERNAL FUNCTIONS *
359 ****************************************************************/
361 /* Create a thumbnail file for this image.
362 * XXX: Thumbnails should be deleted somewhere!
364 static void save_thumbnail(const char *pathname, GdkPixbuf *full)
366 struct stat info;
367 gchar *path;
368 int original_width, original_height;
369 GString *to;
370 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
371 mode_t old_mask;
372 int name_len;
373 GdkPixbuf *thumb;
375 thumb = scale_pixbuf(full, 128, 128);
377 original_width = gdk_pixbuf_get_width(full);
378 original_height = gdk_pixbuf_get_height(full);
380 if (mc_stat(pathname, &info) != 0)
381 return;
383 swidth = g_strdup_printf("%d", original_width);
384 sheight = g_strdup_printf("%d", original_height);
385 ssize = g_strdup_printf("%" SIZE_FMT, info.st_size);
386 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
388 path = pathdup(pathname);
389 uri = g_strconcat("file://", path, NULL);
390 md5 = md5_hash(uri);
391 g_free(path);
393 to = g_string_new(home_dir);
394 g_string_append(to, "/.thumbnails");
395 mkdir(to->str, 0700);
396 g_string_append(to, "/normal/");
397 mkdir(to->str, 0700);
398 g_string_append(to, md5);
399 name_len = to->len + 4; /* Truncate to this length when renaming */
400 g_string_append_printf(to, ".png.ROX-Filer-%ld", (long) getpid());
402 g_free(md5);
404 old_mask = umask(0077);
405 gdk_pixbuf_save(thumb, to->str, "png", NULL,
406 "tEXt::Thumb::Image::Width", swidth,
407 "tEXt::Thumb::Image::Height", sheight,
408 "tEXt::Thumb::Size", ssize,
409 "tEXt::Thumb::MTime", smtime,
410 "tEXt::Thumb::URI", uri,
411 "tEXt::Software", PROJECT,
412 NULL);
413 umask(old_mask);
415 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
416 * a race condition if two programs create the same thumb at
417 * once.
420 gchar *final;
422 final = g_strndup(to->str, name_len);
423 if (rename(to->str, final))
424 g_warning("Failed to rename '%s' to '%s': %s",
425 to->str, final, g_strerror(errno));
426 g_free(final);
429 g_string_free(to, TRUE);
430 g_free(swidth);
431 g_free(sheight);
432 g_free(ssize);
433 g_free(smtime);
434 g_free(uri);
437 /* Called in a subprocess. Load path and create the thumbnail
438 * file. Parent will notice when we die.
440 static void child_create_thumbnail(const gchar *path)
442 GdkPixbuf *image;
444 image = gdk_pixbuf_new_from_file(path, NULL);
446 if (image)
447 save_thumbnail(path, image);
449 /* (no need to unref, as we're about to exit) */
452 /* Called when the child process exits */
453 static void thumbnail_child_done(ChildThumbnail *info)
455 GdkPixbuf *thumb;
457 thumb = get_thumbnail_for(info->path);
459 if (thumb)
461 MaskedPixmap *image;
463 image = masked_pixmap_new(thumb);
464 g_object_unref(thumb);
466 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
467 g_object_unref(image);
469 info->callback(info->data, info->path);
471 else
472 info->callback(info->data, NULL);
474 g_free(info->path);
475 g_free(info);
478 /* Check if we have an up-to-date thumbnail for this image.
479 * If so, return it. Otherwise, returns NULL.
481 static GdkPixbuf *get_thumbnail_for(const char *pathname)
483 GdkPixbuf *thumb = NULL;
484 char *thumb_path, *md5, *uri, *path;
485 const char *ssize, *smtime;
486 struct stat info;
488 path = pathdup(pathname);
489 uri = g_strconcat("file://", path, NULL);
490 md5 = md5_hash(uri);
491 g_free(uri);
493 thumb_path = g_strdup_printf("%s/.thumbnails/normal/%s.png",
494 home_dir, md5);
495 g_free(md5);
497 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
498 if (!thumb)
499 goto err;
501 /* Note that these don't need freeing... */
502 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
503 if (!ssize)
504 goto err;
506 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
507 if (!smtime)
508 goto err;
510 if (mc_stat(path, &info) != 0)
511 goto err;
513 if (info.st_mtime != atol(smtime) || info.st_size != atol(ssize))
514 goto err;
516 goto out;
517 err:
518 if (thumb)
519 gdk_pixbuf_unref(thumb);
520 thumb = NULL;
521 out:
522 g_free(path);
523 g_free(thumb_path);
524 return thumb;
527 /* Load the image 'path' and return a pointer to the resulting
528 * MaskedPixmap. NULL on failure.
529 * Doesn't check for thumbnails (this is for small icons).
531 static MaskedPixmap *image_from_file(const char *path)
533 GdkPixbuf *pixbuf;
534 MaskedPixmap *image;
535 GError *error = NULL;
537 pixbuf = gdk_pixbuf_new_from_file(path, &error);
538 if (!pixbuf)
540 g_warning("%s\n", error->message);
541 g_error_free(error);
542 return NULL;
545 image = masked_pixmap_new(pixbuf);
547 gdk_pixbuf_unref(pixbuf);
549 return image;
552 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
553 * If src is small enough, then ref it and return that.
555 static GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
557 int w, h;
559 w = gdk_pixbuf_get_width(src);
560 h = gdk_pixbuf_get_height(src);
562 if (w <= max_w && h <= max_h)
564 gdk_pixbuf_ref(src);
565 return src;
567 else
569 float scale_x = ((float) w) / max_w;
570 float scale_y = ((float) h) / max_h;
571 float scale = MAX(scale_x, scale_y);
572 int dest_w = w / scale;
573 int dest_h = h / scale;
575 return gdk_pixbuf_scale_simple(src,
576 MAX(dest_w, 1),
577 MAX(dest_h, 1),
578 GDK_INTERP_BILINEAR);
582 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
583 * If src is that size or bigger, then ref it and return that.
585 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
587 int w, h;
589 w = gdk_pixbuf_get_width(src);
590 h = gdk_pixbuf_get_height(src);
592 if (w == 0 || h == 0 || w >= max_w || h >= max_h)
594 gdk_pixbuf_ref(src);
595 return src;
597 else
599 float scale_x = max_w / ((float) w);
600 float scale_y = max_h / ((float) h);
601 float scale = MIN(scale_x, scale_y);
603 return gdk_pixbuf_scale_simple(src,
604 w * scale,
605 h * scale,
606 GDK_INTERP_BILINEAR);
610 /* Return a pointer to the (static) bad image. The ref counter will ensure
611 * that the image is never freed.
613 static MaskedPixmap *get_bad_image(void)
615 GdkPixbuf *bad;
616 MaskedPixmap *mp;
618 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
619 mp = masked_pixmap_new(bad);
620 gdk_pixbuf_unref(bad);
622 return mp;
625 /* Called now and then to clear out old pixmaps */
626 static gint purge(gpointer data)
628 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
630 return TRUE;
633 static gpointer parent_class;
635 static void masked_pixmap_finialize(GObject *object)
637 MaskedPixmap *mp = (MaskedPixmap *) object;
639 if (mp->src_pixbuf)
641 g_object_unref(mp->src_pixbuf);
642 mp->src_pixbuf = NULL;
645 if (mp->huge_pixbuf)
647 g_object_unref(mp->huge_pixbuf);
648 mp->huge_pixbuf = NULL;
650 if (mp->huge_pixbuf_lit)
652 g_object_unref(mp->huge_pixbuf_lit);
653 mp->huge_pixbuf_lit = NULL;
656 if (mp->pixbuf)
658 g_object_unref(mp->pixbuf);
659 mp->pixbuf = NULL;
661 if (mp->pixbuf_lit)
663 g_object_unref(mp->pixbuf_lit);
664 mp->pixbuf_lit = NULL;
667 if (mp->sm_pixbuf)
669 g_object_unref(mp->sm_pixbuf);
670 mp->sm_pixbuf = NULL;
672 if (mp->sm_pixbuf_lit)
674 g_object_unref(mp->sm_pixbuf_lit);
675 mp->sm_pixbuf_lit = NULL;
678 G_OBJECT_CLASS(parent_class)->finalize(object);
681 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
683 GObjectClass *object = (GObjectClass *) gclass;
685 parent_class = g_type_class_peek_parent(gclass);
687 object->finalize = masked_pixmap_finialize;
690 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
692 MaskedPixmap *mp = (MaskedPixmap *) object;
694 mp->src_pixbuf = NULL;
696 mp->huge_pixbuf = NULL;
697 mp->huge_pixbuf_lit = NULL;
698 mp->huge_width = -1;
699 mp->huge_height = -1;
701 mp->pixbuf = NULL;
702 mp->pixbuf_lit = NULL;
703 mp->width = -1;
704 mp->height = -1;
706 mp->sm_pixbuf = NULL;
707 mp->sm_pixbuf_lit = NULL;
708 mp->sm_width = -1;
709 mp->sm_height = -1;
712 static GType masked_pixmap_get_type(void)
714 static GType type = 0;
716 if (!type)
718 static const GTypeInfo info =
720 sizeof (MaskedPixmapClass),
721 NULL, /* base_init */
722 NULL, /* base_finalise */
723 masked_pixmap_class_init,
724 NULL, /* class_finalise */
725 NULL, /* class_data */
726 sizeof(MaskedPixmap),
727 0, /* n_preallocs */
728 masked_pixmap_init
731 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
732 &info, 0);
735 return type;
738 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
740 MaskedPixmap *mp;
741 GdkPixbuf *src_pixbuf, *normal_pixbuf;
743 g_return_val_if_fail(full_size != NULL, NULL);
745 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
746 g_return_val_if_fail(src_pixbuf != NULL, NULL);
748 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
749 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
751 mp = g_object_new(masked_pixmap_get_type(), NULL);
753 mp->src_pixbuf = src_pixbuf;
755 mp->pixbuf = normal_pixbuf;
756 mp->pixbuf_lit = create_spotlight_pixbuf(normal_pixbuf, 0x000099, 128);
757 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
758 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
760 return mp;
763 /* Stolen from eel...and modified to colourize the pixbuf.
764 * 'alpha' is the transparency of 'color' (0xRRGGBB):
765 * 0 = fully opaque, 255 = fully transparent.
767 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src,
768 guint32 color,
769 guchar alpha)
771 GdkPixbuf *dest;
772 int i, j;
773 int width, height, has_alpha, src_row_stride, dst_row_stride;
774 guchar *target_pixels, *original_pixels;
775 guchar *pixsrc, *pixdest;
776 guchar r, g, b;
777 gint n_channels;
779 n_channels = gdk_pixbuf_get_n_channels(src);
780 has_alpha = gdk_pixbuf_get_has_alpha(src);
781 width = gdk_pixbuf_get_width(src);
782 height = gdk_pixbuf_get_height(src);
784 g_return_val_if_fail(gdk_pixbuf_get_colorspace(src) ==
785 GDK_COLORSPACE_RGB, NULL);
786 g_return_val_if_fail((!has_alpha && n_channels == 3) ||
787 (has_alpha && n_channels == 4), NULL);
788 g_return_val_if_fail(gdk_pixbuf_get_bits_per_sample(src) == 8, NULL);
790 dest = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), has_alpha,
791 gdk_pixbuf_get_bits_per_sample(src),
792 width, height);
794 dst_row_stride = gdk_pixbuf_get_rowstride(dest);
795 src_row_stride = gdk_pixbuf_get_rowstride(src);
796 target_pixels = gdk_pixbuf_get_pixels(dest);
797 original_pixels = gdk_pixbuf_get_pixels(src);
799 r = (color & 0xff0000) >> 16;
800 g = (color & 0xff00) >> 8;
801 b = color & 0xff;
803 for (i = 0; i < height; i++)
805 gint tmp;
807 pixdest = target_pixels + i * dst_row_stride;
808 pixsrc = original_pixels + i * src_row_stride;
809 for (j = 0; j < width; j++)
811 tmp = (*pixsrc++ * alpha + r * (255 - alpha)) / 255;
812 *pixdest++ = (guchar) MIN(255, tmp);
813 tmp = (*pixsrc++ * alpha + g * (255 - alpha)) / 255;
814 *pixdest++ = (guchar) MIN(255, tmp);
815 tmp = (*pixsrc++ * alpha + b * (255 - alpha)) / 255;
816 *pixdest++ = (guchar) MIN(255, tmp);
817 if (has_alpha)
818 *pixdest++ = *pixsrc++;
822 return dest;
825 /* Load all the standard pixmaps. Also sets the default window icon. */
826 static void load_default_pixmaps(void)
828 GdkPixbuf *pixbuf;
829 GError *error = NULL;
831 im_error = mp_from_stock(GTK_STOCK_DIALOG_WARNING,
832 GTK_ICON_SIZE_DIALOG);
833 im_unknown = mp_from_stock(GTK_STOCK_DIALOG_QUESTION,
834 GTK_ICON_SIZE_DIALOG);
835 im_symlink = load_pixmap("symlink");
837 im_unmounted = mp_from_stock(ROX_STOCK_MOUNT, mount_icon_size);
838 im_mounted = mp_from_stock(ROX_STOCK_MOUNTED, mount_icon_size);
839 im_appdir = load_pixmap("application");
841 im_dirs = load_pixmap("dirs");
843 pixbuf = gdk_pixbuf_new_from_file(
844 make_path(app_dir, ".DirIcon"), &error);
845 if (pixbuf)
847 GList *icon_list;
849 icon_list = g_list_append(NULL, pixbuf);
850 gtk_window_set_default_icon_list(icon_list);
851 g_list_free(icon_list);
853 g_object_unref(G_OBJECT(pixbuf));
855 else
857 g_warning("%s\n", error->message);
858 g_error_free(error);
862 /* Also purges memory cache */
863 static void purge_disk_cache(GtkWidget *button, gpointer data)
865 char *path;
866 GList *list = NULL;
867 DIR *dir;
868 struct dirent *ent;
870 g_fscache_purge(pixmap_cache, 0);
872 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
874 dir = opendir(path);
875 if (!dir)
877 report_error(_("Can't delete thumbnails in %s:\n%s"),
878 path, g_strerror(errno));
879 goto out;
882 while ((ent = readdir(dir)))
884 if (ent->d_name[0] == '.')
885 continue;
886 list = g_list_prepend(list,
887 g_strconcat(path, ent->d_name, NULL));
890 closedir(dir);
892 if (list)
894 action_delete(list);
895 destroy_glist(&list);
897 else
898 info_message(_("There are no thumbnails to delete"));
899 out:
900 g_free(path);
903 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
905 GtkWidget *button, *align;
907 g_return_val_if_fail(option == NULL, NULL);
909 align = gtk_alignment_new(0, 0.5, 0, 0);
910 button = button_new_mixed(GTK_STOCK_CLEAR,
911 _("Purge thumbnails disk cache"));
912 gtk_container_add(GTK_CONTAINER(align), button);
913 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
915 return g_list_append(NULL, align);