r4379: Add a 'Manage Thumbnails' link to the Options box (Thomas Leonard).
[rox-filer/translations.git] / ROX-Filer / src / pixmaps.c
blob625b7cbf9be78c345af29886e14e3200cd524b58
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;
61 GFSCache *desktop_icon_cache = NULL;
63 static const char * bad_xpm[] = {
64 "12 12 3 1",
65 " c #000000000000",
66 ". c #FFFF00000000",
67 "x c #FFFFFFFFFFFF",
68 " ",
69 " ..xxxxxx.. ",
70 " ...xxxx... ",
71 " x...xx...x ",
72 " xx......xx ",
73 " xxx....xxx ",
74 " xxx....xxx ",
75 " xx......xx ",
76 " x...xx...x ",
77 " ...xxxx... ",
78 " ..xxxxxx.. ",
79 " "};
81 MaskedPixmap *im_error;
82 MaskedPixmap *im_unknown;
83 MaskedPixmap *im_symlink;
85 MaskedPixmap *im_unmounted;
86 MaskedPixmap *im_mounted;
87 MaskedPixmap *im_appdir;
88 MaskedPixmap *im_xattr;
90 MaskedPixmap *im_dirs;
92 typedef struct _ChildThumbnail ChildThumbnail;
94 /* There is one of these for each active child process */
95 struct _ChildThumbnail {
96 gchar *path;
97 GFunc callback;
98 gpointer data;
101 static const char *stocks[] = {
102 ROX_STOCK_SHOW_DETAILS,
103 ROX_STOCK_SHOW_HIDDEN,
104 ROX_STOCK_SELECT,
105 ROX_STOCK_MOUNT,
106 ROX_STOCK_MOUNTED,
109 static GtkIconSize mount_icon_size = -1;
111 /* Static prototypes */
113 static void load_default_pixmaps(void);
114 static gint purge(gpointer data);
115 static MaskedPixmap *image_from_file(const char *path);
116 static MaskedPixmap *image_from_desktop_file(const char *path);
117 static MaskedPixmap *get_bad_image(void);
118 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h);
119 static GdkPixbuf *get_thumbnail_for(const char *path);
120 static void thumbnail_child_done(ChildThumbnail *info);
121 static void child_create_thumbnail(const gchar *path);
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);
138 desktop_icon_cache = g_fscache_new((GFSLoadFunc) image_from_desktop_file, NULL, NULL);
140 g_timeout_add(10000, purge, NULL);
142 factory = gtk_icon_factory_new();
143 for (i = 0; i < G_N_ELEMENTS(stocks); i++)
145 GdkPixbuf *pixbuf;
146 GError *error = NULL;
147 gchar *path;
148 GtkIconSet *iset;
149 const gchar *name = stocks[i];
151 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
152 pixbuf = gdk_pixbuf_new_from_file(path, &error);
153 if (!pixbuf)
155 g_warning("%s", error->message);
156 g_error_free(error);
157 pixbuf = gdk_pixbuf_new_from_xpm_data(bad_xpm);
159 g_free(path);
161 iset = gtk_icon_set_new_from_pixbuf(pixbuf);
162 g_object_unref(G_OBJECT(pixbuf));
163 gtk_icon_factory_add(factory, name, iset);
164 gtk_icon_set_unref(iset);
166 gtk_icon_factory_add_default(factory);
168 mount_icon_size = gtk_icon_size_register("rox-mount-size", 14, 14);
170 load_default_pixmaps();
172 option_register_widget("thumbs-purge-cache", thumbs_purge_cache);
175 /* Load image <appdir>/images/name.png.
176 * Always returns with a valid image.
178 MaskedPixmap *load_pixmap(const char *name)
180 guchar *path;
181 MaskedPixmap *retval;
183 path = g_strconcat(app_dir, "/images/", name, ".png", NULL);
184 retval = image_from_file(path);
185 g_free(path);
187 if (!retval)
188 retval = get_bad_image();
190 return retval;
193 /* Create a MaskedPixmap from a GTK stock ID. Always returns
194 * a valid image.
196 static MaskedPixmap *mp_from_stock(const char *stock_id, int size)
198 GtkIconSet *icon_set;
199 GdkPixbuf *pixbuf;
200 MaskedPixmap *retval;
202 icon_set = gtk_icon_factory_lookup_default(stock_id);
203 if (!icon_set)
204 return get_bad_image();
206 pixbuf = gtk_icon_set_render_icon(icon_set,
207 gtk_widget_get_default_style(), /* Gtk bug */
208 GTK_TEXT_DIR_LTR,
209 GTK_STATE_NORMAL,
210 size,
211 NULL,
212 NULL);
213 retval = masked_pixmap_new(pixbuf);
214 gdk_pixbuf_unref(pixbuf);
216 return retval;
219 void pixmap_make_huge(MaskedPixmap *mp)
221 if (mp->huge_pixbuf)
222 return;
224 g_return_if_fail(mp->src_pixbuf != NULL);
226 /* Limit to small size now, otherwise they get scaled up in mixed mode.
227 * Also looked ugly.
229 mp->huge_pixbuf = scale_pixbuf_up(mp->src_pixbuf,
230 SMALL_WIDTH, SMALL_HEIGHT);
232 if (!mp->huge_pixbuf)
234 mp->huge_pixbuf = mp->src_pixbuf;
235 g_object_ref(mp->huge_pixbuf);
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_width = gdk_pixbuf_get_width(mp->sm_pixbuf);
258 mp->sm_height = gdk_pixbuf_get_height(mp->sm_pixbuf);
261 /* Load image 'path' in the background and insert into pixmap_cache.
262 * Call callback(data, path) when done (path is NULL => error).
263 * If the image is already uptodate, or being created already, calls the
264 * callback right away.
266 void pixmap_background_thumb(const gchar *path, GFunc callback, gpointer data)
268 gboolean found;
269 MaskedPixmap *image;
270 GdkPixbuf *pixbuf;
271 pid_t child;
272 ChildThumbnail *info;
273 MIME_type *type;
274 gchar *thumb_prog;
277 image = g_fscache_lookup_full(pixmap_cache, path,
278 FSCACHE_LOOKUP_ONLY_NEW, &found);
280 if (found)
282 /* Thumbnail is known, or being created */
283 if (image)
284 g_object_unref(image);
285 callback(data, NULL);
286 return;
289 g_return_if_fail(image == NULL);
291 pixbuf = get_thumbnail_for(path);
293 if (!pixbuf)
295 struct stat info1, info2;
296 char *dir;
298 dir = g_path_get_dirname(path);
300 /* If the image itself is in ~/.thumbnails, load it now
301 * (ie, don't create thumbnails for thumbnails!).
303 if (mc_stat(dir, &info1) != 0)
305 callback(data, NULL);
306 g_free(dir);
307 return;
309 g_free(dir);
311 if (mc_stat(make_path(home_dir, ".thumbnails/normal"),
312 &info2) == 0 &&
313 info1.st_dev == info2.st_dev &&
314 info1.st_ino == info2.st_ino)
316 pixbuf = rox_pixbuf_new_from_file_at_scale(path,
317 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, 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 g_object_unref(G_OBJECT(image));
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_filename_to_uri(path, NULL, NULL);
434 if (!uri)
435 uri = g_strconcat("file://", path, NULL);
436 md5 = md5_hash(uri);
437 g_free(path);
439 to = g_string_new(home_dir);
440 g_string_append(to, "/.thumbnails");
441 mkdir(to->str, 0700);
442 g_string_append(to, "/normal/");
443 mkdir(to->str, 0700);
444 g_string_append(to, md5);
445 name_len = to->len + 4; /* Truncate to this length when renaming */
446 g_string_append_printf(to, ".png.ROX-Filer-%ld", (long) getpid());
448 g_free(md5);
450 old_mask = umask(0077);
451 gdk_pixbuf_save(thumb, to->str, "png", NULL,
452 "tEXt::Thumb::Image::Width", swidth,
453 "tEXt::Thumb::Image::Height", sheight,
454 "tEXt::Thumb::Size", ssize,
455 "tEXt::Thumb::MTime", smtime,
456 "tEXt::Thumb::URI", uri,
457 "tEXt::Software", PROJECT,
458 NULL);
459 umask(old_mask);
461 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
462 * a race condition if two programs create the same thumb at
463 * once.
466 gchar *final;
468 final = g_strndup(to->str, name_len);
469 if (rename(to->str, final))
470 g_warning("Failed to rename '%s' to '%s': %s",
471 to->str, final, g_strerror(errno));
472 g_free(final);
475 g_string_free(to, TRUE);
476 g_free(swidth);
477 g_free(sheight);
478 g_free(ssize);
479 g_free(smtime);
480 g_free(uri);
483 static gchar *thumbnail_path(const char *path)
485 gchar *uri, *md5;
486 GString *to;
487 gchar *ans;
489 uri = g_filename_to_uri(path, NULL, NULL);
490 if(!uri)
491 uri = g_strconcat("file://", path, NULL);
492 md5 = md5_hash(uri);
494 to = g_string_new(home_dir);
495 g_string_append(to, "/.thumbnails");
496 mkdir(to->str, 0700);
497 g_string_append(to, "/normal/");
498 mkdir(to->str, 0700);
499 g_string_append(to, md5);
500 g_string_append(to, ".png");
502 g_free(md5);
503 g_free(uri);
505 ans=to->str;
506 g_string_free(to, FALSE);
508 return ans;
511 /* Return a program to create thumbnails for files of this type.
512 * NULL to try to make it ourself (using gdk).
513 * g_free the result.
515 static gchar *thumbnail_program(MIME_type *type)
517 gchar *leaf;
518 gchar *path;
520 if (!type)
521 return NULL;
523 leaf = g_strconcat(type->media_type, "_", type->subtype, NULL);
524 path = choices_find_xdg_path_load(leaf, "MIME-thumb", SITE);
525 g_free(leaf);
526 if (path)
528 return path;
531 path = choices_find_xdg_path_load(type->media_type, "MIME-thumb",
532 SITE);
534 return path;
537 /* Called in a subprocess. Load path and create the thumbnail
538 * file. Parent will notice when we die.
540 static void child_create_thumbnail(const gchar *path)
542 GdkPixbuf *image;
544 image = rox_pixbuf_new_from_file_at_scale(path,
545 PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
547 if (image)
548 save_thumbnail(path, image);
550 /* (no need to unref, as we're about to exit) */
553 /* Called when the child process exits */
554 static void thumbnail_child_done(ChildThumbnail *info)
556 GdkPixbuf *thumb;
558 thumb = get_thumbnail_for(info->path);
560 if (thumb)
562 MaskedPixmap *image;
564 image = masked_pixmap_new(thumb);
565 g_object_unref(thumb);
567 g_fscache_insert(pixmap_cache, info->path, image, FALSE);
568 g_object_unref(image);
570 info->callback(info->data, info->path);
572 else
573 info->callback(info->data, NULL);
575 g_free(info->path);
576 g_free(info);
579 /* Check if we have an up-to-date thumbnail for this image.
580 * If so, return it. Otherwise, returns NULL.
582 static GdkPixbuf *get_thumbnail_for(const char *pathname)
584 GdkPixbuf *thumb = NULL;
585 char *thumb_path, *md5, *uri, *path;
586 const char *ssize, *smtime;
587 struct stat info;
588 time_t ttime, now;
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 ttime=(time_t) atol(smtime);
618 time(&now);
619 if (info.st_mtime != ttime && now>ttime+PIXMAP_THUMB_TOO_OLD_TIME)
620 goto err;
622 if (info.st_size < atol(ssize))
623 goto err;
625 goto out;
626 err:
627 if (thumb)
628 gdk_pixbuf_unref(thumb);
629 thumb = NULL;
630 out:
631 g_free(path);
632 g_free(thumb_path);
633 return thumb;
636 /* Load the image 'path' and return a pointer to the resulting
637 * MaskedPixmap. NULL on failure.
638 * Doesn't check for thumbnails (this is for small icons).
640 static MaskedPixmap *image_from_file(const char *path)
642 GdkPixbuf *pixbuf;
643 MaskedPixmap *image;
644 GError *error = NULL;
646 pixbuf = gdk_pixbuf_new_from_file(path, &error);
647 if (!pixbuf)
649 g_warning("%s\n", error->message);
650 g_error_free(error);
651 return NULL;
654 image = masked_pixmap_new(pixbuf);
656 gdk_pixbuf_unref(pixbuf);
658 return image;
661 /* Load this icon named by this .desktop file from the current theme.
662 * NULL on failure.
664 static MaskedPixmap *image_from_desktop_file(const char *path)
666 GError *error = NULL;
667 MaskedPixmap *image = NULL;
668 char *icon = NULL;
670 icon = get_value_from_desktop_file(path,
671 "Desktop Entry", "Icon", &error);
672 if (error)
674 g_warning("Failed to parse .desktop file '%s':\n%s",
675 path, error->message);
676 goto err;
678 if (!icon)
679 goto err;
681 if (icon[0] == '/')
682 image = image_from_file(icon);
683 else
685 GdkPixbuf *pixbuf;
686 int tmp_fd;
687 char *extension;
689 /* For some unknown reason, some icon names have extensions.
690 * Remove them.
692 extension = strrchr(icon, '.');
693 if (extension && strcmp(extension, ".png") == 0)
695 *extension = '\0';
698 /* SVG reader is very noisy, so redirect stderr to stdout */
699 tmp_fd = dup(2);
700 dup2(1, 2);
701 pixbuf = gtk_icon_theme_load_icon(icon_theme, icon, HUGE_WIDTH,
702 0, NULL);
703 dup2(tmp_fd, 2);
704 close(tmp_fd);
706 if (pixbuf == NULL)
707 goto err; /* Might just not be in the theme */
709 image = masked_pixmap_new(pixbuf);
710 g_object_unref(pixbuf);
712 err:
713 if (error != NULL)
714 g_error_free(error);
715 if (icon != NULL)
716 g_free(icon);
717 return image;
720 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
721 * If src is small enough, then ref it and return that.
723 GdkPixbuf *scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
725 int w, h;
727 w = gdk_pixbuf_get_width(src);
728 h = gdk_pixbuf_get_height(src);
730 if (w <= max_w && h <= max_h)
732 gdk_pixbuf_ref(src);
733 return src;
735 else
737 float scale_x = ((float) w) / max_w;
738 float scale_y = ((float) h) / max_h;
739 float scale = MAX(scale_x, scale_y);
740 int dest_w = w / scale;
741 int dest_h = h / scale;
743 return gdk_pixbuf_scale_simple(src,
744 MAX(dest_w, 1),
745 MAX(dest_h, 1),
746 GDK_INTERP_BILINEAR);
750 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
751 * If src is that size or bigger, then ref it and return that.
753 static GdkPixbuf *scale_pixbuf_up(GdkPixbuf *src, int max_w, int max_h)
755 int w, h;
757 w = gdk_pixbuf_get_width(src);
758 h = gdk_pixbuf_get_height(src);
760 if (w == 0 || h == 0 || w >= max_w || h >= max_h)
762 gdk_pixbuf_ref(src);
763 return src;
765 else
767 float scale_x = max_w / ((float) w);
768 float scale_y = max_h / ((float) h);
769 float scale = MIN(scale_x, scale_y);
771 return gdk_pixbuf_scale_simple(src,
772 w * scale,
773 h * scale,
774 GDK_INTERP_BILINEAR);
778 /* Return a pointer to the (static) bad image. The ref counter will ensure
779 * that the image is never freed.
781 static MaskedPixmap *get_bad_image(void)
783 GdkPixbuf *bad;
784 MaskedPixmap *mp;
786 bad = gdk_pixbuf_new_from_xpm_data(bad_xpm);
787 mp = masked_pixmap_new(bad);
788 gdk_pixbuf_unref(bad);
790 return mp;
793 /* Called now and then to clear out old pixmaps */
794 static gint purge(gpointer data)
796 g_fscache_purge(pixmap_cache, PIXMAP_PURGE_TIME);
798 return TRUE;
801 static gpointer parent_class;
803 static void masked_pixmap_finialize(GObject *object)
805 MaskedPixmap *mp = (MaskedPixmap *) object;
807 if (mp->src_pixbuf)
809 g_object_unref(mp->src_pixbuf);
810 mp->src_pixbuf = NULL;
813 if (mp->huge_pixbuf)
815 g_object_unref(mp->huge_pixbuf);
816 mp->huge_pixbuf = NULL;
818 if (mp->pixbuf)
820 g_object_unref(mp->pixbuf);
821 mp->pixbuf = NULL;
824 if (mp->sm_pixbuf)
826 g_object_unref(mp->sm_pixbuf);
827 mp->sm_pixbuf = NULL;
830 G_OBJECT_CLASS(parent_class)->finalize(object);
833 static void masked_pixmap_class_init(gpointer gclass, gpointer data)
835 GObjectClass *object = (GObjectClass *) gclass;
837 parent_class = g_type_class_peek_parent(gclass);
839 object->finalize = masked_pixmap_finialize;
842 static void masked_pixmap_init(GTypeInstance *object, gpointer gclass)
844 MaskedPixmap *mp = (MaskedPixmap *) object;
846 mp->src_pixbuf = NULL;
848 mp->huge_pixbuf = NULL;
849 mp->huge_width = -1;
850 mp->huge_height = -1;
852 mp->pixbuf = NULL;
853 mp->width = -1;
854 mp->height = -1;
856 mp->sm_pixbuf = NULL;
857 mp->sm_width = -1;
858 mp->sm_height = -1;
861 static GType masked_pixmap_get_type(void)
863 static GType type = 0;
865 if (!type)
867 static const GTypeInfo info =
869 sizeof (MaskedPixmapClass),
870 NULL, /* base_init */
871 NULL, /* base_finalise */
872 masked_pixmap_class_init,
873 NULL, /* class_finalise */
874 NULL, /* class_data */
875 sizeof(MaskedPixmap),
876 0, /* n_preallocs */
877 masked_pixmap_init
880 type = g_type_register_static(G_TYPE_OBJECT, "MaskedPixmap",
881 &info, 0);
884 return type;
887 MaskedPixmap *masked_pixmap_new(GdkPixbuf *full_size)
889 MaskedPixmap *mp;
890 GdkPixbuf *src_pixbuf, *normal_pixbuf;
892 g_return_val_if_fail(full_size != NULL, NULL);
894 src_pixbuf = scale_pixbuf(full_size, HUGE_WIDTH, HUGE_HEIGHT);
895 g_return_val_if_fail(src_pixbuf != NULL, NULL);
897 normal_pixbuf = scale_pixbuf(src_pixbuf, ICON_WIDTH, ICON_HEIGHT);
898 g_return_val_if_fail(normal_pixbuf != NULL, NULL);
900 mp = g_object_new(masked_pixmap_get_type(), NULL);
902 mp->src_pixbuf = src_pixbuf;
904 mp->pixbuf = normal_pixbuf;
905 mp->width = gdk_pixbuf_get_width(normal_pixbuf);
906 mp->height = gdk_pixbuf_get_height(normal_pixbuf);
908 return mp;
911 /* Load all the standard pixmaps. Also sets the default window icon. */
912 static void load_default_pixmaps(void)
914 GdkPixbuf *pixbuf;
915 GError *error = NULL;
917 im_error = mp_from_stock(GTK_STOCK_DIALOG_WARNING,
918 GTK_ICON_SIZE_DIALOG);
919 im_unknown = mp_from_stock(GTK_STOCK_DIALOG_QUESTION,
920 GTK_ICON_SIZE_DIALOG);
921 im_symlink = load_pixmap("symlink");
923 im_unmounted = mp_from_stock(ROX_STOCK_MOUNT, mount_icon_size);
924 im_mounted = mp_from_stock(ROX_STOCK_MOUNTED, mount_icon_size);
925 im_appdir = load_pixmap("application");
926 im_xattr = load_pixmap("rox-xattr");
928 im_dirs = load_pixmap("dirs");
930 pixbuf = gdk_pixbuf_new_from_file(
931 make_path(app_dir, ".DirIcon"), &error);
932 if (pixbuf)
934 GList *icon_list;
936 icon_list = g_list_append(NULL, pixbuf);
937 gtk_window_set_default_icon_list(icon_list);
938 g_list_free(icon_list);
940 g_object_unref(G_OBJECT(pixbuf));
942 else
944 g_warning("%s\n", error->message);
945 g_error_free(error);
949 /* Also purges memory cache */
950 static void purge_disk_cache(GtkWidget *button, gpointer data)
952 char *path;
953 GList *list = NULL;
954 DIR *dir;
955 struct dirent *ent;
957 g_fscache_purge(pixmap_cache, 0);
959 path = g_strconcat(home_dir, "/.thumbnails/normal/", NULL);
961 dir = opendir(path);
962 if (!dir)
964 report_error(_("Can't delete thumbnails in %s:\n%s"),
965 path, g_strerror(errno));
966 goto out;
969 while ((ent = readdir(dir)))
971 if (ent->d_name[0] == '.')
972 continue;
973 list = g_list_prepend(list,
974 g_strconcat(path, ent->d_name, NULL));
977 closedir(dir);
979 if (list)
981 action_delete(list);
982 destroy_glist(&list);
984 else
985 info_message(_("There are no thumbnails to delete"));
986 out:
987 g_free(path);
990 static GList *thumbs_purge_cache(Option *option, xmlNode *node, guchar *label)
992 GtkWidget *button, *align;
994 g_return_val_if_fail(option == NULL, NULL);
996 align = gtk_alignment_new(0, 0.5, 0, 0);
997 button = button_new_mixed(GTK_STOCK_CLEAR,
998 _("Purge thumbnails disk cache"));
999 gtk_container_add(GTK_CONTAINER(align), button);
1000 g_signal_connect(button, "clicked", G_CALLBACK(purge_disk_cache), NULL);
1002 return g_list_append(NULL, align);