1 // rfx-builder.c
2 // LiVES
3 // (c) G. Finch 2004 - 2020 <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 #include <errno.h>
8 
9 #include "main.h"
10 #include "rfx-builder.h"
11 #include "interface.h"
12 #include "paramwindow.h"
13 #include "effects.h"
14 
15 #define BG_LOAD
16 
17 static LiVESWidget *make_param_dialog(int pnum, rfx_build_window_t *rfxbuilder);
18 static LiVESWidget *make_param_window_dialog(int hnum, rfx_build_window_t *rfxbuilder);
19 static LiVESWidget *make_trigger_dialog(int tnum, rfx_build_window_t *rfxbuilder);
20 static void on_table_add_row(LiVESButton *, livespointer rfxbuilder);
21 static void on_table_edit_row(LiVESButton *, livespointer rfxbuilder);
22 static void on_table_swap_row(LiVESButton *, livespointer rfxbuilder);
23 static void on_table_delete_row(LiVESButton *, livespointer rfxbuilder);
24 
25 static void param_set_from_dialog(lives_param_t *copy_param, rfx_build_window_t *rfxbuilder);
26 
27 static void after_param_dp_changed(LiVESSpinButton *, livespointer rfxbuilder);
28 static void after_param_min_changed(LiVESSpinButton *, livespointer rfxbuilder);
29 static void after_param_max_changed(LiVESSpinButton *, livespointer rfxbuilder);
30 static void after_param_def_changed(LiVESSpinButton *, livespointer rfxbuilder);
31 static void after_rfxbuilder_type_toggled(LiVESToggleButton *, livespointer rfxbuilder);
32 static void on_param_type_changed(LiVESCombo *, livespointer rfxbuilder);
33 static void on_paramw_kw_changed(LiVESCombo *, livespointer rfxbuilder);
34 static void on_paramw_sp_changed(LiVESCombo *, livespointer rfxbuilder);
35 static void on_paramw_spsub_changed(LiVESCombo *, livespointer rfxbuilder);
36 static void populate_script_combo(LiVESCombo *script_combo, lives_rfx_status_t status);
37 static void on_script_status_changed(LiVESCombo *status_combo, livespointer script_combo);
38 
39 // builder window widgets/callbacks
40 static rfx_build_window_t *make_rfx_build_window(const char *in_script_name, lives_rfx_status_t in_status);
41 static void on_rfxbuilder_ok(LiVESButton *, livespointer);
42 static void on_rfxbuilder_cancel(LiVESButton *, livespointer);
43 static void rfxbuilder_destroy(rfx_build_window_t *);
44 static void on_list_table_clicked(LiVESButton *, livespointer rfxbuilder);
45 static void on_requirements_ok(LiVESButton *, livespointer rfxbuilder);
46 static void on_requirements_cancel(LiVESButton *, livespointer);
47 static void on_properties_clicked(LiVESButton *, livespointer rfxbuilder);
48 static void on_properties_ok(LiVESButton *, livespointer rfxbuilder);
49 static void on_params_ok(LiVESButton *, livespointer rfxbuilder);
50 static void on_params_cancel(LiVESButton *, livespointer);
51 static void on_param_window_ok(LiVESButton *, livespointer rfxbuilder);
52 static void on_param_window_cancel(LiVESButton *, livespointer);
53 static void on_code_clicked(LiVESButton *, livespointer rfxbuilder);
54 static void on_code_ok(LiVESButton *, livespointer rfxbuilder);
55 static void on_triggers_ok(LiVESButton *, livespointer rfxbuilder);
56 static void on_triggers_cancel(LiVESButton *, livespointer);
57 
58 static LiVESWidget *copy_script_okbutton;
59 
60 static void table_select_row(rfx_build_window_t *rfxbuilder, int row);
61 
on_new_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)62 void on_new_rfx_activate(LiVESMenuItem *menuitem, livespointer user_data) {
63   rfx_build_window_t *rfxbuilder;
64 
65   if (!check_builder_programs()) return;
66   rfxbuilder = make_rfx_build_window(NULL, RFX_STATUS_ANY);
67   lives_widget_show(rfxbuilder->dialog);
68 }
69 
70 
on_edit_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)71 void on_edit_rfx_activate(LiVESMenuItem *menuitem, livespointer user_data) {
72   rfx_build_window_t *rfxbuilder;
73   lives_rfx_status_t status = (lives_rfx_status_t)LIVES_POINTER_TO_INT(user_data);
74   char *script_name;
75 
76   if (!check_builder_programs()) return;
77   if (status != RFX_STATUS_TEST) return; // we only edit test effects currently
78 
79   if ((script_name = prompt_for_script_name(NULL, RFX_STATUS_TEST)) == NULL) return;
80   if (!strlen(script_name)) {
81     lives_free(script_name);
82     return;
83   }
84   if ((rfxbuilder = make_rfx_build_window(script_name, RFX_STATUS_TEST)) == NULL) {
85     lives_free(script_name);
86     return; // script not loaded
87   }
88   lives_free(script_name);
89   rfxbuilder->mode = RFX_BUILDER_MODE_EDIT;
90   rfxbuilder->oname = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->name_entry)));
91   lives_widget_show(rfxbuilder->dialog);
92 }
93 
94 
on_copy_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)95 void on_copy_rfx_activate(LiVESMenuItem *menuitem, livespointer user_data) {
96   lives_rfx_status_t status = (lives_rfx_status_t)LIVES_POINTER_TO_INT(user_data);
97 
98   if (!check_builder_programs()) return;
99   if (status != RFX_STATUS_TEST) return; // we only copy to test effects currently
100 
101   // prompt_for_script_name will create our builder window from the input
102   // script and set the name : note, we have no rfxbuilder reference
103 
104   // TODO - use RFXBUILDER_MODE-*
105   prompt_for_script_name(NULL, RFX_STATUS_COPY);
106 }
107 
108 
on_rename_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)109 void on_rename_rfx_activate(LiVESMenuItem *menuitem, livespointer user_data) {
110   lives_rfx_status_t status = (lives_rfx_status_t)LIVES_POINTER_TO_INT(user_data);
111 
112   if (status != RFX_STATUS_TEST) return; // we only copy to test effects currently
113 
114   prompt_for_script_name(NULL, RFX_STATUS_RENAME);
115 }
116 
117 
make_rfx_build_window(const char * script_name,lives_rfx_status_t status)118 rfx_build_window_t *make_rfx_build_window(const char *script_name, lives_rfx_status_t status) {
119   // TODO - set mnemonic widgets for entries
120 
121   LiVESWidget *dialog_vbox;
122   LiVESWidget *hbox;
123   LiVESWidget *layout;
124   LiVESWidget *label;
125   LiVESWidget *okbutton;
126   LiVESWidget *cancelbutton;
127   LiVESWidget *scrollw;
128   LiVESWidget *top_vbox;
129 
130   LiVESSList *radiobutton_type_group = NULL;
131   LiVESList *langc = NULL;
132 
133   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)lives_malloc(sizeof(rfx_build_window_t));
134 
135   LiVESAccelGroup *accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
136 
137   int winsize_h, winsize_v;
138 
139   char *tmp, *tmp2, *title, *string;
140 
141   rfxbuilder->rfx_version = lives_strdup(RFX_VERSION);
142 
143   rfxbuilder->type = RFX_BUILD_TYPE_EFFECT1; // the default
144 
145   rfxbuilder->num_reqs = 0;
146   rfxbuilder->num_params = 0;
147   rfxbuilder->num_paramw_hints = 0;
148 
149   rfxbuilder->num_triggers = 0;
150 
151   rfxbuilder->has_init_trigger = FALSE;
152   rfxbuilder->props = 0;
153 
154   rfxbuilder->plugin_version = 1;
155 
156   rfxbuilder->pre_code = lives_strdup("");
157   rfxbuilder->loop_code = lives_strdup("");
158   rfxbuilder->post_code = lives_strdup("");
159 
160   rfxbuilder->field_delim = lives_strdup("|");
161 
162   rfxbuilder->script_name = NULL; // use default name.script
163   rfxbuilder->oname = NULL;
164 
165   rfxbuilder->mode = RFX_BUILDER_MODE_NEW;
166 
167   rfxbuilder->table_swap_row1 = -1;
168 
169   /////////////////////////////////////////////////////////
170 
171   if (!script_name) {
172     title = (_("New Test RFX"));
173   } else {
174     title = (_("Edit Test RFX"));
175   }
176 
177   winsize_h = (PREF_RFXDIALOG_W  < GUI_SCREEN_WIDTH - SCR_WIDTH_SAFETY / 5.) ? PREF_RFXDIALOG_W : GUI_SCREEN_WIDTH -
178               SCR_WIDTH_SAFETY / 5.;
179 
180   winsize_v = (PREF_RFXDIALOG_H < GUI_SCREEN_HEIGHT - SCR_HEIGHT_SAFETY / 2.) ? PREF_RFXDIALOG_H : GUI_SCREEN_HEIGHT -
181               SCR_HEIGHT_SAFETY / 2.;
182 
183   rfxbuilder->dialog = lives_standard_dialog_new(title, FALSE, winsize_h, winsize_v);
184   lives_free(title);
185 
186   lives_window_add_accel_group(LIVES_WINDOW(rfxbuilder->dialog), accel_group);
187 
188   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(rfxbuilder->dialog));
189   lives_container_set_border_width(LIVES_CONTAINER(lives_widget_get_parent(dialog_vbox)), widget_opts.border_width >> 1);
190 
191   top_vbox = lives_vbox_new(FALSE, 0);
192 
193   scrollw = lives_standard_scrolled_window_new(winsize_h, winsize_v, top_vbox);
194 
195   lives_box_pack_start(LIVES_BOX(dialog_vbox), scrollw, TRUE, TRUE, 0);
196 
197   // types
198   hbox = lives_hbox_new(FALSE, 0);
199   lives_box_pack_start(LIVES_BOX(top_vbox), hbox, TRUE, TRUE, 0);
200 
201   label = lives_standard_label_new(_("Type:"));
202   lives_box_pack_start(LIVES_BOX(hbox), label, FALSE, FALSE, widget_opts.packing_width);
203 
204   string = lives_fx_cat_to_text(LIVES_FX_CAT_EFFECT, FALSE);
205   rfxbuilder->type_effect1_radiobutton = lives_standard_radio_button_new(string, &radiobutton_type_group, LIVES_BOX(hbox), NULL);
206   lives_free(string);
207 
208   string = lives_fx_cat_to_text(LIVES_FX_CAT_TRANSITION, FALSE);
209   rfxbuilder->type_effect2_radiobutton = lives_standard_radio_button_new(string, &radiobutton_type_group, LIVES_BOX(hbox), NULL);
210   lives_free(string);
211 
212   string = lives_fx_cat_to_text(LIVES_FX_CAT_VIDEO_GENERATOR, FALSE);
213   rfxbuilder->type_effect0_radiobutton = lives_standard_radio_button_new(string, &radiobutton_type_group, LIVES_BOX(hbox), NULL);
214   lives_free(string);
215 
216   rfxbuilder->type_tool_radiobutton = lives_standard_radio_button_new(_("tool"), &radiobutton_type_group, LIVES_BOX(hbox), NULL);
217   lives_widget_show(label);
218 
219   rfxbuilder->type_utility_radiobutton = lives_standard_radio_button_new(_("utility"), &radiobutton_type_group, LIVES_BOX(hbox),
220                                          NULL);
221 
222   layout = lives_layout_new(LIVES_BOX(top_vbox));
223   hbox = lives_layout_hbox_new(LIVES_LAYOUT(layout));
224 
225   // name
226 
227   rfxbuilder->name_entry = lives_standard_entry_new((tmp = (_("Name:          "))), NULL, -1, -1,
228                            LIVES_BOX(hbox), (tmp2 = (_("The name of the plugin. No spaces allowed."))));
229   lives_free(tmp); lives_free(tmp2);
230 
231   // version
232   hbox = lives_layout_row_new(LIVES_LAYOUT(layout));
233 
234   rfxbuilder->spinbutton_version = lives_standard_spin_button_new((tmp = (_("Version:       "))),
235                                    rfxbuilder->plugin_version, rfxbuilder->plugin_version, 1000000., 1., 1., 0,
236                                    LIVES_BOX(hbox), (tmp2 = (_("The script version."))));
237   lives_free(tmp); lives_free(tmp2);
238 
239   // author
240   hbox = lives_layout_row_new(LIVES_LAYOUT(layout));
241 
242   rfxbuilder->author_entry = lives_standard_entry_new((tmp = (_("    Author:       "))), NULL, -1, -1,
243                              LIVES_BOX(hbox), (tmp2 = (_("The script author."))));
244   lives_free(tmp); lives_free(tmp2);
245 
246   // URL
247   hbox = lives_layout_row_new(LIVES_LAYOUT(layout));
248 
249   rfxbuilder->url_entry = lives_standard_entry_new((tmp = (_("    URL (optional):       "))), NULL, -1, -1,
250                           LIVES_BOX(hbox), (tmp2 = (_("URL for the plugin maintainer."))));
251   lives_free(tmp); lives_free(tmp2);
252 
253   // menu entry
254   hbox = lives_layout_row_new(LIVES_LAYOUT(layout));
255 
256   rfxbuilder->menu_text_entry = lives_standard_entry_new((tmp = (_("Menu text:    "))), NULL, -1, -1,
257                                 LIVES_BOX(hbox), (tmp2 = (_("The text to show in the menu."))));
258   lives_free(tmp); lives_free(tmp2);
259 
260   rfxbuilder->action_desc_hsep = add_hsep_to_box(LIVES_BOX(top_vbox));
261 
262   // action description
263 
264   rfxbuilder->action_desc_hbox = lives_hbox_new(FALSE, 0);
265   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->action_desc_hbox, FALSE, FALSE, widget_opts.packing_height);
266 
267   rfxbuilder->action_desc_entry = lives_standard_entry_new((tmp = (_("Action description:        "))), NULL, -1, -1,
268                                   LIVES_BOX(rfxbuilder->action_desc_hbox),
269                                   (tmp2 = (_("Describe what the plugin is doing. E.g. \"Edge detecting\""))));
270   lives_free(tmp); lives_free(tmp2);
271 
272   rfxbuilder->spinbutton_min_frames = lives_standard_spin_button_new((tmp = (_("Minimum frames:"))),
273                                       1., 1., 1000., 1., 1., 0, LIVES_BOX(top_vbox),
274                                       (tmp2 = (_("Minimum number of frames this effect/tool can be applied to. Normally 1."))));
275   lives_free(tmp); lives_free(tmp2);
276   rfxbuilder->min_frames_label = widget_opts.last_label;
277 
278   // requirements
279 
280   add_hsep_to_box(LIVES_BOX(top_vbox));
281 
282   rfxbuilder->requirements_button = lives_standard_button_new_with_label(_("_Requirements..."),
283                                     DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
284 
285   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->requirements_button, TRUE, TRUE, 0);
286   lives_widget_set_tooltip_text(rfxbuilder->requirements_button,
287                                 (_("Enter any binaries required by the plugin.")));
288 
289   add_hsep_to_box(LIVES_BOX(top_vbox));
290 
291   rfxbuilder->properties_button = lives_standard_button_new_with_label(_("_Properties..."),
292                                   DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
293 
294   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->properties_button, TRUE, TRUE, 0);
295   lives_widget_set_tooltip_text(rfxbuilder->properties_button,
296                                 (_("Set properties for the plugin. Optional.")));
297 
298   add_hsep_to_box(LIVES_BOX(top_vbox));
299 
300   rfxbuilder->params_button = lives_standard_button_new_with_label(_("_Parameters..."),
301                               DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
302 
303   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->params_button, TRUE, TRUE, 0);
304   lives_widget_set_tooltip_text(rfxbuilder->params_button,
305                                 (_("Set up parameters used in pre/loop/post/trigger code. Optional.")));
306 
307   add_hsep_to_box(LIVES_BOX(top_vbox));
308 
309   rfxbuilder->param_window_button = lives_standard_button_new_with_label(_("Parameter _Window Hints..."),
310                                     DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
311 
312   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->param_window_button, TRUE, TRUE, 0);
313   lives_widget_set_tooltip_text(rfxbuilder->param_window_button,
314                                 (_("Set hints about how to lay out the parameter window. Optional.")));
315 
316   add_hsep_to_box(LIVES_BOX(top_vbox));
317 
318   langc = lives_list_append(langc, (livespointer)"0xF0 - LiVES-Perl");
319 
320   rfxbuilder->langc_combo = lives_standard_combo_new((tmp = (_("_Language code:"))), langc, LIVES_BOX(top_vbox),
321                             (tmp2 = (_("Language for pre/loop/post/triggers. Optional."))));
322 
323   lives_free(tmp); lives_free(tmp2);
324   lives_list_free(langc);
325 
326   add_hsep_to_box(LIVES_BOX(top_vbox));
327 
328   rfxbuilder->pre_button = lives_standard_button_new_with_label(_("_Pre loop code..."),
329                            DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
330 
331   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->pre_button, TRUE, TRUE, 0);
332   lives_widget_set_tooltip_text(rfxbuilder->pre_button,
333                                 (_("Code to be executed before the loop. Optional.")));
334 
335   add_hsep_to_box(LIVES_BOX(top_vbox));
336 
337   rfxbuilder->loop_button = lives_standard_button_new_with_label(_("_Loop code..."),
338                             DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
339 
340   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->loop_button, TRUE, TRUE, 0);
341   lives_widget_set_tooltip_text(rfxbuilder->loop_button,
342                                 (_("Loop code to be applied to each frame.")));
343 
344   add_hsep_to_box(LIVES_BOX(top_vbox));
345 
346   rfxbuilder->post_button = lives_standard_button_new_with_label(_("_Post loop code..."),
347                             DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
348 
349   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->post_button, TRUE, TRUE, 0);
350   lives_widget_set_tooltip_text(rfxbuilder->post_button,
351                                 (_("Code to be executed after the loop. Optional.")));
352 
353   add_hsep_to_box(LIVES_BOX(top_vbox));
354 
355   rfxbuilder->trigger_button = lives_standard_button_new_with_label(_("_Trigger code..."),
356                                DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
357 
358   lives_box_pack_start(LIVES_BOX(top_vbox), rfxbuilder->trigger_button, TRUE, TRUE, 0);
359   lives_widget_set_tooltip_text(rfxbuilder->trigger_button,
360                                 (_("Set trigger code for when the parameter window is shown, or when a parameter is changed. "
361                                    "Optional (except for Utilities).")));
362 
363   cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(rfxbuilder->dialog), LIVES_STOCK_CANCEL, NULL,
364                  LIVES_RESPONSE_CANCEL);
365 
366   okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(rfxbuilder->dialog), LIVES_STOCK_OK, NULL,
367              LIVES_RESPONSE_OK);
368 
369   lives_button_grab_default_special(okbutton);
370 
371   lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
372                                LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
373 
374   lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
375                             LIVES_GUI_CALLBACK(on_rfxbuilder_ok), (livespointer)rfxbuilder);
376 
377   lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
378                             LIVES_GUI_CALLBACK(on_rfxbuilder_cancel), (livespointer)rfxbuilder);
379 
380   lives_signal_sync_connect(rfxbuilder->requirements_button, LIVES_WIDGET_CLICKED_SIGNAL,
381                             LIVES_GUI_CALLBACK(on_list_table_clicked), (livespointer)rfxbuilder);
382 
383   lives_signal_sync_connect(rfxbuilder->properties_button, LIVES_WIDGET_CLICKED_SIGNAL,
384                             LIVES_GUI_CALLBACK(on_properties_clicked), (livespointer)rfxbuilder);
385 
386   lives_signal_sync_connect(rfxbuilder->params_button, LIVES_WIDGET_CLICKED_SIGNAL,
387                             LIVES_GUI_CALLBACK(on_list_table_clicked), (livespointer)rfxbuilder);
388 
389   lives_signal_sync_connect(rfxbuilder->param_window_button, LIVES_WIDGET_CLICKED_SIGNAL,
390                             LIVES_GUI_CALLBACK(on_list_table_clicked), (livespointer)rfxbuilder);
391 
392   lives_signal_sync_connect(rfxbuilder->trigger_button, LIVES_WIDGET_CLICKED_SIGNAL,
393                             LIVES_GUI_CALLBACK(on_list_table_clicked), (livespointer)rfxbuilder);
394 
395   lives_signal_sync_connect(rfxbuilder->pre_button, LIVES_WIDGET_CLICKED_SIGNAL,
396                             LIVES_GUI_CALLBACK(on_code_clicked), (livespointer)rfxbuilder);
397 
398   lives_signal_sync_connect(rfxbuilder->loop_button, LIVES_WIDGET_CLICKED_SIGNAL,
399                             LIVES_GUI_CALLBACK(on_code_clicked), (livespointer)rfxbuilder);
400 
401   lives_signal_sync_connect(rfxbuilder->post_button, LIVES_WIDGET_CLICKED_SIGNAL,
402                             LIVES_GUI_CALLBACK(on_code_clicked), (livespointer)rfxbuilder);
403 
404   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->type_effect1_radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
405                                   LIVES_GUI_CALLBACK(after_rfxbuilder_type_toggled), (livespointer)rfxbuilder);
406 
407   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->type_effect2_radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
408                                   LIVES_GUI_CALLBACK(after_rfxbuilder_type_toggled), (livespointer)rfxbuilder);
409 
410   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->type_effect0_radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
411                                   LIVES_GUI_CALLBACK(after_rfxbuilder_type_toggled), (livespointer)rfxbuilder);
412 
413   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->type_tool_radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
414                                   LIVES_GUI_CALLBACK(after_rfxbuilder_type_toggled), (livespointer)rfxbuilder);
415 
416   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->type_utility_radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
417                                   LIVES_GUI_CALLBACK(after_rfxbuilder_type_toggled), (livespointer)rfxbuilder);
418 
419   // edit mode
420   if (script_name) {
421     // we assume the name is valid
422     char *script_file;
423 
424     switch (status) {
425     case RFX_STATUS_CUSTOM:
426       script_file = lives_build_filename(prefs->config_datadir,
427                                          PLUGIN_RENDERED_EFFECTS_CUSTOM_SCRIPTS, script_name, NULL);
428       break;
429     case RFX_STATUS_BUILTIN:
430       script_file = lives_build_filename(prefs->prefix_dir, PLUGIN_SCRIPTS_DIR,
431                                          PLUGIN_RENDERED_EFFECTS_BUILTIN_SCRIPTS, script_name, NULL);
432       break;
433     default:
434       script_file = lives_build_filename(prefs->config_datadir,
435                                          PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS, script_name, NULL);
436       break;
437     }
438     if (!script_to_rfxbuilder(rfxbuilder, script_file)) {
439       char *msg = lives_strdup_printf(_("\n\nUnable to parse the script file:\n%s\n%s\n"), script_file, mainw->msg);
440       // must use blocking error dialogs as the scriptname window is modal
441       do_error_dialog(msg);
442       lives_free(msg);
443       rfxbuilder_destroy(rfxbuilder);
444       lives_free(script_file);
445       return NULL;
446     }
447     lives_free(script_file);
448     switch (rfxbuilder->type) {
449     case RFX_BUILD_TYPE_TOOL:
450       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rfxbuilder->type_tool_radiobutton), TRUE);
451       break;
452     case RFX_BUILD_TYPE_EFFECT0:
453       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rfxbuilder->type_effect0_radiobutton), TRUE);
454       break;
455     case RFX_BUILD_TYPE_UTILITY:
456       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rfxbuilder->type_utility_radiobutton), TRUE);
457       break;
458     case RFX_BUILD_TYPE_EFFECT2:
459       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rfxbuilder->type_effect2_radiobutton), TRUE);
460       break;
461     default:
462       break;
463     }
464   } else {
465     lives_widget_grab_focus(rfxbuilder->name_entry);
466   }
467 
468   lives_widget_show_all(rfxbuilder->dialog);
469 
470   return rfxbuilder;
471 }
472 
473 
after_rfxbuilder_type_toggled(LiVESToggleButton * togglebutton,livespointer user_data)474 void after_rfxbuilder_type_toggled(LiVESToggleButton *togglebutton, livespointer user_data) {
475   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
476 
477   lives_widget_set_sensitive(rfxbuilder->pre_button, TRUE);
478   lives_widget_set_sensitive(rfxbuilder->loop_button, TRUE);
479   lives_widget_set_sensitive(rfxbuilder->post_button, TRUE);
480   lives_widget_set_sensitive(rfxbuilder->requirements_button, TRUE);
481   lives_widget_set_sensitive(rfxbuilder->properties_button, TRUE);
482   lives_widget_show(rfxbuilder->spinbutton_min_frames);
483   lives_widget_show(rfxbuilder->min_frames_label);
484   lives_widget_show(rfxbuilder->action_desc_hbox);
485   lives_widget_show(rfxbuilder->action_desc_hsep);
486 
487   if (togglebutton == LIVES_TOGGLE_BUTTON(rfxbuilder->type_effect1_radiobutton)) {
488     rfxbuilder->type = RFX_BUILD_TYPE_EFFECT1;
489   } else if (togglebutton == LIVES_TOGGLE_BUTTON(rfxbuilder->type_effect2_radiobutton)) {
490     rfxbuilder->type = RFX_BUILD_TYPE_EFFECT2;
491     lives_widget_set_sensitive(rfxbuilder->properties_button, FALSE);
492   } else if (togglebutton == LIVES_TOGGLE_BUTTON(rfxbuilder->type_effect0_radiobutton)) {
493     rfxbuilder->type = RFX_BUILD_TYPE_EFFECT0;
494     lives_widget_hide(rfxbuilder->spinbutton_min_frames);
495     lives_widget_hide(rfxbuilder->min_frames_label);
496   } else if (togglebutton == LIVES_TOGGLE_BUTTON(rfxbuilder->type_tool_radiobutton)) {
497     rfxbuilder->type = RFX_BUILD_TYPE_TOOL;
498     lives_widget_hide(rfxbuilder->spinbutton_min_frames);
499     lives_widget_set_sensitive(rfxbuilder->properties_button, FALSE);
500     lives_widget_hide(rfxbuilder->min_frames_label);
501   } else if (togglebutton == LIVES_TOGGLE_BUTTON(rfxbuilder->type_utility_radiobutton)) {
502     rfxbuilder->type = RFX_BUILD_TYPE_UTILITY;
503     lives_widget_hide(rfxbuilder->spinbutton_min_frames);
504     lives_widget_set_sensitive(rfxbuilder->properties_button, FALSE);
505     lives_widget_set_sensitive(rfxbuilder->requirements_button, FALSE);
506     lives_widget_set_sensitive(rfxbuilder->pre_button, FALSE);
507     lives_widget_set_sensitive(rfxbuilder->loop_button, FALSE);
508     lives_widget_set_sensitive(rfxbuilder->post_button, FALSE);
509     lives_widget_hide(rfxbuilder->min_frames_label);
510     lives_widget_hide(rfxbuilder->action_desc_hbox);
511     lives_widget_hide(rfxbuilder->action_desc_hsep);
512   }
513 }
514 
515 
on_list_table_clicked(LiVESButton * button,livespointer user_data)516 static void on_list_table_clicked(LiVESButton *button, livespointer user_data) {
517   LiVESWidget *dialog;
518   LiVESWidget *dialog_vbox;
519   LiVESWidget *hbox;
520   LiVESWidget *button_box;
521 
522   // buttons
523   LiVESWidget *okbutton;
524   LiVESWidget *scrolledwindow;
525   LiVESWidget *cancelbutton;
526 
527   LiVESAccelGroup *accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
528 
529   char *title = NULL;
530 
531   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
532 
533   register int i;
534 
535   if (button == LIVES_BUTTON(rfxbuilder->requirements_button)) {
536     rfxbuilder->table_type = RFX_TABLE_TYPE_REQUIREMENTS;
537   } else if (button == LIVES_BUTTON(rfxbuilder->params_button)) {
538     rfxbuilder->table_type = RFX_TABLE_TYPE_PARAMS;
539   } else if (button == LIVES_BUTTON(rfxbuilder->param_window_button)) {
540     rfxbuilder->table_type = RFX_TABLE_TYPE_PARAM_WINDOW;
541   } else if (button == LIVES_BUTTON(rfxbuilder->trigger_button)) {
542     rfxbuilder->table_type = RFX_TABLE_TYPE_TRIGGERS;
543   }
544 
545   if (rfxbuilder->table_type == RFX_TABLE_TYPE_REQUIREMENTS) {
546     title = (_("RFX Requirements"));
547     rfxbuilder->onum_reqs = rfxbuilder->num_reqs;
548   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_PARAMS) {
549     title = (_("RFX Parameters"));
550     rfxbuilder->onum_params = rfxbuilder->num_params;
551   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_PARAM_WINDOW) {
552     title = (_("RFX Parameter Window Hints"));
553     rfxbuilder->onum_paramw_hints = rfxbuilder->num_paramw_hints;
554   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_TRIGGERS) {
555     title = (_("RFX Triggers"));
556     rfxbuilder->onum_triggers = rfxbuilder->num_triggers;
557   }
558 
559   dialog = lives_standard_dialog_new(title, FALSE, DEF_DIALOG_WIDTH, DEF_DIALOG_HEIGHT);
560   lives_signal_handlers_disconnect_by_func(dialog, LIVES_GUI_CALLBACK(return_true), NULL);
561 
562   if (title) lives_free(title);
563 
564   lives_window_add_accel_group(LIVES_WINDOW(dialog), accel_group);
565 
566   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
567 
568   // create table and add rows
569   rfxbuilder->table_rows = 0;
570 
571   if (rfxbuilder->table_type == RFX_TABLE_TYPE_REQUIREMENTS) {
572     rfxbuilder->table = lives_table_new(rfxbuilder->num_reqs, 1, FALSE);
573     for (i = 0; i < rfxbuilder->num_reqs; i++) {
574       on_table_add_row(NULL, (livespointer)rfxbuilder);
575       lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry[i]), rfxbuilder->reqs[i]);
576     }
577   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_PARAMS) {
578     rfxbuilder->copy_params = (lives_param_t *)lives_malloc(RFXBUILD_MAX_PARAMS * sizeof(lives_param_t));
579     rfxbuilder->table = lives_table_new(rfxbuilder->num_params, 3, FALSE);
580     for (i = 0; i < rfxbuilder->num_params; i++) {
581       param_copy(&rfxbuilder->copy_params[i], &rfxbuilder->params[i], FALSE);
582       on_table_add_row(NULL, (livespointer)rfxbuilder);
583     }
584   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_PARAM_WINDOW) {
585     rfxbuilder->table = lives_table_new(rfxbuilder->table_rows, 2, FALSE);
586     for (i = 0; i < rfxbuilder->num_paramw_hints; i++) {
587       on_table_add_row(NULL, (livespointer)rfxbuilder);
588     }
589   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_TRIGGERS) {
590     rfxbuilder->copy_triggers = (rfx_trigger_t *)lives_malloc((RFXBUILD_MAX_PARAMS + 1) * sizeof(rfx_trigger_t));
591     rfxbuilder->table = lives_table_new(rfxbuilder->table_rows, 1, FALSE);
592     for (i = 0; i < rfxbuilder->num_triggers; i++) {
593       rfxbuilder->copy_triggers[i].when = rfxbuilder->triggers[i].when;
594       rfxbuilder->copy_triggers[i].code = lives_strdup(rfxbuilder->triggers[i].code);
595       on_table_add_row(NULL, (livespointer)rfxbuilder);
596     }
597   }
598 
599   hbox = lives_hbox_new(FALSE, 0);
600 
601   lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, TRUE, TRUE, widget_opts.packing_height);
602 
603   scrolledwindow = lives_standard_scrolled_window_new(RFX_WINSIZE_H, RFX_WINSIZE_V / 4, rfxbuilder->table);
604 
605   lives_box_pack_start(LIVES_BOX(hbox), scrolledwindow, TRUE, TRUE, widget_opts.packing_width);
606 
607   add_vsep_to_box(LIVES_BOX(hbox));
608 
609   // button box on right
610 
611   button_box = lives_vbutton_box_new();
612   lives_button_box_set_layout(LIVES_BUTTON_BOX(button_box), LIVES_BUTTONBOX_START);
613 
614   lives_box_pack_start(LIVES_BOX(hbox), button_box, FALSE, FALSE, 0);
615 
616   rfxbuilder->new_entry_button = lives_standard_button_new_with_label(_("_New Entry"),
617                                  DEF_BUTTON_WIDTH,
618                                  DEF_BUTTON_HEIGHT);
619 
620   lives_box_pack_start(LIVES_BOX(button_box), rfxbuilder->new_entry_button, FALSE, FALSE, widget_opts.packing_height);
621   lives_container_set_border_width(LIVES_CONTAINER(rfxbuilder->new_entry_button), widget_opts.border_width);
622 
623   rfxbuilder->edit_entry_button = lives_standard_button_new_with_label(_("_Edit Entry"),
624                                   DEF_BUTTON_WIDTH,
625                                   DEF_BUTTON_HEIGHT);
626 
627   lives_box_pack_start(LIVES_BOX(button_box), rfxbuilder->edit_entry_button, FALSE, FALSE, widget_opts.packing_height);
628   lives_container_set_border_width(LIVES_CONTAINER(rfxbuilder->edit_entry_button), widget_opts.border_width);
629 
630   rfxbuilder->remove_entry_button = lives_standard_button_new_with_label(_("_Remove Entry"),
631                                     DEF_BUTTON_WIDTH,
632                                     DEF_BUTTON_HEIGHT);
633 
634   lives_box_pack_start(LIVES_BOX(button_box), rfxbuilder->remove_entry_button, FALSE, FALSE, widget_opts.packing_height);
635   lives_container_set_border_width(LIVES_CONTAINER(rfxbuilder->remove_entry_button), widget_opts.border_width);
636 
637   if (rfxbuilder->table_type == RFX_TABLE_TYPE_PARAM_WINDOW) {
638     rfxbuilder->move_up_button = lives_standard_button_new_with_label(_("Move _Up"),
639                                  DEF_BUTTON_WIDTH,
640                                  DEF_BUTTON_HEIGHT);
641 
642     lives_box_pack_start(LIVES_BOX(button_box), rfxbuilder->move_up_button, FALSE, FALSE, widget_opts.packing_height);
643     lives_container_set_border_width(LIVES_CONTAINER(rfxbuilder->move_up_button), widget_opts.border_width);
644 
645     rfxbuilder->move_down_button = lives_standard_button_new_with_label(_("Move _Down"),
646                                    DEF_BUTTON_WIDTH,
647                                    DEF_BUTTON_HEIGHT);
648 
649     lives_box_pack_start(LIVES_BOX(button_box), rfxbuilder->move_down_button, FALSE, FALSE, widget_opts.packing_height);
650     lives_container_set_border_width(LIVES_CONTAINER(rfxbuilder->move_down_button), widget_opts.border_width);
651 
652     lives_widget_set_sensitive(rfxbuilder->move_up_button, FALSE);
653     lives_widget_set_sensitive(rfxbuilder->move_down_button, FALSE);
654   } else {
655     rfxbuilder->move_up_button = rfxbuilder->move_down_button = NULL;
656   }
657 
658   lives_widget_set_sensitive(rfxbuilder->edit_entry_button, FALSE);
659   lives_widget_set_sensitive(rfxbuilder->remove_entry_button, FALSE);
660 
661   if (rfxbuilder->table_type == RFX_TABLE_TYPE_TRIGGERS) {
662     if (rfxbuilder->num_triggers > rfxbuilder->num_params) {
663       lives_widget_set_sensitive(rfxbuilder->new_entry_button, FALSE);
664     }
665   }
666 
667   cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,
668                  LIVES_RESPONSE_CANCEL);
669 
670   okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
671              LIVES_RESPONSE_OK);
672 
673   lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
674                                LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
675 
676   lives_button_grab_default_special(okbutton);
677 
678   if (rfxbuilder->table_type == RFX_TABLE_TYPE_REQUIREMENTS) {
679     lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
680                               LIVES_GUI_CALLBACK(on_requirements_ok), user_data);
681 
682     lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
683                               LIVES_GUI_CALLBACK(on_requirements_cancel), user_data);
684   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_PARAMS) {
685     lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
686                               LIVES_GUI_CALLBACK(on_params_ok), user_data);
687 
688     lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
689                               LIVES_GUI_CALLBACK(on_params_cancel), user_data);
690   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_PARAM_WINDOW) {
691     lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
692                               LIVES_GUI_CALLBACK(on_param_window_ok), user_data);
693 
694     lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
695                               LIVES_GUI_CALLBACK(on_param_window_cancel),
696                               user_data);
697   } else if (rfxbuilder->table_type == RFX_TABLE_TYPE_TRIGGERS) {
698     lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
699                               LIVES_GUI_CALLBACK(on_triggers_ok), user_data);
700 
701     lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
702                               LIVES_GUI_CALLBACK(on_triggers_cancel), user_data);
703   }
704 
705   lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->new_entry_button), LIVES_WIDGET_CLICKED_SIGNAL,
706                             LIVES_GUI_CALLBACK(on_table_add_row), user_data);
707 
708   lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->edit_entry_button), LIVES_WIDGET_CLICKED_SIGNAL,
709                             LIVES_GUI_CALLBACK(on_table_edit_row), user_data);
710 
711   lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->remove_entry_button), LIVES_WIDGET_CLICKED_SIGNAL,
712                             LIVES_GUI_CALLBACK(on_table_delete_row), user_data);
713 
714   if (rfxbuilder->table_type == RFX_TABLE_TYPE_PARAM_WINDOW) {
715     lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->move_up_button), LIVES_WIDGET_CLICKED_SIGNAL,
716                               LIVES_GUI_CALLBACK(on_table_swap_row), user_data);
717 
718     lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->move_down_button), LIVES_WIDGET_CLICKED_SIGNAL,
719                               LIVES_GUI_CALLBACK(on_table_swap_row), user_data);
720   }
721 
722   lives_widget_show_all(dialog);
723   table_select_row(rfxbuilder, -1);
724 }
725 
726 
on_requirements_ok(LiVESButton * button,livespointer user_data)727 static void on_requirements_ok(LiVESButton *button, livespointer user_data) {
728   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
729 
730   int i;
731   for (i = 0; i < rfxbuilder->onum_reqs; i++) {
732     lives_free(rfxbuilder->reqs[i]);
733   }
734   for (i = 0; i < rfxbuilder->num_reqs; i++) {
735     rfxbuilder->reqs[i] = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry[i])));
736   }
737   lives_general_button_clicked(button, NULL);
738 }
739 
740 
on_requirements_cancel(LiVESButton * button,livespointer user_data)741 static void on_requirements_cancel(LiVESButton *button, livespointer user_data) {
742   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
743 
744   rfxbuilder->num_reqs = rfxbuilder->onum_reqs;
745   lives_general_button_clicked(button, NULL);
746 }
747 
748 
on_properties_ok(LiVESButton * button,livespointer user_data)749 static void on_properties_ok(LiVESButton *button, livespointer user_data) {
750   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
751 
752   if (rfxbuilder->type != RFX_BUILD_TYPE_EFFECT0) {
753     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rfxbuilder->prop_slow))) {
754       rfxbuilder->props |= RFX_PROPS_SLOW;
755     } else if (rfxbuilder->props & RFX_PROPS_SLOW) {
756       rfxbuilder->props ^= RFX_PROPS_SLOW;
757     }
758   } else {
759     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rfxbuilder->prop_batchg))) {
760       rfxbuilder->props |= RFX_PROPS_BATCHG;
761     } else if (rfxbuilder->props & RFX_PROPS_BATCHG) {
762       rfxbuilder->props ^= RFX_PROPS_BATCHG;
763     }
764   }
765   lives_general_button_clicked(button, NULL);
766 }
767 
768 
on_params_ok(LiVESButton * button,livespointer user_data)769 static void on_params_ok(LiVESButton *button, livespointer user_data) {
770   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
771 
772   register int i;
773 
774   for (i = 0; i < rfxbuilder->onum_params; i++) {
775     lives_free(rfxbuilder->params[i].name);
776     lives_free(rfxbuilder->params[i].label);
777     if (rfxbuilder->params[i].type == LIVES_PARAM_STRING_LIST) {
778       if (rfxbuilder->params[i].list) lives_list_free(rfxbuilder->params[i].list);
779     }
780     if (rfxbuilder->copy_params[i].def) lives_free(rfxbuilder->params[i].def);
781   }
782   if (rfxbuilder->onum_params) {
783     lives_free(rfxbuilder->params);
784   }
785   rfxbuilder->params = (lives_param_t *)lives_malloc(rfxbuilder->num_params * sizeof(lives_param_t));
786   for (i = 0; i < rfxbuilder->num_params; i++) {
787     param_copy(&rfxbuilder->params[i], &rfxbuilder->copy_params[i], FALSE);
788 
789     // this is the only place these should be freed
790     lives_free(rfxbuilder->copy_params[i].name);
791     lives_free(rfxbuilder->copy_params[i].label);
792     if (rfxbuilder->copy_params[i].type == LIVES_PARAM_STRING_LIST) {
793       if (rfxbuilder->copy_params[i].list) lives_list_free(rfxbuilder->copy_params[i].list);
794     }
795     if (rfxbuilder->copy_params[i].def) lives_free(rfxbuilder->copy_params[i].def);
796   }
797   if (rfxbuilder->num_params) {
798     lives_free(rfxbuilder->copy_params);
799   }
800   lives_general_button_clicked(button, NULL);
801 }
802 
on_params_cancel(LiVESButton * button,livespointer user_data)803 static void on_params_cancel(LiVESButton *button, livespointer user_data) {
804   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
805 
806   for (register int i = 0; i < rfxbuilder->num_params; i++) {
807     // this is the only place these should be freed
808     lives_free(rfxbuilder->copy_params[i].name);
809     lives_free(rfxbuilder->copy_params[i].label);
810     if (rfxbuilder->copy_params[i].type == LIVES_PARAM_STRING_LIST) {
811       if (rfxbuilder->copy_params[i].list) lives_list_free(rfxbuilder->copy_params[i].list);
812     }
813     if (rfxbuilder->copy_params[i].def) lives_free(rfxbuilder->copy_params[i].def);
814   }
815   if (rfxbuilder->num_params) {
816     lives_free(rfxbuilder->copy_params);
817   }
818   rfxbuilder->num_params = rfxbuilder->onum_params;
819   lives_general_button_clicked(button, NULL);
820 }
821 
822 
on_param_window_ok(LiVESButton * button,livespointer user_data)823 static void on_param_window_ok(LiVESButton *button, livespointer user_data) {
824   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
825 
826   register int i;
827 
828   for (i = 0; i < rfxbuilder->onum_paramw_hints; i++) {
829     lives_free(rfxbuilder->paramw_hints[i]);
830   }
831   for (i = 0; i < rfxbuilder->num_paramw_hints; i++) {
832     rfxbuilder->paramw_hints[i] = lives_strdup_printf("%s%s%s", lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry[i])),
833                                   rfxbuilder->field_delim,
834                                   lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry2[i])));
835   }
836   lives_general_button_clicked(button, NULL);
837 }
838 
on_param_window_cancel(LiVESButton * button,livespointer user_data)839 static void on_param_window_cancel(LiVESButton *button, livespointer user_data) {
840   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
841 
842   rfxbuilder->num_paramw_hints = rfxbuilder->onum_paramw_hints;
843   lives_general_button_clicked(button, NULL);
844 }
845 
846 
on_code_ok(LiVESButton * button,livespointer user_data)847 static void on_code_ok(LiVESButton *button, livespointer user_data) {
848   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
849 
850   switch (rfxbuilder->codetype) {
851   case RFX_CODE_TYPE_PRE:
852     lives_free(rfxbuilder->pre_code);
853     rfxbuilder->pre_code = lives_text_view_get_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview));
854     break;
855 
856   case RFX_CODE_TYPE_LOOP:
857     lives_free(rfxbuilder->loop_code);
858     rfxbuilder->loop_code = lives_text_view_get_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview));
859     break;
860 
861   case RFX_CODE_TYPE_POST:
862     lives_free(rfxbuilder->post_code);
863     rfxbuilder->post_code = lives_text_view_get_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview));
864     break;
865 
866   case RFX_CODE_TYPE_STRDEF: {
867     int maxlen = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max));
868     char buf[++maxlen];
869 
870     if (rfxbuilder->copy_params[rfxbuilder->edit_param].def) lives_free(
871         rfxbuilder->copy_params[rfxbuilder->edit_param].def);
872     lives_snprintf(buf, maxlen, "%s", lives_text_view_get_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview)));
873 
874     rfxbuilder->copy_params[rfxbuilder->edit_param].def = subst(buf, rfxbuilder->field_delim, "");
875     break;
876   }
877   case RFX_CODE_TYPE_STRING_LIST: {
878     char *values = lives_text_view_get_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview));
879     int numlines = get_token_count(values, '\n');
880     char **lines = lives_strsplit(values, "\n", numlines);
881     int defindex = get_int_param(rfxbuilder->copy_params[rfxbuilder->edit_param].def);
882 
883     if (rfxbuilder->copy_params[rfxbuilder->edit_param].list) {
884       lives_list_free(rfxbuilder->copy_params[rfxbuilder->edit_param].list);
885       rfxbuilder->copy_params[rfxbuilder->edit_param].list = NULL;
886     }
887     for (register int i = 0; i < numlines; i++) {
888       if (i < numlines - 1 || strlen(lines[i])) {
889         rfxbuilder->copy_params[rfxbuilder->edit_param].list = lives_list_append
890             (rfxbuilder->copy_params[rfxbuilder->edit_param].list, lives_strdup(lines[i]));
891       }
892     }
893     lives_strfreev(lines);
894 
895     // set "default" combo - TODO - try to retain old default using string matching
896     lives_combo_populate(LIVES_COMBO(rfxbuilder->param_def_combo),
897                          rfxbuilder->copy_params[rfxbuilder->edit_param].list);
898     if (!rfxbuilder->copy_params[rfxbuilder->edit_param].list ||
899         defindex > lives_list_length(rfxbuilder->copy_params[rfxbuilder->edit_param].list)) {
900       set_int_param(rfxbuilder->copy_params[rfxbuilder->edit_param].def, (defindex = 0));
901     }
902     if (rfxbuilder->copy_params[rfxbuilder->edit_param].list) {
903       lives_combo_set_active_string(LIVES_COMBO(rfxbuilder->param_def_combo),
904                                     (char *)lives_list_nth_data(rfxbuilder->copy_params[rfxbuilder->edit_param].list,
905                                           defindex));
906       // *INDENT-OFF*
907 }}}
908   // *INDENT-ON*
909 
910   lives_general_button_clicked(button, NULL);
911 }
912 
913 
on_triggers_ok(LiVESButton * button,livespointer user_data)914 static void on_triggers_ok(LiVESButton * button, livespointer user_data) {
915   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
916 
917   int i;
918   for (i = 0; i < rfxbuilder->onum_triggers; i++) {
919     lives_free(rfxbuilder->triggers[i].code);
920   }
921   if (rfxbuilder->onum_triggers) {
922     lives_free(rfxbuilder->triggers);
923   }
924   rfxbuilder->triggers = (rfx_trigger_t *)lives_malloc(rfxbuilder->num_triggers * sizeof(rfx_trigger_t));
925   for (i = 0; i < rfxbuilder->num_triggers; i++) {
926     rfxbuilder->triggers[i].when = rfxbuilder->copy_triggers[i].when;
927     rfxbuilder->triggers[i].code = lives_strdup(rfxbuilder->copy_triggers[i].code);
928     lives_free(rfxbuilder->copy_triggers[i].code);
929   }
930   if (rfxbuilder->num_triggers) {
931     lives_free(rfxbuilder->copy_triggers);
932   }
933   lives_general_button_clicked(button, NULL);
934 }
935 
936 
on_triggers_cancel(LiVESButton * button,livespointer user_data)937 static void on_triggers_cancel(LiVESButton * button, livespointer user_data) {
938   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
939 
940   uint8_t *valid_triggers = NULL;
941 
942   boolean init_trigger_valid = FALSE;
943 
944   int i;
945 
946   if (rfxbuilder->num_params > 0)
947     valid_triggers = (uint8_t *)lives_calloc(rfxbuilder->num_params, 1);
948 
949   for (i = 0; i < rfxbuilder->num_triggers; i++) {
950     lives_free(rfxbuilder->copy_triggers[i].code);
951   }
952   if (rfxbuilder->num_triggers) {
953     lives_free(rfxbuilder->copy_triggers);
954   }
955   rfxbuilder->num_triggers = rfxbuilder->onum_triggers;
956 
957   for (i = 0; i < rfxbuilder->num_triggers; i++) {
958     int when = rfxbuilder->triggers[i].when;
959     if (when == 0) init_trigger_valid = 1;
960     else valid_triggers[when - 1] = 1;
961   }
962 
963   // reset onchange for anything not in triggers
964   for (i = 0; i < rfxbuilder->num_params; i++) {
965     if (!valid_triggers[i]) {
966       rfxbuilder->params[i].onchange = FALSE;
967     } else {
968       rfxbuilder->params[i].onchange = TRUE;
969     }
970   }
971 
972   if (valid_triggers) lives_free(valid_triggers);
973 
974   if (!init_trigger_valid) {
975     rfxbuilder->has_init_trigger = FALSE;
976   } else {
977     rfxbuilder->has_init_trigger = TRUE;
978   }
979 
980   lives_general_button_clicked(button, NULL);
981 }
982 
983 
on_properties_clicked(LiVESButton * button,livespointer user_data)984 static void on_properties_clicked(LiVESButton * button, livespointer user_data) {
985   LiVESWidget *dialog;
986   LiVESWidget *dialog_vbox;
987   LiVESWidget *cancelbutton;
988   LiVESWidget *okbutton;
989 
990   LiVESAccelGroup *accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
991 
992   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
993 
994   widget_opts.transient = LIVES_WINDOW(rfxbuilder->dialog);
995   dialog = lives_standard_dialog_new(_("RFX Properties"), FALSE, -1, -1);
996   lives_window_add_accel_group(LIVES_WINDOW(dialog), accel_group);
997 
998   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
999 
1000   rfxbuilder->prop_slow = lives_standard_check_button_new(_("_Slow (hint to GUI)"), FALSE, LIVES_BOX(dialog_vbox), NULL);
1001 
1002   if (rfxbuilder->type != RFX_BUILD_TYPE_EFFECT0) {
1003     lives_widget_show(rfxbuilder->prop_slow);
1004   }
1005 
1006   if (rfxbuilder->props & RFX_PROPS_SLOW) {
1007     lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rfxbuilder->prop_slow), TRUE);
1008   }
1009 
1010   if (rfxbuilder->type == RFX_BUILD_TYPE_EFFECT0) {
1011     rfxbuilder->prop_batchg = lives_standard_check_button_new(_("_Batch mode generator"), FALSE, LIVES_BOX(dialog_vbox), NULL);
1012     if (rfxbuilder->props & RFX_PROPS_BATCHG) {
1013       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rfxbuilder->prop_batchg), TRUE);
1014     }
1015   }
1016 
1017   cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,
1018                  LIVES_RESPONSE_CANCEL);
1019 
1020   okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
1021              LIVES_RESPONSE_OK);
1022 
1023   lives_button_grab_default_special(okbutton);
1024 
1025   lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
1026                                LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
1027 
1028   lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
1029                             LIVES_GUI_CALLBACK(on_properties_ok), user_data);
1030 
1031   lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
1032                             LIVES_GUI_CALLBACK(lives_general_button_clicked), NULL);
1033 
1034   lives_widget_show_all(dialog);
1035 }
1036 
1037 
table_select_row(rfx_build_window_t * rfxbuilder,int row)1038 static void table_select_row(rfx_build_window_t *rfxbuilder, int row) {
1039   for (register int i = 0; i < rfxbuilder->table_rows; i++) {
1040     if (i == row) {
1041       lives_widget_set_sensitive(rfxbuilder->edit_entry_button, TRUE);
1042       lives_widget_set_sensitive(rfxbuilder->remove_entry_button, TRUE);
1043       if (rfxbuilder->move_up_button) lives_widget_set_sensitive(rfxbuilder->move_up_button, i != 0);
1044       if (rfxbuilder->move_down_button) lives_widget_set_sensitive(rfxbuilder->move_down_button,
1045             i < rfxbuilder->table_rows - 1);
1046 
1047       lives_widget_set_sensitive(rfxbuilder->entry[i], TRUE);
1048       if (rfxbuilder->entry2[i]) {
1049         lives_widget_set_sensitive(rfxbuilder->entry2[i], TRUE);
1050       }
1051       if (rfxbuilder->entry3[i]) {
1052         lives_widget_set_sensitive(rfxbuilder->entry3[i], TRUE);
1053       }
1054     } else {
1055       lives_editable_select_region(LIVES_EDITABLE(rfxbuilder->entry[i]), 0, 0);
1056       lives_widget_set_sensitive(rfxbuilder->entry[i], FALSE);
1057       lives_entry_set_editable(LIVES_ENTRY(rfxbuilder->entry[i]), FALSE);
1058       if (rfxbuilder->entry2[i]) {
1059         lives_editable_select_region(LIVES_EDITABLE(rfxbuilder->entry2[i]), 0, 0);
1060         lives_widget_set_sensitive(rfxbuilder->entry2[i], FALSE);
1061       }
1062       if (rfxbuilder->entry3[i]) {
1063         lives_editable_select_region(LIVES_EDITABLE(rfxbuilder->entry3[i]), 0, 0);
1064         lives_widget_set_sensitive(rfxbuilder->entry3[i], FALSE);
1065       }
1066     }
1067   }
1068 }
1069 
1070 
on_entry_click(LiVESWidget * widget,LiVESXEventButton * event,livespointer user_data)1071 static boolean on_entry_click(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
1072   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
1073 
1074   widget = lives_bin_get_child(LIVES_BIN(widget));
1075   for (register int i = 0; i < rfxbuilder->table_rows; i++) {
1076     if (widget == rfxbuilder->entry[i] || widget == rfxbuilder->entry2[i] || widget == rfxbuilder->entry3[i]) {
1077       table_select_row(rfxbuilder, i);
1078 
1079       if (event->type != LIVES_BUTTON_PRESS) {
1080         //double click
1081         on_table_edit_row(NULL, rfxbuilder);
1082       }
1083       return TRUE;
1084     }
1085   }
1086   return TRUE;
1087 }
1088 
1089 
on_table_add_row(LiVESButton * button,livespointer user_data)1090 static void on_table_add_row(LiVESButton * button, livespointer user_data) {
1091   LiVESWidget *entry = NULL, *entry2 = NULL, *entry3 = NULL;
1092   LiVESWidget *param_dialog = NULL;
1093   LiVESWidget *param_window_dialog = NULL;
1094   LiVESWidget *trigger_dialog = NULL;
1095 
1096   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
1097   lives_param_t *param = NULL;
1098 
1099   register int i;
1100 
1101   char *tmpx;
1102   const char *ctext;
1103 
1104   LiVESWidget *ebox, *ebox2 = NULL, *ebox3 = NULL;
1105 
1106   rfxbuilder->entry2[rfxbuilder->table_rows] = rfxbuilder->entry3[rfxbuilder->table_rows] = NULL;
1107 
1108   switch (rfxbuilder->table_type) {
1109   case RFX_TABLE_TYPE_REQUIREMENTS:
1110     if (rfxbuilder->table_rows >= RFXBUILD_MAX_REQ) return;
1111 
1112     for (i = 0; i < rfxbuilder->table_rows; i++) {
1113       lives_entry_set_editable(LIVES_ENTRY(rfxbuilder->entry[i]), FALSE);
1114     }
1115 
1116     entry = rfxbuilder->entry[rfxbuilder->table_rows] = lives_standard_entry_new(NULL, NULL, -1, -1, NULL, NULL);
1117 
1118     if (button)
1119       lives_entry_set_editable(LIVES_ENTRY(entry), TRUE);
1120     else
1121       lives_entry_set_editable(LIVES_ENTRY(entry), FALSE);
1122 
1123     ebox = lives_event_box_new();
1124     lives_widget_set_events(ebox, LIVES_BUTTON_PRESS_MASK);
1125 
1126     lives_container_add(LIVES_CONTAINER(ebox), entry);
1127 
1128     lives_table_resize(LIVES_TABLE(rfxbuilder->table), ++rfxbuilder->table_rows, 1);
1129     lives_table_attach(LIVES_TABLE(rfxbuilder->table), ebox, 0, 1, rfxbuilder->table_rows - 1,
1130                        rfxbuilder->table_rows,
1131                        (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
1132                        (LiVESAttachOptions)(0), 0, 0);
1133 
1134     lives_widget_context_update();
1135     lives_widget_grab_focus(entry);
1136     if (button) {
1137       rfxbuilder->num_reqs++;
1138     } else goto add_row_done;
1139     break;
1140 
1141   case RFX_TABLE_TYPE_PARAMS:
1142     entry = rfxbuilder->entry[rfxbuilder->table_rows] = lives_entry_new();
1143     entry2 = rfxbuilder->entry2[rfxbuilder->table_rows] = lives_entry_new();
1144 
1145     if (button) {
1146       boolean param_ok = FALSE;
1147       param = &rfxbuilder->copy_params[rfxbuilder->num_params];
1148 
1149       param->def = NULL;
1150       param->list = NULL;
1151       param->type = LIVES_PARAM_UNKNOWN;
1152 
1153       param_dialog = make_param_dialog(-1, rfxbuilder);
1154       do {
1155         if (lives_dialog_run(LIVES_DIALOG(param_dialog)) == LIVES_RESPONSE_CANCEL) {
1156           lives_free(param->def);
1157           lives_widget_destroy(entry);
1158           lives_widget_destroy(entry2);
1159           return;
1160         }
1161         param_ok = perform_param_checks(rfxbuilder, rfxbuilder->num_params, rfxbuilder->num_params + 1);
1162       } while (!param_ok);
1163 
1164       rfxbuilder->num_params++;
1165 
1166       lives_entry_set_text(LIVES_ENTRY(entry2), lives_entry_get_text(LIVES_ENTRY(rfxbuilder->param_name_entry)));
1167     } else {
1168       lives_entry_set_text(LIVES_ENTRY(entry2), rfxbuilder->params[rfxbuilder->table_rows].name);
1169     }
1170 
1171     lives_entry_set_text(LIVES_ENTRY(entry), (tmpx = lives_strdup_printf("p%d", rfxbuilder->table_rows)));
1172     lives_free(tmpx);
1173 
1174     lives_entry_set_editable(LIVES_ENTRY(entry), FALSE);
1175 
1176     ebox = lives_event_box_new();
1177     lives_widget_set_events(ebox, LIVES_BUTTON_PRESS_MASK);
1178 
1179     lives_container_add(LIVES_CONTAINER(ebox), entry);
1180 
1181     lives_table_resize(LIVES_TABLE(rfxbuilder->table), ++rfxbuilder->table_rows, 3);
1182 
1183     lives_table_attach(LIVES_TABLE(rfxbuilder->table), ebox, 0, 1, rfxbuilder->table_rows - 1,
1184                        rfxbuilder->table_rows,
1185                        (LiVESAttachOptions)(0),
1186                        (LiVESAttachOptions)(0), 0, 0);
1187 
1188     lives_entry_set_editable(LIVES_ENTRY(entry2), FALSE);
1189 
1190     ebox2 = lives_event_box_new();
1191     lives_widget_set_events(ebox2, LIVES_BUTTON_PRESS_MASK);
1192     lives_container_add(LIVES_CONTAINER(ebox2), entry2);
1193 
1194     lives_table_attach(LIVES_TABLE(rfxbuilder->table), ebox2, 1, 2, rfxbuilder->table_rows - 1,
1195                        rfxbuilder->table_rows,
1196                        (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
1197                        (LiVESAttachOptions)(0), 0, 0);
1198 
1199     entry3 = rfxbuilder->entry3[rfxbuilder->table_rows - 1] = lives_entry_new();
1200 
1201     if (!button) {
1202       param = &rfxbuilder->params[rfxbuilder->table_rows - 1];
1203     } else {
1204       param_set_from_dialog((param = &rfxbuilder->copy_params[rfxbuilder->table_rows - 1]), rfxbuilder);
1205     }
1206 
1207     switch (param->type) {
1208     case LIVES_PARAM_NUM:
1209       lives_entry_set_text(LIVES_ENTRY(entry3), (tmpx = lives_strdup_printf("num%d", param->dp)));
1210       lives_free(tmpx);
1211       break;
1212     case LIVES_PARAM_BOOL:
1213       lives_entry_set_text(LIVES_ENTRY(entry3), "bool");
1214       break;
1215     case LIVES_PARAM_COLRGB24:
1216       lives_entry_set_text(LIVES_ENTRY(entry3), "colRGB24");
1217       break;
1218     case LIVES_PARAM_STRING:
1219       lives_entry_set_text(LIVES_ENTRY(entry3), "string");
1220       break;
1221     case LIVES_PARAM_STRING_LIST:
1222       lives_entry_set_text(LIVES_ENTRY(entry3), "string_list");
1223       break;
1224     default:
1225       break;
1226     }
1227 
1228     lives_entry_set_editable(LIVES_ENTRY(entry3), FALSE);
1229 
1230     ebox3 = lives_event_box_new();
1231     lives_widget_set_events(ebox3, LIVES_BUTTON_PRESS_MASK);
1232     lives_container_add(LIVES_CONTAINER(ebox3), entry3);
1233 
1234     lives_table_attach(LIVES_TABLE(rfxbuilder->table), ebox3, 2, 3, rfxbuilder->table_rows - 1,
1235                        rfxbuilder->table_rows,
1236                        (LiVESAttachOptions)(LIVES_FILL),
1237                        (LiVESAttachOptions)(0), 0, 0);
1238 
1239     if (param_dialog) lives_widget_destroy(param_dialog);
1240     if (!button) goto add_row_done;
1241 
1242     lives_widget_queue_resize(lives_widget_get_parent(LIVES_WIDGET(rfxbuilder->table)));
1243     break;
1244 
1245   case RFX_TABLE_TYPE_PARAM_WINDOW:
1246     if (button) {
1247       param_window_dialog = make_param_window_dialog(-1, rfxbuilder);
1248       if (lives_dialog_run(LIVES_DIALOG(param_window_dialog)) == LIVES_RESPONSE_CANCEL) {
1249         return;
1250       }
1251       entry = rfxbuilder->entry[rfxbuilder->table_rows] = lives_entry_new();
1252       ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->paramw_kw_combo));
1253       lives_entry_set_text(LIVES_ENTRY(entry), ctext);
1254       rfxbuilder->num_paramw_hints++;
1255     } else {
1256       char **array = lives_strsplit(rfxbuilder->paramw_hints[rfxbuilder->table_rows], rfxbuilder->field_delim, 2);
1257       entry = rfxbuilder->entry[rfxbuilder->table_rows] = lives_entry_new();
1258       lives_entry_set_text(LIVES_ENTRY(entry), array[0]);
1259       lives_strfreev(array);
1260     }
1261 
1262     lives_entry_set_editable(LIVES_ENTRY(entry), FALSE);
1263 
1264     ebox = lives_event_box_new();
1265     lives_widget_set_events(ebox, LIVES_BUTTON_PRESS_MASK);
1266     lives_container_add(LIVES_CONTAINER(ebox), entry);
1267 
1268     lives_table_resize(LIVES_TABLE(rfxbuilder->table), ++rfxbuilder->table_rows, 2);
1269     lives_table_attach(LIVES_TABLE(rfxbuilder->table), ebox, 0, 1, rfxbuilder->table_rows - 1,
1270                        rfxbuilder->table_rows,
1271                        (LiVESAttachOptions)(LIVES_FILL),
1272                        (LiVESAttachOptions)(0), 0, 0);
1273 
1274     entry2 = rfxbuilder->entry2[rfxbuilder->table_rows - 1] = lives_entry_new();
1275 
1276     if (button) {
1277       if (!strcmp(lives_entry_get_text(LIVES_ENTRY(entry)), "layout")) {
1278         lives_entry_set_text(LIVES_ENTRY(entry2), lives_entry_get_text(LIVES_ENTRY(rfxbuilder->paramw_rest_entry)));
1279       } else {
1280         // TODO - use lives_rfx_special_t->has_subtype,name,num_params
1281         ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->paramw_sp_combo));
1282         if (!strcmp(ctext, "framedraw")) {
1283           const char *ctext2 = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->paramw_spsub_combo));
1284           lives_entry_set_text(LIVES_ENTRY(entry2), (tmpx = lives_strdup_printf("%s%s%s%s%s",
1285                                ctext, rfxbuilder->field_delim, ctext2,
1286                                rfxbuilder->field_delim,
1287                                lives_entry_get_text(LIVES_ENTRY(rfxbuilder->paramw_rest_entry)))));
1288         } else {
1289           lives_entry_set_text(LIVES_ENTRY(entry2), (tmpx = lives_strdup_printf("%s%s%s", ctext, rfxbuilder->field_delim,
1290                                lives_entry_get_text(LIVES_ENTRY(rfxbuilder->paramw_rest_entry)))));
1291         }
1292         lives_free(tmpx);
1293       }
1294     } else {
1295       char **array = lives_strsplit(rfxbuilder->paramw_hints[rfxbuilder->table_rows - 1], rfxbuilder->field_delim, 2);
1296       lives_entry_set_text(LIVES_ENTRY(entry2), array[1]);
1297       lives_strfreev(array);
1298     }
1299 
1300     lives_entry_set_editable(LIVES_ENTRY(entry2), FALSE);
1301 
1302     ebox2 = lives_event_box_new();
1303     lives_widget_set_events(ebox2, LIVES_BUTTON_PRESS_MASK);
1304     lives_container_add(LIVES_CONTAINER(ebox2), entry2);
1305 
1306     lives_table_attach(LIVES_TABLE(rfxbuilder->table), ebox2, 1, 2, rfxbuilder->table_rows - 1,
1307                        rfxbuilder->table_rows,
1308                        (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
1309                        (LiVESAttachOptions)(0), 0, 0);
1310 
1311     if (param_window_dialog) lives_widget_destroy(param_window_dialog);
1312     if (!button) goto add_row_done;
1313 
1314     lives_widget_queue_resize(lives_widget_get_parent(LIVES_WIDGET(rfxbuilder->table)));
1315     lives_widget_queue_resize(LIVES_WIDGET(rfxbuilder->table));
1316     break;
1317 
1318   case RFX_TABLE_TYPE_TRIGGERS:
1319     entry = rfxbuilder->entry[rfxbuilder->table_rows] = lives_entry_new();
1320 
1321     if (button) {
1322       trigger_dialog = make_trigger_dialog(-1, rfxbuilder);
1323       if (lives_dialog_run(LIVES_DIALOG(trigger_dialog)) == LIVES_RESPONSE_CANCEL) {
1324         return;
1325       }
1326       lives_entry_set_text(LIVES_ENTRY(entry), lives_entry_get_text(LIVES_ENTRY(rfxbuilder->trigger_when_entry)));
1327       rfxbuilder->num_triggers++;
1328     } else {
1329       char *tmpx2 = NULL;
1330       lives_entry_set_text(LIVES_ENTRY(entry), rfxbuilder->triggers[rfxbuilder->table_rows].when ?
1331                            (tmpx2 = lives_strdup_printf("%d", rfxbuilder->triggers[rfxbuilder->table_rows].when - 1)) : "init");
1332       if (tmpx2) lives_free(tmpx2);
1333     }
1334 
1335     lives_entry_set_editable(LIVES_ENTRY(entry), FALSE);
1336 
1337     ebox = lives_event_box_new();
1338     lives_widget_set_events(ebox, LIVES_BUTTON_PRESS_MASK);
1339     lives_container_add(LIVES_CONTAINER(ebox), entry);
1340 
1341     lives_table_resize(LIVES_TABLE(rfxbuilder->table), ++rfxbuilder->table_rows, 1);
1342     lives_table_attach(LIVES_TABLE(rfxbuilder->table), ebox, 0, 1, rfxbuilder->table_rows - 1,
1343                        rfxbuilder->table_rows,
1344                        (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
1345                        (LiVESAttachOptions)(0), 0, 0);
1346 
1347     if (!button) {
1348       if (trigger_dialog) lives_widget_destroy(trigger_dialog);
1349       goto add_row_done;
1350     }
1351 
1352     if (rfxbuilder->num_triggers > rfxbuilder->num_params) {
1353       lives_widget_set_sensitive(rfxbuilder->new_entry_button, FALSE);
1354     }
1355 
1356     rfxbuilder->copy_triggers[rfxbuilder->table_rows - 1].code = lives_text_view_get_text(LIVES_TEXT_VIEW(
1357           rfxbuilder->code_textview));
1358 
1359     rfxbuilder->copy_triggers[rfxbuilder->table_rows - 1].when = atoi(lives_entry_get_text(LIVES_ENTRY(
1360           rfxbuilder->trigger_when_entry))) + 1;
1361     if (!strcmp(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->trigger_when_entry)), "init"))
1362       rfxbuilder->copy_triggers[rfxbuilder->table_rows - 1].when = 0;
1363 
1364     if (!rfxbuilder->copy_triggers[rfxbuilder->table_rows - 1].when) {
1365       rfxbuilder->has_init_trigger = TRUE;
1366     } else {
1367       rfxbuilder->params[rfxbuilder->copy_triggers[rfxbuilder->table_rows - 1].when - 1].onchange = TRUE;
1368     }
1369 
1370     if (trigger_dialog) lives_widget_destroy(trigger_dialog);
1371     lives_widget_queue_resize(lives_widget_get_parent(LIVES_WIDGET(rfxbuilder->table)));
1372     lives_widget_queue_resize(LIVES_WIDGET(rfxbuilder->table));
1373 
1374     break;
1375   default:
1376     return;
1377   }
1378 
1379 add_row_done:
1380 
1381   lives_widget_show_all(rfxbuilder->table);
1382 
1383   lives_event_box_set_above_child(LIVES_EVENT_BOX(ebox), TRUE);
1384 
1385   lives_signal_sync_connect(LIVES_GUI_OBJECT(ebox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
1386                             LIVES_GUI_CALLBACK(on_entry_click), (livespointer)rfxbuilder);
1387 
1388   if (palette->style & STYLE_1) {
1389     if (palette->style & STYLE_3) {
1390       lives_widget_set_bg_color(entry, LIVES_WIDGET_STATE_INSENSITIVE, &palette->menu_and_bars);
1391       lives_widget_set_fg_color(entry, LIVES_WIDGET_STATE_INSENSITIVE, &palette->menu_and_bars_fore);
1392     } else {
1393       lives_widget_set_bg_color(entry, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_back);
1394       lives_widget_set_fg_color(entry, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_fore);
1395     }
1396   }
1397 
1398   if (entry2) {
1399     lives_event_box_set_above_child(LIVES_EVENT_BOX(ebox2), TRUE);
1400 
1401     lives_signal_sync_connect(LIVES_GUI_OBJECT(ebox2), LIVES_WIDGET_BUTTON_PRESS_EVENT,
1402                               LIVES_GUI_CALLBACK(on_entry_click), (livespointer)rfxbuilder);
1403 
1404     if (palette->style & STYLE_1) {
1405       if (palette->style & STYLE_3) {
1406         lives_widget_set_bg_color(entry2, LIVES_WIDGET_STATE_INSENSITIVE, &palette->menu_and_bars);
1407         lives_widget_set_fg_color(entry2, LIVES_WIDGET_STATE_INSENSITIVE, &palette->menu_and_bars_fore);
1408       } else {
1409         lives_widget_set_bg_color(entry2, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_back);
1410         lives_widget_set_fg_color(entry2, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_fore);
1411       }
1412     }
1413   }
1414 
1415   if (entry3) {
1416     lives_event_box_set_above_child(LIVES_EVENT_BOX(ebox3), TRUE);
1417 
1418     lives_signal_sync_connect(LIVES_GUI_OBJECT(ebox3), LIVES_WIDGET_BUTTON_PRESS_EVENT,
1419                               LIVES_GUI_CALLBACK(on_entry_click), (livespointer)rfxbuilder);
1420 
1421     if (palette->style & STYLE_1) {
1422       if (palette->style & STYLE_3) {
1423         lives_widget_set_bg_color(entry3, LIVES_WIDGET_STATE_INSENSITIVE, &palette->menu_and_bars);
1424         lives_widget_set_fg_color(entry3, LIVES_WIDGET_STATE_INSENSITIVE, &palette->menu_and_bars_fore);
1425       } else {
1426         lives_widget_set_bg_color(entry3, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_back);
1427         lives_widget_set_fg_color(entry3, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_fore);
1428       }
1429     }
1430   }
1431 
1432   if (button) {
1433     table_select_row(rfxbuilder, rfxbuilder->table_rows - 1);
1434 
1435     if (rfxbuilder->table_type == RFX_TABLE_TYPE_REQUIREMENTS) {
1436       on_table_edit_row(NULL, user_data);
1437     }
1438   }
1439 }
1440 
1441 
param_set_from_dialog(lives_param_t * copy_param,rfx_build_window_t * rfxbuilder)1442 static void param_set_from_dialog(lives_param_t *copy_param, rfx_build_window_t *rfxbuilder) {
1443   // set parameter values from param_dialog
1444   // this is called after adding a new copy_param or editing an existing one
1445   const char *ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->param_type_combo));
1446 
1447   copy_param->name = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->param_name_entry)));
1448   copy_param->label = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->param_label_entry)));
1449 
1450   if (!strcmp(ctext, "num")) {
1451     copy_param->type = LIVES_PARAM_NUM;
1452   } else if (!strcmp(ctext, "bool")) {
1453     copy_param->type = LIVES_PARAM_BOOL;
1454   } else if (!strcmp(ctext, "colRGB24")) {
1455     copy_param->type = LIVES_PARAM_COLRGB24;
1456   } else if (!strcmp(ctext, "string")) {
1457     copy_param->type = LIVES_PARAM_STRING;
1458   } else if (!strcmp(ctext, "string_list")) {
1459     copy_param->type = LIVES_PARAM_STRING_LIST;
1460   }
1461 
1462   copy_param->dp = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_dp));
1463   copy_param->group = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_group));
1464   copy_param->onchange = FALSE; // no trigger assigned yet
1465 
1466   // TODO - check
1467   if (copy_param->type != LIVES_PARAM_STRING && copy_param->def) lives_free(copy_param->def);
1468   if (copy_param->type != LIVES_PARAM_STRING_LIST && copy_param->list) lives_list_free(copy_param->list);
1469 
1470   switch (copy_param->type) {
1471   case LIVES_PARAM_NUM:
1472     copy_param->min = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min));
1473     copy_param->max = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max));
1474     copy_param->step_size = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step));
1475     copy_param->wrap = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rfxbuilder->param_wrap_checkbutton));
1476     if (!copy_param->dp) {
1477       copy_param->def = lives_malloc(sizint);
1478       set_int_param(copy_param->def, lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def)));
1479     } else {
1480       copy_param->def = lives_malloc(sizdbl);
1481       set_double_param(copy_param->def, lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def)));
1482     }
1483     break;
1484   case LIVES_PARAM_BOOL:
1485     copy_param->def = lives_malloc(sizint);
1486     set_bool_param(copy_param->def, lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def)));
1487     copy_param->dp = 0;
1488     break;
1489   case LIVES_PARAM_STRING:
1490     copy_param->max = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max));
1491     copy_param->dp = 0;
1492     break;
1493   case LIVES_PARAM_STRING_LIST:
1494     copy_param->dp = 0;
1495     copy_param->def = lives_malloc(sizdbl);
1496     ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->param_def_combo));
1497     set_int_param(copy_param->def, lives_list_strcmp_index(copy_param->list, ctext, TRUE));
1498     break;
1499   case LIVES_PARAM_COLRGB24:
1500     copy_param->def = lives_malloc(3 * sizint);
1501     set_colRGB24_param(copy_param->def, lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def)),
1502                        lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)),
1503                        lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max)));
1504     copy_param->dp = 0;
1505     break;
1506   default:
1507     break;
1508   }
1509 }
1510 
1511 
on_table_edit_row(LiVESButton * button,livespointer user_data)1512 static void on_table_edit_row(LiVESButton * button, livespointer user_data) {
1513   LiVESWidget *param_dialog;
1514   LiVESWidget *paramw_dialog;
1515   LiVESWidget *trigger_dialog;
1516 
1517   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
1518   lives_param_t *param;
1519 
1520   int i;
1521   int found = -1;
1522   boolean param_ok = FALSE;
1523 
1524   char *tmpx;
1525   const char *ctext;
1526 
1527   for (i = 0; i < rfxbuilder->table_rows && found == -1; i++) {
1528     if (lives_widget_is_sensitive(rfxbuilder->entry[i])) {
1529       found = i;
1530       break;
1531     }
1532   }
1533   if (found == -1) return;
1534 
1535   switch (rfxbuilder->table_type) {
1536   case RFX_TABLE_TYPE_REQUIREMENTS:
1537     for (i = 0; i < rfxbuilder->table_rows; i++) {
1538       if (found == i) {
1539         lives_entry_set_editable(LIVES_ENTRY(rfxbuilder->entry[i]), TRUE);
1540         lives_widget_context_update();
1541         lives_widget_grab_focus(rfxbuilder->entry[i]);
1542       } else {
1543         lives_entry_set_editable(LIVES_ENTRY(rfxbuilder->entry[i]), FALSE);
1544       }
1545     }
1546     break;
1547 
1548   case RFX_TABLE_TYPE_PARAMS:
1549     param_dialog = make_param_dialog(found, rfxbuilder);
1550     do {
1551       if (lives_dialog_run(LIVES_DIALOG(param_dialog)) == LIVES_RESPONSE_CANCEL) {
1552         return;
1553       }
1554       param_ok = perform_param_checks(rfxbuilder, found, rfxbuilder->num_params);
1555     } while (!param_ok);
1556 
1557     param_set_from_dialog((param = &rfxbuilder->copy_params[found]), rfxbuilder);
1558     lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry2[found]), param->name);
1559     switch (param->type) {
1560     case LIVES_PARAM_NUM:
1561       lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry3[found]), (tmpx = lives_strdup_printf("num%d", param->dp)));
1562       lives_free(tmpx);
1563       break;
1564     case LIVES_PARAM_BOOL:
1565       lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry3[found]), "bool");
1566       break;
1567     case LIVES_PARAM_COLRGB24:
1568       lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry3[found]), "colRGB24");
1569       break;
1570     case LIVES_PARAM_STRING:
1571       lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry3[found]), "string");
1572       break;
1573     case LIVES_PARAM_STRING_LIST:
1574       lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry3[found]), "string_list");
1575       break;
1576     default:
1577       break;
1578     }
1579     lives_widget_destroy(param_dialog);
1580     break;
1581 
1582   case RFX_TABLE_TYPE_PARAM_WINDOW:
1583     paramw_dialog = make_param_window_dialog(found, rfxbuilder);
1584     if (lives_dialog_run(LIVES_DIALOG(paramw_dialog)) == LIVES_RESPONSE_CANCEL) {
1585       return;
1586     }
1587     ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->paramw_kw_combo));
1588     lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry[found]), ctext);
1589     if (!strcmp(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry[found])), "layout")) {
1590       lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry2[found]),
1591                            lives_entry_get_text(LIVES_ENTRY(rfxbuilder->paramw_rest_entry)));
1592     } else {
1593       // TODO - use lives_rfx_special_t->has_subtype,name,num_params
1594       ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->paramw_sp_combo));
1595       if (!strcmp(ctext, "framedraw")) {
1596         const char *ctext2 = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->paramw_spsub_combo));
1597         lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry2[found]),
1598                              (tmpx = lives_strdup_printf("%s%s%s%s%s", ctext, rfxbuilder->field_delim, ctext2,
1599                                      rfxbuilder->field_delim,
1600                                      lives_entry_get_text(LIVES_ENTRY(rfxbuilder->paramw_rest_entry)))));
1601       } else {
1602         lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry2[found]),
1603                              (tmpx = lives_strdup_printf("%s%s%s", ctext, rfxbuilder->field_delim, lives_entry_get_text
1604                                      (LIVES_ENTRY(rfxbuilder->paramw_rest_entry)))));
1605       }
1606       lives_free(tmpx);
1607     }
1608     lives_widget_destroy(paramw_dialog);
1609     break;
1610 
1611   case RFX_TABLE_TYPE_TRIGGERS:
1612     trigger_dialog = make_trigger_dialog(found, rfxbuilder);
1613     if (lives_dialog_run(LIVES_DIALOG(trigger_dialog)) == LIVES_RESPONSE_CANCEL) {
1614       return;
1615     }
1616 
1617     lives_free(rfxbuilder->copy_triggers[found].code);
1618     rfxbuilder->copy_triggers[found].code = lives_text_view_get_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview));
1619 
1620     lives_widget_destroy(trigger_dialog);
1621     break;
1622   }
1623 }
1624 
1625 
on_table_swap_row(LiVESButton * button,livespointer user_data)1626 static void on_table_swap_row(LiVESButton * button, livespointer user_data) {
1627   char *entry_text;
1628   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
1629   int found = -1;
1630 
1631   for (register int i = 0; i < rfxbuilder->table_rows && found == -1; i++) {
1632     if (!(lives_widget_get_state(rfxbuilder->entry[i])&LIVES_WIDGET_STATE_INSENSITIVE)) {
1633       found = i;
1634       break;
1635     }
1636   }
1637 
1638   if (found == -1) if ((found = rfxbuilder->table_swap_row1) == -1) return;
1639 
1640   switch (rfxbuilder->table_type) {
1641   case RFX_TABLE_TYPE_PARAM_WINDOW:
1642     if (button == LIVES_BUTTON(rfxbuilder->move_up_button)) {
1643       rfxbuilder->table_swap_row2 = found - 1;
1644     } else if (button == LIVES_BUTTON(rfxbuilder->move_down_button)) {
1645       rfxbuilder->table_swap_row2 = found + 1;
1646     }
1647     if (rfxbuilder->table_swap_row2 < 0 || rfxbuilder->table_swap_row2 >= rfxbuilder->table_rows) return;
1648 
1649     entry_text = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry[found])));
1650     lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry[found]),
1651                          lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry[rfxbuilder->table_swap_row2])));
1652     lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry[rfxbuilder->table_swap_row2]), entry_text);
1653     lives_free(entry_text);
1654 
1655     entry_text = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry2[found])));
1656     lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry2[found]),
1657                          lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry2[rfxbuilder->table_swap_row2])));
1658     lives_entry_set_text(LIVES_ENTRY(rfxbuilder->entry2[rfxbuilder->table_swap_row2]), entry_text);
1659     lives_free(entry_text);
1660 
1661     break;
1662   default:
1663     break;
1664   }
1665   rfxbuilder->table_swap_row1 = rfxbuilder->table_swap_row2;
1666   table_select_row(rfxbuilder, rfxbuilder->table_swap_row1);
1667 }
1668 
1669 
on_table_delete_row(LiVESButton * button,livespointer user_data)1670 static void on_table_delete_row(LiVESButton * button, livespointer user_data) {
1671   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
1672 
1673   LiVESWidget *ebox;
1674 
1675 #if !LIVES_TABLE_IS_GRID
1676   LiVESWidget *ebox2 = NULL, *ebox3 = NULL, *ebox4 = NULL;
1677 #else
1678   LiVESWidget *entry = NULL, *entry2;
1679 #endif
1680 
1681   int move = 0;
1682   boolean triggers_adjusted = FALSE;
1683 
1684   register int i;
1685 
1686   switch (rfxbuilder->table_type) {
1687   case RFX_TABLE_TYPE_REQUIREMENTS:
1688     for (i = 0; i < rfxbuilder->table_rows; i++) {
1689       if (move > 0) {
1690 #if !LIVES_TABLE_IS_GRID
1691         if (i < rfxbuilder->table_rows - 1) ebox2 = lives_widget_get_parent(rfxbuilder->entry[i]);
1692         lives_widget_reparent(rfxbuilder->entry[i], ebox);
1693         ebox = ebox2;
1694 #endif
1695         rfxbuilder->entry[i - 1] = rfxbuilder->entry[i];
1696       } else if (!(lives_widget_get_state(rfxbuilder->entry[i])&LIVES_WIDGET_STATE_INSENSITIVE)) {
1697 #if LIVES_TABLE_IS_GRID
1698         lives_grid_remove_row(LIVES_GRID(rfxbuilder->table), i);
1699 #else
1700         ebox = lives_widget_get_parent(rfxbuilder->entry[i]);
1701         if (rfxbuilder->table_rows > 1) {
1702           lives_table_resize(LIVES_TABLE(rfxbuilder->table), rfxbuilder->table_rows - 1, 1);
1703         }
1704         lives_widget_destroy(rfxbuilder->entry[i]);
1705 #endif
1706         move = i + 1;
1707       }
1708     }
1709     if (move == 0) return;
1710     rfxbuilder->table_rows--;
1711     rfxbuilder->num_reqs--;
1712     break;
1713 
1714   case RFX_TABLE_TYPE_PARAMS:
1715     for (i = 0; i < rfxbuilder->table_rows; i++) {
1716       if (move > 0) {
1717         // note - parameters become renumbered here
1718 
1719 #if !LIVES_TABLE_IS_GRID
1720         // move everything up except p%d
1721         if (i < rfxbuilder->table_rows - 1) {
1722           ebox3 = lives_widget_get_parent(rfxbuilder->entry2[i]);
1723           ebox4 = lives_widget_get_parent(rfxbuilder->entry3[i]);
1724         }
1725         lives_widget_reparent(rfxbuilder->entry2[i], ebox);
1726         lives_widget_reparent(rfxbuilder->entry3[i], ebox2);
1727         ebox = ebox3;
1728         ebox2 = ebox4;
1729 #else
1730         // move p%d up so when we remove the row the numbering is ok
1731         entry2 = rfxbuilder->entry[i];
1732         ebox = lives_widget_get_parent(rfxbuilder->entry[i]);
1733         lives_widget_object_ref(entry2);
1734         lives_widget_unparent(entry2);
1735         lives_container_add(LIVES_CONTAINER(ebox), entry);
1736         lives_widget_object_unref(entry);
1737         entry = entry2;
1738 #endif
1739 
1740         rfxbuilder->entry2[i - 1] = rfxbuilder->entry2[i];
1741         rfxbuilder->entry3[i - 1] = rfxbuilder->entry3[i];
1742         param_copy(&rfxbuilder->copy_params[i - 1], &rfxbuilder->copy_params[i], FALSE);
1743         lives_free(rfxbuilder->copy_params[i].name);
1744         lives_free(rfxbuilder->copy_params[i].label);
1745         lives_free(rfxbuilder->copy_params[i].def);
1746 
1747       } else if (!(lives_widget_get_state(rfxbuilder->entry[i])&LIVES_WIDGET_STATE_INSENSITIVE)) {
1748         if (rfxbuilder->copy_params[i].onchange) {
1749           do_error_dialog(_("\n\nCannot remove this parameter as it has a trigger.\nPlease remove the trigger first.\n\n"));
1750           return;
1751         }
1752 
1753 #if LIVES_TABLE_IS_GRID
1754         entry = rfxbuilder->entry[i];
1755         lives_widget_object_ref(entry);
1756         lives_widget_unparent(entry);
1757         lives_grid_remove_row(LIVES_GRID(rfxbuilder->table), i);
1758 #else
1759         ebox = lives_widget_get_parent(rfxbuilder->entry2[i]);
1760         ebox2 = lives_widget_get_parent(rfxbuilder->entry3[i]);
1761 
1762         lives_widget_destroy(rfxbuilder->entry[rfxbuilder->table_rows - 1]);
1763         lives_widget_destroy(rfxbuilder->entry2[i]);
1764         lives_widget_destroy(rfxbuilder->entry3[i]);
1765 
1766         if (rfxbuilder->table_rows > 1) {
1767           lives_table_resize(LIVES_TABLE(rfxbuilder->table), rfxbuilder->table_rows - 1, 3);
1768         }
1769 #endif
1770         lives_free(rfxbuilder->copy_params[i].name);
1771         lives_free(rfxbuilder->copy_params[i].label);
1772         lives_free(rfxbuilder->copy_params[i].def);
1773 
1774         move = i + 1;
1775       }
1776     }
1777 
1778 #if LIVES_TABLE_IS_GRID
1779     if (entry) lives_widget_destroy(entry);
1780 #endif
1781 
1782     if (move == 0) return;
1783     rfxbuilder->table_rows--;
1784     rfxbuilder->num_params--;
1785     for (i = 0; i < rfxbuilder->num_triggers; i++) {
1786       if (rfxbuilder->triggers[i].when > move) {
1787         rfxbuilder->params[rfxbuilder->triggers[i].when - 1].onchange = FALSE;
1788         rfxbuilder->params[--rfxbuilder->triggers[i].when - 1].onchange = TRUE;
1789       }
1790       triggers_adjusted = TRUE;
1791     }
1792     if (triggers_adjusted) {
1793       do_error_dialog(_("\n\nSome triggers were adjusted.\nPlease check the trigger code.\n"));
1794     }
1795     break;
1796 
1797   case RFX_TABLE_TYPE_PARAM_WINDOW:
1798     for (i = 0; i < rfxbuilder->table_rows; i++) {
1799       if (move > 0) {
1800 #if !LIVES_TABLE_IS_GRID
1801         if (i < rfxbuilder->table_rows - 1) {
1802           ebox3 = lives_widget_get_parent(rfxbuilder->entry[i]);
1803           ebox4 = lives_widget_get_parent(rfxbuilder->entry2[i]);
1804         }
1805         lives_widget_reparent(rfxbuilder->entry[i], ebox);
1806         lives_widget_reparent(rfxbuilder->entry2[i], ebox2);
1807         ebox = ebox3;
1808         ebox2 = ebox4;
1809 #endif
1810 
1811         rfxbuilder->entry[i - 1] = rfxbuilder->entry[i];
1812         rfxbuilder->entry2[i - 1] = rfxbuilder->entry2[i];
1813       } else if (!(lives_widget_get_state(rfxbuilder->entry[i])&LIVES_WIDGET_STATE_INSENSITIVE)) {
1814 #if LIVES_TABLE_IS_GRID
1815         lives_grid_remove_row(LIVES_GRID(rfxbuilder->table), i);
1816 #else
1817         ebox = lives_widget_get_parent(rfxbuilder->entry[i]);
1818         ebox2 = lives_widget_get_parent(rfxbuilder->entry2[i]);
1819 
1820         lives_widget_destroy(rfxbuilder->entry[i]);
1821         lives_widget_destroy(rfxbuilder->entry2[i]);
1822         if (rfxbuilder->table_rows > 1) {
1823           lives_table_resize(LIVES_TABLE(rfxbuilder->table), rfxbuilder->table_rows - 1, 1);
1824         }
1825 #endif
1826         move = i + 1;
1827       }
1828     }
1829     if (!move) return;
1830     rfxbuilder->table_rows--;
1831     rfxbuilder->num_paramw_hints--;
1832     break;
1833 
1834   case RFX_TABLE_TYPE_TRIGGERS:
1835     for (i = 0; i < rfxbuilder->table_rows; i++) {
1836       if (move > 0) {
1837 #if !LIVES_TABLE_IS_GRID
1838         if (i < rfxbuilder->table_rows - 1) ebox2 = lives_widget_get_parent(rfxbuilder->entry[i]);
1839         lives_widget_reparent(rfxbuilder->entry[i], ebox);
1840         ebox = ebox2;
1841 #endif
1842 
1843         rfxbuilder->entry[i - 1] = rfxbuilder->entry[i];
1844         rfxbuilder->copy_triggers[i - 1].when = rfxbuilder->copy_triggers[i].when;
1845         rfxbuilder->copy_triggers[i - 1].code = lives_strdup(rfxbuilder->copy_triggers[i].code);
1846         lives_free(rfxbuilder->copy_triggers[i].code);
1847       } else if (!(lives_widget_get_state(rfxbuilder->entry[i])&LIVES_WIDGET_STATE_INSENSITIVE)) {
1848         int when = atoi(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry[i]))) + 1;
1849 
1850         if (!strcmp(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry[i])), "init")) rfxbuilder->has_init_trigger = FALSE;
1851         else rfxbuilder->params[when - 1].onchange = FALSE;
1852 
1853         lives_free(rfxbuilder->copy_triggers[i].code);
1854 
1855 #if LIVES_TABLE_IS_GRID
1856         lives_grid_remove_row(LIVES_GRID(rfxbuilder->table), i);
1857 #else
1858         ebox = lives_widget_get_parent(rfxbuilder->entry[i]);
1859         if (rfxbuilder->table_rows > 1) {
1860           lives_table_resize(LIVES_TABLE(rfxbuilder->table), rfxbuilder->table_rows - 1, 1);
1861         }
1862         lives_widget_destroy(rfxbuilder->entry[i]);
1863 #endif
1864         move = i + 1;
1865       }
1866     }
1867     if (!move) return;
1868     rfxbuilder->table_rows--;
1869     rfxbuilder->num_triggers--;
1870 
1871     if (rfxbuilder->table_rows <= rfxbuilder->num_params) {
1872       lives_widget_set_sensitive(rfxbuilder->new_entry_button, TRUE);
1873     }
1874 
1875     break;
1876   default:
1877     return;
1878   }
1879 
1880   if (rfxbuilder->table_rows == 0) {
1881     lives_widget_set_sensitive(rfxbuilder->edit_entry_button, FALSE);
1882     lives_widget_set_sensitive(rfxbuilder->remove_entry_button, FALSE);
1883     if (rfxbuilder->move_up_button) lives_widget_set_sensitive(rfxbuilder->move_up_button, FALSE);
1884     if (rfxbuilder->move_down_button) lives_widget_set_sensitive(rfxbuilder->move_down_button, FALSE);
1885   } else {
1886     if (move >= rfxbuilder->table_rows) move = rfxbuilder->table_rows - 1;
1887     table_select_row(rfxbuilder, move);
1888   }
1889 }
1890 
1891 
make_param_dialog(int pnum,rfx_build_window_t * rfxbuilder)1892 LiVESWidget *make_param_dialog(int pnum, rfx_build_window_t *rfxbuilder) {
1893   LiVESWidget *dialog;
1894   LiVESWidget *dialog_vbox;
1895 
1896   LiVESAccelGroup *accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
1897 
1898   LiVESList *typelist = NULL;
1899 
1900   lives_colRGB48_t rgb;
1901 
1902   char *tmp, *tmp2, *title;
1903 
1904   if (pnum < 0) {
1905     title = (_("New RFX Parameter"));
1906   } else {
1907     title = (_("Edit RFX Parameter"));
1908   }
1909 
1910   dialog = lives_standard_dialog_new(title, TRUE, -1, -1);
1911   lives_free(title);
1912 
1913   lives_window_add_accel_group(LIVES_WINDOW(dialog), accel_group);
1914 
1915   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
1916 
1917   // name
1918 
1919   rfxbuilder->param_name_entry = lives_standard_entry_new((tmp = (_("_Name:    "))),
1920                                  pnum >= 0 ? rfxbuilder->copy_params[pnum].name : NULL,
1921                                  60.*widget_opts.scale, -1, LIVES_BOX(dialog_vbox),
1922                                  (tmp2 = lives_strdup((_("Name of the parameter, must be unique in the plugin.")))));
1923 
1924   if (pnum < 0) lives_widget_grab_focus(rfxbuilder->param_name_entry);
1925 
1926   lives_free(tmp);
1927   lives_free(tmp2);
1928 
1929   // label
1930 
1931   rfxbuilder->param_label_entry = lives_standard_entry_new((tmp = (_("_Label:    "))),
1932                                   pnum >= 0 ? rfxbuilder->copy_params[pnum].label : NULL,
1933                                   60.*widget_opts.scale, -1, LIVES_BOX(dialog_vbox),
1934                                   (tmp2 = (_("Label to be shown by the parameter. "
1935                                           "An underscore represents mnemonic accelerator."))));
1936   lives_free(tmp);
1937   lives_free(tmp2);
1938 
1939   // type
1940 
1941   typelist = lives_list_append(typelist, (livespointer)"num");
1942   typelist = lives_list_append(typelist, (livespointer)"bool");
1943   typelist = lives_list_append(typelist, (livespointer)"string");
1944   typelist = lives_list_append(typelist, (livespointer)"colRGB24");
1945   typelist = lives_list_append(typelist, (livespointer)"string_list");
1946 
1947   rfxbuilder->param_type_combo = lives_standard_combo_new((tmp = (_("_Type:         "))), typelist,
1948                                  LIVES_BOX(dialog_vbox), (tmp2 = (_("Parameter type (select from list)."))));
1949   lives_free(tmp);
1950   lives_free(tmp2);
1951   lives_list_free(typelist);
1952 
1953   rfxbuilder->edit_param = pnum;
1954 
1955   if (pnum >= 0) {
1956     switch (rfxbuilder->copy_params[pnum].type) {
1957     case LIVES_PARAM_NUM:
1958       lives_combo_set_active_index(LIVES_COMBO(rfxbuilder->param_type_combo), 0); break;
1959     case LIVES_PARAM_BOOL:
1960       lives_combo_set_active_index(LIVES_COMBO(rfxbuilder->param_type_combo), 1); break;
1961     case LIVES_PARAM_STRING:
1962       lives_combo_set_active_index(LIVES_COMBO(rfxbuilder->param_type_combo), 2); break;
1963     case LIVES_PARAM_COLRGB24:
1964       lives_combo_set_active_index(LIVES_COMBO(rfxbuilder->param_type_combo), 3); break;
1965     case LIVES_PARAM_STRING_LIST:
1966       lives_combo_set_active_index(LIVES_COMBO(rfxbuilder->param_type_combo), 4); break;
1967     default: break;
1968     }
1969   } else rfxbuilder->edit_param = rfxbuilder->num_params;
1970 
1971   // dp
1972 
1973   rfxbuilder->spinbutton_param_dp = lives_standard_spin_button_new(_("Decimal _places: "),
1974                                     pnum >= 0 ? rfxbuilder->copy_params[pnum].dp : 0., 0., 16., 1., 1., 0, LIVES_BOX(dialog_vbox), NULL);
1975   rfxbuilder->param_dp_label = widget_opts.last_label;
1976 
1977   add_hsep_to_box(LIVES_BOX(dialog_vbox));
1978 
1979   // default val
1980 
1981   rfxbuilder->spinbutton_param_def = lives_standard_spin_button_new(_("_Default value:    "), 0., -LIVES_MAXINT, LIVES_MAXINT, 1.,
1982                                      1., 0, LIVES_BOX(dialog_vbox), NULL);
1983   rfxbuilder->param_def_label = widget_opts.last_label;
1984 
1985   // extra bits for string/string_list
1986 
1987   rfxbuilder->param_strdef_hbox = lives_hbox_new(FALSE, 0);
1988   lives_box_pack_start(LIVES_BOX(dialog_vbox), rfxbuilder->param_strdef_hbox, TRUE, TRUE, widget_opts.packing_height);
1989 
1990   rfxbuilder->param_strdef_button = lives_standard_button_new(DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
1991 
1992   lives_box_pack_start(LIVES_BOX(rfxbuilder->param_strdef_hbox), rfxbuilder->param_strdef_button, TRUE, TRUE,
1993                        widget_opts.packing_width);
1994 
1995   rfxbuilder->param_strlist_hbox = lives_hbox_new(FALSE, 0);
1996   lives_box_pack_start(LIVES_BOX(dialog_vbox), rfxbuilder->param_strlist_hbox, FALSE, FALSE, widget_opts.packing_width);
1997 
1998   rfxbuilder->param_def_combo = lives_standard_combo_new(_("_Default: "), NULL, LIVES_BOX(rfxbuilder->param_strlist_hbox), NULL);
1999 
2000   lives_label_set_mnemonic_widget(LIVES_LABEL(rfxbuilder->param_def_label), rfxbuilder->param_def_combo);
2001 
2002   if (pnum >= 0) {
2003     switch (rfxbuilder->copy_params[pnum].type) {
2004     case LIVES_PARAM_NUM:
2005       if (!rfxbuilder->copy_params[pnum].dp) {
2006         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def),
2007                                     (double)get_int_param(rfxbuilder->copy_params[pnum].def));
2008       } else {
2009         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def),
2010                                     get_double_param(rfxbuilder->copy_params[pnum].def));
2011       }
2012       break;
2013     case LIVES_PARAM_BOOL:
2014       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def),
2015                                   (double)get_bool_param(rfxbuilder->copy_params[pnum].def));
2016       break;
2017     case LIVES_PARAM_COLRGB24:
2018       get_colRGB24_param(rfxbuilder->copy_params[pnum].def, &rgb);
2019       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), (double)rgb.red);
2020       break;
2021     default:
2022       break;
2023     }
2024   }
2025 
2026   // group
2027 
2028   rfxbuilder->hbox_bg = lives_hbox_new(FALSE, 0);
2029   lives_box_pack_start(LIVES_BOX(dialog_vbox), rfxbuilder->hbox_bg, FALSE, FALSE, widget_opts.packing_height);
2030 
2031   rfxbuilder->spinbutton_param_group = lives_standard_spin_button_new((tmp = (_("Button _Group: "))), 0., 0., 16., 1.,
2032                                        1., 0, LIVES_BOX(rfxbuilder->hbox_bg),
2033                                        (tmp2 = (_("A non-zero value can be used to group radio buttons."))));
2034 
2035   if (pnum >= 0) {
2036     lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_group),
2037                                 (double)rfxbuilder->copy_params[pnum].group);
2038   }
2039 
2040   // min
2041 
2042   rfxbuilder->spinbutton_param_min = lives_standard_spin_button_new(_("_Minimum value: "), 0., -LIVES_MAXINT, LIVES_MAXINT, 1.,
2043                                      1., 0, LIVES_BOX(dialog_vbox), NULL);
2044   rfxbuilder->param_min_label = widget_opts.last_label;
2045 
2046   // max
2047 
2048   rfxbuilder->spinbutton_param_max = lives_standard_spin_button_new(_("Ma_ximum value: "), RFX_DEF_NUM_MAX, -LIVES_MAXINT,
2049                                      LIVES_MAXINT, 1., 1., 0, LIVES_BOX(dialog_vbox), NULL);
2050   rfxbuilder->param_max_label = widget_opts.last_label;
2051 
2052   // step size
2053 
2054   rfxbuilder->spinbutton_param_step = lives_standard_spin_button_new((tmp = (_("     _Step size:   "))), 1., 1.,
2055                                       LIVES_MAXINT, 1., 1., 0, LIVES_BOX(dialog_vbox),
2056                                       (tmp2 = (_("How much the parameter is adjusted when the spinbutton arrows are pressed."))));
2057   rfxbuilder->param_step_label = widget_opts.last_label;
2058   lives_free(tmp);
2059   lives_free(tmp2);
2060 
2061   // wrap
2062 
2063   rfxbuilder->param_wrap_hbox = lives_hbox_new(FALSE, 0);
2064   lives_box_pack_start(LIVES_BOX(dialog_vbox), rfxbuilder->param_wrap_hbox, TRUE, TRUE, widget_opts.packing_height);
2065 
2066   rfxbuilder->param_wrap_checkbutton = lives_standard_check_button_new((tmp = (_("_Wrap value"))), FALSE,
2067                                        LIVES_BOX(rfxbuilder->param_wrap_hbox),
2068                                        (tmp2 = (_("Whether the value wraps max->min and min->max."))));
2069 
2070   lives_free(tmp);
2071   lives_free(tmp2);
2072 
2073   lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->param_strdef_button), LIVES_WIDGET_CLICKED_SIGNAL,
2074                             LIVES_GUI_CALLBACK(on_code_clicked), (livespointer)rfxbuilder);
2075 
2076   lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->param_type_combo), LIVES_WIDGET_CHANGED_SIGNAL,
2077                             LIVES_GUI_CALLBACK(on_param_type_changed), (livespointer)rfxbuilder);
2078 
2079   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->spinbutton_param_dp), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
2080                                   LIVES_GUI_CALLBACK(after_param_dp_changed), (livespointer)rfxbuilder);
2081 
2082   rfxbuilder->def_spin_f = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->spinbutton_param_def),
2083                            LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
2084                            LIVES_GUI_CALLBACK(after_param_def_changed), (livespointer)rfxbuilder);
2085 
2086   rfxbuilder->min_spin_f = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->spinbutton_param_min),
2087                            LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
2088                            LIVES_GUI_CALLBACK(after_param_min_changed), (livespointer)rfxbuilder);
2089 
2090   rfxbuilder->max_spin_f = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rfxbuilder->spinbutton_param_max),
2091                            LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
2092                            LIVES_GUI_CALLBACK(after_param_max_changed), (livespointer)rfxbuilder);
2093 
2094   if (pnum >= 0) {
2095     switch (rfxbuilder->copy_params[pnum].type) {
2096     case LIVES_PARAM_NUM:
2097       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), rfxbuilder->copy_params[pnum].min);
2098       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), rfxbuilder->copy_params[pnum].max);
2099       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step),
2100                                   rfxbuilder->copy_params[pnum].step_size);
2101       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rfxbuilder->param_wrap_checkbutton),
2102                                      rfxbuilder->copy_params[pnum].wrap);
2103       break;
2104     case LIVES_PARAM_COLRGB24:
2105       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), (double)rgb.green);
2106       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), (double)rgb.blue);
2107       break;
2108     case LIVES_PARAM_STRING:
2109       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), rfxbuilder->copy_params[pnum].max);
2110       break;
2111     default:
2112       break;
2113     }
2114   }
2115   lives_widget_show_all(dialog);
2116   on_param_type_changed(LIVES_COMBO(rfxbuilder->param_type_combo), (livespointer)rfxbuilder);
2117   after_param_dp_changed(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_dp), (livespointer)rfxbuilder);
2118   return dialog;
2119 }
2120 
2121 
after_param_dp_changed(LiVESSpinButton * spinbutton,livespointer user_data)2122 static void after_param_dp_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
2123   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2124   const char *ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->param_type_combo));
2125   int dp;
2126 
2127   if (strcmp(ctext, "num")) return;
2128 
2129   dp = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
2130 
2131   lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), dp);
2132   lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), dp);
2133   lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), dp);
2134   lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step), dp);
2135 
2136   if (dp > 0) {
2137     double max = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max));
2138     double min = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min));
2139     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), -LIVES_MAXFLOAT, LIVES_MAXFLOAT);
2140     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), -LIVES_MAXFLOAT, LIVES_MAXFLOAT);
2141     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), -LIVES_MAXFLOAT, LIVES_MAXFLOAT);
2142     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step), 1. / (double)lives_10pow(dp),
2143                                 (max - min) > 1. ? (max - min) : 1.);
2144 
2145     lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_step), MAXFLOATLEN + dp);
2146     lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_def), MAXFLOATLEN + dp);
2147     lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_min), MAXFLOATLEN + dp);
2148     lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_max), MAXFLOATLEN + dp);
2149   } else {
2150     int max = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max));
2151     int min = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min));
2152     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), -LIVES_MAXINT, LIVES_MAXINT);
2153     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), -LIVES_MAXINT, LIVES_MAXINT);
2154     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), -LIVES_MAXINT, LIVES_MAXINT);
2155     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step), 1, (max - min) > 1 ? (max - min) : 1);
2156 
2157     lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_step), MAXINTLEN);
2158     lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_def), MAXINTLEN);
2159     lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_min), MAXINTLEN);
2160     lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_max), MAXINTLEN);
2161   }
2162 }
2163 
2164 
after_param_min_changed(LiVESSpinButton * spinbutton,livespointer user_data)2165 static void after_param_min_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
2166   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2167   int dp;
2168   const char *ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->param_type_combo));
2169 
2170   if (strcmp(ctext, "num")) return;
2171 
2172   lives_signal_handler_block(rfxbuilder->spinbutton_param_max, rfxbuilder->max_spin_f);
2173   lives_signal_handler_block(rfxbuilder->spinbutton_param_def, rfxbuilder->def_spin_f);
2174 
2175   if ((dp = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_dp))) > 0) {
2176     if (lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def)) <
2177         lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min))) {
2178       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def),
2179                                   lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)));
2180     }
2181 
2182     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max),
2183                                 lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)), LIVES_MAXFLOAT);
2184     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step),
2185                                 1. / (double)lives_10pow(dp), lives_spin_button_get_value
2186                                 (LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max)) -
2187                                 lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)));
2188 
2189   } else {
2190     if (lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def)) <
2191         lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min))) {
2192       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def),
2193                                   lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)));
2194     }
2195 
2196     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max),
2197                                 lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)),
2198                                 LIVES_MAXINT);
2199     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step), 1,
2200                                 lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max)) -
2201                                 lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)));
2202   }
2203 
2204   lives_signal_handler_unblock(rfxbuilder->spinbutton_param_max, rfxbuilder->max_spin_f);
2205   lives_signal_handler_unblock(rfxbuilder->spinbutton_param_def, rfxbuilder->def_spin_f);
2206 }
2207 
2208 
after_param_max_changed(LiVESSpinButton * spinbutton,livespointer user_data)2209 static void after_param_max_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
2210   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2211   int dp;
2212   const char *ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->param_type_combo));
2213 
2214   if (strcmp(ctext, "num")) return;
2215 
2216   lives_signal_handler_block(rfxbuilder->spinbutton_param_min, rfxbuilder->min_spin_f);
2217   lives_signal_handler_block(rfxbuilder->spinbutton_param_def, rfxbuilder->def_spin_f);
2218 
2219   if ((dp = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_dp))) > 0) {
2220     if (lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def)) >
2221         lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max))) {
2222       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def),
2223                                   lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max)));
2224     }
2225 
2226     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), -LIVES_MAXFLOAT,
2227                                 lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max)));
2228     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step),
2229                                 1. / (double)lives_10pow(dp),
2230                                 lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max)) -
2231                                 lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)));
2232   } else {
2233     if (lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def)) >
2234         lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max))) {
2235       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def),
2236                                   lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max)));
2237     }
2238 
2239     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), -LIVES_MAXINT,
2240                                 lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max)));
2241     lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step), 1,
2242                                 lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max))
2243                                 - lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min)));
2244   }
2245 
2246   lives_signal_handler_unblock(rfxbuilder->spinbutton_param_min, rfxbuilder->min_spin_f);
2247   lives_signal_handler_unblock(rfxbuilder->spinbutton_param_def, rfxbuilder->def_spin_f);
2248 }
2249 
2250 
after_param_def_changed(LiVESSpinButton * spinbutton,livespointer user_data)2251 static void after_param_def_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
2252   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2253   const char *ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->param_type_combo));
2254 
2255   if (strcmp(ctext, "num")) return;
2256 
2257   if (lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_dp))) {
2258     double dbl_def = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def));
2259     if (dbl_def < lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min))) {
2260       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), dbl_def);
2261     } else if (dbl_def > lives_spin_button_get_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max))) {
2262       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), dbl_def);
2263     }
2264   } else {
2265     int int_def = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def));
2266     if (int_def < lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min))) {
2267       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), (double)int_def);
2268     } else if (int_def > lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max))) {
2269       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), (double)int_def);
2270     }
2271   }
2272 }
2273 
2274 
on_param_type_changed(LiVESCombo * param_type_combo,livespointer user_data)2275 static void on_param_type_changed(LiVESCombo * param_type_combo, livespointer user_data) {
2276   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2277   int pnum = rfxbuilder->edit_param;
2278 
2279   const char *ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->param_type_combo));
2280 
2281   lives_widget_show(rfxbuilder->param_min_label);
2282   lives_widget_show(rfxbuilder->param_max_label);
2283   lives_widget_show(rfxbuilder->spinbutton_param_min);
2284   lives_widget_show(rfxbuilder->spinbutton_param_max);
2285   lives_widget_show(rfxbuilder->spinbutton_param_dp);
2286   lives_widget_show(rfxbuilder->spinbutton_param_def);
2287   lives_widget_show(rfxbuilder->param_dp_label);
2288   lives_widget_show(rfxbuilder->param_def_label);
2289 
2290   lives_widget_hide(rfxbuilder->param_strdef_hbox);
2291   lives_widget_hide(rfxbuilder->param_strlist_hbox);
2292   lives_widget_hide(rfxbuilder->hbox_bg);
2293   lives_widget_hide(rfxbuilder->spinbutton_param_step);
2294   lives_widget_hide(rfxbuilder->param_step_label);
2295   lives_widget_hide(rfxbuilder->param_wrap_hbox);
2296 
2297   if (pnum < 0) return;
2298 
2299   if (!strcmp(ctext, "string_list")) {
2300     int defindex;
2301 
2302     if (rfxbuilder->copy_params[pnum].type != LIVES_PARAM_STRING_LIST) {
2303       if (rfxbuilder->copy_params[pnum].def) {
2304         lives_free(rfxbuilder->copy_params[pnum].def);
2305       }
2306       rfxbuilder->copy_params[pnum].def = lives_malloc(sizint);
2307       set_int_param(rfxbuilder->copy_params[pnum].def, 0);
2308     }
2309 
2310     rfxbuilder->copy_params[pnum].type = LIVES_PARAM_STRING_LIST;
2311     defindex = get_int_param(rfxbuilder->copy_params[pnum].def);
2312 
2313     lives_combo_populate(LIVES_COMBO(rfxbuilder->param_def_combo), rfxbuilder->copy_params[pnum].list);
2314 
2315     if (!rfxbuilder->copy_params[pnum].list || defindex > lives_list_length(rfxbuilder->copy_params[pnum].list)) {
2316       set_int_param(rfxbuilder->copy_params[pnum].def, (defindex = 0));
2317     }
2318 
2319     if (rfxbuilder->copy_params[pnum].list) {
2320       lives_combo_set_active_string(LIVES_COMBO(rfxbuilder->param_def_combo),
2321                                     (char *)lives_list_nth_data(rfxbuilder->copy_params[pnum].list, defindex));
2322     }
2323 
2324     lives_widget_hide(rfxbuilder->spinbutton_param_dp);
2325     lives_widget_hide(rfxbuilder->param_dp_label);
2326     lives_widget_hide(rfxbuilder->spinbutton_param_min);
2327     lives_widget_hide(rfxbuilder->param_min_label);
2328     lives_widget_hide(rfxbuilder->spinbutton_param_def);
2329     lives_widget_hide(rfxbuilder->param_def_label);
2330     lives_widget_hide(rfxbuilder->spinbutton_param_max);
2331     lives_widget_hide(rfxbuilder->param_max_label);
2332 
2333     lives_button_set_label(LIVES_BUTTON(rfxbuilder->param_strdef_button), (_("Set _values")));
2334     lives_widget_show(rfxbuilder->param_strdef_hbox);
2335     lives_widget_show(rfxbuilder->param_strlist_hbox);
2336   } else {
2337     if (!strcmp(ctext, "num")) {
2338       rfxbuilder->copy_params[pnum].type = LIVES_PARAM_NUM;
2339       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_def_label), (_("_Default value:    ")));
2340       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_min_label), (_("_Minimum value: ")));
2341       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_max_label), (_("Ma_ximum value: ")));
2342       lives_widget_show(rfxbuilder->spinbutton_param_step);
2343       lives_widget_show(rfxbuilder->param_step_label);
2344       lives_widget_show_all(rfxbuilder->param_wrap_hbox);
2345 
2346       after_param_dp_changed(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_dp), (livespointer)rfxbuilder);
2347 
2348       if (pnum < 0) {
2349         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), 0.);
2350         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), RFX_DEF_NUM_MAX);
2351         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), 0.);
2352         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_step), 1.);
2353       }
2354     } else if (!strcmp(ctext, "bool")) {
2355       rfxbuilder->copy_params[pnum].type = LIVES_PARAM_BOOL;
2356       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_def_label), (_("_Default value:    ")));
2357       lives_widget_hide(rfxbuilder->param_min_label);
2358       lives_widget_hide(rfxbuilder->param_max_label);
2359       lives_widget_hide(rfxbuilder->spinbutton_param_min);
2360       lives_widget_hide(rfxbuilder->spinbutton_param_max);
2361       lives_widget_hide(rfxbuilder->spinbutton_param_dp);
2362       lives_widget_hide(rfxbuilder->param_dp_label);
2363       lives_widget_show(rfxbuilder->hbox_bg);
2364       lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), 0, 1);
2365       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), 0);
2366       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), 0);
2367       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), 0);
2368     } else if (!strcmp(ctext, "colRGB24")) {
2369       rfxbuilder->copy_params[pnum].type = LIVES_PARAM_COLRGB24;
2370       lives_widget_hide(rfxbuilder->spinbutton_param_dp);
2371       lives_widget_hide(rfxbuilder->param_dp_label);
2372       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_def_label), (_("Default _Red:  ")));
2373       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_min_label), (_("Default _Green:")));
2374       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_max_label), (_("Default _Blue: ")));
2375       lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_def), 4);
2376       lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_min), 4);
2377       lives_entry_set_width_chars(LIVES_ENTRY(rfxbuilder->spinbutton_param_max), 4);
2378       lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), 0, 255);
2379       lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), 0, 255);
2380       lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), 0, 255);
2381       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), 0);
2382       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), 0);
2383       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), 0);
2384       if (pnum < 0) {
2385         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), 0.);
2386         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_min), 0.);
2387         lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), 0.);
2388       }
2389     } else if (!strcmp(ctext, "string")) {
2390       rfxbuilder->copy_params[pnum].type = LIVES_PARAM_STRING;
2391       lives_widget_hide(rfxbuilder->spinbutton_param_dp);
2392       lives_widget_hide(rfxbuilder->param_dp_label);
2393       lives_widget_hide(rfxbuilder->spinbutton_param_min);
2394       lives_widget_hide(rfxbuilder->param_min_label);
2395       lives_widget_hide(rfxbuilder->spinbutton_param_def);
2396 
2397       lives_button_set_label(LIVES_BUTTON(rfxbuilder->param_strdef_button), (_("Set _default")));
2398       lives_widget_show(rfxbuilder->param_strdef_hbox);
2399       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_def_label), (_("Default value:  ")));
2400       lives_label_set_text(LIVES_LABEL(rfxbuilder->param_max_label), (_("Maximum length (chars): ")));
2401       lives_spin_button_set_range(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), 1, RFX_MAXSTRINGLEN);
2402       if (pnum < 0) lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), RFX_TEXT_MAGIC);
2403       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_def), 0);
2404       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max), 0);
2405 
2406       if (!rfxbuilder->copy_params[pnum].def) {
2407         rfxbuilder->copy_params[pnum].def = lives_strdup("");
2408 	// *INDENT-OFF*
2409       }}}
2410   // *INDENT-ONt*
2411 }
2412 
2413 
make_param_window_dialog(int pnum,rfx_build_window_t * rfxbuilder)2414 LiVESWidget *make_param_window_dialog(int pnum, rfx_build_window_t *rfxbuilder) {
2415   LiVESWidget *dialog;
2416   LiVESWidget *dialog_vbox;
2417 
2418   LiVESList *kwlist = NULL;
2419   LiVESList *splist = NULL;
2420   LiVESList *spsublist = NULL;
2421 
2422   char *kw = NULL;
2423   char *sp = NULL;
2424   char *spsub = NULL;
2425   char *rest = NULL;
2426   char *title;
2427 
2428   if (pnum < 0) {
2429     title = (_("New RFX Parameter Window Hint"));
2430   } else {
2431     title = (_("Edit RFX Parameter Window Hint"));
2432   }
2433 
2434   dialog = lives_standard_dialog_new(title, TRUE, -1, -1);
2435   lives_free(title);
2436 
2437   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
2438 
2439   if (pnum >= 0) {
2440     kw = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry[pnum])));
2441     if (!strcmp(kw, "layout")) {
2442       rest = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry2[pnum])));
2443     } else if (!strcmp(kw, "special")) {
2444       int numtok = get_token_count(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry2[pnum])),
2445                                    (int)rfxbuilder->field_delim[0]);
2446       char **array = lives_strsplit(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->entry2[pnum])), rfxbuilder->field_delim, 3);
2447       sp = lives_strdup(array[0]);
2448       if (!strcmp(sp, "framedraw")) {
2449         if (numtok > 1) {
2450           spsub = lives_strdup(array[1]);
2451         }
2452         if (numtok > 2) {
2453           rest = lives_strdup(array[2]);
2454         }
2455       } else {
2456         if (numtok > 1) {
2457           rest = lives_strdup(array[1]);
2458         }
2459       }
2460       lives_strfreev(array);
2461     }
2462   }
2463 
2464   // kw
2465   kwlist = lives_list_append(kwlist, (livespointer)"layout");
2466   kwlist = lives_list_append(kwlist, (livespointer)"special");
2467 
2468   rfxbuilder->paramw_kw_combo = lives_standard_combo_new(_("_Keyword:         "), kwlist, LIVES_BOX(dialog_vbox), NULL);
2469   lives_list_free(kwlist);
2470 
2471   if (pnum >= 0 && kw) {
2472     lives_combo_set_active_string(LIVES_COMBO(rfxbuilder->paramw_kw_combo), kw);
2473   }
2474 
2475   // type
2476   splist = lives_list_append(splist, (livespointer)"aspect");
2477   splist = lives_list_append(splist, (livespointer)"framedraw");
2478   splist = lives_list_append(splist, (livespointer)"fileread");
2479   splist = lives_list_append(splist, (livespointer)"filewrite");
2480   splist = lives_list_append(splist, (livespointer)"password");
2481   if (rfxbuilder->type == RFX_BUILD_TYPE_EFFECT2) {
2482     splist = lives_list_append(splist, (livespointer)"mergealign");
2483   }
2484 
2485   rfxbuilder->paramw_sp_combo = lives_standard_combo_new(_("Special _Type:         "), splist, LIVES_BOX(dialog_vbox), NULL);
2486   lives_list_free(splist);
2487 
2488   if (pnum >= 0 && sp) {
2489     lives_combo_set_active_string(LIVES_COMBO(rfxbuilder->paramw_sp_combo), sp);
2490   }
2491 
2492   // subtype
2493 
2494   spsublist = lives_list_append(spsublist, (livespointer)"rectdemask");
2495   spsublist = lives_list_append(spsublist, (livespointer)"multirect");
2496   spsublist = lives_list_append(spsublist, (livespointer)"singlepoint");
2497   spsublist = lives_list_append(spsublist, (livespointer)"scaledpoint");
2498 
2499   rfxbuilder->paramw_spsub_combo = lives_standard_combo_new(_("Special _Subtype:         "), spsublist, LIVES_BOX(dialog_vbox),
2500                                    NULL);
2501   lives_list_free(spsublist);
2502 
2503   if (pnum >= 0 && spsub) {
2504     lives_combo_set_active_string(LIVES_COMBO(rfxbuilder->paramw_spsub_combo), spsub);
2505   }
2506 
2507   // paramwindow rest
2508 
2509   rfxbuilder->paramw_rest_entry = lives_standard_entry_new(_("Row:    "), pnum >= 0 && rest ? rest : NULL,
2510                                   -1, -1, LIVES_BOX(dialog_vbox), NULL);
2511   rfxbuilder->paramw_rest_label = widget_opts.last_label;
2512 
2513   if (kw) lives_free(kw);
2514   if (sp) lives_free(sp);
2515   if (spsub) lives_free(spsub);
2516   if (rest) lives_free(rest);
2517 
2518   lives_widget_grab_focus(rfxbuilder->paramw_rest_entry);
2519 
2520   lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->paramw_kw_combo), LIVES_WIDGET_CHANGED_SIGNAL,
2521                        LIVES_GUI_CALLBACK(on_paramw_kw_changed), (livespointer)rfxbuilder);
2522 
2523   lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->paramw_sp_combo), LIVES_WIDGET_CHANGED_SIGNAL,
2524                        LIVES_GUI_CALLBACK(on_paramw_sp_changed), (livespointer)rfxbuilder);
2525 
2526   lives_signal_sync_connect(LIVES_GUI_OBJECT(rfxbuilder->paramw_spsub_combo), LIVES_WIDGET_CHANGED_SIGNAL,
2527                        LIVES_GUI_CALLBACK(on_paramw_spsub_changed), (livespointer)rfxbuilder);
2528 
2529   lives_widget_show_all(dialog);
2530   on_paramw_kw_changed(LIVES_COMBO(rfxbuilder->paramw_kw_combo), (livespointer)rfxbuilder);
2531 
2532   return dialog;
2533 }
2534 
2535 
on_paramw_kw_changed(LiVESCombo * combo,livespointer user_data)2536 static void on_paramw_kw_changed(LiVESCombo * combo, livespointer user_data) {
2537   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2538   const char *ctext = lives_combo_get_active_text(combo);
2539 
2540   if (!strcmp(ctext, "special")) {
2541     lives_widget_show_all(lives_widget_get_parent(rfxbuilder->paramw_sp_combo));
2542     on_paramw_sp_changed(LIVES_COMBO(rfxbuilder->paramw_sp_combo), (livespointer)rfxbuilder);
2543     lives_widget_grab_focus(lives_combo_get_entry(LIVES_COMBO(rfxbuilder->paramw_sp_combo)));
2544   } else {
2545     lives_label_set_text(LIVES_LABEL(rfxbuilder->paramw_rest_label), (_("Row:    ")));
2546     lives_widget_hide(lives_widget_get_parent(rfxbuilder->paramw_sp_combo));
2547     lives_widget_hide(lives_widget_get_parent(rfxbuilder->paramw_spsub_combo));
2548     lives_widget_grab_focus(rfxbuilder->paramw_rest_entry);
2549   }
2550 }
2551 
2552 
paramw_set_splabel(LiVESLabel * label,int npars)2553 static void paramw_set_splabel(LiVESLabel * label, int npars) {
2554   char *tmpx;
2555 
2556   if (npars == 1) {
2557     tmpx = (_("Linked parameter:    "));
2558   } else {
2559     char example[256] = "0";
2560     for (register int i = 1; i < npars; i++) {
2561       char *xnew = lives_strdup_printf("|%d", i);
2562       lives_strappend(example, 256, xnew);
2563       lives_free(xnew);
2564     }
2565     tmpx = lives_strdup_printf(_("Linked parameters [%d] (example %s) :"), npars, example);
2566   }
2567   lives_label_set_text(label, tmpx);
2568   lives_free(tmpx);
2569 }
2570 
2571 
on_paramw_sp_changed(LiVESCombo * combo,livespointer user_data)2572 static void on_paramw_sp_changed(LiVESCombo * combo, livespointer user_data) {
2573   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2574   int npars;
2575   const char *ctext = lives_combo_get_active_text(combo);
2576 
2577   if (!strcmp(ctext, "framedraw")) {
2578     lives_widget_show_all(lives_widget_get_parent(rfxbuilder->paramw_spsub_combo));
2579     on_paramw_spsub_changed(LIVES_COMBO(rfxbuilder->paramw_spsub_combo), (livespointer)rfxbuilder);
2580     lives_widget_grab_focus(lives_combo_get_entry(LIVES_COMBO(rfxbuilder->paramw_spsub_combo)));
2581   } else {
2582     if (!strcmp(ctext, "fileread") || !strcmp(ctext, "filewrite") || !strcmp(ctext, "password")) npars = 1;
2583     else npars = 2;
2584     paramw_set_splabel(LIVES_LABEL(rfxbuilder->paramw_rest_label), npars);
2585     lives_widget_hide(lives_widget_get_parent(rfxbuilder->paramw_spsub_combo));
2586   }
2587 
2588   lives_widget_grab_focus(rfxbuilder->paramw_rest_entry);
2589 }
2590 
2591 
on_paramw_spsub_changed(LiVESCombo * combo,livespointer user_data)2592 static void on_paramw_spsub_changed(LiVESCombo * combo, livespointer user_data) {
2593   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2594   const char *ctext = lives_combo_get_active_text(combo);
2595   int npars = 0;
2596 
2597   if (!strcmp(ctext, "rectdemask") || !strcmp(ctext, "multirect")) {
2598     npars = 4;
2599   } else if (!strcmp(ctext, "singlepoint")) {
2600     npars = 2;
2601   } else if (!strcmp(ctext, "scaledpoint")) {
2602     npars = 3;
2603   }
2604   paramw_set_splabel(LIVES_LABEL(rfxbuilder->paramw_rest_label), npars);
2605 }
2606 
2607 
make_trigger_dialog(int tnum,rfx_build_window_t * rfxbuilder)2608 LiVESWidget *make_trigger_dialog(int tnum, rfx_build_window_t *rfxbuilder) {
2609   LiVESWidget *dialog;
2610   LiVESWidget *dialog_vbox;
2611   LiVESWidget *combo;
2612   LiVESWidget *scrolledwindow;
2613 
2614   LiVESList *whenlist = NULL;
2615 
2616   char *title;
2617 
2618   int woat;
2619   int i;
2620 
2621   if (tnum < 0) {
2622     title = (_("New RFX Trigger"));
2623   } else {
2624     title = (_("Edit RFX Trigger"));
2625   }
2626 
2627   dialog = lives_standard_dialog_new(title, TRUE, PREF_RFXDIALOG_W, PREF_RFXDIALOG_H);
2628   lives_free(title);
2629 
2630   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
2631 
2632   // when
2633 
2634   if (tnum >= 0) whenlist = lives_list_append(whenlist, (livespointer)(rfxbuilder->copy_triggers[tnum].when ?
2635                               lives_strdup_printf("%d", rfxbuilder->copy_triggers[tnum].
2636                                   when - 1) : "init"));
2637   else {
2638     if (!rfxbuilder->has_init_trigger) {
2639       whenlist = lives_list_append(whenlist, (livespointer)"init");
2640     }
2641     for (i = 0; i < rfxbuilder->num_params; i++) {
2642       if (!rfxbuilder->params[i].onchange) {
2643         whenlist = lives_list_append(whenlist, lives_strdup_printf("%d", i));
2644       }
2645     }
2646   }
2647 
2648   combo = lives_standard_combo_new(_("When:         "), whenlist, LIVES_BOX(dialog_vbox), NULL);
2649   lives_list_free(whenlist);
2650 
2651   rfxbuilder->trigger_when_entry = lives_combo_get_entry(LIVES_COMBO(combo));
2652 
2653   // code area
2654   rfxbuilder->code_textview = lives_text_view_new();
2655   lives_text_view_set_editable(LIVES_TEXT_VIEW(rfxbuilder->code_textview), TRUE);
2656   lives_text_view_set_wrap_mode(LIVES_TEXT_VIEW(rfxbuilder->code_textview), LIVES_WRAP_WORD);
2657   lives_text_view_set_cursor_visible(LIVES_TEXT_VIEW(rfxbuilder->code_textview), TRUE);
2658 
2659   woat = widget_opts.apply_theme;
2660   widget_opts.apply_theme = 0;
2661   widget_opts.expand = LIVES_EXPAND_EXTRA; // prevent centering
2662   scrolledwindow = lives_standard_scrolled_window_new(RFX_WINSIZE_H * 2. / 3., RFX_WINSIZE_V / 4., rfxbuilder->code_textview);
2663   widget_opts.expand = LIVES_EXPAND_DEFAULT;
2664   widget_opts.apply_theme = woat;
2665 
2666   if (palette->style & STYLE_1) {
2667     lives_widget_set_base_color(rfxbuilder->code_textview, LIVES_WIDGET_STATE_NORMAL, &palette->white);
2668     lives_widget_set_text_color(rfxbuilder->code_textview, LIVES_WIDGET_STATE_NORMAL, &palette->black);
2669   }
2670 
2671   lives_box_pack_start(LIVES_BOX(dialog_vbox), scrolledwindow, TRUE, TRUE, 0);
2672 
2673   if (tnum >= 0) {
2674     lives_text_view_set_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview),
2675                              rfxbuilder->copy_triggers[tnum].code, -1);
2676   }
2677 
2678   if (tnum >= 0 || rfxbuilder->num_params == 0) lives_widget_grab_focus(rfxbuilder->code_textview);
2679 
2680   lives_widget_show_all(dialog);
2681 
2682   return dialog;
2683 }
2684 
2685 
on_code_clicked(LiVESButton * button,livespointer user_data)2686 static void on_code_clicked(LiVESButton * button, livespointer user_data) {
2687   LiVESWidget *dialog;
2688   LiVESWidget *dialog_vbox;
2689   LiVESWidget *cancelbutton;
2690   LiVESWidget *okbutton;
2691   LiVESWidget *scrolledwindow;
2692 
2693   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2694 
2695   int woat;
2696 
2697   char *tmpx;
2698 
2699   dialog = lives_standard_dialog_new(NULL, FALSE, PREF_RFXDIALOG_W, PREF_RFXDIALOG_H);
2700 
2701   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
2702 
2703   // code area
2704   rfxbuilder->code_textview = lives_text_view_new();
2705 
2706   woat = widget_opts.apply_theme;
2707   widget_opts.apply_theme = 0;
2708   widget_opts.expand = LIVES_EXPAND_EXTRA; // prevent centering
2709   scrolledwindow = lives_standard_scrolled_window_new(RFX_WINSIZE_H, RFX_WINSIZE_V, rfxbuilder->code_textview);
2710   widget_opts.expand = LIVES_EXPAND_DEFAULT;
2711   widget_opts.apply_theme = woat;
2712 
2713   if (palette->style & STYLE_1) {
2714     lives_widget_set_base_color(rfxbuilder->code_textview, LIVES_WIDGET_STATE_NORMAL, &palette->white);
2715     lives_widget_set_text_color(rfxbuilder->code_textview, LIVES_WIDGET_STATE_NORMAL, &palette->black);
2716   }
2717 
2718   lives_box_pack_start(LIVES_BOX(dialog_vbox), scrolledwindow, TRUE, TRUE, 0);
2719 
2720   lives_widget_object_ref(lives_scrolled_window_get_hadjustment(LIVES_SCROLLED_WINDOW(scrolledwindow)));
2721   lives_widget_object_ref(lives_scrolled_window_get_vadjustment(LIVES_SCROLLED_WINDOW(scrolledwindow)));
2722 
2723   lives_text_view_set_editable(LIVES_TEXT_VIEW(rfxbuilder->code_textview), TRUE);
2724   lives_text_view_set_wrap_mode(LIVES_TEXT_VIEW(rfxbuilder->code_textview), LIVES_WRAP_WORD);
2725   lives_text_view_set_cursor_visible(LIVES_TEXT_VIEW(rfxbuilder->code_textview), TRUE);
2726 
2727   lives_widget_grab_focus(rfxbuilder->code_textview);
2728 
2729   // TODO !!
2730   /*  if (glib_major_version>=2&&glib_minor_version>=4) {
2731       lives_text_view_set_accepts_tab (LIVES_TEXT_VIEW (rfxbuilder->code_textview),TRUE);
2732     } */
2733 
2734   if (button == LIVES_BUTTON(rfxbuilder->pre_button)) {
2735     rfxbuilder->codetype = RFX_CODE_TYPE_PRE;
2736     lives_window_set_title(LIVES_WINDOW(dialog), _("Pre Loop Code"));
2737     lives_text_view_set_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview),
2738                              rfxbuilder->pre_code, -1);
2739   }
2740 
2741   else if (button == LIVES_BUTTON(rfxbuilder->loop_button)) {
2742     rfxbuilder->codetype = RFX_CODE_TYPE_LOOP;
2743     lives_window_set_title(LIVES_WINDOW(dialog), _("Loop Code"));
2744     lives_text_view_set_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview),
2745                              rfxbuilder->loop_code, -1);
2746   }
2747 
2748   else if (button == LIVES_BUTTON(rfxbuilder->post_button)) {
2749     rfxbuilder->codetype = RFX_CODE_TYPE_POST;
2750     lives_window_set_title(LIVES_WINDOW(dialog), _("Post Loop Code"));
2751     lives_text_view_set_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview),
2752                              rfxbuilder->post_code, -1);
2753   }
2754 
2755   else if (button == LIVES_BUTTON(rfxbuilder->param_strdef_button)) {
2756     if (rfxbuilder->copy_params[rfxbuilder->edit_param].type != LIVES_PARAM_STRING_LIST) {
2757       int len, maxlen = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_param_max));
2758 
2759       if ((len = strlen((char *)rfxbuilder->copy_params[rfxbuilder->edit_param].def)) > maxlen) len = maxlen;
2760 
2761       rfxbuilder->codetype = RFX_CODE_TYPE_STRDEF;
2762       lives_window_set_title(LIVES_WINDOW(dialog), (tmpx = lives_strdup_printf
2763                              (_("Default text (max length %d)"), maxlen)));
2764       lives_free(tmpx);
2765       lives_text_view_set_wrap_mode(LIVES_TEXT_VIEW(rfxbuilder->code_textview), LIVES_WRAP_WORD);
2766 
2767       lives_text_view_set_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview),
2768                                (char *)rfxbuilder->copy_params[rfxbuilder->edit_param].def, len);
2769     } else {
2770       LiVESTextIter start_iter;
2771       char *string = lives_strdup(""), *string_new;
2772       register int i;
2773 
2774       rfxbuilder->codetype = RFX_CODE_TYPE_STRING_LIST;
2775       lives_window_set_title(LIVES_WINDOW(dialog), (tmpx = (_("Enter values, one per line"))));
2776       lives_free(tmpx);
2777       if (rfxbuilder->copy_params[rfxbuilder->edit_param].list) {
2778         for (i = 0; i < lives_list_length(rfxbuilder->copy_params[rfxbuilder->edit_param].list); i++) {
2779           string_new = lives_strconcat(string, (char *)lives_list_nth_data(rfxbuilder->copy_params[rfxbuilder->edit_param].list, i),
2780                                        "\n", NULL);
2781           if (string != string_new) lives_free(string);
2782           string = string_new;
2783         }
2784         lives_text_view_set_text(LIVES_TEXT_VIEW(rfxbuilder->code_textview), string, strlen(string) - 1);
2785         lives_free(string);
2786         lives_text_buffer_get_start_iter(lives_text_view_get_buffer(LIVES_TEXT_VIEW(rfxbuilder->code_textview)), &start_iter);
2787         lives_text_buffer_place_cursor(lives_text_view_get_buffer(LIVES_TEXT_VIEW(rfxbuilder->code_textview)), &start_iter);
2788       }
2789     }
2790   }
2791 
2792   cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,  LIVES_RESPONSE_CANCEL);
2793 
2794   okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL, LIVES_RESPONSE_OK);
2795 
2796   lives_widget_set_can_default(okbutton, TRUE);
2797 
2798   lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
2799 			    LIVES_GUI_CALLBACK(on_code_ok), user_data);
2800 
2801   lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
2802                        LIVES_GUI_CALLBACK(lives_general_button_clicked), NULL);
2803 
2804   lives_widget_show_all(dialog);
2805 }
2806 
2807 
on_rfxbuilder_ok(LiVESButton * button,livespointer user_data)2808 static void on_rfxbuilder_ok(LiVESButton * button, livespointer user_data) {
2809   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2810 
2811   if (!perform_rfxbuilder_checks(rfxbuilder)) return;
2812   if (!rfxbuilder_to_script(rfxbuilder)) return;
2813 
2814   lives_general_button_clicked(button, NULL);
2815   rfxbuilder_destroy(rfxbuilder);
2816 }
2817 
2818 
on_rfxbuilder_cancel(LiVESButton * button,livespointer user_data)2819 static void on_rfxbuilder_cancel(LiVESButton * button, livespointer user_data) {
2820   rfx_build_window_t *rfxbuilder = (rfx_build_window_t *)user_data;
2821 
2822   lives_general_button_clicked(button, NULL);
2823   rfxbuilder_destroy(rfxbuilder);
2824 }
2825 
2826 
rfxbuilder_destroy(rfx_build_window_t * rfxbuilder)2827 static void rfxbuilder_destroy(rfx_build_window_t *rfxbuilder) {
2828   register int i;
2829 
2830   for (i = 0; i < rfxbuilder->num_reqs; i++) {
2831     lives_free(rfxbuilder->reqs[i]);
2832   }
2833   for (i = 0; i < rfxbuilder->num_params; i++) {
2834     lives_free(rfxbuilder->params[i].name);
2835     lives_free(rfxbuilder->params[i].label);
2836     if (rfxbuilder->params[i].type == LIVES_PARAM_STRING_LIST) {
2837       if (rfxbuilder->params[i].list)lives_list_free(rfxbuilder->params[i].list);
2838     }
2839     lives_free(rfxbuilder->params[i].def);
2840   }
2841   if (rfxbuilder->num_params) {
2842     lives_free(rfxbuilder->params);
2843   }
2844   for (i = 0; i < rfxbuilder->num_paramw_hints; i++) {
2845     lives_free(rfxbuilder->paramw_hints[i]);
2846   }
2847   for (i = 0; i < rfxbuilder->num_triggers; i++) {
2848     lives_free(rfxbuilder->triggers[i].code);
2849   }
2850   if (rfxbuilder->num_triggers) {
2851     lives_free(rfxbuilder->triggers);
2852   }
2853 
2854   lives_free(rfxbuilder->pre_code);
2855   lives_free(rfxbuilder->loop_code);
2856   lives_free(rfxbuilder->post_code);
2857 
2858   lives_free(rfxbuilder->field_delim);
2859 
2860   if (rfxbuilder->script_name) {
2861     lives_free(rfxbuilder->script_name);
2862   }
2863   if (rfxbuilder->oname) {
2864     lives_free(rfxbuilder->oname);
2865   }
2866   lives_free(rfxbuilder->rfx_version);
2867 
2868   lives_free(rfxbuilder);
2869 }
2870 
2871 
perform_rfxbuilder_checks(rfx_build_window_t * rfxbuilder)2872 boolean perform_rfxbuilder_checks(rfx_build_window_t *rfxbuilder) {
2873   char *name = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->name_entry)));
2874 
2875   if (!strlen(name)) {
2876     do_error_dialog(_("\n\nName must not be blank.\n"));
2877     lives_widget_grab_focus(rfxbuilder->name_entry);
2878     lives_free(name);
2879     return FALSE;
2880   }
2881   if (get_token_count(name, ' ') > 1) {
2882     do_error_dialog(_("\n\nName must not contain spaces.\n"));
2883     lives_free(name);
2884     return FALSE;
2885   }
2886   if (!strlen(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->menu_text_entry)))) {
2887     do_error_dialog(_("\n\nMenu text must not be blank.\n"));
2888     lives_widget_grab_focus(rfxbuilder->menu_text_entry);
2889     lives_free(name);
2890     return FALSE;
2891   }
2892   if (!strlen(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->action_desc_entry))) &&
2893       rfxbuilder->type != RFX_BUILD_TYPE_UTILITY) {
2894     do_error_dialog(_("\n\nAction description must not be blank.\n"));
2895     lives_widget_grab_focus(rfxbuilder->action_desc_entry);
2896     lives_free(name);
2897     return FALSE;
2898   }
2899   if (!strlen(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->author_entry)))) {
2900     do_error_dialog(_("\n\nAuthor must not be blank.\n"));
2901     lives_widget_grab_focus(rfxbuilder->author_entry);
2902     lives_free(name);
2903     return FALSE;
2904   }
2905 
2906   if (rfxbuilder->mode != RFX_BUILDER_MODE_NEW && !(rfxbuilder->mode == RFX_BUILDER_MODE_EDIT &&
2907       !strcmp(rfxbuilder->oname, name))) {
2908     if (find_rfx_plugin_by_name(name, RFX_STATUS_TEST) > -1 || find_rfx_plugin_by_name
2909         (name, RFX_STATUS_CUSTOM) > -1 || find_rfx_plugin_by_name(name, RFX_STATUS_BUILTIN) > -1) {
2910       do_error_dialog(_("\n\nThere is already a plugin with this name.\nName must be unique.\n"));
2911       lives_free(name);
2912       return FALSE;
2913     }
2914   }
2915 
2916   if (!strlen(rfxbuilder->loop_code) && rfxbuilder->type != RFX_BUILD_TYPE_UTILITY) {
2917     do_error_dialog(_("\n\nLoop code should not be blank.\n"));
2918     lives_free(name);
2919     return FALSE;
2920   }
2921 
2922   if (rfxbuilder->num_triggers == 0 && rfxbuilder->type == RFX_BUILD_TYPE_UTILITY) {
2923     do_error_dialog(_("\n\nTrigger code should not be blank for a utility.\n"));
2924     lives_free(name);
2925     return FALSE;
2926   }
2927 
2928   lives_free(name);
2929   return TRUE;
2930 }
2931 
2932 
perform_param_checks(rfx_build_window_t * rfxbuilder,int index,int rows)2933 boolean perform_param_checks(rfx_build_window_t *rfxbuilder, int index, int rows) {
2934   register int i;
2935 
2936   if (!strlen(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->param_name_entry)))) {
2937     do_error_dialog(_("\n\nParameter name must not be blank.\n"));
2938     lives_widget_grab_focus(rfxbuilder->param_name_entry);
2939     return FALSE;
2940   }
2941   for (i = 0; i < rows; i++) {
2942     if (i != index && !(strcmp(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->param_name_entry)),
2943                                rfxbuilder->copy_params[i].name))) {
2944       do_error_dialog(_("\n\nDuplicate parameter name detected. Parameter names must be unique in a plugin.\n\n"));
2945       return FALSE;
2946     }
2947   }
2948   return TRUE;
2949 }
2950 
2951 
rfxbuilder_to_script(rfx_build_window_t * rfxbuilder)2952 boolean rfxbuilder_to_script(rfx_build_window_t *rfxbuilder) {
2953   FILE *sfile;
2954   lives_colRGB48_t rgb;
2955   char **array;
2956 
2957   double stepwrap;
2958 
2959   uint32_t props;
2960 
2961   const char *name = lives_entry_get_text(LIVES_ENTRY(rfxbuilder->name_entry));
2962 
2963   char *script_file, *script_file_dir;
2964   char *script_name = lives_strdup_printf("%s.%s", name, RFXBUILDER_SCRIPT_SUFFIX);
2965   char *new_name;
2966   char *buf, *tmp, *tmp2;
2967   const char *ctext;
2968 
2969   int retval, i, j;
2970 
2971   if (rfxbuilder->mode != RFX_BUILDER_MODE_EDIT) {
2972     if (!(new_name = prompt_for_script_name(script_name, RFX_STATUS_TEST))) {
2973       lives_free(script_name);
2974       return FALSE;
2975     }
2976     lives_free(script_name);
2977     script_name = new_name;
2978   }
2979 
2980   script_file_dir = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS, NULL);
2981 
2982   if (!lives_file_test(script_file_dir, LIVES_FILE_TEST_IS_DIR)) {
2983     if (lives_mkdir_with_parents(script_file_dir, capable->umask) == -1) {
2984       lives_free(script_name);
2985       return FALSE;
2986     }
2987   }
2988 
2989   script_file = lives_strdup_printf("%s%s", script_file_dir, script_name);
2990   lives_free(script_file_dir);
2991   lives_free(script_name);
2992 
2993   d_print(_("Writing script file %s..."), script_file);
2994 
2995   if (!check_file(script_file, TRUE)) {
2996     lives_free(script_file);
2997     d_print_failed();
2998     return FALSE;
2999   }
3000 
3001   do {
3002     retval = 0;
3003     if (!(sfile = fopen(script_file, "w"))) {
3004       widget_opts.transient = LIVES_WINDOW(rfxbuilder->dialog);
3005       retval = do_write_failed_error_s_with_retry(script_file, lives_strerror(errno));
3006       widget_opts.transient = NULL;
3007 
3008       if (retval == LIVES_RESPONSE_CANCEL) {
3009         lives_free(script_file);
3010         d_print_failed();
3011         return FALSE;
3012       }
3013     } else {
3014       THREADVAR(write_failed) = FALSE;
3015 
3016       lives_fputs("Script file generated from LiVES\n\n", sfile);
3017       lives_fputs("<define>\n", sfile);
3018       lives_fputs(rfxbuilder->field_delim, sfile);
3019       lives_fputs(rfxbuilder->rfx_version, sfile);
3020       lives_fputs("\n</define>\n\n", sfile);
3021       lives_fputs("<name>\n", sfile);
3022       lives_fputs(name, sfile);
3023       lives_fputs("\n</name>\n\n", sfile);
3024       lives_fputs("<version>\n", sfile);
3025       buf = lives_strdup_printf("%d", lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_version)));
3026       lives_fputs(buf, sfile);
3027       lives_free(buf);
3028       lives_fputs("\n</version>\n\n", sfile);
3029       lives_fputs("<author>\n", sfile);
3030       lives_fputs(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->author_entry)), sfile);
3031       lives_fputs(rfxbuilder->field_delim, sfile);
3032       lives_fputs(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->url_entry)), sfile);
3033       lives_fputs("\n</author>\n\n", sfile);
3034       lives_fputs("<description>\n", sfile);
3035       lives_fputs(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->menu_text_entry)), sfile);
3036       lives_fputs(rfxbuilder->field_delim, sfile);
3037       lives_fputs(lives_entry_get_text(LIVES_ENTRY(rfxbuilder->action_desc_entry)), sfile);
3038       lives_fputs(rfxbuilder->field_delim, sfile);
3039       if (rfxbuilder->type == RFX_BUILD_TYPE_UTILITY) {
3040         buf = lives_strdup("-1");
3041       } else {
3042         buf = lives_strdup_printf("%d", lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_min_frames)));
3043       }
3044       lives_fputs(buf, sfile);
3045       lives_free(buf);
3046       lives_fputs(rfxbuilder->field_delim, sfile);
3047       switch (rfxbuilder->type) {
3048       case RFX_BUILD_TYPE_EFFECT2:
3049         buf = lives_strdup("2");
3050         break;
3051       case RFX_BUILD_TYPE_EFFECT0:
3052         buf = lives_strdup("0");
3053         break;
3054       default:
3055         buf = lives_strdup("1");
3056       }
3057       lives_fputs(buf, sfile);
3058       lives_free(buf);
3059       lives_fputs(rfxbuilder->field_delim, sfile);
3060       lives_fputs("\n</description>\n\n", sfile);
3061       lives_fputs("<requires>\n", sfile);
3062       for (i = 0; i < rfxbuilder->num_reqs; i++) {
3063         lives_fputs(rfxbuilder->reqs[i], sfile);
3064         if (i < rfxbuilder->num_reqs - 1)
3065           lives_fputs("\n", sfile);
3066       }
3067       lives_fputs("\n</requires>\n\n", sfile);
3068       lives_fputs("<params>\n", sfile);
3069       for (i = 0; i < rfxbuilder->num_params; i++) {
3070         lives_fputs(rfxbuilder->params[i].name, sfile);
3071         lives_fputs(rfxbuilder->field_delim, sfile);
3072         lives_fputs(rfxbuilder->params[i].label, sfile);
3073         lives_fputs(rfxbuilder->field_delim, sfile);
3074         switch (rfxbuilder->params[i].type) {
3075         case LIVES_PARAM_NUM:
3076           stepwrap = rfxbuilder->params[i].step_size;
3077           if (rfxbuilder->params[i].wrap) stepwrap = -stepwrap;
3078           lives_fputs("num", sfile);
3079           buf = lives_strdup_printf("%d", rfxbuilder->params[i].dp);
3080           lives_fputs(buf, sfile);
3081           lives_free(buf);
3082           lives_fputs(rfxbuilder->field_delim, sfile);
3083           if (!rfxbuilder->params[i].dp) {
3084             buf = lives_strdup_printf("%d", get_int_param(rfxbuilder->params[i].def));
3085             lives_fputs(buf, sfile);
3086             lives_free(buf);
3087             lives_fputs(rfxbuilder->field_delim, sfile);
3088             buf = lives_strdup_printf("%d", (int)rfxbuilder->params[i].min);
3089             lives_fputs(buf, sfile);
3090             lives_free(buf);
3091             lives_fputs(rfxbuilder->field_delim, sfile);
3092             buf = lives_strdup_printf("%d", (int)rfxbuilder->params[i].max);
3093             lives_fputs(buf, sfile);
3094             lives_free(buf);
3095             lives_fputs(rfxbuilder->field_delim, sfile);
3096             if (stepwrap != 1.) {
3097               buf = lives_strdup_printf("%d", (int)stepwrap);
3098               lives_fputs(buf, sfile);
3099               lives_free(buf);
3100               lives_fputs(rfxbuilder->field_delim, sfile);
3101             }
3102           } else {
3103             char *pattern = lives_strdup_printf("%%.%df", rfxbuilder->params[i].dp);
3104             buf = lives_strdup_printf(pattern, get_double_param(rfxbuilder->params[i].def));
3105             lives_fputs(buf, sfile);
3106             lives_free(buf);
3107             lives_fputs(rfxbuilder->field_delim, sfile);
3108             buf = lives_strdup_printf(pattern, rfxbuilder->params[i].min);
3109             lives_fputs(buf, sfile);
3110             lives_free(buf);
3111             lives_fputs(rfxbuilder->field_delim, sfile);
3112             buf = lives_strdup_printf(pattern, rfxbuilder->params[i].max);
3113             lives_fputs(buf, sfile);
3114             lives_free(buf);
3115             lives_fputs(rfxbuilder->field_delim, sfile);
3116             if (stepwrap != 1.) {
3117               buf = lives_strdup_printf(pattern, stepwrap);
3118               lives_fputs(buf, sfile);
3119               lives_free(buf);
3120               lives_fputs(rfxbuilder->field_delim, sfile);
3121             }
3122             lives_free(pattern);
3123           }
3124           break;
3125         case LIVES_PARAM_BOOL:
3126           lives_fputs("bool", sfile);
3127           lives_fputs(rfxbuilder->field_delim, sfile);
3128           buf = lives_strdup_printf("%d", get_bool_param(rfxbuilder->params[i].def));
3129           lives_fputs(buf, sfile);
3130           lives_free(buf);
3131           lives_fputs(rfxbuilder->field_delim, sfile);
3132           if (rfxbuilder->params[i].group != 0) {
3133             buf = lives_strdup_printf("%d", rfxbuilder->params[i].group);
3134             lives_fputs(buf, sfile);
3135             lives_free(buf);
3136             lives_fputs(rfxbuilder->field_delim, sfile);
3137           }
3138           break;
3139         case LIVES_PARAM_COLRGB24:
3140           lives_fputs("colRGB24", sfile);
3141           lives_fputs(rfxbuilder->field_delim, sfile);
3142           get_colRGB24_param(rfxbuilder->params[i].def, &rgb);
3143           buf = lives_strdup_printf("%d", rgb.red);
3144           lives_fputs(buf, sfile);
3145           lives_free(buf);
3146           lives_fputs(rfxbuilder->field_delim, sfile);
3147           buf = lives_strdup_printf("%d", rgb.green);
3148           lives_fputs(buf, sfile);
3149           lives_free(buf);
3150           lives_fputs(rfxbuilder->field_delim, sfile);
3151           buf = lives_strdup_printf("%d", rgb.blue);
3152           lives_fputs(buf, sfile);
3153           lives_free(buf);
3154           lives_fputs(rfxbuilder->field_delim, sfile);
3155           break;
3156         case LIVES_PARAM_STRING:
3157           lives_fputs("string", sfile);
3158           lives_fputs(rfxbuilder->field_delim, sfile);
3159           lives_fputs((tmp = U82L(tmp2 = subst((char *)rfxbuilder->params[i].def, "\n", "\\n"))), sfile);
3160           lives_free(tmp);
3161           lives_free(tmp2);
3162           lives_fputs(rfxbuilder->field_delim, sfile);
3163           buf = lives_strdup_printf("%d", (int)rfxbuilder->params[i].max);
3164           lives_fputs(buf, sfile);
3165           lives_free(buf);
3166           lives_fputs(rfxbuilder->field_delim, sfile);
3167           break;
3168         case LIVES_PARAM_STRING_LIST:
3169           lives_fputs("string_list", sfile);
3170           lives_fputs(rfxbuilder->field_delim, sfile);
3171           if (rfxbuilder->params[i].def) {
3172             buf = lives_strdup_printf("%d", get_bool_param(rfxbuilder->params[i].def));
3173             lives_fputs(buf, sfile);
3174             lives_free(buf);
3175             lives_fputs(rfxbuilder->field_delim, sfile);
3176             for (j = 0; j < lives_list_length(rfxbuilder->params[i].list); j++) {
3177               lives_fputs((tmp = U82L(tmp2 = subst((char *)lives_list_nth_data
3178                                                    (rfxbuilder->params[i].list, j), "\n", "\\n"))), sfile);
3179               lives_free(tmp);
3180               lives_free(tmp2);
3181               lives_fputs(rfxbuilder->field_delim, sfile);
3182             }
3183           }
3184           break;
3185         default:
3186           break;
3187         }
3188         if (i < rfxbuilder->num_params - 1)
3189           lives_fputs("\n", sfile);
3190       }
3191       lives_fputs("\n</params>\n\n", sfile);
3192       lives_fputs("<param_window>\n", sfile);
3193       for (i = 0; i < rfxbuilder->num_paramw_hints; i++) {
3194         lives_fputs(rfxbuilder->paramw_hints[i], sfile);
3195         if (strlen(rfxbuilder->paramw_hints[i]) > strlen(rfxbuilder->field_delim) &&
3196             strcmp(rfxbuilder->paramw_hints[i] + strlen(rfxbuilder->paramw_hints[i])
3197                    - strlen(rfxbuilder->field_delim), rfxbuilder->field_delim))
3198           lives_fputs(rfxbuilder->field_delim, sfile);
3199         lives_fputs("\n", sfile);
3200       }
3201       lives_fputs("</param_window>\n\n", sfile);
3202       lives_fputs("<properties>\n", sfile);
3203       if (rfxbuilder->type == RFX_BUILD_TYPE_TOOL) props = rfxbuilder->props | RFX_PROPS_MAY_RESIZE;
3204       else props = rfxbuilder->props;
3205 
3206       if (rfxbuilder->type != RFX_BUILD_TYPE_EFFECT0 && (rfxbuilder->props & RFX_PROPS_BATCHG))
3207         rfxbuilder->props ^= RFX_PROPS_BATCHG;
3208 
3209       buf = lives_strdup_printf("0x%04X", props);
3210       lives_fputs(buf, sfile);
3211       lives_free(buf);
3212       lives_fputs("\n</properties>\n\n", sfile);
3213       lives_fputs("<language_code>\n", sfile);
3214       ctext = lives_combo_get_active_text(LIVES_COMBO(rfxbuilder->langc_combo));
3215       array = lives_strsplit(ctext, " ", -1);
3216       lives_fputs(array[0], sfile);
3217       lives_strfreev(array);
3218       lives_fputs("\n</language_code>\n\n", sfile);
3219       lives_fputs("<pre>\n", sfile);
3220       lives_fputs(rfxbuilder->pre_code, sfile);
3221       if (strlen(rfxbuilder->pre_code) && strcmp(rfxbuilder->pre_code + strlen(rfxbuilder->pre_code) - 1, "\n"))
3222         lives_fputs("\n", sfile);
3223       lives_fputs("</pre>\n\n", sfile);
3224       lives_fputs("<loop>\n", sfile);
3225       lives_fputs(rfxbuilder->loop_code, sfile);
3226       if (strlen(rfxbuilder->loop_code) && strcmp(rfxbuilder->loop_code + strlen(rfxbuilder->loop_code) - 1, "\n"))
3227         lives_fputs("\n", sfile);
3228       lives_fputs("</loop>\n\n", sfile);
3229       lives_fputs("<post>\n", sfile);
3230       lives_fputs(rfxbuilder->post_code, sfile);
3231       if (strlen(rfxbuilder->post_code) && strcmp(rfxbuilder->post_code + strlen(rfxbuilder->post_code) - 1, "\n"))
3232         lives_fputs("\n", sfile);
3233       lives_fputs("</post>\n\n", sfile);
3234       lives_fputs("<onchange>\n", sfile);
3235       for (i = 0; i < rfxbuilder->num_triggers; i++) {
3236         int numtok = get_token_count(rfxbuilder->triggers[i].code, '\n');
3237         buf = rfxbuilder->triggers[i].when ? lives_strdup_printf("%d", rfxbuilder->triggers[i].when - 1) : lives_strdup("init");
3238         array = lives_strsplit(rfxbuilder->triggers[i].code, "\n", numtok);
3239         for (int j = 0; j < numtok; j++) {
3240           lives_fputs(buf, sfile);
3241           lives_fputs(rfxbuilder->field_delim, sfile);
3242           if (array[j]) lives_fputs(array[j], sfile);
3243           if (j < numtok - 1)
3244             lives_fputs("\n", sfile);
3245         }
3246         lives_fputs("\n", sfile);
3247         lives_free(buf);
3248         lives_strfreev(array);
3249       }
3250       lives_fputs("</onchange>\n\n", sfile);
3251       fclose(sfile);
3252 
3253       if (THREADVAR(write_failed)) {
3254         THREADVAR(write_failed) = FALSE;
3255         widget_opts.transient = LIVES_WINDOW(rfxbuilder->dialog);
3256         retval = do_write_failed_error_s_with_retry(script_file, NULL);
3257         widget_opts.transient = NULL;
3258         if (retval == LIVES_RESPONSE_CANCEL) d_print_file_error_failed();
3259       }
3260     }
3261   } while (retval == LIVES_RESPONSE_RETRY);
3262 
3263   lives_free(script_file);
3264 
3265   if (retval != LIVES_RESPONSE_CANCEL) {
3266     d_print_done();
3267 
3268     lives_widget_set_sensitive(mainw->promote_test_rfx, TRUE);
3269     lives_widget_set_sensitive(mainw->delete_test_rfx, TRUE);
3270     lives_widget_set_sensitive(mainw->rename_test_rfx, TRUE);
3271     lives_widget_set_sensitive(mainw->edit_test_rfx, TRUE);
3272 
3273     return TRUE;
3274   }
3275   return FALSE;
3276 }
3277 
3278 
script_to_rfxbuilder(rfx_build_window_t * rfxbuilder,const char * script_file)3279 boolean script_to_rfxbuilder(rfx_build_window_t *rfxbuilder, const char *script_file) {
3280   LiVESList *list;
3281 
3282   char **array;
3283   char *tmp;
3284   char *line;
3285   char *version;
3286 
3287   int listlen;
3288   int num_channels;
3289   int tnum, found;
3290   int filled_triggers = 0;
3291   int len;
3292 
3293   register int i, j;
3294 
3295   rfxbuilder->type = RFX_BUILD_TYPE_EFFECT1;
3296   if (!(list = get_script_section("define", script_file, TRUE))) {
3297     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", (_("No <define> section found in script.\n")));
3298     return FALSE;
3299   }
3300   tmp = (char *)lives_list_nth_data(list, 0);
3301   if (strlen(tmp) < 2) {
3302     lives_list_free_all(&list);
3303     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", (_("Bad script version.\n")));
3304     return FALSE;
3305   }
3306 
3307   version = lives_strdup(tmp + 1);
3308   if (make_version_hash(version) > make_version_hash(RFX_VERSION)) {
3309     lives_list_free_all(&list);
3310     lives_free(version);
3311     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", (_("Bad script version.\n")));
3312     return FALSE;
3313   }
3314   lives_free(version);
3315 
3316   lives_memset(tmp + 1, 0, 1);
3317   lives_free(rfxbuilder->field_delim);
3318   rfxbuilder->field_delim = lives_strdup(tmp);
3319   lives_list_free_all(&list);
3320 
3321   if (!(list = get_script_section("name", script_file, TRUE))) {
3322     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", (_("No <name> section found in script.\n")));
3323     return FALSE;
3324   }
3325   lives_entry_set_text(LIVES_ENTRY(rfxbuilder->name_entry), (char *)lives_list_nth_data(list, 0));
3326   lives_list_free_all(&list);
3327 
3328   if (!(list = get_script_section("version", script_file, TRUE))) {
3329     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", (_("No <version> section found in script.\n")));
3330     return FALSE;
3331   }
3332   lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_version),
3333                               (double)atoi((char *)lives_list_nth_data(list, 0)));
3334   lives_list_free_all(&list);
3335 
3336   if (!(list = get_script_section("author", script_file, TRUE))) {
3337     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", (_("No <author> section found in script.\n")));
3338     return FALSE;
3339   }
3340   array = lives_strsplit((char *)lives_list_nth_data(list, 0), rfxbuilder->field_delim, 2);
3341   lives_entry_set_text(LIVES_ENTRY(rfxbuilder->author_entry), array[0]);
3342   if (get_token_count((char *)lives_list_nth_data(list, 0), (int)rfxbuilder->field_delim[0]) > 1) {
3343     lives_entry_set_text(LIVES_ENTRY(rfxbuilder->url_entry), array[1]);
3344   }
3345   lives_strfreev(array);
3346   lives_list_free_all(&list);
3347 
3348   if (!(list = get_script_section("description", script_file, TRUE))) {
3349     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", (_("No <description> section found in script.\n")));
3350     return FALSE;
3351   }
3352   if (get_token_count((char *)lives_list_nth_data(list, 0), (int)rfxbuilder->field_delim[0]) < 4) {
3353     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, (_("Bad description. (%s)\n")), (char *)lives_list_nth_data(list, 0));
3354     lives_list_free_all(&list);
3355     return FALSE;
3356   }
3357   array = lives_strsplit((char *)lives_list_nth_data(list, 0), rfxbuilder->field_delim, -1);
3358   lives_list_free_all(&list);
3359   lives_entry_set_text(LIVES_ENTRY(rfxbuilder->menu_text_entry), array[0]);
3360   lives_entry_set_text(LIVES_ENTRY(rfxbuilder->action_desc_entry), array[1]);
3361   lives_spin_button_set_value(LIVES_SPIN_BUTTON(rfxbuilder->spinbutton_min_frames), (double)atoi(array[2]));
3362   num_channels = atoi(array[3]);
3363 
3364   if (num_channels == 2) {
3365     rfxbuilder->type = RFX_BUILD_TYPE_EFFECT2;
3366   }
3367   if (num_channels == 0) {
3368     rfxbuilder->type = RFX_BUILD_TYPE_EFFECT0;
3369   }
3370   if (atoi(array[2]) == -1) rfxbuilder->type = RFX_BUILD_TYPE_UTILITY;
3371   lives_strfreev(array);
3372 
3373   rfxbuilder->script_name = lives_strdup(script_file);
3374 
3375   if ((list = get_script_section("requires", script_file, TRUE))) {
3376     rfxbuilder->num_reqs = lives_list_length(list);
3377     for (i = 0; i < rfxbuilder->num_reqs; i++) {
3378       rfxbuilder->reqs[i] = (char *)lives_list_nth_data(list, i);
3379     }
3380     lives_list_free(list);
3381   }
3382 
3383   rfxbuilder->props = 0;
3384 
3385   if ((list = get_script_section("properties", script_file, TRUE))) {
3386     if (!strncmp((char *)lives_list_nth_data(list, 0), "0x", 2) || !strncmp((char *)lives_list_nth_data(list, 0), "0x", 2)) {
3387       rfxbuilder->props = hextodec((char *)lives_list_nth_data(list, 0) + 2);
3388     } else rfxbuilder->props = atoi((char *)lives_list_nth_data(list, 0));
3389     lives_list_free_all(&list);
3390   }
3391 
3392   if (rfxbuilder->props & RFX_PROPS_MAY_RESIZE) rfxbuilder->type = RFX_BUILD_TYPE_TOOL;
3393 
3394   if ((list = get_script_section("params", script_file, TRUE))) {
3395     char *type;
3396 
3397     listlen = lives_list_length(list);
3398     rfxbuilder->num_params = listlen;
3399     rfxbuilder->params = (lives_param_t *)lives_malloc(rfxbuilder->num_params * sizeof(lives_param_t));
3400     i = 0;
3401 
3402     for (j = 0; j < listlen; j++) {
3403       line = (char *)lives_list_nth_data(list, j);
3404       len = get_token_count(line, (int)rfxbuilder->field_delim[0]); // will fail if field_delim contains repeated chars !
3405       if (len < 4) {
3406         rfxbuilder->num_params--;
3407         rfxbuilder->params = (lives_param_t *)lives_realloc(rfxbuilder->params, rfxbuilder->num_params * sizeof(lives_param_t));
3408         continue;
3409       }
3410       array = lives_strsplit(line, rfxbuilder->field_delim, -1);
3411       lives_free(line);
3412       rfxbuilder->params[i].name = lives_strdup(array[0]);
3413       rfxbuilder->params[i].label = lives_strdup(array[1]);
3414       rfxbuilder->params[i].onchange = FALSE;
3415       rfxbuilder->params[i].dp = 0;
3416       rfxbuilder->params[i].group = 0;
3417 
3418       ////////////////
3419       rfxbuilder->params[i].desc = NULL;
3420       rfxbuilder->params[i].use_mnemonic = TRUE;
3421       rfxbuilder->params[i].step_size = 1.;
3422       rfxbuilder->params[i].hidden = FALSE;
3423       rfxbuilder->params[i].transition = FALSE;
3424       rfxbuilder->params[i].wrap = FALSE;
3425       ////////////////
3426 
3427       type = lives_strdup(array[2]);
3428       if (!strncmp(type, "num", 3)) {
3429         if (len < 6) {
3430           lives_free(rfxbuilder->params[i].name);
3431           lives_free(rfxbuilder->params[i].label);
3432           rfxbuilder->num_params--;
3433           rfxbuilder->params = (lives_param_t *)lives_realloc(rfxbuilder->params, rfxbuilder->num_params * sizeof(lives_param_t));
3434           continue;
3435         }
3436         rfxbuilder->params[i].dp = atoi(type + 3);
3437         rfxbuilder->params[i].type = LIVES_PARAM_NUM;
3438         if (rfxbuilder->params[i].dp) {
3439           rfxbuilder->params[i].def = lives_malloc(sizint);
3440           set_double_param(rfxbuilder->params[i].def, lives_strtod(array[3], NULL));
3441         } else {
3442           rfxbuilder->params[i].def = lives_malloc(sizint);
3443           set_int_param(rfxbuilder->params[i].def, atoi(array[3]));
3444         }
3445         rfxbuilder->params[i].min = lives_strtod(array[4], NULL);
3446         rfxbuilder->params[i].max = lives_strtod(array[5], NULL);
3447         if (len > 6) {
3448           rfxbuilder->params[i].step_size = lives_strtod(array[6], NULL);
3449           if (rfxbuilder->params[i].step_size == 0.) rfxbuilder->params[i].step_size = 1.;
3450           else if (rfxbuilder->params[i].step_size < 0.) {
3451             rfxbuilder->params[i].step_size = -rfxbuilder->params[i].step_size;
3452             rfxbuilder->params[i].wrap = TRUE;
3453           }
3454         }
3455       } else if (!strcmp(type, "colRGB24")) {
3456         if (len < 6) {
3457           lives_free(rfxbuilder->params[i].name);
3458           lives_free(rfxbuilder->params[i].label);
3459           rfxbuilder->num_params--;
3460           rfxbuilder->params = (lives_param_t *)lives_realloc(rfxbuilder->params, rfxbuilder->num_params * sizeof(lives_param_t));
3461           continue;
3462         }
3463         rfxbuilder->params[i].type = LIVES_PARAM_COLRGB24;
3464         rfxbuilder->params[i].def = lives_malloc(3 * sizint);
3465         set_colRGB24_param(rfxbuilder->params[i].def, (short)atoi(array[3]),
3466                            (short)atoi(array[4]), (short)atoi(array[5]));
3467       } else if (!strcmp(type, "string")) {
3468         rfxbuilder->params[i].type = LIVES_PARAM_STRING;
3469         rfxbuilder->params[i].def = subst((tmp = L2U8(array[3])), "\\n", "\n");
3470         lives_free(tmp);
3471         if (len > 4) rfxbuilder->params[i].max = (double)atoi(array[4]);
3472         else rfxbuilder->params[i].max = 1024; // TODO
3473       } else if (!strcmp(type, "string_list")) {
3474         rfxbuilder->params[i].type = LIVES_PARAM_STRING_LIST;
3475         rfxbuilder->params[i].def = lives_malloc(sizint);
3476         set_int_param(rfxbuilder->params[i].def, atoi(array[3]));
3477         if (len > 4) {
3478           rfxbuilder->params[i].list = array_to_string_list(array, 3, len);
3479         } else {
3480           rfxbuilder->params[i].list = NULL;
3481           set_int_param(rfxbuilder->params[i].def, 0);
3482         }
3483       } else {
3484         // default is bool
3485         rfxbuilder->params[i].type = LIVES_PARAM_BOOL;
3486         rfxbuilder->params[i].def = lives_malloc(sizint);
3487         set_bool_param(rfxbuilder->params[i].def, atoi(array[3]));
3488         if (len > 4) rfxbuilder->params[i].group = atoi(array[4]);
3489       }
3490       lives_free(type);
3491       lives_strfreev(array);
3492       i++;
3493     }
3494     lives_list_free(list);
3495   }
3496 
3497   if ((list = get_script_section("param_window", script_file, TRUE))) {
3498     rfxbuilder->num_paramw_hints = lives_list_length(list);
3499     for (i = 0; i < rfxbuilder->num_paramw_hints; i++) {
3500       rfxbuilder->paramw_hints[i] = (char *)lives_list_nth_data(list, i);
3501     }
3502     lives_list_free(list);
3503   }
3504 
3505   if ((list = get_script_section("onchange", script_file, TRUE))) {
3506     listlen = lives_list_length(list);
3507     for (i = 0; i < listlen; i++) {
3508       array = lives_strsplit((char *)lives_list_nth_data(list, i), rfxbuilder->field_delim, -1);
3509       if (!strcmp(array[0], "init")) {
3510         if (!rfxbuilder->has_init_trigger) {
3511           rfxbuilder->has_init_trigger = TRUE;
3512           rfxbuilder->num_triggers++;
3513         }
3514       } else if ((tnum = atoi(array[0]) + 1) <= rfxbuilder->num_params && tnum > 0) {
3515         if (!rfxbuilder->params[tnum - 1].onchange) {
3516           rfxbuilder->params[tnum - 1].onchange = TRUE;
3517           rfxbuilder->num_triggers++;
3518         }
3519       } else {
3520         //invalid trigger
3521         char *msg = lives_strdup_printf(_("\n\nInvalid trigger (%s)\nfound in script.\n\n"), array[0]);
3522         widget_opts.non_modal = TRUE;
3523         do_error_dialog(msg);
3524         widget_opts.non_modal = FALSE;
3525         lives_free(msg);
3526       }
3527       lives_strfreev(array);
3528     }
3529     //end pass 1
3530     rfxbuilder->triggers = (rfx_trigger_t *)lives_malloc(rfxbuilder->num_triggers * sizeof(rfx_trigger_t));
3531 
3532     for (i = 0; i < rfxbuilder->num_triggers; i++) {
3533       rfxbuilder->triggers[i].when = -1;
3534       rfxbuilder->triggers[i].code = lives_strdup("");
3535     }
3536 
3537     filled_triggers = 0;
3538     listlen = lives_list_length(list);
3539     for (i = 0; i < listlen; i++) {
3540       array = lives_strsplit((char *)lives_list_nth_data(list, i), rfxbuilder->field_delim, -1);
3541       if (!strcmp(array[0], "init")) {
3542         // find init trigger and concatenate code
3543         found = filled_triggers;
3544         for (j = 0; j < filled_triggers && found == filled_triggers; j++) if (rfxbuilder->triggers[j].when == 0) found = j;
3545         if (found == filled_triggers) filled_triggers++;
3546         if (!strlen(rfxbuilder->triggers[found].code)) {
3547           tmp = lives_strconcat(rfxbuilder->triggers[found].code, array[1], NULL);
3548         } else {
3549           tmp = lives_strconcat(rfxbuilder->triggers[found].code, "\n", array[1], NULL);
3550         }
3551         lives_free(rfxbuilder->triggers[found].code);
3552         rfxbuilder->triggers[found].when = 0;
3553         rfxbuilder->triggers[found].code = lives_strdup(tmp);
3554         lives_free(tmp);
3555       } else if ((tnum = atoi(array[0]) + 1) <= rfxbuilder->num_params && tnum > 0) {
3556         // find tnum trigger and concatenate code
3557         found = filled_triggers;
3558 
3559         for (j = 0; j < filled_triggers && found == filled_triggers; j++) if (rfxbuilder->triggers[j].when == tnum) found = j;
3560         if (found == filled_triggers) filled_triggers++;
3561 
3562         if (!strlen(rfxbuilder->triggers[found].code)) {
3563           tmp = lives_strconcat(rfxbuilder->triggers[found].code, array[1], NULL);
3564         } else {
3565           tmp = lives_strconcat(rfxbuilder->triggers[found].code, "\n", array[1], NULL);
3566         }
3567         lives_free(rfxbuilder->triggers[found].code);
3568         rfxbuilder->triggers[found].when = tnum;
3569         rfxbuilder->triggers[found].code = lives_strdup(tmp);
3570         lives_free(tmp);
3571       }
3572       lives_strfreev(array);
3573     }
3574     lives_list_free_all(&list);
3575   }
3576 
3577   if ((list = get_script_section("pre", script_file, FALSE))) {
3578     listlen = lives_list_length(list);
3579     for (i = 0; i < listlen; i++) {
3580       tmp = lives_strconcat(rfxbuilder->pre_code, lives_list_nth_data(list, i), NULL);
3581       lives_free(rfxbuilder->pre_code);
3582       rfxbuilder->pre_code = tmp;
3583     }
3584     lives_list_free_all(&list);
3585   }
3586 
3587   if ((list = get_script_section("loop", script_file, FALSE))) {
3588     listlen = lives_list_length(list);
3589     for (i = 0; i < listlen; i++) {
3590       tmp = lives_strconcat(rfxbuilder->loop_code, lives_list_nth_data(list, i), NULL);
3591       lives_free(rfxbuilder->loop_code);
3592       rfxbuilder->loop_code = tmp;
3593     }
3594     lives_list_free_all(&list);
3595   }
3596 
3597   if ((list = get_script_section("post", script_file, FALSE))) {
3598     listlen = lives_list_length(list);
3599     for (i = 0; i < listlen; i++) {
3600       tmp = lives_strconcat(rfxbuilder->post_code, lives_list_nth_data(list, i), NULL);
3601       lives_free(rfxbuilder->post_code);
3602       rfxbuilder->post_code = tmp;
3603     }
3604     lives_list_free_all(&list);
3605   }
3606 
3607   return TRUE;
3608 }
3609 
3610 
get_script_section(const char * section,const char * file,boolean strip)3611 LiVESList *get_script_section(const char *section, const char *file, boolean strip) {
3612   char *com = lives_strdup_printf("\"%s\" -get \"%s\" \"%s\"", EXEC_RFX_BUILDER, section, file);
3613   LiVESList *list = get_plugin_result(com, "\n", FALSE, strip);
3614   lives_free(com);
3615   return list;
3616 }
3617 
3618 
3619 /** @brief parse text and return non empty lines between <section> and </section>
3620 
3621     similar to get_val_from_cached_list, except here we parse each line into a list.
3622     rfx + params can be built from the <params> section
3623 */
3624 /* LiVESList *get_script_section_from_text(const char *section, const char *text, boolean strip) { */
3625 /*   LiVESList *list; */
3626 /*   // TODO */
3627 /*   return list; */
3628 /* } */
3629 
3630 
on_rebuild_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)3631 void on_rebuild_rfx_activate(LiVESMenuItem * menuitem, livespointer user_data) {
3632   char *com;
3633   lives_rfx_status_t status = (lives_rfx_status_t)LIVES_POINTER_TO_INT(user_data);
3634 
3635   if (!check_builder_programs()) return;
3636 
3637   do_threaded_dialog(_("Rebuilding RFX scripts"), FALSE);
3638 
3639   switch (status) {
3640   case RFX_STATUS_CUSTOM:
3641     d_print(_("Rebuilding custom RFX scripts..."));
3642     com = lives_strdup_printf("%s build_rfx_plugins custom", prefs->backend_sync);
3643     lives_system(com, FALSE);
3644     lives_free(com);
3645     break;
3646   case RFX_STATUS_TEST:
3647     d_print(_("Rebuilding test RFX scripts..."));
3648     com = lives_strdup_printf("%s build_rfx_plugins test", prefs->backend_sync);
3649     lives_system(com, FALSE);
3650     lives_free(com);
3651     break;
3652   default:
3653     break;
3654   }
3655 
3656   d_print(_("recreating dynamic menu entries..."));
3657   lives_widget_context_update();
3658   threaded_dialog_spin(0.);
3659   add_rfx_effects(status);
3660   threaded_dialog_spin(0.);
3661   d_print_done();
3662   end_threaded_dialog();
3663   lives_widget_queue_draw(LIVES_MAIN_WINDOW_WIDGET);
3664   lives_widget_context_update();
3665 }
3666 
3667 
check_builder_programs(void)3668 boolean check_builder_programs(void) {
3669 #ifdef IS_MINGW
3670   return TRUE;
3671 #endif
3672 
3673   // check our plugin builder routines are executable
3674   char loc[32];
3675   char *msg;
3676 
3677   get_location(EXEC_RFX_BUILDER, loc, 32);
3678   if (!strlen(loc)) {
3679     msg = lives_strdup_printf(
3680             _("\n\nLiVES was unable to find the program %s.\nPlease check this program is in your path and executable.\n"),
3681             EXEC_RFX_BUILDER);
3682     do_error_dialog(msg);
3683     lives_free(msg);
3684     return FALSE;
3685   }
3686   get_location(EXEC_RFX_BUILDER_MULTI, loc, 32);
3687   if (!strlen(loc)) {
3688     msg = lives_strdup_printf(
3689             _("\n\nLiVES was unable to find the program %s.\nPlease check this program is in your path and executable.\n"),
3690             EXEC_RFX_BUILDER_MULTI);
3691     do_error_dialog(msg);
3692     lives_free(msg);
3693     return FALSE;
3694   }
3695   return TRUE;
3696 }
3697 
3698 
on_delete_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)3699 void on_delete_rfx_activate(LiVESMenuItem * menuitem, livespointer user_data) {
3700   lives_rfx_status_t status = (lives_rfx_status_t)LIVES_POINTER_TO_INT(user_data);
3701   int ret;
3702   char *rfx_script_file, *rfx_exec_dir, *rfx_script_dir;
3703   char *script_name = prompt_for_script_name(NULL, status);
3704   char *msg;
3705 
3706   if (!script_name) return; // user cancelled
3707 
3708   if (strlen(script_name)) {
3709     switch (status) {
3710     case RFX_STATUS_TEST:
3711       rfx_exec_dir = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_TEST, NULL);
3712       rfx_script_dir = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS, NULL);
3713       rfx_script_file = lives_build_filename(rfx_script_dir, script_name, NULL);
3714       break;
3715     case RFX_STATUS_CUSTOM:
3716       rfx_exec_dir = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_CUSTOM, NULL);
3717       rfx_script_dir = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_CUSTOM_SCRIPTS, NULL);
3718       rfx_script_file = lives_build_filename(rfx_script_dir, script_name, NULL);
3719       break;
3720     default:
3721       // we will not delete builtins
3722       lives_free(script_name);
3723       return;
3724     }
3725     lives_free(script_name);
3726 
3727     // double check with user
3728     msg = lives_strdup_printf(_("\n\nReally delete RFX script\n%s ?\n\n"), rfx_script_file);
3729     if (!do_warning_dialog(msg)) {
3730       lives_free(msg);
3731       lives_free(rfx_script_file);
3732       lives_free(rfx_script_dir);
3733       lives_free(rfx_exec_dir);
3734       return;
3735     }
3736     lives_free(msg);
3737 
3738     d_print(_("Deleting rfx script %s..."), rfx_script_file);
3739 
3740     if (!(ret = lives_rm(rfx_script_file))) {
3741       lives_rmdir(rfx_exec_dir, TRUE);
3742       d_print_done();
3743       on_rebuild_rfx_activate(NULL, LIVES_INT_TO_POINTER(status));
3744     } else {
3745       d_print_failed();
3746       msg = lives_strdup_printf(_("\n\nFailed to delete the script\n%s\nError code was %d\n"), rfx_script_file, ret);
3747       widget_opts.non_modal = TRUE;
3748       do_error_dialog(msg);
3749       widget_opts.non_modal = FALSE;
3750       lives_free(msg);
3751     }
3752     lives_free(rfx_script_file);
3753     lives_free(rfx_script_dir);
3754     lives_free(rfx_exec_dir);
3755   }
3756 }
3757 
3758 
on_promote_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)3759 void on_promote_rfx_activate(LiVESMenuItem * menuitem, livespointer user_data) {
3760   char *rfx_script_from = NULL;
3761   char *rfx_script_to = NULL;
3762   char *rfx_dir_from = NULL;
3763   char *rfx_dir_to = NULL;
3764   char *script_name = prompt_for_script_name(NULL, RFX_STATUS_TEST);
3765   char *msg;
3766   int ret = 0;
3767   boolean failed = TRUE;
3768 
3769   if (!script_name) return; // user cancelled
3770 
3771   if (strlen(script_name)) {
3772     rfx_dir_from = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS, NULL);
3773     rfx_script_from = lives_build_filename(rfx_dir_from, script_name, NULL);
3774 
3775     rfx_dir_to = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_CUSTOM_SCRIPTS, NULL);
3776     rfx_script_to = lives_build_filename(rfx_dir_to, script_name, NULL);
3777 
3778     if (lives_file_test(rfx_script_to, LIVES_FILE_TEST_EXISTS)) {
3779       char *msg = lives_strdup_printf(
3780                     _("\nCustom script file:\n%s\nalready exists.\nPlease delete it first, or rename the test script.\n"),
3781                     script_name);
3782       do_error_dialog(msg);
3783       lives_free(msg); lives_free(rfx_dir_from); lives_free(rfx_script_from);
3784       lives_free(rfx_dir_to); lives_free(rfx_script_to); lives_free(script_name);
3785       return;
3786     }
3787 
3788     d_print(_("Promoting rfx test plugin %s to custom..."), script_name);
3789     lives_free(script_name);
3790 
3791     lives_mkdir_with_parents(rfx_dir_to, capable->umask);
3792 
3793     if (!(ret = rename(rfx_script_from, rfx_script_to))) {
3794       d_print_done();
3795       on_rebuild_rfx_activate(NULL, LIVES_INT_TO_POINTER(RFX_STATUS_CUSTOM));
3796       on_rebuild_rfx_activate(NULL, LIVES_INT_TO_POINTER(RFX_STATUS_TEST));
3797       failed = FALSE;
3798     }
3799   }
3800 
3801   if (failed) {
3802     lives_rmdir(rfx_dir_to, FALSE);
3803     d_print_failed();
3804     msg = lives_strdup_printf(_("\n\nFailed to move the plugin script from\n%s to\n%s\nReturn code was %d (%s)\n"),
3805                               rfx_script_from, rfx_script_to, errno, strerror(errno));
3806     widget_opts.non_modal = TRUE;
3807     do_error_dialog(msg);
3808     widget_opts.non_modal = FALSE;
3809     lives_free(msg);
3810   } else lives_rmdir(rfx_dir_from, FALSE);
3811 
3812   if (rfx_script_from) {
3813     lives_free(rfx_dir_from);
3814     lives_free(rfx_script_from);
3815     lives_free(rfx_dir_to);
3816     lives_free(rfx_script_to);
3817   }
3818 }
3819 
3820 
on_export_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)3821 void on_export_rfx_activate(LiVESMenuItem * menuitem, livespointer user_data) {
3822   lives_rfx_status_t status = (lives_rfx_status_t)LIVES_POINTER_TO_INT(user_data);
3823 
3824   char *script_name = prompt_for_script_name(NULL, status);
3825   char *rfx_script_from, *filename;
3826   char *tmp, *tmp2;
3827 
3828   if (!script_name || !*script_name) return; // user cancelled
3829 
3830   filename = choose_file(NULL, script_name, NULL, LIVES_FILE_CHOOSER_ACTION_SAVE, _("Export Script"), NULL);
3831 
3832   if (!filename) return;
3833 
3834   rfx_script_from = lives_build_filename(prefs->config_datadir,
3835                                          PLUGIN_RENDERED_EFFECTS_CUSTOM_SCRIPTS, script_name, NULL);
3836 
3837   d_print(_("Copying %s to %s..."), rfx_script_from, filename);
3838 
3839   lives_cp((tmp = lives_filename_from_utf8(rfx_script_from, -1, NULL, NULL, NULL)),
3840            (tmp2 = lives_filename_from_utf8(filename, -1, NULL, NULL, NULL)));
3841 
3842   if (THREADVAR(com_failed)) d_print_failed();
3843   else d_print_done();
3844   lives_free(tmp); lives_free(tmp2);
3845   lives_free(rfx_script_from); lives_free(filename); lives_free(script_name);
3846 }
3847 
3848 
on_import_rfx_activate(LiVESMenuItem * menuitem,livespointer user_data)3849 void on_import_rfx_activate(LiVESMenuItem * menuitem, livespointer user_data) {
3850   lives_rfx_status_t status = (lives_rfx_status_t)LIVES_POINTER_TO_INT(user_data);
3851 
3852   char *rfx_script_to, *rfx_dir_to;
3853   char *tmp, *tmp2, *tmpx;
3854   char basename[PATH_MAX];
3855 
3856   char *filename = choose_file(NULL, NULL, NULL, LIVES_FILE_CHOOSER_ACTION_OPEN, _("Import Script"), NULL);
3857 
3858   if (!filename) return;
3859 
3860   lives_snprintf(basename, PATH_MAX, "%s", filename);
3861   get_basename(basename);
3862 
3863   THREADVAR(com_failed) = FALSE;
3864 
3865   switch (status) {
3866   case RFX_STATUS_TEST:
3867     rfx_dir_to = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS, NULL);
3868     lives_mkdir_with_parents((tmp = lives_filename_from_utf8(rfx_dir_to, -1, NULL, NULL, NULL)), capable->umask);
3869     lives_free(tmp);
3870 
3871     rfx_script_to = lives_build_filename(rfx_dir_to, basename, NULL);
3872     lives_free(rfx_dir_to);
3873     break;
3874   case RFX_STATUS_CUSTOM:
3875     rfx_dir_to = lives_build_path(prefs->config_datadir, PLUGIN_RENDERED_EFFECTS_CUSTOM_SCRIPTS, NULL);
3876     lives_mkdir_with_parents((tmp = lives_filename_from_utf8(rfx_dir_to, -1, NULL, NULL, NULL)), capable->umask);
3877 
3878     lives_free(tmp);
3879     rfx_script_to = lives_build_filename(rfx_dir_to, basename, NULL);
3880     lives_free(rfx_dir_to);
3881     break;
3882   default:
3883     rfx_script_to = lives_build_filename(prefs->prefix_dir, PLUGIN_SCRIPTS_DIR,
3884                                          PLUGIN_RENDERED_EFFECTS_BUILTIN_SCRIPTS, basename, NULL);
3885     break;
3886   }
3887 
3888   if (THREADVAR(com_failed)) {
3889     lives_free(rfx_script_to);
3890     lives_free(filename);
3891     return;
3892   }
3893 
3894   if (lives_file_test(rfx_script_to, LIVES_FILE_TEST_EXISTS)) {
3895     // needs switch...eventually
3896     do_error_dialog((tmpx = lives_strdup_printf
3897                             (_("\nCustom script file:\n%s\nalready exists.\n"
3898 			       "Please delete it first, or rename the import script.\n"), basename)));
3899     lives_free(tmpx); lives_free(rfx_script_to); lives_free(filename);
3900     return;
3901   }
3902 
3903   d_print(_("Copying %s to %s..."), filename, rfx_script_to);
3904 
3905   lives_cp((tmp = lives_filename_from_utf8(filename, -1, NULL, NULL, NULL)),
3906            (tmp2 = lives_filename_from_utf8(rfx_script_to, -1, NULL, NULL, NULL)));
3907 
3908   lives_free(tmp);
3909   lives_free(tmp2);
3910   if (THREADVAR(com_failed)) d_print_failed();
3911   else {
3912     d_print_done();
3913     on_rebuild_rfx_activate(NULL, LIVES_INT_TO_POINTER(status));
3914   }
3915   lives_free(rfx_script_to);
3916   lives_free(filename);
3917 }
3918 
3919 
prompt_for_script_name(const char * sname,lives_rfx_status_t status)3920 char *prompt_for_script_name(const char *sname, lives_rfx_status_t status) {
3921   // show dialog to get script name of rfx plugin dependant on type
3922   // set type to RFX_STATUS_ANY to let user pick type as well
3923   // return value should be lives_free'd after use
3924   // beware: if the user cancels, return value is NULL
3925 
3926   // sname is suggested name, set it to NULL to prompt for a name from
3927   // status list
3928 
3929   // in copy mode, there are extra entries and the selected script will be copied
3930   // to test
3931 
3932   // in rename mode a test script will be copied to another test script
3933 
3934   char *name = NULL;
3935   char *from_name;
3936   char *from_status;
3937   char *rfx_script_from;
3938   char *rfx_script_to;
3939 
3940   rfx_build_window_t *rfxbuilder;
3941 
3942   LiVESWidget *vbox;
3943   LiVESWidget *hbox;
3944   LiVESWidget *label;
3945   LiVESWidget *script_combo = NULL;
3946   LiVESWidget *name_entry = NULL;
3947   LiVESWidget *script_combo_entry = NULL;
3948   LiVESWidget *status_combo = NULL;
3949   LiVESWidget *status_combo_entry = NULL;
3950   LiVESWidget *dialog;
3951 
3952   LiVESList *status_list = NULL;
3953 
3954   boolean copy_mode = FALSE;
3955   boolean rename_mode = FALSE;
3956   boolean OK;
3957 
3958   if (status == RFX_STATUS_COPY) {
3959     copy_mode = TRUE;
3960     status_list = lives_list_append(status_list, lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_BUILTIN]));
3961     status_list = lives_list_append(status_list, lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_CUSTOM]));
3962     status_list = lives_list_append(status_list, lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_TEST]));
3963   }
3964 
3965   dialog = lives_standard_dialog_new(NULL, FALSE, -1, -1);
3966 
3967   vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
3968 
3969   hbox = lives_hbox_new(FALSE, 0);
3970   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
3971 
3972   vbox = lives_vbox_new(FALSE, 0);
3973   lives_box_pack_start(LIVES_BOX(hbox), vbox, FALSE, FALSE, widget_opts.packing_width);
3974 
3975   add_fill_to_box(LIVES_BOX(vbox));
3976 
3977   hbox = lives_hbox_new(FALSE, 0);
3978   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
3979 
3980   add_fill_to_box(LIVES_BOX(vbox));
3981 
3982   if (copy_mode) {
3983     lives_window_set_title(LIVES_WINDOW(dialog), _("Copy RFX Script"));
3984 
3985     status_combo = lives_standard_combo_new(_("_From type:    "), status_list, LIVES_BOX(hbox), NULL);
3986 
3987     status_combo_entry = lives_combo_get_entry(LIVES_COMBO(status_combo));
3988 
3989     lives_list_free_all(&status_list);
3990   } else {
3991     if (status == RFX_STATUS_RENAME) {
3992       lives_window_set_title(LIVES_WINDOW(dialog), _("Rename Test RFX Script"));
3993       label = lives_standard_label_new(_("From script: "));
3994       rename_mode = TRUE;
3995       status = RFX_STATUS_TEST;
3996     } else {
3997       lives_window_set_title(LIVES_WINDOW(dialog), _("RFX Script Name"));
3998       label = lives_standard_label_new(_("Script name: "));
3999     }
4000     lives_box_pack_start(LIVES_BOX(hbox), label, FALSE, FALSE, 0);
4001   }
4002 
4003   if (!sname || copy_mode || rename_mode) {
4004     script_combo = lives_standard_combo_new(copy_mode ? _("Script:") : NULL, NULL, LIVES_BOX(hbox), NULL);
4005     name_entry = script_combo_entry = lives_combo_get_entry(LIVES_COMBO(script_combo));
4006   }
4007   if (sname || copy_mode || rename_mode) {
4008     // name_entry becomes a normal lives_entry
4009     if (copy_mode || rename_mode) {
4010       hbox = lives_hbox_new(FALSE, 0);
4011       lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
4012     }
4013 
4014     name_entry = lives_standard_entry_new(copy_mode ? _("New name:") : rename_mode ? _("New script name:") : NULL,
4015                                           sname, -1, 128, LIVES_BOX(hbox), NULL);
4016 
4017     if (copy_mode) {
4018       lives_signal_sync_connect(LIVES_GUI_OBJECT(status_combo), LIVES_WIDGET_CHANGED_SIGNAL,
4019                            LIVES_GUI_CALLBACK(on_script_status_changed), (livespointer)script_combo);
4020     }
4021   }
4022   lives_widget_grab_focus(name_entry);
4023   lives_entry_set_activates_default(LIVES_ENTRY(name_entry), TRUE);
4024 
4025   lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL, LIVES_RESPONSE_CANCEL);
4026 
4027   copy_script_okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL, LIVES_RESPONSE_OK);
4028 
4029   lives_button_grab_default_special(copy_script_okbutton);
4030 
4031   lives_widget_show_all(dialog);
4032 
4033   if (status_combo) on_script_status_changed(LIVES_COMBO(status_combo), (livespointer)script_combo);
4034   else if (script_combo) populate_script_combo(LIVES_COMBO(script_combo), status);
4035 
4036   do {
4037     OK = TRUE;
4038     if (lives_dialog_run(LIVES_DIALOG(dialog)) == LIVES_RESPONSE_OK) {
4039       if (name) lives_free(name);
4040       name = lives_strdup(lives_entry_get_text(LIVES_ENTRY(name_entry)));
4041       if (copy_mode) {
4042         if (find_rfx_plugin_by_name(name, RFX_STATUS_TEST) > -1 ||
4043             find_rfx_plugin_by_name(name, RFX_STATUS_CUSTOM) > -1 ||
4044             find_rfx_plugin_by_name(name, RFX_STATUS_BUILTIN) > -1) {
4045           do_error_dialog(_("\n\nThere is already a plugin with this name.\nName must be unique.\n"));
4046           OK = FALSE;
4047         }
4048         //copy selected script to test
4049         else {
4050           from_name = lives_strdup(lives_entry_get_text(LIVES_ENTRY(script_combo_entry)));
4051           from_status = lives_strdup(lives_entry_get_text(LIVES_ENTRY(status_combo_entry)));
4052           if (!strcmp(from_status, mainw->string_constants[LIVES_STRING_CONSTANT_BUILTIN])) status = RFX_STATUS_BUILTIN;
4053           else {
4054             if (!strcmp(from_status, mainw->string_constants[LIVES_STRING_CONSTANT_CUSTOM])) status = RFX_STATUS_CUSTOM;
4055             else status = RFX_STATUS_TEST;
4056           }
4057 
4058           if ((rfxbuilder = make_rfx_build_window(from_name, status)) == NULL) {
4059             // invalid name
4060             OK = FALSE;
4061           }
4062 
4063           lives_free(from_name);
4064           lives_free(from_status);
4065 
4066           if (OK) {
4067             lives_entry_set_text(LIVES_ENTRY(rfxbuilder->name_entry), name);
4068             rfxbuilder->mode = RFX_BUILDER_MODE_COPY;
4069             lives_widget_show(rfxbuilder->dialog);
4070           }
4071         }
4072       }
4073       if (rename_mode) {
4074         LiVESList *nmlist = NULL;
4075         char *xname = ensure_extension(name, LIVES_FILE_EXT_RFX_SCRIPT);
4076 
4077         if (name && lives_list_find((nmlist = get_script_list(status)), xname)) {
4078           do_error_dialog(_("\n\nThere is already a test script with this name.\nScript name must be unique.\n"));
4079           OK = FALSE;
4080         } else {
4081           int ret;
4082           from_name = lives_strdup(lives_entry_get_text(LIVES_ENTRY(script_combo_entry)));
4083           rfx_script_from = lives_build_filename(prefs->config_datadir,
4084                                                  PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS, from_name, NULL);
4085           rfx_script_to = lives_build_filename(prefs->config_datadir,
4086                                                PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS, xname, NULL);
4087           d_print(_("Renaming RFX test script %s to %s..."), from_name, xname);
4088           lives_free(from_name);
4089 
4090           if ((ret = rename(rfx_script_from, rfx_script_to))) {
4091             d_print_failed();
4092             widget_opts.non_modal = TRUE;
4093             do_error_dialogf(_("\n\nFailed to move the plugin script from\n%s to\n%s\n"
4094                                "Return code was %d\n"),
4095                              rfx_script_from,  rfx_script_to, ret);
4096             widget_opts.non_modal = FALSE;
4097           } else {
4098             d_print_done();
4099           }
4100           lives_free(rfx_script_from);
4101           lives_free(rfx_script_to);
4102         }
4103         if (nmlist) {
4104           lives_list_free_all(&nmlist);
4105         }
4106         lives_free(xname);
4107       }
4108     }
4109   } while (!OK);
4110 
4111   lives_widget_destroy(dialog);
4112   return name;
4113 }
4114 
4115 
populate_script_combo(LiVESCombo * script_combo,lives_rfx_status_t status)4116 static void populate_script_combo(LiVESCombo * script_combo, lives_rfx_status_t status) {
4117   LiVESList *list = NULL;
4118   lives_combo_populate(script_combo, (list = get_script_list(status)));
4119   if (list) {
4120     lives_combo_set_active_index(script_combo, 0);
4121     lives_widget_set_sensitive(copy_script_okbutton, TRUE);
4122     lives_list_free_all(&list);
4123   } else {
4124     lives_combo_set_active_string(script_combo, "");
4125     lives_widget_set_sensitive(copy_script_okbutton, FALSE);
4126   }
4127 }
4128 
4129 
on_script_status_changed(LiVESCombo * status_combo,livespointer user_data)4130 static void on_script_status_changed(LiVESCombo * status_combo, livespointer user_data) {
4131   const char *text = lives_combo_get_active_text(status_combo);
4132   LiVESWidget *script_combo = (LiVESWidget *)user_data;
4133 
4134   if (!script_combo || !LIVES_IS_COMBO(script_combo)) return;
4135 
4136   if (!(strcmp(text, mainw->string_constants[LIVES_STRING_CONSTANT_BUILTIN]))) {
4137     populate_script_combo(LIVES_COMBO(script_combo), RFX_STATUS_BUILTIN);
4138   } else {
4139     if (!(strcmp(text, mainw->string_constants[LIVES_STRING_CONSTANT_CUSTOM]))) {
4140       populate_script_combo(LIVES_COMBO(script_combo), RFX_STATUS_CUSTOM);
4141     } else {
4142       if (!(strcmp(text, mainw->string_constants[LIVES_STRING_CONSTANT_TEST]))) {
4143         populate_script_combo(LIVES_COMBO(script_combo), RFX_STATUS_TEST);
4144       }
4145     }
4146   }
4147 }
4148 
4149 
get_script_list(lives_rfx_status_t status)4150 LiVESList *get_script_list(lives_rfx_status_t status) {
4151   LiVESList *script_list = NULL;
4152 
4153   switch (status) {
4154   case RFX_STATUS_TEST:
4155     script_list = get_plugin_list(PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS, TRUE, NULL, LIVES_FILE_EXT_RFX_SCRIPT);
4156     break;
4157   case RFX_STATUS_CUSTOM:
4158     script_list = get_plugin_list(PLUGIN_RENDERED_EFFECTS_CUSTOM_SCRIPTS, TRUE, NULL, LIVES_FILE_EXT_RFX_SCRIPT);
4159     break;
4160   case RFX_STATUS_BUILTIN:
4161   case RFX_STATUS_COPY:
4162     script_list = get_plugin_list(PLUGIN_RENDERED_EFFECTS_BUILTIN_SCRIPTS, TRUE, NULL, LIVES_FILE_EXT_RFX_SCRIPT);
4163     break;
4164   default:
4165     break;
4166   }
4167   return script_list;
4168 }
4169 
4170 static int rfx_slot_count = 0;
4171 
add_rfx_effects(lives_rfx_status_t status)4172 void add_rfx_effects(lives_rfx_status_t status) {
4173   // scan render plugin directories, create a rfx array, and add each to the appropriate menu area
4174   LiVESList *rfx_builtin_list = NULL, *rfx_custom_list = NULL, *rfx_test_list = NULL;
4175 
4176   lives_rfx_t *rfx = NULL;
4177   lives_rfx_t *rendered_fx;
4178 
4179 #if LIVES_HAS_IMAGE_MENU_ITEM
4180   LiVESWidget *rfx_image;
4181 #endif
4182   int i, plugin_idx;
4183 
4184   int rfx_builtin_list_length, rfx_custom_list_length, rfx_test_list_length;
4185   int rfx_list_length, old_list_length;
4186 
4187   // reset / reload values
4188   if (status == RFX_STATUS_ANY)
4189     mainw->num_rendered_effects_builtin = 0;
4190   if (status != RFX_STATUS_TEST)
4191     mainw->num_rendered_effects_custom = 0;
4192   if (status != RFX_STATUS_CUSTOM)
4193     mainw->num_rendered_effects_test = 0;
4194 
4195   rfx_builtin_list_length = mainw->num_rendered_effects_builtin;
4196   rfx_custom_list_length = mainw->num_rendered_effects_custom;
4197   rfx_test_list_length = mainw->num_rendered_effects_test;
4198 
4199   old_list_length = rfx_builtin_list_length + rfx_custom_list_length + rfx_test_list_length;
4200 
4201   if (status != RFX_STATUS_TEST) {
4202     mainw->has_custom_effects = mainw->has_custom_tools = mainw->has_custom_gens = mainw->has_custom_utilities = FALSE;
4203   }
4204 
4205   if (status != RFX_STATUS_ANY) {
4206     for (i = 1; i <= mainw->num_rendered_effects_builtin + mainw->num_rendered_effects_custom
4207          + mainw->num_rendered_effects_test; i++) {
4208       if (mainw->rendered_fx[i].status == status) {
4209         if (mainw->rendered_fx[i].menuitem) {
4210           lives_widget_destroy(mainw->rendered_fx[i].menuitem);
4211           mainw->rendered_fx[i].menuitem = NULL;
4212         }
4213       }
4214     }
4215     threaded_dialog_spin(0.);
4216   }
4217 
4218   if (status == RFX_STATUS_CUSTOM) {
4219     if (mainw->custom_effects_separator) lives_widget_destroy(mainw->custom_effects_separator);
4220     if (mainw->custom_effects_menu) lives_widget_destroy(mainw->custom_effects_menu);
4221     if (mainw->custom_effects_submenu) lives_widget_destroy(mainw->custom_effects_submenu);
4222     if (mainw->custom_gens_menu) lives_widget_destroy(mainw->custom_gens_menu);
4223     if (mainw->custom_gens_submenu) lives_widget_destroy(mainw->custom_gens_submenu);
4224     if (mainw->gens_menu) lives_widget_destroy(mainw->gens_menu);
4225 
4226     if (mainw->custom_utilities_separator) lives_widget_destroy(mainw->custom_utilities_separator);
4227     if (mainw->custom_utilities_menu) lives_widget_destroy(mainw->custom_utilities_menu);
4228     if (mainw->custom_utilities_submenu) lives_widget_destroy(mainw->custom_utilities_submenu);
4229     if (mainw->custom_tools_menu) lives_widget_destroy(mainw->custom_tools_menu);
4230     if (mainw->utilities_menu) lives_widget_destroy(mainw->utilities_menu);
4231 
4232     mainw->custom_effects_separator = mainw->custom_effects_menu = mainw->custom_effects_submenu
4233                                       = mainw->custom_gens_menu = mainw->custom_gens_submenu = mainw->gens_menu =
4234                                             mainw->custom_utilities_separator = mainw->custom_utilities_menu
4235                                                 = mainw->custom_utilities_submenu
4236                                                     = mainw->custom_tools_menu = mainw->utilities_menu = NULL;
4237   }
4238 
4239   if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4240 
4241   // reset / reload values
4242   if (status == RFX_STATUS_ANY)
4243     mainw->num_rendered_effects_builtin = 0;
4244   if (status != RFX_STATUS_TEST)
4245     mainw->num_rendered_effects_custom = 0;
4246   if (status != RFX_STATUS_CUSTOM)
4247     mainw->num_rendered_effects_test = 0;
4248 
4249   //if (status != RFX_STATUS_TEST) make_custom_submenus();
4250 
4251   if (status != RFX_STATUS_CUSTOM) {
4252     //mainw->run_test_rfx_menu = lives_standard_menu_new();
4253     rfx_test_list = get_plugin_list(PLUGIN_RENDERED_EFFECTS_TEST, FALSE, NULL, NULL);
4254     rfx_test_list_length = lives_list_length(rfx_test_list);
4255   }
4256 
4257   if (status != RFX_STATUS_TEST) {
4258     rfx_custom_list = get_plugin_list(PLUGIN_RENDERED_EFFECTS_CUSTOM, FALSE, NULL, NULL);
4259     rfx_custom_list_length = lives_list_length(rfx_custom_list);
4260   }
4261 
4262   if (prefs->load_rfx_builtin) {
4263     if (status == RFX_STATUS_ANY) {
4264       if (!(rfx_builtin_list = get_plugin_list(PLUGIN_RENDERED_EFFECTS_BUILTIN, FALSE, NULL, NULL))) {
4265         do_rendered_fx_dialog();
4266       } else {
4267         rfx_builtin_list_length = lives_list_length(rfx_builtin_list);
4268       }
4269     }
4270   }
4271 
4272   rfx_list_length = rfx_builtin_list_length + rfx_custom_list_length + rfx_test_list_length;
4273 
4274   if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4275 
4276   rendered_fx = (lives_rfx_t *)lives_calloc((rfx_list_length + 1), sizeof(lives_rfx_t));
4277 
4278   if (status != RFX_STATUS_ANY) {
4279     // add others to rendered_fx
4280     for (i = 0; i <= old_list_length; i++) {
4281       if (mainw->rendered_fx[i].status != status) {
4282         rfx_copy(&rendered_fx[rfx_slot_count++], &mainw->rendered_fx[i], FALSE);
4283       }
4284     }
4285     lives_free(mainw->rendered_fx);
4286     mainw->rendered_fx = NULL;
4287   } else {
4288     rendered_fx[0].name = lives_strdup("realtime_fx");
4289     rendered_fx[0].menu_text = (_("_Apply Real Time Effects to Selection"));
4290     rendered_fx[0].action_desc = (_("Applying Current Real Time Effects to"));
4291 
4292     rendered_fx[0].props = 0;
4293     rendered_fx[0].rfx_version[0] = 0;
4294     rendered_fx[0].num_params = 0;
4295     rendered_fx[0].num_in_channels = 1;
4296     rendered_fx[0].menuitem = NULL;
4297     rendered_fx[0].params = NULL;
4298     rendered_fx[0].flags = 0;
4299     rendered_fx[0].gui_strings = NULL;
4300     rendered_fx[0].onchange_strings = NULL;
4301     rendered_fx[0].status = RFX_STATUS_WEED;
4302     rendered_fx[0].is_template = FALSE;
4303     rendered_fx[0].min_frames = 1;
4304     rendered_fx[0].source = NULL;
4305     rendered_fx[0].source_type = LIVES_RFX_SOURCE_RFX;
4306     rfx_slot_count = 1;
4307   }
4308 
4309   if (rfx_list_length) {
4310     LiVESList *define = NULL;
4311     LiVESList *description = NULL;
4312     LiVESList *props = NULL;
4313     LiVESList *rfx_list = rfx_builtin_list;
4314 
4315     lives_rfx_status_t xstatus = RFX_STATUS_BUILTIN;
4316 
4317     char *type = lives_strdup(PLUGIN_RENDERED_EFFECTS_BUILTIN);
4318     char *plugin_name;
4319     char *def = NULL;
4320     char delim[2];
4321 
4322     int offset = 0;
4323 
4324     for (plugin_idx = 0; plugin_idx < rfx_list_length; plugin_idx++) {
4325       if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4326 #ifndef BG_LOAD
4327       if (!mainw->splash_window) lives_widget_context_update();
4328 #endif
4329       if (plugin_idx == rfx_builtin_list_length) {
4330         lives_free(type);
4331         type = lives_strdup(PLUGIN_RENDERED_EFFECTS_CUSTOM);
4332         xstatus = RFX_STATUS_CUSTOM;
4333         rfx_list = rfx_custom_list;
4334         offset = rfx_builtin_list_length;
4335       }
4336       if (plugin_idx == rfx_builtin_list_length + rfx_custom_list_length) {
4337         lives_free(type);
4338         type = lives_strdup(PLUGIN_RENDERED_EFFECTS_TEST);
4339         xstatus = RFX_STATUS_TEST;
4340         rfx_list = rfx_test_list;
4341         offset += rfx_custom_list_length;
4342       }
4343 
4344       if (status != RFX_STATUS_ANY && xstatus != status) continue;
4345 
4346       plugin_name = lives_strdup((char *)lives_list_nth_data(rfx_list, plugin_idx - offset));
4347 
4348 #ifndef BG_LOAD
4349       if (mainw->splash_window) {
4350         splash_msg((tmp = lives_strdup_printf(_("Loading rendered effect %s..."), plugin_name)), SPLASH_LEVEL_LOAD_RFX);
4351         lives_free(tmp);
4352       }
4353 #endif
4354 
4355 #ifdef DEBUG_RENDER_FX
4356       g_print("Checking plugin %s\n", plugin_name);
4357 #endif
4358 
4359       if ((define = plugin_request_by_line(type, plugin_name, "get_define")) == NULL) {
4360 #ifdef DEBUG_RENDER_FX
4361         g_print("No get_define in %s\n", plugin_name);
4362 #endif
4363         lives_free(plugin_name);
4364         continue;
4365       }
4366 
4367       def = lives_strdup((char *)lives_list_nth_data(define, 0));
4368       lives_list_free_all(&define);
4369 
4370       if (strlen(def) < 2) {
4371 #ifdef DEBUG_RENDER_FX
4372         g_print("Invalid get_define in %s\n", plugin_name);
4373 #endif
4374         lives_free(plugin_name);
4375         lives_free(def);
4376         continue;
4377       }
4378       if (make_version_hash(def + 1) > make_version_hash(RFX_VERSION)) {
4379 #ifdef DEBUG_RENDER_FX
4380         g_print("Invalid version %s instead of %s in %s\n", def + 1, RFX_VERSION, plugin_name);
4381 #endif
4382         lives_free(plugin_name);
4383         lives_free(def);
4384         continue;
4385       }
4386 
4387       delim[0] = def[0];
4388       delim[1] = 0;
4389 
4390       if ((description = plugin_request_common(type, plugin_name, "get_description", delim, TRUE)) &&
4391           (props = plugin_request_common(type, plugin_name, "get_capabilities", delim, FALSE)) &&
4392           lives_list_length(description) > 3) {
4393         rfx = &rendered_fx[rfx_slot_count++];
4394         rfx->name = lives_strdup(plugin_name);
4395         lives_memcpy(rfx->delim, delim, 2);
4396         rfx->menu_text = lives_strdup((char *)lives_list_nth_data(description, 0));
4397         rfx->action_desc = lives_strdup((char *)lives_list_nth_data(description, 1));
4398         if (!(rfx->min_frames = atoi((char *)lives_list_nth_data(description, 2)))) rfx->min_frames = 1;
4399         rfx->num_in_channels = atoi((char *)lives_list_nth_data(description, 3));
4400         rfx->status = xstatus;
4401         lives_snprintf(rfx->rfx_version, 64, "%s", def + 1);
4402         rfx->props = atoi((char *)lives_list_nth_data(props, 0));
4403         rfx->num_params = 0;
4404         rfx->is_template = FALSE;
4405         rfx->params = NULL;
4406         rfx->source = NULL;
4407         rfx->source_type = LIVES_RFX_SOURCE_RFX;
4408         rfx->flags = 0;
4409         rfx->gui_strings = NULL;
4410         rfx->onchange_strings = NULL;
4411         rfx->is_template = FALSE;
4412         if (!check_rfx_for_lives(rfx)) rfx_slot_count--;
4413       }
4414 
4415       lives_free(plugin_name);
4416       if (props) {
4417         lives_list_free_all(&props);
4418         props = NULL;
4419       }
4420       if (description) {
4421         lives_list_free_all(&description);
4422         description = NULL;
4423       }
4424       lives_free(def);
4425     }
4426 
4427     if (rfx_builtin_list) lives_list_free_all(&rfx_builtin_list);
4428     if (rfx_custom_list) lives_list_free_all(&rfx_custom_list);
4429     if (rfx_test_list) lives_list_free_all(&rfx_test_list);
4430 
4431     lives_free(type);
4432   }
4433 
4434   lives_freep((void **)&mainw->rendered_fx);
4435 
4436   rfx_slot_count--;
4437 
4438   if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4439 
4440   // sort menu text by alpha order (apart from [0]) and create mainw->rendered_fx
4441   sort_rfx_array(rendered_fx, rfx_slot_count); // each rendered_fx is copied and free()d
4442   lives_free(rendered_fx);
4443 
4444   if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4445 
4446   // now we need to add to the effects menu and set a callback
4447   for (rfx = &mainw->rendered_fx[(plugin_idx = 1)]; plugin_idx <= rfx_slot_count; rfx = &mainw->rendered_fx[++plugin_idx]) {
4448     if (status != RFX_STATUS_ANY && rfx->status != status) continue;
4449 
4450     if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4451 
4452     render_fx_get_params(rfx, rfx->name, rfx->status);
4453     if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4454     rfx->source = NULL;
4455     rfx->flags = 0;
4456     rfx->gui_strings = NULL;
4457     rfx->onchange_strings = NULL;
4458     rfx->menuitem = NULL;
4459 
4460     switch (rfx->status) {
4461     case RFX_STATUS_BUILTIN:
4462       mainw->num_rendered_effects_builtin++; break;
4463     case RFX_STATUS_CUSTOM:
4464       mainw->num_rendered_effects_custom++; break;
4465     case RFX_STATUS_TEST:
4466       mainw->num_rendered_effects_test++; break;
4467     default: break;
4468     }
4469   }
4470 
4471   if (status != RFX_STATUS_ANY) {
4472     if (mainw->num_rendered_effects_test) {
4473       lives_widget_set_sensitive(mainw->run_test_rfx_submenu, TRUE);
4474       lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->run_test_rfx_submenu), mainw->run_test_rfx_menu);
4475       add_rfx_effects2(status);
4476     }
4477   }
4478 }
4479 
4480 
add_rfx_effects2(lives_rfx_status_t status)4481 void add_rfx_effects2(lives_rfx_status_t status) {
4482   LiVESWidget *menuitem;
4483   lives_rfx_t *rfx;
4484   char txt[64]; // menu text
4485   int plugin_idx, tool_posn = RFX_TOOL_MENU_POSN;
4486 
4487   if (status == RFX_STATUS_ANY) {
4488     // recreate effects menu
4489     LiVESWidget *menuitem = lives_standard_menu_item_new_with_label(mainw->rendered_fx[0].menu_text);
4490 
4491     lives_widget_set_sensitive(menuitem, FALSE);
4492     lives_widget_set_tooltip_text(menuitem, _("See: VJ - show VJ keys. Set the realtime effects, and then apply them here."));
4493 
4494     lives_widget_add_accelerator(menuitem, LIVES_WIDGET_ACTIVATE_SIGNAL, mainw->accel_group,
4495                                  LIVES_KEY_e, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
4496 
4497     lives_signal_sync_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
4498                          LIVES_GUI_CALLBACK(on_realfx_activate), &mainw->rendered_fx[0]);
4499 
4500     mainw->rendered_fx[0].menuitem = menuitem;
4501     mainw->rendered_fx[0].num_in_channels = 1;
4502 
4503     if (!LIVES_IS_PLAYING && mainw->current_file > 0 &&
4504         ((has_video_filters(TRUE) && !has_video_filters(FALSE)) ||
4505          (cfile->achans > 0 && prefs->audio_src == AUDIO_SRC_INT && has_audio_filters(AF_TYPE_ANY)) ||
4506          mainw->agen_key != 0)) {
4507       lives_widget_set_sensitive(menuitem, TRUE);
4508     } else lives_widget_set_sensitive(menuitem, FALSE);
4509   }
4510 
4511   if (mainw->num_rendered_effects_test) {
4512     lives_widget_set_sensitive(mainw->run_test_rfx_submenu, TRUE);
4513     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->run_test_rfx_submenu), mainw->run_test_rfx_menu);
4514   }
4515 
4516   if (status != RFX_STATUS_TEST) make_custom_submenus();
4517 
4518   if (status == RFX_STATUS_TEST && mainw->run_test_rfx_menu) {
4519     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->run_test_rfx_submenu), NULL);
4520     if (LIVES_IS_WIDGET(mainw->run_test_rfx_menu)) lives_widget_destroy(mainw->run_test_rfx_menu);
4521   }
4522 
4523   if (status != RFX_STATUS_TEST) {
4524     mainw->custom_effects_menu = lives_standard_menu_new();
4525     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->custom_effects_submenu), mainw->custom_effects_menu);
4526 
4527     mainw->custom_tools_menu = lives_standard_menu_new();
4528     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->custom_tools_submenu), mainw->custom_tools_menu);
4529   }
4530 
4531   if (status == RFX_STATUS_ANY && mainw->rendered_fx[0].menuitem) {
4532     lives_container_add(LIVES_CONTAINER(mainw->effects_menu), mainw->rendered_fx[0].menuitem);
4533     mainw->rte_separator = lives_menu_add_separator(LIVES_MENU(mainw->effects_menu));
4534   }
4535 
4536   if (status != RFX_STATUS_TEST) {
4537     lives_menu_shell_insert(LIVES_MENU_SHELL(mainw->effects_menu), mainw->custom_effects_submenu, 2);
4538     mainw->custom_effects_separator = lives_menu_item_new();
4539     lives_widget_set_sensitive(mainw->custom_effects_separator, FALSE);
4540     lives_menu_shell_insert(LIVES_MENU_SHELL(mainw->effects_menu), mainw->custom_effects_separator, 3);
4541   }
4542 
4543   if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4544 
4545   if (status != RFX_STATUS_TEST) {
4546     // recreate menus for effects, utilities, generators
4547     mainw->utilities_menu = lives_standard_menu_new();
4548     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->utilities_submenu), mainw->utilities_menu);
4549 
4550     mainw->custom_utilities_menu = lives_standard_menu_new();
4551     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->custom_utilities_submenu), mainw->custom_utilities_menu);
4552 
4553     lives_container_add(LIVES_CONTAINER(mainw->utilities_menu), mainw->custom_utilities_submenu);
4554     mainw->custom_utilities_separator = lives_menu_add_separator(LIVES_MENU(mainw->utilities_menu));
4555 
4556     mainw->gens_menu = lives_standard_menu_new();
4557     if (!mainw->multitrack)
4558       lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->gens_submenu), mainw->gens_menu);
4559     else
4560       lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->multitrack->gens_submenu), mainw->gens_menu);
4561 
4562     mainw->custom_gens_menu = lives_standard_menu_new();
4563     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->custom_gens_submenu), mainw->custom_gens_menu);
4564     lives_container_add(LIVES_CONTAINER(mainw->gens_menu), mainw->custom_gens_submenu);
4565 
4566     if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4567 
4568     if (mainw->fx_candidates[FX_CANDIDATE_RESIZER].delegate == -1) mainw->resize_menuitem = NULL;
4569   }
4570 
4571   if (rfx_slot_count) {
4572     for (rfx = &mainw->rendered_fx[(plugin_idx = 1)]; plugin_idx <= rfx_slot_count; rfx = &mainw->rendered_fx[++plugin_idx]) {
4573       if (rfx->status == RFX_STATUS_TEST) {
4574         if (status == RFX_STATUS_CUSTOM) continue;
4575       } else if (status == RFX_STATUS_TEST) continue;
4576 
4577       if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4578 
4579       lives_snprintf(txt, 61, "_%s", _(rfx->menu_text));
4580       if (rfx->num_params) lives_strappend(txt, 64, "...");
4581       menuitem = lives_standard_menu_item_new_with_label(txt);
4582       rfx->menuitem = menuitem;
4583 
4584       if ((rfx->props & RFX_PROPS_MAY_RESIZE && rfx->num_in_channels == 1) || rfx->min_frames < 0) {
4585         // add resizing effects to tools menu
4586         if (!strcmp(rfx->name, "resize")) {
4587           if (mainw->fx_candidates[FX_CANDIDATE_RESIZER].delegate == -1) {
4588             mainw->resize_menuitem = menuitem;
4589           } else continue;
4590         }
4591 
4592         switch (rfx->status) {
4593         case RFX_STATUS_BUILTIN:
4594           if (rfx->min_frames >= 0) {
4595             if (status == RFX_STATUS_CUSTOM) continue;
4596             lives_menu_shell_insert(LIVES_MENU_SHELL(mainw->tools_menu), menuitem, tool_posn++);
4597           } else {
4598             lives_container_add(LIVES_CONTAINER(mainw->utilities_menu), menuitem);
4599           }
4600           break;
4601         case RFX_STATUS_CUSTOM:
4602           if (rfx->min_frames >= 0) {
4603             lives_container_add(LIVES_CONTAINER(mainw->custom_tools_menu), menuitem);
4604             mainw->has_custom_tools = TRUE;
4605           } else {
4606             lives_container_add(LIVES_CONTAINER(mainw->custom_utilities_menu), menuitem);
4607             mainw->has_custom_utilities = TRUE;
4608           }
4609           break;
4610         case RFX_STATUS_TEST:
4611           lives_container_add(LIVES_CONTAINER(mainw->run_test_rfx_menu), menuitem);
4612           break;
4613         default:
4614           break;
4615         }
4616 
4617         if (menuitem != mainw->resize_menuitem) {
4618           if (!rfx->params) {
4619             lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
4620                                  LIVES_GUI_CALLBACK(on_render_fx_activate), (livespointer)rfx);
4621           } else {
4622             lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
4623                                  LIVES_GUI_CALLBACK(on_render_fx_pre_activate), (livespointer)rfx);
4624           }
4625         } else {
4626           mainw->fx_candidates[FX_CANDIDATE_RESIZER].func
4627             = lives_signal_sync_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
4628                                    LIVES_GUI_CALLBACK(on_render_fx_pre_activate), (livespointer)rfx);
4629         }
4630 
4631         if (rfx->min_frames >= 0) lives_widget_set_sensitive(menuitem, FALSE);
4632       } else {
4633         if (rfx->num_in_channels == 0) {
4634           // non-realtime generator
4635           switch (rfx->status) {
4636           case RFX_STATUS_BUILTIN:
4637             lives_container_add(LIVES_CONTAINER(mainw->gens_menu), menuitem); break;
4638           case RFX_STATUS_CUSTOM:
4639             lives_container_add(LIVES_CONTAINER(mainw->custom_gens_menu), menuitem);
4640             mainw->has_custom_gens = TRUE; break;
4641           case RFX_STATUS_TEST:
4642             lives_container_add(LIVES_CONTAINER(mainw->run_test_rfx_menu), menuitem); break;
4643           default: break;
4644           }
4645           lives_signal_sync_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
4646                                LIVES_GUI_CALLBACK(on_render_fx_pre_activate), (livespointer)rfx);
4647         } else {
4648 #if LIVES_HAS_IMAGE_MENU_ITEM
4649           rfx_image = NULL;
4650           if (rfx->props & RFX_PROPS_SLOW) {
4651             rfx_image = lives_image_new_from_stock(LIVES_STOCK_NO, LIVES_ICON_SIZE_MENU);
4652           } else {
4653             rfx_image = lives_image_new_from_stock(LIVES_STOCK_YES, LIVES_ICON_SIZE_MENU);
4654           }
4655           lives_image_menu_item_set_image(LIVES_IMAGE_MENU_ITEM(menuitem), rfx_image);
4656 #endif
4657           if (rfx->num_in_channels == 1) {
4658             lives_container_add(LIVES_CONTAINER(mainw->effects_menu), menuitem);
4659           }
4660           if (!rfx->params) {
4661             lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
4662                                  LIVES_GUI_CALLBACK(on_render_fx_activate), (livespointer)rfx);
4663           } else {
4664             lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
4665                                  LIVES_GUI_CALLBACK(on_render_fx_pre_activate), (livespointer)rfx);
4666           }
4667           if (rfx->min_frames >= 0) lives_widget_set_sensitive(menuitem, FALSE);
4668 	  // *INDENT-OFF*
4669 	}}}}
4670   // *INDENT-ON*
4671 
4672   if (status != RFX_STATUS_ANY) threaded_dialog_spin(0.);
4673 
4674   update_rfx_menus();
4675 
4676   if (status != RFX_STATUS_ANY) {
4677     threaded_dialog_spin(0.);
4678     if (CURRENT_CLIP_IS_VALID && !LIVES_IS_PLAYING) sensitize_rfx();
4679     threaded_dialog_spin(0.);
4680   }
4681 }
4682 
4683 
update_rfx_menus(void)4684 void update_rfx_menus(void) {
4685   if (prefs->show_gui) {
4686     if (!mainw->has_custom_effects) {
4687       if (mainw->custom_effects_separator)
4688         lives_widget_hide(mainw->custom_effects_separator);
4689     } else {
4690       lives_widget_set_no_show_all(mainw->custom_effects_submenu, FALSE);
4691       lives_widget_show_all(mainw->custom_effects_submenu);
4692       lives_widget_show(mainw->custom_effects_separator);
4693     }
4694 
4695     if (mainw->has_custom_tools) {
4696       lives_widget_set_no_show_all(mainw->custom_tools_submenu, FALSE);
4697     } else {
4698       lives_widget_set_no_show_all(mainw->custom_tools_submenu, TRUE);
4699       lives_widget_hide(mainw->custom_tools_submenu);
4700     }
4701 
4702     if (mainw->has_custom_utilities) {
4703       lives_widget_set_no_show_all(mainw->custom_utilities_submenu, FALSE);
4704     }
4705 
4706     lives_widget_show_all(mainw->tools_menu);
4707 
4708     if (!mainw->has_custom_utilities) {
4709       if (mainw->custom_utilities_separator) lives_widget_hide(mainw->custom_utilities_separator);
4710     }
4711 
4712     if (!mainw->has_custom_tools) {
4713       lives_widget_hide(mainw->custom_tools_separator);
4714     }
4715 
4716     if (mainw->has_custom_gens) {
4717       lives_widget_set_no_show_all(mainw->custom_gens_submenu, FALSE);
4718     }
4719     lives_widget_show_all(mainw->gens_submenu);
4720     if (mainw->multitrack) lives_widget_show_all(mainw->multitrack->gens_submenu);
4721   }
4722 
4723   if (mainw->num_rendered_effects_custom > 0) {
4724     lives_widget_set_sensitive(mainw->delete_custom_rfx, TRUE);
4725     lives_widget_set_sensitive(mainw->export_custom_rfx, TRUE);
4726   } else {
4727     lives_widget_set_sensitive(mainw->delete_custom_rfx, FALSE);
4728     lives_widget_set_sensitive(mainw->export_custom_rfx, FALSE);
4729   }
4730 
4731   if (mainw->num_rendered_effects_test > 0) {
4732     lives_widget_show_all(mainw->run_test_rfx_submenu);
4733     lives_widget_set_sensitive(mainw->run_test_rfx_submenu, TRUE);
4734     lives_widget_set_sensitive(mainw->promote_test_rfx, TRUE);
4735     lives_widget_set_sensitive(mainw->delete_test_rfx, TRUE);
4736     lives_widget_set_sensitive(mainw->rename_test_rfx, TRUE);
4737     lives_widget_set_sensitive(mainw->edit_test_rfx, TRUE);
4738   } else {
4739     lives_widget_set_sensitive(mainw->run_test_rfx_submenu, FALSE);
4740     lives_widget_set_sensitive(mainw->promote_test_rfx, FALSE);
4741     lives_widget_set_sensitive(mainw->delete_test_rfx, FALSE);
4742     lives_widget_set_sensitive(mainw->rename_test_rfx, FALSE);
4743     lives_widget_set_sensitive(mainw->edit_test_rfx, FALSE);
4744   }
4745   lives_widget_show_all(mainw->effects_menu);
4746 }
4747 
4748