]> git.street.me.uk Git - andy/viking.git/blame - src/thumbnails.c
Make more text translatable
[andy/viking.git] / src / thumbnails.c
CommitLineData
50a14534
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22/*
23 * Large (and important) sections of this file were adapted from
24 * ROX-Filer source code, Copyright (C) 2003, the ROX-Filer team,
25 * originally licensed under the GPL v2 or greater (as above).
26 *
27 */
28
45acf79e
MA
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
50a14534 33#include <stdlib.h>
45acf79e
MA
34#ifdef HAVE_UNISTD_H
35#include <unistd.h>
36#endif
50a14534
EB
37#include <errno.h>
38#include <string.h>
f83131b9
MA
39#include <glib.h>
40#include <glib/gstdio.h>
50a14534
EB
41#include "viking.h"
42#include "thumbnails.h"
5bfafde9 43#include "icons/icons.h"
bd27baa4 44#include "md5_hash.h"
50a14534
EB
45
46#ifdef __CYGWIN__
47#ifdef __CYGWIN_USE_BIG_TYPES__
48#define ST_SIZE_FMT "%lld"
49#else
50#define ST_SIZE_FMT "%ld"
51#endif
52#else
53/* FIXME -- on some systems this may need to me "lld", see ROX-Filer code */
54#define ST_SIZE_FMT "%ld"
55#endif
56
57#undef MIN /* quit yer whining, gcc */
58#undef MAX
90e25247
GB
59#ifndef MAX
60/* We need MAX macro and some system does not offer it */
61#define MAX(a,b) (((a)>(b))?(a):(b))
62#endif
50a14534 63
118b901b
MA
64#define HOME_DIR g_get_home_dir()
65
50a14534 66#ifdef WINDOWS
50a14534
EB
67#define THUMB_DIR "\\THUMBNAILS\\" /* viking maps default viking\maps */
68#define THUMB_SUB_DIR "normal\\"
50a14534 69#else
50a14534
EB
70#define THUMB_DIR "/.thumbnails/"
71#define THUMB_SUB_DIR "normal/"
72#endif
73
74#define PIXMAP_THUMB_SIZE 128
75
50a14534
EB
76static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full);
77static GdkPixbuf *child_create_thumbnail(const gchar *path);
78
79gboolean a_thumbnails_exists ( const gchar *filename )
80{
81 GdkPixbuf *pixbuf = a_thumbnails_get(filename);
82 if ( pixbuf )
83 {
84 g_object_unref ( G_OBJECT ( pixbuf ) );
85 return TRUE;
86 }
87 return FALSE;
88}
89
90GdkPixbuf *a_thumbnails_get_default ()
91{
5bfafde9 92 return gdk_pixbuf_from_pixdata ( &thumbnails_pixbuf, FALSE, NULL );
50a14534
EB
93}
94
95/* filename must be absolute. you could have a function to make sure it exists and absolutize it */
96
97void a_thumbnails_create(const gchar *filename)
98{
99 GdkPixbuf *pixbuf = a_thumbnails_get(filename);
100
101 if ( ! pixbuf )
102 pixbuf = child_create_thumbnail(filename);
19096e42 103
50a14534
EB
104 if ( pixbuf )
105 g_object_unref ( G_OBJECT ( pixbuf ) );
106}
107
108GdkPixbuf *a_thumbnails_scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
109{
110 int w, h;
111
112 w = gdk_pixbuf_get_width(src);
113 h = gdk_pixbuf_get_height(src);
114
115 if (w <= max_w && h <= max_h)
116 {
f2fcc2d2 117 g_object_ref ( G_OBJECT ( src ) );
50a14534
EB
118 return src;
119 }
120 else
121 {
122 float scale_x = ((float) w) / max_w;
123 float scale_y = ((float) h) / max_h;
124 float scale = MAX(scale_x, scale_y);
125 int dest_w = w / scale;
126 int dest_h = h / scale;
127
128 return gdk_pixbuf_scale_simple(src,
129 MAX(dest_w, 1),
130 MAX(dest_h, 1),
131 GDK_INTERP_BILINEAR);
132 }
133}
134
135static GdkPixbuf *child_create_thumbnail(const gchar *path)
136{
6d0927b1 137 GdkPixbuf *image, *tmpbuf;
50a14534
EB
138
139 image = gdk_pixbuf_new_from_file(path, NULL);
778e43b4 140 if (!image)
19096e42 141 return NULL;
778e43b4 142
6d0927b1
GK
143 tmpbuf = gdk_pixbuf_apply_embedded_orientation(image);
144 g_object_unref(G_OBJECT(image));
145 image = tmpbuf;
50a14534
EB
146
147 if (image)
19096e42 148 {
50a14534 149 GdkPixbuf *thumb = save_thumbnail(path, image);
f2fcc2d2 150 g_object_unref ( G_OBJECT ( image ) );
50a14534
EB
151 return thumb;
152 }
153
154 return NULL;
155}
156
157static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full)
158{
159 struct stat info;
160 gchar *path;
161 int original_width, original_height;
6d0927b1 162 const gchar* orientation;
50a14534
EB
163 GString *to;
164 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
165 mode_t old_mask;
166 int name_len;
167 GdkPixbuf *thumb;
168
169 if (stat(pathname, &info) != 0)
170 return NULL;
171
172 thumb = a_thumbnails_scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
173
6d0927b1
GK
174 orientation = gdk_pixbuf_get_option (full, "orientation");
175
50a14534
EB
176 original_width = gdk_pixbuf_get_width(full);
177 original_height = gdk_pixbuf_get_height(full);
178
179
180 swidth = g_strdup_printf("%d", original_width);
181 sheight = g_strdup_printf("%d", original_height);
182 ssize = g_strdup_printf(ST_SIZE_FMT, info.st_size);
183 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
184
1b14d0d2 185 path = file_realpath_dup(pathname);
50a14534
EB
186 uri = g_strconcat("file://", path, NULL);
187 md5 = md5_hash(uri);
188 g_free(path);
19096e42 189
50a14534 190 to = g_string_new(HOME_DIR);
50a14534 191 g_string_append(to, THUMB_DIR);
50a14534 192 g_string_append(to, THUMB_SUB_DIR);
5e4cce8f
RN
193 if ( g_mkdir_with_parents(to->str, 0700) != 0 )
194 g_warning ("%s: Failed to mkdir %s", __FUNCTION__, to->str );
50a14534
EB
195 g_string_append(to, md5);
196 name_len = to->len + 4; /* Truncate to this length when renaming */
197#ifdef WINDOWS
198 g_string_append_printf(to, ".png.Viking");
199#else
200 g_string_append_printf(to, ".png.Viking-%ld", (long) getpid());
201#endif
202
203 g_free(md5);
204
d31be35e
RN
205 // Thumb::URI must be in ISO-8859-1 encoding otherwise gdk_pixbuf_save() will fail
206 // - e.g. if characters such as 'ě' are encountered
207 // Also see http://en.wikipedia.org/wiki/ISO/IEC_8859-1
86571e94
RN
208 // ATM GLIB Manual doesn't specify in which version this function became available
209 // find out that it's fairly recent so may break builds without this test
210#if GLIB_CHECK_VERSION(2,40,0)
d31be35e 211 char *thumb_uri = g_str_to_ascii ( uri, NULL );
86571e94
RN
212#else
213 char *thumb_uri = g_strdup ( uri );
214#endif
50a14534 215 old_mask = umask(0077);
56f6a97c
RN
216 GError *error = NULL;
217 gdk_pixbuf_save(thumb, to->str, "png", &error,
19096e42
RN
218 "tEXt::Thumb::Image::Width", swidth,
219 "tEXt::Thumb::Image::Height", sheight,
220 "tEXt::Thumb::Size", ssize,
221 "tEXt::Thumb::MTime", smtime,
d31be35e 222 "tEXt::Thumb::URI", thumb_uri,
19096e42
RN
223 "tEXt::Software", PROJECT,
224 "tEXt::Software::Orientation", orientation ? orientation : "0",
225 NULL);
50a14534 226 umask(old_mask);
d31be35e 227 g_free(thumb_uri);
50a14534 228
56f6a97c
RN
229 if (error) {
230 g_warning ( "%s::%s", __FUNCTION__, error->message );
231 g_error_free ( error );
232 g_object_unref ( G_OBJECT(thumb) );
233 thumb = NULL; /* return NULL */
234 }
235 else
236 /* We create the file ###.png.Viking-PID and rename it to avoid
50a14534
EB
237 * a race condition if two programs create the same thumb at
238 * once.
239 */
240 {
241 gchar *final;
242
243 final = g_strndup(to->str, name_len);
244 if (rename(to->str, final))
245 {
246 g_warning("Failed to rename '%s' to '%s': %s",
247 to->str, final, g_strerror(errno));
248 g_object_unref ( G_OBJECT(thumb) );
249 thumb = NULL; /* return NULL */
250 }
251
252 g_free(final);
253 }
254
255 g_string_free(to, TRUE);
256 g_free(swidth);
257 g_free(sheight);
258 g_free(ssize);
259 g_free(smtime);
260 g_free(uri);
261
262 return thumb;
263}
264
265
266GdkPixbuf *a_thumbnails_get(const gchar *pathname)
267{
268 GdkPixbuf *thumb = NULL;
269 char *thumb_path, *md5, *uri, *path;
270 const char *ssize, *smtime;
271 struct stat info;
272
1b14d0d2 273 path = file_realpath_dup(pathname);
50a14534
EB
274 uri = g_strconcat("file://", path, NULL);
275 md5 = md5_hash(uri);
276 g_free(uri);
19096e42 277
50a14534
EB
278 thumb_path = g_strdup_printf("%s%s%s%s.png", HOME_DIR, THUMB_DIR, THUMB_SUB_DIR, md5);
279
280 g_free(md5);
281
282 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
283 if (!thumb)
284 goto err;
285
286 /* Note that these don't need freeing... */
287 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
288 if (!ssize)
289 goto err;
290
291 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
292 if (!smtime)
293 goto err;
294
295 if (stat(path, &info) != 0)
296 goto err;
297
298 if (info.st_mtime != atol(smtime) || info.st_size != atol(ssize))
299 goto err;
300
301 goto out;
302err:
303 if (thumb)
f2fcc2d2 304 g_object_unref ( G_OBJECT ( thumb ) );
50a14534
EB
305 thumb = NULL;
306out:
307 g_free(path);
308 g_free(thumb_path);
309 return thumb;
310}