r3909: World-writable directories can't be app dirs (Thomas Leonard).
[rox-filer/translations.git] / ROX-Filer / src / pixmaps.c
blobac9c7e6531b6dedc90e9416b361fe2bf5a0a8884
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
33 #define PIXMAP_THUMB_TOO_OLD_TIME 5
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <string.h>
43 #include <gtk/gtk.h>
45 #include "global.h"
47 #include "fscache.h"
48 #include "support.h"
49 #include "gui_support.h"
50 #include "pixmaps.h"
51 #include "main.h"
52 #include "filer.h"
53 #include "dir.h"
54 #include "diritem.h"
55 #include "choices.h"
56 #include "options.h"
57 #include "action.h"
58 #include "type.h"
60 GFSCache *pixmap_cache = NULL;
62 static const char * bad_xpm[] = {
63 "12 12 3 1",
64 " c #000000000000",
65 ". c #FFFF00000000",
66 "x c #FFFFFFFFFFFF",
67 " ",
68 " ..xxxxxx.. ",
69 " ...xxxx... ",
70 " x...xx...x ",
71 " xx......xx ",
72 " xxx....xxx ",
73 " xxx....xxx ",
74 " xx......xx ",
75 " x...xx...x ",
76 " ...xxxx... ",
77 " ..xxxxxx.. ",
78 " "};
80 MaskedPixmap *im_error;
81 MaskedPixmap *im_unknown;
82 MaskedPixmap *im_symlink;
84 MaskedPixmap *im_unmounted;
85 MaskedPixmap *im_mounted;
86 MaskedPixmap *im_appdir;
87 MaskedPixmap *im_xattr;
89 MaskedPixmap *im_dirs;
91 typedef struct _ChildThumbnail ChildThumbnail;
93 /* There is one of these for each active child process */
94 struct _ChildThumbnail {
95 gchar *path;
96 GFunc callback;
97 gpointer data;
100 static const char *stocks[] = {
101 ROX_STOCK_SHOW_DETAILS,
102 ROX_STOCK_SHOW_HIDDEN,
103 ROX_STOCK_SELECT,
104 ROX_STOCK_MOUNT,
105 ROX_STOCK_MOUNTED,
108 static GtkIconSize mount_icon_size = -1;
110 /* Static prototypes */
112 static void load_default_pixmaps(void);
113 static gint purge(gpointer data);
114 static MaskedPixmap *image_from_file(const char *path);
115 static MaskedPixmap *get_bad_image(void);
116 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h);
117 static GdkPixbuf *get_thumbnail_for(const char *path);
118 static void thumbnail_child_done(ChildThumbnail *info);
119 static void child_create_thumbnail(const gchar *path);
120 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src, guint32 color,
121 guchar alpha);
122 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label);
123 static gchar *thumbnail_path(const gchar *path);
124 static gchar *thumbnail_program(MIME_type *type);
126 /****************************************************************
127 * EXTERNAL INTERFACE *
128 ****************************************************************/
130 void pixmaps_init(void)
132 GtkIconFactory *factory;
133 int i;
135 gtk_widget_push_colormap(gdk_rgb_get_colormap());
137 pixmap_cache = g_fscache_new((GFSLoadFunc) image_from_file, NULL, NULL);
139 g_timeout_add(10000, purge, NULL);
141 factory = gtk_icon_factory_new();
142 for (i = 0; i < G_N_ELEMENTS(stocks); i++)
144 GdkPixbuf *pixbuf;
145 GError *error = NULL;
146 gchar *path;
147 GtkIconSet *iset;
148 const gchar *name = stocks[i];
150 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
151 pixbuf = gdk_pixbuf_new_from_file(path, &error);
152 if (!pixbuf)
154 g_warning("%s", error->message);
155 g_error_free(error);
156 pixbuf = gdk_pixbuf_new_from_xpm_data(bad_xpm);
158 g_free(path);
160 iset = gtk_icon_set_new_from_pixbuf(pixbuf);
161 g_object_unref(G_OBJECT(pixbuf));
162 gtk_icon_factory_add(factory, name, iset);
163 gtk_icon_set_unref(iset);
165 gtk_icon_factory_add_default(factory);
167 mount_icon_size = gtk_icon_size_register("rox-mount-size", 14, 14);
169 load_default_pixmaps();
171 option_register_widget("thumbs-purge-cache", thumbs_purge_cache);
174 /* Load image <appdir>/images/name.png.
175 * Always returns with a valid image.
177 MaskedPixmap *load_pixmap(const char *name)
179 guchar *path;
180 MaskedPixmap *retval;
182 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
183 retval = image_from_file(path);
184 g_free(path);
186 if (!retval)
187 retval = get_bad_image();
189 return retval;
192 /* Create a MaskedPixmap from a GTK stock ID. Always returns
193 * a valid image.
195 static MaskedPixmap *mp_from_stock(const char *stock_id, int size)
197 GtkIconSet *icon_set;
198 GdkPixbuf *pixbuf;
199 MaskedPixmap *retval;
201 icon_set = gtk_icon_factory_lookup_default(stock_id);
202 if (!icon_set)
203 return get_bad_image();
205 pixbuf = gtk_icon_set_render_icon(icon_set,
206 gtk_widget_get_default_style(), /* Gtk bug */
207 GTK_TEXT_DIR_LTR,
208 GTK_STATE_NORMAL,
209 size,
210 NULL,
211 NULL);
212 retval = masked_pixmap_new(pixbuf);
213 gdk_pixbuf_unref(pixbuf);
215 return retval;
218 void pixmap_make_huge(MaskedPixmap *mp)
220 if (mp->huge_pixbuf)
221 return;
223 g_return_if_fail(mp->src_pixbuf != NULL);
225 /* Limit to small size now, otherwise they get scaled up in mixed mode.
226 * Also looked ugly.
228 mp->huge_pixbuf = scale_pixbuf_up(mp->src_pixbuf,
229 SMALL_WIDTH, SMALL_HEIGHT);
231 if (!mp->huge_pixbuf)
233 mp->huge_pixbuf = mp->src_pixbuf;
234 g_object_ref(mp->huge_pixbuf);
237 mp->huge_pixbuf_lit = create_spotlight_pixbuf(mp->huge_pixbuf,
238 0x000099, 128);
239 mp->huge_width = gdk_pixbuf_get_width(mp->huge_pixbuf);
240 mp->huge_height = gdk_pixbuf_get_height(mp->huge_pixbuf);
243 void pixmap_make_small(MaskedPixmap *mp)
245 if (mp->sm_pixbuf)
246 return;
248 g_return_if_fail(mp->src_pixbuf != NULL);
250 mp->sm_pixbuf = scale_pixbuf(mp->src_pixbuf, SMALL_WIDTH, SMALL_HEIGHT);
252 if (!mp->sm_pixbuf)
254 mp->sm_pixbuf = mp->src_pixbuf;
255 g_object_ref(mp->sm_pixbuf);
258 mp->sm_pixbuf_lit = create_spotlight_pixbuf(mp->sm_pixbuf,
259 0x000099, 128);
261 mp->sm_width = gdk_pixbuf_get_width(mp->sm_pixbuf);
262 mp->sm_height = gdk_pixbuf_get_height(mp->sm_pixbuf);
265 /* Load image 'path' in the background and insert into pixmap_cache.
266 * Call callback(data, path) when done (path is NULL => error).
267 * If the image is already uptodate, or being created already, calls the
268 * callback right away.
270 void pixmap_background_thumb(const gchar *path, GFunc callback, gpointer data)
272 gboolean found;
273 MaskedPixmap *image;
274 GdkPixbuf *pixbuf;
275 pid_t child;
276 ChildThumbnail *info;
277 MIME_type *type;
278 gchar *thumb_prog;
281 image = g_fscache_lookup_full(pixmap_cache, path,
282 FSCACHE_LOOKUP_ONLY_NEW, &found);
284 if (found)
286 /* Thumbnail is known, or being created */
287 if (image)
288 g_object_unref(image);
289 callback(data, NULL);
290 return;
293 g_return_if_fail(image == NULL);
295 pixbuf = get_thumbnail_for(path);
297 if (!pixbuf)
299 struct stat info1, info2;
300 char *dir;
302 dir = g_path_get_dirname(path);
304 /* If the image itself is in ~/.thumbnails, load it now
305 * (ie, don't create thumbnails for thumbnails!).
307 if (mc_stat(dir, &info1) != 0)
309 callback(data, NULL);
310 g_free(dir);
311 return;
313 g_free(dir);
315 if (mc_stat(make_path(home_dir, ".thumbnails/normal"),
316 &info2) == 0 &&
317 info1.st_dev == info2.st_dev &&
318 info1.st_ino == info2.st_ino)
320 pixbuf = rox_pixbuf_new_from_file_at_scale(path,
321 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
322 if (!pixbuf)
324 g_fscache_insert(pixmap_cache,
325 path, NULL, TRUE);
326 callback(data, NULL);
327 return;
332 if (pixbuf)
334 MaskedPixmap *image;
336 image = masked_pixmap_new(pixbuf);
337 gdk_pixbuf_unref(pixbuf);
338 g_fscache_insert(pixmap_cache, path, image, TRUE);
339 callback(data, (gchar *) path);
340 g_object_unref(G_OBJECT(image));
341 return;
344 type = type_from_path(path);
345 if (!type)
346 type = text_plain;
348 /* Add an entry, set to NULL, so no-one else tries to load this
349 * image.
351 g_fscache_insert(pixmap_cache, path, NULL, TRUE);
353 thumb_prog = thumbnail_program(type);
355 /* Only attempt to load 'images' types ourselves */
356 if (thumb_prog == NULL && strcmp(type->media_type, "image") != 0)
358 callback(data, NULL);
359 return; /* Don't know how to handle this type */
362 child = fork();
364 if (child == -1)
366 g_free(thumb_prog);
367 delayed_error("fork(): %s", g_strerror(errno));
368 callback(data, NULL);
369 return;
372 if (child == 0)
374 /* We are the child process. (We are sloppy with freeing
375 memory, but since we go away very quickly, that's ok.) */
376 if (thumb_prog)
378 DirItem *item;
380 item = diritem_new(g_basename(thumb_prog));
382 diritem_restat(thumb_prog, item, NULL);
383 if (item->flags & ITEM_FLAG_APPDIR)
384 thumb_prog = g_strconcat(thumb_prog, "/AppRun",
385 NULL);
387 execl(thumb_prog, thumb_prog, path,
388 thumbnail_path(path),
389 g_strdup_printf("%d", PIXMAP_THUMB_SIZE),
390 NULL);
391 _exit(1);
394 child_create_thumbnail(path);
395 _exit(0);
398 g_free(thumb_prog);
400 info = g_new(ChildThumbnail, 1);
401 info->path = g_strdup(path);
402 info->callback = callback;
403 info->data = data;
404 on_child_death(child, (CallbackFn) thumbnail_child_done, info);
407 /****************************************************************
408 * INTERNAL FUNCTIONS *
409 ****************************************************************/
411 /* Create a thumbnail file for this image */
412 static void save_thumbnail(const char *pathname, GdkPixbuf *full)
414 struct stat info;
415 gchar *path;
416 int original_width, original_height;
417 GString *to;
418 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
419 mode_t old_mask;
420 int name_len;
421 GdkPixbuf *thumb;
423 thumb = scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
425 original_width = gdk_pixbuf_get_width(full);
426 original_height = gdk_pixbuf_get_height(full);
428 if (mc_stat(pathname, &info) != 0)
429 return;
431 swidth = g_strdup_printf("%d", original_width);
432 sheight = g_strdup_printf("%d", original_height);
433 ssize = g_strdup_printf("%" SIZE_FMT, info.st_size);
434 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
436 path = pathdup(pathname);
437 uri = g_filename_to_uri(path, NULL, NULL);
438 if (!uri)
439 uri = g_strconcat("file://", path, NULL);
440 md5 = md5_hash(uri);
441 g_free(path);
443 to = g_string_new(home_dir);
444 g_string_append(to, "/.thumbnails");
445 mkdir(to->str, 0700);
446 g_string_append(to, "/normal/");
447 mkdir(to->str, 0700);
448 g_string_append(to, md5);
449 name_len = to->len + 4; /* Truncate to this length when renaming */
450 g_string_append_printf(to, ".png.ROX-Filer-%ld", (long) getpid());
452 g_free(md5);
454 old_mask = umask(0077);
455 gdk_pixbuf_save(thumb, to->str, "png", NULL,
456 "tEXt::Thumb::Image::Width", swidth,
457 "tEXt::Thumb::Image::Height", sheight,
458 "tEXt::Thumb::Size", ssize,
459 "tEXt::Thumb::MTime", smtime,
460 "tEXt::Thumb::URI", uri,
461 "tEXt::Software", PROJECT,
462 NULL);
463 umask(old_mask);
465 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
466 * a race condition if two programs create the same thumb at
467 * once.
470 gchar *final;
472 final = g_strndup(to->str, name_len);
473 if (rename(to->str, final))
474 g_warning("Failed to rename '%s' to '%s': %s",
475 to->str, final, g_strerror(errno));
476 g_free(final);
479 g_string_free(to, TRUE);
480 g_free(swidth);
481 g_free(sheight);
482 g_free(ssize);
483 g_free(smtime);
484 g_free(uri);
487 static gchar *thumbnail_path(const char *path)
489 gchar *uri, *md5;
490 GString *to;
491 gchar *ans;
493 uri = g_filename_to_uri(path, NULL, NULL);
494 if(!uri)
495 uri = g_strconcat("file://", path, NULL);
496 md5 = md5_hash(uri);
498 to = g_string_new(home_dir);
499 g_string_append(to, "/.thumbnails");
500 mkdir(to->str, 0700);
501 g_string_append(to, "/normal/");
502 mkdir(to->str, 0700);
503 g_string_append(to, md5);
504 g_string_append(to, ".png");
506 g_free(md5);
507 g_free(uri);
509 ans=to->str;
510 g_string_free(to, FALSE);
512 return ans;
515 /* Return a program to create thumbnails for files of this type.
516 * NULL to try to make it ourself (using gdk).
517 * g_free the result.
519 static gchar *thumbnail_program(MIME_type *type)
521 gchar *leaf;
522 gchar *path;
524 if (!type)
525 return NULL;
527 leaf = g_strconcat(type->media_type, "_", type->subtype, NULL);
528 path = choices_find_xdg_path_load(leaf, "MIME-thumb", SITE);
529 g_free(leaf);
530 if (path)
532 return path;
535 path = choices_find_xdg_path_load(type->media_type, "MIME-thumb",
536 SITE);
538 return path;
541 /* Called in a subprocess. Load path and create the thumbnail
542 * file. Parent will notice when we die.
544 static void child_create_thumbnail(const gchar *path)
546 GdkPixbuf *image;
548 image = rox_pixbuf_new_from_file_at_scale(path,
549 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
551 if (image)
552 save_thumbnail(path, image);
554 /* (no need to unref, as we're about to exit) */
557 /* Called when the child process exits */
558 static void thumbnail_child_done(ChildThumbnail *info)
560 GdkPixbuf *thumb;
562 thumb = get_thumbnail_for(info->path);
564 if (thumb)
566 MaskedPixmap *image;
568 image = masked_pixmap_new(thumb);
569 g_object_unref(thumb);
571 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
572 g_object_unref(image);
574 info->callback(info->data, info->path);
576 else
577 info->callback(info->data, NULL);
579 g_free(info->path);
580 g_free(info);
583 /* Check if we have an up-to-date thumbnail for this image.
584 * If so, return it. Otherwise, returns NULL.
586 static GdkPixbuf *get_thumbnail_for(const char *pathname)
588 GdkPixbuf *thumb = NULL;
589 char *thumb_path, *md5, *uri, *path;
590 const char *ssize, *smtime;
591 struct stat info;
592 time_t ttime, now;
594 path = pathdup(pathname);
595 uri = g_filename_to_uri(path, NULL, NULL);
596 if(!uri)
597 uri = g_strconcat("file://", path, NULL);
598 md5 = md5_hash(uri);
599 g_free(uri);
601 thumb_path = g_strdup_printf("%s/.thumbnails/normal/%s.png",
602 home_dir, md5);
603 g_free(md5);
605 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
606 if (!thumb)
607 goto err;
609 /* Note that these don't need freeing... */
610 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
611 if (!ssize)
612 goto err;
614 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
615 if (!smtime)
616 goto err;
618 if (mc_stat(path, &info) != 0)
619 goto err;
621 ttime=(time_t) atol(smtime);
622 time(&now);
623 if (info.st_mtime != ttime && now>ttime+PIXMAP_THUMB_TOO_OLD_TIME)
624 goto err;
626 if (info.st_size < atol(ssize))
627 goto err;
629 goto out;
630 err:
631 if (thumb)
632 gdk_pixbuf_unref(thumb);
633 thumb = NULL;
634 out:
635 g_free(path);
636 g_free(thumb_path);
637 return thumb;
640 /* Load the image 'path' and return a pointer to the resulting
641 * MaskedPixmap. NULL on failure.
642 * Doesn't check for thumbnails (this is for small icons).
644 static MaskedPixmap *image_from_file(const char *path)
646 GdkPixbuf *pixbuf;
647 MaskedPixmap *image;
648 GError *error = NULL;
650 pixbuf = gdk_pixbuf_new_from_file(path, &error);
651 if (!pixbuf)
653 g_warning("%s\n", error->message);
654 g_error_free(error);
655 return NULL;
658 image = masked_pixmap_new(pixbuf);
660 gdk_pixbuf_unref(pixbuf);
662 return image;
665 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
666 * If src is small enough, then ref it and return that.
668 GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
670 int w, h;
672 w = gdk_pixbuf_get_width(src);
673 h = gdk_pixbuf_get_height(src);
675 if (w <= max_w && h <= max_h)
677 gdk_pixbuf_ref(src);
678 return src;
680 else
682 float scale_x = ((float) w) / max_w;
683 float scale_y = ((float) h) / max_h;
684 float scale = MAX(scale_x, scale_y);
685 int dest_w = w / scale;
686 int dest_h = h / scale;
688 return gdk_pixbuf_scale_simple(src,
689 MAX(dest_w, 1),
690 MAX(dest_h, 1),
691 GDK_INTERP_BILINEAR);
695 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
696 * If src is that size or bigger, then ref it and return that.
698 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
700 int w, h;
702 w = gdk_pixbuf_get_width(src);
703 h = gdk_pixbuf_get_height(src);
705 if (w == 0 || h == 0 || w >= max_w || h >= max_h)
707 gdk_pixbuf_ref(src);
708 return src;
710 else
712 float scale_x = max_w / ((float) w);
713 float scale_y = max_h / ((float) h);
714 float scale = MIN(scale_x, scale_y);
716 return gdk_pixbuf_scale_simple(src,
717 w * scale,
718 h * scale,
719 GDK_INTERP_BILINEAR);
723 /* Return a pointer to the (static) bad image. The ref counter will ensure
724 * that the image is never freed.
726 static MaskedPixmap *get_bad_image(void)
728 GdkPixbuf *bad;
729 MaskedPixmap *mp;
731 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
732 mp = masked_pixmap_new(bad);
733 gdk_pixbuf_unref(bad);
735 return mp;
738 /* Called now and then to clear out old pixmaps */
739 static gint purge(gpointer data)
741 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
743 return TRUE;
746 static gpointer parent_class;
748 static void masked_pixmap_finialize(GObject *object)
750 MaskedPixmap *mp = (MaskedPixmap *) object;
752 if (mp->src_pixbuf)
754 g_object_unref(mp->src_pixbuf);
755 mp->src_pixbuf = NULL;
758 if (mp->huge_pixbuf)
760 g_object_unref(mp->huge_pixbuf);
761 mp->huge_pixbuf = NULL;
763 if (mp->huge_pixbuf_lit)
765 g_object_unref(mp->huge_pixbuf_lit);
766 mp->huge_pixbuf_lit = NULL;
769 if (mp->pixbuf)
771 g_object_unref(mp->pixbuf);
772 mp->pixbuf = NULL;
774 if (mp->pixbuf_lit)
776 g_object_unref(mp->pixbuf_lit);
777 mp->pixbuf_lit = NULL;
780 if (mp->sm_pixbuf)
782 g_object_unref(mp->sm_pixbuf);
783 mp->sm_pixbuf = NULL;
785 if (mp->sm_pixbuf_lit)
787 g_object_unref(mp->sm_pixbuf_lit);
788 mp->sm_pixbuf_lit = NULL;
791 G_OBJECT_CLASS(parent_class)->finalize(object);
794 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
796 GObjectClass *object = (GObjectClass *) gclass;
798 parent_class = g_type_class_peek_parent(gclass);
800 object->finalize = masked_pixmap_finialize;
803 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
805 MaskedPixmap *mp = (MaskedPixmap *) object;
807 mp->src_pixbuf = NULL;
809 mp->huge_pixbuf = NULL;
810 mp->huge_pixbuf_lit = NULL;
811 mp->huge_width = -1;
812 mp->huge_height = -1;
814 mp->pixbuf = NULL;
815 mp->pixbuf_lit = NULL;
816 mp->width = -1;
817 mp->height = -1;
819 mp->sm_pixbuf = NULL;
820 mp->sm_pixbuf_lit = NULL;
821 mp->sm_width = -1;
822 mp->sm_height = -1;
825 static GType masked_pixmap_get_type(void)
827 static GType type = 0;
829 if (!type)
831 static const GTypeInfo info =
833 sizeof (MaskedPixmapClass),
834 NULL, /* base_init */
835 NULL, /* base_finalise */
836 masked_pixmap_class_init,
837 NULL, /* class_finalise */
838 NULL, /* class_data */
839 sizeof(MaskedPixmap),
840 0, /* n_preallocs */
841 masked_pixmap_init
844 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
845 &info, 0);
848 return type;
851 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
853 MaskedPixmap *mp;
854 GdkPixbuf *src_pixbuf, *normal_pixbuf;
856 g_return_val_if_fail(full_size != NULL, NULL);
858 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
859 g_return_val_if_fail(src_pixbuf != NULL, NULL);
861 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
862 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
864 mp = g_object_new(masked_pixmap_get_type(), NULL);
866 mp->src_pixbuf = src_pixbuf;
868 mp->pixbuf = normal_pixbuf;
869 mp->pixbuf_lit = create_spotlight_pixbuf(normal_pixbuf, 0x000099, 128);
870 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
871 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
873 return mp;
876 /* Stolen from eel...and modified to colourize the pixbuf.
877 * 'alpha' is the transparency of 'color' (0xRRGGBB):
878 * 0 = fully opaque, 255 = fully transparent.
880 static GdkPixbuf *create_spotlight_pixbuf(GdkPixbuf *src,
881 guint32 color,
882 guchar alpha)
884 GdkPixbuf *dest;
885 int i, j;
886 int width, height, has_alpha, src_row_stride, dst_row_stride;
887 guchar *target_pixels, *original_pixels;
888 guchar *pixsrc, *pixdest;
889 guchar r, g, b;
890 gint n_channels;
892 n_channels = gdk_pixbuf_get_n_channels(src);
893 has_alpha = gdk_pixbuf_get_has_alpha(src);
894 width = gdk_pixbuf_get_width(src);
895 height = gdk_pixbuf_get_height(src);
897 g_return_val_if_fail(gdk_pixbuf_get_colorspace(src) ==
898 GDK_COLORSPACE_RGB, NULL);
899 g_return_val_if_fail((!has_alpha && n_channels == 3) ||
900 (has_alpha && n_channels == 4), NULL);
901 g_return_val_if_fail(gdk_pixbuf_get_bits_per_sample(src) == 8, NULL);
903 dest = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), has_alpha,
904 gdk_pixbuf_get_bits_per_sample(src),
905 width, height);
907 dst_row_stride = gdk_pixbuf_get_rowstride(dest);
908 src_row_stride = gdk_pixbuf_get_rowstride(src);
909 target_pixels = gdk_pixbuf_get_pixels(dest);
910 original_pixels = gdk_pixbuf_get_pixels(src);
912 r = (color & 0xff0000) >> 16;
913 g = (color & 0xff00) >> 8;
914 b = color & 0xff;
916 for (i = 0; i < height; i++)
918 gint tmp;
920 pixdest = target_pixels + i * dst_row_stride;
921 pixsrc = original_pixels + i * src_row_stride;
922 for (j = 0; j < width; j++)
924 tmp = (*pixsrc++ * alpha + r * (255 - alpha)) / 255;
925 *pixdest++ = (guchar) MIN(255, tmp);
926 tmp = (*pixsrc++ * alpha + g * (255 - alpha)) / 255;
927 *pixdest++ = (guchar) MIN(255, tmp);
928 tmp = (*pixsrc++ * alpha + b * (255 - alpha)) / 255;
929 *pixdest++ = (guchar) MIN(255, tmp);
930 if (has_alpha)
931 *pixdest++ = *pixsrc++;
935 return dest;
938 /* Load all the standard pixmaps. Also sets the default window icon. */
939 static void load_default_pixmaps(void)
941 GdkPixbuf *pixbuf;
942 GError *error = NULL;
944 im_error = mp_from_stock(GTK_STOCK_DIALOG_WARNING,
945 GTK_ICON_SIZE_DIALOG);
946 im_unknown = mp_from_stock(GTK_STOCK_DIALOG_QUESTION,
947 GTK_ICON_SIZE_DIALOG);
948 im_symlink = load_pixmap("symlink");
950 im_unmounted = mp_from_stock(ROX_STOCK_MOUNT, mount_icon_size);
951 im_mounted = mp_from_stock(ROX_STOCK_MOUNTED, mount_icon_size);
952 im_appdir = load_pixmap("application");
953 im_xattr = load_pixmap("rox-xattr");
955 im_dirs = load_pixmap("dirs");
957 pixbuf = gdk_pixbuf_new_from_file(
958 make_path(app_dir, ".DirIcon"), &error);
959 if (pixbuf)
961 GList *icon_list;
963 icon_list = g_list_append(NULL, pixbuf);
964 gtk_window_set_default_icon_list(icon_list);
965 g_list_free(icon_list);
967 g_object_unref(G_OBJECT(pixbuf));
969 else
971 g_warning("%s\n", error->message);
972 g_error_free(error);
976 /* Also purges memory cache */
977 static void purge_disk_cache(GtkWidget *button, gpointer data)
979 char *path;
980 GList *list = NULL;
981 DIR *dir;
982 struct dirent *ent;
984 g_fscache_purge(pixmap_cache, 0);
986 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
988 dir = opendir(path);
989 if (!dir)
991 report_error(_("Can't delete thumbnails in %s:\n%s"),
992 path, g_strerror(errno));
993 goto out;
996 while ((ent = readdir(dir)))
998 if (ent->d_name[0] == '.')
999 continue;
1000 list = g_list_prepend(list,
1001 g_strconcat(path, ent->d_name, NULL));
1004 closedir(dir);
1006 if (list)
1008 action_delete(list);
1009 destroy_glist(&list);
1011 else
1012 info_message(_("There are no thumbnails to delete"));
1013 out:
1014 g_free(path);
1017 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
1019 GtkWidget *button, *align;
1021 g_return_val_if_fail(option == NULL, NULL);
1023 align = gtk_alignment_new(0, 0.5, 0, 0);
1024 button = button_new_mixed(GTK_STOCK_CLEAR,
1025 _("Purge thumbnails disk cache"));
1026 gtk_container_add(GTK_CONTAINER(align), button);
1027 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
1029 return g_list_append(NULL, align);