Krolyxon
Commit c03ec39

Branches

Refs →

nsxivcommands.c

487 lines · 9.1 KB · C

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