Krolyxon
Commit c21a3e3

Branches

Refs →

nsxivthumbs.c

382 lines · 8.0 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;
31const int thumb_dim = THUMB_SIZE + 10;
32
33int tns_cache_enabled();
34void tns_cache_write(thumb_t*, Bool);
35
36void tns_init(tns_t *tns, int cnt) {
37 if (!tns)
38 return;
39
40 tns->cnt = tns->first = tns->sel = 0;
41 tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
42 memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
43 tns->cap = cnt;
44 tns->dirty = 0;
45}
46
47void tns_free(tns_t *tns, win_t *win) {
48 int i;
49 Bool cache;
50
51 if (!tns || !tns->thumbs)
52 return;
53
54 cache = tns_cache_enabled();
55
56 for (i = 0; i < tns->cnt; ++i) {
57 if (tns->thumbs[i].im) {
58 if (cache)
59 tns_cache_write(&tns->thumbs[i], False);
60 imlib_context_set_image(tns->thumbs[i].im);
61 imlib_free_image();
62 }
63 }
64
65 free(tns->thumbs);
66 tns->thumbs = NULL;
67}
68
69void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
70 int w, h;
71 float z, zw, zh;
72 thumb_t *t;
73 Imlib_Image *im;
74
75 if (!tns || !win || !filename)
76 return;
77
78 if (n >= tns->cap)
79 return;
80 else if (n >= tns->cnt)
81 tns->cnt = n + 1;
82
83 t = &tns->thumbs[n];
84
85 if (t->im) {
86 imlib_context_set_image(t->im);
87 imlib_free_image();
88 }
89
90 if ((im = imlib_load_image(filename)))
91 imlib_context_set_image(im);
92 else
93 imlib_context_set_image(im_invalid);
94
95 w = imlib_image_get_width();
96 h = imlib_image_get_height();
97
98 if (im) {
99 t->filename = filename;
100 zw = (float) THUMB_SIZE / (float) w;
101 zh = (float) THUMB_SIZE / (float) h;
102 z = MIN(zw, zh);
103 } else {
104 t->filename = NULL;
105 z = 1.0;
106 }
107
108 t->w = z * w;
109 t->h = z * h;
110
111 imlib_context_set_anti_alias(1);
112 if (!(t->im = imlib_create_cropped_scaled_image(0, 0, w, h, t->w, t->h)))
113 die("could not allocate memory");
114 if (im)
115 imlib_free_image_and_decache();
116
117 tns->dirty = 1;
118}
119
120void tns_check_view(tns_t *tns, Bool scrolled) {
121 int r;
122
123 if (!tns)
124 return;
125
126 tns->first -= tns->first % tns->cols;
127 r = tns->sel % tns->cols;
128
129 if (scrolled) {
130 /* move selection into visible area */
131 if (tns->sel >= tns->first + tns->cols * tns->rows)
132 tns->sel = tns->first + r + tns->cols * (tns->rows - 1);
133 else if (tns->sel < tns->first)
134 tns->sel = tns->first + r;
135 } else {
136 /* scroll to selection */
137 if (tns->first + tns->cols * tns->rows <= tns->sel) {
138 tns->first = tns->sel - r - tns->cols * (tns->rows - 1);
139 tns->dirty = 1;
140 } else if (tns->first > tns->sel) {
141 tns->first = tns->sel - r;
142 tns->dirty = 1;
143 }
144 }
145}
146
147void tns_render(tns_t *tns, win_t *win) {
148 int i, cnt, r, x, y;
149 thumb_t *t;
150
151 if (!tns || !tns->dirty || !win)
152 return;
153
154 win_clear(win);
155 imlib_context_set_drawable(win->pm);
156
157 tns->cols = MAX(1, win->w / thumb_dim);
158 tns->rows = MAX(1, win->h / thumb_dim);
159
160 if (tns->cnt < tns->cols * tns->rows) {
161 tns->first = 0;
162 cnt = tns->cnt;
163 } else {
164 tns_check_view(tns, False);
165 cnt = tns->cols * tns->rows;
166 if ((r = tns->first + cnt - tns->cnt) >= tns->cols)
167 tns->first -= r - r % tns->cols;
168 if (r > 0)
169 cnt -= r % tns->cols;
170 }
171
172 r = cnt % tns->cols ? 1 : 0;
173 tns->x = x = (win->w - MIN(cnt, tns->cols) * thumb_dim) / 2 + 5;
174 tns->y = y = (win->h - (cnt / tns->cols + r) * thumb_dim) / 2 + 5;
175
176 for (i = 0; i < cnt; ++i) {
177 t = &tns->thumbs[tns->first + i];
178 t->x = x + (THUMB_SIZE - t->w) / 2;
179 t->y = y + (THUMB_SIZE - t->h) / 2;
180 imlib_context_set_image(t->im);
181 imlib_render_image_part_on_drawable_at_size(0, 0, t->w, t->h,
182 t->x, t->y, t->w, t->h);
183 if ((i + 1) % tns->cols == 0) {
184 x = tns->x;
185 y += thumb_dim;
186 } else {
187 x += thumb_dim;
188 }
189 }
190
191 tns->dirty = 0;
192 tns_highlight(tns, win, tns->sel, True);
193}
194
195void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
196 thumb_t *t;
197 unsigned long col;
198
199 if (!tns || !win)
200 return;
201
202 if (n >= 0 && n < tns->cnt) {
203 t = &tns->thumbs[n];
204
205 if (hl)
206 col = win->selcol;
207 else if (win->fullscreen)
208 col = win->black;
209 else
210 col = win->bgcol;
211
212 win_draw_rect(win, win->pm, t->x - 2, t->y - 2, t->w + 4, t->h + 4,
213 False, 2, col);
214 }
215
216 win_draw(win);
217}
218
219int tns_move_selection(tns_t *tns, win_t *win, tnsdir_t dir) {
220 int old;
221
222 if (!tns || !win)
223 return 0;
224
225 old = tns->sel;
226
227 switch (dir) {
228 case TNS_LEFT:
229 if (tns->sel > 0)
230 --tns->sel;
231 break;
232 case TNS_RIGHT:
233 if (tns->sel < tns->cnt - 1)
234 ++tns->sel;
235 break;
236 case TNS_UP:
237 if (tns->sel >= tns->cols)
238 tns->sel -= tns->cols;
239 break;
240 case TNS_DOWN:
241 if (tns->sel + tns->cols < tns->cnt)
242 tns->sel += tns->cols;
243 break;
244 }
245
246 if (tns->sel != old) {
247 tns_highlight(tns, win, old, False);
248 tns_check_view(tns, False);
249 if (!tns->dirty)
250 tns_highlight(tns, win, tns->sel, True);
251 }
252
253 return tns->sel != old;
254}
255
256int tns_scroll(tns_t *tns, tnsdir_t dir) {
257 int old;
258
259 if (!tns)
260 return 0;
261
262 old = tns->first;
263
264 if (dir == TNS_DOWN && tns->first + tns->cols * tns->rows < tns->cnt) {
265 tns->first += tns->cols;
266 tns_check_view(tns, True);
267 tns->dirty = 1;
268 } else if (dir == TNS_UP && tns->first >= tns->cols) {
269 tns->first -= tns->cols;
270 tns_check_view(tns, True);
271 tns->dirty = 1;
272 }
273
274 return tns->first != old;
275}
276
277int tns_translate(tns_t *tns, int x, int y) {
278 int n;
279 thumb_t *t;
280
281 if (!tns || x < tns->x || y < tns->y)
282 return -1;
283
284 n = tns->first + (y - tns->y) / thumb_dim * tns->cols +
285 (x - tns->x) / thumb_dim;
286
287 if (n < tns->cnt) {
288 t = &tns->thumbs[n];
289 if (x >= t->x && x <= t->x + t->w && y >= t->y && y <= t->y + t->h)
290 return n;
291 }
292
293 return -1;
294}
295
296
297/* thumbnail caching */
298
299int tns_cache_enabled() {
300 int len, ret = 0;
301 char *cpath, *homedir;
302 struct stat stats;
303
304 if ((homedir = getenv("HOME"))) {
305 len = strlen(homedir) + 10;
306 cpath = (char*) s_malloc(len * sizeof(char));
307 snprintf(cpath, len, "%s/.sxiv", homedir);
308 ret = !stat(cpath, &stats) && S_ISDIR(stats.st_mode) &&
309 !access(cpath, W_OK);
310 free(cpath);
311 }
312
313 return ret;
314}
315
316char* tns_cache_filename(const char *filename) {
317 size_t len;
318 int i;
319 char *cfile, *abspath, *homedir;
320
321 if (!filename)
322 return NULL;
323 if (!(homedir = getenv("HOME")))
324 return NULL;
325
326 if (*filename != '/') {
327 if (!(abspath = absolute_path(filename)))
328 return NULL;
329 } else {
330 abspath = (char*) s_malloc(strlen(filename) + 1);
331 strcpy(abspath, filename);
332 }
333
334 len = strlen(abspath);
335 for (i = 1; i < len; ++i) {
336 if (abspath[i] == '/')
337 abspath[i] = '%';
338 }
339
340 len += strlen(homedir) + 15;
341 cfile = (char*) s_malloc(len);
342 snprintf(cfile, len, "%s/.sxiv/%s.png", homedir, abspath + 1);
343
344 free(abspath);
345
346 return cfile;
347}
348
349void tns_cache_write(thumb_t *t, Bool force) {
350 char *cfile;
351 struct stat cstats, fstats;
352 struct timeval times[2];
353 Imlib_Load_Error err;
354
355 if (!t || !t->im || !t->filename)
356 return;
357
358 if ((cfile = tns_cache_filename(t->filename))) {
359 if (stat(t->filename, &fstats))
360 goto end;
361
362 if (force || stat(cfile, &cstats) ||
363 cstats.st_mtim.tv_sec != fstats.st_mtim.tv_sec ||
364 cstats.st_mtim.tv_nsec != fstats.st_mtim.tv_nsec)
365 {
366 imlib_context_set_image(t->im);
367 imlib_image_set_format("png");
368 imlib_save_image_with_error_return(cfile, &err);
369
370 if (err) {
371 warn("could not cache thumbnail:", t->filename);
372 } else {
373 TIMESPEC_TO_TIMEVAL(&times[0], &fstats.st_atim);
374 TIMESPEC_TO_TIMEVAL(&times[1], &fstats.st_mtim);
375 utimes(cfile, times);
376 }
377 }
378 }
379
380end:
381 free(cfile);
382}