Krolyxon
Commit 3804b50

Branches

Refs →

nsxivcommands.c

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