r3305: Misc changes for next release.
[rox-filer.git] / ROX-Filer / src / pixmaps.c
blobd21b689473189a3fac574b3fa5dacd8857d5f7a1
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 "diritem.h"
54 #include "choices.h"
55 #include "options.h"
56 #include "action.h"
57 #include "type.h"
59 GFSCache *pixmap_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;
87 MaskedPixmap *im_dirs;
89 typedef struct _ChildThumbnail ChildThumbnail;
91 /* There is one of these for each active child process */
92 struct _ChildThumbnail {
93 gchar *path;
94 GFunc callback;
95 gpointer data;
98 static const char *stocks[] = {
99 ROX_STOCK_SHOW_DETAILS,
100 ROX_STOCK_SHOW_HIDDEN,
101 ROX_STOCK_SELECT,
102 ROX_STOCK_MOUNT,
103 ROX_STOCK_MOUNTED,
106 static GtkIconSize mount_icon_size = -1;
108 /* Static prototypes */
110 static void load_default_pixmaps(void);
111 static gint purge(gpointer data);
112 static MaskedPixmap *image_from_file(const char *path);
113 static MaskedPixmap *get_bad_image(void);
114 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h);
115 static GdkPixbuf *get_thumbnail_for(const char *path);
116 static void thumbnail_child_done(ChildThumbnail *info);
117 static void child_create_thumbnail(const gchar *path);
118 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src, guint32 color,
119 guchar alpha);
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);
137 gtk_timeout_add(10000, purge, NULL);
139 factory = gtk_icon_factory_new();
140 for (i = 0; i < G_N_ELEMENTS(stocks); i++)
142 GdkPixbuf *pixbuf;
143 GError *error = NULL;
144 gchar *path;
145 GtkIconSet *iset;
146 const gchar *name = stocks[i];
148 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
149 pixbuf = gdk_pixbuf_new_from_file(path, &error);
150 if (!pixbuf)
152 g_warning("%s", error->message);
153 g_error_free(error);
154 pixbuf = gdk_pixbuf_new_from_xpm_data(bad_xpm);
156 g_free(path);
158 iset = gtk_icon_set_new_from_pixbuf(pixbuf);
159 g_object_unref(G_OBJECT(pixbuf));
160 gtk_icon_factory_add(factory, name, iset);
161 gtk_icon_set_unref(iset);
163 gtk_icon_factory_add_default(factory);
165 mount_icon_size = gtk_icon_size_register("rox-mount-size", 14, 14);
167 load_default_pixmaps();
169 option_register_widget("thumbs-purge-cache", thumbs_purge_cache);
172 /* Load image <appdir>/images/name.png.
173 * Always returns with a valid image.
175 MaskedPixmap *load_pixmap(const char *name)
177 guchar *path;
178 MaskedPixmap *retval;
180 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
181 retval = image_from_file(path);
182 g_free(path);
184 if (!retval)
185 retval = get_bad_image();
187 return retval;
190 /* Create a MaskedPixmap from a GTK stock ID. Always returns
191 * a valid image.
193 static MaskedPixmap *mp_from_stock(const char *stock_id, int size)
195 GtkIconSet *icon_set;
196 GdkPixbuf *pixbuf;
197 MaskedPixmap *retval;
199 icon_set = gtk_icon_factory_lookup_default(stock_id);
200 if (!icon_set)
201 return get_bad_image();
203 pixbuf = gtk_icon_set_render_icon(icon_set,
204 gtk_widget_get_default_style(), /* Gtk bug */
205 GTK_TEXT_DIR_LTR,
206 GTK_STATE_NORMAL,
207 size,
208 NULL,
209 NULL);
210 retval = masked_pixmap_new(pixbuf);
211 gdk_pixbuf_unref(pixbuf);
213 return retval;
216 void pixmap_make_huge(MaskedPixmap *mp)
218 if (mp->huge_pixbuf)
219 return;
221 g_return_if_fail(mp->src_pixbuf != NULL);
223 /* Limit to small size now, otherwise they get scaled up in mixed mode.
224 * Also looked ugly.
226 mp->huge_pixbuf = scale_pixbuf_up(mp->src_pixbuf,
227 SMALL_WIDTH, SMALL_HEIGHT);
229 if (!mp->huge_pixbuf)
231 mp->huge_pixbuf = mp->src_pixbuf;
232 g_object_ref(mp->huge_pixbuf);
235 mp->huge_pixbuf_lit = create_spotlight_pixbuf(mp->huge_pixbuf,
236 0x000099, 128);
237 mp->huge_width = gdk_pixbuf_get_width(mp->huge_pixbuf);
238 mp->huge_height = gdk_pixbuf_get_height(mp->huge_pixbuf);
241 void pixmap_make_small(MaskedPixmap *mp)
243 if (mp->sm_pixbuf)
244 return;
246 g_return_if_fail(mp->src_pixbuf != NULL);
248 mp->sm_pixbuf = scale_pixbuf(mp->src_pixbuf, SMALL_WIDTH, SMALL_HEIGHT);
250 if (!mp->sm_pixbuf)
252 mp->sm_pixbuf = mp->src_pixbuf;
253 g_object_ref(mp->sm_pixbuf);
256 mp->sm_pixbuf_lit = create_spotlight_pixbuf(mp->sm_pixbuf,
257 0x000099, 128);
259 mp->sm_width = gdk_pixbuf_get_width(mp->sm_pixbuf);
260 mp->sm_height = gdk_pixbuf_get_height(mp->sm_pixbuf);
263 /* Load image 'path' in the background and insert into pixmap_cache.
264 * Call callback(data, path) when done (path is NULL => error).
265 * If the image is already uptodate, or being created already, calls the
266 * callback right away.
268 void pixmap_background_thumb(const gchar *path, GFunc callback, gpointer data)
270 gboolean found;
271 MaskedPixmap *image;
272 GdkPixbuf *pixbuf;
273 pid_t child;
274 ChildThumbnail *info;
275 MIME_type *type;
276 gchar *thumb_prog;
279 image = g_fscache_lookup_full(pixmap_cache, path,
280 FSCACHE_LOOKUP_ONLY_NEW, &found);
282 if (found)
284 /* Thumbnail is known, or being created */
285 if (image)
286 g_object_unref(image);
287 callback(data, NULL);
288 return;
291 g_return_if_fail(image == NULL);
293 pixbuf = get_thumbnail_for(path);
295 if (!pixbuf)
297 struct stat info1, info2;
298 char *dir;
300 dir = g_path_get_dirname(path);
302 /* If the image itself is in ~/.thumbnails, load it now
303 * (ie, don't create thumbnails for thumbnails!).
305 if (mc_stat(dir, &info1) != 0)
307 callback(data, NULL);
308 g_free(dir);
309 return;
311 g_free(dir);
313 if (mc_stat(make_path(home_dir, ".thumbnails/normal"),
314 &info2) == 0 &&
315 info1.st_dev == info2.st_dev &&
316 info1.st_ino == info2.st_ino)
318 pixbuf = gdk_pixbuf_new_from_file(path, NULL);
319 if (!pixbuf)
321 g_fscache_insert(pixmap_cache,
322 path, NULL, TRUE);
323 callback(data, NULL);
324 return;
329 if (pixbuf)
331 MaskedPixmap *image;
333 image = masked_pixmap_new(pixbuf);
334 gdk_pixbuf_unref(pixbuf);
335 g_fscache_insert(pixmap_cache, path, image, TRUE);
336 callback(data, (gchar *) path);
337 return;
340 type = type_from_path(path);
341 if (!type)
342 type = text_plain;
344 /* Add an entry, set to NULL, so no-one else tries to load this
345 * image.
347 g_fscache_insert(pixmap_cache, path, NULL, TRUE);
349 thumb_prog = thumbnail_program(type);
351 /* Only attempt to load 'images' types ourselves */
352 if (thumb_prog == NULL && strcmp(type->media_type, "image") != 0)
354 callback(data, NULL);
355 return; /* Don't know how to handle this type */
358 child = fork();
360 if (child == -1)
362 g_free(thumb_prog);
363 delayed_error("fork(): %s", g_strerror(errno));
364 callback(data, NULL);
365 return;
368 if (child == 0)
370 /* We are the child process. (We are sloppy with freeing
371 memory, but since we go away very quickly, that's ok.) */
372 if (thumb_prog)
374 DirItem *item;
376 item = diritem_new(g_basename(thumb_prog));
378 diritem_restat(thumb_prog, item, NULL);
379 if (item->flags & ITEM_FLAG_APPDIR)
380 thumb_prog = g_strconcat(thumb_prog, "/AppRun",
381 NULL);
383 execl(thumb_prog, thumb_prog, path,
384 thumbnail_path(path),
385 g_strdup_printf("%d", PIXMAP_THUMB_SIZE),
386 NULL);
387 _exit(1);
390 child_create_thumbnail(path);
391 _exit(0);
394 g_free(thumb_prog);
396 info = g_new(ChildThumbnail, 1);
397 info->path = g_strdup(path);
398 info->callback = callback;
399 info->data = data;
400 on_child_death(child, (CallbackFn) thumbnail_child_done, info);
403 /****************************************************************
404 * INTERNAL FUNCTIONS *
405 ****************************************************************/
407 /* Create a thumbnail file for this image */
408 static void save_thumbnail(const char *pathname, GdkPixbuf *full)
410 struct stat info;
411 gchar *path;
412 int original_width, original_height;
413 GString *to;
414 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
415 mode_t old_mask;
416 int name_len;
417 GdkPixbuf *thumb;
419 thumb = scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
421 original_width = gdk_pixbuf_get_width(full);
422 original_height = gdk_pixbuf_get_height(full);
424 if (mc_stat(pathname, &info) != 0)
425 return;
427 swidth = g_strdup_printf("%d", original_width);
428 sheight = g_strdup_printf("%d", original_height);
429 ssize = g_strdup_printf("%" SIZE_FMT, info.st_size);
430 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
432 path = pathdup(pathname);
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_strconcat("file://", path, NULL);
488 md5 = md5_hash(uri);
490 to = g_string_new(home_dir);
491 g_string_append(to, "/.thumbnails");
492 mkdir(to->str, 0700);
493 g_string_append(to, "/normal/");
494 mkdir(to->str, 0700);
495 g_string_append(to, md5);
496 g_string_append(to, ".png");
498 g_free(md5);
499 g_free(uri);
501 ans=to->str;
502 g_string_free(to, FALSE);
504 return ans;
507 /* Return a program to create thumbnails for files of this type.
508 * NULL to try to make it ourself (using gdk).
509 * g_free the result.
511 static gchar *thumbnail_program(MIME_type *type)
513 gchar *leaf;
514 gchar *path;
516 if (!type)
517 return NULL;
519 leaf = g_strconcat(type->media_type, "_", type->subtype, NULL);
520 path = choices_find_path_load(leaf, "MIME-thumb");
521 if (path)
523 g_free(leaf);
524 return path;
527 path = choices_find_path_load(type->media_type, "MIME-thumb");
529 return path;
532 /* Called in a subprocess. Load path and create the thumbnail
533 * file. Parent will notice when we die.
535 static void child_create_thumbnail(const gchar *path)
537 GdkPixbuf *image;
539 image = gdk_pixbuf_new_from_file(path, NULL);
541 if (image)
542 save_thumbnail(path, image);
544 /* (no need to unref, as we're about to exit) */
547 /* Called when the child process exits */
548 static void thumbnail_child_done(ChildThumbnail *info)
550 GdkPixbuf *thumb;
552 thumb = get_thumbnail_for(info->path);
554 if (thumb)
556 MaskedPixmap *image;
558 image = masked_pixmap_new(thumb);
559 g_object_unref(thumb);
561 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
562 g_object_unref(image);
564 info->callback(info->data, info->path);
566 else
567 info->callback(info->data, NULL);
569 g_free(info->path);
570 g_free(info);
573 /* Check if we have an up-to-date thumbnail for this image.
574 * If so, return it. Otherwise, returns NULL.
576 static GdkPixbuf *get_thumbnail_for(const char *pathname)
578 GdkPixbuf *thumb = NULL;
579 char *thumb_path, *md5, *uri, *path;
580 const char *ssize, *smtime;
581 struct stat info;
583 path = pathdup(pathname);
584 uri = g_strconcat("file://", path, NULL);
585 md5 = md5_hash(uri);
586 g_free(uri);
588 thumb_path = g_strdup_printf("%s/.thumbnails/normal/%s.png",
589 home_dir, md5);
590 g_free(md5);
592 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
593 if (!thumb)
594 goto err;
596 /* Note that these don't need freeing... */
597 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
598 if (!ssize)
599 goto err;
601 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
602 if (!smtime)
603 goto err;
605 if (mc_stat(path, &info) != 0)
606 goto err;
608 if (info.st_mtime != atol(smtime) || info.st_size != atol(ssize))
609 goto err;
611 goto out;
612 err:
613 if (thumb)
614 gdk_pixbuf_unref(thumb);
615 thumb = NULL;
616 out:
617 g_free(path);
618 g_free(thumb_path);
619 return thumb;
622 /* Load the image 'path' and return a pointer to the resulting
623 * MaskedPixmap. NULL on failure.
624 * Doesn't check for thumbnails (this is for small icons).
626 static MaskedPixmap *image_from_file(const char *path)
628 GdkPixbuf *pixbuf;
629 MaskedPixmap *image;
630 GError *error = NULL;
632 pixbuf = gdk_pixbuf_new_from_file(path, &error);
633 if (!pixbuf)
635 g_warning("%s\n", error->message);
636 g_error_free(error);
637 return NULL;
640 image = masked_pixmap_new(pixbuf);
642 gdk_pixbuf_unref(pixbuf);
644 return image;
647 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
648 * If src is small enough, then ref it and return that.
650 GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
652 int w, h;
654 w = gdk_pixbuf_get_width(src);
655 h = gdk_pixbuf_get_height(src);
657 if (w <= max_w && h <= max_h)
659 gdk_pixbuf_ref(src);
660 return src;
662 else
664 float scale_x = ((float) w) / max_w;
665 float scale_y = ((float) h) / max_h;
666 float scale = MAX(scale_x, scale_y);
667 int dest_w = w / scale;
668 int dest_h = h / scale;
670 return gdk_pixbuf_scale_simple(src,
671 MAX(dest_w, 1),
672 MAX(dest_h, 1),
673 GDK_INTERP_BILINEAR);
677 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
678 * If src is that size or bigger, then ref it and return that.
680 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
682 int w, h;
684 w = gdk_pixbuf_get_width(src);
685 h = gdk_pixbuf_get_height(src);
687 if (w == 0 || h == 0 || w >= max_w || h >= max_h)
689 gdk_pixbuf_ref(src);
690 return src;
692 else
694 float scale_x = max_w / ((float) w);
695 float scale_y = max_h / ((float) h);
696 float scale = MIN(scale_x, scale_y);
698 return gdk_pixbuf_scale_simple(src,
699 w * scale,
700 h * scale,
701 GDK_INTERP_BILINEAR);
705 /* Return a pointer to the (static) bad image. The ref counter will ensure
706 * that the image is never freed.
708 static MaskedPixmap *get_bad_image(void)
710 GdkPixbuf *bad;
711 MaskedPixmap *mp;
713 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
714 mp = masked_pixmap_new(bad);
715 gdk_pixbuf_unref(bad);
717 return mp;
720 /* Called now and then to clear out old pixmaps */
721 static gint purge(gpointer data)
723 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
725 return TRUE;
728 static gpointer parent_class;
730 static void masked_pixmap_finialize(GObject *object)
732 MaskedPixmap *mp = (MaskedPixmap *) object;
734 if (mp->src_pixbuf)
736 g_object_unref(mp->src_pixbuf);
737 mp->src_pixbuf = NULL;
740 if (mp->huge_pixbuf)
742 g_object_unref(mp->huge_pixbuf);
743 mp->huge_pixbuf = NULL;
745 if (mp->huge_pixbuf_lit)
747 g_object_unref(mp->huge_pixbuf_lit);
748 mp->huge_pixbuf_lit = NULL;
751 if (mp->pixbuf)
753 g_object_unref(mp->pixbuf);
754 mp->pixbuf = NULL;
756 if (mp->pixbuf_lit)
758 g_object_unref(mp->pixbuf_lit);
759 mp->pixbuf_lit = NULL;
762 if (mp->sm_pixbuf)
764 g_object_unref(mp->sm_pixbuf);
765 mp->sm_pixbuf = NULL;
767 if (mp->sm_pixbuf_lit)
769 g_object_unref(mp->sm_pixbuf_lit);
770 mp->sm_pixbuf_lit = NULL;
773 G_OBJECT_CLASS(parent_class)->finalize(object);
776 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
778 GObjectClass *object = (GObjectClass *) gclass;
780 parent_class = g_type_class_peek_parent(gclass);
782 object->finalize = masked_pixmap_finialize;
785 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
787 MaskedPixmap *mp = (MaskedPixmap *) object;
789 mp->src_pixbuf = NULL;
791 mp->huge_pixbuf = NULL;
792 mp->huge_pixbuf_lit = NULL;
793 mp->huge_width = -1;
794 mp->huge_height = -1;
796 mp->pixbuf = NULL;
797 mp->pixbuf_lit = NULL;
798 mp->width = -1;
799 mp->height = -1;
801 mp->sm_pixbuf = NULL;
802 mp->sm_pixbuf_lit = NULL;
803 mp->sm_width = -1;
804 mp->sm_height = -1;
807 static GType masked_pixmap_get_type(void)
809 static GType type = 0;
811 if (!type)
813 static const GTypeInfo info =
815 sizeof (MaskedPixmapClass),
816 NULL, /* base_init */
817 NULL, /* base_finalise */
818 masked_pixmap_class_init,
819 NULL, /* class_finalise */
820 NULL, /* class_data */
821 sizeof(MaskedPixmap),
822 0, /* n_preallocs */
823 masked_pixmap_init
826 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
827 &info, 0);
830 return type;
833 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
835 MaskedPixmap *mp;
836 GdkPixbuf *src_pixbuf, *normal_pixbuf;
838 g_return_val_if_fail(full_size != NULL, NULL);
840 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
841 g_return_val_if_fail(src_pixbuf != NULL, NULL);
843 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
844 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
846 mp = g_object_new(masked_pixmap_get_type(), NULL);
848 mp->src_pixbuf = src_pixbuf;
850 mp->pixbuf = normal_pixbuf;
851 mp->pixbuf_lit = create_spotlight_pixbuf(normal_pixbuf, 0x000099, 128);
852 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
853 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
855 return mp;
858 /* Stolen from eel...and modified to colourize the pixbuf.
859 * 'alpha' is the transparency of 'color' (0xRRGGBB):
860 * 0 = fully opaque, 255 = fully transparent.
862 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src,
863 guint32 color,
864 guchar alpha)
866 GdkPixbuf *dest;
867 int i, j;
868 int width, height, has_alpha, src_row_stride, dst_row_stride;
869 guchar *target_pixels, *original_pixels;
870 guchar *pixsrc, *pixdest;
871 guchar r, g, b;
872 gint n_channels;
874 n_channels = gdk_pixbuf_get_n_channels(src);
875 has_alpha = gdk_pixbuf_get_has_alpha(src);
876 width = gdk_pixbuf_get_width(src);
877 height = gdk_pixbuf_get_height(src);
879 g_return_val_if_fail(gdk_pixbuf_get_colorspace(src) ==
880 GDK_COLORSPACE_RGB, NULL);
881 g_return_val_if_fail((!has_alpha && n_channels == 3) ||
882 (has_alpha && n_channels == 4), NULL);
883 g_return_val_if_fail(gdk_pixbuf_get_bits_per_sample(src) == 8, NULL);
885 dest = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), has_alpha,
886 gdk_pixbuf_get_bits_per_sample(src),
887 width, height);
889 dst_row_stride = gdk_pixbuf_get_rowstride(dest);
890 src_row_stride = gdk_pixbuf_get_rowstride(src);
891 target_pixels = gdk_pixbuf_get_pixels(dest);
892 original_pixels = gdk_pixbuf_get_pixels(src);
894 r = (color & 0xff0000) >> 16;
895 g = (color & 0xff00) >> 8;
896 b = color & 0xff;
898 for (i = 0; i < height; i++)
900 gint tmp;
902 pixdest = target_pixels + i * dst_row_stride;
903 pixsrc = original_pixels + i * src_row_stride;
904 for (j = 0; j < width; j++)
906 tmp = (*pixsrc++ * alpha + r * (255 - alpha)) / 255;
907 *pixdest++ = (guchar) MIN(255, tmp);
908 tmp = (*pixsrc++ * alpha + g * (255 - alpha)) / 255;
909 *pixdest++ = (guchar) MIN(255, tmp);
910 tmp = (*pixsrc++ * alpha + b * (255 - alpha)) / 255;
911 *pixdest++ = (guchar) MIN(255, tmp);
912 if (has_alpha)
913 *pixdest++ = *pixsrc++;
917 return dest;
920 /* Load all the standard pixmaps. Also sets the default window icon. */
921 static void load_default_pixmaps(void)
923 GdkPixbuf *pixbuf;
924 GError *error = NULL;
926 im_error = mp_from_stock(GTK_STOCK_DIALOG_WARNING,
927 GTK_ICON_SIZE_DIALOG);
928 im_unknown = mp_from_stock(GTK_STOCK_DIALOG_QUESTION,
929 GTK_ICON_SIZE_DIALOG);
930 im_symlink = load_pixmap("symlink");
932 im_unmounted = mp_from_stock(ROX_STOCK_MOUNT, mount_icon_size);
933 im_mounted = mp_from_stock(ROX_STOCK_MOUNTED, mount_icon_size);
934 im_appdir = load_pixmap("application");
936 im_dirs = load_pixmap("dirs");
938 pixbuf = gdk_pixbuf_new_from_file(
939 make_path(app_dir, ".DirIcon"), &error);
940 if (pixbuf)
942 GList *icon_list;
944 icon_list = g_list_append(NULL, pixbuf);
945 gtk_window_set_default_icon_list(icon_list);
946 g_list_free(icon_list);
948 g_object_unref(G_OBJECT(pixbuf));
950 else
952 g_warning("%s\n", error->message);
953 g_error_free(error);
957 /* Also purges memory cache */
958 static void purge_disk_cache(GtkWidget *button, gpointer data)
960 char *path;
961 GList *list = NULL;
962 DIR *dir;
963 struct dirent *ent;
965 g_fscache_purge(pixmap_cache, 0);
967 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
969 dir = opendir(path);
970 if (!dir)
972 report_error(_("Can't delete thumbnails in %s:\n%s"),
973 path, g_strerror(errno));
974 goto out;
977 while ((ent = readdir(dir)))
979 if (ent->d_name[0] == '.')
980 continue;
981 list = g_list_prepend(list,
982 g_strconcat(path, ent->d_name, NULL));
985 closedir(dir);
987 if (list)
989 action_delete(list);
990 destroy_glist(&list);
992 else
993 info_message(_("There are no thumbnails to delete"));
994 out:
995 g_free(path);
998 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
1000 GtkWidget *button, *align;
1002 g_return_val_if_fail(option == NULL, NULL);
1004 align = gtk_alignment_new(0, 0.5, 0, 0);
1005 button = button_new_mixed(GTK_STOCK_CLEAR,
1006 _("Purge thumbnails disk cache"));
1007 gtk_container_add(GTK_CONTAINER(align), button);
1008 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
1010 return g_list_append(NULL, align);