r3768: Updated years.
[rox-filer.git] / ROX-Filer / src / pixmaps.c
blobbe872f8d796d6bff840334c473a1c256b65cb9cc
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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;
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 *get_bad_image(void);
115 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h);
116 static GdkPixbuf *get_thumbnail_for(const char *path);
117 static void thumbnail_child_done(ChildThumbnail *info);
118 static void child_create_thumbnail(const gchar *path);
119 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src, guint32 color,
120 guchar alpha);
121 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label);
122 static gchar *thumbnail_path(const gchar *path);
123 static gchar *thumbnail_program(MIME_type *type);
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 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_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;
276 MIME_type *type;
277 gchar *thumb_prog;
280 image = g_fscache_lookup_full(pixmap_cache, path,
281 FSCACHE_LOOKUP_ONLY_NEW, &found);
283 if (found)
285 /* Thumbnail is known, or being created */
286 if (image)
287 g_object_unref(image);
288 callback(data, NULL);
289 return;
292 g_return_if_fail(image == NULL);
294 pixbuf = get_thumbnail_for(path);
296 if (!pixbuf)
298 struct stat info1, info2;
299 char *dir;
301 dir = g_path_get_dirname(path);
303 /* If the image itself is in ~/.thumbnails, load it now
304 * (ie, don't create thumbnails for thumbnails!).
306 if (mc_stat(dir, &info1) != 0)
308 callback(data, NULL);
309 g_free(dir);
310 return;
312 g_free(dir);
314 if (mc_stat(make_path(home_dir, ".thumbnails/normal"),
315 &info2) == 0 &&
316 info1.st_dev == info2.st_dev &&
317 info1.st_ino == info2.st_ino)
319 pixbuf = rox_pixbuf_new_from_file_at_scale(path,
320 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
321 if (!pixbuf)
323 g_fscache_insert(pixmap_cache,
324 path, NULL, TRUE);
325 callback(data, NULL);
326 return;
331 if (pixbuf)
333 MaskedPixmap *image;
335 image = masked_pixmap_new(pixbuf);
336 gdk_pixbuf_unref(pixbuf);
337 g_fscache_insert(pixmap_cache, path, image, TRUE);
338 callback(data, (gchar *) path);
339 return;
342 type = type_from_path(path);
343 if (!type)
344 type = text_plain;
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. (We are sloppy with freeing
373 memory, but since we go away very quickly, that's ok.) */
374 if (thumb_prog)
376 DirItem *item;
378 item = diritem_new(g_basename(thumb_prog));
380 diritem_restat(thumb_prog, item, NULL);
381 if (item->flags & ITEM_FLAG_APPDIR)
382 thumb_prog = g_strconcat(thumb_prog, "/AppRun",
383 NULL);
385 execl(thumb_prog, thumb_prog, path,
386 thumbnail_path(path),
387 g_strdup_printf("%d", PIXMAP_THUMB_SIZE),
388 NULL);
389 _exit(1);
392 child_create_thumbnail(path);
393 _exit(0);
396 g_free(thumb_prog);
398 info = g_new(ChildThumbnail, 1);
399 info->path = g_strdup(path);
400 info->callback = callback;
401 info->data = data;
402 on_child_death(child, (CallbackFn) thumbnail_child_done, info);
405 /****************************************************************
406 * INTERNAL FUNCTIONS *
407 ****************************************************************/
409 /* Create a thumbnail file for this image */
410 static void save_thumbnail(const char *pathname, GdkPixbuf *full)
412 struct stat info;
413 gchar *path;
414 int original_width, original_height;
415 GString *to;
416 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
417 mode_t old_mask;
418 int name_len;
419 GdkPixbuf *thumb;
421 thumb = scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
423 original_width = gdk_pixbuf_get_width(full);
424 original_height = gdk_pixbuf_get_height(full);
426 if (mc_stat(pathname, &info) != 0)
427 return;
429 swidth = g_strdup_printf("%d", original_width);
430 sheight = g_strdup_printf("%d", original_height);
431 ssize = g_strdup_printf("%" SIZE_FMT, info.st_size);
432 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
434 path = pathdup(pathname);
435 uri = g_filename_to_uri(path, NULL, NULL);
436 if (!uri)
437 uri = g_strconcat("file://", path, NULL);
438 md5 = md5_hash(uri);
439 g_free(path);
441 to = g_string_new(home_dir);
442 g_string_append(to, "/.thumbnails");
443 mkdir(to->str, 0700);
444 g_string_append(to, "/normal/");
445 mkdir(to->str, 0700);
446 g_string_append(to, md5);
447 name_len = to->len + 4; /* Truncate to this length when renaming */
448 g_string_append_printf(to, ".png.ROX-Filer-%ld", (long) getpid());
450 g_free(md5);
452 old_mask = umask(0077);
453 gdk_pixbuf_save(thumb, to->str, "png", NULL,
454 "tEXt::Thumb::Image::Width", swidth,
455 "tEXt::Thumb::Image::Height", sheight,
456 "tEXt::Thumb::Size", ssize,
457 "tEXt::Thumb::MTime", smtime,
458 "tEXt::Thumb::URI", uri,
459 "tEXt::Software", PROJECT,
460 NULL);
461 umask(old_mask);
463 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
464 * a race condition if two programs create the same thumb at
465 * once.
468 gchar *final;
470 final = g_strndup(to->str, name_len);
471 if (rename(to->str, final))
472 g_warning("Failed to rename '%s' to '%s': %s",
473 to->str, final, g_strerror(errno));
474 g_free(final);
477 g_string_free(to, TRUE);
478 g_free(swidth);
479 g_free(sheight);
480 g_free(ssize);
481 g_free(smtime);
482 g_free(uri);
485 static gchar *thumbnail_path(const char *path)
487 gchar *uri, *md5;
488 GString *to;
489 gchar *ans;
491 uri = g_filename_to_uri(path, NULL, NULL);
492 if(!uri)
493 uri = g_strconcat("file://", path, NULL);
494 md5 = md5_hash(uri);
496 to = g_string_new(home_dir);
497 g_string_append(to, "/.thumbnails");
498 mkdir(to->str, 0700);
499 g_string_append(to, "/normal/");
500 mkdir(to->str, 0700);
501 g_string_append(to, md5);
502 g_string_append(to, ".png");
504 g_free(md5);
505 g_free(uri);
507 ans=to->str;
508 g_string_free(to, FALSE);
510 return ans;
513 /* Return a program to create thumbnails for files of this type.
514 * NULL to try to make it ourself (using gdk).
515 * g_free the result.
517 static gchar *thumbnail_program(MIME_type *type)
519 gchar *leaf;
520 gchar *path;
522 if (!type)
523 return NULL;
525 leaf = g_strconcat(type->media_type, "_", type->subtype, NULL);
526 path = choices_find_path_load(leaf, "MIME-thumb");
527 g_free(leaf);
528 if (path)
530 return path;
533 path = choices_find_path_load(type->media_type, "MIME-thumb");
535 return path;
538 /* Called in a subprocess. Load path and create the thumbnail
539 * file. Parent will notice when we die.
541 static void child_create_thumbnail(const gchar *path)
543 GdkPixbuf *image;
545 image = rox_pixbuf_new_from_file_at_scale(path,
546 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
548 if (image)
549 save_thumbnail(path, image);
551 /* (no need to unref, as we're about to exit) */
554 /* Called when the child process exits */
555 static void thumbnail_child_done(ChildThumbnail *info)
557 GdkPixbuf *thumb;
559 thumb = get_thumbnail_for(info->path);
561 if (thumb)
563 MaskedPixmap *image;
565 image = masked_pixmap_new(thumb);
566 g_object_unref(thumb);
568 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
569 g_object_unref(image);
571 info->callback(info->data, info->path);
573 else
574 info->callback(info->data, NULL);
576 g_free(info->path);
577 g_free(info);
580 /* Check if we have an up-to-date thumbnail for this image.
581 * If so, return it. Otherwise, returns NULL.
583 static GdkPixbuf *get_thumbnail_for(const char *pathname)
585 GdkPixbuf *thumb = NULL;
586 char *thumb_path, *md5, *uri, *path;
587 const char *ssize, *smtime;
588 struct stat info;
590 path = pathdup(pathname);
591 uri = g_filename_to_uri(path, NULL, NULL);
592 if(!uri)
593 uri = g_strconcat("file://", path, NULL);
594 md5 = md5_hash(uri);
595 g_free(uri);
597 thumb_path = g_strdup_printf("%s/.thumbnails/normal/%s.png",
598 home_dir, md5);
599 g_free(md5);
601 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
602 if (!thumb)
603 goto err;
605 /* Note that these don't need freeing... */
606 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
607 if (!ssize)
608 goto err;
610 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
611 if (!smtime)
612 goto err;
614 if (mc_stat(path, &info) != 0)
615 goto err;
617 if (info.st_mtime != atol(smtime) || info.st_size != atol(ssize))
618 goto err;
620 goto out;
621 err:
622 if (thumb)
623 gdk_pixbuf_unref(thumb);
624 thumb = NULL;
625 out:
626 g_free(path);
627 g_free(thumb_path);
628 return thumb;
631 /* Load the image 'path' and return a pointer to the resulting
632 * MaskedPixmap. NULL on failure.
633 * Doesn't check for thumbnails (this is for small icons).
635 static MaskedPixmap *image_from_file(const char *path)
637 GdkPixbuf *pixbuf;
638 MaskedPixmap *image;
639 GError *error = NULL;
641 pixbuf = gdk_pixbuf_new_from_file(path, &error);
642 if (!pixbuf)
644 g_warning("%s\n", error->message);
645 g_error_free(error);
646 return NULL;
649 image = masked_pixmap_new(pixbuf);
651 gdk_pixbuf_unref(pixbuf);
653 return image;
656 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
657 * If src is small enough, then ref it and return that.
659 GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
661 int w, h;
663 w = gdk_pixbuf_get_width(src);
664 h = gdk_pixbuf_get_height(src);
666 if (w <= max_w && h <= max_h)
668 gdk_pixbuf_ref(src);
669 return src;
671 else
673 float scale_x = ((float) w) / max_w;
674 float scale_y = ((float) h) / max_h;
675 float scale = MAX(scale_x, scale_y);
676 int dest_w = w / scale;
677 int dest_h = h / scale;
679 return gdk_pixbuf_scale_simple(src,
680 MAX(dest_w, 1),
681 MAX(dest_h, 1),
682 GDK_INTERP_BILINEAR);
686 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
687 * If src is that size or bigger, then ref it and return that.
689 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
691 int w, h;
693 w = gdk_pixbuf_get_width(src);
694 h = gdk_pixbuf_get_height(src);
696 if (w == 0 || h == 0 || w >= max_w || h >= max_h)
698 gdk_pixbuf_ref(src);
699 return src;
701 else
703 float scale_x = max_w / ((float) w);
704 float scale_y = max_h / ((float) h);
705 float scale = MIN(scale_x, scale_y);
707 return gdk_pixbuf_scale_simple(src,
708 w * scale,
709 h * scale,
710 GDK_INTERP_BILINEAR);
714 /* Return a pointer to the (static) bad image. The ref counter will ensure
715 * that the image is never freed.
717 static MaskedPixmap *get_bad_image(void)
719 GdkPixbuf *bad;
720 MaskedPixmap *mp;
722 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
723 mp = masked_pixmap_new(bad);
724 gdk_pixbuf_unref(bad);
726 return mp;
729 /* Called now and then to clear out old pixmaps */
730 static gint purge(gpointer data)
732 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
734 return TRUE;
737 static gpointer parent_class;
739 static void masked_pixmap_finialize(GObject *object)
741 MaskedPixmap *mp = (MaskedPixmap *) object;
743 if (mp->src_pixbuf)
745 g_object_unref(mp->src_pixbuf);
746 mp->src_pixbuf = NULL;
749 if (mp->huge_pixbuf)
751 g_object_unref(mp->huge_pixbuf);
752 mp->huge_pixbuf = NULL;
754 if (mp->huge_pixbuf_lit)
756 g_object_unref(mp->huge_pixbuf_lit);
757 mp->huge_pixbuf_lit = NULL;
760 if (mp->pixbuf)
762 g_object_unref(mp->pixbuf);
763 mp->pixbuf = NULL;
765 if (mp->pixbuf_lit)
767 g_object_unref(mp->pixbuf_lit);
768 mp->pixbuf_lit = NULL;
771 if (mp->sm_pixbuf)
773 g_object_unref(mp->sm_pixbuf);
774 mp->sm_pixbuf = NULL;
776 if (mp->sm_pixbuf_lit)
778 g_object_unref(mp->sm_pixbuf_lit);
779 mp->sm_pixbuf_lit = NULL;
782 G_OBJECT_CLASS(parent_class)->finalize(object);
785 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
787 GObjectClass *object = (GObjectClass *) gclass;
789 parent_class = g_type_class_peek_parent(gclass);
791 object->finalize = masked_pixmap_finialize;
794 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
796 MaskedPixmap *mp = (MaskedPixmap *) object;
798 mp->src_pixbuf = NULL;
800 mp->huge_pixbuf = NULL;
801 mp->huge_pixbuf_lit = NULL;
802 mp->huge_width = -1;
803 mp->huge_height = -1;
805 mp->pixbuf = NULL;
806 mp->pixbuf_lit = NULL;
807 mp->width = -1;
808 mp->height = -1;
810 mp->sm_pixbuf = NULL;
811 mp->sm_pixbuf_lit = NULL;
812 mp->sm_width = -1;
813 mp->sm_height = -1;
816 static GType masked_pixmap_get_type(void)
818 static GType type = 0;
820 if (!type)
822 static const GTypeInfo info =
824 sizeof (MaskedPixmapClass),
825 NULL, /* base_init */
826 NULL, /* base_finalise */
827 masked_pixmap_class_init,
828 NULL, /* class_finalise */
829 NULL, /* class_data */
830 sizeof(MaskedPixmap),
831 0, /* n_preallocs */
832 masked_pixmap_init
835 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
836 &info, 0);
839 return type;
842 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
844 MaskedPixmap *mp;
845 GdkPixbuf *src_pixbuf, *normal_pixbuf;
847 g_return_val_if_fail(full_size != NULL, NULL);
849 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
850 g_return_val_if_fail(src_pixbuf != NULL, NULL);
852 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
853 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
855 mp = g_object_new(masked_pixmap_get_type(), NULL);
857 mp->src_pixbuf = src_pixbuf;
859 mp->pixbuf = normal_pixbuf;
860 mp->pixbuf_lit = create_spotlight_pixbuf(normal_pixbuf, 0x000099, 128);
861 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
862 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
864 return mp;
867 /* Stolen from eel...and modified to colourize the pixbuf.
868 * 'alpha' is the transparency of 'color' (0xRRGGBB):
869 * 0 = fully opaque, 255 = fully transparent.
871 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src,
872 guint32 color,
873 guchar alpha)
875 GdkPixbuf *dest;
876 int i, j;
877 int width, height, has_alpha, src_row_stride, dst_row_stride;
878 guchar *target_pixels, *original_pixels;
879 guchar *pixsrc, *pixdest;
880 guchar r, g, b;
881 gint n_channels;
883 n_channels = gdk_pixbuf_get_n_channels(src);
884 has_alpha = gdk_pixbuf_get_has_alpha(src);
885 width = gdk_pixbuf_get_width(src);
886 height = gdk_pixbuf_get_height(src);
888 g_return_val_if_fail(gdk_pixbuf_get_colorspace(src) ==
889 GDK_COLORSPACE_RGB, NULL);
890 g_return_val_if_fail((!has_alpha && n_channels == 3) ||
891 (has_alpha && n_channels == 4), NULL);
892 g_return_val_if_fail(gdk_pixbuf_get_bits_per_sample(src) == 8, NULL);
894 dest = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), has_alpha,
895 gdk_pixbuf_get_bits_per_sample(src),
896 width, height);
898 dst_row_stride = gdk_pixbuf_get_rowstride(dest);
899 src_row_stride = gdk_pixbuf_get_rowstride(src);
900 target_pixels = gdk_pixbuf_get_pixels(dest);
901 original_pixels = gdk_pixbuf_get_pixels(src);
903 r = (color & 0xff0000) >> 16;
904 g = (color & 0xff00) >> 8;
905 b = color & 0xff;
907 for (i = 0; i < height; i++)
909 gint tmp;
911 pixdest = target_pixels + i * dst_row_stride;
912 pixsrc = original_pixels + i * src_row_stride;
913 for (j = 0; j < width; j++)
915 tmp = (*pixsrc++ * alpha + r * (255 - alpha)) / 255;
916 *pixdest++ = (guchar) MIN(255, tmp);
917 tmp = (*pixsrc++ * alpha + g * (255 - alpha)) / 255;
918 *pixdest++ = (guchar) MIN(255, tmp);
919 tmp = (*pixsrc++ * alpha + b * (255 - alpha)) / 255;
920 *pixdest++ = (guchar) MIN(255, tmp);
921 if (has_alpha)
922 *pixdest++ = *pixsrc++;
926 return dest;
929 /* Load all the standard pixmaps. Also sets the default window icon. */
930 static void load_default_pixmaps(void)
932 GdkPixbuf *pixbuf;
933 GError *error = NULL;
935 im_error = mp_from_stock(GTK_STOCK_DIALOG_WARNING,
936 GTK_ICON_SIZE_DIALOG);
937 im_unknown = mp_from_stock(GTK_STOCK_DIALOG_QUESTION,
938 GTK_ICON_SIZE_DIALOG);
939 im_symlink = load_pixmap("symlink");
941 im_unmounted = mp_from_stock(ROX_STOCK_MOUNT, mount_icon_size);
942 im_mounted = mp_from_stock(ROX_STOCK_MOUNTED, mount_icon_size);
943 im_appdir = load_pixmap("application");
944 im_xattr = load_pixmap("rox-xattr");
946 im_dirs = load_pixmap("dirs");
948 pixbuf = gdk_pixbuf_new_from_file(
949 make_path(app_dir, ".DirIcon"), &error);
950 if (pixbuf)
952 GList *icon_list;
954 icon_list = g_list_append(NULL, pixbuf);
955 gtk_window_set_default_icon_list(icon_list);
956 g_list_free(icon_list);
958 g_object_unref(G_OBJECT(pixbuf));
960 else
962 g_warning("%s\n", error->message);
963 g_error_free(error);
967 /* Also purges memory cache */
968 static void purge_disk_cache(GtkWidget *button, gpointer data)
970 char *path;
971 GList *list = NULL;
972 DIR *dir;
973 struct dirent *ent;
975 g_fscache_purge(pixmap_cache, 0);
977 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
979 dir = opendir(path);
980 if (!dir)
982 report_error(_("Can't delete thumbnails in %s:\n%s"),
983 path, g_strerror(errno));
984 goto out;
987 while ((ent = readdir(dir)))
989 if (ent->d_name[0] == '.')
990 continue;
991 list = g_list_prepend(list,
992 g_strconcat(path, ent->d_name, NULL));
995 closedir(dir);
997 if (list)
999 action_delete(list);
1000 destroy_glist(&list);
1002 else
1003 info_message(_("There are no thumbnails to delete"));
1004 out:
1005 g_free(path);
1008 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
1010 GtkWidget *button, *align;
1012 g_return_val_if_fail(option == NULL, NULL);
1014 align = gtk_alignment_new(0, 0.5, 0, 0);
1015 button = button_new_mixed(GTK_STOCK_CLEAR,
1016 _("Purge thumbnails disk cache"));
1017 gtk_container_add(GTK_CONTAINER(align), button);
1018 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
1020 return g_list_append(NULL, align);