1 /* This file is part of GEGL editor -- a gtk frontend for GEGL
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <https://www.gnu.org/licenses/>.
15 *
16 * Copyright (C) 2019 Øyvind Kolås
17 */
18 #include <ctype.h>
19 #include <string.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <math.h>
27 #include <mrg.h>
28 #include <mrg-string.h>
29 #include <gegl.h>
30 #include <gexiv2/gexiv2.h>
31 #include <gegl-paramspecs.h>
32 #include <gegl-operation.h>
33 #include <gegl-audio-fragment.h>
34 #include "mrg-gegl.h"
35 #include "argvs.h"
36 #include "ui.h"
37
entry_load(MrgEvent * event,void * data1,void * data2)38 static void entry_load (MrgEvent *event, void *data1, void *data2)
39 {
40 GeState *o = data1;
41 char *newpath;
42 if (o->rev)
43 argvs_eval ("save");
44
45 o->entry_no = GPOINTER_TO_INT(data2);
46 newpath = get_item_path_no (o, o->entry_no);
47 g_free (o->path);
48 o->path = newpath;
49 ui_load_path (o);
50 mrg_event_stop_propagate (event);
51 mrg_queue_draw (event->mrg, NULL);
52 }
53
on_viewer_motion(MrgEvent * e,void * data1,void * data2)54 static void on_viewer_motion (MrgEvent *e, void *data1, void *data2)
55 {
56 GeState *o = data1;
57 {
58 if (!o->show_controls)
59 {
60 o->show_controls = 1;
61 mrg_queue_draw (o->mrg, NULL);
62 }
63 if (o->controls_timeout)
64 {
65 mrg_remove_idle (o->mrg, o->controls_timeout);
66 o->controls_timeout = 0;
67 }
68 o->controls_timeout = mrg_add_timeout (o->mrg, 2000, ui_hide_controls_cb, o);
69 }
70 }
71
72
fade_thumbbar_cb(Mrg * mrg,void * data)73 static int fade_thumbbar_cb (Mrg *mrg, void *data)
74 {
75 GeState *o = data;
76 o->show_thumbbar = 1;
77 mrg_queue_draw (o->mrg, NULL);
78 return 0;
79 }
80
on_thumbbar_motion(MrgEvent * e,void * data1,void * data2)81 static void on_thumbbar_motion (MrgEvent *e, void *data1, void *data2)
82 {
83 GeState *o = data1;
84 on_viewer_motion (e, data1, NULL);
85 {
86 o->show_thumbbar = 2;
87 if (o->thumbbar_timeout)
88 {
89 mrg_remove_idle (o->mrg, o->thumbbar_timeout);
90 o->thumbbar_timeout = 0;
91 }
92 o->thumbbar_timeout = mrg_add_timeout (o->mrg, 4000, fade_thumbbar_cb, o);
93 }
94 }
95
draw_edit(Mrg * mrg,float x,float y,float w,float h)96 static void draw_edit (Mrg *mrg, float x, float y, float w, float h)
97 {
98 cairo_t *cr = mrg_cr (mrg);
99 cairo_new_path (cr);
100 cairo_arc (cr, x+0.5*w, y+0.5*h, h * .4, 0.0, G_PI * 2);
101 }
102
draw_grid(Mrg * mrg,float x,float y,float w,float h)103 static void draw_grid (Mrg *mrg, float x, float y, float w, float h)
104 {
105 cairo_t *cr = mrg_cr (mrg);
106 cairo_new_path (cr);
107 cairo_rectangle (cr, 0.00 *w + x, 0.00 * h + y, 0.33 * w, 0.33 * h);
108 cairo_rectangle (cr, 0.66 *w + x, 0.00 * h + y, 0.33 * w, 0.33 * h);
109 cairo_rectangle (cr, 0.00 *w + x, 0.66 * h + y, 0.33 * w, 0.33 * h);
110 cairo_rectangle (cr, 0.66 *w + x, 0.66 * h + y, 0.33 * w, 0.33 * h);
111 }
112
113
draw_back(Mrg * mrg,float x,float y,float w,float h)114 static void draw_back (Mrg *mrg, float x, float y, float w, float h)
115 {
116 cairo_t *cr = mrg_cr (mrg);
117 cairo_new_path (cr);
118 cairo_new_path (cr);
119 cairo_move_to (cr, x+0.9*w, y+0.1*h);
120 cairo_line_to (cr, x+0.9*w, y+0.9*h);
121 cairo_line_to (cr, x+0.1*w, y+0.5*h);
122 }
123
draw_forward(Mrg * mrg,float x,float y,float w,float h)124 static void draw_forward (Mrg *mrg, float x, float y, float w, float h)
125 {
126 cairo_t *cr = mrg_cr (mrg);
127 cairo_new_path (cr);
128 cairo_move_to (cr, x+0.1*w, y+0.1*h);
129 cairo_line_to (cr, x+0.1*w, y+0.9*h);
130 cairo_line_to (cr, x+0.9*w, y+0.5*h);
131
132 }
133
on_thumbbar_drag(MrgEvent * e,void * data1,void * data2)134 static void on_thumbbar_drag (MrgEvent *e, void *data1, void *data2)
135 {
136 static float pinch_coord[4][2] = {0,};
137 static int pinch = 0;
138 static float orig_zoom = 1.0;
139
140 GeState *o = data1;
141 //GeglNode *node = data2;
142
143 on_viewer_motion (e, data1, data2);
144 if (e->type == MRG_DRAG_RELEASE)
145 {
146 pinch = 0;
147 } else if (e->type == MRG_DRAG_PRESS)
148 {
149 if (e->device_no == 5) /* 5 is second finger/touch point */
150 {
151 pinch_coord[1][0] = e->device_x;
152 pinch_coord[1][1] = e->device_y;
153 pinch_coord[2][0] = pinch_coord[0][0];
154 pinch_coord[2][1] = pinch_coord[0][1];
155 pinch_coord[3][0] = pinch_coord[1][0];
156 pinch_coord[3][1] = pinch_coord[1][1];
157 pinch = 1;
158 orig_zoom = o->graph_scale;
159 }
160 else if (e->device_no == 1 || e->device_no == 4) /* 1 is mouse pointer 4 is first finger */
161 {
162 pinch_coord[0][0] = e->device_x;
163 pinch_coord[0][1] = e->device_y;
164 }
165 } else if (e->type == MRG_DRAG_MOTION)
166 {
167 if (e->device_no == 1 || e->device_no == 4) /* 1 is mouse pointer 4 is first finger */
168 {
169 pinch_coord[0][0] = e->device_x;
170 pinch_coord[0][1] = e->device_y;
171 }
172 if (e->device_no == 5)
173 {
174 pinch_coord[1][0] = e->device_x;
175 pinch_coord[1][1] = e->device_y;
176 }
177
178 if (pinch)
179 {
180 float orig_dist = hypotf ( pinch_coord[2][0]- pinch_coord[3][0],
181 pinch_coord[2][1]- pinch_coord[3][1]);
182 float dist = hypotf (pinch_coord[0][0] - pinch_coord[1][0],
183 pinch_coord[0][1] - pinch_coord[1][1]);
184 {
185 float x, y;
186 float screen_cx = (pinch_coord[0][0] + pinch_coord[1][0])/2;
187 float screen_cy = (pinch_coord[0][1] + pinch_coord[1][1])/2;
188 //get_coords_graph (o, screen_cx, screen_cy, &x, &y);
189
190 x = (o->thumbbar_pan_x + screen_cx) / o->thumbbar_scale;
191 y = (o->thumbbar_pan_y + screen_cy) / o->thumbbar_scale;
192
193 o->thumbbar_scale = orig_zoom * (dist / orig_dist);
194
195 o->thumbbar_pan_x = x * o->thumbbar_scale - screen_cx;
196 o->thumbbar_pan_y = y * o->thumbbar_scale - screen_cy;
197
198 o->thumbbar_pan_x -= (e->delta_x )/2; /* doing half contribution of motion per finger */
199 o->thumbbar_pan_y -= (e->delta_y )/2; /* is simple and roughly right */
200 }
201
202 }
203 else
204 {
205 if (e->device_no == 1 || e->device_no == 4)
206 {
207 o->thumbbar_pan_x -= (e->delta_x );
208 o->thumbbar_pan_y -= (e->delta_y );
209 }
210 }
211 mrg_queue_draw (e->mrg, NULL);
212 }
213 mrg_event_stop_propagate (e);
214 }
215
216
on_thumbbar_scroll(MrgEvent * event,void * data1,void * data2)217 static void on_thumbbar_scroll (MrgEvent *event, void *data1, void *data2)
218 {
219 GeState *o = data1;
220 on_viewer_motion (event, data1, NULL);
221 switch (event->scroll_direction)
222 {
223 case MRG_SCROLL_DIRECTION_DOWN:
224 o->thumbbar_scale /= 1.1;
225 if (o->thumbbar_scale < 0.2)
226 o->thumbbar_scale = 0.2;
227 break;
228 case MRG_SCROLL_DIRECTION_UP:
229 o->thumbbar_scale *= 1.1;
230 if (o->thumbbar_scale > 3)
231 o->thumbbar_scale = 3;
232 break;
233 default:
234 break;
235 }
236 mrg_queue_draw (event->mrg, NULL);
237 mrg_event_stop_propagate (event);
238 }
239
draw_thumb_bar(GeState * o)240 static void draw_thumb_bar (GeState *o)
241 {
242 Mrg *mrg = o->mrg;
243 float width = mrg_width(mrg);
244 float height = mrg_height(mrg);
245 cairo_t *cr = mrg_cr (mrg);
246
247 float dim = height * 0.15 * o->thumbbar_scale;
248 float padding = .025;
249 float opacity;
250
251 cairo_save (cr);
252
253 if (o->show_thumbbar > 1)
254 {
255 opacity = o->thumbbar_opacity * (1.0 - 0.14) + 0.14 * 1.0;
256 if (opacity < 0.99)
257 mrg_queue_draw (o->mrg, NULL);
258 }
259 else
260 {
261 opacity = o->thumbbar_opacity * (1.0 - 0.07) + 0.07 * 0.00;
262 if (opacity > 0.02)
263 mrg_queue_draw (o->mrg, NULL);
264 }
265 o->thumbbar_opacity = opacity;
266
267 cairo_rectangle (cr, 0, height-dim, width, dim);
268 mrg_listen (mrg, MRG_DRAG, on_thumbbar_drag, o, NULL);
269 mrg_listen (mrg, MRG_SCROLL, on_thumbbar_scroll, o, NULL);
270 mrg_listen (mrg, MRG_DRAG, on_thumbbar_motion, o, NULL);
271 mrg_listen (mrg, MRG_MOTION, on_thumbbar_motion, o, NULL);
272 mrg_listen (mrg, MRG_SCROLL, on_thumbbar_motion, o, NULL);
273 cairo_new_path (cr);
274
275 if (opacity > 0.01)
276 {
277 float x = mrg_width(mrg)/2-dim/2 - o->thumbbar_pan_x;
278 int entry_no = o->entry_no;
279 int entries = g_list_length (o->index) + g_list_length (o->paths);
280
281 while (x < width && entry_no < entries)
282 {
283 char *upath = get_item_path_no (o, entry_no);
284 char *path = ui_suffix_path (upath);
285 char *thumbpath = ui_get_thumb_path (upath);
286 int w, h;
287
288 if (
289 access (thumbpath, F_OK) == 0 &&
290 mrg_query_image (mrg, thumbpath, &w, &h))
291 {
292
293 float wdim = dim, hdim = dim;
294 if (w > h) hdim = dim / (1.0 * w / h);
295 else wdim = dim * (1.0 * w / h);
296
297 if (w!=0 && h!=0)
298 {
299 cairo_rectangle (mrg_cr (mrg), x, height-dim, wdim, hdim);
300 if (entry_no == o->entry_no)
301 cairo_set_source_rgba (mrg_cr (mrg), 1,1,0,.7 * opacity);
302 else
303 cairo_set_source_rgba (mrg_cr (mrg), 1,1,1,.1 * opacity);
304 mrg_listen (mrg, MRG_TAP, entry_load, o, GINT_TO_POINTER(entry_no));
305 cairo_fill (mrg_cr (mrg));
306 mrg_image (mrg, x + dim * padding, height-dim*(1.0-padding),
307 wdim * (1.0-padding*2), hdim *(1.0-padding*2), opacity, thumbpath, NULL, NULL);
308 }
309 }
310 else
311 {
312 if (access (thumbpath, F_OK) != 0) // only queue if does not exist,
313 // mrg/stb_image seem to suffer on some of our pngs
314 {
315 ui_queue_thumb (upath);
316 }
317 }
318 x += dim;
319 g_free (thumbpath);
320 g_free (path);
321 g_free (upath);
322 entry_no ++;
323 }
324 x = mrg_width(mrg)/2-dim/2 - o->thumbbar_pan_x;
325 dim = height * 0.15 * o->thumbbar_scale;
326 x -= dim;
327 entry_no = o->entry_no-1;
328 while (x > -dim && entry_no >= 0)
329 {
330 char *upath = get_item_path_no (o, entry_no);
331 char *path = ui_suffix_path (upath);
332 char *thumbpath = ui_get_thumb_path (upath);
333 int w, h;
334
335 if (
336 access (thumbpath, F_OK) == 0 &&
337 mrg_query_image (mrg, thumbpath, &w, &h))
338 {
339
340 float wdim = dim, hdim = dim;
341 if (w > h) hdim = dim / (1.0 * w / h);
342 else wdim = dim * (1.0 * w / h);
343
344 if (w!=0 && h!=0)
345 {
346 cairo_rectangle (mrg_cr (mrg), x, height-dim, wdim, hdim);
347 if (entry_no == o->entry_no)
348 cairo_set_source_rgba (mrg_cr (mrg), 1,1,0,.7 * opacity);
349 else
350 cairo_set_source_rgba (mrg_cr (mrg), 1,1,1,.1 * opacity);
351 mrg_listen (mrg, MRG_TAP, entry_load, o, GINT_TO_POINTER(entry_no));
352 cairo_fill (mrg_cr (mrg));
353 mrg_image (mrg, x + dim * padding, height-dim*(1.0-padding),
354 wdim * (1.0-padding*2), hdim *(1.0-padding*2), opacity, thumbpath, NULL, NULL);
355 }
356 }
357 else
358 {
359 if (access (thumbpath, F_OK) != 0) // only queue if does not exist,
360 {
361 ui_queue_thumb (upath);
362 }
363 }
364 x -= dim;
365 g_free (thumbpath);
366 g_free (path);
367 g_free (upath);
368 entry_no --;
369 }
370 }
371 cairo_restore (cr);
372 }
373
on_timeline_drag(MrgEvent * e,void * data1,void * data2)374 static void on_timeline_drag (MrgEvent *e, void *data1, void *data2)
375 {
376 //static float pinch_coord[4][2] = {0,};
377 //static int pinch = 0;
378 //static float orig_zoom = 1.0;
379 GeState *o = data1;
380 //float pos = o->pos;
381 float end = o->duration;
382
383
384 on_viewer_motion (e, data1, data2);
385
386 if (e->type == MRG_DRAG_RELEASE)
387 {
388 //pinch = 0;
389 } else if (e->type == MRG_DRAG_PRESS)
390 {
391 } else if (e->type == MRG_DRAG_MOTION)
392 {
393 }
394
395 set_clip_position (o, e->x / mrg_width (o->mrg) * end);
396
397 mrg_event_stop_propagate (e);
398 }
399
draw_timeline(GeState * o)400 static void draw_timeline (GeState *o)
401 {
402 Mrg *mrg = o->mrg;
403 float width = mrg_width(mrg);
404 float height = mrg_height(mrg);
405 cairo_t *cr = mrg_cr (mrg);
406 float pos = o->pos;
407 float end = o->duration;
408
409 cairo_save (cr);
410 cairo_set_line_width (cr, 2);
411 cairo_new_path (cr);
412 cairo_rectangle (cr, 0, height * .9, width, height * .1);
413 cairo_set_source_rgba (cr, 1,1,1,.5);
414 mrg_listen (mrg, MRG_DRAG, on_timeline_drag, o, NULL);
415
416 cairo_fill (cr);
417
418 cairo_set_source_rgba (cr, 1,0,0,1);
419 cairo_rectangle (cr, width * pos/end, height * .9, 2, height * .1);
420 cairo_fill (cr);
421
422 cairo_restore (cr);
423 }
424
ui_viewer(GeState * o)425 void ui_viewer (GeState *o)
426 {
427 Mrg *mrg = o->mrg;
428 float width = mrg_width(mrg);
429 float height = mrg_height(mrg);
430 cairo_t *cr = mrg_cr (mrg);
431 cairo_save (cr);
432 cairo_rectangle (cr, 0,0, width, height);
433
434 draw_grid (mrg, height * 0.1/4, height * 0.1/4, height * 0.10, height * 0.10);
435 if (o->show_controls)
436 ui_contrasty_stroke (cr);
437 else
438 cairo_new_path (cr);
439 cairo_rectangle (cr, 0, 0, height * 0.15, height * 0.15);
440 if (o->show_controls)
441 {
442 cairo_set_source_rgba (cr, 1,1,1,.1);
443 cairo_fill_preserve (cr);
444 }
445 mrg_listen (mrg, MRG_PRESS, ui_run_command, "parent", NULL);
446
447 draw_back (mrg, height * .1 / 4, height * .5, height * .1, height *.1);
448 cairo_close_path (cr);
449 if (o->show_controls)
450 ui_contrasty_stroke (cr);
451 else
452 cairo_new_path (cr);
453 cairo_rectangle (cr, 0, height * .3, height * .15, height *.7);
454 if (o->show_controls)
455 {
456 cairo_set_source_rgba (cr, 1,1,1,.1);
457 cairo_fill_preserve (cr);
458 }
459 mrg_listen (mrg, MRG_TAP, ui_run_command, "prev", NULL);
460 cairo_new_path (cr);
461
462 draw_forward (mrg, width - height * .12, height * .5, height * .1, height *.1);
463 cairo_close_path (cr);
464 if (o->show_controls)
465 ui_contrasty_stroke (cr);
466 else
467 cairo_new_path (cr);
468 cairo_rectangle (cr, width - height * .15, height * .3, height * .15, height *.7);
469
470 if (o->show_controls)
471 {
472 cairo_set_source_rgba (cr, 1,1,1,.1);
473 cairo_fill_preserve (cr);
474 }
475 mrg_listen (mrg, MRG_TAP, ui_run_command, "next", NULL);
476 draw_edit (mrg, width - height * .15, height * .0, height * .15, height *.15);
477
478 if (o->show_controls)
479 ui_contrasty_stroke (cr);
480 else
481 cairo_new_path (cr);
482 cairo_rectangle (cr, width - height * .15, height * .0, height * .15, height *.15);
483 if (o->show_controls)
484 {
485 cairo_set_source_rgba (cr, 1,1,1,.1);
486 cairo_fill_preserve (cr);
487 }
488 mrg_listen (mrg, MRG_PRESS, ui_run_command, "toggle editing", NULL);
489 cairo_new_path (cr);
490
491 if (o->show_thumbbar)
492 draw_thumb_bar (o);
493
494 if (o->is_video && o->show_controls)
495 {
496 draw_timeline (o);
497 }
498
499
500 cairo_restore (cr);
501
502 mrg_add_binding (mrg, "control-s", NULL, NULL, ui_run_command, "toggle slideshow");
503
504 if (o->is_fit)
505 {
506 mrg_add_binding (mrg, "right", NULL, "next image", ui_run_command, "next");
507 mrg_add_binding (mrg, "left", NULL, "previous image", ui_run_command, "prev");
508 }
509
510 mrg_add_binding (mrg, "page-down", NULL, NULL, ui_run_command, "next");
511 mrg_add_binding (mrg, "page-up", NULL, NULL, ui_run_command, "prev");
512
513 mrg_add_binding (mrg, "alt-right", NULL, "next image", ui_run_command, "next");
514 mrg_add_binding (mrg, "alt-left", NULL, "previous image", ui_run_command, "prev");
515
516 if (o->commandline[0]==0)
517 {
518 mrg_add_binding (mrg, "+", NULL, NULL, ui_run_command, "zoom in");
519 mrg_add_binding (mrg, "=", NULL, NULL, ui_run_command, "zoom in");
520 mrg_add_binding (mrg, "-", NULL, NULL, ui_run_command, "zoom out");
521 mrg_add_binding (mrg, "8", NULL, "pixel for pixel", ui_run_command, "zoom 1.0");
522 mrg_add_binding (mrg, "9", NULL, NULL, ui_run_command, "zoom fit");
523 mrg_add_binding (mrg, "0", NULL, NULL, ui_run_command, "star 0");
524 mrg_add_binding (mrg, "1", NULL, NULL, ui_run_command, "star 1");
525 mrg_add_binding (mrg, "2", NULL, NULL, ui_run_command, "star 2");
526 mrg_add_binding (mrg, "3", NULL, NULL, ui_run_command, "star 3");
527 mrg_add_binding (mrg, "4", NULL, NULL, ui_run_command, "star 4");
528 mrg_add_binding (mrg, "5", NULL, NULL, ui_run_command, "star 5");
529 }
530
531 mrg_add_binding (mrg, "control-m", NULL, NULL, ui_run_command, "toggle mipmap");
532 mrg_add_binding (mrg, "control-y", NULL, NULL, ui_run_command, "toggle colormanaged-display");
533 mrg_add_binding (mrg, "control-delete", NULL, NULL, ui_run_command, "discard");
534 }
535