Krolyxon
Commit eee921e

Branches

Refs →

nsxivthumbs.c

411 lines · 8.8 KB · C

1/* sxiv: thumbs.c
2 * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <sys/time.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25
26#include "config.h"
27#include "thumbs.h"
28#include "util.h"
29
30extern Imlib_Image *im_invalid;
31
32const int thumb_dim = THUMB_SIZE + 10;
33char *cache_dir = NULL;
34
35int tns_cache_enabled() {
36 struct stat stats;
37
38 return cache_dir && !stat(cache_dir, &stats) && S_ISDIR(stats.st_mode) &&
39 !access(cache_dir, W_OK);
40}
41
42char* tns_cache_filename(const char *filename) {
43 size_t len;
44 char *cfile = NULL;
45 const char *abspath;
46
47 if (!cache_dir || !filename)
48 return NULL;
49
50 if (*filename != '/') {
51 if (!(abspath = absolute_path(filename)))
52 return NULL;
53 } else {
54 abspath = filename;
55 }
56
57 if (strncmp(abspath, cache_dir, strlen(cache_dir))) {
58 len = strlen(cache_dir) + strlen(abspath) + 6;
59 cfile = (char*) s_malloc(len);
60 snprintf(cfile, len, "%s/%s.png", cache_dir, abspath + 1);
61 }
62
63 if (abspath != filename)
64 free((void*) abspath);
65
66 return cfile;
67}
68
69Imlib_Image* tns_cache_load(const char *filename) {
70 char *cfile;
71 struct stat cstats, fstats;
72 Imlib_Image *im = NULL;
73
74 if (!filename || stat(filename, &fstats))
75 return NULL;
76
77 if ((cfile = tns_cache_filename(filename))) {
78 if (!stat(cfile, &cstats) &&
79 cstats.st_mtim.tv_sec == fstats.st_mtim.tv_sec &&
80 cstats.st_mtim.tv_nsec == fstats.st_mtim.tv_nsec)
81 {
82 printf("cache hit: %s\n", filename);
83 im = imlib_load_image(cfile);
84 } else
85 printf("cache MISS: %s\n", filename);
86 free(cfile);
87 }
88
89 return im;
90}
91
92void tns_cache_write(thumb_t *t, Bool force) {
93 char *cfile, *dirend;
94 struct stat cstats, fstats;
95 struct timeval times[2];
96 Imlib_Load_Error err = 0;
97
98 if (!t || !t->im || !t->filename)
99 return;
100 if (stat(t->filename, &fstats))
101 return;
102
103 if ((cfile = tns_cache_filename(t->filename))) {
104 if (force || stat(cfile, &cstats) ||
105 cstats.st_mtim.tv_sec != fstats.st_mtim.tv_sec ||
106 cstats.st_mtim.tv_nsec != fstats.st_mtim.tv_nsec)
107 {
108 if ((dirend = strrchr(cfile, '/'))) {
109 *dirend = '\0';
110 err = r_mkdir(cfile);
111 *dirend = '/';
112 }
113
114 if (!err) {
115 imlib_context_set_image(t->im);
116 imlib_image_set_format("png");
117 imlib_save_image_with_error_return(cfile, &err);
118 }
119
120 if (err) {
121 warn("could not cache thumbnail: %s", t->filename);
122 } else {
123 TIMESPEC_TO_TIMEVAL(&times[0], &fstats.st_atim);
124 TIMESPEC_TO_TIMEVAL(&times[1], &fstats.st_mtim);
125 utimes(cfile, times);
126 printf("thumbnail cache file written: %s\n", t->filename);
127 }
128 }
129 free(cfile);
130 }
131}
132
133
134void tns_init(tns_t *tns, int cnt) {
135 int len;
136 char *homedir;
137
138 if (!tns)
139 return;
140
141 tns->cnt = tns->first = tns->sel = 0;
142 tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
143 memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
144 tns->cap = cnt;
145 tns->dirty = 0;
146
147 if ((homedir = getenv("HOME"))) {
148 if (cache_dir)
149 free(cache_dir);
150 len = strlen(homedir) + 10;
151 cache_dir = (char*) s_malloc(len * sizeof(char));
152 snprintf(cache_dir, len, "%s/.sxiv", homedir);
153 }
154}
155
156void tns_free(tns_t *tns, win_t *win) {
157 int i;
158
159 if (!tns || !tns->thumbs)
160 return;
161
162 for (i = 0; i < tns->cnt; ++i) {
163 if (tns->thumbs[i].im) {
164 imlib_context_set_image(tns->thumbs[i].im);
165 imlib_free_image();
166 }
167 }
168
169 free(tns->thumbs);
170 tns->thumbs = NULL;
171
172 if (cache_dir) {
173 free(cache_dir);
174 cache_dir = NULL;
175 }
176}
177
178void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
179 int w, h;
180 int use_cache, cached = 0;
181 float z, zw, zh;
182 thumb_t *t;
183 Imlib_Image *im;
184
185 if (!tns || !win || !filename)
186 return;
187
188 if (n >= tns->cap)
189 return;
190 else if (n >= tns->cnt)
191 tns->cnt = n + 1;
192
193 t = &tns->thumbs[n];
194
195 if (t->im) {
196 imlib_context_set_image(t->im);
197 imlib_free_image();
198 }
199
200 if ((use_cache = tns_cache_enabled())) {
201 if ((im = tns_cache_load(filename)))
202 cached = 1;
203 }
204
205 if (cached || (im = imlib_load_image(filename)))
206 imlib_context_set_image(im);
207 else
208 imlib_context_set_image(im_invalid);
209
210 w = imlib_image_get_width();
211 h = imlib_image_get_height();
212
213 if (im) {
214 t->filename = filename;
215 zw = (float) THUMB_SIZE / (float) w;
216 zh = (float) THUMB_SIZE / (float) h;
217 z = MIN(zw, zh);
218 } else {
219 t->filename = NULL;
220 z = 1.0;
221 }
222
223 t->w = z * w;
224 t->h = z * h;
225
226 imlib_context_set_anti_alias(1);
227 if (!(t->im = imlib_create_cropped_scaled_image(0, 0, w, h, t->w, t->h)))
228 die("could not allocate memory");
229 if (im)
230 imlib_free_image_and_decache();
231 if (use_cache && !cached)
232 tns_cache_write(t, False);
233
234 tns->dirty = 1;
235}
236
237void tns_check_view(tns_t *tns, Bool scrolled) {
238 int r;
239
240 if (!tns)
241 return;
242
243 tns->first -= tns->first % tns->cols;
244 r = tns->sel % tns->cols;
245
246 if (scrolled) {
247 /* move selection into visible area */
248 if (tns->sel >= tns->first + tns->cols * tns->rows)
249 tns->sel = tns->first + r + tns->cols * (tns->rows - 1);
250 else if (tns->sel < tns->first)
251 tns->sel = tns->first + r;
252 } else {
253 /* scroll to selection */
254 if (tns->first + tns->cols * tns->rows <= tns->sel) {
255 tns->first = tns->sel - r - tns->cols * (tns->rows - 1);
256 tns->dirty = 1;
257 } else if (tns->first > tns->sel) {
258 tns->first = tns->sel - r;
259 tns->dirty = 1;
260 }
261 }
262}
263
264void tns_render(tns_t *tns, win_t *win) {
265 int i, cnt, r, x, y;
266 thumb_t *t;
267
268 if (!tns || !tns->dirty || !win)
269 return;
270
271 win_clear(win);
272 imlib_context_set_drawable(win->pm);
273
274 tns->cols = MAX(1, win->w / thumb_dim);
275 tns->rows = MAX(1, win->h / thumb_dim);
276
277 if (tns->cnt < tns->cols * tns->rows) {
278 tns->first = 0;
279 cnt = tns->cnt;
280 } else {
281 tns_check_view(tns, False);
282 cnt = tns->cols * tns->rows;
283 if ((r = tns->first + cnt - tns->cnt) >= tns->cols)
284 tns->first -= r - r % tns->cols;
285 if (r > 0)
286 cnt -= r % tns->cols;
287 }
288
289 r = cnt % tns->cols ? 1 : 0;
290 tns->x = x = (win->w - MIN(cnt, tns->cols) * thumb_dim) / 2 + 5;
291 tns->y = y = (win->h - (cnt / tns->cols + r) * thumb_dim) / 2 + 5;
292
293 for (i = 0; i < cnt; ++i) {
294 t = &tns->thumbs[tns->first + i];
295 t->x = x + (THUMB_SIZE - t->w) / 2;
296 t->y = y + (THUMB_SIZE - t->h) / 2;
297 imlib_context_set_image(t->im);
298 imlib_render_image_part_on_drawable_at_size(0, 0, t->w, t->h,
299 t->x, t->y, t->w, t->h);
300 if ((i + 1) % tns->cols == 0) {
301 x = tns->x;
302 y += thumb_dim;
303 } else {
304 x += thumb_dim;
305 }
306 }
307
308 tns->dirty = 0;
309 tns_highlight(tns, win, tns->sel, True);
310}
311
312void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
313 thumb_t *t;
314 unsigned long col;
315
316 if (!tns || !win)
317 return;
318
319 if (n >= 0 && n < tns->cnt) {
320 t = &tns->thumbs[n];
321
322 if (hl)
323 col = win->selcol;
324 else if (win->fullscreen)
325 col = win->black;
326 else
327 col = win->bgcol;
328
329 win_draw_rect(win, win->pm, t->x - 2, t->y - 2, t->w + 4, t->h + 4,
330 False, 2, col);
331 }
332
333 win_draw(win);
334}
335
336int tns_move_selection(tns_t *tns, win_t *win, tnsdir_t dir) {
337 int old;
338
339 if (!tns || !win)
340 return 0;
341
342 old = tns->sel;
343
344 switch (dir) {
345 case TNS_LEFT:
346 if (tns->sel > 0)
347 --tns->sel;
348 break;
349 case TNS_RIGHT:
350 if (tns->sel < tns->cnt - 1)
351 ++tns->sel;
352 break;
353 case TNS_UP:
354 if (tns->sel >= tns->cols)
355 tns->sel -= tns->cols;
356 break;
357 case TNS_DOWN:
358 if (tns->sel + tns->cols < tns->cnt)
359 tns->sel += tns->cols;
360 break;
361 }
362
363 if (tns->sel != old) {
364 tns_highlight(tns, win, old, False);
365 tns_check_view(tns, False);
366 if (!tns->dirty)
367 tns_highlight(tns, win, tns->sel, True);
368 }
369
370 return tns->sel != old;
371}
372
373int tns_scroll(tns_t *tns, tnsdir_t dir) {
374 int old;
375
376 if (!tns)
377 return 0;
378
379 old = tns->first;
380
381 if (dir == TNS_DOWN && tns->first + tns->cols * tns->rows < tns->cnt) {
382 tns->first += tns->cols;
383 tns_check_view(tns, True);
384 tns->dirty = 1;
385 } else if (dir == TNS_UP && tns->first >= tns->cols) {
386 tns->first -= tns->cols;
387 tns_check_view(tns, True);
388 tns->dirty = 1;
389 }
390
391 return tns->first != old;
392}
393
394int tns_translate(tns_t *tns, int x, int y) {
395 int n;
396 thumb_t *t;
397
398 if (!tns || x < tns->x || y < tns->y)
399 return -1;
400
401 n = tns->first + (y - tns->y) / thumb_dim * tns->cols +
402 (x - tns->x) / thumb_dim;
403
404 if (n < tns->cnt) {
405 t = &tns->thumbs[n];
406 if (x >= t->x && x <= t->x + t->w && y >= t->y && y <= t->y + t->h)
407 return n;
408 }
409
410 return -1;
411}