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
21 #include <sys/types.h>
29 #include <mrg-string.h>
31 #include <gexiv2/gexiv2.h>
32 #include <gegl-paramspecs.h>
33 #include <gegl-operation.h>
34 #include <gegl-audio-fragment.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
);
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
);
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
)
72 if (!o
->show_controls
)
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
)
94 o
->path
= g_strdup (data2
);
100 mrg_event_stop_propagate (event
);
101 mrg_queue_draw (event
->mrg
, NULL
);
104 static void entry_select (MrgEvent
*event
, void *data1
, void *data2
)
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;
118 if (e
->type
== MRG_DRAG_RELEASE
)
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];
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
;
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 */
172 if (e
->device_no
== 1 || e
->device_no
== 4)
174 o
->u
-= (e
->delta_x
);
175 o
->v
-= (e
->delta_y
);
181 int count
= ui_items_count (o
);
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
);
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");
203 case MRG_SCROLL_DIRECTION_UP
:
204 argvs_eval ("zoom in");
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
);
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
;
236 for (p
= event
->string
; *p
; p
++)
244 copy_one_file (str
->str
, get_item_dir (o
));
245 mrg_string_set (str
, "");
249 mrg_string_append_unichar (str
, *p
);
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
)
282 dir_scroll_dragged
= 1;
284 case MRG_DRAG_RELEASE
:
285 dir_scroll_dragged
= 0;
287 case MRG_DRAG_MOTION
:
289 int count
= ui_items_count (o
);
290 float height
= mrg_height (e
->mrg
);
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
;
299 o
->v
+= e
->delta_y
/height
* (count
/hack_cols
* hack_dim
);
303 if (o
->v
> count
/hack_cols
* hack_dim
- height
/2)
304 o
->v
= count
/hack_cols
* hack_dim
- height
/2;
309 mrg_event_stop_propagate (e
);
313 void ui_collection (GeState
*o
)
316 cairo_t
*cr
= mrg_cr (mrg
);
321 float padding
= 0.025;
322 float em
= mrg_em (mrg
);
323 dir_touch_handling (mrg
, o
);
329 count
= ui_items_count (o
);
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
));
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
);
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");
365 for (int idx
= 0; idx
< count
; idx
++, no
++)
367 char *basename
= meta_get_child (o
, o
->path
, idx
);
368 struct stat stat_buf
;
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
);
377 if (y
< -dim
* 4 + o
->v
|| y
> mrg_height (mrg
) + dim
* 1.5 + o
->v
)
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
));
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
);
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)
429 (suffixed_stat_buf
.st_mtime
> thumb_stat_buf
.st_mtime
)) ||
430 (stat_buf
.st_mtime
> thumb_stat_buf
.st_mtime
))
433 mrg_forget_image (mrg
, thumbpath
);
439 access (thumbpath
, F_OK
) == 0 && //XXX: query image should suffice
440 mrg_query_image (mrg
, thumbpath
, &w
, &h
))
446 hdim
= dim
/ (1.0 * w
/ h
);
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
));
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
);
469 if (!g_file_test (thumbpath
, G_FILE_TEST_IS_REGULAR
))
471 ui_queue_thumb (path
);
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");
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);
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);
512 cairo_new_path (mrg_cr(mrg
));
513 cairo_rectangle (mrg_cr(mrg
), x
, y
, dim
, dim
);
515 if (no
== o
->entry_no
+ 1)
516 cairo_set_source_rgb (mrg_cr(mrg
), 1, 1,0);
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
));
522 if (no
== o
->entry_no
+ 1)
523 mrg_listen_full (mrg
, MRG_TAP
, entry_load
, o
, (void*)g_intern_string (path
), NULL
, NULL
);
525 mrg_listen_full (mrg
, MRG_TAP
, entry_select
, o
, GINT_TO_POINTER(no
-1), NULL
, NULL
);
526 cairo_new_path (mrg_cr(mrg
));
533 float height
= mrg_height(mrg
) * ( mrg_height (mrg
) / (count
/cols
* dim
) );
537 yoffset
= (4 * em
- height
)/2;
541 mrg_width(mrg
) - 4 * em
,
542 mrg_height(mrg
) * ( o
->v
/ (count
/cols
* dim
) ) - yoffset
,
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
);
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
;
599 printf ("current item: %i\n", o
->entry_no
);
602 if (!strcmp(argv
[1], "first"))
606 else if (!strcmp(argv
[1], "last"))
608 o
->entry_no
= ui_items_count (o
)-1;
610 else if (!strcmp(argv
[1], "right"))
614 else if (!strcmp(argv
[1], "left"))
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)
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
);
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)
651 char *dirname
= get_item_dir (o
);
652 meta_swap_children (o
, dirname
, o
->entry_no
-1, NULL
, o
->entry_no
, NULL
);
657 else if (!strcmp (argv
[1], "next"))
659 if (o
->entry_no
+ 1>= g_list_length (o
->index
))
663 char *dirname
= get_item_dir (o
);
664 meta_swap_children (o
, dirname
, o
->entry_no
, NULL
, o
->entry_no
+1, NULL
);
669 populate_path_list (o
);
671 mrg_queue_draw (o
->mrg
, NULL
);
677 void ui_center_active_entry (GeState
*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
||
688 o
->v
= hack_dim
* (row
) - mrg_height (o
->mrg
)/2 + hack_dim
;