1 // framedraw.c
2 // LiVES
3 // (c) G. Finch (salsaman+lives@gmail.com) 2002 - 2019
4 // see file COPYING for licensing details : released under the GNU GPL 3 or later
5 
6 // functions for the 'framedraw' widget - lets users draw on frames :-)
7 
8 #include "main.h"
9 #include "callbacks.h"
10 #include "interface.h"
11 #include "effects.h"
12 #include "cvirtual.h"
13 #include "framedraw.h"
14 
15 // set by mouse button press
16 static double xstart, ystart;
17 static double xcurrent, ycurrent;
18 static double xinit, yinit;
19 static volatile boolean b1_held;
20 
21 static volatile boolean noupdate = FALSE;
22 
23 static LiVESWidget *fbord_eventbox;
24 
25 
calc_fd_scale(int width,int height)26 static double calc_fd_scale(int width, int height) {
27   double scale = 1.;
28 
29   if (width < MIN_PRE_X) {
30     width = MIN_PRE_X;
31   }
32   if (height < MIN_PRE_Y) {
33     height = MIN_PRE_Y;
34   }
35 
36   if (width > MAX_PRE_X) scale = (double)width / (double)MAX_PRE_X;
37   if (height > MAX_PRE_Y && (height / MAX_PRE_Y < scale)) scale = (double)height / (double)MAX_PRE_Y;
38   return scale;
39 }
40 
41 
invalidate_preview(lives_special_framedraw_rect_t * frame_draw)42 void invalidate_preview(lives_special_framedraw_rect_t *frame_draw) {
43   /// this is called when a parameter in a rendered effect is changed
44   /// the current preview is invalid and we must reset back to the start frame
45   ///
46   /// the exception is for effects which can resize, since we can only show
47   /// an approximate preview anyway
48 
49   if (frame_draw->rfx->props & RFX_PROPS_MAY_RESIZE) return;
50 
51   lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
52   lives_signal_handler_block(mainw->framedraw_spinbutton, mainw->fd_spin_func);
53   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), cfile->start);
54   lives_range_set_value(LIVES_RANGE(mainw->framedraw_scale), cfile->start);
55   lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), cfile->start, cfile->start);
56   mainw->framedraw_frame = cfile->start;
57   lives_signal_handler_unblock(mainw->framedraw_spinbutton, mainw->fd_spin_func);
58 }
59 
60 
start_preview(LiVESButton * button,lives_rfx_t * rfx)61 static void start_preview(LiVESButton *button, lives_rfx_t *rfx) {
62   int i;
63   char *com;
64 
65   if (fx_dialog[0]) {
66     if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, FALSE);
67     if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, FALSE);
68   }
69 
70   if (!check_filewrite_overwrites()) {
71     if (fx_dialog[0]) {
72       if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, TRUE);
73       if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, TRUE);
74     }
75     return;
76   }
77 
78   clear_widget_bg(LIVES_WIDGET(mainw->framedraw), mainw->fd_surface);
79   lives_widget_set_sensitive(mainw->framedraw_preview, FALSE);
80 
81   if (mainw->did_rfx_preview) {
82     lives_kill_subprocesses(cfile->handle, TRUE);
83 
84     if (cfile->start == 0) {
85       cfile->start = 1;
86       cfile->end = cfile->frames;
87     }
88 
89     do_rfx_cleanup(rfx);
90   }
91 
92   com = lives_strdup_printf("%s clear_pre_files \"%s\" 2>%s", prefs->backend_sync, cfile->handle, LIVES_DEVNULL);
93   lives_system(com, TRUE); // clear any .pre files from before
94 
95   for (i = 0; i < rfx->num_params; i++) {
96     rfx->params[i].changed = FALSE;
97   }
98 
99   mainw->cancelled = CANCEL_NONE;
100   mainw->error = FALSE;
101 
102   if (rfx->num_in_channels == 0) {
103     // reset values to specified
104     cfile->hsize = cfile->ohsize;
105     cfile->vsize = cfile->ovsize;
106     cfile->fps = cfile->pb_fps;
107   }
108 
109   // within do_effect() we check and if
110   do_effect(rfx, TRUE); // actually start effect processing in the background
111   load_rfx_preview(rfx);
112 
113   lives_widget_set_sensitive(mainw->framedraw_spinbutton, TRUE);
114   lives_widget_set_sensitive(mainw->framedraw_scale, TRUE);
115   if (fx_dialog[0]) {
116     if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, TRUE);
117     if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, TRUE);
118   }
119   lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), cfile->start, cfile->start + 1);
120   mainw->did_rfx_preview = TRUE;
121 }
122 
123 
framedraw_redraw_cb(LiVESWidget * widget,lives_special_framedraw_rect_t * framedraw)124 static void framedraw_redraw_cb(LiVESWidget *widget, lives_special_framedraw_rect_t *framedraw) {
125   if (mainw->multitrack) return;
126   framedraw_redraw(framedraw, mainw->fd_layer_orig);
127 }
128 
129 
after_framedraw_frame_spinbutton_changed(LiVESSpinButton * spinbutton,lives_special_framedraw_rect_t * framedraw)130 static void after_framedraw_frame_spinbutton_changed(LiVESSpinButton *spinbutton, lives_special_framedraw_rect_t *framedraw) {
131   // update the single frame/framedraw preview
132   // after the "frame number" spinbutton has changed
133   lives_signal_handler_block(mainw->framedraw_spinbutton, mainw->fd_spin_func);
134   if (fx_dialog[0]) {
135     if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, FALSE);
136     if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, FALSE);
137   }
138   mainw->framedraw_frame = lives_spin_button_get_value_as_int(spinbutton);
139   if (!(framedraw->rfx->props & RFX_PROPS_MAY_RESIZE)) {
140     if (mainw->framedraw_preview) lives_widget_set_sensitive(mainw->framedraw_preview, FALSE);
141     //lives_widget_context_update();
142     load_rfx_preview(framedraw->rfx);
143   } else framedraw_redraw(framedraw, NULL);
144   if (fx_dialog[0]) {
145     if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, TRUE);
146     if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, TRUE);
147   }
148   lives_signal_handler_unblock(mainw->framedraw_spinbutton, mainw->fd_spin_func);
149 }
150 
151 
framedraw_connect_spinbutton(lives_special_framedraw_rect_t * framedraw,lives_rfx_t * rfx)152 void framedraw_connect_spinbutton(lives_special_framedraw_rect_t *framedraw, lives_rfx_t *rfx) {
153   framedraw->rfx = rfx;
154 
155   mainw->fd_spin_func = lives_signal_connect_after(LIVES_GUI_OBJECT(mainw->framedraw_spinbutton),
156                         LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
157                         LIVES_GUI_CALLBACK(after_framedraw_frame_spinbutton_changed), framedraw);
158   lives_signal_connect_after(LIVES_GUI_OBJECT(mainw->framedraw_opscale), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
159                              LIVES_GUI_CALLBACK(framedraw_redraw_cb), framedraw);
160   lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw_cbutton), LIVES_WIDGET_COLOR_SET_SIGNAL,
161                        LIVES_GUI_CALLBACK(framedraw_redraw_cb), framedraw);
162 }
163 
164 
framedraw_connect(lives_special_framedraw_rect_t * framedraw,int width,int height,lives_rfx_t * rfx)165 void framedraw_connect(lives_special_framedraw_rect_t *framedraw, int width, int height, lives_rfx_t *rfx) {
166   // add mouse fn's so we can draw on frames
167   lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_MOTION_NOTIFY_EVENT,
168                        LIVES_GUI_CALLBACK(on_framedraw_mouse_update), framedraw);
169   lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
170                        LIVES_GUI_CALLBACK(on_framedraw_mouse_reset), framedraw);
171   lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_BUTTON_PRESS_EVENT,
172                        LIVES_GUI_CALLBACK(on_framedraw_mouse_start), framedraw);
173   lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_ENTER_EVENT,
174                        LIVES_GUI_CALLBACK(on_framedraw_enter), framedraw);
175   lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_LEAVE_NOTIFY_EVENT,
176                        LIVES_GUI_CALLBACK(on_framedraw_leave), framedraw);
177   lives_signal_sync_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_SCROLL_EVENT,
178                             LIVES_GUI_CALLBACK(on_framedraw_scroll), NULL);
179 
180   framedraw_connect_spinbutton(framedraw, rfx);
181 
182   lives_widget_set_bg_color(mainw->fd_frame, LIVES_WIDGET_STATE_NORMAL, &palette->light_red);
183   lives_widget_set_bg_color(fbord_eventbox, LIVES_WIDGET_STATE_NORMAL, &palette->light_red);
184 
185   if (mainw->fd_layer) framedraw_redraw(framedraw, mainw->fd_layer_orig);
186 }
187 
188 
framedraw_add_label(LiVESVBox * box)189 void framedraw_add_label(LiVESVBox *box) {
190   LiVESWidget *label;
191 
192   // TRANSLATORS - Preview refers to preview window; keep this phrase short
193   widget_opts.justify = LIVES_JUSTIFY_CENTER;
194   label = lives_standard_label_new(_("You can click in Preview to change these values"));
195   lives_box_pack_start(LIVES_BOX(box), label, FALSE, FALSE, widget_opts.packing_height / 2.);
196   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
197 }
198 
199 
framedraw_add_reset(LiVESVBox * box,lives_special_framedraw_rect_t * framedraw)200 void framedraw_add_reset(LiVESVBox *box, lives_special_framedraw_rect_t *framedraw) {
201   LiVESWidget *hbox_rst;
202 
203   framedraw_add_label(box);
204 
205   mainw->framedraw_reset = lives_standard_button_new_from_stock(LIVES_STOCK_REFRESH, NULL,
206                            DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
207   hbox_rst = lives_hbox_new(FALSE, 0);
208   lives_box_pack_start(LIVES_BOX(box), hbox_rst, FALSE, FALSE, 4. * widget_opts.scale);
209 
210   lives_button_set_label(LIVES_BUTTON(mainw->framedraw_reset), _("_Reset Values"));
211   lives_box_pack_start(LIVES_BOX(hbox_rst), mainw->framedraw_reset, TRUE, FALSE, 0);
212   lives_widget_set_sensitive(mainw->framedraw_reset, FALSE);
213 
214   lives_signal_connect(mainw->framedraw_reset, LIVES_WIDGET_CLICKED_SIGNAL, LIVES_GUI_CALLBACK(on_framedraw_reset_clicked),
215                        framedraw);
216 }
217 
218 
redraw_framedraw_image(weed_layer_t * layer)219 static void redraw_framedraw_image(weed_layer_t *layer) {
220   // layer should be mainw->fd_frame
221   // size layer, convert palette
222   // then draw it in mainw->fd_surface
223   // changes 'layer' itself
224 
225   lives_painter_t *cr, *cr2;
226   LiVESWidget *fd_widget;
227 
228   int fd_width, fd_height;
229   int width, height, cx, cy;
230 
231   if (!layer) return;
232 
233   if (!CURRENT_CLIP_IS_VALID) return;
234 
235   if (mainw->multitrack) fd_widget = mainw->multitrack->preview_eventbox;
236   else fd_widget = mainw->framedraw;
237 
238   if (!LIVES_IS_WIDGET(fd_widget)) return;
239 
240   fd_width = lives_widget_get_allocation_width(fd_widget);
241   fd_height = lives_widget_get_allocation_height(fd_widget);
242 
243   width = cfile->hsize;
244   height = cfile->vsize;
245 
246   if (!mainw->multitrack || prefs->letterbox_mt)
247     calc_maxspect(fd_width, fd_height, &width, &height);
248 
249   // resize to correct size
250   resize_layer(layer, width, height, LIVES_INTERP_BEST,
251                LIVES_PAINTER_COLOR_PALETTE(capable->byte_order), 0);
252 
253   cr2 = lives_painter_create_from_surface(mainw->fd_surface);
254 
255   cr = layer_to_lives_painter(layer);
256 
257   cx = (fd_width - width) / 2;
258   cy = (fd_height - height) / 2;
259 
260   lives_painter_set_source_surface(cr2, lives_painter_get_target(cr), cx, cy);
261   lives_painter_rectangle(cr2, cx, cy, width, height);
262   lives_painter_fill(cr2);
263   lives_painter_destroy(cr2);
264 
265   lives_painter_to_layer(cr, layer);
266   lives_widget_queue_draw(fd_widget);
267 }
268 
269 
expose_fd_event(LiVESWidget * widget,lives_painter_t * cr,livespointer user_data)270 static boolean expose_fd_event(LiVESWidget *widget, lives_painter_t *cr, livespointer user_data) {
271   if (!LIVES_IS_WIDGET(widget)) return TRUE;
272   //redraw_framedraw_image(mainw->fd_layer, cr);
273   lives_painter_set_source_surface(cr, mainw->fd_surface, 0., 0.);
274   lives_painter_paint(cr);
275   return TRUE;
276 }
277 
278 
widget_add_framedraw(LiVESVBox * box,int start,int end,boolean add_preview_button,int width,int height,lives_rfx_t * rfx)279 void widget_add_framedraw(LiVESVBox *box, int start, int end, boolean add_preview_button, int width, int height,
280                           lives_rfx_t *rfx) {
281   // adds the frame draw widget to box
282   // the redraw button should be connected to an appropriate redraw function
283   // after calling this function
284 
285   LiVESAdjustment *spinbutton_adj;
286 
287   LiVESWidget *vseparator;
288   LiVESWidget *vbox;
289   LiVESWidget *hbox;
290   LiVESWidget *label;
291   LiVESWidget *cbutton;
292   LiVESWidget *frame;
293   lives_colRGBA64_t opcol;
294 
295   double fd_scale;
296 
297   b1_held = FALSE;
298 
299   mainw->framedraw_reset = NULL;
300 
301   vseparator = lives_vseparator_new();
302   lives_box_pack_start(LIVES_BOX(lives_widget_get_parent(LIVES_WIDGET(box))), vseparator, FALSE, FALSE, 0);
303   lives_widget_show(vseparator);
304 
305   vbox = lives_vbox_new(FALSE, 0);
306   lives_box_pack_start(LIVES_BOX(lives_widget_get_parent(LIVES_WIDGET(box))), vbox, FALSE, FALSE, 0);
307   lives_container_set_border_width(LIVES_CONTAINER(vbox), widget_opts.border_width);
308 
309   fd_scale = calc_fd_scale(width, height);
310   width /= fd_scale;
311   height /= fd_scale;
312 
313   hbox = lives_hbox_new(FALSE, 0);
314   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
315 
316   fbord_eventbox = lives_event_box_new();
317   lives_container_set_border_width(LIVES_CONTAINER(fbord_eventbox), widget_opts.border_width);
318 
319   frame = lives_standard_frame_new(_("Preview"), 0., TRUE);
320 
321   lives_box_pack_start(LIVES_BOX(hbox), frame, TRUE, TRUE, 0);
322 
323   mainw->fd_frame = frame;
324 
325   hbox = lives_hbox_new(FALSE, 0);
326   lives_container_set_border_width(LIVES_CONTAINER(hbox), 0);
327   lives_container_add(LIVES_CONTAINER(frame), hbox);
328 
329   if (palette->style & STYLE_1) {
330     lives_widget_set_bg_color(frame, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
331     lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
332   }
333 
334   //mainw->framedraw = lives_event_box_new();
335   mainw->framedraw = lives_standard_drawing_area_new(LIVES_GUI_CALLBACK(expose_fd_event),
336                      &mainw->fd_surface);
337   lives_widget_set_size_request(mainw->framedraw, width, height);
338 
339   if (palette->style & STYLE_1) {
340     lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
341   }
342 
343   lives_widget_set_events(mainw->framedraw, LIVES_BUTTON1_MOTION_MASK | LIVES_BUTTON_RELEASE_MASK |
344                           LIVES_BUTTON_PRESS_MASK | LIVES_ENTER_NOTIFY_MASK | LIVES_LEAVE_NOTIFY_MASK);
345 
346   mainw->framedraw_frame = start;
347 
348   lives_box_pack_start(LIVES_BOX(hbox), fbord_eventbox, FALSE, FALSE, 0);
349 
350   lives_container_add(LIVES_CONTAINER(fbord_eventbox), mainw->framedraw);
351 
352   if (palette->style & STYLE_1) {
353     lives_widget_set_bg_color(mainw->framedraw, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
354     lives_widget_set_fg_color(mainw->framedraw, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
355   }
356 
357   /* if (mainw->multitrack == NULL) */
358   /*   lives_signal_connect_after(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_EXPOSE_EVENT, */
359   /*                              LIVES_GUI_CALLBACK(expose_fd_event), NULL); */
360 
361   // mask colour and opacity controls
362   mainw->framedraw_maskbox = lives_hbox_new(FALSE, 2);
363   lives_box_pack_start(LIVES_BOX(vbox), mainw->framedraw_maskbox, FALSE, FALSE, widget_opts.packing_height);
364   label = lives_standard_label_new(_("Mask: opacity"));
365   lives_box_pack_start(LIVES_BOX(mainw->framedraw_maskbox), label, FALSE, FALSE, widget_opts.packing_width);
366   spinbutton_adj = lives_adjustment_new(DEF_MASK_OPACITY, 0.0, 1.0, 0.1, 0.1, 0);
367   mainw->framedraw_opscale = lives_standard_hscale_new(LIVES_ADJUSTMENT(spinbutton_adj));
368   lives_box_pack_start(LIVES_BOX(mainw->framedraw_maskbox), mainw->framedraw_opscale, TRUE, TRUE, 0);
369   opcol = lives_rgba_col_new(0, 0, 0, 65535);
370   cbutton = lives_standard_color_button_new(LIVES_BOX(mainw->framedraw_maskbox), _("color"),
371             FALSE, &opcol, NULL, NULL, NULL, NULL);
372   mainw->framedraw_cbutton = cbutton;
373 
374   hbox = lives_hbox_new(FALSE, 2);
375   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
376 
377   mainw->framedraw_spinbutton = lives_standard_spin_button_new(_("_Frame"),
378                                 start, start, end, 1., 10., 0, LIVES_BOX(hbox), NULL);
379 
380   spinbutton_adj = lives_spin_button_get_adjustment(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton));
381 
382   mainw->framedraw_preview = lives_standard_button_new_from_stock(LIVES_STOCK_REFRESH, _("Preview"),
383                              DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
384 
385   lives_box_pack_start(LIVES_BOX(hbox), mainw->framedraw_preview, TRUE, FALSE, 0);
386   lives_widget_set_no_show_all(mainw->framedraw_preview, TRUE);
387 
388   hbox = lives_hbox_new(FALSE, 2);
389   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
390 
391   mainw->framedraw_scale = lives_standard_hscale_new(LIVES_ADJUSTMENT(spinbutton_adj));
392   if (palette->style & STYLE_1) {
393     lives_widget_set_fg_color(mainw->framedraw_scale, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
394   }
395   lives_box_pack_start(LIVES_BOX(hbox), mainw->framedraw_scale, TRUE, TRUE, widget_opts.border_width);
396   if (!(rfx->props & RFX_PROPS_MAY_RESIZE)) {
397     gtk_range_set_show_fill_level(GTK_RANGE(mainw->framedraw_scale), TRUE);
398     gtk_range_set_restrict_to_fill_level(GTK_RANGE(mainw->framedraw_scale), TRUE);
399     gtk_range_set_fill_level(GTK_RANGE(mainw->framedraw_scale), end > start ? (double)start + 1. :
400                              (double)start);
401     lives_widget_set_sensitive(mainw->framedraw_spinbutton, FALSE);
402     lives_widget_set_sensitive(mainw->framedraw_scale, FALSE);
403   }
404   lives_signal_connect(mainw->framedraw_preview, LIVES_WIDGET_CLICKED_SIGNAL, LIVES_GUI_CALLBACK(start_preview), rfx);
405 
406   lives_widget_show_all(vbox);
407 
408   if (add_preview_button) {
409     lives_widget_set_no_show_all(mainw->framedraw_preview, FALSE);
410     lives_widget_show_all(mainw->framedraw_preview);
411   }
412 
413   lives_widget_hide(mainw->framedraw_maskbox);
414 
415   if (start == 0) {
416     mainw->fd_max_frame = 1;
417   } else {
418     mainw->fd_max_frame = start;
419   }
420 }
421 
422 
framedraw_redraw(lives_special_framedraw_rect_t * framedraw,weed_layer_t * layer)423 weed_plant_t *framedraw_redraw(lives_special_framedraw_rect_t *framedraw, weed_layer_t *layer) {
424   // overlay framedrawing on a layer
425   // if layer is NULL then we reload the frame from current file into mainw->fd_layer_orig
426   // if called from multitrack then the preview layer is passed in via mt_framedraw()
427   // for overlaying we pass in mainw->fd_layer_orig again
428   // from c.e, layer is mainw->fd_orig_layer
429   //
430   // the input layer is copied: (mainw->fd_layer_orig -> mainw->fd_layer)
431   // mainw->fd_layer is drawn over, resized, gamma corrected and painted
432   // the output layer is also returned (mainw->fd_layer)
433 
434   lives_painter_t *cr;
435   LiVESWidget *fd_widget;
436 
437   double xstartf, ystartf, xendf, yendf;
438 
439   int fd_height;
440   int fd_width;
441   int width, height;
442 
443   if (!CURRENT_CLIP_IS_VALID) return NULL;
444 
445   if (framedraw->rfx->source_type == LIVES_RFX_SOURCE_RFX)
446     if (noupdate) return NULL; // static volatile
447 
448   if (mainw->multitrack) fd_widget = mainw->multitrack->preview_eventbox;
449   else fd_widget = mainw->framedraw;
450   if (!LIVES_IS_WIDGET(fd_widget)) return NULL;
451 
452   fd_width = lives_widget_get_allocation_width(fd_widget);
453   fd_height = lives_widget_get_allocation_height(fd_widget);
454 
455   if (fd_width < 4 || fd_height < 4) return NULL;
456 
457   width = cfile->hsize;
458   height = cfile->vsize;
459 
460   if (!mainw->multitrack || prefs->letterbox_mt)
461     calc_maxspect(fd_width, fd_height, &width, &height);
462 
463   // copy from orig, resize
464 
465   if (!layer) {
466     // forced reload: get frame from clip, and set in mainw->fd_layer_orig
467     const char *img_ext;
468     if (framedraw->rfx->num_in_channels > 0 && !(framedraw->rfx->props & RFX_PROPS_MAY_RESIZE)) {
469       img_ext = LIVES_FILE_EXT_PRE;
470     } else {
471       img_ext = get_image_ext_for_type(cfile->img_type);
472     }
473 
474     layer = lives_layer_new_for_frame(mainw->current_file, mainw->framedraw_frame);
475     if (!pull_frame(layer, img_ext, 0)) {
476       weed_plant_free(layer);
477       return NULL;
478     }
479     if (mainw->fd_layer_orig) weed_layer_free(mainw->fd_layer_orig);
480     mainw->fd_layer_orig = layer;
481   } else {
482     if (mainw->fd_layer_orig && mainw->fd_layer_orig != layer) {
483       weed_layer_free(mainw->fd_layer_orig);
484       mainw->fd_layer_orig = layer;
485     }
486   }
487 
488   // copy orig layer to new layer
489   if (mainw->fd_layer) {
490     weed_layer_free(mainw->fd_layer);
491     mainw->fd_layer = NULL;
492   }
493 
494   mainw->fd_layer = weed_layer_copy(NULL, layer);
495 
496   // resize to correct size
497   resize_layer(mainw->fd_layer, width, height, LIVES_INTERP_BEST,
498                LIVES_PAINTER_COLOR_PALETTE(capable->byte_order), 0);
499 
500   cr = layer_to_lives_painter(mainw->fd_layer);
501 
502   // draw on the lives_painter
503 
504   switch (framedraw->type) {
505   case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT:
506   case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
507     if (framedraw->xstart_param->dp == 0) {
508       xstartf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
509       xstartf = xstartf / (double)cfile->hsize * (double)width;
510     } else {
511       xstartf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
512       xstartf = xstartf * (double)width;
513     }
514 
515     if (framedraw->xend_param->dp == 0) {
516       xendf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]));
517       xendf = xendf / (double)cfile->hsize * (double)width;
518     } else {
519       xendf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]));
520       xendf = xendf * (double)width;
521     }
522 
523     if (framedraw->ystart_param->dp == 0) {
524       ystartf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
525       ystartf = ystartf / (double)cfile->vsize * (double)height;
526     } else {
527       ystartf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
528       ystartf = ystartf * (double)height;
529     }
530 
531     if (framedraw->yend_param->dp == 0) {
532       yendf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]));
533       yendf = yendf / (double)cfile->vsize * (double)height;
534     } else {
535       yendf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]));
536       yendf = yendf * (double)height;
537     }
538 
539     if (framedraw->type == LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT) {
540       lives_painter_set_source_rgb(cr, 1., 0., 0.);  // TODO - make colour configurable
541       lives_painter_rectangle(cr, xstartf - 1., ystartf - 1., xendf, yendf);
542       lives_painter_stroke(cr);
543     } else {
544       if (b1_held) {
545         lives_painter_set_source_rgb(cr, 1., 0., 0.); // TODO - make colour configurable
546         lives_painter_rectangle(cr, xstartf - 1., ystartf - 1., xendf - xstartf + 2., yendf - ystartf + 2.);
547         lives_painter_stroke(cr);
548       }
549       // create a mask which is only opaque within the clipping area
550       LiVESWidgetColor maskcol;
551       double opacity = lives_range_get_value(LIVES_RANGE(mainw->framedraw_opscale));
552       lives_color_button_get_color(LIVES_COLOR_BUTTON(mainw->framedraw_cbutton), &maskcol);
553       lives_painter_rectangle(cr, 0, 0, width, height);
554       lives_painter_rectangle(cr, xstartf, ystartf, xendf - xstartf + 1., yendf - ystartf + 1.);
555       lives_painter_set_source_rgba(cr, LIVES_WIDGET_COLOR_SCALE(maskcol.red), LIVES_WIDGET_COLOR_SCALE(maskcol.green),
556                                     LIVES_WIDGET_COLOR_SCALE(maskcol.blue), opacity);
557       lives_painter_set_fill_rule(cr, LIVES_PAINTER_FILL_RULE_EVEN_ODD);
558       lives_painter_fill(cr);
559     }
560     break;
561 
562   case LIVES_PARAM_SPECIAL_TYPE_SINGLEPOINT:
563   case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT:
564     if (framedraw->type == LIVES_PARAM_SPECIAL_TYPE_SINGLEPOINT) {
565       if (framedraw->xstart_param->dp == 0) {
566         xstartf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
567         xstartf = xstartf / (double)cfile->hsize * (double)width;
568       } else {
569         xstartf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
570         xstartf *= (double)width;
571       }
572 
573       if (framedraw->ystart_param->dp == 0) {
574         ystartf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
575         ystartf = ystartf / (double)cfile->vsize * (double)height;
576       } else {
577         ystartf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
578         ystartf *= (double)height;
579       }
580     } else {
581       if (b1_held) {
582         xstartf = xcurrent * (double)width;
583         ystartf = ycurrent * (double)height;
584       } else {
585         double xpos, ypos;
586         double scale = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->scale_param->widgets[0]));
587         if (scale == 0.) break;
588 
589         if (framedraw->xstart_param->dp > 0)
590           xpos = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
591         else
592           xpos = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]))
593                  / (double)cfile->hsize;
594         if (framedraw->ystart_param->dp > 0)
595           ypos = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
596         else
597           ypos = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]))
598                  / (double)cfile->vsize;
599 
600         xstartf = 0.5;
601         ystartf = 0.5;
602 
603         if (xpos - xstartf / scale < 0.) xstartf = xpos * scale;
604         else if (xpos + xstartf / scale > 1.) xstartf = 1. - (1. - xpos) * scale;
605         if (ypos - ystartf / scale < 0.) ystartf = ypos * scale;
606         else if (ypos + ystartf / scale > 1.) ystartf = 1. - (1. - ypos) * scale;
607         xstartf *= (double)width;
608         ystartf *= (double)height;
609       }
610     }
611 
612     // draw a crosshair
613     lives_painter_set_source_rgb(cr, 1., 0., 0.); // TODO - make colour configurable
614 
615     lives_painter_move_to(cr, xstartf, ystartf - CROSSHAIR_SIZE);
616     lives_painter_line_to(cr, xstartf, ystartf + CROSSHAIR_SIZE);
617 
618     lives_painter_stroke(cr);
619 
620     lives_painter_move_to(cr, xstartf - CROSSHAIR_SIZE, ystartf);
621     lives_painter_line_to(cr, xstartf + CROSSHAIR_SIZE, ystartf);
622 
623     lives_painter_stroke(cr);
624     break;
625 
626   default:
627     break;
628   }
629 
630   lives_painter_to_layer(cr, mainw->fd_layer);
631 
632   if (!mainw->multitrack)
633     redraw_framedraw_image(mainw->fd_layer);
634   else {
635     LiVESPixbuf *pixbuf;
636     int palette = weed_layer_get_palette(mainw->fd_layer);
637     if (weed_palette_has_alpha(palette)) {
638       palette = WEED_PALETTE_RGBA32;
639     } else {
640       palette = WEED_PALETTE_RGB24;
641     }
642 
643     resize_layer(mainw->fd_layer, weed_layer_get_width(mainw->fd_layer_orig)
644                  * weed_palette_get_pixels_per_macropixel(weed_layer_get_palette(mainw->fd_layer_orig)),
645                  weed_layer_get_height(mainw->fd_layer_orig), LIVES_INTERP_BEST, palette, 0);
646 
647     convert_layer_palette(mainw->fd_layer, palette, 0);
648     pixbuf = layer_to_pixbuf(mainw->fd_layer, TRUE, TRUE);
649     weed_layer_nullify_pixel_data(mainw->fd_layer);
650     weed_layer_free(mainw->fd_layer);
651     mainw->fd_layer = NULL;
652     if (pixbuf) {
653       if (mainw->multitrack->frame_pixbuf != mainw->imframe) {
654         if (mainw->multitrack->frame_pixbuf) lives_widget_object_unref(mainw->multitrack->frame_pixbuf);
655       }
656       // set frame_pixbuf, this gets painted in in expose_event
657       mainw->multitrack->frame_pixbuf = pixbuf;
658       set_drawing_area_from_pixbuf(mainw->play_image, pixbuf, mainw->play_surface);
659       lives_widget_queue_draw(mainw->multitrack->preview_eventbox);
660       lives_widget_object_unref(mainw->multitrack->frame_pixbuf);
661       mainw->multitrack->frame_pixbuf = NULL;
662     }
663   }
664 
665   lives_widget_queue_draw(fd_widget);
666   // update the widget
667   return mainw->fd_layer;
668 }
669 
670 
load_rfx_preview(lives_rfx_t * rfx)671 void load_rfx_preview(lives_rfx_t *rfx) {
672   // load a preview of an rfx (rendered effect) in clip editor
673   weed_layer_t *layer;
674   FILE *infofile = NULL;
675   lives_alarm_t alarm_handle;
676   ticks_t timeout;
677   int tot_frames = 0;
678   int retval;
679   int current_file = mainw->current_file;
680 
681   const char *img_ext;
682 
683   if (mainw->framedraw_frame > mainw->fd_max_frame) {
684     lives_signal_handler_block(mainw->framedraw_spinbutton, mainw->fd_spin_func);
685     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), mainw->fd_max_frame);
686     lives_signal_handler_unblock(mainw->framedraw_spinbutton, mainw->fd_spin_func);
687     //lives_widget_context_update();
688   }
689 
690   if (mainw->framedraw_frame == 0) mainw->framedraw_frame = 1;
691 
692   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
693 
694   clear_mainw_msg();
695   THREADVAR(write_failed) = FALSE;
696 
697   if (cfile->clip_type == CLIP_TYPE_FILE && cfile->fx_frame_pump && !cfile->pumper) {
698     // pull frames in background
699     cfile->pumper = lives_proc_thread_create(LIVES_THRDATTR_NONE, (lives_funcptr_t)virtual_to_images,
700                     -1, "iiibV", mainw->current_file,
701                     cfile->undo_start, cfile->undo_end, FALSE, NULL);
702   }
703 
704   if (mainw->cancelled) {
705     if (cfile->pumper) {
706       lives_nanosleep_until_nonzero(lives_proc_thread_cancel(cfile->pumper));
707       weed_plant_free(cfile->pumper);
708       cfile->pumper = NULL;
709     }
710     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
711     return;
712   }
713 
714   // get message from back end processor
715   alarm_handle = lives_alarm_set(LIVES_LONGER_TIMEOUT);
716   while ((timeout = lives_alarm_check(alarm_handle)) > 0 && !(infofile = fopen(cfile->info_file, "r")) && !mainw->cancelled) {
717     // wait until we get at least 1 frame
718     //lives_widget_context_update();
719     lives_nanosleep(1000);
720     sched_yield();
721   }
722   lives_alarm_clear(alarm_handle);
723 
724   if (!timeout) {
725     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
726     return;
727   }
728 
729   if (mainw->cancelled) {
730     if (infofile) fclose(infofile);
731     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
732     if (cfile->pumper) {
733       lives_nanosleep_until_nonzero(lives_proc_thread_cancel(cfile->pumper));
734       weed_plant_free(cfile->pumper);
735       cfile->pumper = NULL;
736     }
737     return;
738   }
739 
740   do {
741     retval = 0;
742     lives_fgets(mainw->msg, MAINW_MSG_SIZE, infofile);
743     if (THREADVAR(read_failed)) {
744       // TODO : should check fd
745       THREADVAR(read_failed) = 0;
746       retval = do_read_failed_error_s_with_retry(cfile->info_file, NULL);
747     }
748   } while (retval == LIVES_RESPONSE_RETRY);
749 
750   fclose(infofile);
751 
752   if (lives_strncmp(mainw->msg, "completed", 9)) {
753     if (rfx->num_in_channels > 0) {
754       mainw->fd_max_frame = atoi(mainw->msg);
755       tot_frames = cfile->end;
756     } else {
757       int numtok = get_token_count(mainw->msg, '|');
758       if (numtok > 4) {
759         char **array = lives_strsplit(mainw->msg, "|", numtok);
760         mainw->fd_max_frame = atoi(array[0]);
761         cfile->hsize = atoi(array[1]);
762         cfile->vsize = atoi(array[2]);
763         cfile->fps = cfile->pb_fps = strtod(array[3], NULL);
764         if (cfile->fps == 0) cfile->fps = cfile->pb_fps = prefs->default_fps;
765         tot_frames = atoi(array[4]);
766         lives_strfreev(array);
767       }
768     }
769   } else {
770     tot_frames = mainw->fd_max_frame = cfile->end;
771   }
772 
773   if (!(rfx->props & RFX_PROPS_MAY_RESIZE)) {
774     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
775 
776     if (mainw->fd_max_frame > 0) {
777       int maxlen = calc_spin_button_width(1., (double)tot_frames, 0);
778       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), cfile->start, tot_frames);
779       lives_entry_set_width_chars(LIVES_ENTRY(mainw->framedraw_spinbutton), maxlen);
780       lives_widget_queue_draw(mainw->framedraw_spinbutton);
781       lives_widget_queue_draw(mainw->framedraw_scale);
782       if (rfx->num_in_channels == 0) {
783         cfile->frames = tot_frames;
784       }
785 
786       if (!(rfx->props & RFX_PROPS_MAY_RESIZE)) {
787         gtk_range_set_fill_level(GTK_RANGE(mainw->framedraw_scale), (double)mainw->fd_max_frame);
788         if (mainw->framedraw_frame > mainw->fd_max_frame) {
789           lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), mainw->fd_max_frame);
790           lives_range_set_value(LIVES_RANGE(mainw->framedraw_scale), mainw->fd_max_frame);
791           mainw->current_file = current_file;
792           return;
793 	  // *INDENT-OFF*
794         }}}}
795   // *INDENT-ON*
796 
797   layer = lives_layer_new_for_frame(mainw->current_file, mainw->framedraw_frame);
798   if (rfx->num_in_channels > 0 && !(rfx->props & RFX_PROPS_MAY_RESIZE)) {
799     img_ext = LIVES_FILE_EXT_PRE;
800   } else {
801     img_ext = get_image_ext_for_type(cfile->img_type);
802   }
803   if (!pull_frame(layer, img_ext, 0)) {
804     weed_plant_free(layer);
805   } else {
806     if (mainw->fd_layer_orig && mainw->fd_layer_orig != layer)
807       weed_layer_free(mainw->fd_layer_orig);
808     mainw->fd_layer_orig = layer;
809     if (mainw->fd_layer) weed_layer_free(mainw->fd_layer);
810     mainw->fd_layer = weed_layer_copy(NULL, mainw->fd_layer_orig);
811     redraw_framedraw_image(mainw->fd_layer);
812   }
813   mainw->current_file = current_file;
814 }
815 
816 
817 // change cursor maybe when we enter or leave the framedraw window
818 
on_framedraw_enter(LiVESWidget * widget,LiVESXEventCrossing * event,lives_special_framedraw_rect_t * framedraw)819 boolean on_framedraw_enter(LiVESWidget * widget, LiVESXEventCrossing * event, lives_special_framedraw_rect_t *framedraw) {
820   if (!framedraw && mainw->multitrack) {
821     framedraw = mainw->multitrack->framedraw;
822     if (!framedraw && mainw->multitrack->cursor_style == LIVES_CURSOR_NORMAL)
823       lives_set_cursor_style(LIVES_CURSOR_NORMAL, mainw->multitrack->preview_eventbox);
824   }
825 
826   if (!framedraw) return FALSE;
827   if (mainw->multitrack && (mainw->multitrack->track_index == -1 ||
828                             mainw->multitrack->cursor_style != LIVES_CURSOR_NORMAL)) return FALSE;
829 
830   switch (framedraw->type) {
831   case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
832   case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT:
833     if (!mainw->multitrack) {
834       lives_set_cursor_style(LIVES_CURSOR_TOP_LEFT_CORNER, mainw->framedraw);
835     } else {
836       lives_set_cursor_style(LIVES_CURSOR_TOP_LEFT_CORNER, mainw->multitrack->preview_eventbox);
837     }
838     break;
839 
840   case LIVES_PARAM_SPECIAL_TYPE_SINGLEPOINT:
841   case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT:
842     if (!mainw->multitrack) {
843       lives_set_cursor_style(LIVES_CURSOR_CROSSHAIR, mainw->framedraw);
844     } else {
845       lives_set_cursor_style(LIVES_CURSOR_CROSSHAIR, mainw->multitrack->preview_eventbox);
846     }
847     break;
848 
849   default:
850     break;
851   }
852   return FALSE;
853 }
854 
on_framedraw_leave(LiVESWidget * widget,LiVESXEventCrossing * event,lives_special_framedraw_rect_t * framedraw)855 boolean on_framedraw_leave(LiVESWidget * widget, LiVESXEventCrossing * event, lives_special_framedraw_rect_t *framedraw) {
856   LiVESWidget *fd_widget;
857   if (!framedraw) return FALSE;
858 
859   if (mainw->multitrack) fd_widget = mainw->multitrack->preview_eventbox;
860   else fd_widget = mainw->framedraw;
861 
862   if (!LIVES_IS_WIDGET(fd_widget)) return FALSE;
863 
864   lives_set_cursor_style(LIVES_CURSOR_NORMAL, fd_widget);
865   return FALSE;
866 }
867 
868 
869 // using these 3 functions, the user can draw on frames
870 
on_framedraw_mouse_start(LiVESWidget * widget,LiVESXEventButton * event,lives_special_framedraw_rect_t * framedraw)871 boolean on_framedraw_mouse_start(LiVESWidget * widget, LiVESXEventButton * event, lives_special_framedraw_rect_t *framedraw) {
872   // user clicked in the framedraw widget (or multitrack playback widget)
873   double xend, yend;
874 
875   int fd_height;
876   int fd_width;
877 
878   int width = cfile->hsize;
879   int height = cfile->vsize;
880 
881   int xstarti, ystarti;
882 
883   if (!framedraw && mainw->multitrack) framedraw = mainw->multitrack->framedraw;
884 
885   if (!framedraw) return FALSE;
886   if (mainw->multitrack && mainw->multitrack->track_index == -1) return FALSE;
887 
888   if (!framedraw && mainw->multitrack && event->button == 3) {
889     // right click brings up context menu
890     frame_context(widget, event, LIVES_INT_TO_POINTER(0));
891   }
892 
893   if (event->button != 1) return FALSE;
894 
895   b1_held = TRUE;
896 
897   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
898                            widget, &xstarti, &ystarti);
899   xstarti -= widget_opts.border_width;
900 
901   if ((framedraw->type == LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT ||
902        framedraw->type == LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK) &&
903       (!mainw->multitrack || mainw->multitrack->cursor_style == 0)) {
904     if (!mainw->multitrack) {
905       lives_set_cursor_style(LIVES_CURSOR_BOTTOM_RIGHT_CORNER, widget);
906     } else {
907       lives_set_cursor_style(LIVES_CURSOR_BOTTOM_RIGHT_CORNER, mainw->multitrack->preview_eventbox);
908     }
909   }
910 
911   fd_width = lives_widget_get_allocation_width(widget);
912   fd_height = lives_widget_get_allocation_height(widget);
913 
914   calc_maxspect(fd_width, fd_height, &width, &height);
915 
916   xstart = (double)xstarti - (double)(fd_width - width) / 2.;
917   ystart = (double)ystarti - (double)(fd_height - height) / 2.;
918 
919   xstart /= (double)(width - 1);
920   ystart /= (double)(height - 1);
921 
922   xend = xcurrent = xstart;
923   yend = ycurrent = ystart;
924 
925   noupdate = TRUE;
926 
927   switch (framedraw->type) {
928   case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT: {
929     // current center is xinit, yinit
930     // ystart, xstart equal cursor (crosshair posn.)
931 
932     if (framedraw->xstart_param->dp > 0)
933       xinit = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
934     else
935       xinit = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]))
936               / (double)cfile->hsize;
937     if (framedraw->ystart_param->dp > 0)
938       yinit = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
939     else
940       yinit = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]))
941               / (double)cfile->vsize;
942   }
943   break;
944 
945   case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT:
946     xend = yend = 0.;
947 
948   case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
949     if (framedraw->xstart_param->dp > 0)
950       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xstart);
951     else
952       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
953                                   (int)(xstart * (double)cfile->hsize + .5));
954     if (framedraw->xstart_param->dp > 0)
955       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), ystart);
956     else
957       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
958                                   (int)(ystart * (double)cfile->vsize + .5));
959 
960     if (framedraw->xend_param->dp > 0)
961       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), xend);
962     else
963       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), (int)(xend * (double)cfile->hsize + .5));
964     if (framedraw->xend_param->dp > 0)
965       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), yend);
966     else
967       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), (int)(yend * (double)cfile->vsize + .5));
968 
969     break;
970 
971   default:
972     break;
973   }
974 
975   if (mainw->framedraw_reset) {
976     lives_widget_set_sensitive(mainw->framedraw_reset, TRUE);
977   }
978   if (mainw->framedraw_preview) {
979     lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
980   }
981 
982   noupdate = FALSE;
983 
984   framedraw_redraw(framedraw, mainw->fd_layer_orig);
985 
986   return FALSE;
987 }
988 
on_framedraw_mouse_update(LiVESWidget * widget,LiVESXEventMotion * event,lives_special_framedraw_rect_t * framedraw)989 boolean on_framedraw_mouse_update(LiVESWidget * widget, LiVESXEventMotion * event, lives_special_framedraw_rect_t *framedraw) {
990   // pointer moved in the framedraw widget
991   int xcurrenti, ycurrenti;
992 
993   int fd_width, fd_height, width, height;
994 
995   if (noupdate) return FALSE;
996 
997   if (!b1_held) return FALSE;
998 
999   if (!framedraw && mainw->multitrack) framedraw = mainw->multitrack->framedraw;
1000   if (!framedraw) return FALSE;
1001   if (mainw->multitrack && mainw->multitrack->track_index == -1) return FALSE;
1002 
1003   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
1004                            widget, &xcurrenti, &ycurrenti);
1005   xcurrenti -= widget_opts.border_width;
1006 
1007   width = cfile->hsize;
1008   height = cfile->vsize;
1009 
1010   fd_width = lives_widget_get_allocation_width(widget);
1011   fd_height = lives_widget_get_allocation_height(widget);
1012 
1013   calc_maxspect(fd_width, fd_height, &width, &height);
1014 
1015   xcurrent = (double)xcurrenti - (fd_width - width) / 2.;
1016   ycurrent = (double)ycurrenti - (fd_height - height) / 2.;
1017 
1018   xcurrent /= (double)(width - 1);
1019   ycurrent /= (double)(height - 1);
1020 
1021   noupdate = TRUE;
1022 
1023   switch (framedraw->type) {
1024   case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT: {
1025     // end parameters provide a scale relative to the output channel
1026     double xscale, yscale;
1027 
1028     xscale = xcurrent - xstart;
1029     yscale = ycurrent - ystart;
1030 
1031     if (xscale > 0.) {
1032       if (framedraw->xend_param->dp > 0)
1033         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), xscale);
1034       else
1035         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1036                                     (int)(xscale * (double)cfile->hsize + .5));
1037     } else {
1038       if (framedraw->xstart_param->dp > 0) {
1039         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), -xscale);
1040         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xcurrent);
1041       } else {
1042         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1043                                     (int)(-xscale * (double)cfile->hsize - .5));
1044         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1045                                     (int)(xcurrent * (double)cfile->hsize + .5));
1046       }
1047     }
1048 
1049     if (yscale > 0.) {
1050       if (framedraw->yend_param->dp > 0)
1051         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), yscale);
1052       else
1053         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1054                                     (int)(yscale * (double)cfile->vsize + .5));
1055     } else {
1056       if (framedraw->xstart_param->dp > 0) {
1057         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), -yscale);
1058         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), ycurrent);
1059       } else {
1060         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1061                                     (int)(-yscale * (double)cfile->vsize - .5));
1062         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1063                                     (int)(ycurrent * (double)cfile->vsize + .5));
1064       }
1065     }
1066   }
1067   break;
1068 
1069   case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
1070     // end parameters provide the absolute point, in output channel ratio
1071     if (xcurrent > xstart) {
1072       if (framedraw->xend_param->dp > 0)
1073         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), xcurrent);
1074       else
1075         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1076                                     (int)(xcurrent * (double)cfile->hsize + .5));
1077     } else {
1078       if (framedraw->xstart_param->dp > 0) {
1079         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), xstart);
1080         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xcurrent);
1081       } else {
1082         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1083                                     (int)(xstart * (double)cfile->hsize + .5));
1084         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1085                                     (int)(xcurrent * (double)cfile->hsize + .5));
1086       }
1087     }
1088 
1089     if (ycurrent > ystart) {
1090       if (framedraw->yend_param->dp > 0)
1091         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), ycurrent);
1092       else
1093         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1094                                     (int)(ycurrent * (double)cfile->vsize + .5));
1095     } else {
1096       if (framedraw->xstart_param->dp > 0) {
1097         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), ystart);
1098         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), ycurrent);
1099       } else {
1100         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1101                                     (int)(ystart * (double)cfile->vsize + .5));
1102         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1103                                     (int)(ycurrent * (double)cfile->vsize + .5));
1104       }
1105     }
1106     break;
1107 
1108   case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT: {
1109     // current center is xinit, yinit
1110     // ystart, xstart equal cursor (crosshair posn.)
1111     double offs_x, offs_y;
1112     double scale = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->scale_param->widgets[0]));
1113     if (scale == 0.) break;
1114     offs_x = (xstart - xcurrent) / scale;
1115     offs_y = (ystart - ycurrent) / scale;
1116 
1117     if (xinit + offs_x < .5 / scale) offs_x = .5 / scale - xinit;
1118     if (xinit + offs_x > 1. - .5 / scale) offs_x = 1. - .5 / scale - xinit;
1119     if (yinit + offs_y < .5 / scale) offs_y = .5 / scale - yinit;
1120     if (yinit + offs_y > 1. - .5 / scale) offs_y = 1. - .5 / scale - yinit;
1121 
1122     if (framedraw->xstart_param->dp > 0)
1123       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xinit + offs_x);
1124     else
1125       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), (int)((xinit + offs_x)
1126                                   * (double)cfile->hsize + .5));
1127     if (framedraw->xstart_param->dp > 0)
1128       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), yinit + offs_y);
1129     else
1130       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), (int)((yinit + offs_y)
1131                                   * (double)cfile->vsize + .5));
1132   }
1133   break;
1134 
1135   default:
1136     break;
1137   }
1138 
1139 #if !GTK_CHECK_VERSION(3, 0, 0)
1140   lives_widget_context_update();
1141 #endif
1142 
1143   if (mainw->framedraw_reset) {
1144     lives_widget_set_sensitive(mainw->framedraw_reset, TRUE);
1145   }
1146   if (mainw->framedraw_preview) {
1147     lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
1148   }
1149 
1150   noupdate = FALSE;
1151   framedraw_redraw(framedraw, mainw->fd_layer_orig);
1152 
1153   return FALSE;
1154 }
1155 
on_framedraw_mouse_reset(LiVESWidget * widget,LiVESXEventButton * event,lives_special_framedraw_rect_t * framedraw)1156 boolean on_framedraw_mouse_reset(LiVESWidget * widget, LiVESXEventButton * event, lives_special_framedraw_rect_t *framedraw) {
1157   // user released the mouse button in framedraw widget
1158   int xcurrenti, ycurrenti;
1159   int fd_width, fd_height, width, height;
1160 
1161   if (event->button != 1 || !b1_held) return FALSE;
1162 
1163   b1_held = FALSE;
1164 
1165   if (!framedraw && mainw->multitrack) framedraw = mainw->multitrack->framedraw;
1166   if (!framedraw) return FALSE;
1167   if (mainw->multitrack && mainw->multitrack->track_index == -1) return FALSE;
1168 
1169   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
1170                            widget, &xcurrenti, &ycurrenti);
1171   xcurrenti -= widget_opts.border_width;
1172 
1173   width = cfile->hsize;
1174   height = cfile->vsize;
1175 
1176   fd_width = lives_widget_get_allocation_width(widget);
1177   fd_height = lives_widget_get_allocation_height(widget);
1178 
1179   calc_maxspect(fd_width, fd_height, &width, &height);
1180 
1181   xcurrent = (double)xcurrenti - (fd_width - width) / 2.;
1182   ycurrent = (double)ycurrenti - (fd_height - height) / 2.;
1183 
1184   xcurrent /= (double)(width - 1);
1185   ycurrent /= (double)(height - 1);
1186 
1187   switch (framedraw->type) {
1188   case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT:
1189   case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
1190     if (!mainw->multitrack) {
1191       lives_set_cursor_style(LIVES_CURSOR_TOP_LEFT_CORNER, widget);
1192     } else if (mainw->multitrack->cursor_style == 0) {
1193       lives_set_cursor_style(LIVES_CURSOR_TOP_LEFT_CORNER, mainw->multitrack->preview_eventbox);
1194     }
1195     break;
1196 
1197   case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT: {
1198     double offs_x, offs_y, scale, xend, yend;
1199     if (noupdate) break;
1200 
1201     scale = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->scale_param->widgets[0]));
1202     if (scale == 0.) break;
1203 
1204     if (framedraw->xstart_param->dp > 0)
1205       xend = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
1206     else
1207       xend = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]))
1208              / (double)cfile->hsize;
1209     if (framedraw->ystart_param->dp > 0)
1210       yend = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
1211     else
1212       yend = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]))
1213              / (double)cfile->vsize;
1214 
1215     if (xend == xinit && yend == yinit && xcurrent == xstart && ycurrent == ystart) {
1216       /// the focus is at xend, yend, but the crosshair may be offcenter. We need its position, and then we can find the offset to
1217       /// xcurrent, ycurrent. The offset is then added to the center.
1218       xstart = 0.5;
1219       ystart = 0.5;
1220 
1221       if (xend - xstart / scale < 0.) xstart = xend * scale;
1222       else if (xend + xstart / scale > 1.) xstart = 1. - (1. - xend) * scale;
1223       if (yend - ystart / scale < 0.) ystart = yend * scale;
1224       else if (yend + ystart / scale > 1.) ystart = 1. - (1. - yend) * scale;
1225 
1226       offs_x = (xcurrent - xstart) / scale;
1227       offs_y = (ycurrent - ystart) / scale;
1228 
1229       if (framedraw->xstart_param->dp > 0)
1230         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xend + offs_x);
1231       else
1232         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), (int)((xend + offs_x)
1233                                     * (double)cfile->hsize + .5));
1234       if (framedraw->xstart_param->dp > 0)
1235         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), yend + offs_y);
1236       else
1237         lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), (int)((yend + offs_y)
1238                                     * cfile->vsize + .5));
1239     }
1240   }
1241   break;
1242 
1243   case LIVES_PARAM_SPECIAL_TYPE_SINGLEPOINT:
1244     if (framedraw->xstart_param->dp > 0)
1245       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xstart);
1246     else
1247       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1248                                   (int)(xstart * (double)cfile->hsize + .5));
1249     if (framedraw->xstart_param->dp > 0)
1250       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), ystart);
1251     else
1252       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1253                                   (int)(ystart * (double)cfile->vsize + .5));
1254     break;
1255 
1256   default:
1257     break;
1258   }
1259   framedraw_redraw(framedraw, mainw->fd_layer_orig);
1260   return FALSE;
1261 }
1262 
on_framedraw_scroll(LiVESWidget * widget,LiVESXEventScroll * event,lives_special_framedraw_rect_t * framedraw)1263 boolean on_framedraw_scroll(LiVESWidget * widget, LiVESXEventScroll * event,
1264                             lives_special_framedraw_rect_t *framedraw) {
1265   if (!framedraw && mainw->multitrack) framedraw = mainw->multitrack->framedraw;
1266   if (!framedraw) return FALSE;
1267   if (mainw->multitrack && mainw->multitrack->track_index == -1) return FALSE;
1268 
1269   if (framedraw->type == LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT) {
1270     LiVESSpinButton *spb = (LiVESSpinButton *)framedraw->scale_param->widgets[0];
1271     LiVESAdjustment *adj = lives_spin_button_get_adjustment(spb);
1272     double step = lives_adjustment_get_step_increment(adj);
1273     double scale = lives_spin_button_get_value(spb);
1274     if (lives_get_scroll_direction(event) == LIVES_SCROLL_UP) scale += step;
1275     if (lives_get_scroll_direction(event) == LIVES_SCROLL_DOWN) scale -= step;
1276     lives_spin_button_set_value(spb, scale);
1277   }
1278   return FALSE;
1279 }
1280 
1281 
after_framedraw_widget_changed(LiVESWidget * widget,lives_special_framedraw_rect_t * framedraw)1282 void after_framedraw_widget_changed(LiVESWidget * widget, lives_special_framedraw_rect_t *framedraw) {
1283   if (mainw->block_param_updates || noupdate) return;
1284 
1285   // redraw mask when spin values change
1286   framedraw_redraw(framedraw, mainw->fd_layer_orig);
1287   if (mainw->framedraw_reset) {
1288     lives_widget_set_sensitive(mainw->framedraw_reset, TRUE);
1289   }
1290   if (mainw->framedraw_preview) {
1291     lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
1292   }
1293 }
1294 
1295 
on_framedraw_reset_clicked(LiVESButton * button,lives_special_framedraw_rect_t * framedraw)1296 void on_framedraw_reset_clicked(LiVESButton * button, lives_special_framedraw_rect_t *framedraw) {
1297   // reset to defaults
1298   if (fx_dialog[0]) {
1299     if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, FALSE);
1300     if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, FALSE);
1301   }
1302 
1303   noupdate = TRUE;
1304   if (framedraw->xend_param) {
1305     if (framedraw->xend_param->dp == 0)
1306       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1307                                   (double)get_int_param(framedraw->xend_param->def));
1308     else
1309       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), get_double_param(framedraw->xend_param->def));
1310   }
1311   if (framedraw->yend_param) {
1312     if (framedraw->yend_param->dp == 0)
1313       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1314                                   (double)get_int_param(framedraw->yend_param->def));
1315     else
1316       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), get_double_param(framedraw->yend_param->def));
1317   }
1318   if (framedraw->xstart_param) {
1319     if (framedraw->xstart_param->dp == 0)
1320       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1321                                   (double)get_int_param(framedraw->xstart_param->def));
1322     else
1323       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1324                                   get_double_param(framedraw->xstart_param->def));
1325   }
1326   if (framedraw->ystart_param) {
1327     if (framedraw->ystart_param->dp == 0)
1328       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1329                                   (double)get_int_param(framedraw->ystart_param->def));
1330     else
1331       lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1332                                   get_double_param(framedraw->ystart_param->def));
1333   }
1334 
1335   if (mainw->framedraw_reset) {
1336     lives_widget_set_sensitive(mainw->framedraw_reset, TRUE);
1337   }
1338   if (mainw->framedraw_preview) {
1339     lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
1340   }
1341 
1342   // update widgets now
1343   //lives_widget_context_update();
1344 
1345   noupdate = FALSE;
1346 
1347   framedraw_redraw(framedraw, mainw->fd_layer_orig);
1348   if (fx_dialog[0]) {
1349     if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, TRUE);
1350     if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, TRUE);
1351   }
1352 }
1353