1 // paramwindow.c
2 // LiVES
3 // (c) G. Finch 2004 - 2018 <salsaman+lives@gmail.com>
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING or www.gnu.org for licensing details
6 
7 // dynamic window generation from parameter arrays :-)
8 
9 #include "main.h"
10 #include "paramwindow.h"
11 #include "callbacks.h"
12 #include "resample.h"
13 #include "effects.h"
14 #include "rte_window.h"
15 #include "framedraw.h"
16 #include "ce_thumbs.h"
17 #include "interface.h"
18 
19 #ifdef ENABLE_GIW
20 #include "giw/giwknob.h"
21 #endif
22 
23 static int ireinit = 0;
24 
25 extern boolean do_effect(lives_rfx_t *, boolean is_preview);  //effects.c in LiVES
26 extern void on_realfx_activate(LiVESMenuItem *, livespointer rfx);  // effects.c in LiVES
27 
28 static void after_param_text_buffer_changed(LiVESTextBuffer *textbuffer, lives_rfx_t *rfx);
29 
30 // TODO -
31 // use list of these in case we have multiple windows open
32 // right now this is single threaded because of this
33 static LiVESSList *usrgrp_to_livesgrp[2] = {NULL, NULL}; // ordered list of lives_widget_group_t
34 
do_onchange_init(lives_rfx_t * rfx)35 LiVESList *do_onchange_init(lives_rfx_t *rfx) {
36   LiVESList *onchange = NULL;
37   LiVESList *retvals = NULL;
38   char **array;
39   char *type;
40 
41   register int i;
42 
43   if (rfx->status == RFX_STATUS_WEED) return NULL;
44 
45   switch (rfx->status) {
46   case RFX_STATUS_SCRAP:
47     type = lives_strdup(PLUGIN_RFX_SCRAP);
48     break;
49   case RFX_STATUS_BUILTIN:
50     type = lives_strdup(PLUGIN_RENDERED_EFFECTS_BUILTIN);
51     break;
52   case RFX_STATUS_CUSTOM:
53     type = lives_strdup(PLUGIN_RENDERED_EFFECTS_CUSTOM);
54     break;
55   default:
56     type = lives_strdup_printf(PLUGIN_RENDERED_EFFECTS_TEST);
57     break;
58   }
59   if ((onchange = plugin_request_by_line(type, rfx->name, "get_onchange")) != NULL) {
60     for (i = 0; i < lives_list_length(onchange); i++) {
61       array = lives_strsplit((char *)lives_list_nth_data(onchange, i), rfx->delim, -1);
62       if (!strcmp(array[0], "init")) {
63         // onchange is init
64         // create dummy object with data
65         LiVESWidget *dummy_widget = lives_label_new(NULL);
66         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(dummy_widget), PARAM_NUMBER_KEY,
67                                      LIVES_INT_TO_POINTER(-1));
68         retvals = do_onchange(LIVES_WIDGET_OBJECT(dummy_widget), rfx);
69         lives_widget_destroy(dummy_widget);
70         lives_strfreev(array);
71         break;
72       }
73       lives_strfreev(array);
74     }
75     lives_list_free_all(&onchange);
76   }
77   lives_free(type);
78 
79   return retvals;
80 }
81 
82 
on_paramwindow_button_clicked2(LiVESButton * button,lives_rfx_t * rfx)83 static void on_paramwindow_button_clicked2(LiVESButton *button, lives_rfx_t *rfx) {
84   // close from rte window
85   on_paramwindow_button_clicked(button, rfx);
86   lives_freep((void **)&fx_dialog[1]);
87 }
88 
89 
on_paramwindow_button_clicked(LiVESButton * button,lives_rfx_t * rfx)90 void on_paramwindow_button_clicked(LiVESButton *button, lives_rfx_t *rfx) {
91   LiVESWidget *dialog = NULL;
92   boolean def_ok = FALSE;
93   int i;
94 
95   if (button) {
96     lives_widget_set_sensitive(LIVES_WIDGET(button), FALSE);
97     dialog = lives_widget_get_toplevel(LIVES_WIDGET(button));
98   }
99 
100   if (dialog && LIVES_IS_DIALOG(dialog)) {
101     if (lives_dialog_get_response_for_widget(LIVES_DIALOG(dialog), LIVES_WIDGET(button)) == LIVES_RESPONSE_OK) {
102       def_ok = TRUE;
103     }
104   }
105 
106   if (mainw->textwidget_focus && LIVES_IS_WIDGET_OBJECT(mainw->textwidget_focus)) {
107     // make sure text widgets are updated if they activate the default
108     LiVESWidget *textwidget =
109       (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mainw->textwidget_focus), TEXTWIDGET_KEY);
110     after_param_text_changed(textwidget, rfx);
111   }
112 
113   if (!special_cleanup(def_ok)) {
114     lives_dialog_response(LIVES_DIALOG(lives_widget_get_toplevel(LIVES_WIDGET(button))), LIVES_RESPONSE_RETRY);
115     if (button) lives_widget_set_sensitive(LIVES_WIDGET(button), TRUE);
116     return;
117   }
118 
119   mainw->textwidget_focus = NULL;
120 
121   if (def_ok && rfx && rfx->status != RFX_STATUS_SCRAP) mainw->keep_pre = mainw->did_rfx_preview;
122 
123   mainw->block_param_updates = TRUE;
124 
125   if (mainw->did_rfx_preview) {
126     if (def_ok) {
127       for (i = 0; i < rfx->num_params; i++) {
128         if (rfx->params[i].changed) {
129           mainw->keep_pre = FALSE;
130           break;
131         }
132       }
133     }
134 
135     if (!mainw->keep_pre) {
136       lives_kill_subprocesses(cfile->handle, TRUE);
137 
138       if (cfile->start == 0) {
139         cfile->start = 1;
140         cfile->end = cfile->frames;
141       }
142 
143       do_rfx_cleanup(rfx);
144       mainw->did_rfx_preview = FALSE;
145     }
146     mainw->show_procd = TRUE;
147   }
148 
149   if (!def_ok) {
150     if (rfx && mainw->is_generating && rfx->source_type == LIVES_RFX_SOURCE_NEWCLIP &&
151         CURRENT_CLIP_IS_NORMAL && rfx->source == cfile &&
152         rfx->name && rfx->status != RFX_STATUS_WEED && rfx->status != RFX_STATUS_SCRAP &&
153         rfx->num_in_channels == 0 && rfx->min_frames >= 0 && !rfx->is_template) {
154       // for a generator, we silently close the (now) temporary file we would have generated frames into
155       mainw->suppress_dprint = TRUE;
156       close_current_file(mainw->pre_src_file);
157       mainw->suppress_dprint = FALSE;
158       if (mainw->multitrack) mainw->pre_src_file = -1;
159       mainw->is_generating = FALSE;
160       rfx->source = NULL;
161       rfx->source_type = LIVES_RFX_SOURCE_RFX;
162     }
163     mainw->keep_pre = FALSE;
164   }
165 
166   if (!rfx) {
167     if (usrgrp_to_livesgrp[1]) lives_slist_free(usrgrp_to_livesgrp[1]);
168     usrgrp_to_livesgrp[1] = NULL;
169   } else {
170     if (rfx->status == RFX_STATUS_WEED) {
171       if (usrgrp_to_livesgrp[1]) lives_slist_free(usrgrp_to_livesgrp[1]);
172       usrgrp_to_livesgrp[1] = NULL;
173       if (rfx != mainw->fx_candidates[FX_CANDIDATE_RESIZER].rfx) {
174         rfx_free(rfx);
175         lives_free(rfx);
176       }
177     } else {
178       if (usrgrp_to_livesgrp[0]) lives_slist_free(usrgrp_to_livesgrp[0]);
179       usrgrp_to_livesgrp[0] = NULL;
180     }
181   }
182 
183   mainw->block_param_updates = FALSE;
184 
185   if (def_ok && rfx && rfx->status == RFX_STATUS_SCRAP) return;
186 
187   if (button)
188     if (dialog) {
189       // prevent a gtk+ crash by removing the focus before detroying the dialog
190       LiVESWidget *content_area = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
191       lives_container_set_focus_child(LIVES_CONTAINER(content_area), NULL);
192     }
193 
194   lives_general_button_clicked(button, NULL);
195 
196   if (def_ok) {
197     if (rfx->status == RFX_STATUS_WEED) on_realfx_activate(NULL, rfx);
198     else on_render_fx_activate(NULL, rfx);
199   }
200 
201   if (mainw->multitrack) {
202     polymorph(mainw->multitrack, POLY_NONE);
203     polymorph(mainw->multitrack, POLY_CLIPS);
204     mt_sensitise(mainw->multitrack);
205   }
206 }
207 
208 
209 /**
210    get a (radiobutton) list from an index
211 */
get_group(lives_rfx_t * rfx,lives_param_t * param)212 static lives_widget_group_t *get_group(lives_rfx_t *rfx, lives_param_t *param) {
213   if (rfx->status == RFX_STATUS_WEED) {
214     return livesgrp_from_usrgrp(usrgrp_to_livesgrp[1], param->group);
215   } else {
216     return livesgrp_from_usrgrp(usrgrp_to_livesgrp[0], param->group);
217   }
218   return NULL;
219 }
220 
221 
on_render_fx_activate(LiVESMenuItem * menuitem,lives_rfx_t * rfx)222 void on_render_fx_activate(LiVESMenuItem *menuitem, lives_rfx_t *rfx) {
223   uint32_t chk_mask = 0;
224 
225   if (menuitem && rfx->num_in_channels > 0) {
226     chk_mask = WARN_MASK_LAYOUT_ALTER_FRAMES;
227     if (!check_for_layout_errors(NULL, mainw->current_file, cfile->start, cfile->end, &chk_mask)) {
228       return;
229     }
230   }
231 
232   // do onchange|init
233   if (menuitem) {
234     LiVESList *retvals = do_onchange_init(rfx);
235     lives_list_free_all(&retvals);
236   }
237   if (rfx->min_frames > -1) {
238     do_effect(rfx, FALSE);
239   }
240 
241   if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
242 }
243 
244 
gen_width_changed(LiVESSpinButton * spin,livespointer user_data)245 static void gen_width_changed(LiVESSpinButton *spin, livespointer user_data) {
246   weed_plant_t *ctmpl = (weed_plant_t *)user_data;
247   int val = lives_spin_button_get_value_as_int(spin);
248   int error, old_val = 0;
249   int step;
250   // value in chantmp in pixels, not macropixels
251   if (weed_plant_has_leaf(ctmpl, WEED_LEAF_HOST_WIDTH)) old_val = weed_get_int_value(ctmpl, WEED_LEAF_HOST_WIDTH, &error);
252   if (val == old_val) return;
253   step = 1;
254   if (weed_plant_has_leaf(ctmpl, WEED_LEAF_HSTEP)) step = weed_get_int_value(ctmpl, WEED_LEAF_HSTEP, &error);
255 
256   val = ALIGN_CEIL(val, step);
257   weed_set_int_value(ctmpl, WEED_LEAF_HOST_WIDTH, val);
258   lives_spin_button_set_value(spin, (double)val);
259 }
260 
261 
gen_height_changed(LiVESSpinButton * spin,livespointer user_data)262 static void gen_height_changed(LiVESSpinButton *spin, livespointer user_data) {
263   weed_plant_t *ctmpl = (weed_plant_t *)user_data;
264   int val = lives_spin_button_get_value_as_int(spin);
265   int error, old_val = 0;
266   int step;
267 
268   if (weed_plant_has_leaf(ctmpl, WEED_LEAF_HOST_HEIGHT)) old_val = weed_get_int_value(ctmpl, WEED_LEAF_HOST_HEIGHT, &error);
269 
270   if (val == old_val) return;
271   step = 1;
272   if (weed_plant_has_leaf(ctmpl, WEED_LEAF_HSTEP)) step = weed_get_int_value(ctmpl, WEED_LEAF_HSTEP, &error);
273 
274   val = ALIGN_CEIL(val, step);
275   weed_set_int_value(ctmpl, WEED_LEAF_HOST_HEIGHT, val);
276   lives_spin_button_set_value(spin, (double)val);
277 }
278 
279 
gen_fps_changed(LiVESSpinButton * spin,livespointer user_data)280 static void gen_fps_changed(LiVESSpinButton *spin, livespointer user_data) {
281   weed_plant_t *filter = (weed_plant_t *)user_data;
282   double val = lives_spin_button_get_value(spin);
283   weed_set_double_value(filter, WEED_LEAF_HOST_FPS, val);
284 }
285 
286 
trans_in_out_pressed(lives_rfx_t * rfx,boolean in)287 static void trans_in_out_pressed(lives_rfx_t *rfx, boolean in) {
288   weed_plant_t **in_params;
289 
290   weed_plant_t *inst = (weed_plant_t *)rfx->source;
291   weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
292   weed_plant_t *tparam;
293   weed_plant_t *tparamtmpl;
294 
295   int key = -1;
296   int ptype, nparams;
297   int trans = get_transition_param(filter, FALSE);
298 
299   do {
300     // handle compound fx
301     if (weed_plant_has_leaf(inst, WEED_LEAF_IN_PARAMETERS)) {
302       nparams = weed_leaf_num_elements(inst, WEED_LEAF_IN_PARAMETERS);
303       if (trans < nparams) break;
304       trans -= nparams;
305     }
306   } while (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE) &&
307            (inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL)) != NULL);
308 
309   in_params = weed_instance_get_in_params(inst, NULL);
310   tparam = in_params[trans];
311   tparamtmpl = weed_param_get_template(tparam);
312   ptype = weed_paramtmpl_get_type(tparamtmpl);
313 
314   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
315   if (!filter_mutex_trylock(key)) {
316     if (ptype == WEED_PARAM_INTEGER) {
317       if (in) weed_set_int_value(tparam, WEED_LEAF_VALUE, weed_get_int_value(tparamtmpl, WEED_LEAF_MIN, NULL));
318       else weed_set_int_value(tparam, WEED_LEAF_VALUE, weed_get_int_value(tparamtmpl, WEED_LEAF_MAX, NULL));
319     } else {
320       if (in) weed_set_double_value(tparam, WEED_LEAF_VALUE, weed_get_double_value(tparamtmpl, WEED_LEAF_MIN, NULL));
321       else weed_set_double_value(tparam, WEED_LEAF_VALUE, weed_get_double_value(tparamtmpl, WEED_LEAF_MAX, NULL));
322     }
323     filter_mutex_unlock(key);
324     set_copy_to(inst, trans, rfx, TRUE);
325   }
326 
327   update_visual_params(rfx, FALSE);
328   lives_free(in_params);
329   activate_mt_preview(mainw->multitrack);
330 }
331 
332 
transition_in_pressed(LiVESToggleButton * tbut,livespointer rfx)333 static void transition_in_pressed(LiVESToggleButton *tbut, livespointer rfx) {
334   trans_in_out_pressed((lives_rfx_t *)rfx, TRUE);
335 }
336 
337 
transition_out_pressed(LiVESToggleButton * tbut,livespointer rfx)338 static void transition_out_pressed(LiVESToggleButton *tbut, livespointer rfx) {
339   trans_in_out_pressed((lives_rfx_t *)rfx, FALSE);
340 }
341 
342 
after_transaudio_toggled(LiVESToggleButton * togglebutton,livespointer rfx)343 static void after_transaudio_toggled(LiVESToggleButton *togglebutton, livespointer rfx) {
344   weed_plant_t *init_event = mainw->multitrack->init_event;
345 
346   if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(togglebutton)))
347     weed_set_boolean_value(init_event, WEED_LEAF_HOST_AUDIO_TRANSITION, WEED_TRUE);
348   else weed_set_boolean_value(init_event, WEED_LEAF_HOST_AUDIO_TRANSITION, WEED_FALSE);
349 }
350 
351 
transition_add_in_out(LiVESBox * vbox,lives_rfx_t * rfx,boolean add_audio_check)352 void transition_add_in_out(LiVESBox *vbox, lives_rfx_t *rfx, boolean add_audio_check) {
353   // add in/out radios for multitrack transitions
354   LiVESWidget *radiobutton_in;
355   LiVESWidget *radiobutton_out;
356   LiVESWidget *radiobutton_dummy;
357   LiVESWidget *hbox, *hbox2;
358   LiVESWidget *hseparator;
359 
360   LiVESSList *radiobutton_group = NULL;
361 
362   weed_plant_t *filter = weed_instance_get_filter((weed_plant_t *)rfx->source, TRUE);
363   int trans = get_transition_param(filter, FALSE);
364 
365   char *tmp, *tmp2;
366 
367   hbox = lives_hbox_new(FALSE, 0);
368   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_width);
369 
370   if (add_audio_check) {
371     int error;
372 
373     LiVESWidget *checkbutton;
374 
375     hbox2 = lives_hbox_new(FALSE, 0);
376 
377     if (has_video_chans_in(filter, FALSE))
378       lives_box_pack_start(LIVES_BOX(hbox), hbox2, FALSE, FALSE, widget_opts.packing_width);
379 
380     checkbutton = lives_standard_check_button_new((tmp = (_("_Crossfade audio"))),
381                   weed_plant_has_leaf(mainw->multitrack->init_event, WEED_LEAF_HOST_AUDIO_TRANSITION) &&
382                   weed_get_boolean_value(mainw->multitrack->init_event, WEED_LEAF_HOST_AUDIO_TRANSITION, &error) == WEED_TRUE,
383                   LIVES_BOX(hbox2), (tmp2 = lives_strdup(
384                       _("If checked, audio from both layers is mixed relative to the transition parameter.\n"
385                         "The setting is applied instantly to the entire transition."))));
386 
387     lives_free(tmp);
388     lives_free(tmp2);
389 
390     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
391                                     LIVES_GUI_CALLBACK(after_transaudio_toggled),
392                                     (livespointer)rfx);
393 
394     after_transaudio_toggled(LIVES_TOGGLE_BUTTON(checkbutton), (livespointer)rfx);
395   }
396 
397   // dummy radiobutton so we can have neither in nor out set
398   radiobutton_dummy = lives_standard_radio_button_new(NULL, &radiobutton_group, LIVES_BOX(hbox), NULL);
399   lives_widget_set_no_show_all(radiobutton_dummy, TRUE);
400 
401   widget_opts.pack_end = TRUE;
402   radiobutton_out = lives_standard_radio_button_new((tmp = (_("Transition _Out"))),
403                     &radiobutton_group, LIVES_BOX(hbox),
404                     (tmp2 = (_("Click to set the transition parameter to show only the rear frame"))));
405 
406   lives_free(tmp);
407   lives_free(tmp2);
408 
409   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(radiobutton_out), LIVES_WIDGET_TOGGLED_SIGNAL,
410                                   LIVES_GUI_CALLBACK(transition_out_pressed),
411                                   (livespointer)rfx);
412 
413   radiobutton_in = lives_standard_radio_button_new((tmp = (_("Transition _In"))),
414                    &radiobutton_group, LIVES_BOX(hbox),
415                    (tmp2 = (_("Click to set the transition parameter to show only the front frame"))));
416   lives_free(tmp); lives_free(tmp2);
417 
418   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(radiobutton_in), LIVES_WIDGET_TOGGLED_SIGNAL,
419                                   LIVES_GUI_CALLBACK(transition_in_pressed), (livespointer)rfx);
420 
421   widget_opts.pack_end = FALSE;
422 
423   if (palette->style & STYLE_1) {
424     lives_widget_set_fg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
425   }
426 
427   hseparator = lives_hseparator_new();
428   lives_box_pack_start(vbox, hseparator, FALSE, FALSE, 0);
429 
430   rfx->params[trans].widgets[WIDGET_RB_IN] = radiobutton_in;
431   rfx->params[trans].widgets[WIDGET_RB_OUT] = radiobutton_out;
432   rfx->params[trans].widgets[WIDGET_RB_DUMMY] = radiobutton_dummy;
433 }
434 
435 
add_sizes(LiVESBox * vbox,boolean add_fps,boolean has_param,lives_rfx_t * rfx)436 static boolean add_sizes(LiVESBox *vbox, boolean add_fps, boolean has_param, lives_rfx_t *rfx) {
437   // add size settings for generators and resize effects
438   LiVESWidget *label, *hbox;
439   LiVESWidget *spinbuttonh = NULL, *spinbuttonw = NULL;
440   LiVESWidget *spinbuttonf;
441   int num_chans = 0;
442 
443   weed_plant_t *filter = weed_instance_get_filter((weed_plant_t *)rfx->source, TRUE), *tmpl;
444   weed_plant_t **ctmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &num_chans);
445 
446   double def_fps = 0.;
447 
448   char *cname, *ltxt;
449 
450   boolean chk_params = (vbox == NULL);
451   boolean added = FALSE;
452 
453   int def_width = 0, max_width, width_step;
454   int def_height = 0, max_height, height_step;
455   int wopw = widget_opts.packing_width;
456 
457   register int i;
458 
459   if (chk_params) {
460     if (add_fps) return TRUE;
461   } else {
462     if (!has_param) lives_widget_set_size_request(LIVES_WIDGET(vbox), RFX_WINSIZE_H, RFX_WINSIZE_V);
463 
464     if (add_fps) {
465       added = TRUE;
466 
467       hbox = lives_hbox_new(FALSE, 0);
468       lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height * 2);
469 
470       add_fill_to_box(LIVES_BOX(hbox));
471 
472       if (weed_plant_has_leaf(filter, WEED_LEAF_HOST_FPS)) def_fps = weed_get_double_value(filter, WEED_LEAF_HOST_FPS, NULL);
473       else if (weed_plant_has_leaf(filter, WEED_LEAF_PREFERRED_FPS))
474         def_fps = weed_get_double_value(filter, WEED_LEAF_PREFERRED_FPS, NULL);
475 
476       if (def_fps == 0.) def_fps = prefs->default_fps;
477 
478       spinbuttonf = lives_standard_spin_button_new(_("Target _FPS (plugin may override this)"),
479                     def_fps, 1., FPS_MAX, 1., 10., 3, LIVES_BOX(hbox), NULL);
480 
481       lives_signal_sync_connect_after(LIVES_GUI_OBJECT(spinbuttonf), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
482                                       LIVES_GUI_CALLBACK(gen_fps_changed), filter);
483 
484       add_fill_to_box(LIVES_BOX(hbox));
485     }
486   }
487 
488   for (i = 0; i < num_chans; i++) {
489     tmpl = ctmpls[i];
490 
491     // TODO ***: allow alteration of "host_disabled" under some circumstances
492     // (e.g. allow enabling a first or second in channel, or first out_channel, or more for alphas)
493 
494     // make this into function called from here and from effects with optional enable-able channels
495     if (weed_get_boolean_value(tmpl, WEED_LEAF_HOST_DISABLED, NULL) == WEED_TRUE) continue;
496     if (weed_get_int_value(tmpl, WEED_LEAF_WIDTH, NULL)) continue;
497     if (weed_get_int_value(tmpl, WEED_LEAF_HEIGHT, NULL)) continue;
498 
499     if (chk_params) return TRUE;
500 
501     added = TRUE;
502 
503     if (rfx->is_template) {
504       cname = weed_get_string_value(tmpl, WEED_LEAF_NAME, NULL);
505       ltxt = lives_strdup_printf(_("%s : size"), cname);
506       lives_free(cname);
507     } else {
508       ltxt = (_("New size (pixels)"));
509     }
510 
511     label = lives_standard_label_new(ltxt);
512     lives_free(ltxt);
513 
514     hbox = lives_hbox_new(FALSE, 0);
515     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
516     lives_box_pack_start(LIVES_BOX(hbox), label, FALSE, FALSE, widget_opts.packing_width);
517 
518     def_width = weed_get_int_value(tmpl, WEED_LEAF_HOST_WIDTH, NULL);
519     if (!def_width) def_width = DEF_GEN_WIDTH;
520     max_width = weed_get_int_value(tmpl, WEED_LEAF_MAXWIDTH, NULL);
521     if (!max_width) max_width = INT_MAX;
522     if (def_width > max_width) def_width = max_width;
523     width_step = weed_get_int_value(tmpl, WEED_LEAF_HSTEP, NULL);
524     if (!width_step) width_step = 4;
525 
526     spinbuttonw = lives_standard_spin_button_new(_("_Width"), def_width, width_step, max_width, width_step,
527                   width_step, 0, LIVES_BOX(hbox), NULL);
528     lives_spin_button_set_snap_to_multiples(LIVES_SPIN_BUTTON(spinbuttonw), width_step);
529     lives_spin_button_update(LIVES_SPIN_BUTTON(spinbuttonw));
530 
531     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(spinbuttonw), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
532                                     LIVES_GUI_CALLBACK(gen_width_changed), tmpl);
533     weed_leaf_delete(tmpl, WEED_LEAF_HOST_WIDTH); // force a reset
534     gen_width_changed(LIVES_SPIN_BUTTON(spinbuttonw), tmpl);
535 
536     widget_opts.packing_width >>= 1;
537     add_fill_to_box(LIVES_BOX(hbox));
538     widget_opts.packing_width = wopw;
539 
540     def_height = weed_get_int_value(tmpl, WEED_LEAF_HOST_HEIGHT, NULL);
541     if (!def_height) def_height = DEF_GEN_HEIGHT;
542     max_height = weed_get_int_value(tmpl, WEED_LEAF_MAXHEIGHT, NULL);
543     if (!max_height) max_height = INT_MAX;
544     if (def_height > max_height) def_height = max_height;
545     height_step = weed_get_int_value(tmpl, WEED_LEAF_VSTEP, NULL);
546     if (!height_step) height_step = 4;
547 
548     spinbuttonh = lives_standard_spin_button_new(_("_Height"), def_height, height_step, max_height, height_step,
549                   height_step, 0, LIVES_BOX(hbox), NULL);
550     lives_spin_button_set_snap_to_multiples(LIVES_SPIN_BUTTON(spinbuttonh), height_step);
551     lives_spin_button_update(LIVES_SPIN_BUTTON(spinbuttonh));
552 
553     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(spinbuttonh), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
554                                     LIVES_GUI_CALLBACK(gen_height_changed), tmpl);
555     weed_leaf_delete(tmpl, WEED_LEAF_HOST_HEIGHT); // force a reset
556     gen_height_changed(LIVES_SPIN_BUTTON(spinbuttonh), tmpl);
557   }
558 
559   if (!rfx->is_template && num_chans == 1) {
560     if (chk_params) return TRUE;
561     added = TRUE;
562     // add "aspectratio" widget
563     add_aspect_ratio_button(LIVES_SPIN_BUTTON(spinbuttonw), LIVES_SPIN_BUTTON(spinbuttonh), LIVES_BOX(vbox));
564   }
565 
566   if (added) {
567     if (has_param) {
568       add_fill_to_box(LIVES_BOX(vbox));
569       add_hsep_to_box(vbox);
570     } else has_param = TRUE;
571   }
572   return has_param;
573 }
574 
575 
add_gen_to(LiVESBox * vbox,lives_rfx_t * rfx)576 static void add_gen_to(LiVESBox *vbox, lives_rfx_t *rfx) {
577   // add "generate to clipboard/new clip" for rendered generators
578   LiVESSList *radiobutton_group = NULL;
579 
580   LiVESWidget *radiobutton;
581   LiVESWidget *hseparator;
582 
583   LiVESWidget *hbox = lives_hbox_new(FALSE, 0);
584 
585   char *tmp, *tmp2;
586 
587   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
588 
589   radiobutton = lives_standard_radio_button_new((tmp = (_("Generate to _Clipboard"))),
590                 &radiobutton_group, LIVES_BOX(hbox),
591                 (tmp2 = (_("Generate frames to the clipboard"))));
592 
593   lives_free(tmp);
594   lives_free(tmp2);
595 
596   widget_opts.pack_end = TRUE;
597   radiobutton = lives_standard_radio_button_new((tmp = (_("Generate to _New Clip"))),
598                 &radiobutton_group, LIVES_BOX(hbox),
599                 (tmp2 = (_("Generate frames to a new clip"))));
600   widget_opts.pack_end = FALSE;
601 
602   lives_free(tmp);
603   lives_free(tmp2);
604 
605   hseparator = lives_hseparator_new();
606   lives_box_pack_start(vbox, hseparator, FALSE, FALSE, 0);
607 
608   toggle_toggles_var(LIVES_TOGGLE_BUTTON(radiobutton), &mainw->gen_to_clipboard, TRUE);
609 }
610 
611 
xspinw_changed(LiVESSpinButton * spinbutton,livespointer user_data)612 static void xspinw_changed(LiVESSpinButton *spinbutton, livespointer user_data) {
613   cfile->ohsize = cfile->hsize = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
614   reset_framedraw_preview();
615 }
616 
xspinh_changed(LiVESSpinButton * spinbutton,livespointer user_data)617 static void xspinh_changed(LiVESSpinButton *spinbutton, livespointer user_data) {
618   cfile->ovsize = cfile->vsize = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
619   reset_framedraw_preview();
620 }
621 
xspinfr_changed(LiVESSpinButton * spinbutton,livespointer user_data)622 static void xspinfr_changed(LiVESSpinButton *spinbutton, livespointer user_data) {
623   cfile->end = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
624 }
625 
xspinfps_changed(LiVESSpinButton * spinbutton,livespointer user_data)626 static void xspinfps_changed(LiVESSpinButton *spinbutton, livespointer user_data) {
627   cfile->pb_fps = cfile->fps = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
628   reset_framedraw_preview();
629 }
630 
add_genparams(LiVESWidget * vbox,lives_rfx_t * rfx)631 static void add_genparams(LiVESWidget *vbox, lives_rfx_t *rfx) {
632   // add nframes, fps, width, heights
633   LiVESWidget *sp_width, *sp_height, *sp_frames, *sp_fps;
634   LiVESWidget *frame = add_video_options(&sp_width, cfile->hsize, &sp_height, cfile->vsize, &sp_fps, cfile->fps,
635                                          &sp_frames, cfile->end, TRUE, NULL);
636   lives_box_pack_start(LIVES_BOX(vbox), frame, FALSE, TRUE, 0);
637 
638   lives_spin_button_update(LIVES_SPIN_BUTTON(sp_width));
639   lives_spin_button_update(LIVES_SPIN_BUTTON(sp_height));
640   if (sp_frames) lives_spin_button_update(LIVES_SPIN_BUTTON(sp_frames));
641   lives_spin_button_update(LIVES_SPIN_BUTTON(sp_fps));
642   cfile->ohsize = cfile->hsize = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(sp_width));
643   cfile->ovsize = cfile->vsize = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(sp_height));
644 
645   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(sp_width), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
646                                   LIVES_GUI_CALLBACK(xspinw_changed), NULL);
647   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(sp_height), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
648                                   LIVES_GUI_CALLBACK(xspinh_changed), NULL);
649   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(sp_frames), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
650                                   LIVES_GUI_CALLBACK(xspinfr_changed), NULL);
651   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(sp_fps), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
652                                   LIVES_GUI_CALLBACK(xspinfps_changed), NULL);
653 }
654 
655 
on_render_fx_pre_activate(LiVESMenuItem * menuitem,lives_rfx_t * rfx)656 LIVES_GLOBAL_INLINE void on_render_fx_pre_activate(LiVESMenuItem *menuitem, lives_rfx_t *rfx) {
657   _fx_dialog *fxdialog;
658   uint32_t chk_mask;
659   int start, end;
660 
661   if (!check_storage_space(mainw->current_file, FALSE)) return;
662   if ((rfx->props & RFX_PROPS_MAY_RESIZE && rfx->num_in_channels == 1) || rfx->min_frames < 0) {
663     start = 1;
664     end = 0;
665   } else {
666     start = cfile->start;
667     end = cfile->end;
668   }
669 
670   if (rfx->num_in_channels > 0) {
671     chk_mask = WARN_MASK_LAYOUT_ALTER_FRAMES;
672     if (!check_for_layout_errors(NULL, mainw->current_file, start, end, &chk_mask)) {
673       return;
674     }
675   }
676   fxdialog = on_fx_pre_activate(rfx, FALSE, NULL);
677   if (fxdialog) {
678     if (menuitem == LIVES_MENU_ITEM(mainw->resize_menuitem)) add_resnn_label(LIVES_DIALOG(fxdialog->dialog));
679     /* do { */
680     /*   resp = lives_dialog_run(LIVES_DIALOG(fxdialog->dialog)); */
681     /* } while (resp == LIVES_RESPONSE_RETRY); */
682   }
683 
684 }
685 
686 
on_fx_pre_activate(lives_rfx_t * rfx,boolean is_realtime,LiVESWidget * pbox)687 _fx_dialog *on_fx_pre_activate(lives_rfx_t *rfx, boolean is_realtime, LiVESWidget *pbox) {
688   // render a pre dialog for: rendered effects (fx_dialog[0]), or rte(fx_dialog[1]), or encoder plugin, or vpp (fx_dialog[1])
689   LiVESWidget *top_dialog_vbox = NULL;
690   LiVESAccelGroup *fxw_accel_group;
691   LiVESList *retvals = NULL;
692 
693   char *txt;
694 
695   boolean no_process = FALSE;
696   boolean is_defaults = FALSE;
697   boolean add_reset_ok = FALSE;
698   boolean has_param;
699 
700   int scrw, didx = 0;
701 
702   if (mainw->multitrack) {
703     if (mainw->multitrack->idlefunc > 0) {
704       lives_source_remove(mainw->multitrack->idlefunc);
705       mainw->multitrack->idlefunc = 0;
706     }
707     mt_desensitise(mainw->multitrack);
708   }
709 
710   if (is_realtime) {
711     didx = 1;
712     no_process = TRUE;
713   } else if (rfx->status != RFX_STATUS_WEED) {
714     retvals = do_onchange_init(rfx);
715   }
716   if (rfx->min_frames < 0) no_process = TRUE;
717 
718   if (!no_process && rfx->num_in_channels == 0) {
719     int new_file;
720     mainw->pre_src_file = mainw->current_file;
721 
722     // create a new file to generate frames into
723     if (!get_new_handle((new_file = mainw->first_free_file), NULL)) {
724       if (mainw->multitrack) {
725         mt_sensitise(mainw->multitrack);
726         mainw->multitrack->idlefunc = mt_idle_add(mainw->multitrack);
727       }
728 
729       lives_list_free_all(&retvals);
730 
731       return NULL;
732     }
733 
734     if (CURRENT_CLIP_IS_NORMAL) {
735       mainw->files[new_file]->hsize = cfile->hsize;
736       mainw->files[new_file]->vsize = cfile->vsize;
737       mainw->files[new_file]->fps = cfile->fps;
738     } else {
739       mainw->files[new_file]->hsize = DEF_GEN_WIDTH;
740       mainw->files[new_file]->vsize = DEF_GEN_HEIGHT;
741       mainw->files[new_file]->fps = DEF_FPS;
742     }
743 
744     mainw->is_generating = TRUE;
745     mainw->current_file = new_file;
746     rfx->source_type = LIVES_RFX_SOURCE_NEWCLIP;
747     rfx->source = cfile;
748 
749     cfile->ohsize = cfile->hsize;
750     cfile->ovsize = cfile->vsize;
751     cfile->pb_fps = cfile->fps;
752 
753     // dummy values
754     cfile->start = 1;
755     cfile->end = 100;
756   }
757 
758   if (!no_process && rfx->num_in_channels > 0) {
759     // check we have a real clip open
760     if (!CURRENT_CLIP_IS_VALID) {
761       lives_list_free_all(&retvals);
762       return NULL;
763     }
764     if (cfile->end - cfile->start + 1 < rfx->min_frames) {
765       lives_list_free_all(&retvals);
766       txt = lives_strdup_printf(_("\nYou must select at least %d frames to use this effect.\n\n"),
767                                 rfx->min_frames);
768       do_error_dialog(txt);
769       lives_free(txt);
770       return NULL;
771     }
772 
773     // here we invalidate cfile->ohsize, cfile->ovsize
774     cfile->ohsize = cfile->hsize;
775     cfile->ovsize = cfile->vsize;
776 
777     if (cfile->undo_action == UNDO_RESIZABLE) {
778       set_undoable(NULL, FALSE);
779     }
780   }
781 
782   if (rfx->status == RFX_STATUS_WEED && rfx->is_template) is_defaults = TRUE;
783 
784   if (!pbox) {
785     char *title, *defstr;
786 
787     // width works well with scale 0.7
788     if (rfx->status == RFX_STATUS_WEED || no_process || (rfx->num_in_channels == 0 &&
789         rfx->props & RFX_PROPS_BATCHG)) scrw = RFX_WINSIZE_H * 2. * widget_opts.scale;
790     else scrw = GUI_SCREEN_WIDTH - SCR_WIDTH_SAFETY;
791 
792     fx_dialog[didx] = (_fx_dialog *)lives_malloc(sizeof(_fx_dialog));
793     fx_dialog[didx]->okbutton = fx_dialog[didx]->cancelbutton = fx_dialog[didx]->resetbutton = NULL;
794     fx_dialog[didx]->rfx = NULL;
795     fx_dialog[didx]->key = fx_dialog[didx]->mode = -1;
796     if (is_defaults) defstr = (_("Defaults for "));
797     else defstr = lives_strdup("");
798     title = lives_strdup_printf("%s%s", defstr, _(rfx->menu_text[0] == '_' ? rfx->menu_text + 1 : rfx->menu_text));
799 
800     fx_dialog[didx]->dialog = lives_standard_dialog_new(title, FALSE, scrw, RFX_WINSIZE_V);
801     lives_free(defstr);
802     lives_free(title);
803     pbox = top_dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(fx_dialog[didx]->dialog));
804     fx_dialog[didx]->rfx = rfx;
805     lives_widget_set_hexpand(pbox, TRUE);
806   }
807 
808   if (rfx->status != RFX_STATUS_WEED && !no_process) {
809     // rendered fx preview
810 
811     LiVESWidget *hbox = lives_hbox_new(FALSE, 0);
812     lives_box_pack_start(LIVES_BOX(top_dialog_vbox), hbox, TRUE, TRUE, 0);
813 
814     lives_widget_set_hexpand(hbox, TRUE);
815     lives_widget_set_vexpand(hbox, TRUE);
816 
817     pbox = lives_vbox_new(FALSE, 0);
818     lives_box_pack_start(LIVES_BOX(hbox), pbox, TRUE, TRUE, 0);
819 
820     lives_widget_set_hexpand(pbox, TRUE);
821     lives_widget_set_vexpand(pbox, TRUE);
822 
823     // add preview window
824     if (rfx->num_in_channels > 0 || !(rfx->props & RFX_PROPS_BATCHG)) {
825       mainw->framedraw_frame = cfile->start;
826       widget_add_framedraw(LIVES_VBOX(pbox), cfile->start, cfile->end, !(rfx->props & RFX_PROPS_MAY_RESIZE),
827                            cfile->hsize, cfile->vsize, rfx);
828       if (rfx->props & RFX_PROPS_MAY_RESIZE) mainw->fd_max_frame = cfile->end;
829     }
830 
831     if (!(rfx->props & RFX_PROPS_BATCHG)) {
832       // connect spinbutton to preview
833       fd_connect_spinbutton(rfx);
834     }
835   }
836 
837   // add the param widgets; here we also set parameters for any special widgets in the framedraw
838   //main_thread_execute((lives_funcptr_t)make_param_box, WEED_SEED_BOOLEAN, &has_param, "vv", pbox, rfx);
839   has_param = make_param_box(LIVES_VBOX(pbox), rfx);
840 
841   // update widgets from onchange_init here
842   if (top_dialog_vbox) {
843     fxw_accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
844     lives_window_add_accel_group(LIVES_WINDOW(fx_dialog[didx]->dialog), fxw_accel_group);
845 
846     if (!no_process || is_defaults || rfx->status == RFX_STATUS_SCRAP) {
847       if (!is_defaults) {
848         fx_dialog[didx]->cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(fx_dialog[didx]->dialog),
849                                         LIVES_STOCK_CANCEL, NULL, LIVES_RESPONSE_CANCEL);
850         fx_dialog[didx]->okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(fx_dialog[didx]->dialog),
851                                     LIVES_STOCK_OK, NULL, LIVES_RESPONSE_OK);
852       } else add_reset_ok = TRUE;
853     } else {
854       if (rfx->status == RFX_STATUS_WEED) {
855         add_reset_ok = TRUE;
856       }
857     }
858 
859     if (add_reset_ok) {
860       fx_dialog[didx]->resetbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(fx_dialog[didx]->dialog),
861                                      LIVES_STOCK_REVERT_TO_SAVED, _("Reset"), LIVES_RESPONSE_RESET);
862       fx_dialog[didx]->okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(fx_dialog[didx]->dialog), LIVES_STOCK_APPLY,
863                                   _("Set as default"), LIVES_RESPONSE_OK);
864       if (!has_param) {
865         lives_widget_set_sensitive(fx_dialog[didx]->resetbutton, FALSE);
866         lives_widget_set_sensitive(fx_dialog[didx]->okbutton, FALSE);
867       }
868     }
869 
870     if (fx_dialog[didx]->cancelbutton == NULL) {
871       fx_dialog[didx]->cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(fx_dialog[didx]->dialog), LIVES_STOCK_CLOSE,
872                                       _("_Close Window"), LIVES_RESPONSE_CANCEL);
873     }
874     lives_widget_add_accelerator(fx_dialog[didx]->cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, fxw_accel_group,
875                                  LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
876 
877     if (fx_dialog[didx]->okbutton) {
878       lives_button_grab_default_special(fx_dialog[didx]->okbutton);
879     } else {
880       lives_button_grab_default_special(fx_dialog[didx]->cancelbutton);
881     }
882 
883     if (no_process && !is_defaults) {
884       if (!is_realtime) {
885         if (fx_dialog[didx]->okbutton)
886           lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
887                                     LIVES_GUI_CALLBACK(on_paramwindow_button_clicked), rfx);
888         lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
889                                   LIVES_GUI_CALLBACK(on_paramwindow_button_clicked), rfx);
890         lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->dialog), LIVES_WIDGET_DELETE_EVENT,
891                                   LIVES_GUI_CALLBACK(on_paramwindow_button_clicked), rfx);
892       } else {
893         lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
894                                   LIVES_GUI_CALLBACK(on_paramwindow_button_clicked2), rfx);
895         if (rfx->status == RFX_STATUS_SCRAP)
896           lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
897                                     LIVES_GUI_CALLBACK(on_paramwindow_button_clicked2), rfx);
898         else {
899           if (fx_dialog[didx]->okbutton)
900             lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
901                                       LIVES_GUI_CALLBACK(rte_set_key_defs), rfx);
902           if (fx_dialog[didx]->resetbutton) {
903             lives_signal_sync_connect_after(LIVES_GUI_OBJECT(fx_dialog[didx]->resetbutton), LIVES_WIDGET_CLICKED_SIGNAL,
904                                             LIVES_GUI_CALLBACK(rte_reset_defs_clicked), rfx);
905           }
906         }
907         lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->dialog), LIVES_WIDGET_DELETE_EVENT,
908                                   LIVES_GUI_CALLBACK(on_paramwindow_button_clicked2), rfx);
909       }
910     } else {
911       if (!is_defaults) {
912         if (fx_dialog[didx]->okbutton)
913           lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
914                                     LIVES_GUI_CALLBACK(on_paramwindow_button_clicked), (livespointer)rfx);
915         lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
916                                   LIVES_GUI_CALLBACK(on_paramwindow_button_clicked), (livespointer)rfx);
917         lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->dialog), LIVES_WIDGET_DELETE_EVENT,
918                                   LIVES_GUI_CALLBACK(on_paramwindow_button_clicked), (livespointer)rfx);
919       } else {
920         if (fx_dialog[didx]->okbutton)
921           lives_signal_sync_connect_after(LIVES_GUI_OBJECT(fx_dialog[didx]->okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
922                                           LIVES_GUI_CALLBACK(rte_set_defs_ok), rfx);
923         if (fx_dialog[didx]->resetbutton) {
924           lives_signal_sync_connect_after(LIVES_GUI_OBJECT(fx_dialog[didx]->resetbutton), LIVES_WIDGET_CLICKED_SIGNAL,
925                                           LIVES_GUI_CALLBACK(rte_reset_defs_clicked), rfx);
926         }
927         lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
928                                   LIVES_GUI_CALLBACK(rte_set_defs_cancel), rfx);
929         lives_signal_sync_connect(LIVES_GUI_OBJECT(fx_dialog[didx]->dialog), LIVES_WIDGET_DELETE_EVENT,
930                                   LIVES_GUI_CALLBACK(rte_set_defs_cancel), rfx);
931       }
932     }
933   }
934 
935   // tweak some things to do with framedraw preview
936   if (mainw->framedraw) fd_tweak(rfx);
937 
938   if (!mainw->ce_thumbs)
939     lives_widget_show_all(fx_dialog[didx]->dialog);
940 
941   if (retvals) {
942     // now apply visually anything we got from onchange_init
943     param_demarshall(rfx, retvals, TRUE, TRUE);
944     lives_list_free_all(&retvals);
945   }
946   return fx_dialog[didx];
947 }
948 
949 
check_hidden_gui(weed_plant_t * inst,lives_param_t * param,int idx)950 static void check_hidden_gui(weed_plant_t *inst, lives_param_t *param, int idx) {
951   weed_plant_t *wparam;
952   if (param->type == LIVES_PARAM_UNDISPLAYABLE || param->type == LIVES_PARAM_UNKNOWN)
953     param->hidden |= HIDDEN_UNDISPLAYABLE;
954   if ((param->reinit & REINIT_FUNCTIONAL)
955       && weed_get_int_value(inst, WEED_LEAF_HOST_REFS, NULL) >= 2) {
956     // effect is running and user is editing the params, we should hide reinit params
957     // so as not to disturb the flow !
958     param->hidden |= HIDDEN_NEEDS_REINIT;
959   } else param->hidden &= ~HIDDEN_NEEDS_REINIT;
960 
961   if (is_hidden_param(inst, idx)) param->hidden |= HIDDEN_GUI_PERM;
962 
963   wparam = weed_inst_in_param(inst, idx, FALSE, FALSE);
964 
965   if (wparam) {
966     if (weed_param_is_hidden(wparam, WEED_FALSE) == WEED_FALSE) {
967       if (weed_param_is_hidden(wparam, WEED_TRUE)) param->hidden |= HIDDEN_GUI_TEMP;
968       else param->hidden &= ~HIDDEN_GUI_TEMP;
969     }
970   }
971 }
972 
973 
num_in_params_for_nth_instance(weed_plant_t * inst,int idx)974 static int num_in_params_for_nth_instance(weed_plant_t *inst, int idx) {
975   // get number of params for nth instance in a compound effect - gives an offset for param number within the compound
976   while (--idx > 0) inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL);
977   return weed_leaf_num_elements(inst, WEED_LEAF_IN_PARAMETERS);
978 }
979 
980 
fmt_match(char * fmt_string)981 static boolean fmt_match(char *fmt_string) {
982   const char *myfmt = fmt_string, *xfmt = myfmt + FMT_STRING_SIZE;
983   size_t xlen = lives_strlen(myfmt), ylen;
984 
985   // g_print("\nROW\n");
986   if (xlen == 0) {
987     //g_print("HSEP\n");
988     return FALSE;
989   }
990   ylen = lives_strlen(xfmt);
991   if (ylen == 0) {
992     //g_print("2HSEP\n");
993     return FALSE;
994   }
995 
996   if (xlen < ylen) ylen = xlen;
997 
998   for (int j = 0; j < ylen; j++) {
999     //g_print(" CF %d %d", myfmt[j], xfmt[j]);
1000     if (xfmt[j] != -1 && myfmt[j] != -1 && xfmt[j] != myfmt[j]) return FALSE;
1001     if ((xfmt[j] == -2 || myfmt[j] == -2) && xfmt[j] != myfmt[j]) return FALSE;
1002   }
1003 
1004   //g_print("\nMATch\n");
1005   return TRUE;
1006 }
1007 
1008 
1009 /**
1010    @brief make a dynamic parameter window
1011 
1012    if top_vbox is NULL: we just check for displayable params, returning FALSE there are none to be shown.
1013    otherwise, adds widgets to top_vbox, returning FALSE if nothing was added
1014 */
make_param_box(LiVESVBox * top_vbox,lives_rfx_t * rfx)1015 boolean make_param_box(LiVESVBox *top_vbox, lives_rfx_t *rfx) {
1016   lives_param_t *param = NULL;
1017 
1018   LiVESWidget *param_vbox = NULL;
1019   LiVESWidget *top_hbox = NULL;
1020   LiVESWidget *hbox = NULL;
1021   LiVESWidget *last_label = NULL;
1022   LiVESWidget *layoutx = NULL;
1023   LiVESWidget *dummy_label = NULL;
1024 
1025   // put whole thing in scrolled window
1026   LiVESWidget *scrolledwindow;
1027 
1028   LiVESList *hints = NULL;
1029   LiVESList *onchange = NULL;
1030   LiVESList *layout = NULL;
1031   LiVESList *list;
1032 
1033   char **array;
1034   char label_text[256]; // max length of a label in layout hints
1035 
1036   char *line;
1037   char *type = NULL;
1038   char *format = NULL;
1039 
1040   char fmt_strings[MAX_FMT_STRINGS][FMT_STRING_SIZE];
1041 
1042   size_t fmtlen, ll;
1043 
1044   boolean used[rfx->num_params];
1045   boolean has_box = FALSE;
1046   boolean internal = FALSE;
1047   boolean noslid;
1048   boolean has_param = FALSE;
1049   boolean chk_params = FALSE;
1050   boolean needs_sizes = FALSE;
1051   boolean layout_mode = FALSE;
1052   boolean keepsmall;
1053 
1054   int pnum;
1055   int length;
1056   int poffset = 0, inum = 0;
1057   int wofl = widget_opts.filler_len;
1058 
1059   int num_tok;
1060 
1061   int c_fmt_strings = 0;
1062   int pass;
1063   int woph = widget_opts.packing_height;
1064 
1065   int i, j, k;
1066 
1067   char sepnpnum[1024];
1068   size_t sepnpnumlen;
1069 
1070   lives_snprintf(sepnpnum, 1024, "s%s", rfx->delim);
1071   sepnpnumlen = strlen(sepnpnum);
1072 
1073   if (!top_vbox) {
1074     // just check how many non-hidden params without displaying
1075     chk_params = TRUE;
1076   } else {
1077     dummy_label = lives_label_new(NULL);
1078     lives_widget_object_ref_sink(LIVES_WIDGET_OBJECT(dummy_label));
1079 
1080     mainw->textwidget_focus = NULL;
1081 
1082     // initialise special widgets
1083     init_special();
1084 
1085     if (rfx->status == RFX_STATUS_WEED) usrgrp_to_livesgrp[1] = NULL;
1086     else usrgrp_to_livesgrp[0] = NULL;
1087 
1088     // paramwindow start, everything goes in top_hbox
1089     top_hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
1090 
1091     // param_vbox holds the dynamic parameters
1092     param_vbox = lives_vbox_new(FALSE, widget_opts.packing_height);
1093     lives_widget_set_halign(param_vbox, LIVES_ALIGN_FILL);
1094     lives_widget_set_valign(param_vbox, LIVES_ALIGN_CENTER);
1095     lives_box_pack_start(LIVES_BOX(top_hbox), param_vbox, TRUE, TRUE, widget_opts.packing_width);
1096 
1097     for (i = 0; i < rfx->num_params; i++) {
1098       used[i] = FALSE;
1099       for (j = 0; j < MAX_PARAM_WIDGETS; j++) {
1100         if (rfx->params[i].transition && j > 0 && j < 4) continue;
1101         rfx->params[i].widgets[j] = NULL;
1102       }
1103     }
1104   }
1105 
1106   switch (rfx->status) {
1107   case RFX_STATUS_BUILTIN:
1108     if (!chk_params) type = lives_strdup(PLUGIN_RENDERED_EFFECTS_BUILTIN);
1109     break;
1110   case RFX_STATUS_CUSTOM:
1111     if (!chk_params) type = lives_strdup(PLUGIN_RENDERED_EFFECTS_CUSTOM);
1112     break;
1113   case RFX_STATUS_SCRAP:
1114     if (!chk_params) type = lives_strdup(PLUGIN_RFX_SCRAP);
1115     break;
1116   case RFX_STATUS_WEED:
1117     if (!mainw->multitrack && rfx->is_template) {
1118       weed_plant_t *filter = weed_instance_get_filter((weed_plant_t *)rfx->source, TRUE);
1119       if (enabled_in_channels(filter, FALSE) == 0 && enabled_out_channels(filter, FALSE) > 0
1120           && has_video_chans_out(filter, TRUE)) {
1121         // out channel size(s) and target_fps for generators
1122         needs_sizes = TRUE;
1123       }
1124     }
1125     // extras for converters
1126     if (weed_instance_is_resizer((weed_plant_t *)rfx->source)) {
1127       has_param = add_sizes(LIVES_BOX(param_vbox), FALSE, FALSE, rfx);
1128       if (chk_params && has_param) return TRUE;
1129     }
1130     internal = TRUE;
1131     break;
1132   default:
1133     if (!chk_params) type = lives_strdup(PLUGIN_RENDERED_EFFECTS_TEST);
1134     break;
1135   }
1136 
1137   if (internal) {
1138     if (mainw->multitrack) {
1139       // extras for multitrack
1140       weed_plant_t *filter = weed_instance_get_filter((weed_plant_t *)rfx->source, TRUE);
1141       if (enabled_in_channels(filter, FALSE) == 2 && get_transition_param(filter, FALSE) != -1) {
1142         // add in/out for multitrack transition
1143         if (chk_params) return TRUE;
1144         has_param = TRUE;
1145         transition_add_in_out(LIVES_BOX(param_vbox), rfx, (mainw->multitrack->opts.pertrack_audio));
1146       }
1147     }
1148     if (!chk_params) hints = get_external_window_hints(rfx);
1149   } else {
1150     if (rfx->status != RFX_STATUS_SCRAP && rfx->num_in_channels == 0 && rfx->min_frames > -1) {
1151       if (!mainw->multitrack) {
1152         if (chk_params) return TRUE;
1153         add_gen_to(LIVES_BOX(param_vbox), rfx);
1154       } else mainw->gen_to_clipboard = FALSE;
1155       /// add nframes, fps, width, height
1156       add_genparams(param_vbox, rfx);
1157       has_param = TRUE;
1158     }
1159 
1160     if (!chk_params) {
1161       // do onchange|init
1162       if ((onchange = plugin_request_by_line(type, rfx->name, "get_onchange"))) {
1163         for (i = 0; i < lives_list_length(onchange); i++) {
1164           array = lives_strsplit((char *)lives_list_nth_data(onchange, i), rfx->delim, -1);
1165           if (strcmp(array[0], "init")) {
1166             // note other onchanges so we don't have to keep parsing the list
1167             int which = atoi(array[0]);
1168             if (which >= 0 && which < rfx->num_params) {
1169               rfx->params[which].onchange = TRUE;
1170             }
1171           }
1172           lives_strfreev(array);
1173         }
1174         lives_list_free_all(&onchange);
1175       }
1176       hints = plugin_request_by_line(type, rfx->name, "get_param_window");
1177       lives_free(type);
1178     }
1179   }
1180 
1181   // do param window hints
1182   if (hints) {
1183     LiVESList *list;
1184     char *lstring = lives_strconcat("layout", rfx->delim, NULL);
1185     char *sstring = lives_strconcat("special", rfx->delim, NULL);
1186     char *istring = lives_strconcat("internal", rfx->delim, NULL);
1187     for (list = hints; list; list = list->next) {
1188       char *line = (char *)list->data;
1189       if (!lives_strncmp(line, lstring, 7)) {
1190         layout = lives_list_append(layout, lives_strdup(line + 7));
1191       } else if (!lives_strncmp(line, istring, 9)) {
1192         layout = lives_list_append(layout, lives_strdup(line + 9));
1193       } else if (!lives_strncmp(line, sstring, 8)) {
1194         add_to_special(line + 8, rfx); // add any special actions to the framedraw preview
1195       }
1196     }
1197     lives_list_free_all(&hints);
1198     lives_free(lstring);
1199     lives_free(sstring);
1200     lives_free(istring);
1201   }
1202 
1203   lives_memset(fmt_strings, 0, MAX_FMT_STRINGS * FMT_STRING_SIZE);
1204 
1205   for (pass = 0; pass < 2; pass++) {
1206     // in this mode we do 2 passes: first check if the row is similar to the following row
1207     // (ignoring any rows with just labels or hseparators)
1208     // if so we mark it as 'layoutable'
1209 
1210     // to compare: make a string with the following vals: paramtype, or label (-2), or fill (-1)
1211     // following this we compare the strings
1212 
1213     // if the string has the same value as its successor we will create or extend the layout
1214     if (chk_params) pass = 1;
1215     //g_print("in pass %d\n", pass);
1216 
1217     list = layout;
1218     // use layout hints to build as much as we can
1219     for (i = 0; list; i++) {
1220       line = (char *)list->data;
1221       list = list->next;
1222       layout_mode = FALSE;
1223       has_box = FALSE;
1224       last_label = NULL;
1225       noslid = FALSE;
1226       if (i < MAX_FMT_STRINGS - 1) {
1227         format = fmt_strings[i];
1228         if (pass == 1 && !chk_params && (i > 0 || list)) {
1229           if (fmt_match((char *)fmt_strings[list == NULL ? i  - 1 : i])) {
1230             layout_mode = TRUE;
1231             if (!layoutx) {
1232               widget_opts.packing_height *= 2;
1233               layoutx = lives_layout_new(LIVES_BOX(param_vbox));
1234               lives_widget_set_halign(layoutx, LIVES_ALIGN_CENTER);
1235               widget_opts.packing_height = woph;
1236             }
1237             //g_print("LAYOUT MODE\n");
1238           }
1239         }
1240       } else if (pass == 0) break;
1241 
1242       num_tok = get_token_count(line, (unsigned int)rfx->delim[0]);
1243       // ignore | inside strings
1244       array = lives_strsplit(line, rfx->delim, num_tok);
1245       if (!*(array[num_tok - 1])) num_tok--;
1246 
1247       for (j = 0; j < num_tok; j++) {
1248         if (!strcmp(array[j], "nextfilter")) {
1249           // handling for compound fx - add an offset to the param number
1250           poffset += num_in_params_for_nth_instance((weed_plant_t *)rfx->source, inum);
1251           inum++;
1252           continue;
1253         }
1254 
1255         if (!strcmp(array[j], "hseparator")) {
1256           // hseparator ///////////////
1257           if (pass == 1 && !chk_params) {
1258             // add a separator
1259             if (layoutx) lives_layout_add_separator(LIVES_LAYOUT(layoutx), TRUE);
1260             else add_hsep_to_box(LIVES_BOX(param_vbox));
1261           }
1262           break; // ignore anything after hseparator
1263         }
1264 
1265         if (!strncmp(array[j], "p", 1) && (pnum = atoi((char *)(array[j] + 1))) >= 0
1266             && (pnum = pnum + poffset) < rfx->num_params && !used[pnum]) {
1267           // parameter, eg. p1 ////////////////////////////
1268           param = &rfx->params[pnum];
1269           if (!chk_params && !(rfx->flags & RFX_FLAGS_NO_RESET)) {
1270             rfx->params[pnum].changed = FALSE;
1271           }
1272           if (rfx->source_type == LIVES_RFX_SOURCE_WEED) {
1273             check_hidden_gui((weed_plant_t *)rfx->source, param, pnum);
1274             if (param->hidden & HIDDEN_STRUCTURAL) continue;
1275           }
1276 
1277           has_param = TRUE;
1278 
1279           if (pass == 0) {
1280             if ((fmtlen = lives_strlen((const char *)format)) < FMT_STRING_SIZE - 1) format[fmtlen] = (unsigned char)param->type;
1281           } else {
1282             used[pnum] = TRUE;
1283             if (!has_box) {
1284               // add a new row if needed
1285               if (layoutx) lives_layout_add_row(LIVES_LAYOUT(layoutx));
1286               else {
1287                 hbox = lives_hbox_new(FALSE, 0);
1288                 lives_box_pack_start(LIVES_BOX(param_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1289               }
1290               has_box = TRUE;
1291             } else {
1292               widget_opts.filler_len >>= 2;
1293               if (layoutx) lives_layout_add_fill(LIVES_LAYOUT(layoutx), TRUE);
1294               else add_fill_to_box(LIVES_BOX(hbox));
1295               widget_opts.filler_len = wofl;
1296             }
1297 
1298             if (last_label) {
1299               lives_widget_set_halign(last_label, LIVES_ALIGN_START);
1300             }
1301             if (layoutx) hbox = lives_layout_hbox_new(LIVES_LAYOUT(layoutx));
1302             if (add_param_to_box(LIVES_BOX(hbox), rfx, pnum, (j == (num_tok - 1)) && !noslid)) noslid = TRUE;
1303           }
1304         } else if (!strncmp(array[j], "fill", 4)) {
1305           //// fill //////////////////
1306           // (can be filln)
1307 
1308           if (strlen(array[j]) == 4 || (length = atoi(array[j] + 4)) == 0) length = 1;
1309 
1310           if (pass == 1) {
1311             if (!has_box) {
1312               // add a new row if needed
1313               if (layoutx) lives_layout_add_row(LIVES_LAYOUT(layoutx));
1314               else {
1315                 hbox = lives_hbox_new(FALSE, 0);
1316                 lives_box_pack_start(LIVES_BOX(param_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1317               }
1318               if (layoutx) lives_layout_add_fill(LIVES_LAYOUT(layoutx), TRUE);
1319               else add_fill_to_box(LIVES_BOX(hbox));
1320               has_box = TRUE;
1321             } else {
1322               if (last_label) lives_widget_set_halign(last_label, LIVES_ALIGN_START);
1323               widget_opts.filler_len >>= 1;
1324               if (layoutx) {
1325                 lives_layout_add_fill(LIVES_LAYOUT(layoutx), TRUE);
1326                 lives_layout_add_fill(LIVES_LAYOUT(layoutx), TRUE);
1327               } else {
1328                 add_fill_to_box(LIVES_BOX(hbox));
1329                 add_fill_to_box(LIVES_BOX(hbox));
1330               }
1331               widget_opts.filler_len = wofl;
1332             }
1333           }
1334 
1335           for (k = 1; k < length; k++) {
1336             if (pass == 1) {
1337               widget_opts.filler_len >>= 1;
1338               if (layoutx) {
1339                 lives_layout_add_fill(LIVES_LAYOUT(layoutx), TRUE);
1340                 lives_layout_add_fill(LIVES_LAYOUT(layoutx), TRUE);
1341               } else {
1342                 add_fill_to_box(LIVES_BOX(hbox));
1343                 add_fill_to_box(LIVES_BOX(hbox));
1344               }
1345               widget_opts.filler_len = wofl;
1346             } else if ((fmtlen = lives_strlen((const char *)format)) < FMT_STRING_SIZE) format[fmtlen] = -1;
1347           }
1348         } else if (*array[j] == '"') {
1349           // add a label
1350           if (pass == 0) {
1351             if ((fmtlen = lives_strlen((const char *)format)) < FMT_STRING_SIZE) format[fmtlen] = -2;
1352             if (has_box) last_label = dummy_label;
1353             continue;
1354           }
1355 
1356           if (!has_box) {
1357             // add a new row if needed
1358             if (layoutx) lives_layout_add_row(LIVES_LAYOUT(layoutx));
1359             else {
1360               hbox = lives_hbox_new(FALSE, 0);
1361               lives_box_pack_start(LIVES_BOX(param_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1362             }
1363             has_box = TRUE;
1364           } else {
1365             widget_opts.filler_len >>= 1;
1366             if (layoutx) lives_layout_add_fill(LIVES_LAYOUT(layoutx), TRUE);
1367             else add_fill_to_box(LIVES_BOX(hbox));
1368             widget_opts.filler_len = wofl;
1369           }
1370 
1371           ll = lives_snprintf(label_text, 256, "%s", array[j] + 1);
1372           if (ll > 255) ll = 255;
1373 
1374           while (j < num_tok - 1 && label_text[ll - 1] != '"') {
1375             // handle separators within label text
1376             ll += lives_strappend(label_text, 256, rfx->delim);
1377             ll += lives_strappend(label_text, 256, array[++j]);
1378           }
1379 
1380           keepsmall = TRUE;
1381           if (!last_label && !has_param) {
1382             if (j == num_tok - 1 || strncmp(array[j + 1], sepnpnum, sepnpnumlen)) keepsmall = TRUE;
1383           }
1384 
1385           if (ll) {
1386             if (label_text[ll - 1] == '"') label_text[ll - 1] = 0;
1387 
1388             if (!keepsmall) widget_opts.justify = LIVES_JUSTIFY_CENTER;
1389             else if (last_label) {
1390               lives_widget_set_halign(last_label, LIVES_ALIGN_START);
1391               lives_widget_set_hexpand(last_label, FALSE);
1392             }
1393 
1394             if (layoutx) {
1395               last_label = lives_layout_add_label(LIVES_LAYOUT(layoutx), label_text, keepsmall);
1396             } else last_label = add_param_label_to_box(LIVES_BOX(hbox), !keepsmall, label_text);
1397             widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
1398             lives_widget_set_hexpand(last_label, TRUE);
1399           }
1400         }
1401       }
1402       if (!layout_mode) layoutx = NULL;
1403       lives_strfreev(array);
1404     }
1405 
1406     if (!chk_params) {
1407       c_fmt_strings = i;
1408       if (pass == 1) lives_list_free_all(&layout);
1409     }
1410 
1411     // add any unused parameters
1412     for (i = 0; i < rfx->num_params; i++) {
1413       if (!chk_params && !(rfx->flags & RFX_FLAGS_NO_RESET)) {
1414         rfx->params[i].changed = FALSE;
1415         if (used[i]) continue;
1416       }
1417 
1418       layout_mode = FALSE;
1419       format = NULL;
1420 
1421       if (c_fmt_strings + i < MAX_FMT_STRINGS - 1) {
1422         format = fmt_strings[c_fmt_strings + i];
1423         if (pass == 1 && !chk_params) {
1424           if (fmt_match((char *)fmt_strings[i + c_fmt_strings])) {
1425             layout_mode = TRUE;
1426             if (!layoutx) {
1427               widget_opts.packing_height *= 2;
1428               layoutx = lives_layout_new(LIVES_BOX(param_vbox));
1429               lives_widget_set_halign(layoutx, LIVES_ALIGN_CENTER);
1430               widget_opts.packing_height = woph;
1431             }
1432             //g_print("LAYOUT MODE\n");
1433           }
1434         }
1435       } else if (pass == 0) break;
1436 
1437       if (rfx->source_type == LIVES_RFX_SOURCE_WEED) {
1438         check_hidden_gui((weed_plant_t *)rfx->source, &rfx->params[i], i);
1439         if (rfx->params[i].hidden & HIDDEN_STRUCTURAL) continue;
1440       }
1441 
1442       if (chk_params) return TRUE;
1443 
1444       has_param = TRUE;
1445       if (pass == 0) {
1446         if ((fmtlen = lives_strlen((const char *)format)) < FMT_STRING_SIZE) format[fmtlen] =
1447             (unsigned char)(rfx->params[i].type);
1448       } else {
1449         if (layoutx) {
1450           add_param_to_box(LIVES_BOX(lives_layout_row_new(LIVES_LAYOUT(layoutx))), rfx, i, TRUE);
1451         } else add_param_to_box(LIVES_BOX(param_vbox), rfx, i, TRUE);
1452       }
1453       if (!layout_mode) layoutx = NULL;
1454     }
1455   }
1456 
1457   if (needs_sizes) has_param = add_sizes(chk_params ? NULL : LIVES_BOX(top_vbox), TRUE, has_param, rfx);
1458   if (chk_params) return has_param;
1459 
1460   if (!has_param) {
1461     widget_opts.justify = LIVES_JUSTIFY_CENTER;
1462     LiVESWidget *label = lives_standard_label_new(_("No parameters"));
1463     hbox = lives_hbox_new(FALSE, 0);
1464     lives_box_pack_start(LIVES_BOX(param_vbox), hbox, TRUE, FALSE, 0);
1465     lives_box_pack_start(LIVES_BOX(hbox), label, TRUE, FALSE, 0);
1466     widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
1467   }
1468 
1469   if (!mainw->multitrack || rfx->status != RFX_STATUS_WEED) {
1470     float box_scale = 1.;
1471     // for resize effects we add the framedraw to get its widgets, but hide it, so the box should get extra width and less height
1472     if (rfx->props & RFX_PROPS_MAY_RESIZE) box_scale = 1.5 * widget_opts.scale;
1473     scrolledwindow = lives_standard_scrolled_window_new(RFX_WINSIZE_H * box_scale, RFX_WINSIZE_V >> 1, top_hbox);
1474   } else scrolledwindow = lives_standard_scrolled_window_new(-1, -1, top_hbox);
1475 
1476   lives_box_pack_start(LIVES_BOX(top_vbox), scrolledwindow, TRUE, TRUE, 0);
1477   lives_widget_destroy(dummy_label);
1478   if (has_param)
1479     update_widget_vis(rfx, -1, -1);
1480   return has_param;
1481 }
1482 
1483 
add_param_to_box(LiVESBox * box,lives_rfx_t * rfx,int pnum,boolean add_slider)1484 boolean add_param_to_box(LiVESBox *box, lives_rfx_t *rfx, int pnum, boolean add_slider) {
1485   // box here is vbox inside top_hbox inside top_dialog
1486 
1487   // add paramter pnum for rfx to box
1488 
1489   LiVESWidget *label;
1490   LiVESWidget *checkbutton;
1491   LiVESWidget *radiobutton;
1492   LiVESWidget *spinbutton;
1493   LiVESWidget *scale = NULL;
1494   LiVESWidget *spinbutton_red;
1495   LiVESWidget *spinbutton_green;
1496   LiVESWidget *spinbutton_blue;
1497   LiVESWidget *cbutton;
1498   LiVESWidget *entry = NULL;
1499   LiVESWidget *hbox;
1500   LiVESWidget *combo;
1501   //LiVESWidget *dlabel = NULL;
1502   LiVESWidget *textview = NULL;
1503   LiVESWidget *scrolledwindow;
1504   LiVESWidget *layout = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(box),
1505                         WH_LAYOUT_KEY);
1506 
1507   LiVESAdjustment *spinbutton_adj;
1508 
1509   LiVESTextBuffer *textbuffer = NULL;
1510 
1511   lives_param_t *param;
1512   lives_widget_group_t *group;
1513   LiVESSList *rbgroup;
1514 
1515   lives_colRGB48_t rgb;
1516   lives_colRGBA64_t rgba;
1517 
1518   char *name;
1519   char *txt;//, *tmp;
1520   //char *disp_string;
1521 
1522   int wcount = 0;
1523 
1524   boolean use_mnemonic;
1525   boolean was_num = FALSE;
1526 
1527   boolean add_scalers = TRUE;
1528 
1529   if (pnum >= rfx->num_params) {
1530     add_param_label_to_box(box, FALSE, (_("Invalid parameter")));
1531     return FALSE;
1532   }
1533 
1534   param = &rfx->params[pnum];
1535 
1536   name = lives_strdup_printf("%s", param->label);
1537   use_mnemonic = param->use_mnemonic;
1538 
1539   // reinit can cause the window to be redrawn, which invalidates the slider adjustment...and bang !
1540   // so dont add sliders for such params
1541   if (param->reinit) add_scalers = FALSE;
1542 
1543   // for plugins (encoders and video playback) sliders look silly
1544   if (rfx->flags & RFX_FLAGS_NO_SLIDERS) add_scalers = FALSE;
1545 
1546   if (LIVES_IS_HBOX(LIVES_WIDGET(box))) {
1547     hbox = LIVES_WIDGET(box);
1548   } else {
1549     hbox = lives_hbox_new(FALSE, 0);
1550     lives_box_pack_start(LIVES_BOX(box), hbox, FALSE, FALSE, widget_opts.packing_height);
1551   }
1552 
1553   // see if there were any 'special' hints
1554   if (!layout)
1555     check_for_special_type(rfx, param, LIVES_BOX(lives_widget_get_parent(LIVES_WIDGET(box))));
1556   else
1557     check_for_special_type(rfx, param, LIVES_BOX(lives_widget_get_parent(layout)));
1558 
1559   switch (param->type) {
1560   case LIVES_PARAM_BOOL:
1561     if (!param->group) {
1562       widget_opts.mnemonic_label = use_mnemonic;
1563       checkbutton = lives_standard_check_button_new(name, get_bool_param(param->value), (LiVESBox *)hbox, param->desc);
1564       lives_signal_sync_connect_after(LIVES_GUI_OBJECT(checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
1565                                       LIVES_GUI_CALLBACK(after_boolean_param_toggled),
1566                                       (livespointer)rfx);
1567       widget_opts.mnemonic_label = TRUE;
1568 
1569       // store parameter so we know whose trigger to use
1570       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(checkbutton), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1571       param->widgets[0] = checkbutton;
1572     } else {
1573       group = get_group(rfx, param);
1574 
1575       if (group) rbgroup = group->rbgroup;
1576       else rbgroup = NULL;
1577 
1578       widget_opts.mnemonic_label = use_mnemonic;
1579       radiobutton = lives_standard_radio_button_new(name, &rbgroup, LIVES_BOX(hbox), param->desc);
1580       widget_opts.mnemonic_label = TRUE;
1581 
1582       if (group == NULL) {
1583         if (rfx->status == RFX_STATUS_WEED) {
1584           usrgrp_to_livesgrp[1] = add_usrgrp_to_livesgrp(usrgrp_to_livesgrp[1],
1585                                   rbgroup, param->group);
1586         } else {
1587           usrgrp_to_livesgrp[0] = add_usrgrp_to_livesgrp(usrgrp_to_livesgrp[0],
1588                                   rbgroup, param->group);
1589         }
1590       }
1591 
1592       group = get_group(rfx, param);
1593 
1594       if (group) {
1595         group->rbgroup = rbgroup;
1596         if (get_bool_param(param->value)) {
1597           group->active_param = pnum + 1;
1598         }
1599       } else LIVES_WARN("Button group was NULL");
1600 
1601       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(radiobutton), get_bool_param(param->value));
1602 
1603       lives_signal_sync_connect_after(LIVES_GUI_OBJECT(radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
1604                                       LIVES_GUI_CALLBACK(after_boolean_param_toggled), (livespointer)rfx);
1605 
1606       // store parameter so we know whose trigger to use
1607       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(radiobutton), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1608       param->widgets[0] = radiobutton;
1609     }
1610     param->widgets[1] = widget_opts.last_label;
1611     break;
1612 
1613   case LIVES_PARAM_NUM:
1614     was_num = TRUE;
1615 
1616     widget_opts.mnemonic_label = use_mnemonic;
1617     if (param->dp) {
1618       spinbutton = lives_standard_spin_button_new(name, get_double_param(param->value), param->min,
1619                    param->max, param->step_size, param->step_size, param->dp,
1620                    (LiVESBox *)hbox, param->desc);
1621     } else {
1622       spinbutton = lives_standard_spin_button_new(name, (double)get_int_param(param->value), param->min,
1623                    param->max, param->step_size, param->step_size, param->dp,
1624                    (LiVESBox *)hbox, param->desc);
1625     }
1626     widget_opts.mnemonic_label = TRUE;
1627 
1628     lives_spin_button_set_wrap(LIVES_SPIN_BUTTON(spinbutton), param->wrap);
1629 
1630     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(spinbutton), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
1631                                     LIVES_GUI_CALLBACK(after_param_value_changed), (livespointer)rfx);
1632 
1633     // store parameter so we know whose trigger to use
1634     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(spinbutton), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1635     param->widgets[0] = spinbutton;
1636     param->widgets[++wcount] = widget_opts.last_label;
1637     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(param->widgets[0]), RFX_KEY, rfx);
1638 
1639     if (add_scalers) {
1640       spinbutton_adj = lives_spin_button_get_adjustment(LIVES_SPIN_BUTTON(spinbutton));
1641 #ifdef ENABLE_GIW
1642       if (prefs->lamp_buttons) {
1643         scale = giw_knob_new(LIVES_ADJUSTMENT(spinbutton_adj));
1644         giw_knob_set_wrap(GIW_KNOB(scale), param->wrap);
1645         lives_widget_set_size_request(scale, GIW_KNOB_WIDTH, GIW_KNOB_HEIGHT);
1646         giw_knob_set_legends_digits(GIW_KNOB(scale), 0);
1647         if (layout) {
1648           hbox = lives_layout_hbox_new(LIVES_LAYOUT(layout));
1649           lives_layout_pack(LIVES_HBOX(hbox), scale);
1650           lives_widget_set_show_hide_with(spinbutton, hbox);
1651         } else
1652           lives_box_pack_start(LIVES_BOX(hbox), scale, FALSE, FALSE, widget_opts.packing_width >> 1);
1653         if (param->desc) lives_widget_set_tooltip_text(scale, param->desc);
1654         lives_widget_set_fg_color(scale, LIVES_WIDGET_STATE_NORMAL, &palette->white);
1655         lives_widget_set_fg_color(scale, LIVES_WIDGET_STATE_PRELIGHT, &palette->dark_orange);
1656         lives_widget_set_bg_color(scale, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
1657         param->widgets[++wcount] = scale;
1658       }
1659 #endif
1660 
1661       if (add_slider && !param->wrap && (param->dp || param->transition)) {
1662         spinbutton_adj = lives_spin_button_get_adjustment(LIVES_SPIN_BUTTON(spinbutton));
1663         scale = lives_standard_hscale_new(LIVES_ADJUSTMENT(spinbutton_adj));
1664         lives_widget_set_size_request(scale, DEF_SLIDER_WIDTH, -1);
1665         if (layout) {
1666           hbox = lives_layout_hbox_new(LIVES_LAYOUT(layout));
1667           lives_layout_pack(LIVES_HBOX(hbox), scale);
1668           lives_widget_set_show_hide_with(spinbutton, hbox);
1669         } else {
1670           lives_box_pack_start(LIVES_BOX(hbox), scale, TRUE, TRUE, widget_opts.packing_width >> 1);
1671           if (!LIVES_IS_HBOX(LIVES_WIDGET(box))) add_fill_to_box(LIVES_BOX(hbox));
1672         }
1673         lives_widget_apply_theme(scale, LIVES_WIDGET_STATE_NORMAL);
1674         if (param->desc) lives_widget_set_tooltip_text(scale, param->desc);
1675         param->widgets[++wcount] = scale;
1676       }
1677     }
1678 
1679     if (param->desc) lives_widget_set_tooltip_text(scale, param->desc);
1680     break;
1681 
1682   case LIVES_PARAM_COLRGB24:
1683     get_colRGB24_param(param->value, &rgb);
1684 
1685     rgba.red = rgb.red << 8;
1686     rgba.green = rgb.green << 8;
1687     rgba.blue = rgb.blue << 8;
1688     rgba.alpha = 65535;
1689 
1690     widget_opts.mnemonic_label = use_mnemonic;
1691     cbutton = lives_standard_color_button_new(LIVES_BOX(hbox), _(name), FALSE, &rgba, &spinbutton_red, &spinbutton_green,
1692               &spinbutton_blue, NULL);
1693     widget_opts.mnemonic_label = TRUE;
1694     lives_widget_set_size_request(cbutton, DEF_BUTTON_WIDTH / 2, -1);
1695 
1696     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(cbutton), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1697     if (param->desc) lives_widget_set_tooltip_text(cbutton, param->desc);
1698 
1699     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(spinbutton_red), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
1700                                     LIVES_GUI_CALLBACK(after_param_red_changed), (livespointer)rfx);
1701     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(spinbutton_green), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
1702                                     LIVES_GUI_CALLBACK(after_param_green_changed), (livespointer)rfx);
1703     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(spinbutton_blue), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
1704                                     LIVES_GUI_CALLBACK(after_param_blue_changed), (livespointer)rfx);
1705 
1706     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(cbutton), LIVES_WIDGET_COLOR_SET_SIGNAL,
1707                                     LIVES_GUI_CALLBACK(on_pwcolsel), (livespointer)rfx);
1708 
1709     // store parameter so we know whose trigger to use
1710     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(spinbutton_red), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1711     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(spinbutton_green), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1712     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(spinbutton_blue), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1713 
1714     param->widgets[0] = spinbutton_red;
1715     param->widgets[1] = spinbutton_green;
1716     param->widgets[2] = spinbutton_blue;
1717     //param->widgets[3]=spinbutton_alpha;
1718     param->widgets[4] = cbutton;
1719     param->widgets[5] = widget_opts.last_label;
1720     break;
1721 
1722   case LIVES_PARAM_STRING:
1723     if (param->max == 0.) txt = lives_strdup((char *)param->value);
1724     else txt = lives_strndup((char *)param->value, (int)param->max);
1725 
1726     if (((int)param->max > RFX_TEXT_MAGIC || param->max == 0.) &&
1727         param->special_type != LIVES_PARAM_SPECIAL_TYPE_FILEREAD
1728         && param->special_type != LIVES_PARAM_SPECIAL_TYPE_FONT_CHOOSER
1729         && param->special_type != LIVES_PARAM_SPECIAL_TYPE_FILEWRITE) {
1730       LiVESWidget *vbox = lives_vbox_new(FALSE, 0);
1731       int woat = widget_opts.apply_theme;
1732 
1733       widget_opts.justify = LIVES_JUSTIFY_CENTER;
1734       if (use_mnemonic) label = lives_standard_label_new_with_mnemonic_widget(_(name), NULL);
1735       else label = lives_standard_label_new(_(name));
1736       widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
1737 
1738       lives_box_pack_start(LIVES_BOX(hbox), vbox, TRUE, TRUE, widget_opts.packing_width);
1739       if (layout) lives_layout_expansion_row_new(LIVES_LAYOUT(layout), vbox);
1740 
1741       lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height >> 1);
1742 
1743       hbox = lives_hbox_new(FALSE, 0);
1744       lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height >> 1);
1745 
1746       param->widgets[0] = textview = lives_text_view_new();
1747       if (param->desc) lives_widget_set_tooltip_text(textview, param->desc);
1748       textbuffer = lives_text_view_get_buffer(LIVES_TEXT_VIEW(textview));
1749 
1750       lives_signal_sync_connect_after(LIVES_WIDGET_OBJECT(textbuffer), LIVES_WIDGET_CHANGED_SIGNAL,
1751                                       LIVES_GUI_CALLBACK(after_param_text_buffer_changed),
1752                                       (livespointer) rfx);
1753 
1754       lives_text_view_set_editable(LIVES_TEXT_VIEW(textview), TRUE);
1755       lives_text_view_set_wrap_mode(LIVES_TEXT_VIEW(textview), LIVES_WRAP_WORD);
1756       lives_text_view_set_cursor_visible(LIVES_TEXT_VIEW(textview), TRUE);
1757 
1758       lives_text_buffer_set_text(textbuffer, txt, -1);
1759 
1760       widget_opts.apply_theme = 0;
1761       widget_opts.expand = LIVES_EXPAND_EXTRA;
1762       scrolledwindow = lives_standard_scrolled_window_new(-1, RFX_TEXT_SCROLL_HEIGHT, textview);
1763       widget_opts.expand = LIVES_EXPAND_DEFAULT;
1764       widget_opts.apply_theme = woat;
1765 
1766       if (mainw->multitrack == NULL)
1767         lives_widget_apply_theme3(textview, LIVES_WIDGET_STATE_NORMAL);
1768       else
1769         lives_widget_apply_theme2(textview, LIVES_WIDGET_STATE_NORMAL, TRUE);
1770 
1771       lives_box_pack_start(LIVES_BOX(hbox), scrolledwindow, TRUE, TRUE, 0);
1772 
1773       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(textbuffer), "textview", textview);
1774     } else {
1775       if (use_mnemonic) label = lives_standard_label_new_with_mnemonic_widget(_(name), NULL);
1776       else label = lives_standard_label_new(_(name));
1777 
1778       lives_box_pack_start(LIVES_BOX(hbox), label, FALSE, FALSE, widget_opts.packing_width);
1779       param->widgets[0] = entry = lives_standard_entry_new(NULL, txt, (int)param->max,
1780                                   (int)param->max, LIVES_BOX(hbox), param->desc);
1781 
1782       if (rfx->status == RFX_STATUS_WEED && param->special_type != LIVES_PARAM_SPECIAL_TYPE_FILEREAD) {
1783         lives_signal_sync_connect_after(LIVES_WIDGET_OBJECT(entry), LIVES_WIDGET_CHANGED_SIGNAL,
1784                                         LIVES_GUI_CALLBACK(after_param_text_changed), (livespointer)rfx);
1785       }
1786     }
1787     param->widgets[1] = widget_opts.last_label;
1788 
1789     if (param->desc) lives_widget_set_tooltip_text(label, param->desc);
1790 
1791     lives_signal_sync_connect_after(LIVES_WIDGET_OBJECT(hbox), LIVES_WIDGET_SET_FOCUS_CHILD_SIGNAL,
1792                                     LIVES_GUI_CALLBACK(after_param_text_focus_changed),
1793                                     (livespointer)rfx);
1794 
1795     if (use_mnemonic) lives_label_set_mnemonic_widget(LIVES_LABEL(label), param->widgets[0]);
1796 
1797     lives_free(txt);
1798 
1799     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(hbox), TEXTWIDGET_KEY, (livespointer)param->widgets[0]);
1800     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(param->widgets[0]), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1801     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(param->widgets[0]), RFX_KEY, rfx);
1802 
1803     param->widgets[1] = label;
1804 
1805     break;
1806 
1807   case LIVES_PARAM_STRING_LIST:
1808     widget_opts.expand = LIVES_EXPAND_EXTRA;
1809     widget_opts.mnemonic_label = use_mnemonic;
1810 
1811     combo = lives_standard_combo_new(name, param->list, (LiVESBox *)hbox, param->desc);
1812     widget_opts.mnemonic_label = TRUE;
1813     widget_opts.expand = LIVES_EXPAND_DEFAULT;
1814 
1815     if (param->list) {
1816       lives_combo_set_active_string(LIVES_COMBO(combo),
1817                                     (char *)lives_list_nth_data(param->list, get_int_param(param->value)));
1818     }
1819 
1820     lives_signal_sync_connect_after(LIVES_WIDGET_OBJECT(combo), LIVES_WIDGET_CHANGED_SIGNAL,
1821                                     LIVES_GUI_CALLBACK(after_string_list_changed), (livespointer)rfx);
1822 
1823     // store parameter so we know whose trigger to use
1824     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(combo), PARAM_NUMBER_KEY, LIVES_INT_TO_POINTER(pnum));
1825     param->widgets[0] = combo;
1826     param->widgets[1] = widget_opts.last_label;
1827     break;
1828 
1829   default:
1830     break;
1831   }
1832 
1833   // see if there were any 'special' hints
1834   if (!layout) {
1835     check_for_special(rfx, param, LIVES_BOX(lives_widget_get_parent(LIVES_WIDGET(box))));
1836   } else {
1837     check_for_special(rfx, param, LIVES_BOX(lives_widget_get_parent(layout)));
1838   }
1839   lives_free(name);
1840   return was_num;
1841 }
1842 
1843 
add_param_label_to_box(LiVESBox * box,boolean do_trans,const char * text)1844 LiVESWidget *add_param_label_to_box(LiVESBox *box, boolean do_trans, const char *text) {
1845   LiVESWidget *label;
1846 
1847   lives_box_set_homogeneous(LIVES_BOX(box), FALSE);
1848 
1849   if (do_trans) {
1850     char *markup;
1851 #ifdef GUI_GTK
1852     markup = g_markup_printf_escaped("<span weight=\"bold\" style=\"italic\"> %s </span>", _(text));
1853 #endif
1854 #ifdef GUI_QT
1855     QString qs = QString("<span weight=\"bold\" style=\"italic\"> %s </span>").arg(_(text));
1856     markup = strdup((const char *)qs.toHtmlEscaped().constData());
1857 #endif
1858     label = lives_standard_label_new(NULL);
1859     lives_label_set_markup(LIVES_LABEL(label), markup);
1860     lives_free(markup);
1861   } else label = lives_standard_label_new_with_mnemonic_widget(text, NULL);
1862 
1863   if (LIVES_IS_HBOX(LIVES_WIDGET(box)))
1864     lives_box_pack_start(box, label, FALSE, FALSE, widget_opts.packing_width);
1865   else
1866     lives_box_pack_start(box, label, FALSE, FALSE, widget_opts.packing_height);
1867 
1868   return label;
1869 }
1870 
1871 
add_usrgrp_to_livesgrp(LiVESSList * u2l,LiVESSList * rbgroup,int usr_number)1872 LiVESSList *add_usrgrp_to_livesgrp(LiVESSList *u2l, LiVESSList *rbgroup, int usr_number) {
1873   lives_widget_group_t *wgroup = (lives_widget_group_t *)lives_malloc(sizeof(lives_widget_group_t));
1874   wgroup->usr_number = usr_number;
1875   wgroup->rbgroup = rbgroup;
1876   wgroup->active_param = 0;
1877   u2l = lives_slist_append(u2l, (livespointer)wgroup);
1878   return u2l;
1879 }
1880 
1881 
livesgrp_from_usrgrp(LiVESSList * u2l,int usrgrp)1882 lives_widget_group_t *livesgrp_from_usrgrp(LiVESSList *u2l, int usrgrp) {
1883   lives_widget_group_t *group;
1884   LiVESSList *list = u2l;
1885   for (; list; list = list->next) {
1886     group = (lives_widget_group_t *)list->data;
1887     if (group->usr_number == usrgrp) return group;
1888   }
1889   return NULL;
1890 }
1891 
1892 
update_widget_vis(lives_rfx_t * rfx,int key,int mode)1893 boolean update_widget_vis(lives_rfx_t *rfx, int key, int mode) {
1894   weed_plant_t *wparam = NULL, *inst;
1895   int keyw, modew;
1896   lives_param_t *param;
1897 
1898   if (mainw->multitrack == NULL) {
1899     if (fx_dialog[1]) {
1900       rfx = fx_dialog[1]->rfx;
1901       if (!rfx->is_template) {
1902         keyw = fx_dialog[1]->key;
1903         modew = fx_dialog[1]->mode;
1904       }
1905       if (!rfx->is_template && (key != keyw && mode != modew)) return FALSE;
1906     }
1907   }
1908 
1909   if ((!fx_dialog[1] && !mainw->multitrack) || !rfx || rfx->status != RFX_STATUS_WEED) return FALSE;
1910   inst = (weed_plant_t *)rfx->source;
1911   for (int i = 0; i < rfx->num_params; i++) {
1912     param = &rfx->params[i];
1913     if ((wparam = weed_inst_in_param(inst, i, FALSE, FALSE)) != NULL) {
1914       check_hidden_gui(inst, param, i);
1915       if (param->hidden & HIDDEN_STRUCTURAL) continue;
1916       for (int j = 0; j < RFX_MAX_NORM_WIDGETS; j++) {
1917         if (param->type == LIVES_PARAM_COLRGB24 && j == 3 && !param->widgets[j]) continue;
1918         if (!param->widgets[j]) break;
1919         if (param->hidden) {
1920           lives_widget_hide(param->widgets[j]);
1921           lives_widget_set_no_show_all(param->widgets[j], TRUE);
1922         } else {
1923           lives_widget_set_no_show_all(param->widgets[j], FALSE);
1924           lives_widget_show_all(param->widgets[j]);
1925 	  // *INDENT-OFF*
1926 	}}}}
1927   // *INDENT-ON*
1928 
1929   return TRUE;
1930 }
1931 
1932 
after_any_changed_1(lives_rfx_t * rfx,int param_number,int index)1933 static void after_any_changed_1(lives_rfx_t *rfx, int param_number, int index) {
1934   weed_plant_t *inst = (weed_plant_t *)rfx->source;
1935   weed_plant_t *wparam = weed_inst_in_param(inst, param_number, FALSE, FALSE), *paramtmpl;
1936   int numvals = weed_leaf_num_elements(wparam, WEED_LEAF_VALUE);
1937   int *ign, nvals;
1938   //// update pt. 1:
1939   /// fill param vals and  set "ignore" values
1940   if (index >= numvals) {
1941     paramtmpl = weed_param_get_template(wparam);
1942     fill_param_vals_to(wparam, paramtmpl, index);
1943     numvals = index + 1;
1944   }
1945 
1946   if (mainw->multitrack && is_perchannel_multi(rfx, param_number)) {
1947     if (weed_plant_has_leaf(wparam, WEED_LEAF_IGNORE)) {
1948       ign = weed_get_boolean_array_counted(wparam, WEED_LEAF_IGNORE, &nvals);
1949       if (index >= 0 && index < nvals) {
1950         ign[index] = WEED_FALSE;
1951         weed_set_boolean_array(wparam, WEED_LEAF_IGNORE, nvals, ign);
1952       }
1953       lives_freep((void **)&ign);
1954     }
1955   }
1956 }
1957 
1958 
1959 /**
1960   @brief part 2 function for updating params visually
1961 */
after_any_changed_2(lives_rfx_t * rfx,lives_param_t * param,boolean needs_update)1962 static void after_any_changed_2(lives_rfx_t *rfx, lives_param_t *param, boolean needs_update) {
1963   weed_plant_t *wparam = NULL, *gui, *inst = NULL;
1964   lives_filter_error_t retval = FILTER_SUCCESS;
1965 
1966   /// update widgets on screen (as a result of copying values or triggers)
1967   if (needs_update) update_visual_params(rfx, FALSE);
1968   needs_update = FALSE;
1969 
1970   /// only the first param in the chain can reinit
1971   if (--ireinit > 0) {
1972     param->changed = TRUE;
1973     param->change_blocked = FALSE;
1974     return;
1975   }
1976 
1977   /// plugin is allowed to show / hide params during its init_func()
1978   /// so here we record the state b4 calling it; if any changes occur we want to refresh the whole window.
1979   if (rfx->status == RFX_STATUS_WEED) {
1980     if (mainw->multitrack) {
1981       for (int i = 0; i < rfx->num_params; i++) {
1982         if ((wparam = weed_inst_in_param(inst, i, FALSE, FALSE)) != NULL) {
1983           if ((gui = weed_param_get_gui(wparam, FALSE)) != NULL) {
1984             if (retval != FILTER_INFO_REDRAWN) {
1985               if (weed_get_boolean_value(gui, "host_hidden_backup", NULL) != weed_get_boolean_value(gui, WEED_LEAF_HIDDEN, NULL))
1986                 needs_update = TRUE;
1987             }
1988             weed_leaf_delete(gui, "host_hidden_backup");
1989 	  // *INDENT-OFF*
1990           }}}}
1991     // *INDENT-ON*
1992 
1993     inst = (weed_plant_t *)rfx->source;
1994     if (rfx->needs_reinit) {
1995       if (!(rfx->needs_reinit & REINIT_FUNCTIONAL)) {
1996         weed_instance_set_flags(inst, weed_instance_get_flags(inst) | WEED_INSTANCE_UPDATE_GUI_ONLY);
1997       }
1998 
1999       retval = weed_reinit_effect(inst, FALSE);
2000 
2001       if (!(rfx->needs_reinit & REINIT_FUNCTIONAL)) {
2002         weed_instance_set_flags(inst, weed_instance_get_flags(inst) ^ WEED_INSTANCE_UPDATE_GUI_ONLY);
2003       }
2004       rfx->needs_reinit = 0;
2005     }
2006   }
2007 
2008   needs_update = FALSE;
2009   rfx->needs_reinit = 0;
2010 
2011   if (fx_dialog[1]) {
2012     // transfer param changes from rte_window to ce_thumbs window, and vice-versa
2013     lives_rfx_t *rte_rfx = fx_dialog[1]->rfx;
2014     int key = fx_dialog[1]->key;
2015     int mode = fx_dialog[1]->mode;
2016     mainw->block_param_updates = TRUE;
2017     if (rfx == rte_rfx && mainw->ce_thumbs) ce_thumbs_update_visual_params(key);
2018     else if (mode == rte_key_getmode(key + 1)) ce_thumbs_check_for_rte(rfx, rte_rfx, key);
2019     mainw->block_param_updates = FALSE;
2020   }
2021 
2022   if (!weed_param_value_irrelevant(wparam)) {
2023     param->changed = TRUE;
2024   }
2025 
2026   if (mainw->multitrack && rfx->status == RFX_STATUS_WEED) {
2027     update_widget_vis(rfx, -1, -1);
2028     activate_mt_preview(mainw->multitrack);
2029   }
2030 
2031   param->change_blocked = FALSE;
2032 }
2033 
2034 
after_boolean_param_toggled(LiVESToggleButton * togglebutton,lives_rfx_t * rfx)2035 void after_boolean_param_toggled(LiVESToggleButton * togglebutton, lives_rfx_t *rfx) {
2036   int param_number = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(togglebutton), PARAM_NUMBER_KEY));
2037   LiVESList *retvals = NULL;
2038   weed_plant_t *inst = NULL;
2039   lives_param_t *param = &rfx->params[param_number];
2040   boolean old_bool = get_bool_param(param->value), new_bool;
2041   boolean needs_update = FALSE;
2042   int copyto = -1;
2043 
2044   new_bool = lives_toggle_button_get_active(togglebutton);
2045   if (old_bool == new_bool) return;
2046 
2047   if (mainw->block_param_updates) {
2048     if (rfx->status == RFX_STATUS_WEED && param->reinit) rfx->needs_reinit |= param->reinit;
2049     return; // updates are blocked until all params are ready
2050   }
2051 
2052   ireinit++;
2053 
2054   set_bool_param(param->value, new_bool);
2055   if (mainw->framedraw_preview) reset_framedraw_preview();
2056   param->change_blocked = TRUE;
2057 
2058   if (rfx->status == RFX_STATUS_WEED) {
2059     inst = (weed_plant_t *)rfx->source;
2060     if (inst && WEED_PLANT_IS_FILTER_INSTANCE(inst)) {
2061       //char *disp_string;
2062       int index = 0, numvals;
2063       int key = -1;
2064       weed_plant_t *wparam = weed_inst_in_param(inst, param_number, FALSE, FALSE);
2065       int *valis = weed_get_boolean_array(wparam, WEED_LEAF_VALUE, NULL);
2066 
2067       if (mainw->multitrack && is_perchannel_multi(rfx, param_number)) {
2068         index = mainw->multitrack->track_index;
2069       }
2070 
2071       after_any_changed_1(rfx, param_number, index);
2072 
2073       valis[index] = new_bool;
2074       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
2075       numvals = weed_leaf_num_elements(wparam, WEED_LEAF_VALUE);
2076       if (!filter_mutex_trylock(key)) {
2077         weed_set_boolean_array(wparam, WEED_LEAF_VALUE, numvals, valis);
2078         copyto = set_copy_to(inst, param_number, rfx, TRUE);
2079         filter_mutex_unlock(key); \
2080         if (copyto != -1) needs_update = TRUE;
2081       }
2082       lives_freep((void **)&valis);
2083 
2084       if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
2085         // if we are recording, add this change to our event_list
2086         rec_param_change(inst, param_number);
2087       }
2088       if (param->reinit) rfx->needs_reinit |= param->reinit;
2089     }
2090   }
2091 
2092   if (get_bool_param(param->value) != old_bool && param->onchange) {
2093     param->change_blocked = TRUE;
2094     retvals = do_onchange(LIVES_WIDGET_OBJECT(togglebutton), rfx);
2095     lives_list_free_all(&retvals);
2096     needs_update = TRUE;
2097   }
2098   after_any_changed_2(rfx, param, needs_update);
2099 }
2100 
2101 
after_param_value_changed(LiVESSpinButton * spinbutton,lives_rfx_t * rfx)2102 void after_param_value_changed(LiVESSpinButton * spinbutton, lives_rfx_t *rfx) {
2103   int param_number = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(spinbutton), PARAM_NUMBER_KEY));
2104   LiVESList *retvals = NULL;
2105   lives_param_t *param = &rfx->params[param_number];
2106   double new_double = 0., old_double = 0.;
2107   int new_int = 0, old_int = 0;
2108   boolean needs_update = FALSE;
2109   int copyto = -1;
2110 
2111   lives_spin_button_update(LIVES_SPIN_BUTTON(spinbutton));
2112 
2113   if (param->dp > 0) {
2114     old_double = get_double_param(param->value);
2115     new_double = lives_spin_button_get_value(LIVES_SPIN_BUTTON(spinbutton));
2116     if (old_double == new_double) return;
2117   } else {
2118     old_int = get_int_param(param->value);
2119     new_int = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
2120     if (old_int == new_int) return;
2121   }
2122 
2123   if (mainw->block_param_updates) {
2124     if (rfx->status == RFX_STATUS_WEED && param->reinit) rfx->needs_reinit |= param->reinit;
2125     return; // updates are blocked until all params are ready
2126   }
2127 
2128   ireinit++;
2129 
2130   if (mainw->framedraw_preview) reset_framedraw_preview();
2131 
2132   if (rfx->status == RFX_STATUS_WEED && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING &&
2133       (prefs->rec_opts & REC_EFFECTS)) {
2134     // if we are recording, add this (pre)change to our event_list
2135     rec_param_change((weed_plant_t *)rfx->source, param_number);
2136     copyto = set_copy_to((weed_plant_t *)rfx->source, param_number, rfx, FALSE);
2137   }
2138 
2139   if (param->dp > 0) {
2140     set_double_param(param->value, new_double);
2141   } else {
2142     set_int_param(param->value, new_int);
2143   }
2144 
2145   param->change_blocked = TRUE;
2146 
2147   if (rfx->status == RFX_STATUS_WEED) {
2148     weed_plant_t *inst = (weed_plant_t *)rfx->source;
2149     if (inst && WEED_PLANT_IS_FILTER_INSTANCE(inst)) {
2150       weed_plant_t *wparam = weed_inst_in_param(inst, param_number, FALSE, FALSE);
2151       int index = 0, numvals;
2152       int key = -1;
2153       double *valds;
2154       int *valis;
2155 
2156       // update transition in/out radios
2157       if (mainw->multitrack) {
2158         weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
2159         if (enabled_in_channels(filter, FALSE) == 2 && param->transition) {
2160           if (param->dp == 0) {
2161             if (new_int == (int)param->min)
2162               lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(param->widgets[WIDGET_RB_IN]), TRUE);
2163             else if (new_int == (int)param->max)
2164               lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(param->widgets[WIDGET_RB_OUT]), TRUE);
2165             else lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(param->widgets[WIDGET_RB_DUMMY]), TRUE);
2166           } else {
2167             if (new_double == param->min)
2168               lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(param->widgets[WIDGET_RB_IN]), TRUE);
2169             else if (new_double == param->max)
2170               lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(param->widgets[WIDGET_RB_OUT]), TRUE);
2171             else lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(param->widgets[WIDGET_RB_DUMMY]), TRUE);
2172           }
2173         }
2174       }
2175 
2176       if (mainw->multitrack && is_perchannel_multi(rfx, param_number)) {
2177         index = mainw->multitrack->track_index;
2178       }
2179 
2180       after_any_changed_1(rfx, param_number, index);
2181 
2182       numvals = weed_leaf_num_elements(wparam, WEED_LEAF_VALUE);
2183       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
2184       if (weed_leaf_seed_type(wparam, WEED_LEAF_VALUE) == WEED_SEED_DOUBLE) {
2185         valds = weed_get_double_array(wparam, WEED_LEAF_VALUE, NULL);
2186         if (param->dp > 0) valds[index] = new_double;
2187         else valds[index] = (double)new_int;
2188         if (!filter_mutex_trylock(key)) {
2189           weed_set_double_array(wparam, WEED_LEAF_VALUE, numvals, valds);
2190           copyto = set_copy_to(inst, param_number, rfx, TRUE);
2191           filter_mutex_unlock(key);
2192           if (copyto != -1) needs_update = TRUE;
2193         }
2194         lives_freep((void **)&valds);
2195       } else {
2196         valis = weed_get_int_array(wparam, WEED_LEAF_VALUE, NULL);
2197         valis[index] = new_int;
2198         weed_set_int_array(wparam, WEED_LEAF_VALUE, numvals, valis);
2199         copyto = set_copy_to(inst, param_number, rfx, TRUE);
2200         filter_mutex_unlock(key);
2201         if (copyto != -1) needs_update = TRUE;
2202         lives_freep((void **)&valis);
2203       }
2204     }
2205 
2206     if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
2207       // if we are recording, add this change to our event_list
2208       rec_param_change(inst, param_number);
2209     }
2210     if (param->reinit) rfx->needs_reinit |= param->reinit;
2211   }
2212 
2213   if (((param->dp > 0 && (get_double_param(param->value) != old_double)) || (param->dp == 0 &&
2214        (get_int_param(param->value) != old_int))) && param->onchange) {
2215     param->change_blocked = TRUE;
2216     retvals = do_onchange(LIVES_WIDGET_OBJECT(spinbutton), rfx);
2217     lives_list_free_all(&retvals);
2218     needs_update = TRUE;
2219   }
2220 
2221   after_any_changed_2(rfx, param, needs_update);
2222 }
2223 
2224 
update_weed_color_value(weed_plant_t * plant,int pnum,int c1,int c2,int c3,int c4,lives_rfx_t * rfx)2225 void update_weed_color_value(weed_plant_t *plant, int pnum, int c1, int c2, int c3, int c4, lives_rfx_t *rfx) {
2226   weed_plant_t *ptmpl;
2227   weed_plant_t *param = NULL;
2228 
2229   int *maxs = NULL, *mins = NULL;
2230   int cols[4] = {c1, c2, c3, c4};
2231   int cspace;
2232   int rmax, rmin, gmax, gmin, bmax, bmin;
2233 
2234   boolean is_default = WEED_PLANT_IS_FILTER_CLASS(plant);
2235   boolean is_int;
2236 
2237   double *maxds = NULL, *minds = NULL;
2238   double colds[4];
2239   double rmaxd, rmind, gmaxd, gmind, bmaxd, bmind;
2240 
2241   if (!is_default) {
2242     param = weed_inst_in_param(plant, pnum, FALSE, FALSE);
2243     ptmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, NULL);
2244   } else {
2245     // called only from rte_set_defs_ok
2246     ptmpl = weed_filter_in_paramtmpl(plant, pnum, FALSE);
2247   }
2248 
2249   if (mainw->block_param_updates) return; // updates are blocked until all params are ready
2250 
2251   is_int = (weed_leaf_seed_type(ptmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT);
2252   cspace = weed_get_int_value(ptmpl, WEED_LEAF_COLORSPACE, NULL);
2253 
2254   switch (cspace) {
2255   // TODO - other cspaces
2256   case WEED_COLORSPACE_RGB:
2257     if (is_int) {
2258       if (weed_leaf_num_elements(ptmpl, WEED_LEAF_MAX) == 3) {
2259         maxs = weed_get_int_array(ptmpl, WEED_LEAF_MAX, NULL);
2260         rmax = maxs[0];
2261         gmax = maxs[1];
2262         bmax = maxs[2];
2263         lives_free(maxs);
2264       } else rmax = gmax = bmax = weed_get_int_value(ptmpl, WEED_LEAF_MAX, NULL);
2265       if (weed_leaf_num_elements(ptmpl, WEED_LEAF_MIN) == 3) {
2266         mins = weed_get_int_array(ptmpl, WEED_LEAF_MIN, NULL);
2267         rmin = mins[0];
2268         gmin = mins[1];
2269         bmin = mins[2];
2270         lives_free(mins);
2271       } else rmin = gmin = bmin = weed_get_int_value(ptmpl, WEED_LEAF_MIN, NULL);
2272 
2273       cols[0] = rmin + (int)((double)cols[0] / 255.*(double)(rmax - rmin));
2274       cols[1] = gmin + (int)((double)cols[1] / 255.*(double)(gmax - gmin));
2275       cols[2] = bmin + (int)((double)cols[2] / 255.*(double)(bmax - bmin));
2276       if (is_default) {
2277         weed_set_int_array(ptmpl, WEED_LEAF_HOST_DEFAULT, 3, cols);
2278       } else {
2279         int index = 0, numvals;
2280         int *valis;
2281 
2282         if (mainw->multitrack && is_perchannel_multiw(ptmpl)) {
2283           index = mainw->multitrack->track_index;
2284         }
2285         numvals = weed_leaf_num_elements(param, WEED_LEAF_VALUE);
2286         if (index * 3 >= numvals) {
2287           weed_plant_t *paramtmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, NULL);
2288           fill_param_vals_to(param, paramtmpl, index);
2289           numvals = (index + 1) * 3;
2290         }
2291 
2292         if (mainw->multitrack && is_perchannel_multi(rfx, pnum)) {
2293           if (weed_plant_has_leaf(param, WEED_LEAF_IGNORE)) {
2294             int nvals = weed_leaf_num_elements(param, WEED_LEAF_IGNORE);
2295             if (index >= 0 && index < nvals) {
2296               int *ign = weed_get_boolean_array(param, WEED_LEAF_IGNORE, NULL);
2297               ign[index] = WEED_FALSE;
2298               weed_set_boolean_array(param, WEED_LEAF_IGNORE, nvals, ign);
2299               lives_free(ign);
2300             }
2301           }
2302         }
2303 
2304         valis = weed_get_int_array(param, WEED_LEAF_VALUE, NULL);
2305         valis[index * 3] = cols[0];
2306         valis[index * 3 + 1] = cols[1];
2307         valis[index * 3 + 2] = cols[2];
2308         weed_set_int_array(param, WEED_LEAF_VALUE, numvals, valis);
2309         lives_free(valis);
2310       }
2311       break;
2312     } else {
2313       // double
2314       if (weed_leaf_num_elements(ptmpl, WEED_LEAF_MAX) == 3) {
2315         maxds = weed_get_double_array(ptmpl, WEED_LEAF_MAX, NULL);
2316         rmaxd = maxds[0];
2317         gmaxd = maxds[1];
2318         bmaxd = maxds[2];
2319         lives_free(maxds);
2320       } else rmaxd = gmaxd = bmaxd = weed_get_double_value(ptmpl, WEED_LEAF_MAX, NULL);
2321       if (weed_leaf_num_elements(ptmpl, WEED_LEAF_MIN) == 3) {
2322         minds = weed_get_double_array(ptmpl, WEED_LEAF_MIN, NULL);
2323         rmind = minds[0];
2324         gmind = minds[1];
2325         bmind = minds[2];
2326         lives_free(minds);
2327       } else rmind = gmind = bmind = weed_get_double_value(ptmpl, WEED_LEAF_MIN, NULL);
2328       colds[0] = rmind + (double)cols[0] / 255.*(rmaxd - rmind);
2329       colds[1] = gmind + (double)cols[1] / 255.*(gmaxd - gmind);
2330       colds[2] = bmind + (double)cols[2] / 255.*(bmaxd - bmind);
2331       if (is_default) {
2332         weed_set_double_array(ptmpl, WEED_LEAF_HOST_DEFAULT, 3, colds);
2333       } else {
2334         int index = 0, numvals;
2335         double *valds;
2336 
2337         if (mainw->multitrack && is_perchannel_multiw(ptmpl)) {
2338           index = mainw->multitrack->track_index;
2339         }
2340         numvals = weed_leaf_num_elements(param, WEED_LEAF_VALUE);
2341         if (index * 3 >= numvals) {
2342           weed_plant_t *paramtmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, NULL);
2343           fill_param_vals_to(param, paramtmpl, index);
2344           numvals = (index + 1) * 3;
2345         }
2346 
2347         if (mainw->multitrack && is_perchannel_multi(rfx, pnum)) {
2348           if (weed_plant_has_leaf(param, WEED_LEAF_IGNORE)) {
2349             int nvals = weed_leaf_num_elements(param, WEED_LEAF_IGNORE);
2350             if (index >= 0 && index < nvals) {
2351               int *ign = weed_get_boolean_array(param, WEED_LEAF_IGNORE, NULL);
2352               ign[index] = WEED_FALSE;
2353               weed_set_boolean_array(param, WEED_LEAF_IGNORE, nvals, ign);
2354               lives_free(ign);
2355             }
2356           }
2357         }
2358 
2359         valds = weed_get_double_array(param, WEED_LEAF_VALUE, NULL);
2360         valds[index * 3] = colds[0];
2361         valds[index * 3 + 1] = colds[1];
2362         valds[index * 3 + 2] = colds[2];
2363         weed_set_double_array(param, WEED_LEAF_VALUE, numvals, valds);
2364         lives_free(valds);
2365       }
2366     }
2367     break;
2368   }
2369 }
2370 
2371 
after_param_red_changed(LiVESSpinButton * spinbutton,lives_rfx_t * rfx)2372 void after_param_red_changed(LiVESSpinButton * spinbutton, lives_rfx_t *rfx) {
2373   LiVESList *retvals = NULL;
2374   lives_colRGB48_t old_value;
2375   int param_number = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(spinbutton), PARAM_NUMBER_KEY));
2376   int new_red;
2377   boolean needs_update = FALSE;
2378   int copyto = -1;
2379   lives_param_t *param = &rfx->params[param_number];
2380 
2381   get_colRGB24_param(param->value, &old_value);
2382   new_red = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
2383   if (old_value.red == new_red) return;
2384 
2385   if (mainw->block_param_updates) {
2386     if (rfx->status == RFX_STATUS_WEED && param->reinit) rfx->needs_reinit |= param->reinit;
2387     return; // updates are blocked until all params are ready
2388   }
2389 
2390   ireinit++;
2391 
2392   if (rfx->status == RFX_STATUS_WEED && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING &&
2393       (prefs->rec_opts & REC_EFFECTS)) {
2394     // if we are recording, add this change to our event_list
2395 
2396     rec_param_change((weed_plant_t *)rfx->source, param_number);
2397     copyto = set_copy_to((weed_plant_t *)rfx->source, param_number, rfx, FALSE);
2398   }
2399 
2400   set_colRGB24_param(param->value, new_red, old_value.green, old_value.blue);
2401 
2402   if (mainw->framedraw_preview) reset_framedraw_preview();
2403   param->change_blocked = TRUE;
2404 
2405   if (rfx->status == RFX_STATUS_WEED) {
2406     int key = -1;
2407     weed_plant_t *inst = (weed_plant_t *)rfx->source;
2408     if (inst && WEED_PLANT_IS_FILTER_INSTANCE(inst)) {
2409       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
2410       if (!filter_mutex_trylock(key)) {
2411         update_weed_color_value(inst, param_number, new_red, old_value.green, old_value.blue, 0, rfx);
2412         copyto = set_copy_to(inst, param_number, rfx, TRUE);
2413         filter_mutex_unlock(key);
2414         if (copyto != -1) needs_update = TRUE;
2415       }
2416 
2417       if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
2418         // if we are recording, add this change to our event_list
2419         rec_param_change(inst, param_number);
2420       }
2421       if (param->reinit) rfx->needs_reinit |= param->reinit;
2422     }
2423   }
2424 
2425   if (new_red != old_value.red && param->onchange) {
2426     param->change_blocked = TRUE;
2427     retvals = do_onchange(LIVES_WIDGET_OBJECT(spinbutton), rfx);
2428     lives_list_free_all(&retvals);
2429     needs_update = TRUE;
2430   }
2431   after_any_changed_2(rfx, param, needs_update);
2432 }
2433 
2434 
after_param_green_changed(LiVESSpinButton * spinbutton,lives_rfx_t * rfx)2435 void after_param_green_changed(LiVESSpinButton * spinbutton, lives_rfx_t *rfx) {
2436   LiVESList *retvals = NULL;
2437   lives_colRGB48_t old_value;
2438   int new_green;
2439   int copyto = -1;
2440   boolean needs_update = FALSE;
2441   int param_number = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(spinbutton), PARAM_NUMBER_KEY));
2442   lives_param_t *param = &rfx->params[param_number];
2443 
2444   get_colRGB24_param(param->value, &old_value);
2445   new_green = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
2446   if (old_value.green == new_green) return;
2447 
2448   if (mainw->block_param_updates) {
2449     if (rfx->status == RFX_STATUS_WEED && param->reinit) rfx->needs_reinit |= param->reinit;
2450     return; // updates are blocked until all params are ready
2451   }
2452 
2453   ireinit++;
2454 
2455   if (rfx->status == RFX_STATUS_WEED && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING &&
2456       (prefs->rec_opts & REC_EFFECTS)) {
2457     // if we are recording, add this change to our event_list
2458     rec_param_change((weed_plant_t *)rfx->source, param_number);
2459     copyto = set_copy_to((weed_plant_t *)rfx->source, param_number, rfx, FALSE);
2460   }
2461 
2462   set_colRGB24_param(param->value, old_value.red, new_green, old_value.blue);
2463 
2464   if (mainw->framedraw_preview) reset_framedraw_preview();
2465   param->change_blocked = TRUE;
2466 
2467   if (rfx->status == RFX_STATUS_WEED) {
2468     int key = -1;
2469     weed_plant_t *inst = (weed_plant_t *)rfx->source;
2470     if (inst && WEED_PLANT_IS_FILTER_INSTANCE(inst)) {
2471       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
2472       if (!filter_mutex_trylock(key)) {
2473         update_weed_color_value(inst, param_number, old_value.red, new_green, old_value.blue, 0, rfx);
2474         copyto = set_copy_to(inst, param_number, rfx, TRUE);
2475         filter_mutex_unlock(key);
2476         if (copyto != -1) needs_update = TRUE;
2477       }
2478 
2479       if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
2480         // if we are recording, add this change to our event_list
2481         rec_param_change(inst, param_number);
2482       }
2483       rfx->needs_reinit |= param->reinit;
2484     }
2485   }
2486 
2487   if (new_green != old_value.green && param->onchange) {
2488     param->change_blocked = TRUE;
2489     retvals = do_onchange(LIVES_WIDGET_OBJECT(spinbutton), rfx);
2490     lives_list_free_all(&retvals);
2491     needs_update = TRUE;
2492   }
2493   after_any_changed_2(rfx, param, needs_update);
2494 }
2495 
2496 
after_param_blue_changed(LiVESSpinButton * spinbutton,lives_rfx_t * rfx)2497 void after_param_blue_changed(LiVESSpinButton * spinbutton, lives_rfx_t *rfx) {
2498   LiVESList *retvals = NULL;
2499   lives_colRGB48_t old_value;
2500   int new_blue;
2501   int copyto = -1;
2502   boolean needs_update = FALSE;
2503   int param_number = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(spinbutton), PARAM_NUMBER_KEY));
2504   lives_param_t *param = &rfx->params[param_number];
2505 
2506   get_colRGB24_param(param->value, &old_value);
2507   new_blue = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
2508   if (old_value.blue == new_blue) return;
2509 
2510   if (mainw->block_param_updates) {
2511     if (rfx->status == RFX_STATUS_WEED && param->reinit) rfx->needs_reinit |= param->reinit;
2512     return; // updates are blocked until all params are ready
2513   }
2514 
2515   ireinit++;
2516 
2517   if (rfx->status == RFX_STATUS_WEED && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING &&
2518       (prefs->rec_opts & REC_EFFECTS)) {
2519     // if we are recording, add this change to our event_list
2520     rec_param_change((weed_plant_t *)rfx->source, param_number);
2521   }
2522 
2523   set_colRGB24_param(param->value, old_value.red, old_value.green, new_blue);
2524 
2525   if (mainw->framedraw_preview) reset_framedraw_preview();
2526   param->change_blocked = TRUE;
2527 
2528   if (rfx->status == RFX_STATUS_WEED) {
2529     int key = -1;
2530     weed_plant_t *inst = (weed_plant_t *)rfx->source;
2531     if (inst && WEED_PLANT_IS_FILTER_INSTANCE(inst)) {
2532       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
2533       if (!filter_mutex_trylock(key)) {
2534         update_weed_color_value(inst, param_number, old_value.red, old_value.green, new_blue, 0, rfx);
2535         copyto = set_copy_to(inst, param_number, rfx, TRUE);
2536         filter_mutex_unlock(key);
2537         if (copyto != -1) needs_update = TRUE;
2538       }
2539 
2540       if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
2541         // if we are recording, add this change to our event_list
2542         rec_param_change(inst, param_number);
2543       }
2544       rfx->needs_reinit |= param->reinit;
2545     }
2546   }
2547 
2548   if (new_blue != old_value.blue && param->onchange) {
2549     param->change_blocked = TRUE;
2550     retvals = do_onchange(LIVES_WIDGET_OBJECT(spinbutton), rfx);
2551     lives_list_free_all(&retvals);
2552     needs_update = TRUE;
2553   }
2554   after_any_changed_2(rfx, param, needs_update);
2555 }
2556 
2557 
after_param_alpha_changed(LiVESSpinButton * spinbutton,lives_rfx_t * rfx)2558 void after_param_alpha_changed(LiVESSpinButton * spinbutton, lives_rfx_t *rfx) {
2559   // not used yet
2560   int param_number = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(spinbutton), PARAM_NUMBER_KEY));
2561   LiVESList *retvals = NULL;
2562   lives_param_t *param = &rfx->params[param_number];
2563   lives_colRGBA64_t old_value;
2564   int new_alpha = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
2565   int copyto = -1;
2566   boolean needs_update = FALSE;
2567 
2568   if (mainw->block_param_updates) {
2569     if (rfx->status == RFX_STATUS_WEED && param->reinit) rfx->needs_reinit |= param->reinit;
2570     return; // updates are blocked until all params are ready
2571   }
2572 
2573   ireinit++;
2574 
2575   if (rfx->status == RFX_STATUS_WEED && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING &&
2576       (prefs->rec_opts & REC_EFFECTS)) {
2577     // if we are recording, add this change to our event_list
2578     rec_param_change((weed_plant_t *)rfx->source, param_number);
2579     copyto = set_copy_to((weed_plant_t *)rfx->source, param_number, rfx, FALSE);
2580   }
2581 
2582   get_colRGBA32_param(param->value, &old_value);
2583 
2584   if (mainw->framedraw_preview) reset_framedraw_preview();
2585 
2586   set_colRGBA32_param(param->value, old_value.red, old_value.green, old_value.blue, new_alpha);
2587   param->change_blocked = TRUE;
2588 
2589   if (rfx->status == RFX_STATUS_WEED && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING &&
2590       (prefs->rec_opts & REC_EFFECTS)) {
2591     // if we are recording, add this change to our event_list
2592     rec_param_change((weed_plant_t *)rfx->source, param_number);
2593     if (copyto != -1) rec_param_change((weed_plant_t *)rfx->source, copyto);
2594   }
2595 
2596   if (new_alpha != old_value.alpha && param->onchange) {
2597     param->change_blocked = TRUE;
2598     retvals = do_onchange(LIVES_WIDGET_OBJECT(spinbutton), rfx);
2599     lives_list_free_all(&retvals);
2600     needs_update = TRUE;
2601   }
2602   after_any_changed_2(rfx, param, needs_update);
2603 }
2604 
2605 
after_param_text_focus_changed(LiVESWidget * hbox,LiVESWidget * child,lives_rfx_t * rfx)2606 boolean after_param_text_focus_changed(LiVESWidget * hbox, LiVESWidget * child, lives_rfx_t *rfx) {
2607   // for non realtime effects
2608   // we don't usually want to run the trigger every single time the user presses a key in a text widget
2609   // so we only update when the user clicks OK or focusses out of the widget
2610 
2611   LiVESWidget *textwidget;
2612 
2613   if (rfx == NULL) return FALSE;
2614 
2615   if (mainw->multitrack) {
2616     if (child)
2617       lives_window_remove_accel_group(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), mainw->multitrack->accel_group);
2618     else
2619       lives_window_add_accel_group(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), mainw->multitrack->accel_group);
2620   }
2621 
2622   if (mainw->textwidget_focus) {
2623     textwidget = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mainw->textwidget_focus), TEXTWIDGET_KEY);
2624     after_param_text_changed(textwidget, rfx);
2625   }
2626 
2627   if (hbox) {
2628     mainw->textwidget_focus = hbox;
2629   }
2630 
2631   return FALSE;
2632 }
2633 
2634 
after_param_text_changed(LiVESWidget * textwidget,lives_rfx_t * rfx)2635 void after_param_text_changed(LiVESWidget * textwidget, lives_rfx_t *rfx) {
2636   //LiVESTextBuffer *textbuffer = NULL;
2637   weed_plant_t *inst = NULL, *wparam = NULL;
2638   LiVESList *retvals = NULL;
2639   lives_param_t *param;
2640   char *old_text;
2641   const char *new_text;
2642   int copyto = -1;
2643   boolean needs_update = FALSE;
2644   int param_number;
2645 
2646   if (rfx == NULL || rfx->params == NULL || textwidget == NULL) return;
2647 
2648 
2649   param_number = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(textwidget), PARAM_NUMBER_KEY));
2650   param = &rfx->params[param_number];
2651   old_text = (char *)param->value;
2652 
2653   if (LIVES_IS_TEXT_VIEW(textwidget)) {
2654     new_text = lives_text_view_get_text(LIVES_TEXT_VIEW(textwidget));
2655     if (!lives_strcmp(new_text, old_text)) return;
2656   } else {
2657     new_text = lives_entry_get_text(LIVES_ENTRY(textwidget));
2658     if (!lives_strcmp(new_text, old_text)) return;
2659   }
2660 
2661   if (mainw->block_param_updates) {
2662     if (rfx->status == RFX_STATUS_WEED && param->reinit) rfx->needs_reinit |= param->reinit;
2663     return; // updates are blocked until all params are ready
2664   }
2665 
2666   ireinit++;
2667 
2668   param->value = lives_strdup(new_text);
2669 
2670   if (mainw->framedraw_preview) reset_framedraw_preview();
2671   param->change_blocked = TRUE;
2672 
2673   if (rfx->status == RFX_STATUS_WEED) {
2674     inst = (weed_plant_t *)rfx->source;
2675     if (inst && WEED_PLANT_IS_FILTER_INSTANCE(inst)) {
2676       char **valss;
2677       int index = 0, numvals, key = -1;
2678       wparam = weed_inst_in_param(inst, param_number, FALSE, FALSE);
2679 
2680       if (mainw->multitrack && is_perchannel_multi(rfx, param_number)) {
2681         index = mainw->multitrack->track_index;
2682       }
2683 
2684       after_any_changed_1(rfx, param_number, index);
2685 
2686       numvals = weed_leaf_num_elements(wparam, WEED_LEAF_VALUE);
2687 
2688       valss = weed_get_string_array(wparam, WEED_LEAF_VALUE, NULL);
2689       valss[index] = lives_strdup((char *)param->value);
2690 
2691       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
2692       if (!filter_mutex_trylock(key)) {
2693         weed_set_string_array(wparam, WEED_LEAF_VALUE, numvals, valss);
2694         copyto = set_copy_to(inst, param_number, rfx, TRUE);
2695         filter_mutex_unlock(key);
2696         if (copyto != -1) needs_update = TRUE;
2697       }
2698       for (int i = 0; i < numvals; i++) lives_free(valss[i]);
2699       lives_free(valss);
2700 
2701       if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
2702         // if we are recording, add this change to our event_list
2703         rec_param_change(inst, param_number);
2704         //if (copyto != -1) rec_param_change(inst, copyto);
2705       }
2706       rfx->needs_reinit |= param->reinit;
2707     }
2708   }
2709 
2710   if (lives_strcmp(old_text, (char *)param->value) && param->onchange) {
2711     param->change_blocked = TRUE;
2712     retvals = do_onchange(LIVES_WIDGET_OBJECT(textwidget), rfx);
2713     lives_list_free_all(&retvals);
2714     needs_update = TRUE;
2715   }
2716   after_any_changed_2(rfx, param, needs_update);
2717 }
2718 
2719 
after_param_text_buffer_changed(LiVESTextBuffer * textbuffer,lives_rfx_t * rfx)2720 static void after_param_text_buffer_changed(LiVESTextBuffer * textbuffer, lives_rfx_t *rfx) {
2721   LiVESWidget *textview = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(textbuffer), "textview");
2722   after_param_text_changed(textview, rfx);
2723 }
2724 
2725 
after_string_list_changed(LiVESWidget * entry,lives_rfx_t * rfx)2726 void after_string_list_changed(LiVESWidget * entry, lives_rfx_t *rfx) {
2727   LiVESList *retvals = NULL;
2728   int param_number = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(entry), PARAM_NUMBER_KEY));
2729   LiVESCombo *combo = (LiVESCombo *)(rfx->params[param_number].widgets[0]);
2730   lives_param_t *param = &rfx->params[param_number];
2731   const char *txt = lives_combo_get_active_text(combo);
2732   int old_index = get_int_param(param->value);
2733   int new_index = lives_list_strcmp_index(param->list, txt, TRUE);
2734   boolean needs_update = FALSE;
2735   int copyto = -1;
2736 
2737   if (new_index == -1) return;
2738   if (new_index == old_index) return;
2739 
2740   if (mainw->block_param_updates) {
2741     if (rfx->status == RFX_STATUS_WEED && param->reinit) rfx->needs_reinit |= param->reinit;
2742     return; // updates are blocked until all params are ready
2743   }
2744 
2745   ireinit++;
2746 
2747   set_int_param(param->value, new_index);
2748 
2749   if (mainw->framedraw_preview) reset_framedraw_preview();
2750   param->change_blocked = TRUE;
2751   if (rfx->status == RFX_STATUS_WEED) {
2752     weed_plant_t *inst = (weed_plant_t *)rfx->source;
2753     if (inst && WEED_PLANT_IS_FILTER_INSTANCE(inst)) {
2754       //char *disp_string = get_weed_display_string(inst, param_number);
2755       weed_plant_t *wparam = weed_inst_in_param(inst, param_number, FALSE, FALSE);
2756       int index = 0, numvals;
2757       int key = -1;
2758       int *valis;
2759 
2760       if (mainw->multitrack && is_perchannel_multi(rfx, param_number)) {
2761         index = mainw->multitrack->track_index;
2762       }
2763 
2764       after_any_changed_1(rfx, param_number, index);
2765 
2766       valis = weed_get_int_array(wparam, WEED_LEAF_VALUE, NULL);
2767       valis[index] = new_index;
2768       numvals = weed_leaf_num_elements(wparam, WEED_LEAF_VALUE);
2769       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
2770       if (!filter_mutex_trylock(key)) {
2771         weed_set_int_array(wparam, WEED_LEAF_VALUE, numvals, valis);
2772         copyto = set_copy_to(inst, param_number, rfx, TRUE);
2773         filter_mutex_unlock(key);
2774         if (copyto != -1) needs_update = TRUE;
2775       }
2776       lives_free(valis);
2777 
2778       if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
2779         // if we are recording, add this change to our event_list
2780         rec_param_change(inst, param_number);
2781         //if (copyto != -1) rec_param_change(inst, copyto);
2782       }
2783       rfx->needs_reinit |= param->reinit;
2784     }
2785   }
2786 
2787   if (old_index != new_index && param->onchange) {
2788     param->change_blocked = TRUE;
2789     retvals = do_onchange(LIVES_WIDGET_OBJECT(combo), rfx);
2790     lives_list_free_all(&retvals);
2791     needs_update = TRUE;
2792   }
2793   after_any_changed_2(rfx, param, needs_update);
2794 }
2795 
2796 
param_marshall_to_argv(lives_rfx_t * rfx)2797 char **param_marshall_to_argv(lives_rfx_t *rfx) {
2798   // this function will marshall all parameters into a argv array
2799   // last array element will be NULL
2800 
2801   // the returned **argv should be lives_free()'ed after use
2802 
2803   lives_colRGB48_t rgb;
2804 
2805   char **argv = (char **)lives_malloc((rfx->num_params + 1) * (sizeof(char *)));
2806 
2807   char *tmp;
2808 
2809   register int i;
2810 
2811   for (i = 0; i < rfx->num_params; i++) {
2812     switch (rfx->params[i].type) {
2813     case LIVES_PARAM_COLRGB24:
2814       get_colRGB24_param(rfx->params[i].value, &rgb);
2815       argv[i] = lives_strdup_printf("%u", (((rgb.red << 8) + rgb.green) << 8) + rgb.blue);
2816       break;
2817 
2818     case LIVES_PARAM_STRING:
2819       // escape strings
2820       argv[i] = lives_strdup_printf("%s", (tmp = U82L((char *)rfx->params[i].value)));
2821       lives_free(tmp);
2822       break;
2823 
2824     case LIVES_PARAM_STRING_LIST:
2825       // escape strings
2826       argv[i] = lives_strdup_printf("%d", get_int_param(rfx->params[i].value));
2827       break;
2828 
2829     default:
2830       if (rfx->params[i].dp) {
2831         char *return_pattern = lives_strdup_printf("%%.%df", rfx->params[i].dp);
2832         argv[i] = lives_strdup_printf(return_pattern, get_double_param(rfx->params[i].value));
2833         lives_free(return_pattern);
2834       } else {
2835         argv[i] = lives_strdup_printf("%d", get_int_param(rfx->params[i].value));
2836       }
2837     }
2838   }
2839   argv[i] = NULL;
2840   return argv;
2841 }
2842 
2843 
param_marshall(lives_rfx_t * rfx,boolean with_min_max)2844 char *param_marshall(lives_rfx_t *rfx, boolean with_min_max) {
2845   // this function will marshall all parameters into a space separated string
2846   // in case of string parameters, these will be surrounded by " and all
2847   // quotes will be escaped \"
2848 
2849   // the returned string should be lives_free()'ed after use
2850   lives_colRGB48_t rgb;
2851 
2852   char *new_return = lives_strdup("");
2853   char *old_return = new_return;
2854   char *return_pattern;
2855   char *tmp, *mysubst, *mysubst2;
2856 
2857   for (int i = 0; i < rfx->num_params; i++) {
2858     switch (rfx->params[i].type) {
2859     case LIVES_PARAM_UNKNOWN:
2860       continue;
2861     case LIVES_PARAM_COLRGB24:
2862       get_colRGB24_param(rfx->params[i].value, &rgb);
2863       if (!with_min_max) {
2864         new_return = lives_strdup_printf("%s %u", old_return, (((rgb.red << 8) + rgb.green) << 8) + rgb.blue);
2865       } else {
2866         new_return = lives_strdup_printf("%s %d %d %d", old_return, rgb.red, rgb.green, rgb.blue);
2867       }
2868       lives_free(old_return);
2869       old_return = new_return;
2870       break;
2871 
2872     case LIVES_PARAM_STRING:
2873       // we need to doubly escape strings
2874       mysubst = subst((char *)rfx->params[i].value, "\\", "\\\\\\\\");
2875       mysubst2 = subst(mysubst, "\"", "\\\\\\\"");
2876       lives_free(mysubst);
2877       mysubst = subst(mysubst2, "`", "\\`");
2878       lives_free(mysubst2);
2879       mysubst2 = subst(mysubst, "'", "\\`");
2880       lives_free(mysubst);
2881       new_return = lives_strdup_printf("%s \"%s\"", old_return, (tmp = U82L(mysubst2)));
2882       lives_free(tmp);
2883       lives_free(mysubst2);
2884       lives_free(old_return);
2885       old_return = new_return;
2886       break;
2887 
2888     case LIVES_PARAM_STRING_LIST:
2889       new_return = lives_strdup_printf("%s %d", old_return, get_int_param(rfx->params[i].value));
2890       lives_free(old_return);
2891       old_return = new_return;
2892       break;
2893 
2894     default:
2895       if (rfx->params[i].dp) {
2896         return_pattern = lives_strdup_printf("%%s %%.%df", rfx->params[i].dp);
2897         new_return = lives_strdup_printf(return_pattern, old_return, get_double_param(rfx->params[i].value));
2898         if (with_min_max) {
2899           lives_free(old_return);
2900           old_return = new_return;
2901           new_return = lives_strdup_printf(return_pattern, old_return, rfx->params[i].min);
2902           lives_free(old_return);
2903           old_return = new_return;
2904           new_return = lives_strdup_printf(return_pattern, old_return, rfx->params[i].max);
2905         }
2906         lives_free(return_pattern);
2907       } else {
2908         new_return = lives_strdup_printf("%s %d", old_return, get_int_param(rfx->params[i].value));
2909         if (with_min_max && rfx->params[i].type != LIVES_PARAM_BOOL) {
2910           lives_free(old_return);
2911           old_return = new_return;
2912           new_return = lives_strdup_printf("%s %d", old_return, (int)rfx->params[i].min);
2913           lives_free(old_return);
2914           old_return = new_return;
2915           new_return = lives_strdup_printf("%s %d", old_return, (int)rfx->params[i].max);
2916         }
2917       }
2918       lives_free(old_return);
2919       old_return = new_return;
2920     }
2921   }
2922   if (mainw->current_file > 0 && with_min_max) {
2923     if (rfx->num_in_channels < 2) {
2924       new_return = lives_strdup_printf("%s %d %d %d %d %d", old_return, cfile->hsize, cfile->vsize, cfile->start,
2925                                        cfile->end, cfile->frames);
2926     } else {
2927       // for transitions, change the end to indicate the merge section
2928       // this is better for length calculations
2929       int cb_frames = clipboard->frames;
2930       int start = cfile->start, end = cfile->end, ttl;
2931 
2932       if (prefs->ins_resample && clipboard->fps != cfile->fps) {
2933         cb_frames = count_resampled_frames(clipboard->frames, clipboard->fps, cfile->fps);
2934       }
2935 
2936       if (merge_opts->spinbutton_loops
2937           && cfile->end - cfile->start + 1 > (cb_frames * (ttl = lives_spin_button_get_value_as_int
2938                                               (LIVES_SPIN_BUTTON(merge_opts->spinbutton_loops)))) &&
2939           !merge_opts->loop_to_fit) {
2940         end = cb_frames * ttl;
2941         if (!merge_opts->align_start) {
2942           start = cfile->end - end + 1;
2943           end = cfile->end;
2944         } else {
2945           start = cfile->start;
2946           end += start - 1;
2947         }
2948       }
2949       new_return = lives_strdup_printf("%s %d %d %d %d %d %d %d", old_return, cfile->hsize, cfile->vsize, start, end,
2950                                        cfile->frames, clipboard->hsize, clipboard->vsize);
2951     }
2952   } else {
2953     new_return = lives_strdup(old_return);
2954   }
2955   lives_free(old_return);
2956 
2957   return new_return;
2958 }
2959 
2960 
reconstruct_string(LiVESList * plist,int start,int * offs)2961 char *reconstruct_string(LiVESList * plist, int start, int *offs) {
2962   // convert each piece from locale to utf8
2963   // concat list entries to get reconstruct
2964   // replace \" with "
2965 
2966   char *word = NULL;
2967   char *ret = lives_strdup(""), *ret2;
2968   char *tmp;
2969 
2970   boolean lastword = FALSE;
2971 
2972   register int i;
2973 
2974   word = L2U8((char *)lives_list_nth_data(plist, start));
2975 
2976   if (!word || !*word || word[0] != '\"') {
2977     if (word) lives_free(word);
2978     return 0;
2979   }
2980 
2981   word++;
2982 
2983   for (i = start; i < lives_list_length(plist); i++) {
2984     size_t wl = lives_strlen(word);
2985     if (wl > 0) {
2986       if ((word[wl - 1] == '\"') && (wl == 1 || word[wl - 2] != '\\')) {
2987         lastword = TRUE;
2988         lives_memset(word + wl - 1, 0, 1);
2989       }
2990     }
2991 
2992     ret2 = lives_strconcat(ret, (tmp = subst(word, "\\\"", "\"")), " ", NULL);
2993     lives_free(tmp);
2994     if (ret2 != ret) lives_free(ret);
2995     ret = ret2;
2996 
2997     if (i == start) word--;
2998     lives_free(word);
2999 
3000     if (lastword) break;
3001 
3002     if (i < lives_list_length(plist) - 1) word = L2U8((char *)lives_list_nth_data(plist, i + 1));
3003   }
3004 
3005   set_int_param(offs, i - start + 1);
3006 
3007   // remove trailing space
3008   lives_memset(ret + lives_strlen(ret) - 1, 0, 1);
3009   return ret;
3010 }
3011 
3012 
param_demarshall(lives_rfx_t * rfx,LiVESList * plist,boolean with_min_max,boolean upd)3013 void param_demarshall(lives_rfx_t *rfx, LiVESList * plist, boolean with_min_max, boolean upd) {
3014   int i;
3015   int pnum = 0;
3016   lives_param_t *param;
3017 
3018   // here we take a LiVESList * of param values, set them in rfx, and if upd is TRUE we also update their visual appearance
3019 
3020   // param->widgets[n] are only valid if upd==TRUE
3021 
3022   if (plist == NULL) return;
3023 
3024   for (i = 0; i < rfx->num_params; i++) {
3025     param = &rfx->params[i];
3026     pnum = set_param_from_list(plist, param, pnum, with_min_max, upd);
3027   }
3028 }
3029 
3030 
argv_to_marshalled_list(lives_rfx_t * rfx,int argc,char ** argv)3031 LiVESList *argv_to_marshalled_list(lives_rfx_t *rfx, int argc, char **argv) {
3032   LiVESList *plist = NULL;
3033 
3034   char *tmp, *tmp2, *tmp3;
3035 
3036   register int i;
3037 
3038   if (argc == 0) return plist;
3039 
3040   for (i = 0; i <= argc && argv[i]; i++) {
3041     if (rfx->params[i].type == LIVES_PARAM_STRING) {
3042       tmp = lives_strdup_printf("\"%s\"", (tmp2 = U82L(tmp3 = subst(argv[i], "\"", "\\\""))));
3043       plist = lives_list_append(plist, tmp);
3044       lives_free(tmp2);
3045       lives_free(tmp3);
3046     } else {
3047       plist = lives_list_append(plist, lives_strdup(argv[i]));
3048     }
3049   }
3050   return plist;
3051 }
3052 
3053 
3054 /**
3055    @brief  update values for param using values in plist
3056   if upd is TRUE, the widgets for that param also are updated;
3057   otherwise, we do not update the widgets, but we do update the default
3058 
3059   for LIVES_PARAM_NUM, setting pnum negative avoids having to send min,max
3060   - deprecated, use with_min_max = FALSE
3061   (other types dont have a min/max anyway)
3062 
3063   pnum here is not param number, but rather the offset of the element in plist
3064 */
set_param_from_list(LiVESList * plist,lives_param_t * param,int pnum,boolean with_min_max,boolean upd)3065 int set_param_from_list(LiVESList * plist, lives_param_t *param, int pnum, boolean with_min_max, boolean upd) {
3066   char *tmp;
3067   char *strval;
3068   int red, green, blue;
3069   int offs = 0;
3070   int maxlen = lives_list_length(plist) - 1;
3071 
3072   if (ABS(pnum) > maxlen) return 0;
3073 
3074   switch (param->type) {
3075   case LIVES_PARAM_BOOL:
3076     if (param->change_blocked) {
3077       pnum++;
3078       break;
3079     }
3080     tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3081     if (upd) {
3082       if (param->widgets[0] && LIVES_IS_TOGGLE_BUTTON(param->widgets[0])) {
3083         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(param->widgets[0]), atoi(tmp));
3084       }
3085     } else set_bool_param(param->def, (atoi(tmp)));
3086     if (upd && param->widgets[0] && LIVES_IS_TOGGLE_BUTTON(param->widgets[0])) {
3087       set_bool_param(param->value,
3088                      lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(param->widgets[0])));
3089     } else set_bool_param(param->value, (atoi(tmp)));
3090     lives_free(tmp);
3091     break;
3092   case LIVES_PARAM_NUM:
3093     if (param->change_blocked) {
3094       pnum++;
3095       if (with_min_max) pnum += 2;
3096       break;
3097     }
3098     if (param->dp) {
3099       double double_val;
3100       tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3101       double_val = lives_strtod(tmp, NULL);
3102       lives_free(tmp);
3103       if (with_min_max) {
3104         if (ABS(pnum) > maxlen) return 1;
3105         tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3106         param->min = lives_strtod(tmp, NULL);
3107         lives_free(tmp);
3108         if (ABS(pnum) > maxlen) return 2;
3109         tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3110         param->max = lives_strtod(tmp, NULL);
3111         lives_free(tmp);
3112         if (double_val < param->min) double_val = param->min;
3113         if (double_val > param->max) double_val = param->max;
3114       }
3115       if (upd) {
3116         if (param->widgets[0] && LIVES_IS_SPIN_BUTTON(param->widgets[0])) {
3117           lives_rfx_t *rfx = (lives_rfx_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(param->widgets[0]), RFX_KEY);
3118           lives_signal_handlers_block_by_func(param->widgets[0], (livespointer)after_param_value_changed, (livespointer)rfx);
3119           lives_spin_button_set_range(LIVES_SPIN_BUTTON(param->widgets[0]), param->min, param->max);
3120           lives_spin_button_update(LIVES_SPIN_BUTTON(param->widgets[0]));
3121           lives_signal_handlers_unblock_by_func(param->widgets[0], (livespointer)after_param_value_changed, (livespointer)rfx);
3122           lives_spin_button_set_value(LIVES_SPIN_BUTTON(param->widgets[0]), double_val);
3123           lives_spin_button_update(LIVES_SPIN_BUTTON(param->widgets[0]));
3124         }
3125       } else set_double_param(param->def, double_val);
3126       if (upd && param->widgets[0] && LIVES_IS_SPIN_BUTTON(param->widgets[0])) {
3127         set_double_param(param->value,
3128                          lives_spin_button_get_value(LIVES_SPIN_BUTTON(param->widgets[0])));
3129       } else set_double_param(param->value, double_val);
3130     } else {
3131       int int_value;
3132       int int_min, int_max;
3133       tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3134       int_value = atoi(tmp);
3135       lives_free(tmp);
3136       if (param->step_size > 1.)
3137         int_value = (int)((double)int_value / param->step_size + .5) * (int)param->step_size;
3138       int_min = (int)param->min;
3139       int_max = (int)param->max;
3140       if (int_value < int_min) int_value = int_min;
3141       if (int_value > int_max) int_value = int_max;
3142 
3143       if (with_min_max) {
3144         if (ABS(pnum) > maxlen) return 1;
3145         tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3146         int_min = atoi(tmp);
3147         lives_free(tmp);
3148         if (ABS(pnum) > maxlen) return 2;
3149         tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3150         int_max = atoi(tmp);
3151         lives_free(tmp);
3152         if (int_value < int_min) int_value = int_min;
3153         if (int_value > int_max) int_value = int_max;
3154         param->min = (double)int_min;
3155         param->max = (double)int_max;
3156       }
3157       if (upd) {
3158         if (param->widgets[0] && LIVES_IS_SPIN_BUTTON(param->widgets[0])) {
3159           lives_rfx_t *rfx = (lives_rfx_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(param->widgets[0]), RFX_KEY);
3160           lives_signal_handlers_block_by_func(param->widgets[0], (livespointer)after_param_value_changed, (livespointer)rfx);
3161           lives_spin_button_set_range(LIVES_SPIN_BUTTON(param->widgets[0]), param->min, param->max);
3162           lives_spin_button_update(LIVES_SPIN_BUTTON(param->widgets[0]));
3163           lives_spin_button_set_value(LIVES_SPIN_BUTTON(param->widgets[0]), (double)int_value);
3164           lives_spin_button_update(LIVES_SPIN_BUTTON(param->widgets[0]));
3165           lives_signal_handlers_unblock_by_func(param->widgets[0], (livespointer)after_param_value_changed, (livespointer)rfx);
3166         }
3167       } else set_int_param(param->def, int_value);
3168       if (upd && param->widgets[0] && LIVES_IS_SPIN_BUTTON(param->widgets[0])) {
3169         set_int_param(param->value,
3170                       lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(param->widgets[0])));
3171       } else set_int_param(param->value, int_value);
3172     }
3173     break;
3174   case LIVES_PARAM_COLRGB24:
3175     tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3176     red = atoi(tmp);
3177     lives_free(tmp);
3178     if (ABS(pnum) > maxlen) return 1;
3179     tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3180     green = atoi(tmp);
3181     lives_free(tmp);
3182     if (ABS(pnum) > maxlen) return 2;
3183     tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3184     blue = atoi(tmp);
3185     lives_free(tmp);
3186     if (param->change_blocked) break;
3187     if (upd) {
3188       if (param->widgets[0] && LIVES_IS_SPIN_BUTTON(param->widgets[0])) {
3189         lives_spin_button_set_value(LIVES_SPIN_BUTTON(param->widgets[0]), (double)red);
3190       }
3191       if (param->widgets[1] && LIVES_IS_SPIN_BUTTON(param->widgets[1])) {
3192         lives_spin_button_set_value(LIVES_SPIN_BUTTON(param->widgets[1]), (double)green);
3193       }
3194       if (param->widgets[2] && LIVES_IS_SPIN_BUTTON(param->widgets[2])) {
3195         lives_spin_button_set_value(LIVES_SPIN_BUTTON(param->widgets[2]), (double)blue);
3196       }
3197     } else set_colRGB24_param(param->def, red, green, blue);
3198     if (upd && param->widgets[0] && LIVES_IS_SPIN_BUTTON(param->widgets[0])
3199         && param->widgets[1] && LIVES_IS_SPIN_BUTTON(param->widgets[1])
3200         && param->widgets[2] && LIVES_IS_SPIN_BUTTON(param->widgets[2])) {
3201       set_colRGB24_param(param->value,
3202                          lives_spin_button_get_value(LIVES_SPIN_BUTTON(param->widgets[0])),
3203                          lives_spin_button_get_value(LIVES_SPIN_BUTTON(param->widgets[1])),
3204                          lives_spin_button_get_value(LIVES_SPIN_BUTTON(param->widgets[2])));
3205     } else set_colRGB24_param(param->value, red, green, blue);
3206     break;
3207   case LIVES_PARAM_STRING:
3208     strval = reconstruct_string(plist, pnum, &offs);
3209     pnum += offs;
3210     if (param->change_blocked) {
3211       lives_free(strval);
3212       break;
3213     }
3214     if (upd) {
3215       if (param->widgets[0]) {
3216         if (LIVES_IS_TEXT_VIEW(param->widgets[0])) {
3217           lives_text_view_set_text(LIVES_TEXT_VIEW(param->widgets[0]), strval, -1);
3218         } else {
3219           lives_entry_set_text(LIVES_ENTRY(param->widgets[0]), strval);
3220 
3221         }
3222       }
3223     } else {
3224       if (param->def) lives_free(param->def);
3225       param->def = (void *)lives_strdup(strval);
3226     }
3227     if (param->value) lives_free(param->value);
3228 
3229     /// read value back from widget in case some callback changed the value
3230     if (upd && param->widgets[0] && (LIVES_IS_TEXT_VIEW(param->widgets[0])
3231                                      || LIVES_IS_ENTRY(param->widgets[0]))) {
3232       lives_free(strval);
3233       if (LIVES_IS_TEXT_VIEW(param->widgets[0])) {
3234         param->value = lives_strdup(lives_text_view_get_text(LIVES_TEXT_VIEW(param->widgets[0])));
3235       } else {
3236         param->value = lives_strdup(lives_entry_get_text(LIVES_ENTRY(param->widgets[0])));
3237       }
3238     } else {
3239       param->value = strval;
3240     }
3241     break;
3242   case LIVES_PARAM_STRING_LIST: {
3243     int int_value;
3244     tmp = lives_strdup((char *)lives_list_nth_data(plist, pnum++));
3245     int_value = atoi(tmp);
3246     lives_free(tmp);
3247     if (param->change_blocked) break;
3248     if (upd && param->widgets[0] && LIVES_IS_COMBO(param->widgets[0]) && int_value < lives_list_length(param->list))
3249       lives_combo_set_active_string(LIVES_COMBO(param->widgets[0]), (char *)lives_list_nth_data(param->list, int_value));
3250     if (!upd) set_int_param(param->def, int_value);
3251     if (upd && param->widgets[0] && LIVES_IS_COMBO(param->widgets[0])) {
3252       const char *txt = lives_combo_get_active_text(LIVES_COMBO(param->widgets[0]));
3253       int new_index = lives_list_strcmp_index(param->list, txt, TRUE);
3254       set_int_param(param->value, new_index);
3255     } else set_int_param(param->value, int_value);
3256     break;
3257   }
3258   default:
3259     break;
3260   }
3261   return pnum;
3262 }
3263 
3264 
do_onchange(LiVESWidgetObject * object,lives_rfx_t * rfx)3265 LiVESList *do_onchange(LiVESWidgetObject * object, lives_rfx_t *rfx) {
3266   LiVESList *retvals;
3267 
3268   int which = LIVES_POINTER_TO_INT(lives_widget_object_get_data(object, PARAM_NUMBER_KEY));
3269   int width = 0, height = 0;
3270 
3271   const char *handle = "";
3272 
3273   char *plugdir;
3274   char *com, *tmp;
3275 
3276   // weed plugins do not have triggers
3277   if (rfx->status == RFX_STATUS_WEED) return NULL;
3278 
3279   if (which < 0) {
3280     // init
3281     switch (rfx->status) {
3282     case RFX_STATUS_BUILTIN:
3283       plugdir = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_RENDERED_EFFECTS_BUILTIN, NULL);
3284       break;
3285     case RFX_STATUS_CUSTOM:
3286       plugdir = lives_build_filename(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_CUSTOM, NULL);
3287       break;
3288     case RFX_STATUS_TEST:
3289       plugdir = lives_build_filename(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_TEST, NULL);
3290       break;
3291     default:
3292       plugdir = lives_strdup_printf("%s", prefs->workdir);
3293     }
3294 
3295     if (mainw->current_file > 0) {
3296       width = cfile->hsize;
3297       height = cfile->vsize;
3298       handle = cfile->handle;
3299     }
3300 
3301     com = lives_strdup_printf("%s \"fxinit_%s\" \"%s\" \"%s\" %d %d %s", prefs->backend_sync, rfx->name, handle, plugdir,
3302                               width, height, (tmp = param_marshall(rfx, TRUE)));
3303     retvals = plugin_request_by_space(NULL, NULL, com);
3304 
3305     lives_free(tmp);
3306     lives_free(plugdir);
3307   } else {
3308     com = lives_strdup_printf("onchange_%d%s", which, param_marshall(rfx, TRUE));
3309     switch (rfx->status) {
3310     case RFX_STATUS_BUILTIN:
3311       retvals = plugin_request_by_space(PLUGIN_RENDERED_EFFECTS_BUILTIN, rfx->name, com);
3312       break;
3313     case RFX_STATUS_CUSTOM:
3314       retvals = plugin_request_by_space(PLUGIN_RENDERED_EFFECTS_CUSTOM, rfx->name, com);
3315       break;
3316     case RFX_STATUS_TEST:
3317       retvals = plugin_request_by_space(PLUGIN_RENDERED_EFFECTS_TEST, rfx->name, com);
3318       break;
3319     default:
3320       retvals = plugin_request_by_space(PLUGIN_RFX_SCRAP, rfx->name, com);
3321     }
3322   }
3323 
3324   if (retvals) {
3325     param_demarshall(rfx, retvals, TRUE, which >= 0);
3326   } else {
3327     if (which <= 0 && mainw->error) {
3328       mainw->error = FALSE;
3329       do_error_dialog(lives_strdup_printf("\n\n%s\n\n", mainw->msg));
3330     }
3331   }
3332   lives_free(com);
3333 
3334   return retvals;
3335 }
3336 
3337 
on_pwcolsel(LiVESButton * button,lives_rfx_t * rfx)3338 void on_pwcolsel(LiVESButton * button, lives_rfx_t *rfx) {
3339   LiVESWidgetColor selected;
3340 
3341   int pnum = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(button), PARAM_NUMBER_KEY));
3342   int r, g, b;
3343 
3344   lives_param_t *param = &rfx->params[pnum];
3345 
3346   lives_color_button_get_color(LIVES_COLOR_BUTTON(button), &selected);
3347 
3348   r = (int)((double)(selected.red + LIVES_WIDGET_COLOR_SCALE_255(0.5)) / (double)LIVES_WIDGET_COLOR_SCALE_255(1.));
3349   g = (int)((double)(selected.green + LIVES_WIDGET_COLOR_SCALE_255(0.5)) / (double)LIVES_WIDGET_COLOR_SCALE_255(1.));
3350   b = (int)((double)(selected.blue + LIVES_WIDGET_COLOR_SCALE_255(0.5)) / (double)LIVES_WIDGET_COLOR_SCALE_255(1.));
3351 
3352   set_colRGB24_param(param->value, r, g, b);
3353 
3354   lives_spin_button_set_value(LIVES_SPIN_BUTTON(param->widgets[0]), (double)r);
3355   lives_spin_button_set_value(LIVES_SPIN_BUTTON(param->widgets[1]), (double)g);
3356   lives_spin_button_set_value(LIVES_SPIN_BUTTON(param->widgets[2]), (double)b);
3357   lives_color_button_set_color(LIVES_COLOR_BUTTON(param->widgets[4]), &selected);
3358 }
3359 
3360 
update_visual_params(lives_rfx_t * rfx,boolean update_hidden)3361 void update_visual_params(lives_rfx_t *rfx, boolean update_hidden) {
3362   // update parameters visually from an rfx object
3363   LiVESList *list;
3364 
3365   weed_plant_t **in_params, *in_param;
3366   weed_plant_t *inst = (weed_plant_t *)rfx->source;
3367   weed_plant_t *paramtmpl;
3368 
3369   int *colsi, *colsis, *valis;
3370   int *maxis = NULL, *minis = NULL;
3371 
3372   double *colsd, *colsds, *valds;
3373   double *maxds = NULL, *minds = NULL;
3374 
3375   double red_maxd, green_maxd, blue_maxd;
3376   double red_mind, green_mind, blue_mind;
3377   double vald, mind, maxd;
3378 
3379   char **valss;
3380 
3381   char *vals, *pattern;
3382   char *tmp, *tmp2;
3383 
3384   int cspace;
3385   int error;
3386   int num_params = 0;
3387   int param_type;
3388   int vali, mini, maxi;
3389 
3390   int red_max, green_max, blue_max;
3391   int red_min, green_min, blue_min;
3392 
3393   int index, numvals;
3394   int key = -1;
3395 
3396   register int i, j;
3397 
3398   if (rfx->source_type != LIVES_RFX_SOURCE_WEED) return;
3399 
3400   in_params = weed_instance_get_in_params(inst, &num_params);
3401   if (num_params == 0) return;
3402 
3403   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, &error);
3404 
3405   for (i = 0; i < num_params; i++) {
3406     if (!is_hidden_param(inst, i) || (!(rfx->params[i].hidden & HIDDEN_STRUCTURAL) && update_hidden)) {
3407       // by default we dont update hidden or reinit params
3408       in_param = in_params[i];
3409       paramtmpl = weed_param_get_template(in_param);
3410       param_type = weed_paramtmpl_get_type(paramtmpl);
3411       list = NULL;
3412 
3413       // assume index is 0, unless we are a framedraw multi parameter
3414       // most of the time this will be ok, as other such multivalued parameters should be hidden
3415       index = 0;
3416 
3417       if (mainw->multitrack && is_perchannel_multi(rfx, i)) {
3418         index = mainw->multitrack->track_index;
3419       }
3420 
3421       filter_mutex_lock(key);
3422 
3423       numvals = weed_leaf_num_elements(in_param, WEED_LEAF_VALUE);
3424 
3425       if (param_type != WEED_PARAM_COLOR && index >= numvals) {
3426         fill_param_vals_to(in_param, paramtmpl, index);
3427         numvals = index + 1;
3428       }
3429 
3430       switch (param_type) {
3431       case WEED_PARAM_INTEGER:
3432         valis = weed_get_int_array(in_param, WEED_LEAF_VALUE, &error);
3433         vali = valis[index];
3434         lives_free(valis);
3435 
3436         mini = weed_get_int_value(paramtmpl, WEED_LEAF_MIN, &error);
3437         maxi = weed_get_int_value(paramtmpl, WEED_LEAF_MAX, &error);
3438 
3439         list = lives_list_append(list, lives_strdup_printf("%d", vali));
3440         list = lives_list_append(list, lives_strdup_printf("%d", mini));
3441         list = lives_list_append(list, lives_strdup_printf("%d", maxi));
3442         set_param_from_list(list, &rfx->params[i], 0, TRUE, TRUE);
3443         lives_list_free_all(&list);
3444 
3445         break;
3446       case WEED_PARAM_FLOAT:
3447         valds = weed_get_double_array(in_param, WEED_LEAF_VALUE, &error);
3448         vald = valds[index];
3449         lives_free(valds);
3450 
3451         mind = weed_get_double_value(paramtmpl, WEED_LEAF_MIN, &error);
3452         maxd = weed_get_double_value(paramtmpl, WEED_LEAF_MAX, &error);
3453 
3454         pattern = lives_strdup("%.2f");
3455 
3456         if (weed_plant_has_leaf(paramtmpl, WEED_LEAF_GUI)) {
3457           weed_plant_t *gui = weed_get_plantptr_value(paramtmpl, WEED_LEAF_GUI, &error);
3458           if (weed_plant_has_leaf(gui, WEED_LEAF_DECIMALS)) {
3459             int dp = weed_get_int_value(gui, WEED_LEAF_DECIMALS, &error);
3460             lives_free(pattern);
3461             pattern = lives_strdup_printf("%%.%df", dp);
3462           }
3463         }
3464 
3465         list = lives_list_append(list, lives_strdup_printf(pattern, vald));
3466         list = lives_list_append(list, lives_strdup_printf(pattern, mind));
3467         list = lives_list_append(list, lives_strdup_printf(pattern, maxd));
3468 
3469         lives_free(pattern);
3470 
3471         set_param_from_list(list, &rfx->params[i], 0, TRUE, TRUE);
3472         lives_list_free_all(&list);
3473 
3474         break;
3475       case WEED_PARAM_SWITCH:
3476         valis = weed_get_boolean_array(in_param, WEED_LEAF_VALUE, &error);
3477         vali = valis[index];
3478         lives_free(valis);
3479 
3480         list = lives_list_append(list, lives_strdup_printf("%d", vali));
3481         set_param_from_list(list, &rfx->params[i], 0, FALSE, TRUE);
3482         lives_list_free_all(&list);
3483 
3484         break;
3485       case WEED_PARAM_TEXT:
3486         valss = weed_get_string_array(in_param, WEED_LEAF_VALUE, &error);
3487         vals = valss[index];
3488         list = lives_list_append(list, lives_strdup_printf("\"%s\"", (tmp = U82L(tmp2 = subst(vals, "\"", "\\\"")))));
3489         lives_free(tmp);
3490         lives_free(tmp2);
3491         set_param_from_list(list, &rfx->params[i], 0, FALSE, TRUE);
3492         for (j = 0; j < numvals; j++) {
3493           lives_free(valss[j]);
3494         }
3495         lives_free(valss);
3496         lives_list_free_all(&list);
3497 
3498         break;
3499       case WEED_PARAM_COLOR:
3500         cspace = weed_get_int_value(paramtmpl, WEED_LEAF_COLORSPACE, &error);
3501         switch (cspace) {
3502         case WEED_COLORSPACE_RGB:
3503           numvals = weed_leaf_num_elements(in_param, WEED_LEAF_VALUE);
3504           if (index * 3 >= numvals) fill_param_vals_to(in_param, paramtmpl, index);
3505 
3506           if (weed_leaf_seed_type(paramtmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT) {
3507             colsis = weed_get_int_array(in_param, WEED_LEAF_VALUE, &error);
3508             colsi = &colsis[3 * index];
3509 
3510             if (weed_leaf_num_elements(paramtmpl, WEED_LEAF_MAX) == 1) {
3511               red_max = green_max = blue_max = weed_get_int_value(paramtmpl, WEED_LEAF_MAX, &error);
3512             } else {
3513               maxis = weed_get_int_array(paramtmpl, WEED_LEAF_MAX, &error);
3514               red_max = maxis[0];
3515               green_max = maxis[1];
3516               blue_max = maxis[2];
3517             }
3518             if (weed_leaf_num_elements(paramtmpl, WEED_LEAF_MIN) == 1) {
3519               red_min = green_min = blue_min = weed_get_int_value(paramtmpl, WEED_LEAF_MIN, &error);
3520             } else {
3521               minis = weed_get_int_array(paramtmpl, WEED_LEAF_MIN, &error);
3522               red_min = minis[0];
3523               green_min = minis[1];
3524               blue_min = minis[2];
3525             }
3526 
3527             colsi[0] = (int)((double)(colsi[0] - red_min) / (double)(red_max - red_min) * 255. + .5);
3528             colsi[1] = (int)((double)(colsi[1] - green_min) / (double)(green_max - green_min) * 255. + .5);
3529             colsi[2] = (int)((double)(colsi[2] - blue_min) / (double)(blue_max - blue_min) * 255. + .5);
3530 
3531             if (colsi[0] < red_min) colsi[0] = red_min;
3532             if (colsi[1] < green_min) colsi[1] = green_min;
3533             if (colsi[2] < blue_min) colsi[2] = blue_min;
3534             if (colsi[0] > red_max) colsi[0] = red_max;
3535             if (colsi[1] > green_max) colsi[1] = green_max;
3536             if (colsi[2] > blue_max) colsi[2] = blue_max;
3537 
3538             list = lives_list_append(list, lives_strdup_printf("%d", colsi[0]));
3539             list = lives_list_append(list, lives_strdup_printf("%d", colsi[1]));
3540             list = lives_list_append(list, lives_strdup_printf("%d", colsi[2]));
3541 
3542             set_param_from_list(list, &rfx->params[i], 0, FALSE, TRUE);
3543 
3544             lives_list_free_all(&list);
3545             lives_free(colsis);
3546             if (maxis) lives_free(maxis);
3547             if (minis) lives_free(minis);
3548           } else {
3549             colsds = weed_get_double_array(in_param, WEED_LEAF_VALUE, &error);
3550             colsd = &colsds[3 * index];
3551             if (weed_leaf_num_elements(paramtmpl, WEED_LEAF_MAX) == 1) {
3552               red_maxd = green_maxd = blue_maxd = weed_get_double_value(paramtmpl, WEED_LEAF_MAX, &error);
3553             } else {
3554               maxds = weed_get_double_array(paramtmpl, WEED_LEAF_MAX, &error);
3555               red_maxd = maxds[0];
3556               green_maxd = maxds[1];
3557               blue_maxd = maxds[2];
3558             }
3559             if (weed_leaf_num_elements(paramtmpl, WEED_LEAF_MIN) == 1) {
3560               red_mind = green_mind = blue_mind = weed_get_double_value(paramtmpl, WEED_LEAF_MIN, &error);
3561             } else {
3562               minds = weed_get_double_array(paramtmpl, WEED_LEAF_MIN, &error);
3563               red_mind = minds[0];
3564               green_mind = minds[1];
3565               blue_mind = minds[2];
3566             }
3567             colsd[0] = (colsd[0] - red_mind) / (red_maxd - red_mind) * 255. + .5;
3568             colsd[1] = (colsd[1] - green_mind) / (green_maxd - green_mind) * 255. + .5;
3569             colsd[2] = (colsd[2] - blue_mind) / (blue_maxd - blue_mind) * 255. + .5;
3570 
3571             if (colsd[0] < red_mind) colsd[0] = red_mind;
3572             if (colsd[1] < green_mind) colsd[1] = green_mind;
3573             if (colsd[2] < blue_mind) colsd[2] = blue_mind;
3574             if (colsd[0] > red_maxd) colsd[0] = red_maxd;
3575             if (colsd[1] > green_maxd) colsd[1] = green_maxd;
3576             if (colsd[2] > blue_maxd) colsd[2] = blue_maxd;
3577 
3578             list = lives_list_append(list, lives_strdup_printf("%.2f", colsd[0]));
3579             list = lives_list_append(list, lives_strdup_printf("%.2f", colsd[1]));
3580             list = lives_list_append(list, lives_strdup_printf("%.2f", colsd[2]));
3581             set_param_from_list(list, &rfx->params[i], 0, FALSE, TRUE);
3582 
3583             lives_list_free_all(&list);
3584             lives_free(colsds);
3585             if (maxds) lives_free(maxds);
3586             if (minds) lives_free(minds);
3587           }
3588           break;
3589           // TODO - other color spaces, e.g. RGBA24
3590         }
3591         break;
3592       } // hint
3593     }
3594     filter_mutex_unlock(key);
3595   }
3596   lives_free(in_params);
3597 }
3598