Update Portuguese translation
[gegl.git] / bin / ui-collection.c
blob671d2dc1fc074f99d8a3b1c9d59cc4d1956da75b
1 /* This file is part of GEGL editor -- a gtk frontend for GEGL
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.
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.
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/>.
16 * Copyright (C) 2019 Øyvind Kolås
19 #include <ctype.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <math.h>
28 #include <mrg.h>
29 #include <mrg-string.h>
30 #include <gegl.h>
31 #include <gexiv2/gexiv2.h>
32 #include <gegl-paramspecs.h>
33 #include <gegl-operation.h>
34 #include <gegl-audio-fragment.h>
35 #include "mrg-gegl.h"
36 #include "argvs.h"
37 #include "ui.h"
40 static int hack_cols = 5;
41 static float hack_dim = 5;
43 static void update_grid_dim (GeState *o)
45 hack_dim = mrg_height (o->mrg) * 0.2 * o->dir_scale;
46 hack_cols = mrg_width (o->mrg) / hack_dim;
49 static void draw_left_triangle (Mrg *mrg, float x, float y, float w, float h)
51 cairo_t *cr = mrg_cr (mrg);
52 cairo_new_path (cr);
53 cairo_new_path (cr);
54 cairo_move_to (cr, x+0.9*w, y+0.1*h);
55 cairo_line_to (cr, x+0.9*w, y+0.9*h);
56 cairo_line_to (cr, x+0.1*w, y+0.5*h);
59 static void draw_folder (Mrg *mrg, float x, float y, float w, float h)
61 cairo_t *cr = mrg_cr (mrg);
62 cairo_new_path (cr);
63 cairo_rectangle (cr, 0.00 *w + x, 0.00 * h + y, 0.33 * w, 0.10 * h);
64 cairo_rectangle (cr, 0.00 *w + x, 0.10 * h + y, 0.66 * w, 0.66 * h);
68 static void on_viewer_motion (MrgEvent *e, void *data1, void *data2)
70 GeState *o = data1;
72 if (!o->show_controls)
74 o->show_controls = 1;
75 mrg_queue_draw (o->mrg, NULL);
77 if (o->controls_timeout)
79 mrg_remove_idle (o->mrg, o->controls_timeout);
80 o->controls_timeout = 0;
82 o->controls_timeout = mrg_add_timeout (o->mrg, 2000, ui_hide_controls_cb, o);
86 static void entry_load (MrgEvent *event, void *data1, void *data2)
88 GeState *o = data1;
90 if (o->rev)
91 argvs_eval ("save");
93 g_free (o->path);
94 o->path = g_strdup (data2);
95 ui_load_path (o);
100 mrg_event_stop_propagate (event);
101 mrg_queue_draw (event->mrg, NULL);
104 static void entry_select (MrgEvent *event, void *data1, void *data2)
106 GeState *o = data1;
107 o->entry_no = GPOINTER_TO_INT (data2);
108 mrg_queue_draw (event->mrg, NULL);
111 static void on_dir_drag (MrgEvent *e, void *data1, void *data2)
113 static float zoom_pinch_coord[4][2] = {0,};
114 static int zoom_pinch = 0;
115 static float orig_zoom = 1.0;
117 GeState *o = data1;
118 if (e->type == MRG_DRAG_RELEASE)
120 zoom_pinch = 0;
121 mrg_queue_draw (e->mrg, NULL);
122 } else if (e->type == MRG_DRAG_PRESS)
124 if (e->device_no == 5)
126 zoom_pinch_coord[1][0] = e->x;
127 zoom_pinch_coord[1][1] = e->y;
129 zoom_pinch_coord[2][0] = zoom_pinch_coord[0][0];
130 zoom_pinch_coord[2][1] = zoom_pinch_coord[0][1];
131 zoom_pinch_coord[3][0] = zoom_pinch_coord[1][0];
132 zoom_pinch_coord[3][1] = zoom_pinch_coord[1][1];
134 zoom_pinch = 1;
137 orig_zoom = o->dir_scale;
139 else if (e->device_no == 1 || e->device_no == 4) /* 1 is mouse pointer 4 is first finger */
141 zoom_pinch_coord[0][0] = e->x;
142 zoom_pinch_coord[0][1] = e->y;
144 } else if (e->type == MRG_DRAG_MOTION)
146 if (e->device_no == 1 || e->device_no == 4) /* 1 is mouse pointer 4 is first finger */
148 zoom_pinch_coord[0][0] = e->x;
149 zoom_pinch_coord[0][1] = e->y;
151 if (e->device_no == 5)
153 zoom_pinch_coord[1][0] = e->x;
154 zoom_pinch_coord[1][1] = e->y;
157 if (zoom_pinch)
159 float orig_dist = hypotf ( zoom_pinch_coord[2][0]- zoom_pinch_coord[3][0],
160 zoom_pinch_coord[2][1]- zoom_pinch_coord[3][1]);
161 float dist = hypotf (zoom_pinch_coord[0][0] - zoom_pinch_coord[1][0],
162 zoom_pinch_coord[0][1] - zoom_pinch_coord[1][1]);
163 o->dir_scale = orig_zoom * dist / orig_dist;
164 if (o->dir_scale > 2) o->dir_scale = 2;
166 ui_center_active_entry (o);
167 o->u -= (e->delta_x )/2; /* doing half contribution of motion per finger */
168 o->v -= (e->delta_y )/2; /* is simple and roughly right */
170 else
172 if (e->device_no == 1 || e->device_no == 4)
174 o->u -= (e->delta_x );
175 o->v -= (e->delta_y );
179 /* auto-center */
181 int count = ui_items_count (o);
182 if (o->v < 0)
183 o->v = 0;
184 if (o->v > count/hack_cols * hack_dim - mrg_height(e->mrg)/2)
185 o->v = count/hack_cols * hack_dim - mrg_height(e->mrg)/2;
188 o->renderer_state = 0;
189 mrg_queue_draw (e->mrg, NULL);
190 mrg_event_stop_propagate (e);
192 // drag_preview (e);
196 static void dir_scroll_cb (MrgEvent *event, void *data1, void *data2)
198 switch (event->scroll_direction)
200 case MRG_SCROLL_DIRECTION_DOWN:
201 argvs_eval ("zoom out");
202 break;
203 case MRG_SCROLL_DIRECTION_UP:
204 argvs_eval ("zoom in");
205 break;
206 default:
207 break;
211 static void
212 copy_one_file (const char *file_path,
213 const char *dest_dir)
215 GError *error = NULL;
216 GFile *src = g_file_new_for_uri (file_path);
217 GFile *dstp = g_file_new_for_path (dest_dir);
218 GFile *dst = g_file_get_child (dstp, g_file_get_basename (src));
219 g_file_copy (src, dst, G_FILE_COPY_OVERWRITE , NULL, NULL, NULL, &error);
220 if (error)
222 g_warning ("file copy failed %s to %s %s\n",
223 file_path, dest_dir, error->message);
225 g_object_unref (src);
226 g_object_unref (dstp);
227 g_object_unref (dst);
230 static void dir_drop_cb (MrgEvent *event, void *data1, void *data2)
232 MrgString *str = mrg_string_new ("");
233 GeState *o = global_state;
234 const char *p;
236 for (p = event->string; *p; p++)
238 switch (*p)
240 case '\r':
241 case '\n':
242 if (str->str[0])
244 copy_one_file (str->str, get_item_dir (o));
245 mrg_string_set (str, "");
247 break;
248 default:
249 mrg_string_append_unichar (str, *p);
250 break;
253 if (str->str[0])
255 copy_one_file (str->str, get_item_dir (o));
257 mrg_string_free (str, TRUE);
259 populate_path_list (o);
262 static void dir_touch_handling (Mrg *mrg, GeState *o)
264 cairo_new_path (mrg_cr (mrg));
265 cairo_rectangle (mrg_cr (mrg), 0,0, mrg_width(mrg), mrg_height(mrg));
266 mrg_listen (mrg, MRG_DRAG, on_dir_drag, o, NULL);
267 mrg_listen (mrg, MRG_MOTION, on_viewer_motion, o, NULL);
268 mrg_listen (mrg, MRG_SCROLL, dir_scroll_cb, o, NULL);
270 mrg_listen (mrg, MRG_DROP, dir_drop_cb, o, NULL);
271 cairo_new_path (mrg_cr (mrg));
274 static int dir_scroll_dragged = 0;
275 static void on_dir_scroll_drag (MrgEvent *e, void *data1, void *data2)
277 GeState *o = data1;
278 switch (e->type)
280 default: break;
281 case MRG_DRAG_PRESS:
282 dir_scroll_dragged = 1;
283 break;
284 case MRG_DRAG_RELEASE:
285 dir_scroll_dragged = 0;
286 break;
287 case MRG_DRAG_MOTION:
289 int count = ui_items_count (o);
290 float height = mrg_height (e->mrg);
291 #if 0
292 y = height * ( o->v / (count/hack_cols * hack_dim) )
294 y = height * ( o->v / (count/hack_cols * hack_dim) )
295 y/height = ( o->v / (count/hack_cols * hack_dim) )
296 y/height * (count/hack_cols * hack_dim) = o->v;
297 #endif
299 o->v += e->delta_y /height * (count/hack_cols * hack_dim);
301 if (o->v < 0)
302 o->v = 0;
303 if (o->v > count/hack_cols * hack_dim - height/2)
304 o->v = count/hack_cols * hack_dim - height/2;
306 break;
309 mrg_event_stop_propagate (e);
313 void ui_collection (GeState *o)
315 Mrg *mrg = o->mrg;
316 cairo_t *cr = mrg_cr (mrg);
317 float dim;
318 int cols;
319 int no = 0;
320 int count;
321 float padding = 0.025;
322 float em = mrg_em (mrg);
323 dir_touch_handling (mrg, o);
325 update_grid_dim (o);
326 cols = hack_cols;
327 dim = hack_dim;
329 count = ui_items_count (o);
331 cairo_save (cr);
332 cairo_translate (cr, 0, -(int)o->v);
334 float x = dim * (no%cols);
335 float y = dim * (no/cols);
336 float wdim = dim * .6;
337 float hdim = dim * .6;
339 cairo_new_path (mrg_cr(mrg));
341 cairo_rectangle (mrg_cr (mrg), x, y, dim, dim);
342 if (no == o->entry_no + 1)
344 cairo_set_source_rgba (mrg_cr (mrg), 1,1,0,.5);
345 cairo_fill_preserve (mrg_cr (mrg));
347 mrg_listen_full (mrg, MRG_CLICK, ui_run_command, "parent", NULL, NULL, NULL);
349 draw_left_triangle (mrg, x + (dim-wdim)/2 + dim * padding, y + (dim-hdim)/2 + dim * padding,
350 wdim * (1.0-padding*2), hdim *(1.0-padding*2));
351 cairo_set_source_rgba (mrg_cr (mrg), 1,1,1,.5);
352 cairo_fill (mrg_cr (mrg));
353 #if 0
354 mrg_image (mrg, x + (dim-wdim)/2 + dim * padding, y + (dim-hdim)/2 + dim * padding,
355 wdim * (1.0-padding*2), hdim *(1.0-padding*2), 1.0,
356 "/usr/share/icons/HighContrast/256x256/actions/go-up.png", NULL, NULL);
357 #endif
359 cairo_new_path (mrg_cr(mrg));
360 mrg_set_xy (mrg, x, y + dim - mrg_em(mrg) * 2);
361 mrg_printf (mrg, "parent\nfolder");
362 no++;
365 for (int idx = 0; idx < count; idx ++, no++)
367 char *basename = meta_get_child (o, o->path, idx);
368 struct stat stat_buf;
369 int w, h;
370 gchar *path = g_strdup_printf ("%s/%s", o->path, basename);
371 char *lastslash = strrchr (path, '/');
372 float x = dim * (no%cols);
373 float y = dim * (no/cols);
374 int is_dir = 0;
375 g_free (basename);
377 if (y < -dim * 4 + o->v || y > mrg_height (mrg) + dim * 1.5 + o->v)
379 g_free (path);
380 continue;
383 lstat (path, &stat_buf);
386 if (S_ISDIR (stat_buf.st_mode))
388 float wdim = dim * .6;
389 float hdim = dim * .6;
391 cairo_rectangle (mrg_cr (mrg), x, y, dim, dim);
392 if (no == o->entry_no + 1)
394 cairo_set_source_rgba (mrg_cr (mrg), 1,1,0,.5);
395 cairo_fill (mrg_cr (mrg));
398 draw_folder (mrg, x + (dim-wdim)/2 + dim * padding, y + (dim-hdim)/2 + dim * padding,
399 wdim * (1.0-padding*2), hdim *(1.0-padding*2));
400 cairo_set_source_rgba (mrg_cr (mrg), 1,1,1,.5);
401 cairo_fill (mrg_cr (mrg));
402 #if 0
403 mrg_image (mrg, x + (dim-wdim)/2 + dim * padding, y + (dim-hdim)/2 + dim * padding,
404 wdim * (1.0-padding*2), hdim *(1.0-padding*2), 1.0,
405 "/usr/share/icons/HighContrast/256x256/places/folder.png", NULL, NULL);
406 #endif
408 is_dir = 1;
410 else
412 struct stat thumb_stat_buf;
413 struct stat suffixed_stat_buf;
415 gchar *p2 = ui_suffix_path (path);
416 gchar *thumbpath = ui_get_thumb_path (path);
418 /* we compute the thumbpath as the hash of the suffixed path, even for
419 * gegl documents - for gegl documents this is slightly inaccurate but consistent.
421 if (g_file_test (thumbpath, G_FILE_TEST_IS_REGULAR))
423 int suffix_exist = 0;
424 lstat (thumbpath, &thumb_stat_buf);
425 if (lstat (p2, &suffixed_stat_buf) == 0)
426 suffix_exist = 1;
428 if ((suffix_exist &&
429 (suffixed_stat_buf.st_mtime > thumb_stat_buf.st_mtime)) ||
430 (stat_buf.st_mtime > thumb_stat_buf.st_mtime))
432 unlink (thumbpath);
433 mrg_forget_image (mrg, thumbpath);
436 g_free (p2);
438 if (
439 access (thumbpath, F_OK) == 0 && //XXX: query image should suffice
440 mrg_query_image (mrg, thumbpath, &w, &h))
442 float wdim = dim;
443 float hdim = dim;
445 if (w > h)
446 hdim = dim / (1.0 * w / h);
447 else
448 wdim = dim * (1.0 * w / h);
450 cairo_rectangle (mrg_cr (mrg), x, y, wdim, hdim);
452 if (no == o->entry_no + 1)
454 cairo_set_source_rgba (mrg_cr (mrg), 1,1,0,1.0);
455 cairo_fill_preserve (mrg_cr (mrg));
458 mrg_listen (mrg, MRG_TAP, entry_load, o, (void*)g_intern_string (path));
459 cairo_new_path (mrg_cr (mrg));
461 if (w!=0 && h!=0)
462 mrg_image (mrg, x + (dim-wdim)/2 + dim * padding, y + (dim-hdim)/2 + dim * padding,
463 wdim * (1.0-padding*2), hdim *(1.0-padding*2), 1.0, thumbpath, NULL, NULL);
467 else
469 if (!g_file_test (thumbpath, G_FILE_TEST_IS_REGULAR))
471 ui_queue_thumb (path);
474 g_free (thumbpath);
478 if (no == o->entry_no + 1 || is_dir)
480 mrg_set_xy (mrg, x, y + dim - mrg_em(mrg));
481 mrg_printf (mrg, "%s\n", lastslash+1);
483 int stars = meta_get_key_int (o, path, "stars");
484 if (stars >= 0)
486 mrg_start (mrg, "div.collstars", NULL);
487 mrg_set_xy (mrg, x + mrg_em (mrg) * .2, y + mrg_em(mrg) * 1.5);
488 for (int i = 0; i < stars; i ++)
490 mrg_printf (mrg, "★", lastslash+1);
492 mrg_set_style (mrg, "color:gray;");
493 for (int i = stars; i < 5; i ++)
495 mrg_printf (mrg, "★", lastslash+1);
497 mrg_end (mrg);
499 else if (!is_dir)
501 mrg_start (mrg, "div.collstars", NULL);
502 mrg_set_xy (mrg, x + mrg_em (mrg) * .2, y + mrg_em(mrg) * 1.5);
503 mrg_set_style (mrg, "color:gray;");
504 for (int i = 0; i < 5; i ++)
506 mrg_printf (mrg, "★", lastslash+1);
508 mrg_end (mrg);
512 cairo_new_path (mrg_cr(mrg));
513 cairo_rectangle (mrg_cr(mrg), x, y, dim, dim);
514 #if 0
515 if (no == o->entry_no + 1)
516 cairo_set_source_rgb (mrg_cr(mrg), 1, 1,0);
517 else
518 cairo_set_source_rgb (mrg_cr(mrg), 0, 0,0);
519 cairo_set_line_width (mrg_cr(mrg), 4);
520 cairo_stroke_preserve (mrg_cr(mrg));
521 #endif
522 if (no == o->entry_no + 1)
523 mrg_listen_full (mrg, MRG_TAP, entry_load, o, (void*)g_intern_string (path), NULL, NULL);
524 else
525 mrg_listen_full (mrg, MRG_TAP, entry_select, o, GINT_TO_POINTER(no-1), NULL, NULL);
526 cairo_new_path (mrg_cr(mrg));
527 g_free (path);
530 cairo_restore (cr);
533 float height = mrg_height(mrg) * ( mrg_height (mrg) / (count/cols * dim) );
534 float yoffset = 0;
535 if (height < 4 * em)
537 yoffset = (4 * em - height)/2;
538 height = 4 * em;
540 cairo_rectangle (cr,
541 mrg_width(mrg) - 4 * em,
542 mrg_height(mrg) * ( o->v / (count/cols * dim) ) - yoffset,
543 4 * em,
544 height);
546 cairo_set_source_rgba (cr, 1,1,1, dir_scroll_dragged?.3:.2);
547 mrg_listen (mrg, MRG_DRAG, on_dir_scroll_drag, o, NULL);
548 cairo_fill (cr);
550 mrg_add_binding (mrg, "control-left", NULL, NULL, ui_run_command, "colswap prev");
551 mrg_add_binding (mrg, "control-right", NULL, NULL, ui_run_command, "colswap next");
553 mrg_add_binding (mrg, "left", NULL, NULL, ui_run_command, "collection left");
554 mrg_add_binding (mrg, "right", NULL, NULL, ui_run_command, "collection right");
555 mrg_add_binding (mrg, "up", NULL, NULL, ui_run_command, "collection up");
556 mrg_add_binding (mrg, "down", NULL, NULL, ui_run_command, "collection down");
558 mrg_add_binding (mrg, "page-up", NULL, NULL, ui_run_command, "collection page-up");
559 mrg_add_binding (mrg, "page-down", NULL, NULL, ui_run_command, "collection page-down");
561 mrg_add_binding (mrg, "home", NULL, NULL, ui_run_command, "collection first");
562 mrg_add_binding (mrg, "end", NULL, NULL, ui_run_command, "collection last");
564 if (o->commandline[0] == 0)
566 mrg_add_binding (mrg, "space", NULL, NULL, ui_run_command, "collection right");
567 mrg_add_binding (mrg, "backspace", NULL, NULL, ui_run_command, "collection left");
570 mrg_add_binding (mrg, "alt-right", NULL, NULL, ui_run_command, "collection right");
571 mrg_add_binding (mrg, "alt-left", NULL, NULL, ui_run_command, "collection left");
573 if (o->commandline[0]==0)
575 mrg_add_binding (mrg, "+", NULL, NULL, ui_run_command, "zoom in");
576 mrg_add_binding (mrg, "=", NULL, NULL, ui_run_command, "zoom in");
577 mrg_add_binding (mrg, "-", NULL, NULL, ui_run_command, "zoom out");
580 mrg_add_binding (mrg, "0", NULL, NULL, ui_run_command, "star 0");
581 mrg_add_binding (mrg, "1", NULL, NULL, ui_run_command, "star 1");
582 mrg_add_binding (mrg, "2", NULL, NULL, ui_run_command, "star 2");
583 mrg_add_binding (mrg, "3", NULL, NULL, ui_run_command, "star 3");
584 mrg_add_binding (mrg, "4", NULL, NULL, ui_run_command, "star 4");
585 mrg_add_binding (mrg, "5", NULL, NULL, ui_run_command, "star 5");
587 mrg_add_binding (mrg, "escape", NULL, "parent folder", ui_run_command, "parent");
588 mrg_add_binding (mrg, "control-delete", NULL, NULL, ui_run_command, "discard");
592 int cmd_collection (COMMAND_ARGS); /* "collection", -1, "<up|left|right|down|first|last>", ""*/
593 int cmd_collection (COMMAND_ARGS)
595 GeState *o = global_state;
597 if (!argv[1])
599 printf ("current item: %i\n", o->entry_no);
600 return 0;
602 if (!strcmp(argv[1], "first"))
604 o->entry_no = -1;
606 else if (!strcmp(argv[1], "last"))
608 o->entry_no = ui_items_count (o)-1;
610 else if (!strcmp(argv[1], "right"))
612 o->entry_no++;
614 else if (!strcmp(argv[1], "left"))
616 o->entry_no--;
618 else if (!strcmp(argv[1], "up"))
620 o->entry_no-= hack_cols;
622 else if (!strcmp(argv[1], "down"))
624 o->entry_no+= hack_cols;
627 if (o->entry_no < -1)
628 o->entry_no = -1;
630 if (o->entry_no >= ui_items_count (o))
631 o->entry_no = ui_items_count (o)-1;
633 ui_center_active_entry (o);
635 mrg_queue_draw (o->mrg, NULL);
636 return 0;
640 int cmd_colswap (COMMAND_ARGS); /* "colswap", 1, "<prev|next>", "swap with previous or next collection item "*/
642 cmd_colswap (COMMAND_ARGS)
644 GeState *o = global_state;
645 if (!strcmp (argv[1], "prev"))
647 if (o->entry_no <= 0)
648 return 0;
649 else
651 char *dirname = get_item_dir (o);
652 meta_swap_children (o, dirname, o->entry_no-1, NULL, o->entry_no, NULL);
653 g_free (dirname);
654 o->entry_no--;
657 else if (!strcmp (argv[1], "next"))
659 if (o->entry_no + 1>= g_list_length (o->index))
660 return 0;
661 else
663 char *dirname = get_item_dir (o);
664 meta_swap_children (o, dirname, o->entry_no, NULL, o->entry_no+1, NULL);
665 g_free (dirname);
666 o->entry_no++;
669 populate_path_list (o);
671 mrg_queue_draw (o->mrg, NULL);
672 return 0;
677 void ui_center_active_entry (GeState *o)
679 int row;
680 float pos;
681 update_grid_dim (o);
683 row = (o->entry_no+1) / hack_cols;
684 pos = row * hack_dim;
686 if (pos > o->v + mrg_height (o->mrg) - hack_dim ||
687 pos < o->v)
688 o->v = hack_dim * (row) - mrg_height (o->mrg)/2 + hack_dim;