Krolyxon
Commit e9a0096

Branches

Refs →

nsxivcommands.c

480 lines · 8.9 KB · C

1/* Copyright 2011-2020 Bert Muennich
2 * Copyright 2021-2022 nsxiv contributors
3 *
4 * This file is a part of nsxiv.
5 *
6 * nsxiv is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License,
9 * or (at your option) any later version.
10 *
11 * nsxiv 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 nsxiv. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "nsxiv.h"
21
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25#include <sys/wait.h>
26
27#include "commands.h"
28
29extern img_t img;
30extern tns_t tns;
31extern win_t win;
32
33bool cg_quit(arg_t status)
34{
35 unsigned int i;
36
37 if (options->to_stdout && markcnt > 0) {
38 for (i = 0; i < filecnt; i++) {
39 if (files[i].flags & FF_MARK)
40 printf("%s%c", files[i].name, options->using_null ? '\0' : '\n');
41 }
42 }
43 exit(status);
44 return None; /* silence tcc warning */
45}
46
47bool cg_switch_mode(arg_t _)
48{
49 if (mode == MODE_IMAGE) {
50 if (tns.thumbs == NULL)
51 tns_init(&tns, files, &filecnt, &fileidx, &win);
52 img_close(&img, false);
53 reset_timeout(reset_cursor);
54 if (img.ss.on) {
55 img.ss.on = false;
56 reset_timeout(slideshow);
57 }
58 tns.dirty = true;
59 mode = MODE_THUMB;
60 } else {
61 load_image(fileidx);
62 mode = MODE_IMAGE;
63 }
64 close_info();
65 open_info();
66 title_dirty = true;
67 return true;
68}
69
70bool cg_toggle_fullscreen(arg_t _)
71{
72 win_toggle_fullscreen(&win);
73 /* redraw after next ConfigureNotify event */
74 set_timeout(redraw, TO_REDRAW_RESIZE, false);
75 if (mode == MODE_IMAGE)
76 img.checkpan = img.dirty = true;
77 else
78 tns.dirty = true;
79 return false;
80}
81
82bool cg_toggle_bar(arg_t _)
83{
84 win_toggle_bar(&win);
85 if (mode == MODE_IMAGE) {
86 if (win.bar.h > 0)
87 open_info();
88 else
89 close_info();
90 img.checkpan = img.dirty = true;
91 } else {
92 tns.dirty = true;
93 }
94 return true;
95}
96
97bool cg_prefix_external(arg_t _)
98{
99 handle_key_handler(true);
100 return false;
101}
102
103bool cg_reload_image(arg_t _)
104{
105 if (mode == MODE_IMAGE) {
106 load_image(fileidx);
107 } else {
108 win_set_cursor(&win, CURSOR_WATCH);
109 if (!tns_load(&tns, fileidx, true, false)) {
110 remove_file(fileidx, false);
111 tns.dirty = true;
112 }
113 }
114 return true;
115}
116
117bool cg_remove_image(arg_t _)
118{
119 remove_file(fileidx, true);
120 if (mode == MODE_IMAGE)
121 load_image(fileidx);
122 else
123 tns.dirty = true;
124 return true;
125}
126
127bool cg_first(arg_t _)
128{
129 if (mode == MODE_IMAGE && fileidx != 0) {
130 load_image(0);
131 return true;
132 } else if (mode == MODE_THUMB && fileidx != 0) {
133 fileidx = 0;
134 tns.dirty = true;
135 return true;
136 } else {
137 return false;
138 }
139}
140
141bool cg_n_or_last(arg_t _)
142{
143 int n = prefix != 0 && prefix - 1 < filecnt ? prefix - 1 : filecnt - 1;
144
145 if (mode == MODE_IMAGE && fileidx != n) {
146 load_image(n);
147 return true;
148 } else if (mode == MODE_THUMB && fileidx != n) {
149 fileidx = n;
150 tns.dirty = true;
151 return true;
152 } else {
153 return false;
154 }
155}
156
157bool cg_scroll_screen(arg_t dir)
158{
159 if (mode == MODE_IMAGE)
160 return img_pan(&img, dir, -1);
161 else
162 return tns_scroll(&tns, dir, true);
163}
164
165bool cg_zoom(arg_t d)
166{
167 if (mode == MODE_THUMB)
168 return tns_zoom(&tns, d);
169 else
170 return img_zoom(&img, d);
171}
172
173bool cg_toggle_image_mark(arg_t _)
174{
175 return mark_image(fileidx, !(files[fileidx].flags & FF_MARK));
176}
177
178bool cg_reverse_marks(arg_t _)
179{
180 int i;
181
182 for (i = 0; i < filecnt; i++) {
183 files[i].flags ^= FF_MARK;
184 markcnt += files[i].flags & FF_MARK ? 1 : -1;
185 }
186 if (mode == MODE_THUMB)
187 tns.dirty = true;
188 return true;
189}
190
191bool cg_mark_range(arg_t _)
192{
193 int d = markidx < fileidx ? 1 : -1, end, i;
194 bool dirty = false, on = !!(files[markidx].flags & FF_MARK);
195
196 for (i = markidx + d, end = fileidx + d; i != end; i += d)
197 dirty |= mark_image(i, on);
198 return dirty;
199}
200
201bool cg_unmark_all(arg_t _)
202{
203 int i;
204
205 for (i = 0; i < filecnt; i++)
206 files[i].flags &= ~FF_MARK;
207 markcnt = 0;
208 if (mode == MODE_THUMB)
209 tns.dirty = true;
210 return true;
211}
212
213bool cg_navigate_marked(arg_t n)
214{
215 int d, i;
216 int new = fileidx;
217
218 if (prefix > 0)
219 n *= prefix;
220 d = n > 0 ? 1 : -1;
221 for (i = fileidx + d; n != 0 && i >= 0 && i < filecnt; i += d) {
222 if (files[i].flags & FF_MARK) {
223 n -= d;
224 new = i;
225 }
226 }
227 if (new != fileidx) {
228 if (mode == MODE_IMAGE) {
229 load_image(new);
230 } else {
231 fileidx = new;
232 tns.dirty = true;
233 }
234 return true;
235 } else {
236 return false;
237 }
238}
239
240bool cg_change_gamma(arg_t d)
241{
242 if (img_change_gamma(&img, d * (prefix > 0 ? prefix : 1))) {
243 if (mode == MODE_THUMB)
244 tns.dirty = true;
245 return true;
246 } else {
247 return false;
248 }
249}
250
251bool ci_navigate(arg_t n)
252{
253 if (prefix > 0)
254 n *= prefix;
255 n += fileidx;
256 n = MAX(0, MIN(n, filecnt - 1));
257
258 if (n != fileidx) {
259 load_image(n);
260 return true;
261 } else {
262 return false;
263 }
264}
265
266bool ci_cursor_navigate(arg_t _)
267{
268 return ci_navigate(nav_button() - 1);
269}
270
271bool ci_alternate(arg_t _)
272{
273 load_image(alternate);
274 return true;
275}
276
277bool ci_navigate_frame(arg_t d)
278{
279 if (prefix > 0)
280 d *= prefix;
281 return !img.multi.animate && img_frame_navigate(&img, d);
282}
283
284bool ci_toggle_animation(arg_t _)
285{
286 bool dirty = false;
287
288 if (img.multi.cnt > 0) {
289 img.multi.animate = !img.multi.animate;
290 if (img.multi.animate) {
291 dirty = img_frame_animate(&img);
292 set_timeout(animate, img.multi.frames[img.multi.sel].delay, true);
293 } else {
294 reset_timeout(animate);
295 }
296 }
297 return dirty;
298}
299
300bool ci_scroll(arg_t dir)
301{
302 return img_pan(&img, dir, prefix);
303}
304
305bool ci_scroll_to_center(arg_t _)
306{
307 return img_pan_center(&img);
308}
309
310bool ci_scroll_to_edge(arg_t dir)
311{
312 return img_pan_edge(&img, dir);
313}
314
315bool ci_drag(arg_t drag_mode)
316{
317 int x, y, ox, oy;
318 float px, py;
319 XEvent e;
320
321 if ((int)(img.w * img.zoom) <= win.w && (int)(img.h * img.zoom) <= win.h)
322 return false;
323
324 win_set_cursor(&win, drag_mode == DRAG_ABSOLUTE ? CURSOR_DRAG_ABSOLUTE : CURSOR_DRAG_RELATIVE);
325 win_cursor_pos(&win, &x, &y);
326 ox = x;
327 oy = y;
328
329 while (true) {
330 if (drag_mode == DRAG_ABSOLUTE) {
331 px = MIN(MAX(0.0, x - win.w*0.1), win.w*0.8) / (win.w*0.8)
332 * (win.w - img.w * img.zoom);
333 py = MIN(MAX(0.0, y - win.h*0.1), win.h*0.8) / (win.h*0.8)
334 * (win.h - img.h * img.zoom);
335 } else {
336 px = img.x + x - ox;
337 py = img.y + y - oy;
338 }
339
340 if (img_pos(&img, px, py)) {
341 img_render(&img);
342 win_draw(&win);
343 }
344 XMaskEvent(win.env.dpy,
345 ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e);
346 if (e.type == ButtonPress || e.type == ButtonRelease)
347 break;
348 while (XCheckTypedEvent(win.env.dpy, MotionNotify, &e));
349 ox = x;
350 oy = y;
351 x = e.xmotion.x;
352 y = e.xmotion.y;
353 }
354 set_timeout(reset_cursor, TO_CURSOR_HIDE, true);
355 reset_cursor();
356
357 return true;
358}
359
360bool ci_set_zoom(arg_t zl)
361{
362 return img_zoom_to(&img, (prefix ? prefix : zl) / 100.0);
363}
364
365bool ci_fit_to_win(arg_t sm)
366{
367 return img_fit_win(&img, sm);
368}
369
370bool ci_rotate(arg_t degree)
371{
372 img_rotate(&img, degree);
373 return true;
374}
375
376bool ci_flip(arg_t dir)
377{
378 img_flip(&img, dir);
379 return true;
380}
381
382bool ci_toggle_antialias(arg_t _)
383{
384 img_toggle_antialias(&img);
385 return true;
386}
387
388bool ci_toggle_alpha(arg_t _)
389{
390 img.alpha = !img.alpha;
391 img.dirty = true;
392 return true;
393}
394
395bool ci_slideshow(arg_t _)
396{
397 if (prefix > 0) {
398 img.ss.on = true;
399 img.ss.delay = prefix * 10;
400 set_timeout(slideshow, img.ss.delay * 100, true);
401 } else if (img.ss.on) {
402 img.ss.on = false;
403 reset_timeout(slideshow);
404 } else {
405 img.ss.on = true;
406 }
407 return true;
408}
409
410bool ct_move_sel(arg_t dir)
411{
412 bool dirty = tns_move_selection(&tns, dir, prefix);
413 if (dirty) {
414 close_info();
415 open_info();
416 }
417 return dirty;
418}
419
420bool ct_reload_all(arg_t _)
421{
422 tns_free(&tns);
423 tns_init(&tns, files, &filecnt, &fileidx, &win);
424 tns.dirty = true;
425 return true;
426}
427
428bool ct_scroll(arg_t dir)
429{
430 return tns_scroll(&tns, dir, false);
431}
432
433bool ct_drag_mark_image(arg_t _)
434{
435 int sel;
436
437 if ((sel = tns_translate(&tns, xbutton_ev->x, xbutton_ev->y)) >= 0) {
438 XEvent e;
439 bool on = !(files[sel].flags & FF_MARK);
440
441 while (true) {
442 if (sel >= 0 && mark_image(sel, on))
443 redraw();
444 XMaskEvent(win.env.dpy,
445 ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e);
446 if (e.type == ButtonPress || e.type == ButtonRelease)
447 break;
448 while (XCheckTypedEvent(win.env.dpy, MotionNotify, &e));
449 sel = tns_translate(&tns, e.xbutton.x, e.xbutton.y);
450 }
451 }
452
453 return false;
454}
455
456bool ct_select(arg_t _)
457{
458 int sel;
459 bool dirty = false;
460 static Time firstclick;
461
462 if ((sel = tns_translate(&tns, xbutton_ev->x, xbutton_ev->y)) >= 0) {
463 if (sel != fileidx) {
464 tns_highlight(&tns, fileidx, false);
465 tns_highlight(&tns, sel, true);
466 fileidx = sel;
467 firstclick = xbutton_ev->time;
468 dirty = true;
469 } else if (xbutton_ev->time - firstclick <= TO_DOUBLE_CLICK) {
470 mode = MODE_IMAGE;
471 set_timeout(reset_cursor, TO_CURSOR_HIDE, true);
472 load_image(fileidx);
473 dirty = true;
474 } else {
475 firstclick = xbutton_ev->time;
476 }
477 }
478
479 return dirty;
480}