r3119: Updated manual.
[rox-filer.git] / ROX-Filer / src / pixmaps.c
blob90f6277a950828bb61591693278c6449ded31a9d
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
32 #define PIXMAP_THUMB_SIZE 128
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <string.h>
42 #include <gtk/gtk.h>
44 #include "global.h"
46 #include "fscache.h"
47 #include "support.h"
48 #include "gui_support.h"
49 #include "pixmaps.h"
50 #include "main.h"
51 #include "filer.h"
52 #include "dir.h"
53 #include "choices.h"
54 #include "options.h"
55 #include "action.h"
56 #include "type.h"
58 GFSCache *pixmap_cache = NULL;
60 static const char * bad_xpm[] = {
61 "12 12 3 1",
62 " c #000000000000",
63 ". c #FFFF00000000",
64 "x c #FFFFFFFFFFFF",
65 " ",
66 " ..xxxxxx.. ",
67 " ...xxxx... ",
68 " x...xx...x ",
69 " xx......xx ",
70 " xxx....xxx ",
71 " xxx....xxx ",
72 " xx......xx ",
73 " x...xx...x ",
74 " ...xxxx... ",
75 " ..xxxxxx.. ",
76 " "};
78 MaskedPixmap *im_error;
79 MaskedPixmap *im_unknown;
80 MaskedPixmap *im_symlink;
82 MaskedPixmap *im_unmounted;
83 MaskedPixmap *im_mounted;
84 MaskedPixmap *im_appdir;
86 MaskedPixmap *im_dirs;
88 typedef struct _ChildThumbnail ChildThumbnail;
90 /* There is one of these for each active child process */
91 struct _ChildThumbnail {
92 gchar *path;
93 GFunc callback;
94 gpointer data;
97 static const char *stocks[] = {
98 ROX_STOCK_SHOW_DETAILS,
99 ROX_STOCK_SHOW_HIDDEN,
100 ROX_STOCK_SELECT,
101 ROX_STOCK_MOUNT,
102 ROX_STOCK_MOUNTED,
105 static GtkIconSize mount_icon_size = -1;
107 /* Static prototypes */
109 static void load_default_pixmaps(void);
110 static gint purge(gpointer data);
111 static MaskedPixmap *image_from_file(const char *path);
112 static MaskedPixmap *get_bad_image(void);
113 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h);
114 static GdkPixbuf *get_thumbnail_for(const char *path);
115 static void thumbnail_child_done(ChildThumbnail *info);
116 static void child_create_thumbnail(const gchar *path);
117 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src, guint32 color,
118 guchar alpha);
119 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label);
120 static gchar *thumbnail_path(const gchar *path);
121 static gchar *thumbnail_program(MIME_type *type);
123 /****************************************************************
124 * EXTERNAL INTERFACE *
125 ****************************************************************/
127 void pixmaps_init(void)
129 GtkIconFactory *factory;
130 int i;
132 gtk_widget_push_colormap(gdk_rgb_get_colormap());
134 pixmap_cache = g_fscache_new((GFSLoadFunc) image_from_file, NULL, NULL);
136 gtk_timeout_add(10000, purge, NULL);
138 factory = gtk_icon_factory_new();
139 for (i = 0; i < G_N_ELEMENTS(stocks); i++)
141 GdkPixbuf *pixbuf;
142 GError *error = NULL;
143 gchar *path;
144 GtkIconSet *iset;
145 const gchar *name = stocks[i];
147 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
148 pixbuf = gdk_pixbuf_new_from_file(path, &error);
149 if (!pixbuf)
151 g_warning("%s", error->message);
152 g_error_free(error);
153 pixbuf = gdk_pixbuf_new_from_xpm_data(bad_xpm);
155 g_free(path);
157 iset = gtk_icon_set_new_from_pixbuf(pixbuf);
158 g_object_unref(G_OBJECT(pixbuf));
159 gtk_icon_factory_add(factory, name, iset);
160 gtk_icon_set_unref(iset);
162 gtk_icon_factory_add_default(factory);
164 mount_icon_size = gtk_icon_size_register("rox-mount-size", 14, 14);
166 load_default_pixmaps();
168 option_register_widget("thumbs-purge-cache", thumbs_purge_cache);
171 /* Load image <appdir>/images/name.png.
172 * Always returns with a valid image.
174 MaskedPixmap *load_pixmap(const char *name)
176 guchar *path;
177 MaskedPixmap *retval;
179 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
180 retval = image_from_file(path);
181 g_free(path);
183 if (!retval)
184 retval = get_bad_image();
186 return retval;
189 /* Create a MaskedPixmap from a GTK stock ID. Always returns
190 * a valid image.
192 static MaskedPixmap *mp_from_stock(const char *stock_id, int size)
194 GtkIconSet *icon_set;
195 GdkPixbuf *pixbuf;
196 MaskedPixmap *retval;
198 icon_set = gtk_icon_factory_lookup_default(stock_id);
199 if (!icon_set)
200 return get_bad_image();
202 pixbuf = gtk_icon_set_render_icon(icon_set,
203 gtk_widget_get_default_style(), /* Gtk bug */
204 GTK_TEXT_DIR_LTR,
205 GTK_STATE_NORMAL,
206 size,
207 NULL,
208 NULL);
209 retval = masked_pixmap_new(pixbuf);
210 gdk_pixbuf_unref(pixbuf);
212 return retval;
215 void pixmap_make_huge(MaskedPixmap *mp)
217 if (mp->huge_pixbuf)
218 return;
220 g_return_if_fail(mp->src_pixbuf != NULL);
222 /* Limit to small size now, otherwise they get scaled up in mixed mode.
223 * Also looked ugly.
225 mp->huge_pixbuf = scale_pixbuf_up(mp->src_pixbuf,
226 SMALL_WIDTH, SMALL_HEIGHT);
228 if (!mp->huge_pixbuf)
230 mp->huge_pixbuf = mp->src_pixbuf;
231 g_object_ref(mp->huge_pixbuf);
234 mp->huge_pixbuf_lit = create_spotlight_pixbuf(mp->huge_pixbuf,
235 0x000099, 128);
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_pixbuf_lit = create_spotlight_pixbuf(mp->sm_pixbuf,
256 0x000099, 128);
258 mp->sm_width = gdk_pixbuf_get_width(mp->sm_pixbuf);
259 mp->sm_height = gdk_pixbuf_get_height(mp->sm_pixbuf);
262 /* Load image 'path' in the background and insert into pixmap_cache.
263 * Call callback(data, path) when done (path is NULL => error).
264 * If the image is already uptodate, or being created already, calls the
265 * callback right away.
267 void pixmap_background_thumb(const gchar *path, GFunc callback, gpointer data)
269 gboolean found;
270 MaskedPixmap *image;
271 GdkPixbuf *pixbuf;
272 pid_t child;
273 ChildThumbnail *info;
274 MIME_type *type;
275 gchar *thumb_prog;
278 image = g_fscache_lookup_full(pixmap_cache, path,
279 FSCACHE_LOOKUP_ONLY_NEW, &found);
281 if (found)
283 /* Thumbnail is known, or being created */
284 if (image)
285 g_object_unref(image);
286 callback(data, NULL);
287 return;
290 g_return_if_fail(image == NULL);
292 pixbuf = get_thumbnail_for(path);
294 if (!pixbuf)
296 struct stat info1, info2;
297 char *dir;
299 dir = g_path_get_dirname(path);
301 /* If the image itself is in ~/.thumbnails, load it now
302 * (ie, don't create thumbnails for thumbnails!).
304 if (mc_stat(dir, &info1) != 0)
306 callback(data, NULL);
307 g_free(dir);
308 return;
310 g_free(dir);
312 if (mc_stat(make_path(home_dir, ".thumbnails/normal"),
313 &info2) == 0 &&
314 info1.st_dev == info2.st_dev &&
315 info1.st_ino == info2.st_ino)
317 pixbuf = gdk_pixbuf_new_from_file(path, NULL);
318 if (!pixbuf)
320 g_fscache_insert(pixmap_cache,
321 path, NULL, TRUE);
322 callback(data, NULL);
323 return;
328 if (pixbuf)
330 MaskedPixmap *image;
332 image = masked_pixmap_new(pixbuf);
333 gdk_pixbuf_unref(pixbuf);
334 g_fscache_insert(pixmap_cache, path, image, TRUE);
335 callback(data, (gchar *) path);
336 return;
339 type = type_from_path(path);
340 if (!type)
342 callback(data, NULL);
343 return; /* Can't create thumbnail */
346 /* Add an entry, set to NULL, so no-one else tries to load this
347 * image.
349 g_fscache_insert(pixmap_cache, path, NULL, TRUE);
351 thumb_prog = thumbnail_program(type);
353 /* Only attempt to load 'images' types ourselves */
354 if (thumb_prog == NULL && strcmp(type->media_type, "image") != 0)
356 callback(data, NULL);
357 return; /* Don't know how to handle this type */
360 child = fork();
362 if (child == -1)
364 g_free(thumb_prog);
365 delayed_error("fork(): %s", g_strerror(errno));
366 callback(data, NULL);
367 return;
370 if (child == 0)
372 /* We are the child process */
373 if (thumb_prog) {
374 execl(thumb_prog, thumb_prog, path,
375 thumbnail_path(path),
376 g_strdup_printf("%d", PIXMAP_THUMB_SIZE), NULL);
377 _exit(1);
380 child_create_thumbnail(path);
381 _exit(0);
384 g_free(thumb_prog);
386 info = g_new(ChildThumbnail, 1);
387 info->path = g_strdup(path);
388 info->callback = callback;
389 info->data = data;
390 on_child_death(child, (CallbackFn) thumbnail_child_done, info);
393 /****************************************************************
394 * INTERNAL FUNCTIONS *
395 ****************************************************************/
397 /* Create a thumbnail file for this image */
398 static void save_thumbnail(const char *pathname, GdkPixbuf *full)
400 struct stat info;
401 gchar *path;
402 int original_width, original_height;
403 GString *to;
404 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
405 mode_t old_mask;
406 int name_len;
407 GdkPixbuf *thumb;
409 thumb = scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
411 original_width = gdk_pixbuf_get_width(full);
412 original_height = gdk_pixbuf_get_height(full);
414 if (mc_stat(pathname, &info) != 0)
415 return;
417 swidth = g_strdup_printf("%d", original_width);
418 sheight = g_strdup_printf("%d", original_height);
419 ssize = g_strdup_printf("%" SIZE_FMT, info.st_size);
420 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
422 path = pathdup(pathname);
423 uri = g_strconcat("file://", path, NULL);
424 md5 = md5_hash(uri);
425 g_free(path);
427 to = g_string_new(home_dir);
428 g_string_append(to, "/.thumbnails");
429 mkdir(to->str, 0700);
430 g_string_append(to, "/normal/");
431 mkdir(to->str, 0700);
432 g_string_append(to, md5);
433 name_len = to->len + 4; /* Truncate to this length when renaming */
434 g_string_append_printf(to, ".png.ROX-Filer-%ld", (long) getpid());
436 g_free(md5);
438 old_mask = umask(0077);
439 gdk_pixbuf_save(thumb, to->str, "png", NULL,
440 "tEXt::Thumb::Image::Width", swidth,
441 "tEXt::Thumb::Image::Height", sheight,
442 "tEXt::Thumb::Size", ssize,
443 "tEXt::Thumb::MTime", smtime,
444 "tEXt::Thumb::URI", uri,
445 "tEXt::Software", PROJECT,
446 NULL);
447 umask(old_mask);
449 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
450 * a race condition if two programs create the same thumb at
451 * once.
454 gchar *final;
456 final = g_strndup(to->str, name_len);
457 if (rename(to->str, final))
458 g_warning("Failed to rename '%s' to '%s': %s",
459 to->str, final, g_strerror(errno));
460 g_free(final);
463 g_string_free(to, TRUE);
464 g_free(swidth);
465 g_free(sheight);
466 g_free(ssize);
467 g_free(smtime);
468 g_free(uri);
471 static gchar *thumbnail_path(const char *path)
473 gchar *uri, *md5;
474 GString *to;
475 gchar *ans;
477 uri = g_strconcat("file://", path, NULL);
478 md5 = md5_hash(uri);
480 to = g_string_new(home_dir);
481 g_string_append(to, "/.thumbnails");
482 mkdir(to->str, 0700);
483 g_string_append(to, "/normal/");
484 mkdir(to->str, 0700);
485 g_string_append(to, md5);
486 g_string_append(to, ".png");
488 g_free(md5);
489 g_free(uri);
491 ans=to->str;
492 g_string_free(to, FALSE);
494 return ans;
497 /* Return a program to create thumbnails for files of this type.
498 * NULL to try to make it ourself (using gdk).
499 * g_free the result.
501 static gchar *thumbnail_program(MIME_type *type)
503 gchar *leaf;
504 gchar *path;
506 if (!type)
507 return NULL;
509 leaf = g_strconcat(type->media_type, "_", type->subtype, NULL);
510 path = choices_find_path_load(leaf, "MIME-thumb");
511 if (path) {
512 g_free(leaf);
513 return path;
516 path = choices_find_path_load(type->media_type, "MIME-thumb");
518 return path;
521 /* Called in a subprocess. Load path and create the thumbnail
522 * file. Parent will notice when we die.
524 static void child_create_thumbnail(const gchar *path)
526 GdkPixbuf *image;
528 image = gdk_pixbuf_new_from_file(path, NULL);
530 if (image)
531 save_thumbnail(path, image);
533 /* (no need to unref, as we're about to exit) */
536 /* Called when the child process exits */
537 static void thumbnail_child_done(ChildThumbnail *info)
539 GdkPixbuf *thumb;
541 thumb = get_thumbnail_for(info->path);
543 if (thumb)
545 MaskedPixmap *image;
547 image = masked_pixmap_new(thumb);
548 g_object_unref(thumb);
550 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
551 g_object_unref(image);
553 info->callback(info->data, info->path);
555 else
556 info->callback(info->data, NULL);
558 g_free(info->path);
559 g_free(info);
562 /* Check if we have an up-to-date thumbnail for this image.
563 * If so, return it. Otherwise, returns NULL.
565 static GdkPixbuf *get_thumbnail_for(const char *pathname)
567 GdkPixbuf *thumb = NULL;
568 char *thumb_path, *md5, *uri, *path;
569 const char *ssize, *smtime;
570 struct stat info;
572 path = pathdup(pathname);
573 uri = g_strconcat("file://", path, NULL);
574 md5 = md5_hash(uri);
575 g_free(uri);
577 thumb_path = g_strdup_printf("%s/.thumbnails/normal/%s.png",
578 home_dir, md5);
579 g_free(md5);
581 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
582 if (!thumb)
583 goto err;
585 /* Note that these don't need freeing... */
586 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
587 if (!ssize)
588 goto err;
590 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
591 if (!smtime)
592 goto err;
594 if (mc_stat(path, &info) != 0)
595 goto err;
597 if (info.st_mtime != atol(smtime) || info.st_size != atol(ssize))
598 goto err;
600 goto out;
601 err:
602 if (thumb)
603 gdk_pixbuf_unref(thumb);
604 thumb = NULL;
605 out:
606 g_free(path);
607 g_free(thumb_path);
608 return thumb;
611 /* Load the image 'path' and return a pointer to the resulting
612 * MaskedPixmap. NULL on failure.
613 * Doesn't check for thumbnails (this is for small icons).
615 static MaskedPixmap *image_from_file(const char *path)
617 GdkPixbuf *pixbuf;
618 MaskedPixmap *image;
619 GError *error = NULL;
621 pixbuf = gdk_pixbuf_new_from_file(path, &error);
622 if (!pixbuf)
624 g_warning("%s\n", error->message);
625 g_error_free(error);
626 return NULL;
629 image = masked_pixmap_new(pixbuf);
631 gdk_pixbuf_unref(pixbuf);
633 return image;
636 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
637 * If src is small enough, then ref it and return that.
639 GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
641 int w, h;
643 w = gdk_pixbuf_get_width(src);
644 h = gdk_pixbuf_get_height(src);
646 if (w <= max_w && h <= max_h)
648 gdk_pixbuf_ref(src);
649 return src;
651 else
653 float scale_x = ((float) w) / max_w;
654 float scale_y = ((float) h) / max_h;
655 float scale = MAX(scale_x, scale_y);
656 int dest_w = w / scale;
657 int dest_h = h / scale;
659 return gdk_pixbuf_scale_simple(src,
660 MAX(dest_w, 1),
661 MAX(dest_h, 1),
662 GDK_INTERP_BILINEAR);
666 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
667 * If src is that size or bigger, then ref it and return that.
669 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
671 int w, h;
673 w = gdk_pixbuf_get_width(src);
674 h = gdk_pixbuf_get_height(src);
676 if (w == 0 || h == 0 || w >= max_w || h >= max_h)
678 gdk_pixbuf_ref(src);
679 return src;
681 else
683 float scale_x = max_w / ((float) w);
684 float scale_y = max_h / ((float) h);
685 float scale = MIN(scale_x, scale_y);
687 return gdk_pixbuf_scale_simple(src,
688 w * scale,
689 h * scale,
690 GDK_INTERP_BILINEAR);
694 /* Return a pointer to the (static) bad image. The ref counter will ensure
695 * that the image is never freed.
697 static MaskedPixmap *get_bad_image(void)
699 GdkPixbuf *bad;
700 MaskedPixmap *mp;
702 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
703 mp = masked_pixmap_new(bad);
704 gdk_pixbuf_unref(bad);
706 return mp;
709 /* Called now and then to clear out old pixmaps */
710 static gint purge(gpointer data)
712 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
714 return TRUE;
717 static gpointer parent_class;
719 static void masked_pixmap_finialize(GObject *object)
721 MaskedPixmap *mp = (MaskedPixmap *) object;
723 if (mp->src_pixbuf)
725 g_object_unref(mp->src_pixbuf);
726 mp->src_pixbuf = NULL;
729 if (mp->huge_pixbuf)
731 g_object_unref(mp->huge_pixbuf);
732 mp->huge_pixbuf = NULL;
734 if (mp->huge_pixbuf_lit)
736 g_object_unref(mp->huge_pixbuf_lit);
737 mp->huge_pixbuf_lit = NULL;
740 if (mp->pixbuf)
742 g_object_unref(mp->pixbuf);
743 mp->pixbuf = NULL;
745 if (mp->pixbuf_lit)
747 g_object_unref(mp->pixbuf_lit);
748 mp->pixbuf_lit = NULL;
751 if (mp->sm_pixbuf)
753 g_object_unref(mp->sm_pixbuf);
754 mp->sm_pixbuf = NULL;
756 if (mp->sm_pixbuf_lit)
758 g_object_unref(mp->sm_pixbuf_lit);
759 mp->sm_pixbuf_lit = NULL;
762 G_OBJECT_CLASS(parent_class)->finalize(object);
765 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
767 GObjectClass *object = (GObjectClass *) gclass;
769 parent_class = g_type_class_peek_parent(gclass);
771 object->finalize = masked_pixmap_finialize;
774 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
776 MaskedPixmap *mp = (MaskedPixmap *) object;
778 mp->src_pixbuf = NULL;
780 mp->huge_pixbuf = NULL;
781 mp->huge_pixbuf_lit = NULL;
782 mp->huge_width = -1;
783 mp->huge_height = -1;
785 mp->pixbuf = NULL;
786 mp->pixbuf_lit = NULL;
787 mp->width = -1;
788 mp->height = -1;
790 mp->sm_pixbuf = NULL;
791 mp->sm_pixbuf_lit = NULL;
792 mp->sm_width = -1;
793 mp->sm_height = -1;
796 static GType masked_pixmap_get_type(void)
798 static GType type = 0;
800 if (!type)
802 static const GTypeInfo info =
804 sizeof (MaskedPixmapClass),
805 NULL, /* base_init */
806 NULL, /* base_finalise */
807 masked_pixmap_class_init,
808 NULL, /* class_finalise */
809 NULL, /* class_data */
810 sizeof(MaskedPixmap),
811 0, /* n_preallocs */
812 masked_pixmap_init
815 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
816 &info, 0);
819 return type;
822 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
824 MaskedPixmap *mp;
825 GdkPixbuf *src_pixbuf, *normal_pixbuf;
827 g_return_val_if_fail(full_size != NULL, NULL);
829 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
830 g_return_val_if_fail(src_pixbuf != NULL, NULL);
832 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
833 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
835 mp = g_object_new(masked_pixmap_get_type(), NULL);
837 mp->src_pixbuf = src_pixbuf;
839 mp->pixbuf = normal_pixbuf;
840 mp->pixbuf_lit = create_spotlight_pixbuf(normal_pixbuf, 0x000099, 128);
841 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
842 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
844 return mp;
847 /* Stolen from eel...and modified to colourize the pixbuf.
848 * 'alpha' is the transparency of 'color' (0xRRGGBB):
849 * 0 = fully opaque, 255 = fully transparent.
851 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src,
852 guint32 color,
853 guchar alpha)
855 GdkPixbuf *dest;
856 int i, j;
857 int width, height, has_alpha, src_row_stride, dst_row_stride;
858 guchar *target_pixels, *original_pixels;
859 guchar *pixsrc, *pixdest;
860 guchar r, g, b;
861 gint n_channels;
863 n_channels = gdk_pixbuf_get_n_channels(src);
864 has_alpha = gdk_pixbuf_get_has_alpha(src);
865 width = gdk_pixbuf_get_width(src);
866 height = gdk_pixbuf_get_height(src);
868 g_return_val_if_fail(gdk_pixbuf_get_colorspace(src) ==
869 GDK_COLORSPACE_RGB, NULL);
870 g_return_val_if_fail((!has_alpha && n_channels == 3) ||
871 (has_alpha && n_channels == 4), NULL);
872 g_return_val_if_fail(gdk_pixbuf_get_bits_per_sample(src) == 8, NULL);
874 dest = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), has_alpha,
875 gdk_pixbuf_get_bits_per_sample(src),
876 width, height);
878 dst_row_stride = gdk_pixbuf_get_rowstride(dest);
879 src_row_stride = gdk_pixbuf_get_rowstride(src);
880 target_pixels = gdk_pixbuf_get_pixels(dest);
881 original_pixels = gdk_pixbuf_get_pixels(src);
883 r = (color & 0xff0000) >> 16;
884 g = (color & 0xff00) >> 8;
885 b = color & 0xff;
887 for (i = 0; i < height; i++)
889 gint tmp;
891 pixdest = target_pixels + i * dst_row_stride;
892 pixsrc = original_pixels + i * src_row_stride;
893 for (j = 0; j < width; j++)
895 tmp = (*pixsrc++ * alpha + r * (255 - alpha)) / 255;
896 *pixdest++ = (guchar) MIN(255, tmp);
897 tmp = (*pixsrc++ * alpha + g * (255 - alpha)) / 255;
898 *pixdest++ = (guchar) MIN(255, tmp);
899 tmp = (*pixsrc++ * alpha + b * (255 - alpha)) / 255;
900 *pixdest++ = (guchar) MIN(255, tmp);
901 if (has_alpha)
902 *pixdest++ = *pixsrc++;
906 return dest;
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");
925 im_dirs = load_pixmap("dirs");
927 pixbuf = gdk_pixbuf_new_from_file(
928 make_path(app_dir, ".DirIcon"), &error);
929 if (pixbuf)
931 GList *icon_list;
933 icon_list = g_list_append(NULL, pixbuf);
934 gtk_window_set_default_icon_list(icon_list);
935 g_list_free(icon_list);
937 g_object_unref(G_OBJECT(pixbuf));
939 else
941 g_warning("%s\n", error->message);
942 g_error_free(error);
946 /* Also purges memory cache */
947 static void purge_disk_cache(GtkWidget *button, gpointer data)
949 char *path;
950 GList *list = NULL;
951 DIR *dir;
952 struct dirent *ent;
954 g_fscache_purge(pixmap_cache, 0);
956 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
958 dir = opendir(path);
959 if (!dir)
961 report_error(_("Can't delete thumbnails in %s:\n%s"),
962 path, g_strerror(errno));
963 goto out;
966 while ((ent = readdir(dir)))
968 if (ent->d_name[0] == '.')
969 continue;
970 list = g_list_prepend(list,
971 g_strconcat(path, ent->d_name, NULL));
974 closedir(dir);
976 if (list)
978 action_delete(list);
979 destroy_glist(&list);
981 else
982 info_message(_("There are no thumbnails to delete"));
983 out:
984 g_free(path);
987 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
989 GtkWidget *button, *align;
991 g_return_val_if_fail(option == NULL, NULL);
993 align = gtk_alignment_new(0, 0.5, 0, 0);
994 button = button_new_mixed(GTK_STOCK_CLEAR,
995 _("Purge thumbnails disk cache"));
996 gtk_container_add(GTK_CONTAINER(align), button);
997 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
999 return g_list_append(NULL, align);