1 // effects-data.c
2 // LiVES (lives-exe)
3 // (c) G. Finch 2005 - 2019 (salsaman+lives@gmail.com)
4 // Released under the GPL 3 or later
5 // see file ../COPYING for licensing details
6 
7 // functions for chaining and data passing between fx plugins
8 
9 //#define DEBUG_PCONX
10 
11 #include "main.h"
12 #include "effects.h"
13 #include "ce_thumbs.h"
14 #include "omc-learn.h"
15 
16 static lives_pconnect_t *spconx;
17 static lives_cconnect_t *scconx;
18 
19 static boolean do_chan_connected_query(lives_conx_w *, int okey, int omode, int ocnum, boolean is_same_key);
20 static boolean do_param_connected_query(lives_conx_w *, int okey, int omode, int opnum, boolean is_same_key);
21 static void do_param_incompatible_error(lives_conx_w *);
22 
23 static void ptable_row_add_standard_widgets(lives_conx_w *, int idx);
24 static void ptable_row_add_variable_widgets(lives_conx_w *, int idx, int row, int pidx);
25 
26 static void ctable_row_add_standard_widgets(lives_conx_w *, int idx);
27 static void ctable_row_add_variable_widgets(lives_conx_w *, int idx, int row, int cidx);
28 
29 static void padd_clicked(LiVESWidget *button, livespointer user_data);
30 static void cadd_clicked(LiVESWidget *button, livespointer user_data);
31 
32 static void dfxp_changed(LiVESWidget *, livespointer conxwp);
33 
34 static weed_plant_t *active_dummy = NULL;
35 
36 static LiVESTreeModel *pmodel;
37 static LiVESTreeModel *cmodel;
38 
39 static char *lctext;
40 
41 #define BW (40. * widget_opts.scale)
42 #define BH (40. * widget_opts.scale)
43 
44 #ifdef DEBUG_PCONX
dump_connections(void)45 static void dump_connections(void) {
46   lives_pconnect_t *pconx = mainw->pconx;
47   while (pconx) {
48     int p = 0;
49     for (int i = 0; i < pconx->nparams; i++) {
50       int pnum = pconx->params[i];
51       for (int j = 0; j < pconx->nconns[i + 1]; j++) {
52         p++;
53 	// *INDENT-OFF*
54       }}
55     // *INDENT-ON*
56     pconx = pconx->next;
57   }
58 }
59 #endif
60 
get_param_name(weed_plant_t * param,int pnum,boolean is_in)61 static char *get_param_name(weed_plant_t *param, int pnum, boolean is_in) {
62   char *name = weed_get_string_value(param, WEED_LEAF_NAME, NULL);
63   if (!*name) {
64     lives_free(name);
65     name = NULL;
66   }
67   if (!name) {
68     if (is_in) name = lives_strdup_printf(_("In param %d"), pnum);
69     else name = lives_strdup_printf(_("Out param %d"), pnum);
70   }
71   return name;
72 }
73 
74 
get_chan_name(weed_plant_t * chan,int cnum,boolean is_in)75 static char *get_chan_name(weed_plant_t *chan, int cnum, boolean is_in) {
76   char *name = weed_get_string_value(chan, WEED_LEAF_NAME, NULL);
77   if (!*name) {
78     lives_free(name);
79     name = NULL;
80   }
81   if (!name) {
82     if (is_in) name = lives_strdup_printf(_("In channel %d"), cnum);
83     else name = lives_strdup_printf(_("Out channel %d"), cnum);
84   }
85   return name;
86 }
87 
88 
switch_fx_state(int hotkey)89 static void switch_fx_state(int hotkey) {
90   // switch effect state when a connection to ACTIVATE is present
91   uint32_t last_grabbable_effect = mainw->last_grabbable_effect;
92   // use -hotkey to indicate auto
93   rte_key_toggle(-hotkey);
94   mainw->last_grabbable_effect = last_grabbable_effect;
95 }
96 
97 
override_if_active_input(int hotkey)98 void override_if_active_input(int hotkey) {
99   // if we have a connection to ACTIVATE, allow override if the user changes the state from the kbd
100   lives_pconnect_t *pconx = mainw->pconx;
101 
102   int totcons;
103   int imode = rte_key_getmode(hotkey);
104 
105   int i, j;
106 
107   while (pconx) {
108     totcons = 0;
109     j = 0;
110     for (i = 0; i < pconx->nparams; i++) {
111       totcons += pconx->nconns[i];
112       for (; j < totcons; j++) {
113         if (pconx->ikey[j] == hotkey && pconx->imode[j] == imode && pconx->ipnum[j] == FX_DATA_PARAM_ACTIVE) {
114           // out param is "ACTIVATED"
115           // abuse "autoscale" for this
116           pconx->autoscale[j] = TRUE;
117           return;
118 	  // *INDENT-OFF*
119         }}}
120     pconx = pconx->next;
121     // *INDENT-ON*
122   }
123 }
124 
125 
end_override_if_activate_output(int hotkey)126 void end_override_if_activate_output(int hotkey) {
127   // if we activate an effect and it is connected to ACTIVATE another effect, end any user override
128   lives_pconnect_t *pconx = mainw->pconx;
129 
130   int totcons;
131   int omode = rte_key_getmode(hotkey);
132 
133   int j;
134 
135   while (pconx) {
136     if (pconx->okey == hotkey && pconx->omode == omode) {
137       totcons = 0;
138       j = 0;
139       for (int i = 0; i < pconx->nparams; i++) {
140         totcons += pconx->nconns[i];
141         for (; j < totcons; j++) {
142           if (pconx->ipnum[j] == FX_DATA_PARAM_ACTIVE) {
143             // abuse "autoscale" for this
144             pconx->autoscale[j] = FALSE;
145 	    // *INDENT-OFF*
146           }}}}
147     // *INDENT-ON*
148     pconx = pconx->next;
149   }
150 }
151 
152 
pconx_delete_all(void)153 void pconx_delete_all(void) {
154   lives_pconnect_t *pconx = mainw->pconx, *pconx_next;
155   int i;
156 
157   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) pthread_mutex_lock(&mainw->fx_mutex[i]);
158 
159   while (pconx) {
160     pconx_next = pconx->next;
161     lives_free(pconx->params); lives_free(pconx->nconns); lives_free(pconx->last_boolval);
162     lives_free(pconx->ikey); lives_free(pconx->imode); lives_free(pconx->ipnum);
163     lives_free(pconx->autoscale); lives_free(pconx);
164     pconx = pconx_next;
165   }
166   mainw->pconx = NULL;
167 
168   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) pthread_mutex_unlock(&mainw->fx_mutex[i]);
169 }
170 
171 
pconx_new(int okey,int omode)172 static lives_pconnect_t *pconx_new(int okey, int omode) {
173   lives_pconnect_t *pconx = (lives_pconnect_t *)lives_calloc(sizeof(struct _lives_pconnect_t), 1);
174   pconx->okey = okey;
175   pconx->omode = omode;
176   return pconx;
177 }
178 
179 
pconx_copy(lives_pconnect_t * spconx)180 static lives_pconnect_t *pconx_copy(lives_pconnect_t *spconx) {
181   lives_pconnect_t *pconx = NULL, *dpconx, *last_dpconx;
182 
183   int totcons = 0;
184 
185   int j = 0;
186 
187   while (spconx) {
188     dpconx = pconx_new(spconx->okey, spconx->omode);
189     if (!pconx) pconx = dpconx;
190     else last_dpconx->next = dpconx;
191 
192     dpconx->nparams = spconx->nparams;
193 
194     dpconx->nconns = (int *)lives_malloc(dpconx->nparams * sizint);
195     dpconx->params = (int *)lives_malloc(dpconx->nparams * sizint);
196     dpconx->last_boolval = (int *)lives_malloc(dpconx->nparams * sizint);
197 
198     dpconx->ikey = dpconx->imode = dpconx->ipnum = NULL;
199     dpconx->autoscale = NULL;
200 
201     j = 0;
202 
203     for (int i = 0; i < dpconx->nparams; i++) {
204       dpconx->params[i] = spconx->params[i];
205       dpconx->nconns[i] = spconx->nconns[i];
206       dpconx->last_boolval[i] = spconx->last_boolval[i];
207       totcons += dpconx->nconns[i];
208 
209       dpconx->ikey = (int *)lives_realloc(dpconx->ikey, totcons * sizint);
210       dpconx->imode = (int *)lives_realloc(dpconx->imode, totcons * sizint);
211       dpconx->ipnum = (int *)lives_realloc(dpconx->ipnum, totcons * sizint);
212       dpconx->autoscale = (boolean *)lives_realloc(dpconx->autoscale, totcons * sizint);
213 
214       while (j < totcons) {
215         dpconx->ikey[j] = spconx->ikey[j];
216         dpconx->imode[j] = spconx->imode[j];
217         dpconx->ipnum[j] = spconx->ipnum[j];
218         dpconx->autoscale[j] = spconx->autoscale[j];
219         j++;
220       }
221     }
222 
223     spconx = spconx->next;
224     last_dpconx = dpconx;
225   }
226 
227   return pconx;
228 }
229 
230 
pconx_list(int okey,int omode,int opnum)231 char *pconx_list(int okey, int omode, int opnum) {
232   char *st1 = lives_strdup(""), *st2;
233   lives_pconnect_t *pconx = mainw->pconx;
234   int totcons = 0;
235 
236   while (pconx) {
237     if (pconx->okey == okey && pconx->omode == omode) {
238       for (int i = 0; i < pconx->nparams; i++) {
239         if (pconx->params[i] == opnum) {
240           for (int j = totcons; j < totcons + pconx->nconns[i]; j++) {
241             if (!*st1)
242               st2 = lives_strdup_printf("%d %d %d %d", pconx->ikey[j] + 1, pconx->imode[j] + 1, pconx->ipnum[j],
243                                         pconx->autoscale[j]);
244             else
245               st2 = lives_strdup_printf("%s %d %d %d %d", st1, pconx->ikey[j] + 1, pconx->imode[j] + 1,
246                                         pconx->ipnum[j], pconx->autoscale[j]);
247             lives_free(st1);
248             st1 = st2;
249           }
250           return st1;
251         }
252         totcons += pconx->nconns[i];
253       }
254       return st1;
255     }
256     pconx = pconx->next;
257   }
258   return st1;
259 }
260 
261 
pconx_delete(int okey,int omode,int opnum,int ikey,int imode,int ipnum)262 void pconx_delete(int okey, int omode, int opnum, int ikey, int imode, int ipnum) {
263   lives_pconnect_t *pconx = mainw->pconx, *pconx_next, *pconx_prev = NULL;
264 
265   int i, j = 0, k;
266   int totcons = 0, maxcons = 0;
267 
268   if (okey >= 0 && okey != FX_DATA_WILDCARD)
269     for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++)
270       pthread_mutex_lock(&mainw->fx_mutex[i]);
271 
272   while (pconx) {
273     pconx_next = pconx->next;
274 
275 #ifdef DEBUG_PCONX
276     g_print("Deletion check, want %d / %d, found %d / %d\n", okey, omode, pconx->okey, pconx->omode);
277 #endif
278     if ((okey == FX_DATA_WILDCARD || pconx->okey == okey) && (omode == FX_DATA_WILDCARD || pconx->omode == omode)) {
279 
280 #ifdef DEBUG_PCONX
281       g_print("GOT MATCH\n");
282 #endif
283       if (opnum == FX_DATA_WILDCARD && ikey == FX_DATA_WILDCARD && imode == FX_DATA_WILDCARD
284           && ipnum == FX_DATA_WILDCARD) {
285         //g_print("rem all cons from %d %d to any param\n",okey,omode);
286 
287         // delete entire node
288         lives_free(pconx->params); lives_free(pconx->last_boolval); lives_free(pconx->nconns);
289         lives_free(pconx->ikey); lives_free(pconx->imode); lives_free(pconx->ipnum);
290         lives_free(pconx->autoscale); lives_free(pconx);
291         if (mainw->pconx == pconx) mainw->pconx = pconx_next;
292         else pconx_prev->next = pconx_next;
293         if (okey >= 0 && okey != FX_DATA_WILDCARD) for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++)
294             pthread_mutex_unlock(&mainw->fx_mutex[i]);
295         return;
296       }
297 
298       maxcons = 0;
299       totcons = 0;
300       j = 0;
301 
302       for (i = 0; i < pconx->nparams; i++) {
303         maxcons += pconx->nconns[i];
304       }
305 
306       for (i = 0; pconx && i < pconx->nparams; i++) {
307 #ifdef DEBUG_PCONX
308         g_print("Checking oparams, want %d, got %d with %d connections\n", opnum, pconx->params[i], pconx->nconns[i]);
309 #endif
310         totcons += pconx->nconns[i];
311 
312         if (opnum != FX_DATA_WILDCARD && pconx->params[i] != opnum) {
313           j = totcons;
314           continue;
315         }
316 
317         for (; j < totcons; j++) {
318 #ifdef DEBUG_PCONX
319           g_print("For inputs, want %d / %d (%d), found %d / %d (%d)\n", ikey, imode, ipnum, pconx->ikey[j], pconx->imode[j],
320                   pconx->ipnum[j]);
321 #endif
322           if ((ikey == FX_DATA_WILDCARD || pconx->ikey[j] == ikey)
323               && (imode == FX_DATA_WILDCARD || pconx->imode[j] == imode)
324               && (ipnum == FX_DATA_WILDCARD
325                   || (ipnum == FX_DATA_WILDCARD_KEEP_ACTIVATED && pconx->ipnum[j] != FX_DATA_PARAM_ACTIVE)
326                   || pconx->ipnum[j] == ipnum)) {
327 #ifdef DEBUG_PCONX
328             g_print("removing connection to %d / %d param (%d)\n", ikey, imode, ipnum);
329 #endif
330             maxcons--;
331             for (k = j; k < maxcons; k++) {
332               pconx->ikey[k] = pconx->ikey[k + 1];
333               pconx->imode[k] = pconx->imode[k + 1];
334               pconx->ipnum[k] = pconx->ipnum[k + 1];
335               pconx->autoscale[k] = pconx->autoscale[k + 1];
336             }
337 
338             pconx->ikey = (int *)lives_realloc(pconx->ikey, maxcons * sizint);
339             pconx->imode = (int *)lives_realloc(pconx->imode, maxcons * sizint);
340             pconx->ipnum = (int *)lives_realloc(pconx->ipnum, maxcons * sizint);
341             pconx->autoscale = (boolean *)lives_realloc(pconx->autoscale, maxcons * sizint);
342 
343             pconx->nconns[i]--;
344 
345             if (pconx->nconns[i] == 0) {
346               pconx->nparams--;
347               for (k = i; k < pconx->nparams; k++) {
348                 pconx->params[k] = pconx->params[k + 1];
349                 pconx->last_boolval[k] = pconx->last_boolval[k + 1];
350                 pconx->nconns[k] = pconx->nconns[k + 1];
351               }
352 
353               if (pconx->nparams == 0) {
354                 // delete entire node
355                 lives_free(pconx->params); lives_free(pconx->nconns); lives_free(pconx->ikey);
356                 lives_free(pconx->imode); lives_free(pconx->ipnum); lives_free(pconx->last_boolval);
357                 lives_free(pconx->autoscale);
358                 lives_free(pconx);
359                 if (mainw->pconx == pconx) {
360                   mainw->pconx = pconx_next;
361                   pconx = NULL;
362                 } else {
363                   pconx = pconx_prev;
364                   pconx->next = pconx_next;
365                 }
366               } else {
367                 pconx->nconns = (int *)lives_realloc(pconx->nconns, pconx->nparams * sizint);
368 		// *INDENT-OFF*
369 	      }}}}}}
370       // *INDENT-ON*
371 
372     pconx_prev = pconx;
373     pconx = pconx_next;
374   }
375   if (okey >= 0 && okey != FX_DATA_WILDCARD)
376     for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) pthread_mutex_unlock(&mainw->fx_mutex[i]);
377 }
378 
379 
pconx_remap_mode(int key,int omode,int nmode)380 void pconx_remap_mode(int key, int omode, int nmode) {
381   lives_pconnect_t *pconx = mainw->pconx;
382 
383   register int i, j, totcons;
384 
385   while (pconx) {
386     if (pconx->okey == key && pconx->omode == omode) {
387       pconx->omode = nmode;
388     }
389     j = 0;
390     totcons = 0;
391     for (i = 0; i < pconx->nparams; i++) {
392       totcons += pconx->nconns[i];
393       for (; j < totcons; j++) {
394         if (pconx->ikey[j] == key && pconx->imode[j] == omode) {
395           pconx->imode[j] = nmode;
396 	  // *INDENT-OFF*
397         }}}
398     // *INDENT-ON*
399     pconx = pconx->next;
400   }
401 }
402 
403 
pconx_append(lives_pconnect_t * pconx)404 static void pconx_append(lives_pconnect_t *pconx) {
405   lives_pconnect_t *opconx = mainw->pconx;
406   lives_pconnect_t *last_pconx = opconx;
407 
408   while (opconx) {
409     last_pconx = opconx;
410     opconx = opconx->next;
411   }
412 
413   if (last_pconx) last_pconx->next = pconx;
414   if (!mainw->pconx) mainw->pconx = pconx;
415 }
416 
417 
pconx_find(int okey,int omode)418 static lives_pconnect_t *pconx_find(int okey, int omode) {
419   //
420   lives_pconnect_t *pconx = mainw->pconx;
421   while (pconx) {
422     if (pconx->okey == okey && pconx->omode == omode) {
423       return pconx;
424     }
425     pconx = pconx->next;
426   }
427   return NULL;
428 }
429 
430 
pconx_get_numcons(lives_conx_w * conxwp,int pnum)431 static int pconx_get_numcons(lives_conx_w * conxwp, int pnum) {
432   // get displayed number
433   int totcons = 0;
434 
435   if (pnum != FX_DATA_WILDCARD) return conxwp->dispp[pnum + EXTRA_PARAMS_OUT];
436 
437   for (int j = 0; j < conxwp->num_params; j++) {
438     totcons += conxwp->dispp[j];
439   }
440 
441   return totcons;
442 }
443 
444 
pconx_get_nconns(lives_pconnect_t * pconx,int pnum)445 static int pconx_get_nconns(lives_pconnect_t *pconx, int pnum) {
446   // get actual number of connections
447   int totcons = 0;
448 
449   if (!pconx) return 0;
450 
451   for (int j = 0; j < pconx->nparams; j++) {
452     if (pnum != FX_DATA_WILDCARD) {
453       if (pconx->params[j] == pnum) return pconx->nconns[j];
454     } else totcons += pconx->nconns[j];
455   }
456   return totcons;
457 }
458 
459 
pconx_add_connection_private(int okey,int omode,int opnum,int ikey,int imode,int ipnum,boolean autoscale)460 static lives_pconnect_t *pconx_add_connection_private(int okey, int omode, int opnum, int ikey, int imode, int ipnum,
461     boolean autoscale) {
462   lives_pconnect_t *pconx;
463   int posn = 0, totcons = 0;
464   int i, j;
465 
466   // delete any existing connection to the input param
467   pconx_delete(FX_DATA_WILDCARD, FX_DATA_WILDCARD, FX_DATA_WILDCARD, ikey, imode, ipnum);
468   pconx = pconx_find(okey, omode);
469 
470   if (ikey >= 0) for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) pthread_mutex_lock(&mainw->fx_mutex[i]);
471 
472   if (!pconx) {
473     // add whole new node
474     pconx = pconx_new(okey, omode);
475     pconx_append(pconx);
476   } else {
477     // see if already in params
478     for (i = 0; i < pconx->nparams; i++) {
479       if (pconx->params[i] == opnum) {
480         // located !
481         // add connection to existing
482 
483         for (j = 0; j < pconx->nparams; j++) {
484           if (j < i) {
485             // calc posn
486             posn += pconx->nconns[j];
487           }
488           totcons += pconx->nconns[j];
489         }
490 
491         // if already there, do not add again, just update autoscale
492         for (j = posn; j < posn + pconx->nconns[i]; j++) {
493           if (pconx->ikey[j] == ikey && pconx->imode[j] == imode && pconx->ipnum[j] == ipnum) {
494             pconx->autoscale[j] = autoscale;
495             if (ikey >= 0) for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) pthread_mutex_unlock(&mainw->fx_mutex[i]);
496             return pconx;
497           }
498 
499           // add in order key/mode/chan
500           if (pconx->ikey[j] > ikey || (pconx->ikey[j] == ikey && pconx->imode[j] > imode) ||
501               (pconx->ikey[j] == ikey && pconx->imode[j] == imode && pconx->ipnum[j] > ipnum)) break;
502         }
503 
504         // increment nconns for this param
505         pconx->nconns[i]++;
506 
507         totcons++;
508 
509         posn = j;
510 
511         // make space for new
512         pconx->ikey = (int *)lives_realloc(pconx->ikey, totcons * sizint);
513         pconx->imode = (int *)lives_realloc(pconx->imode, totcons * sizint);
514         pconx->ipnum = (int *)lives_realloc(pconx->ipnum, totcons * sizint);
515         pconx->autoscale = (boolean *)lives_realloc(pconx->autoscale, totcons * sizint);
516 
517         // move up 1
518         for (j = totcons - 1; j > posn; j--) {
519           pconx->ikey[j] = pconx->ikey[j - 1];
520           pconx->imode[j] = pconx->imode[j - 1];
521           pconx->ipnum[j] = pconx->ipnum[j - 1];
522           pconx->autoscale[j] = pconx->autoscale[j - 1];
523         }
524 
525         // insert at posn
526         pconx->ikey[posn] = ikey;
527         pconx->imode[posn] = imode;
528         pconx->ipnum[posn] = ipnum;
529         pconx->autoscale[posn] = autoscale;
530 
531         if (ikey >= 0) for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) pthread_mutex_unlock(&mainw->fx_mutex[i]);
532 
533         return pconx;
534       }
535     }
536 
537     // so, we have data for key/mode but this is a new param to be mapped
538 
539     for (i = 0; i < pconx->nparams; i++) {
540       totcons += pconx->nconns[i];
541     }
542 
543     totcons++;
544 
545     pconx->nparams++;
546     posn = pconx->nparams;
547 
548     // make space for new
549     pconx->nconns = (int *)lives_realloc(pconx->nconns, posn * sizint);
550     pconx->params = (int *)lives_realloc(pconx->params, posn * sizint);
551     pconx->last_boolval = (int *)lives_realloc(pconx->last_boolval, posn * sizint);
552 
553     pconx->ikey = (int *)lives_realloc(pconx->ikey, totcons * sizint);
554     pconx->imode = (int *)lives_realloc(pconx->imode, totcons * sizint);
555     pconx->ipnum = (int *)lives_realloc(pconx->ipnum, totcons * sizint);
556     pconx->autoscale = (boolean *)lives_realloc(pconx->autoscale, totcons * sizint);
557 
558     pconx->params[posn - 1] = opnum;
559 
560     pconx->last_boolval[posn - 1] = WEED_FALSE;  // TODO ***: get value from default
561 
562     pconx->nconns[posn - 1] = 1;
563 
564     posn = totcons - 1;
565 
566     // insert at posn
567     pconx->ikey[posn] = ikey;
568     pconx->imode[posn] = imode;
569     pconx->ipnum[posn] = ipnum;
570     pconx->autoscale[posn] = autoscale;
571 
572 #ifdef DEBUG_PCONX
573     g_print("added another pconx from %d %d %d to %d %d %d\n", okey, omode, opnum, ikey, imode, ipnum);
574     dump_connections();
575 #endif
576 
577     if (ikey >= 0) for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) pthread_mutex_unlock(&mainw->fx_mutex[i]);
578 
579     return pconx;
580   }
581 
582   // add new
583 
584   totcons = pconx_get_nconns(pconx, FX_DATA_WILDCARD) + 1;
585   pconx->nparams++;
586 
587   pconx->nconns = (int *)lives_realloc(pconx->params, pconx->nparams * sizint);
588   pconx->nconns[pconx->nparams - 1] = 1;
589 
590   pconx->params = (int *)lives_realloc(pconx->params, pconx->nparams * sizint);
591   pconx->params[pconx->nparams - 1] = opnum;
592 
593   pconx->last_boolval = (int *)lives_realloc(pconx->last_boolval, pconx->nparams * sizint);
594   pconx->last_boolval[pconx->nparams - 1] = WEED_FALSE; // TODO: ****
595 
596   pconx->ikey = (int *)lives_realloc(pconx->ikey, totcons * sizint);
597   pconx->ikey[totcons - 1] = ikey;
598 
599   pconx->imode = (int *)lives_realloc(pconx->imode, totcons * sizint);
600   pconx->imode[totcons - 1] = imode;
601 
602   pconx->ipnum = (int *)lives_realloc(pconx->ipnum, totcons * sizint);
603   pconx->ipnum[totcons - 1] = ipnum;
604 
605   pconx->autoscale = (boolean *)lives_realloc(pconx->autoscale, totcons * sizint);
606   pconx->autoscale[totcons - 1] = autoscale;
607 
608 #ifdef DEBUG_PCONX
609   g_print("added new pconx from %d %d %d to %d %d %d (%d)\n", okey, omode, opnum, ikey, imode, ipnum, autoscale);
610 #endif
611 
612   if (ikey >= 0) for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) pthread_mutex_unlock(&mainw->fx_mutex[i]);
613 
614   return pconx;
615 }
616 
617 
pconx_add_connection(int okey,int omode,int opnum,int ikey,int imode,int ipnum,boolean autoscale)618 void pconx_add_connection(int okey, int omode, int opnum, int ikey, int imode, int ipnum, boolean autoscale) {
619   pconx_add_connection_private(okey, omode, opnum, ikey, imode, ipnum, autoscale);
620 }
621 
622 
pconx_get_out_param(boolean use_filt,int ikey,int imode,int ipnum,int * okey,int * omode,int * opnum,int * autoscale)623 static weed_plant_t *pconx_get_out_param(boolean use_filt, int ikey, int imode, int ipnum, int *okey, int *omode, int *opnum,
624     int *autoscale) {
625   // walk all pconx and find one which has ikey/imode/ipnum as destination
626   // then all we need do is copy the "value" leaf
627 
628   // use_filt is TRUE if we should use the filter template (otherwise we use the instance)
629 
630   // TODO: OMC
631 
632   lives_pconnect_t *pconx = mainw->pconx;
633   weed_plant_t *inst = NULL, *filter = NULL;
634   int totcons, i, j;
635 
636   while (pconx) {
637     if (!use_filt) {
638       if (mainw->is_rendering) return NULL;
639       else {
640         inst = rte_keymode_get_instance(pconx->okey + 1, pconx->omode);
641       }
642       if (inst) filter = weed_instance_get_filter(inst, TRUE); // inst could be NULL if we connected to "Activated"
643       else filter = NULL;
644     } else {
645       inst = NULL;
646       filter = rte_keymode_get_filter(pconx->okey + 1, pconx->omode);
647       if (!filter) {
648         pconx = pconx->next;
649         continue;
650       }
651     }
652     totcons = 0;
653     j = 0;
654     for (i = 0; i < pconx->nparams; i++) {
655       totcons += pconx->nconns[i];
656       for (; j < totcons; j++) {
657         if (pconx->ikey[j] == ikey && pconx->imode[j] == imode && pconx->ipnum[j] == ipnum) {
658           weed_plant_t *param = NULL;
659 
660           // out param is "ACTIVATED"
661           if (pconx->params[i] == FX_DATA_PARAM_ACTIVE) {
662             weed_plant_t *gui = weed_instance_get_gui(inst, FALSE);
663             pthread_mutex_lock(&mainw->fxd_active_mutex);
664             if (!active_dummy) {
665               active_dummy = weed_plant_new(WEED_PLANT_PARAMETER);
666               weed_set_plantptr_value(active_dummy, WEED_LEAF_TEMPLATE, NULL);
667             }
668 
669             weed_set_boolean_value(active_dummy, WEED_LEAF_VALUE, inst != NULL
670                                    && (!gui || !weed_plant_has_leaf(gui, WEED_LEAF_EASE_OUT)));
671             param = active_dummy;
672             pthread_mutex_unlock(&mainw->fxd_active_mutex);
673           } else {
674             if (use_filt) {
675               weed_plant_t **outparams = weed_get_plantptr_array(filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, NULL);
676               if (pconx->params[i] < weed_leaf_num_elements(filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES)) {
677                 param = outparams[pconx->params[i]];
678               }
679               lives_free(outparams);
680             } else {
681               if (!inst) return NULL;
682               param = weed_inst_out_param(inst, pconx->params[i]);
683             }
684           }
685           if (okey) *okey = pconx->okey;
686           if (omode) *omode = pconx->omode;
687           if (opnum) *opnum = pconx->params[i];
688           if (autoscale) *autoscale = pconx->autoscale[j];
689           if (inst) weed_instance_unref(inst);
690           return param;
691         }
692       }
693     }
694     pconx = pconx->next;
695     if (inst) weed_instance_unref(inst);
696   }
697 
698   return NULL;
699 }
700 
701 
params_compatible(weed_plant_t * sparam,weed_plant_t * dparam)702 static boolean params_compatible(weed_plant_t *sparam, weed_plant_t *dparam) {
703   // allowed conversions
704   // type -> type
705 
706   // bool -> double, bool -> int, bool -> string, (bool -> int64)
707   // int -> double, int -> string,  (int -> int64)
708   // double -> string
709   // (int64 -> string)
710 
711   // int[3x]/double[3x] -> colourRGB
712   // int[4x]/double[4x] -> colourRGBA
713   //
714 
715   weed_plant_t *dptmpl = NULL;
716 
717   int dtype = 0, stype = 0;
718   int ndvals = 0, nsvals = 0;
719   int ptype, dflags = 0;
720 
721   if (dparam == active_dummy) {
722     dptmpl = NULL;
723     dtype = WEED_SEED_BOOLEAN;
724     ndvals = 1;
725   } else {
726     if (WEED_PLANT_IS_PARAMETER(dparam)) {
727       dptmpl = weed_get_plantptr_value(dparam, WEED_LEAF_TEMPLATE, NULL);
728       dtype = weed_leaf_seed_type(dparam, WEED_LEAF_VALUE);
729       ndvals = weed_leaf_num_elements(dparam, WEED_LEAF_VALUE);
730     } else {
731       dptmpl = dparam;
732       dtype = weed_leaf_seed_type(dparam, WEED_LEAF_DEFAULT);
733       ndvals = weed_leaf_num_elements(dparam, WEED_LEAF_DEFAULT);
734     }
735   }
736 
737   if (sparam == active_dummy) {
738     stype = WEED_SEED_BOOLEAN;
739     nsvals = 1;
740   } else {
741     if (WEED_PLANT_IS_PARAMETER(sparam)) {
742       stype = weed_leaf_seed_type(sparam, WEED_LEAF_VALUE);
743       nsvals = weed_leaf_num_elements(sparam, WEED_LEAF_VALUE);
744     } else {
745       stype = weed_leaf_seed_type(sparam, WEED_LEAF_DEFAULT);
746       nsvals = weed_leaf_num_elements(sparam, WEED_LEAF_DEFAULT);
747     }
748   }
749 
750   if (dptmpl) {
751     ptype = weed_paramtmpl_get_type(dptmpl);
752     dflags = weed_paramtmpl_get_flags(dptmpl);
753 
754     if (ptype == WEED_PARAM_COLOR) {
755       int cspace = weed_get_int_value(dptmpl, WEED_LEAF_COLORSPACE, NULL);
756       if (cspace == WEED_COLORSPACE_RGB) {
757         if (!(nsvals & 3)) return FALSE;
758       } else if (nsvals & 3) return FALSE;
759     }
760   }
761 
762   if (ndvals > nsvals) {
763     if (!((dflags & WEED_PARAMETER_VARIABLE_SIZE) && !(dflags & WEED_PARAMETER_VALUE_PER_CHANNEL))) return FALSE;
764   }
765 
766   if (dtype == stype) return TRUE;
767 
768   switch (stype) {
769   case WEED_SEED_DOUBLE:
770     if (dtype == WEED_SEED_STRING) return TRUE;
771     return FALSE;
772   case WEED_SEED_INT:
773     if (dtype == WEED_SEED_DOUBLE || dtype == WEED_SEED_STRING) return TRUE;
774     return FALSE;
775   case WEED_SEED_BOOLEAN:
776     if (dtype == WEED_SEED_DOUBLE || dtype == WEED_SEED_INT || dtype == WEED_SEED_STRING) return TRUE;
777     return FALSE;
778   default:
779     return FALSE;
780   }
781 
782   return FALSE;
783 }
784 
785 
pconx_convert_value_data(weed_plant_t * inst,int pnum,int key,weed_plant_t * dparam,int okey,weed_plant_t * sparam,boolean autoscale,boolean is_audio_thread,boolean * toggle_fx)786 static boolean pconx_convert_value_data(weed_plant_t *inst, int pnum, int key, weed_plant_t *dparam, int okey,
787                                         weed_plant_t *sparam, boolean autoscale, boolean is_audio_thread, boolean * toggle_fx) {
788   // try to convert values of various type, if we succeed, copy the "value" and return TRUE (if changed)
789   weed_plant_t *dptmpl = NULL, *sptmpl;
790 
791   double ratio;
792 
793   int dtype = 0, stype, nsvals, ndvals = 0;
794   int ondvals = 0;
795   int nsmin = 0, nsmax = 0;
796   int minct = 0, maxct = 0;
797   int sminct = 0, smaxct = 0;
798   int nmax = 0, nmin = 0;
799   boolean retval = FALSE;
800 
801   int i;
802 
803   if (toggle_fx) *toggle_fx = FALSE;
804 
805   if (dparam == sparam && (dparam != active_dummy || !active_dummy)) return FALSE;
806 
807   if (sparam == active_dummy) {
808     nsvals = 1;
809     sptmpl = NULL;
810     stype = WEED_SEED_BOOLEAN;
811   }
812 
813   nsvals = weed_leaf_num_elements(sparam, WEED_LEAF_VALUE);
814   if (nsvals == 0) return FALSE;
815   sptmpl = weed_param_get_template(sparam);
816   stype = weed_leaf_seed_type(sparam, WEED_LEAF_VALUE);
817 
818   if (dparam != active_dummy) {
819     ondvals = ndvals = weed_leaf_num_elements(dparam, WEED_LEAF_VALUE);
820     dptmpl = weed_param_get_template(dparam);
821     dtype = weed_leaf_seed_type(dparam, WEED_LEAF_VALUE);
822   } else {
823     dtype = WEED_SEED_BOOLEAN;
824     ondvals = ndvals = 1;
825     dptmpl = NULL;
826   }
827 
828   if (!params_compatible(sparam, dparam)) return FALSE;
829 
830   if (ndvals > nsvals) ndvals = nsvals;
831 
832   if (dparam != active_dummy && sparam != active_dummy) {// && autoscale) {
833     if (weed_plant_has_leaf(sptmpl, WEED_LEAF_MIN) && weed_plant_has_leaf(sptmpl, WEED_LEAF_MAX)) {
834       nsmin = weed_leaf_num_elements(sptmpl, WEED_LEAF_MIN);
835       nsmax = weed_leaf_num_elements(sptmpl, WEED_LEAF_MAX);
836     }
837   }
838 
839   if (dptmpl && weed_plant_has_leaf(dptmpl, WEED_LEAF_MAX)) {
840     nmax = weed_leaf_num_elements(dptmpl, WEED_LEAF_MAX);
841     nmin = weed_leaf_num_elements(dptmpl, WEED_LEAF_MIN);
842   }
843 
844   switch (stype) {
845   case WEED_SEED_STRING:
846     switch (dtype) {
847     case WEED_SEED_STRING: {
848       char **valsS, **valss;
849       if ((valsS = weed_get_string_array(sparam, WEED_LEAF_VALUE, NULL)) == NULL) return retval;
850       if ((valss = weed_get_string_array(dparam, WEED_LEAF_VALUE, NULL)) == NULL) return retval;
851 
852       if (ndvals > ondvals) valss = (char **)lives_realloc(valss, ndvals * sizeof(char *));
853 
854       for (i = 0; i < ndvals; i++) {
855         if (i >= ondvals || strcmp(valss[i], valsS[i])) {
856           retval = TRUE;
857           if (i < ondvals) lives_free(valss[i]);
858           valss[i] = valsS[i];
859         } else lives_free(valsS[i]);
860       }
861       if (!retval) {
862         for (i = 0; i < ndvals; i++) lives_free(valss[i]);
863         lives_free(valss);
864         lives_free(valsS);
865         return FALSE;
866       }
867 
868       weed_set_string_array(dparam, WEED_LEAF_VALUE, ndvals, valss);
869 
870       for (i = 0; i < ndvals; i++) lives_free(valss[i]);
871       lives_free(valss);
872       lives_free(valsS);
873     }
874     return TRUE;
875     default:
876       return retval;
877     }
878   case WEED_SEED_DOUBLE:
879     switch (dtype) {
880     case WEED_SEED_DOUBLE: {
881       double *valsD = weed_get_double_array(sparam, WEED_LEAF_VALUE, NULL);
882       double *valsd = weed_get_double_array(dparam, WEED_LEAF_VALUE, NULL);
883 
884       double *maxd = weed_get_double_array(dptmpl, WEED_LEAF_MAX, NULL);
885       double *mind = weed_get_double_array(dptmpl, WEED_LEAF_MIN, NULL);
886 
887       double *mins = NULL, *maxs = NULL;
888 
889       if (autoscale) {
890         mins = weed_get_double_array(sptmpl, WEED_LEAF_MIN, NULL);
891         maxs = weed_get_double_array(sptmpl, WEED_LEAF_MAX, NULL);
892       }
893 
894       if (ndvals > ondvals) valsd = (double *)lives_realloc(valsd, ndvals * sizeof(double));
895 
896       for (i = 0; i < ndvals; i++) {
897         if (autoscale) {
898           ratio = (valsD[i] - mins[sminct]) / (maxs[smaxct] - mins[sminct]);
899           valsD[i] = mind[minct] + (maxd[maxct] - mind[minct]) * ratio;
900           if (++smaxct == nsmax) smaxct = 0;
901           if (++sminct == nsmin) sminct = 0;
902 
903           if (valsD[i] > maxd[maxct]) valsD[i] = maxd[maxct];
904           if (valsD[i] < mind[minct]) valsD[i] = mind[minct];
905         }
906 
907         if (i >= ondvals || valsd[i] != valsD[i]) {
908           retval = TRUE;
909           valsd[i] = valsD[i];
910         }
911         if (++maxct == nmax) maxct = 0;
912         if (++minct == nmin) minct = 0;
913       }
914 
915       if (mins) {
916         lives_free(mins);
917         lives_free(maxs);
918       }
919 
920       if (retval) {
921         if (inst && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
922           // if we are recording, add this change to our event_list
923           rec_param_change(inst, pnum);
924         }
925 
926         weed_set_double_array(dparam, WEED_LEAF_VALUE, ndvals, valsd);
927       }
928       lives_free(maxd); lives_free(mind); lives_free(valsD); lives_free(valsd);
929     }
930     return retval;
931 
932     case WEED_SEED_STRING: {
933       char *opstring, *tmp, *bit;
934       double *valsd = weed_get_double_array(sparam, WEED_LEAF_VALUE, NULL);
935       char **valss, *vals;
936 
937       if (ndvals == 1) {
938         opstring = lives_strdup("");
939         vals = weed_get_string_value(dparam, WEED_LEAF_VALUE, NULL);
940         for (i = 0; i < nsvals; i++) {
941           bit = lives_strdup_printf("%.4f", valsd[i]);
942           if (!*opstring)
943             tmp = lives_strconcat(opstring, bit, NULL);
944           else
945             tmp = lives_strconcat(opstring, " ", bit, NULL);
946           lives_free(bit);
947           lives_free(opstring);
948           opstring = tmp;
949         }
950         if (strcmp(vals, opstring)) {
951           weed_set_string_value(dparam, WEED_LEAF_VALUE, opstring);
952           retval = TRUE;
953         }
954         lives_free(vals); lives_free(valsd); lives_free(opstring);
955         return retval;
956       }
957 
958       valss = weed_get_string_array(dparam, WEED_LEAF_VALUE, NULL);
959 
960       if (ndvals > ondvals) valss = (char **)lives_realloc(valsd, ndvals * sizeof(char *));
961 
962       for (i = 0; i < ndvals; i++) {
963         bit = lives_strdup_printf("%.4f", valsd[i]);
964         if (i >= ondvals || strcmp(valss[i], bit)) {
965           retval = TRUE;
966           if (i < ondvals) lives_free(valss[i]);
967           valss[i] = bit;
968         } else lives_free(bit);
969       }
970       if (!retval) {
971         for (i = 0; i < ndvals; i++) lives_free(valss[i]);
972         lives_free(valss);
973         lives_free(valsd);
974         return FALSE;
975       }
976 
977       weed_set_string_array(dparam, WEED_LEAF_VALUE, ndvals, valss);
978 
979       for (i = 0; i < ndvals; i++) lives_free(valss[i]);
980       lives_free(valss);
981       lives_free(valsd);
982     }
983     return TRUE;
984     default: break;
985     }
986 
987     break;
988 
989   case WEED_SEED_INT:
990     switch (dtype) {
991     case WEED_SEED_STRING: {
992       char *opstring, *tmp, *bit;
993       int *valsi = weed_get_int_array(sparam, WEED_LEAF_VALUE, NULL);
994 
995       char **valss, *vals;
996 
997       if (ndvals == 1) {
998         opstring = lives_strdup("");
999         vals = weed_get_string_value(dparam, WEED_LEAF_VALUE, NULL);
1000         for (i = 0; i < nsvals; i++) {
1001           bit = lives_strdup_printf("%d", valsi[i]);
1002           if (!*opstring)
1003             tmp = lives_strconcat(opstring, bit, NULL);
1004           else
1005             tmp = lives_strconcat(opstring, " ", bit, NULL);
1006           lives_free(bit);
1007           lives_free(opstring);
1008           opstring = tmp;
1009         }
1010         if (strcmp(vals, opstring)) {
1011           weed_set_string_value(dparam, WEED_LEAF_VALUE, opstring);
1012           retval = TRUE;
1013         }
1014         lives_free(vals); lives_free(valsi); lives_free(opstring);
1015         return retval;
1016       }
1017 
1018       valss = weed_get_string_array(dparam, WEED_LEAF_VALUE, NULL);
1019 
1020       if (ndvals > ondvals) valss = (char **)lives_realloc(valss, ndvals * sizeof(char *));
1021 
1022       for (i = 0; i < ndvals; i++) {
1023         bit = lives_strdup_printf("%d", valsi[i]);
1024         if (i >= ondvals || strcmp(valss[i], bit)) {
1025           retval = TRUE;
1026           if (i < ondvals) lives_free(valss[i]);
1027           valss[i] = bit;
1028         } else lives_free(bit);
1029       }
1030       if (!retval) {
1031         for (i = 0; i < ndvals; i++) lives_free(valss[i]);
1032         lives_free(valss); lives_free(valsi);
1033         return FALSE;
1034       }
1035 
1036       weed_set_string_array(dparam, WEED_LEAF_VALUE, ndvals, valss);
1037 
1038       for (i = 0; i < ndvals; i++) lives_free(valss[i]);
1039       lives_free(valss);
1040       lives_free(valsi);
1041     }
1042     return retval;
1043     case WEED_SEED_DOUBLE: {
1044       int *valsi = weed_get_int_array(sparam, WEED_LEAF_VALUE, NULL);
1045       double *valsd = weed_get_double_array(dparam, WEED_LEAF_VALUE, NULL);
1046 
1047       double *maxd = weed_get_double_array(dptmpl, WEED_LEAF_MAX, NULL);
1048       double *mind = weed_get_double_array(dptmpl, WEED_LEAF_MIN, NULL);
1049       double vald;
1050 
1051       int *mins = NULL, *maxs = NULL;
1052 
1053       if (autoscale) {
1054         mins = weed_get_int_array(sptmpl, WEED_LEAF_MIN, NULL);
1055         maxs = weed_get_int_array(sptmpl, WEED_LEAF_MAX, NULL);
1056       }
1057 
1058       if (ndvals > ondvals) valsd = (double *)lives_realloc(valsd, ndvals * sizeof(double));
1059 
1060       for (i = 0; i < ndvals; i++) {
1061         if (autoscale) {
1062           ratio = (double)(valsi[i] - mins[sminct]) / (double)(maxs[smaxct] - mins[sminct]);
1063           vald = mind[minct] + (maxd[maxct] - mind[minct]) * ratio;
1064           if (++smaxct == nsmax) smaxct = 0;
1065           if (++sminct == nsmin) sminct = 0;
1066 
1067           if (vald > maxd[maxct]) vald = maxd[maxct];
1068           if (vald < mind[minct]) vald = mind[minct];
1069         } else vald = (double)valsi[i];
1070 
1071         if (i >= ondvals || valsd[i] != vald) {
1072           retval = TRUE;
1073           valsd[i] = vald;
1074         }
1075         if (++maxct == nmax) maxct = 0;
1076         if (++minct == nmin) minct = 0;
1077       }
1078 
1079       if (mins) {
1080         lives_free(mins);
1081         lives_free(maxs);
1082       }
1083 
1084       if (retval) {
1085         if (inst && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
1086           // if we are recording, add this change to our event_list
1087           rec_param_change(inst, pnum);
1088         }
1089 
1090         weed_set_double_array(dparam, WEED_LEAF_VALUE, ndvals, valsd);
1091       }
1092       lives_free(maxd); lives_free(mind);
1093       lives_free(valsi); lives_free(valsd);
1094     }
1095     return retval;
1096 
1097     case WEED_SEED_INT: {
1098       int *valsI, *valsi, *maxi, *mini;
1099       int *mins = NULL, *maxs = NULL;
1100 
1101       if ((valsI = weed_get_int_array(sparam, WEED_LEAF_VALUE, NULL)) == NULL) return retval;
1102       if ((valsi = weed_get_int_array(dparam, WEED_LEAF_VALUE, NULL)) == NULL) return retval;
1103       if ((maxi = weed_get_int_array(dptmpl, WEED_LEAF_MAX, NULL)) == NULL) return retval;
1104       if ((mini = weed_get_int_array(dptmpl, WEED_LEAF_MIN, NULL)) == NULL) return retval;
1105 
1106       if (autoscale) {
1107         mins = weed_get_int_array(sptmpl, WEED_LEAF_MIN, NULL);
1108         maxs = weed_get_int_array(sptmpl, WEED_LEAF_MAX, NULL);
1109       }
1110 
1111       if (ndvals > ondvals) valsi = (int *)lives_realloc(valsi, ndvals * sizeof(int));
1112 
1113       for (i = 0; i < ndvals; i++) {
1114         if (autoscale) {
1115           ratio = (double)(valsI[i] - mins[sminct]) / (double)(maxs[smaxct] - mins[sminct]);
1116           valsI[i] = myround(mini[minct] + (double)(maxi[maxct] - mini[minct]) * ratio);
1117           if (++smaxct == nsmax) smaxct = 0;
1118           if (++sminct == nsmin) sminct = 0;
1119 
1120           if (valsI[i] > maxi[maxct]) valsI[i] = maxi[maxct];
1121           if (valsI[i] < mini[minct]) valsI[i] = mini[minct];
1122         }
1123 
1124         if (i >= ondvals || valsi[i] != valsI[i]) {
1125           retval = TRUE;
1126           valsi[i] = valsI[i];
1127         }
1128         if (++maxct == nmax) maxct = 0;
1129         if (++minct == nmin) minct = 0;
1130       }
1131 
1132       if (mins) {
1133         lives_free(mins);
1134         lives_free(maxs);
1135       }
1136 
1137       if (retval) {
1138         if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
1139           // if we are recording, add this change to our event_list
1140           rec_param_change(inst, pnum);
1141         }
1142 
1143         weed_set_int_array(dparam, WEED_LEAF_VALUE, ndvals, valsi);
1144       }
1145       lives_free(maxi); lives_free(mini);
1146       lives_free(valsI); lives_free(valsi);
1147     }
1148     return retval;
1149     }
1150     break;
1151 
1152   case WEED_SEED_BOOLEAN: {
1153     int *valsb;
1154     if (dparam == active_dummy) {
1155       // ACTIVATE / DEACTIVATE
1156       if (!autoscale) { // autoscale is now "user override"
1157         int valb = weed_get_boolean_value(sparam, WEED_LEAF_VALUE, NULL);
1158         if ((valb == WEED_TRUE && (mainw->rte & (GU641 << (key))) == 0) ||
1159             (valb == WEED_FALSE && (mainw->rte & (GU641 << (key)))) != 0) {
1160           if (toggle_fx) *toggle_fx = TRUE;
1161         }
1162       }
1163       return retval;
1164     }
1165     valsb = weed_get_boolean_array(sparam, WEED_LEAF_VALUE, NULL);
1166     switch (dtype) {
1167     case WEED_SEED_STRING: {
1168       char *opstring, *tmp, *bit;
1169       char **valss, *vals;
1170 
1171       if (ndvals == 1) {
1172         opstring = lives_strdup("");
1173         vals = weed_get_string_value(dparam, WEED_LEAF_VALUE, NULL);
1174         for (i = 0; i < nsvals; i++) {
1175           bit = lives_strdup_printf("%d", valsb[i]);
1176           if (!*opstring)
1177             tmp = lives_strconcat(opstring, bit, NULL);
1178           else
1179             tmp = lives_strconcat(opstring, " ", bit, NULL);
1180           lives_free(bit);
1181           lives_free(opstring);
1182           opstring = tmp;
1183         }
1184         if (strcmp(vals, opstring)) {
1185           weed_set_string_value(dparam, WEED_LEAF_VALUE, opstring);
1186           retval = TRUE;
1187         }
1188         lives_free(vals); lives_free(valsb); lives_free(opstring);
1189         return retval;
1190       }
1191 
1192       valss = weed_get_string_array(dparam, WEED_LEAF_VALUE, NULL);
1193       if (ndvals > ondvals) valss = (char **)lives_realloc(valss, ndvals * sizeof(char *));
1194 
1195       for (i = 0; i < ndvals; i++) {
1196         bit = lives_strdup_printf("%d", valsb[i]);
1197         if (i >= ondvals || strcmp(valss[i], bit)) {
1198           retval = TRUE;
1199           if (i < ondvals) lives_free(valss[i]);
1200           valss[i] = bit;
1201         } else lives_free(bit);
1202       }
1203       if (!retval) {
1204         for (i = 0; i < ndvals; i++) lives_free(valss[i]);
1205         lives_free(valss);
1206         lives_free(valsb);
1207         return FALSE;
1208       }
1209 
1210       weed_set_string_array(dparam, WEED_LEAF_VALUE, ndvals, valss);
1211 
1212       for (i = 0; i < ndvals; i++) lives_free(valss[i]);
1213       lives_free(valss);
1214       lives_free(valsb);
1215     }
1216     return retval;
1217     case WEED_SEED_DOUBLE: {
1218       double *valsd = weed_get_double_array(dparam, WEED_LEAF_VALUE, NULL);
1219       double *maxd = weed_get_double_array(dptmpl, WEED_LEAF_MAX, NULL);
1220       double *mind = weed_get_double_array(dptmpl, WEED_LEAF_MIN, NULL);
1221       double vald;
1222 
1223       if (ndvals > ondvals) valsd = (double *)lives_realloc(valsd, ndvals * sizeof(double));
1224 
1225       for (i = 0; i < ndvals; i++) {
1226         if (autoscale) {
1227           if (valsb[i] == WEED_TRUE) vald = maxd[maxct];
1228           else vald = mind[minct];
1229         } else {
1230           vald = (double)valsb[i];
1231           if (vald > maxd[maxct]) vald = maxd[maxct];
1232           if (vald < mind[minct]) vald = mind[minct];
1233         }
1234         if (i >= ondvals || valsd[i] != vald) {
1235           retval = TRUE;
1236           valsd[i] = vald;
1237         }
1238         if (++maxct == nmax) maxct = 0;
1239         if (++minct == nmin) minct = 0;
1240       }
1241       if (retval) {
1242         if (inst && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
1243           // if we are recording, add this change to our event_list
1244           rec_param_change(inst, pnum);
1245         }
1246 
1247         weed_set_double_array(dparam, WEED_LEAF_VALUE, ndvals, valsd);
1248       }
1249       lives_free(maxd); lives_free(mind);
1250       lives_free(valsb); lives_free(valsd);
1251     }
1252     return retval;
1253     case WEED_SEED_INT: {
1254       int *valsi = weed_get_int_array(dparam, WEED_LEAF_VALUE, NULL);
1255       int *maxi = weed_get_int_array(dptmpl, WEED_LEAF_MAX, NULL);
1256       int *mini = weed_get_int_array(dptmpl, WEED_LEAF_MIN, NULL);
1257 
1258       if (ndvals > ondvals) valsi = (int *)lives_realloc(valsi, ndvals * sizeof(int));
1259 
1260       for (i = 0; i < ndvals; i++) {
1261         if (autoscale) {
1262           if (valsb[i] == WEED_TRUE) valsb[i] = maxi[maxct];
1263           else valsb[i] = mini[minct];
1264         } else {
1265           if (valsb[i] > maxi[maxct]) valsb[i] = maxi[maxct];
1266           if (valsb[i] < mini[minct]) valsb[i] = mini[maxct];
1267         }
1268         if (i >= ondvals || valsi[i] != valsb[i]) {
1269           retval = TRUE;
1270           valsi[i] = valsb[i];
1271         }
1272         if (++maxct == nmax) maxct = 0;
1273         if (++minct == nmin) minct = 0;
1274       }
1275       if (retval) {
1276         if (inst && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
1277           // if we are recording, add this change to our event_list
1278           rec_param_change(inst, pnum);
1279         }
1280 
1281         weed_set_int_array(dparam, WEED_LEAF_VALUE, ndvals, valsi);
1282       }
1283       lives_free(maxi); lives_free(mini);
1284       lives_free(valsi); lives_free(valsb);
1285     }
1286     return retval;
1287 
1288     case WEED_SEED_BOOLEAN: {
1289       int *valsB = weed_get_boolean_array(dparam, WEED_LEAF_VALUE, NULL);
1290 
1291       if (ndvals > ondvals) valsB = (int *)lives_realloc(valsB, ndvals * sizeof(int));
1292 
1293       for (i = 0; i < ndvals; i++) {
1294         if (i >= ondvals || valsB[i] != valsb[i]) {
1295           retval = TRUE;
1296           valsB[i] = valsb[i];
1297         }
1298       }
1299       if (retval) {
1300         if (inst && mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
1301           // if we are recording, add this change to our event_list
1302           rec_param_change(inst, pnum);
1303         }
1304         weed_set_boolean_array(dparam, WEED_LEAF_VALUE, ndvals, valsB);
1305       }
1306       lives_free(valsb);
1307       lives_free(valsB);
1308     }
1309     return retval;
1310     default:
1311       lives_free(valsb);
1312       break;
1313     }
1314     break;
1315   }
1316   default: break;
1317   }
1318 
1319   return retval;
1320 }
1321 
1322 
pconx_chain_data_omc(weed_plant_t * inst,int okey,int omode)1323 int pconx_chain_data_omc(weed_plant_t *inst, int okey, int omode) {
1324   // push anything to connected OMC macros
1325   lives_pconnect_t *pconx = mainw->pconx;
1326 
1327   char *valstr = NULL;
1328 
1329   int totcons, pidx;
1330   int actions = 0;
1331 
1332   boolean cbval, lbval;
1333 
1334   int i, j;
1335 
1336   if (!inst) return 0;
1337 
1338   if (mainw->event_list && !mainw->record && !mainw->record_paused) return 0;
1339 
1340   // check for any inkeys == FX_DATA_KEY_OMC_MACRO and matching okey, omode
1341   // for each match we construct a string and send it to the OMC learner
1342 
1343   while (pconx) {
1344     if (pconx->okey == okey && pconx->omode == omode) {
1345       totcons = 0;
1346       j = 0;
1347       for (i = 0; i < pconx->nparams; i++) {
1348         totcons += pconx->nconns[i];
1349         pidx = pconx->params[i];
1350         lbval = pconx->last_boolval[i];
1351         for (; j < totcons; j++) {
1352           if (pconx->ikey[j] == FX_DATA_KEY_OMC_MACRO) {
1353             // out param is "ACTIVATED"
1354             if (pidx >= 0) {
1355               weed_plant_t *oparam = weed_inst_out_param(inst, pidx);
1356               switch (weed_leaf_seed_type(oparam, WEED_LEAF_VALUE)) {
1357               case WEED_SEED_BOOLEAN:
1358                 cbval = weed_get_boolean_value(oparam, WEED_LEAF_VALUE, NULL);
1359                 if (cbval == WEED_TRUE && lbval == WEED_FALSE) {
1360                   // state changed, so trigger act
1361                   valstr = lives_strdup_printf("%d", cbval);
1362                 }
1363                 pconx->last_boolval[i] = cbval;
1364                 break;
1365               default: break;
1366               }
1367             } else {
1368               valstr = lives_strdup_printf("%d", WEED_TRUE);
1369               pconx->last_boolval[i] = WEED_TRUE;
1370             }
1371           }
1372           if (valstr) {
1373             // construct string to pass
1374             char *msgstring = lives_strdup_printf("%d %d %d %s", OMC_INTERNAL, pconx->imode[j], pconx->ipnum[j], valstr);
1375             lives_free(valstr);
1376             valstr = NULL;
1377             omc_process_string(OMC_INTERNAL, (const char *)msgstring, FALSE, NULL);
1378             actions++;
1379             lives_free(msgstring);
1380 	    // *INDENT-OFF*
1381           }}}}
1382     // *INDENT-ON*
1383 
1384     pconx = pconx->next;
1385   }
1386   return actions;
1387 }
1388 
1389 
pconx_chain_data(int key,int mode,boolean is_audio_thread)1390 boolean pconx_chain_data(int key, int mode, boolean is_audio_thread) {
1391   weed_plant_t **inparams = NULL;
1392   weed_plant_t *oparam, *inparam = NULL;
1393   weed_plant_t *inst = NULL;
1394 
1395   boolean changed, reinit_inst = FALSE;
1396 
1397   int nparams = 0, start = 0;
1398   int autoscale;
1399   int pflags;
1400   int okey, omode;
1401   boolean toggle_fx = FALSE;
1402   int i;
1403 
1404   if (mainw->is_rendering) return FALSE;
1405 
1406   if (key == FX_DATA_KEY_PLAYBACK_PLUGIN) {
1407     // playback plugin
1408     if (!mainw->vpp) return FALSE;
1409     inparams = mainw->vpp->play_params;
1410     nparams = mainw->vpp->num_play_params;
1411   } else {
1412     filter_mutex_lock(key);
1413     inst = rte_keymode_get_instance(key + 1, mode);
1414     if (!inst) filter_mutex_unlock(key);
1415     start = -EXTRA_PARAMS_IN;
1416   }
1417 
1418   if (inst) {
1419     if (weed_plant_has_leaf(inst, WEED_LEAF_IN_PARAMETERS))
1420       inparams = weed_get_plantptr_array_counted(inst, WEED_LEAF_IN_PARAMETERS, &nparams);
1421   } else {
1422     if (rte_keymode_get_filter_idx(key + 1, mode) == -1) return FALSE;
1423   }
1424 
1425 
1426   for (i = start; i < nparams; i++) {
1427     //g_print("NOW at %d %d\n ", key, mode);
1428     if ((oparam = pconx_get_out_param(FALSE, key, mode, i, &okey, &omode, NULL, &autoscale))) {
1429       //#define DEBUG_PCONX
1430 #ifdef DEBUG_PCONX
1431       g_print("got pconx to %d %d %d\n", key, mode, i);
1432 #endif
1433       if (i == FX_DATA_PARAM_ACTIVE) {
1434         pthread_mutex_lock(&mainw->fxd_active_mutex);
1435         if (!active_dummy) {
1436           active_dummy = weed_plant_new(WEED_PLANT_PARAMETER);
1437           weed_set_plantptr_value(active_dummy, WEED_LEAF_TEMPLATE, NULL);
1438         }
1439         inparam = active_dummy;
1440         pthread_mutex_unlock(&mainw->fxd_active_mutex);
1441       } else inparam = inparams[i];
1442 
1443       /// we need to keep these locked for as little time as possible so as not to hang up the video / audio thread
1444       filter_mutex_lock(okey);
1445       if (oparam != active_dummy) {
1446         weed_plant_t *oinst = rte_keymode_get_instance(okey + 1, omode);
1447         if (!oinst) {
1448           filter_mutex_unlock(okey);
1449           if (inst) {
1450             weed_instance_unref(inst);
1451             filter_mutex_unlock(key);
1452           }
1453           return FALSE;
1454         }
1455         weed_instance_unref(oinst);
1456       }
1457 
1458       changed = pconx_convert_value_data(inst, i, key, key == FX_DATA_KEY_PLAYBACK_PLUGIN
1459                                          ? (weed_plant_t *)pp_get_param(mainw->vpp->play_params, i)
1460                                          : inparam, okey, oparam, autoscale, is_audio_thread, &toggle_fx);
1461 
1462       if (toggle_fx) {
1463         if (is_audio_thread) {
1464           // in the audio thread, don't activate / dectivate video fx. It could cause an underflow if it takes too long
1465           // let the video thread handle it
1466           weed_plant_t *filter = rte_keymode_get_filter(key + 1, rte_key_getmode(key + 1));
1467           if (!is_pure_audio(filter, FALSE)) {
1468             if (inst) {
1469               weed_instance_unref(inst);
1470               inst = NULL;
1471               filter_mutex_unlock(key);
1472             }
1473             filter_mutex_unlock(okey);
1474             return FALSE;
1475           }
1476         }
1477         if (inst) {
1478           weed_instance_unref(inst);
1479           inst = NULL;
1480           filter_mutex_unlock(key);
1481         }
1482         switch_fx_state(key + 1);
1483         filter_mutex_unlock(okey);
1484       } else {
1485         filter_mutex_unlock(okey);
1486         if (changed && inst && key > -1) {
1487           // only store value if it changed; for int, double or colour, store old value too
1488 
1489           //copyto = set_copy_to(inst, i, TRUE);
1490           if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS)) {
1491             // if we are recording, add this change to our event_list
1492             rec_param_change(inst, i);
1493             //if (copyto != -1) rec_param_change(inst, copyto);
1494           }
1495 
1496           pflags = weed_get_int_value(inparams[i], WEED_LEAF_FLAGS, NULL);
1497           if (pflags & WEED_PARAMETER_REINIT_ON_VALUE_CHANGE) reinit_inst = TRUE;
1498 
1499           if (fx_dialog[1] && !reinit_inst) {
1500             lives_rfx_t *rfx = fx_dialog[1]->rfx;
1501             if (!rfx->is_template) {
1502               int keyw = fx_dialog[1]->key;
1503               int modew = fx_dialog[1]->mode;
1504               if (keyw == key && modew == mode)
1505                 // ask the main thread to update the param window
1506                 mainw->vrfx_update = rfx;
1507             }
1508           }
1509           if (mainw->ce_thumbs) ce_thumbs_register_rfx_change(key, mode);
1510 	  // *INDENT-OFF*
1511         }}}}
1512   // *INDENT-ON*
1513 
1514   if (inst) {
1515     weed_instance_unref(inst);
1516     inst = NULL;
1517     filter_mutex_unlock(key);
1518   }
1519 
1520   if (key != FX_DATA_KEY_PLAYBACK_PLUGIN && inparams) lives_free(inparams);
1521   return reinit_inst;
1522 }
1523 
1524 
pconx_chain_data_internal(weed_plant_t * inst)1525 boolean pconx_chain_data_internal(weed_plant_t *inst) {
1526   // special version for compound fx internal connections
1527   weed_plant_t **in_params;
1528 
1529   boolean autoscale, reinit_inst = FALSE;
1530 
1531   int nparams = 0, pflags, i;
1532 
1533   nparams = num_in_params(inst, FALSE, FALSE);
1534   if (!nparams) return FALSE;
1535 
1536   in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
1537 
1538   for (i = 0; i < nparams; i++) {
1539     if (weed_plant_has_leaf(in_params[i], WEED_LEAF_HOST_INTERNAL_CONNECTION)) {
1540       autoscale = FALSE;
1541       if (weed_plant_has_leaf(in_params[i], WEED_LEAF_HOST_INTERNAL_CONNECTION_AUTOSCALE) &&
1542           weed_get_boolean_value(in_params[i],
1543                                  WEED_LEAF_HOST_INTERNAL_CONNECTION_AUTOSCALE, NULL) == WEED_TRUE) autoscale = TRUE;
1544       if (pconx_convert_value_data(inst, i, -1, in_params[i], -1, weed_get_plantptr_value(in_params[i],
1545                                    WEED_LEAF_HOST_INTERNAL_CONNECTION, NULL), autoscale, FALSE, NULL)) {
1546 
1547         pflags = weed_get_int_value(in_params[i], WEED_LEAF_FLAGS, NULL);
1548         if (pflags & WEED_PARAMETER_REINIT_ON_VALUE_CHANGE) reinit_inst = TRUE;
1549       }
1550     }
1551   }
1552 
1553   lives_free(in_params);
1554   return reinit_inst;
1555 }
1556 
1557 // alpha channs
1558 
1559 
cconx_delete_all(void)1560 void cconx_delete_all(void) {
1561   lives_cconnect_t *cconx = mainw->cconx, *cconx_next;
1562   while (cconx) {
1563     cconx_next = cconx->next;
1564     if (cconx->nchans > 0) {
1565       lives_free(cconx->chans);
1566       lives_free(cconx->nconns);
1567       lives_free(cconx->ikey);
1568       lives_free(cconx->imode);
1569       lives_free(cconx->icnum);
1570     }
1571     lives_free(cconx);
1572     cconx = cconx_next;
1573   }
1574   mainw->cconx = NULL;
1575 }
1576 
1577 
cconx_new(int okey,int omode)1578 static lives_cconnect_t *cconx_new(int okey, int omode) {
1579   lives_cconnect_t *cconx = (lives_cconnect_t *)lives_calloc(sizeof(struct _lives_cconnect_t), 1);
1580   cconx->next = NULL;
1581   cconx->okey = okey;
1582   cconx->omode = omode;
1583   cconx->nchans = 0;
1584   cconx->nconns = NULL;
1585   return cconx;
1586 }
1587 
1588 
cconx_copy(lives_cconnect_t * scconx)1589 static lives_cconnect_t *cconx_copy(lives_cconnect_t *scconx) {
1590   lives_cconnect_t *cconx = NULL, *dcconx, *last_dcconx;
1591 
1592   int totcons = 0;
1593 
1594   register int i, j = 0;
1595 
1596   while (scconx) {
1597     dcconx = cconx_new(scconx->okey, scconx->omode);
1598     if (!cconx) cconx = dcconx;
1599     else last_dcconx->next = dcconx;
1600 
1601     dcconx->nchans = scconx->nchans;
1602 
1603     dcconx->nconns = (int *)lives_malloc(dcconx->nchans * sizint);
1604     dcconx->chans = (int *)lives_malloc(dcconx->nchans * sizint);
1605 
1606     dcconx->ikey = dcconx->imode = dcconx->icnum = NULL;
1607 
1608     j = 0;
1609 
1610     for (i = 0; i < dcconx->nchans; i++) {
1611       dcconx->chans[i] = scconx->chans[i];
1612       dcconx->nconns[i] = scconx->nconns[i];
1613       totcons += dcconx->nconns[i];
1614 
1615       dcconx->ikey = (int *)lives_realloc(dcconx->ikey, totcons * sizint);
1616       dcconx->imode = (int *)lives_realloc(dcconx->imode, totcons * sizint);
1617       dcconx->icnum = (int *)lives_realloc(dcconx->icnum, totcons * sizint);
1618 
1619       while (j < totcons) {
1620         dcconx->ikey[j] = scconx->ikey[j];
1621         dcconx->imode[j] = scconx->imode[j];
1622         dcconx->icnum[j] = scconx->icnum[j];
1623         j++;
1624       }
1625     }
1626 
1627     scconx = scconx->next;
1628     last_dcconx = dcconx;
1629   }
1630 
1631   return cconx;
1632 }
1633 
1634 
cconx_list(int okey,int omode,int ocnum)1635 char *cconx_list(int okey, int omode, int ocnum) {
1636   char *st1 = lives_strdup(""), *st2;
1637   lives_cconnect_t *cconx = mainw->cconx;
1638   int totcons = 0;
1639 
1640   while (cconx) {
1641     if (cconx->okey == okey && cconx->omode == omode) {
1642       for (int i = 0; i < cconx->nchans; i++) {
1643         if (cconx->chans[i] == ocnum) {
1644           for (int j = totcons; j < totcons + cconx->nconns[i]; j++) {
1645             if (!*st1) st2 = lives_strdup_printf("%d %d %d", cconx->ikey[j] + 1, cconx->imode[j] + 1, cconx->icnum[j]);
1646             else
1647               st2 = lives_strdup_printf("%s %d %d %d", st1, cconx->ikey[j] + 1, cconx->imode[j] + 1, cconx->icnum[j]);
1648             lives_free(st1);
1649             st1 = st2;
1650           }
1651           return st1;
1652         }
1653         totcons += cconx->nconns[i];
1654       }
1655       return st1;
1656     }
1657     cconx = cconx->next;
1658   }
1659   return st1;
1660 }
1661 
1662 
cconx_delete(int okey,int omode,int ocnum,int ikey,int imode,int icnum)1663 void cconx_delete(int okey, int omode, int ocnum, int ikey, int imode, int icnum) {
1664   lives_cconnect_t *cconx = mainw->cconx, *cconx_next, *cconx_prev = NULL;
1665 
1666   register int i, j = 0, k;
1667 
1668   int totcons = 0, maxcons = 0;
1669 
1670   while (cconx) {
1671     cconx_next = cconx->next;
1672     if (okey == FX_DATA_WILDCARD || (cconx->okey == okey && cconx->omode == omode)) {
1673       if (ikey == FX_DATA_WILDCARD) {
1674         // delete entire node
1675         lives_free(cconx->chans); lives_free(cconx->nconns); lives_free(cconx->ikey);
1676         lives_free(cconx->imode); lives_free(cconx->icnum); lives_free(cconx);
1677         if (mainw->cconx == cconx) mainw->cconx = cconx_next;
1678         else cconx_prev->next = cconx_next;
1679         return;
1680       }
1681 
1682       maxcons = 0;
1683       totcons = 0;
1684       j = 0;
1685 
1686       for (i = 0; i < cconx->nchans; i++) {
1687         maxcons += cconx->nconns[i];
1688       }
1689 
1690       for (i = 0; cconx && i < cconx->nchans; i++) {
1691         totcons += cconx->nconns[i];
1692 
1693         if (okey != FX_DATA_WILDCARD && cconx->chans[i] != ocnum) {
1694           j = totcons;
1695           continue;
1696         }
1697 
1698         for (; j < totcons; j++) {
1699           if (cconx->ikey[j] == ikey && cconx->imode[j] == imode && (icnum == FX_DATA_WILDCARD || cconx->icnum[j] == icnum)) {
1700             maxcons--;
1701             for (k = j; k < maxcons; k++) {
1702               cconx->ikey[k] = cconx->ikey[k + 1];
1703               cconx->imode[k] = cconx->imode[k + 1];
1704               cconx->icnum[k] = cconx->icnum[k + 1];
1705             }
1706 
1707             cconx->ikey = (int *)lives_realloc(cconx->ikey, maxcons * sizint);
1708             cconx->imode = (int *)lives_realloc(cconx->imode, maxcons * sizint);
1709             cconx->icnum = (int *)lives_realloc(cconx->icnum, maxcons * sizint);
1710 
1711             cconx->nconns[i]--;
1712 
1713             if (cconx->nconns[i] == 0) {
1714               cconx->nchans--;
1715               for (k = i; k < cconx->nchans; k++) {
1716                 cconx->chans[k] = cconx->chans[k + 1];
1717                 cconx->nconns[k] = cconx->nconns[k + 1];
1718               }
1719 
1720               if (cconx->nchans == 0) {
1721                 // delete entire node
1722                 lives_free(cconx->chans); lives_free(cconx->nconns); lives_free(cconx->ikey);
1723                 lives_free(cconx->imode); lives_free(cconx->icnum); lives_free(cconx);
1724                 if (mainw->cconx == cconx) {
1725                   mainw->cconx = cconx_next;
1726                   cconx = NULL;
1727                 } else {
1728                   cconx = cconx_prev;
1729                   cconx->next = cconx_next;
1730                 }
1731               } else {
1732                 cconx->nconns = (int *)lives_realloc(cconx->nconns, cconx->nchans * sizint);
1733               }
1734 	      // *INDENT-OFF*
1735             }}}}}
1736 	  // *INDENT-ON*
1737 
1738     cconx_prev = cconx;
1739     cconx = cconx_next;
1740   }
1741 }
1742 
1743 
cconx_remap_mode(int key,int omode,int nmode)1744 void cconx_remap_mode(int key, int omode, int nmode) {
1745   lives_cconnect_t *cconx = mainw->cconx;
1746 
1747   register int i, j, totcons;
1748 
1749   while (cconx) {
1750     if (cconx->okey == key && cconx->omode == omode) {
1751       cconx->omode = nmode;
1752     }
1753     j = 0;
1754     totcons = 0;
1755     for (i = 0; i < cconx->nchans; i++) {
1756       totcons += cconx->nconns[i];
1757       for (; j < totcons; j++) {
1758         if (cconx->ikey[j] == key && cconx->imode[j] == omode) {
1759           cconx->imode[j] = nmode;
1760         }
1761       }
1762     }
1763     cconx = cconx->next;
1764   }
1765 }
1766 
1767 
cconx_append(lives_cconnect_t * cconx)1768 static void cconx_append(lives_cconnect_t *cconx) {
1769   lives_cconnect_t *occonx = mainw->cconx;
1770   lives_cconnect_t *last_cconx = occonx;
1771 
1772   while (occonx) {
1773     last_cconx = occonx;
1774     occonx = occonx->next;
1775   }
1776 
1777   if (last_cconx) last_cconx->next = cconx;
1778   if (!mainw->cconx) mainw->cconx = cconx;
1779 }
1780 
1781 
cconx_find(int okey,int omode)1782 static lives_cconnect_t *cconx_find(int okey, int omode) {
1783   lives_cconnect_t *cconx = mainw->cconx;
1784   while (cconx) {
1785     if (cconx->okey == okey && cconx->omode == omode) {
1786       return cconx;
1787     }
1788     cconx = cconx->next;
1789   }
1790   return NULL;
1791 }
1792 
1793 
cconx_get_numcons(lives_conx_w * conxwp,int cnum)1794 static int cconx_get_numcons(lives_conx_w * conxwp, int cnum) {
1795   // get displayed number
1796   int totcons = 0;
1797 
1798   register int j;
1799 
1800   if (cnum != FX_DATA_WILDCARD) return conxwp->dispc[cnum];
1801 
1802   for (j = 0; j < conxwp->num_alpha; j++) {
1803     totcons += conxwp->dispc[j];
1804   }
1805 
1806   return totcons;
1807 }
1808 
1809 
cconx_get_nconns(lives_cconnect_t * cconx,int cnum)1810 static int cconx_get_nconns(lives_cconnect_t *cconx, int cnum) {
1811   // get actual number of connections
1812 
1813   int totcons = 0, j;
1814 
1815   if (!cconx) return 0;
1816 
1817   for (j = 0; j < cconx->nchans; j++) {
1818     if (cnum != FX_DATA_WILDCARD) {
1819       if (cconx->chans[j] == cnum) return cconx->nconns[j];
1820     } else totcons += cconx->nconns[j];
1821   }
1822   return totcons;
1823 }
1824 
1825 
cconx_add_connection_private(int okey,int omode,int ocnum,int ikey,int imode,int icnum)1826 static  lives_cconnect_t *cconx_add_connection_private(int okey, int omode, int ocnum, int ikey, int imode, int icnum) {
1827   lives_cconnect_t *cconx;
1828   int posn = 0, totcons = 0, i, j;
1829 
1830   // delete any existing connection to the input channel
1831   cconx_delete(FX_DATA_WILDCARD, 0, 0, ikey, imode, icnum);
1832   cconx = cconx_find(okey, omode);
1833 
1834   if (!cconx) {
1835     // add whole new node
1836     cconx = cconx_new(okey, omode);
1837     cconx_append(cconx);
1838   } else {
1839     // see if already in chans
1840 
1841     for (i = 0; i < cconx->nchans; i++) {
1842 
1843       if (cconx->chans[i] == ocnum) {
1844         // add connection to existing
1845 
1846         for (j = 0; j < cconx->nchans; j++) {
1847           if (j < i) {
1848             // calc posn
1849             posn += cconx->nconns[j];
1850           }
1851           totcons += cconx->nconns[j];
1852         }
1853 
1854         // if already there, do not add again
1855         for (j = posn; j < posn + cconx->nconns[i]; j++) {
1856           if (cconx->ikey[j] == ikey && cconx->imode[j] == imode && cconx->icnum[j] == icnum) {
1857             return cconx;
1858           }
1859 
1860           // add in order key/mode/chan
1861           if (cconx->ikey[j] > ikey || (cconx->ikey[j] == ikey && cconx->imode[j] > imode) ||
1862               (cconx->ikey[j] == ikey && cconx->imode[j] == imode && cconx->icnum[j] > icnum)) break;
1863         }
1864 
1865         posn = j; // we will insert here
1866         cconx->nconns[i]++;
1867         totcons++;
1868 
1869         // make space for new
1870         cconx->ikey = (int *)lives_realloc(cconx->ikey, totcons * sizint);
1871         cconx->imode = (int *)lives_realloc(cconx->imode, totcons * sizint);
1872         cconx->icnum = (int *)lives_realloc(cconx->icnum, totcons * sizint);
1873 
1874         // move up 1
1875         for (j = totcons - 1; j > posn; j--) {
1876           cconx->ikey[j] = cconx->ikey[j - 1];
1877           cconx->imode[j] = cconx->imode[j - 1];
1878           cconx->icnum[j] = cconx->icnum[j - 1];
1879         }
1880 
1881         // insert at posn
1882         cconx->ikey[posn] = ikey;
1883         cconx->imode[posn] = imode;
1884         cconx->icnum[posn] = icnum;
1885 
1886         return cconx;
1887       }
1888     }
1889 
1890     // so, we have data for key/mode but this is a new channel to be mapped
1891 
1892     for (i = 0; i < cconx->nchans; i++) {
1893       totcons += cconx->nconns[i];
1894     }
1895 
1896     totcons++;
1897 
1898     cconx->nchans++;
1899     posn = cconx->nchans;
1900 
1901     // make space for new
1902     cconx->nconns = (int *)lives_realloc(cconx->nconns, posn * sizint);
1903     cconx->chans = (int *)lives_realloc(cconx->chans, posn * sizint);
1904 
1905     cconx->ikey = (int *)lives_realloc(cconx->ikey, totcons * sizint);
1906     cconx->imode = (int *)lives_realloc(cconx->imode, totcons * sizint);
1907     cconx->icnum = (int *)lives_realloc(cconx->icnum, totcons * sizint);
1908 
1909     cconx->chans[posn - 1] = ocnum;
1910 
1911     cconx->nconns[posn - 1] = 1;
1912 
1913     posn = totcons - 1;
1914 
1915     // insert at posn
1916     cconx->ikey[posn] = ikey;
1917     cconx->imode[posn] = imode;
1918     cconx->icnum[posn] = icnum;
1919 
1920 #ifdef DEBUG_CCONX
1921     g_print("added another cconx from %d %d %d to %d %d %d\n", okey, omode, ocnum, ikey, imode, icnum);
1922 #endif
1923 
1924     return cconx;
1925   }
1926 
1927   // add new
1928 
1929   totcons = cconx_get_nconns(cconx, FX_DATA_WILDCARD) + 1;
1930   cconx->nchans++;
1931 
1932   cconx->nconns = (int *)lives_realloc(cconx->chans, cconx->nchans * sizint);
1933   cconx->nconns[cconx->nchans - 1] = 1;
1934 
1935   cconx->chans = (int *)lives_realloc(cconx->chans, cconx->nchans * sizint);
1936   cconx->chans[cconx->nchans - 1] = ocnum;
1937 
1938   cconx->ikey = (int *)lives_realloc(cconx->ikey, totcons * sizint);
1939   cconx->ikey[totcons - 1] = ikey;
1940 
1941   cconx->imode = (int *)lives_realloc(cconx->imode, totcons * sizint);
1942   cconx->imode[totcons - 1] = imode;
1943 
1944   cconx->icnum = (int *)lives_realloc(cconx->icnum, totcons * sizint);
1945   cconx->icnum[totcons - 1] = icnum;
1946 
1947 #ifdef DEBUG_CCONX
1948   g_print("added new cconx from %d %d %d to %d %d %d\n", okey, omode, ocnum, ikey, imode, icnum);
1949 #endif
1950   return cconx;
1951 }
1952 
1953 
cconx_add_connection(int okey,int omode,int ocnum,int ikey,int imode,int icnum)1954 void cconx_add_connection(int okey, int omode, int ocnum, int ikey, int imode, int icnum) {
1955   cconx_add_connection_private(okey, omode, ocnum, ikey, imode, icnum);
1956 }
1957 
1958 
cconx_get_out_alpha(boolean use_filt,int ikey,int imode,int icnum,int * okey,int * omode,int * ocnum)1959 static weed_plant_t *cconx_get_out_alpha(boolean use_filt, int ikey, int imode, int icnum, int *okey, int *omode, int *ocnum) {
1960   // walk all cconx and find one which has ikey/imode/icnum as destination
1961   // then all we need do is convert the pixel_data
1962 
1963   lives_cconnect_t *cconx = mainw->cconx;
1964   weed_plant_t *inst = NULL, *filter = NULL, *orig_inst = NULL;
1965   int totcons, i, j;
1966 
1967   while (cconx) {
1968     if (!use_filt) {
1969       if (mainw->is_rendering) {
1970         inst = get_new_inst_for_keymode(cconx->okey, cconx->omode);
1971       } else {
1972         inst = rte_keymode_get_instance(cconx->okey + 1, cconx->omode);
1973       }
1974       if (!inst) {
1975         cconx = cconx->next;
1976         continue;
1977       }
1978       filter = weed_instance_get_filter(inst, TRUE);
1979     } else {
1980       inst = NULL;
1981       filter = rte_keymode_get_filter(cconx->okey + 1, cconx->omode);
1982       if (!filter) {
1983         cconx = cconx->next;
1984         continue;
1985       }
1986     }
1987     if (!weed_plant_has_leaf(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES)) {
1988       if (inst) weed_instance_unref(inst);
1989       cconx = cconx->next;
1990       continue;
1991     }
1992     orig_inst = inst;
1993     totcons = 0;
1994     j = 0;
1995     for (i = 0; i < cconx->nchans; i++) {
1996       totcons += cconx->nconns[i];
1997       for (; j < totcons; j++) {
1998         if (cconx->ikey[j] == ikey && cconx->imode[j] == imode && cconx->icnum[j] == icnum) {
1999           weed_plant_t **outchans;
2000           weed_plant_t *channel = NULL;
2001           if (use_filt) {
2002             outchans = weed_get_plantptr_array(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, NULL);
2003             if (cconx->chans[i] < weed_leaf_num_elements(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES)) {
2004               channel = outchans[cconx->chans[i]];
2005             }
2006           } else {
2007             while (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE)) inst = weed_get_plantptr_value(inst,
2008                   WEED_LEAF_HOST_NEXT_INSTANCE, NULL);
2009             outchans = weed_get_plantptr_array(inst, WEED_LEAF_OUT_CHANNELS, NULL);
2010             if (cconx->chans[i] < weed_leaf_num_elements(inst, WEED_LEAF_OUT_CHANNELS)) {
2011               channel = outchans[cconx->chans[i]];
2012             }
2013           }
2014           lives_free(outchans);
2015           if (okey) *okey = cconx->okey;
2016           if (omode) *omode = cconx->omode;
2017           if (ocnum) *ocnum = cconx->chans[i];
2018           if (orig_inst) weed_instance_unref(orig_inst);
2019           return channel;
2020         }
2021       }
2022     }
2023     cconx = cconx->next;
2024     weed_instance_unref(inst);
2025   }
2026 
2027   return NULL;
2028 }
2029 
2030 
cconx_convert_pixel_data(weed_plant_t * dchan,weed_plant_t * schan)2031 boolean cconx_convert_pixel_data(weed_plant_t *dchan, weed_plant_t *schan) {
2032   // convert pixel_data by possibly converting the type (palette)
2033 
2034   // return TRUE if we need to reinit the instance (because channel palette changed)
2035 
2036   // we set boolean "host_orig_pdata" if we steal the schan pdata (so do not free....)
2037 
2038   int iwidth, iheight, ipal, irow;
2039   int owidth, oheight, opal, orow, oflags;
2040   boolean pal_ok, needs_reinit = FALSE;
2041 
2042   weed_plant_t *dtmpl = weed_get_plantptr_value(dchan, WEED_LEAF_TEMPLATE, NULL);
2043 
2044   uint8_t *spdata, *dpdata;
2045 
2046   int i;
2047 
2048   ipal = weed_get_int_value(schan, WEED_LEAF_CURRENT_PALETTE, NULL);
2049   if (!weed_palette_is_alpha(ipal)) return FALSE;
2050 
2051   iwidth = weed_get_int_value(schan, WEED_LEAF_WIDTH, NULL);
2052   iheight = weed_get_int_value(schan, WEED_LEAF_HEIGHT, NULL);
2053   irow = weed_get_int_value(schan, WEED_LEAF_ROWSTRIDES, NULL);
2054 
2055   owidth = weed_get_int_value(dchan, WEED_LEAF_WIDTH, NULL);
2056   oheight = weed_get_int_value(dchan, WEED_LEAF_HEIGHT, NULL);
2057   opal = weed_get_int_value(dchan, WEED_LEAF_CURRENT_PALETTE, NULL);
2058   orow = weed_get_int_value(dchan, WEED_LEAF_ROWSTRIDES, NULL);
2059 
2060   spdata = (uint8_t *)weed_get_voidptr_value(schan, WEED_LEAF_PIXEL_DATA, NULL);
2061 
2062 #ifdef DEBUG_CCONX
2063   g_print("spd is %p %d %d %d %d %d %d %d %d\n", spdata, ipal, opal, iwidth, owidth, iheight, oheight, irow, orow);
2064   if (spdata) g_print("spd2 is %p %d\n", spdata, spdata[0]);
2065 #endif
2066   if (ipal == opal && iwidth == owidth && iheight == oheight && irow == orow) {
2067     /// everything matches - we can just do a steal
2068     weed_set_voidptr_value(dchan, WEED_LEAF_PIXEL_DATA, spdata);
2069     /// caller - do not free in dchan
2070     weed_set_boolean_value(dchan, WEED_LEAF_HOST_ORIG_PDATA, WEED_TRUE);
2071     return FALSE;
2072   }
2073 
2074   /// check first if we can set the in-channel palette to match
2075   if (ipal == opal) pal_ok = TRUE;
2076   else {
2077     /// see if dest chan supports the source chan palette
2078     int num_palettes = weed_leaf_num_elements(dtmpl, WEED_LEAF_PALETTE_LIST);
2079     int *palettes = weed_get_int_array(dtmpl, WEED_LEAF_PALETTE_LIST, NULL);
2080     if (best_palette_match(palettes, num_palettes, ipal) == ipal) pal_ok = TRUE; ///< yes
2081     else pal_ok = FALSE; ///<no
2082     oflags = weed_get_int_value(dtmpl, WEED_LEAF_FLAGS, NULL);
2083     if (ipal != opal && (oflags & WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE)) needs_reinit = TRUE;
2084     lives_free(palettes);
2085   }
2086 
2087   weed_layer_pixel_data_free(dchan);
2088 
2089   weed_set_int_value(dchan, WEED_LEAF_WIDTH, iwidth);
2090   weed_set_int_value(dchan, WEED_LEAF_HEIGHT, iheight);
2091   weed_set_int_value(dchan, WEED_LEAF_CURRENT_PALETTE, ipal);
2092 
2093   if (pal_ok) {
2094     weed_set_voidptr_value(dchan, WEED_LEAF_PIXEL_DATA, (void *)spdata);
2095     weed_set_int_value(dchan, WEED_LEAF_ROWSTRIDES, irow);
2096 
2097     /// caller - do not free in dchan
2098     weed_set_boolean_value(dchan, WEED_LEAF_HOST_ORIG_PDATA, WEED_TRUE);
2099 #ifdef DEBUG_CCONX
2100     if (spdata) g_print("spd3 is %p %d\n", spdata, spdata[0]);
2101 #endif
2102     return FALSE;
2103   }
2104 
2105   create_empty_pixel_data(dchan, FALSE, TRUE);
2106   dpdata = (uint8_t *)weed_get_voidptr_value(dchan, WEED_LEAF_PIXEL_DATA, NULL);
2107 
2108   orow = weed_get_int_value(dchan, WEED_LEAF_ROWSTRIDES, NULL);
2109 
2110   if (irow == orow) {
2111     lives_memcpy((void *)dpdata, (void *)spdata, irow * iheight);
2112   } else {
2113     int ipwidth = iwidth * weed_palette_get_bits_per_macropixel(ipal) / 8;
2114     for (i = 0; i < iheight; i++) {
2115       lives_memcpy((void *)dpdata, (void *)spdata, ipwidth);
2116       spdata += irow;
2117       dpdata += orow;
2118     }
2119   }
2120 
2121   convert_layer_palette(dchan, opal, 0);
2122 
2123   if (needs_reinit) return TRUE;
2124 
2125   return FALSE;
2126 }
2127 
2128 
cconx_chain_data(int key,int mode)2129 boolean cconx_chain_data(int key, int mode) {
2130   // ret TRUE if we should reinit inst (because of palette change)
2131 
2132   weed_plant_t *ichan, *ochan;
2133   weed_plant_t *inst = NULL;
2134 
2135   boolean needs_reinit = FALSE;
2136 
2137   int i = 0;
2138 
2139   if (key > -1) {
2140     if (mainw->is_rendering) {
2141       if ((inst = get_new_inst_for_keymode(key, mode)) == NULL) {
2142         return FALSE; ///< dest effect is not found
2143       }
2144     } else {
2145       if ((inst = rte_keymode_get_instance(key + 1, mode)) == NULL) {
2146         return FALSE; ///< dest effect is not enabled
2147       }
2148     }
2149   } else if (key == FX_DATA_KEY_PLAYBACK_PLUGIN) {
2150     if (!mainw->vpp || mainw->vpp->num_alpha_chans == 0) return FALSE;
2151   }
2152 
2153   while ((ichan = (key == FX_DATA_KEY_PLAYBACK_PLUGIN ? (weed_plant_t *)pp_get_chan(mainw->vpp->play_params, i)
2154                    : get_enabled_channel(inst, i, TRUE))) != NULL) {
2155     if ((ochan = cconx_get_out_alpha(FALSE, key, mode, i++, NULL, NULL, NULL))) {
2156       filter_mutex_lock(key);
2157       if (cconx_convert_pixel_data(ichan, ochan)) needs_reinit = TRUE;
2158       filter_mutex_unlock(key);
2159     }
2160   }
2161   //if (inst) weed_instance_unref(inst);
2162   return needs_reinit;
2163 }
2164 
2165 
cconx_chain_data_internal(weed_plant_t * ichan)2166 boolean cconx_chain_data_internal(weed_plant_t *ichan) {
2167   // special version for compound fx internal connections
2168   boolean needs_reinit = FALSE;
2169 
2170   if (weed_plant_has_leaf(ichan, WEED_LEAF_HOST_INTERNAL_CONNECTION)) {
2171     weed_plant_t *ochan = weed_get_plantptr_value(ichan, WEED_LEAF_HOST_INTERNAL_CONNECTION, NULL);
2172     if (cconx_convert_pixel_data(ichan, ochan)) needs_reinit = TRUE;
2173   }
2174   return needs_reinit;
2175 }
2176 
2177 
feeds_to_video_filters(int okey,int omode)2178 boolean feeds_to_video_filters(int okey, int omode) {
2179   weed_plant_t *filter;
2180   char **array;
2181   char *chlist;
2182   int nparams, niparams;
2183   int ikey, imode, i, j;
2184 
2185   filter = rte_keymode_get_filter(okey + 1, omode);
2186 
2187   nparams = num_out_params(filter);
2188 
2189   for (i = 0; i < nparams; i++) {
2190     chlist = pconx_list(okey, omode, i);
2191     niparams = get_token_count(chlist, ' ') / 4;
2192     array = lives_strsplit(chlist, " ", niparams * 4);
2193     for (j = 0; j < niparams; j += 4) {
2194       ikey = atoi(array[j]);
2195       imode = atoi(array[j + 1]);
2196       if (imode != rte_key_getmode(ikey + 1)) continue;
2197       filter = rte_keymode_get_filter(ikey + 1, imode);
2198       if (has_video_chans_in(filter, TRUE) || has_video_chans_out(filter, TRUE)) {
2199         lives_strfreev(array);
2200         lives_free(chlist);
2201         return TRUE;
2202       }
2203     }
2204     lives_strfreev(array);
2205     lives_free(chlist);
2206   }
2207 
2208   for (i = 0; i < nparams; i++) {
2209     chlist = cconx_list(okey, omode, i);
2210     niparams = get_token_count(chlist, ' ') / 3;
2211     array = lives_strsplit(chlist, " ", niparams * 3);
2212     for (j = 0; j < niparams; j += 3) {
2213       ikey = atoi(array[j]);
2214       imode = atoi(array[j + 1]);
2215       if (imode != rte_key_getmode(ikey + 1)) continue;
2216       lives_strfreev(array);
2217       lives_free(chlist);
2218       return TRUE;
2219     }
2220     lives_strfreev(array);
2221     lives_free(chlist);
2222   }
2223 
2224   return FALSE;
2225 }
2226 
2227 
feeds_to_audio_filters(int okey,int omode)2228 boolean feeds_to_audio_filters(int okey, int omode) {
2229   weed_plant_t *filter;
2230   char **array;
2231   char *chlist;
2232   int nparams, niparams;
2233   int ikey, imode;
2234   int i, j;
2235 
2236   filter = rte_keymode_get_filter(okey + 1, omode);
2237 
2238   nparams = num_out_params(filter);
2239 
2240   for (i = 0; i < nparams; i++) {
2241     chlist = pconx_list(okey, omode, i);
2242     niparams = get_token_count(chlist, ' ') / 4;
2243     array = lives_strsplit(chlist, " ", niparams * 4);
2244     for (j = 0; j < niparams; j += 4) {
2245       ikey = atoi(array[j]);
2246       if (ikey < 0) continue;
2247       imode = atoi(array[j + 1]);
2248       if (imode != rte_key_getmode(ikey + 1)) continue;
2249       filter = rte_keymode_get_filter(ikey + 1, imode);
2250       if (has_audio_chans_in(filter, TRUE) || has_audio_chans_out(filter, TRUE)) {
2251         lives_strfreev(array);
2252         lives_free(chlist);
2253         return TRUE;
2254       }
2255     }
2256     lives_strfreev(array);
2257     lives_free(chlist);
2258   }
2259 
2260   for (i = 0; i < nparams; i++) {
2261     chlist = cconx_list(okey, omode, i);
2262     niparams = get_token_count(chlist, ' ') / 3;
2263     array = lives_strsplit(chlist, " ", niparams * 3);
2264     for (j = 0; j < niparams; j += 3) {
2265       ikey = atoi(array[j]);
2266       if (ikey < 0) continue;
2267       imode = atoi(array[j + 1]);
2268       if (imode != rte_key_getmode(ikey + 1)) continue;
2269       filter = rte_keymode_get_filter(ikey + 1, imode);
2270       if (has_audio_chans_in(filter, TRUE) || has_audio_chans_out(filter, TRUE)) {
2271         lives_strfreev(array);
2272         lives_free(chlist);
2273         return TRUE;
2274       }
2275     }
2276     lives_strfreev(array);
2277     lives_free(chlist);
2278   }
2279 
2280   return FALSE;
2281 }
2282 
2283 
2284 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2285 
2286 // channel/param connection window
2287 
2288 enum {
2289   KEY_COLUMN,
2290   NAME_COLUMN,
2291   KEYVAL_COLUMN,
2292   MODEVAL_COLUMN,
2293   EFD_NUM_COLUMNS
2294 };
2295 
2296 
disconbutton_clicked(LiVESButton * button,livespointer user_data)2297 static void disconbutton_clicked(LiVESButton * button, livespointer user_data) {
2298   // disconnect all channels/params
2299   lives_conx_w *conxwp = (lives_conx_w *)user_data;
2300 
2301   int totparams, totchans;
2302   int pidx, pidx_last, cidx, cidx_last;
2303 
2304   register int i;
2305 
2306   totparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2307   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2308 
2309   for (i = 0; i < totchans; i++) {
2310     lives_combo_set_active_index(LIVES_COMBO(conxwp->cfxcombo[i]), 0);
2311 
2312     if (i == 0) lives_widget_set_sensitive(conxwp->del_button[i], FALSE);
2313     else {
2314       cidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->ccombo[i]), "cidx"));
2315       cidx_last = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->ccombo[i - 1]), "cidx"));
2316       lives_widget_set_sensitive(conxwp->del_button[i], cidx == cidx_last);
2317     }
2318   }
2319 
2320   for (i = 0; i < totparams; i++) {
2321     lives_combo_set_active_index(LIVES_COMBO(conxwp->pfxcombo[i]), 0);
2322 
2323     if (i == 0) lives_widget_set_sensitive(conxwp->del_button[i + totchans], FALSE);
2324     else {
2325       pidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[i]), "pidx"));
2326       pidx_last = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[i - 1]), "pidx"));
2327       lives_widget_set_sensitive(conxwp->del_button[i + totchans], pidx == pidx_last);
2328     }
2329   }
2330 }
2331 
2332 
apbutton_clicked(LiVESButton * button,livespointer user_data)2333 static void apbutton_clicked(LiVESButton * button, livespointer user_data) {
2334   // autoconnect each param with a compatible one in the target
2335   lives_conx_w *conxwp = (lives_conx_w *)user_data;
2336 
2337   LiVESWidget *combo;
2338 
2339   LiVESTreeIter iter;
2340   LiVESTreeModel *model;
2341   LiVESTreePath *tpath;
2342 
2343   weed_plant_t **iparams, **oparams;
2344   weed_plant_t *filter, *param, *oparam;
2345 
2346   int fidx, key, mode, totchans;
2347   int niparams, ours, addn, stparam;
2348 
2349   int i, k = 1;
2350 
2351   // get filter from last connection from first parameter
2352 
2353   ours = pconx_get_numcons(conxwp, -EXTRA_PARAMS_OUT) + pconx_get_numcons(conxwp, 0) - 1;
2354 
2355   combo = (LiVESWidget *)conxwp->pfxcombo[ours];
2356 
2357   if (!lives_combo_get_active_iter(LIVES_COMBO(combo), &iter)) return;
2358   model = lives_combo_get_model(LIVES_COMBO(combo));
2359 
2360   lives_tree_model_get(model, &iter, KEYVAL_COLUMN, &key, MODEVAL_COLUMN, &mode, -1);
2361 
2362   if (key == FX_DATA_KEY_OMC_MACRO) {
2363     // match with OMC macro
2364     //
2365     //
2366     return;
2367   }
2368 
2369   fidx = rte_keymode_get_filter_idx(key, mode);
2370 
2371   if (fidx == -1) return;
2372 
2373   tpath = lives_tree_model_get_path(model, &iter);
2374   lives_tree_model_get_iter(model, &iter, tpath);
2375 
2376   // find the receiving filter/instance
2377   filter = get_weed_filter(fidx);
2378 
2379   iparams = weed_get_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, NULL);
2380   niparams = weed_leaf_num_elements(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES);
2381 
2382 #ifdef COMBO_LIST_LIMIT
2383   if (niparams + EXTRA_PARAMS_IN > COMBO_LIST_LIMIT) {
2384     niparams = COMBO_LIST_LIMIT - EXTRA_PARAMS_IN;
2385   }
2386 #endif
2387 
2388   oparams = weed_get_plantptr_array(rte_keymode_get_filter(conxwp->okey + 1, conxwp->omode),
2389                                     WEED_LEAF_OUT_PARAMETER_TEMPLATES, NULL);
2390 
2391   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2392 
2393   // get first param connected
2394   stparam = conxwp->idx[totchans + ours];
2395 
2396   if (conxwp->ikeys[totchans + ours] < 0) {
2397     // first out not connected, we will add this back in
2398     ours -= pconx_get_numcons(conxwp, 0);
2399     k = 0;
2400   } else stparam++;
2401 
2402   // set all pcombo with params
2403   for (i = stparam; i < niparams; i++) {
2404 
2405     param = iparams[i];
2406 
2407     if (weed_plant_has_leaf(param, WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
2408 
2409     if (pconx_get_out_param(TRUE, key - 1, mode, i, NULL, NULL, NULL, NULL)) continue;
2410 
2411     oparam = oparams[k];
2412 
2413     if (!params_compatible(oparam, param)) continue;
2414 
2415     addn = pconx_get_numcons(conxwp, k);
2416 
2417     ours += addn;
2418 
2419     combo = conxwp->pcombo[ours];
2420 
2421     if (conxwp->ikeys[ours + totchans] >= 0) {
2422       // add another if needed
2423       padd_clicked(conxwp->add_button[ours + totchans], (livespointer)conxwp);
2424       ours++;
2425     }
2426 
2427     combo = conxwp->pfxcombo[ours];
2428     model = lives_combo_get_model(LIVES_COMBO(combo));
2429     lives_tree_model_get_iter(model, &iter, tpath);
2430     lives_combo_set_active_iter(LIVES_COMBO(combo), &iter);
2431     lives_widget_context_update();
2432 
2433     lives_combo_set_active_index(LIVES_COMBO(conxwp->pcombo[ours]), i + EXTRA_PARAMS_IN);
2434 
2435     if (++k >= conxwp->num_params - EXTRA_PARAMS_OUT) break;
2436   }
2437 
2438   lives_tree_path_free(tpath);
2439 
2440   lives_free(iparams);
2441   lives_free(oparams);
2442 }
2443 
2444 
acbutton_clicked(LiVESButton * button,livespointer user_data)2445 static void acbutton_clicked(LiVESButton * button, livespointer user_data) {
2446   // autoconnect each channel with a compatible one in the target
2447   lives_conx_w *conxwp = (lives_conx_w *)user_data;
2448 
2449   LiVESWidget *combo;
2450 
2451   LiVESTreeIter iter;
2452   LiVESTreeModel *model;
2453   LiVESTreePath *tpath;
2454 
2455   weed_plant_t **ichans, **ochans;
2456   weed_plant_t *filter, *chan, *ochan;
2457 
2458   int fidx, key, mode;
2459   int nichans, nochans, ours, addn, stchan;
2460 
2461   int i, j = 0, k = 1;
2462 
2463   // get filter from last connection from first parameter
2464 
2465   ours = cconx_get_numcons(conxwp, 0) - 1;
2466 
2467   combo = (LiVESWidget *)conxwp->cfxcombo[ours];
2468 
2469   if (!lives_combo_get_active_iter(LIVES_COMBO(combo), &iter)) return;
2470   model = lives_combo_get_model(LIVES_COMBO(combo));
2471 
2472   lives_tree_model_get(model, &iter, KEYVAL_COLUMN, &key, MODEVAL_COLUMN, &mode, -1);
2473   fidx = rte_keymode_get_filter_idx(key, mode);
2474 
2475   if (fidx == -1) return;
2476 
2477   tpath = lives_tree_model_get_path(model, &iter);
2478   lives_tree_model_get_iter(model, &iter, tpath);
2479 
2480   // find the receiving filter/instance
2481   filter = get_weed_filter(fidx);
2482 
2483   ichans = weed_get_plantptr_array(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, NULL);
2484   nichans = weed_leaf_num_elements(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES);
2485 
2486   ochans = weed_get_plantptr_array(rte_keymode_get_filter(conxwp->okey + 1, conxwp->omode),
2487                                    WEED_LEAF_OUT_CHANNEL_TEMPLATES, NULL);
2488   nochans = weed_leaf_num_elements(rte_keymode_get_filter(conxwp->okey + 1, conxwp->omode),
2489                                    WEED_LEAF_OUT_CHANNEL_TEMPLATES);
2490 
2491   // get first param connected
2492   stchan = conxwp->idx[ours];
2493 
2494   if (conxwp->ikeys[ours] < 0) {
2495     // first out not connected, we will add this back in
2496     ours -= cconx_get_numcons(conxwp, 0);
2497     k = 0;
2498   } else {
2499     stchan++;
2500 
2501     for (i = 0; i < nichans; i++) {
2502       j++;
2503       chan = ichans[i];
2504       if (!has_alpha_palette(chan, filter)) continue;
2505       if (i == conxwp->idx[ours]) break;
2506     }
2507   }
2508 
2509   // set all ccombo with chans
2510   for (i = stchan; i < nichans; i++) {
2511 
2512     chan = ichans[i];
2513 
2514     if (!has_alpha_palette(chan, filter)) continue;
2515 
2516     if (cconx_get_out_alpha(TRUE, key - 1, mode, i, NULL, NULL, NULL)) continue;
2517 
2518     ochan = ochans[k];
2519 
2520     if (!has_alpha_palette(ochan, filter)) {
2521       if (++k >= nochans) break;
2522       continue;
2523     }
2524 
2525     addn = cconx_get_numcons(conxwp, k);
2526 
2527     ours += addn;
2528 
2529     combo = conxwp->ccombo[ours];
2530 
2531     if (conxwp->ikeys[ours] >= 0) {
2532       // add another if needed
2533       cadd_clicked(conxwp->add_button[ours], (livespointer)conxwp);
2534       ours++;
2535     }
2536 
2537     combo = conxwp->cfxcombo[ours];
2538     model = lives_combo_get_model(LIVES_COMBO(combo));
2539     lives_tree_model_get_iter(model, &iter, tpath);
2540     lives_combo_set_active_iter(LIVES_COMBO(combo), &iter);
2541     lives_widget_context_update();
2542 
2543     lives_combo_set_active_index(LIVES_COMBO(conxwp->ccombo[ours]), j++);
2544 
2545     if (++k >= nochans) break;
2546   }
2547 
2548   lives_tree_path_free(tpath);
2549 
2550   lives_free(ichans);
2551   lives_free(ochans);
2552 }
2553 
2554 
padd_clicked(LiVESWidget * button,livespointer user_data)2555 static void padd_clicked(LiVESWidget * button, livespointer user_data) {
2556   // add another param row below the add button
2557   lives_conx_w *conxwp = (lives_conx_w *)user_data;
2558 
2559   int totparams, totchans;
2560   int ours = -1, pidx;
2561 #if LIVES_TABLE_IS_GRID
2562   int trows;
2563 #else
2564   LiVESWidget *hbox[5], *hboxb[5], *achbox, *comhbox;
2565 #endif
2566 
2567   int i;
2568 
2569   totparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2570   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2571 
2572   for (i = 0; i < totparams; i++) {
2573     if (conxwp->add_button[i + totchans] == button) {
2574       ours = i;
2575       break;
2576     }
2577   }
2578 
2579   pidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[ours]), "pidx"));
2580 
2581   totparams++;
2582 
2583   conxwp->pclabel = (LiVESWidget **)lives_realloc(conxwp->pclabel, (totchans + totparams) * sizeof(LiVESWidget *));
2584 
2585   conxwp->add_button = (LiVESWidget **)lives_realloc(conxwp->add_button, (totchans + totparams) * sizeof(LiVESWidget *));
2586   conxwp->del_button = (LiVESWidget **)lives_realloc(conxwp->del_button, (totchans + totparams) * sizeof(LiVESWidget *));
2587   conxwp->clabel = (LiVESWidget **)lives_realloc(conxwp->clabel, (totchans + totparams) * sizeof(LiVESWidget *));
2588 
2589   conxwp->ikeys = (int *)lives_realloc(conxwp->ikeys, (totchans + totparams) * sizint);
2590   conxwp->imodes = (int *)lives_realloc(conxwp->imodes, (totchans + totparams) * sizint);
2591   conxwp->idx = (int *)lives_realloc(conxwp->idx, (totchans + totparams) * sizint);
2592 
2593   conxwp->pfxcombo = (LiVESWidget **)lives_realloc(conxwp->pfxcombo, totparams * sizeof(LiVESWidget *));
2594   conxwp->pcombo = (LiVESWidget **)lives_realloc(conxwp->pcombo, totparams * sizeof(LiVESWidget *));
2595 
2596   conxwp->dpp_func = (ulong *)lives_realloc(conxwp->dpp_func, totparams * sizeof(ulong));
2597   conxwp->acheck_func = (ulong *)lives_realloc(conxwp->acheck_func, totparams * sizeof(ulong));
2598 
2599   conxwp->acheck = (LiVESWidget **)lives_realloc(conxwp->acheck, totparams * sizeof(LiVESWidget *));
2600 
2601   conxwp->trowsp++;
2602 
2603   for (i = conxwp->trowsp - 3; i > ours; i--) {
2604     conxwp->del_button[i + 1] = conxwp->del_button[i];
2605     conxwp->add_button[i + 1] = conxwp->add_button[i];
2606     conxwp->clabel[i + 1] = conxwp->clabel[i];
2607   }
2608 
2609 #if !LIVES_TABLE_IS_GRID
2610   lives_table_resize(LIVES_TABLE(conxwp->tablep), conxwp->trowsp, 7);
2611 
2612   // add parent widgets to new row
2613 
2614   for (i = 0; i < 5; i++) {
2615     hbox[i] = lives_hbox_new(FALSE, 0);
2616 
2617     lives_table_attach(LIVES_TABLE(conxwp->tablep), hbox[i], i, i + 1, conxwp->trowsp - 1, conxwp->trowsp,
2618                        (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
2619                        (LiVESAttachOptions)(0), 0, 0);
2620 
2621   }
2622 
2623   ptable_row_add_standard_widgets(conxwp, totparams - 1);
2624 #else
2625   lives_grid_insert_row(LIVES_GRID(conxwp->tablep), ours + 2);
2626   trows = conxwp->trowsp;
2627   conxwp->trowsp = ours + 3;
2628   ptable_row_add_standard_widgets(conxwp, ours + 1);
2629   conxwp->trowsp = trows;
2630 #endif
2631 
2632   // subtract 1 from trowsp because of title row
2633   for (i = conxwp->trowsp - 3; i > ours; i--) {
2634 #if !LIVES_TABLE_IS_GRID
2635 
2636     // reparent widgets from row i to row i+1
2637     hboxb[0] = lives_widget_get_parent(conxwp->pclabel[i]);
2638     lives_widget_reparent(conxwp->pclabel[i], hbox[0]);
2639     hbox[0] = hboxb[0];
2640 
2641     hboxb[2] = lives_widget_get_parent(conxwp->pfxcombo[i]);
2642     lives_widget_reparent(conxwp->pfxcombo[i], hbox[2]);
2643     hbox[2] = hboxb[2];
2644 
2645     comhbox = lives_widget_get_parent(conxwp->pcombo[i]);
2646     hboxb[3] = lives_widget_get_parent(comhbox);
2647     lives_widget_reparent(comhbox, hbox[3]);
2648     hbox[3] = hboxb[3];
2649 
2650     if (conxwp->acheck[i]) {
2651       achbox = lives_widget_get_parent(conxwp->acheck[i]);
2652       hboxb[4] = lives_widget_get_parent(achbox);
2653       lives_widget_reparent(achbox, hbox[4]);
2654       hbox[4] = hboxb[4];
2655     }
2656 
2657 #endif
2658 
2659     conxwp->pclabel[i + 1] = conxwp->pclabel[i];
2660 
2661     conxwp->pfxcombo[i + 1] = conxwp->pfxcombo[i];
2662     conxwp->pcombo[i + 1] = conxwp->pcombo[i];
2663 
2664     conxwp->acheck[i + 1] = conxwp->acheck[i];
2665     conxwp->acheck_func[i + 1] = conxwp->acheck_func[i];
2666 
2667     conxwp->ikeys[i + 1] = conxwp->ikeys[i];
2668     conxwp->imodes[i + 1] = conxwp->imodes[i];
2669     conxwp->idx[i + 1] = conxwp->idx[i];
2670 
2671     conxwp->dpp_func[i + 1] = conxwp->dpp_func[i];
2672   }
2673 
2674   ptable_row_add_variable_widgets(conxwp, ours + 1, ours + 2, pidx);
2675 
2676   conxwp->ikeys[ours + 1] = conxwp->imodes[ours + 1] = conxwp->idx[i + 1] = 0;
2677 
2678   conxwp->dispp[pidx + EXTRA_PARAMS_OUT]++;
2679 
2680   lives_widget_set_sensitive(conxwp->del_button[ours + 1], TRUE);
2681   lives_widget_set_sensitive(conxwp->add_button[ours], FALSE);
2682 
2683   lives_widget_show_all(conxwp->tablep);
2684 }
2685 
2686 
pdel_clicked(LiVESWidget * button,livespointer user_data)2687 static void pdel_clicked(LiVESWidget * button, livespointer user_data) {
2688   //  remove the param row at the del button
2689   lives_conx_w *conxwp = (lives_conx_w *)user_data;
2690 
2691   int totparams, totchans;
2692   int ours = -1, pidx;
2693 
2694 #if !LIVES_TABLE_IS_GRID
2695   LiVESWidget *comhbox;
2696   LiVESWidget *hbox[4], *hboxb[4], *achbox;
2697   int pidx_next;
2698 #endif
2699 
2700   register int i;
2701 
2702 #if !LIVES_TABLE_IS_GRID
2703   hbox[3] = NULL;
2704 #endif
2705 
2706   totparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2707   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2708 
2709   for (i = 0; i < totparams; i++) {
2710     if (conxwp->del_button[i + totchans] == button) {
2711       ours = i;
2712       break;
2713     }
2714   }
2715 
2716   pidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[ours]), "pidx"));
2717 
2718   lives_combo_set_active_index(LIVES_COMBO(conxwp->pfxcombo[ours]), 0);
2719 
2720   if (conxwp->dispp[pidx + EXTRA_PARAMS_OUT] <= 1) {
2721     // last one for this param, dont delete, just clear
2722     lives_widget_set_sensitive(conxwp->del_button[totchans + ours], FALSE);
2723     return;
2724   }
2725 
2726   if (ours > 0) {
2727     if (lives_widget_get_sensitive(conxwp->del_button[totchans + ours - 1])
2728         && lives_widget_get_sensitive(conxwp->add_button[totchans + ours])) {
2729       lives_widget_set_sensitive(conxwp->add_button[totchans + ours - 1], TRUE);
2730     }
2731   }
2732 
2733   conxwp->dispp[pidx + EXTRA_PARAMS_OUT]--;
2734 
2735   // force callback for pfxcombo before destroying it
2736   lives_widget_context_update();
2737   totparams--;
2738 
2739 #if !LIVES_TABLE_IS_GRID
2740   hbox[0] = lives_widget_get_parent(conxwp->pclabel[totchans + ours]);
2741   hbox[1] = lives_widget_get_parent(conxwp->pfxcombo[ours]);
2742 
2743   comhbox = lives_widget_get_parent(conxwp->pcombo[ours]);
2744   hbox[2] = lives_widget_get_parent(comhbox);
2745 
2746   lives_widget_destroy(conxwp->pfxcombo[ours]);
2747   lives_widget_destroy(conxwp->pcombo[ours]);
2748   lives_widget_destroy(comhbox);
2749 #endif
2750 
2751   conxwp->trowsp--;
2752 
2753   // subtract 1 from trowsp because of title row
2754   for (i = ours; i < conxwp->trowsp - 1; i++) {
2755 #if !LIVES_TABLE_IS_GRID
2756 
2757     // reparent widgets from row i to row i+1
2758 
2759     hboxb[0] = lives_widget_get_parent(conxwp->pclabel[totchans + i + 1]);
2760 
2761     if (i == ours) {
2762       pidx_next = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[i + 1]), "pidx"));
2763       if (pidx_next != pidx) {
2764         // secondary param
2765         lives_widget_destroy(conxwp->pclabel[totchans + i]);
2766         lives_widget_reparent(conxwp->pclabel[totchans + i + 1], hbox[0]);
2767         conxwp->pclabel[totchans + i] = conxwp->pclabel[totchans + i + 1];
2768       } else {
2769         // primary param
2770         lives_widget_destroy(conxwp->pclabel[totchans + i + 1]);
2771       }
2772     } else {
2773       lives_widget_reparent(conxwp->pclabel[totchans + i + 1], hbox[0]);
2774       conxwp->pclabel[totchans + i] = conxwp->pclabel[totchans + i + 1];
2775     }
2776 
2777     hbox[0] = hboxb[0];
2778 
2779     hboxb[1] = lives_widget_get_parent(conxwp->pfxcombo[i + 1]);
2780     lives_widget_reparent(conxwp->pfxcombo[i + 1], hbox[1]);
2781     hbox[1] = hboxb[1];
2782 
2783     comhbox = lives_widget_get_parent(conxwp->pcombo[i + 1]);
2784     hboxb[2] = lives_widget_get_parent(comhbox);
2785     lives_widget_reparent(comhbox, hbox[2]);
2786     hbox[2] = hboxb[2];
2787 
2788     if (conxwp->acheck[i]) {
2789       if (!hbox[3]) {
2790         achbox = lives_widget_get_parent(conxwp->acheck[i]);
2791         hbox[3] = lives_widget_get_parent(achbox);
2792         lives_widget_destroy(achbox);
2793       }
2794       achbox = lives_widget_get_parent(conxwp->acheck[i + 1]);
2795       hboxb[3] = lives_widget_get_parent(achbox);
2796       lives_widget_reparent(achbox, hbox[3]);
2797       hbox[3] = hboxb[3];
2798     }
2799 
2800     lives_widget_set_sensitive(conxwp->del_button[totchans + i],
2801                                lives_widget_get_sensitive(conxwp->del_button[totchans + i + 1]));
2802 
2803 #else
2804     conxwp->add_button[i] = conxwp->add_button[i + 1];
2805     conxwp->del_button[i] = conxwp->del_button[i + 1];
2806     conxwp->clabel[i] = conxwp->clabel[i + 1];
2807     conxwp->pclabel[i] = conxwp->pclabel[i + 1];
2808 
2809     if (i == ours) {
2810       widget_opts.mnemonic_label = FALSE;
2811       lives_label_set_text(LIVES_LABEL(conxwp->pclabel[i + 1]), lives_label_get_text(LIVES_LABEL(conxwp->pclabel[i])));
2812       widget_opts.mnemonic_label = TRUE;
2813     }
2814 #endif
2815     conxwp->pfxcombo[i] = conxwp->pfxcombo[i + 1];
2816     conxwp->pcombo[i] = conxwp->pcombo[i + 1];
2817 
2818     conxwp->acheck[i] = conxwp->acheck[i + 1];
2819     conxwp->acheck_func[i] = conxwp->acheck_func[i + 1];
2820 
2821     conxwp->ikeys[i] = conxwp->ikeys[i + 1];
2822     conxwp->imodes[i] = conxwp->imodes[i + 1];
2823     conxwp->idx[i] = conxwp->idx[i + 1];
2824 
2825     conxwp->dpp_func[i] = conxwp->dpp_func[i + 1];
2826   }
2827 
2828 #if !LIVES_TABLE_IS_GRID
2829   lives_widget_destroy(conxwp->clabel[conxwp->trowsp - 1 + totchans]);
2830   lives_widget_destroy(conxwp->add_button[conxwp->trowsp - 1 + totchans]);
2831   lives_widget_destroy(conxwp->del_button[conxwp->trowsp - 1 + totchans]);
2832 
2833   // destroy (empty) last row parent widgets
2834   lives_widget_destroy(hbox[0]);
2835   lives_widget_destroy(hbox[1]);
2836   lives_widget_destroy(hbox[2]);
2837   lives_widget_destroy(hbox[3]);
2838 
2839   lives_table_resize(LIVES_TABLE(conxwp->tablep), conxwp->trowsp, 7);
2840 #else
2841   lives_grid_remove_row(LIVES_GRID(conxwp->tablep), ours + 1);
2842 #endif
2843 
2844   conxwp->pclabel = (LiVESWidget **)lives_realloc(conxwp->pclabel, (totchans + totparams) * sizeof(LiVESWidget *));
2845 
2846   conxwp->add_button = (LiVESWidget **)lives_realloc(conxwp->add_button, (totchans + totparams) * sizeof(LiVESWidget *));
2847   conxwp->del_button = (LiVESWidget **)lives_realloc(conxwp->del_button, (totchans + totparams) * sizeof(LiVESWidget *));
2848   conxwp->clabel = (LiVESWidget **)lives_realloc(conxwp->clabel, (totchans + totparams) * sizeof(LiVESWidget *));
2849 
2850   conxwp->ikeys = (int *)lives_realloc(conxwp->ikeys, (totchans + totparams) * sizint);
2851   conxwp->imodes = (int *)lives_realloc(conxwp->imodes, (totchans + totparams) * sizint);
2852   conxwp->idx = (int *)lives_realloc(conxwp->idx, (totchans + totparams) * sizint);
2853 
2854   conxwp->pfxcombo = (LiVESWidget **)lives_realloc(conxwp->pfxcombo, totparams * sizeof(LiVESWidget *));
2855   conxwp->pcombo = (LiVESWidget **)lives_realloc(conxwp->pcombo, totparams * sizeof(LiVESWidget *));
2856 
2857   conxwp->dpp_func = (ulong *)lives_realloc(conxwp->dpp_func, totparams * sizeof(ulong));
2858   conxwp->acheck_func = (ulong *)lives_realloc(conxwp->acheck_func, totparams * sizeof(ulong));
2859 
2860   conxwp->acheck = (LiVESWidget **)lives_realloc(conxwp->acheck, totparams * sizeof(LiVESWidget *));
2861 }
2862 
2863 
cadd_clicked(LiVESWidget * button,livespointer user_data)2864 static void cadd_clicked(LiVESWidget * button, livespointer user_data) {
2865   // add another channel row below the add button
2866   lives_conx_w *conxwp = (lives_conx_w *)user_data;
2867 
2868 #if LIVES_TABLE_IS_GRID
2869   int trows;
2870 #else
2871   LiVESWidget *hbox[4], *hboxb[4], *comhbox;
2872 #endif
2873 
2874   int totparams, totchans;
2875   int ours = -1, cidx;
2876 
2877   register int i;
2878 
2879   totparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2880   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
2881 
2882   for (i = 0; i < totchans; i++) {
2883     if (conxwp->add_button[i] == button) {
2884       ours = i;
2885       break;
2886     }
2887   }
2888 
2889   cidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->cfxcombo[ours]), "cidx"));
2890 
2891   conxwp->dispc[cidx]++;
2892 
2893   totchans++;
2894 
2895   conxwp->pclabel = (LiVESWidget **)lives_realloc(conxwp->pclabel, (totchans + totparams) * sizeof(LiVESWidget *));
2896 
2897   conxwp->add_button = (LiVESWidget **)lives_realloc(conxwp->add_button, (totchans + totparams) * sizeof(LiVESWidget *));
2898   conxwp->del_button = (LiVESWidget **)lives_realloc(conxwp->del_button, (totchans + totparams) * sizeof(LiVESWidget *));
2899   conxwp->clabel = (LiVESWidget **)lives_realloc(conxwp->clabel, (totchans + totparams) * sizeof(LiVESWidget *));
2900 
2901   conxwp->ikeys = (int *)lives_realloc(conxwp->ikeys, (totchans + totparams) * sizint);
2902   conxwp->imodes = (int *)lives_realloc(conxwp->imodes, (totchans + totparams) * sizint);
2903   conxwp->idx = (int *)lives_realloc(conxwp->idx, (totchans + totparams) * sizint);
2904 
2905   conxwp->cfxcombo = (LiVESWidget **)lives_realloc(conxwp->cfxcombo, totchans * sizeof(LiVESWidget *));
2906   conxwp->ccombo = (LiVESWidget **)lives_realloc(conxwp->ccombo, totchans * sizeof(LiVESWidget *));
2907 
2908   conxwp->dpc_func = (ulong *)lives_realloc(conxwp->dpc_func, totchans * sizeof(ulong));
2909 
2910   conxwp->trowsc++;
2911 
2912   for (i = conxwp->trowsc - 3; i > ours; i--) {
2913     conxwp->del_button[i + 1] = conxwp->del_button[i];
2914     conxwp->add_button[i + 1] = conxwp->add_button[i];
2915     conxwp->clabel[i + 1] = conxwp->clabel[i];
2916   }
2917 
2918 #if !LIVES_TABLE_IS_GRID
2919   lives_table_resize(LIVES_TABLE(conxwp->tablec), conxwp->trowsc, 6);
2920 
2921   // add parent widgets to new row
2922 
2923   for (i = 0; i < 4; i++) {
2924     hbox[i] = lives_hbox_new(FALSE, 0);
2925 
2926     lives_table_attach(LIVES_TABLE(conxwp->tablec), hbox[i], i, i + 1, conxwp->trowsc - 1, conxwp->trowsc,
2927                        (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
2928                        (LiVESAttachOptions)(0), 0, 0);
2929   }
2930 
2931   ctable_row_add_standard_widgets(conxwp, totchans - 1);
2932 #else
2933   lives_grid_insert_row(LIVES_GRID(conxwp->tablec), ours + 2);
2934   trows = conxwp->trowsc;
2935   conxwp->trowsc = ours + 3;
2936   ctable_row_add_standard_widgets(conxwp, ours + 1);
2937   conxwp->trowsc = trows;
2938 #endif
2939 
2940   // subtract 1 from trowsp because of title row
2941   for (i = conxwp->trowsc - 3; i > ours; i--) {
2942 #if !LIVES_TABLE_IS_GRID
2943 
2944     // reparent widgets from row i to row i+1
2945     hboxb[0] = lives_widget_get_parent(conxwp->pclabel[i]);
2946     lives_widget_reparent(conxwp->pclabel[i], hbox[0]);
2947     hbox[0] = hboxb[0];
2948 
2949     hboxb[2] = lives_widget_get_parent(conxwp->cfxcombo[i]);
2950     lives_widget_reparent(conxwp->cfxcombo[i], hbox[2]);
2951     hbox[2] = hboxb[2];
2952 
2953     comhbox = lives_widget_get_parent(conxwp->ccombo[i]);
2954     hboxb[3] = lives_widget_get_parent(comhbox);
2955     lives_widget_reparent(comhbox, hbox[3]);
2956     hbox[3] = hboxb[3];
2957 
2958 #endif
2959 
2960     conxwp->pclabel[i + 1] = conxwp->pclabel[i];
2961 
2962     conxwp->cfxcombo[i + 1] = conxwp->cfxcombo[i];
2963     conxwp->ccombo[i + 1] = conxwp->ccombo[i];
2964 
2965     conxwp->ikeys[i + 1] = conxwp->ikeys[i];
2966     conxwp->imodes[i + 1] = conxwp->imodes[i];
2967     conxwp->idx[i + 1] = conxwp->idx[i];
2968 
2969     conxwp->dpc_func[i + 1] = conxwp->dpc_func[i];
2970   }
2971 
2972   ctable_row_add_variable_widgets(conxwp, ours + 1, ours + 2, cidx);
2973 
2974   conxwp->ikeys[ours + 1] = -1;
2975   conxwp->imodes[ours + 1] = conxwp->idx[i + 1] = 0;
2976 
2977   conxwp->dispc[cidx]++;
2978 
2979   lives_widget_set_sensitive(conxwp->del_button[ours + 1], TRUE);
2980   lives_widget_set_sensitive(conxwp->add_button[ours], FALSE);
2981 
2982   lives_widget_show_all(conxwp->tablec);
2983 }
2984 
2985 
cdel_clicked(LiVESWidget * button,livespointer user_data)2986 static void cdel_clicked(LiVESWidget * button, livespointer user_data) {
2987   //  remove the channel  row at the del button
2988   lives_conx_w *conxwp = (lives_conx_w *)user_data;
2989 
2990   int totparams, totchans;
2991   int ours = -1, cidx;
2992 
2993 #if !LIVES_TABLE_IS_GRID
2994   LiVESWidget *hbox[3], *comhbox;
2995   LiVESWidget *hboxb[3];
2996   int cidx_next;
2997 #endif
2998 
2999   register int i;
3000 
3001   totparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3002   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3003 
3004   for (i = 0; i < totchans; i++) {
3005     if (conxwp->del_button[i] == button) {
3006       ours = i;
3007       break;
3008     }
3009   }
3010 
3011   cidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->ccombo[ours]), "cidx"));
3012 
3013   lives_combo_set_active_index(LIVES_COMBO(conxwp->cfxcombo[ours]), 0);
3014 
3015   if (conxwp->dispc[cidx] <= 1) {
3016     // last one, dont delete, just clear
3017     lives_widget_set_sensitive(conxwp->del_button[ours], FALSE);
3018     return;
3019   }
3020 
3021   // force callback for cfxcombo before destroying it
3022   lives_widget_context_update();
3023 
3024   conxwp->dispc[cidx]--;
3025 
3026   totchans--;
3027 
3028 #if !LIVES_TABLE_IS_GRID
3029   hbox[0] = lives_widget_get_parent(conxwp->pclabel[ours]);
3030   hbox[1] = lives_widget_get_parent(conxwp->cfxcombo[ours]);
3031 
3032   comhbox = lives_widget_get_parent(conxwp->ccombo[ours]);
3033   hbox[2] = lives_widget_get_parent(comhbox);
3034 
3035   lives_widget_destroy(conxwp->cfxcombo[ours]);
3036   lives_widget_destroy(conxwp->ccombo[ours]);
3037   lives_widget_destroy(comhbox);
3038 #endif
3039 
3040   conxwp->trowsc--;
3041 
3042   // subtract 1 from trowsc because of title row
3043   for (i = ours; i < conxwp->trowsc - 1; i++) {
3044 #if !LIVES_TABLE_IS_GRID
3045 
3046     // reparent widgets from row i to row i+1
3047 
3048     hboxb[0] = lives_widget_get_parent(conxwp->pclabel[i + 1]);
3049 
3050     if (i == ours) {
3051       cidx_next = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->ccombo[i + 1]), "cidx"));
3052       if (cidx_next != cidx) {
3053         // secondary chan
3054         lives_widget_destroy(conxwp->pclabel[i]);
3055         lives_widget_reparent(conxwp->pclabel[i + 1], hbox[0]);
3056         conxwp->pclabel[i] = conxwp->pclabel[i + 1];
3057       } else {
3058         // primary chan
3059         lives_widget_destroy(conxwp->pclabel[i + 1]);
3060       }
3061     } else {
3062       lives_widget_reparent(conxwp->pclabel[i + 1], hbox[0]);
3063       conxwp->pclabel[i] = conxwp->pclabel[i + 1];
3064     }
3065 
3066     hbox[0] = hboxb[0];
3067 
3068     hboxb[1] = lives_widget_get_parent(conxwp->cfxcombo[i + 1]);
3069     lives_widget_reparent(conxwp->cfxcombo[i + 1], hbox[1]);
3070     hbox[1] = hboxb[1];
3071 
3072     comhbox = lives_widget_get_parent(conxwp->ccombo[i + 1]);
3073     hboxb[2] = lives_widget_get_parent(comhbox);
3074     lives_widget_reparent(comhbox, hbox[2]);
3075     hbox[2] = hboxb[2];
3076 
3077     lives_widget_set_sensitive(conxwp->del_button[i],
3078                                lives_widget_get_sensitive(conxwp->del_button[i + 1]));
3079 
3080 #else
3081     conxwp->add_button[i] = conxwp->add_button[i + 1];
3082     conxwp->del_button[i] = conxwp->del_button[i + 1];
3083     conxwp->clabel[i] = conxwp->clabel[i + 1];
3084     conxwp->pclabel[i] = conxwp->pclabel[i + 1];
3085 
3086     if (i == ours) {
3087       widget_opts.mnemonic_label = FALSE;
3088       lives_label_set_text(LIVES_LABEL(conxwp->pclabel[i + 1]), lives_label_get_text(LIVES_LABEL(conxwp->pclabel[i])));
3089       widget_opts.mnemonic_label = TRUE;
3090     }
3091 #endif
3092     conxwp->cfxcombo[i] = conxwp->cfxcombo[i + 1];
3093     conxwp->ccombo[i] = conxwp->ccombo[i + 1];
3094 
3095     conxwp->ikeys[i] = conxwp->ikeys[i + 1];
3096     conxwp->imodes[i] = conxwp->imodes[i + 1];
3097     conxwp->idx[i] = conxwp->idx[i + 1];
3098 
3099     conxwp->dpc_func[i] = conxwp->dpc_func[i + 1];
3100   }
3101 
3102 #if !LIVES_TABLE_IS_GRID
3103   lives_widget_destroy(conxwp->clabel[conxwp->trowsc - 1]);
3104   lives_widget_destroy(conxwp->add_button[conxwp->trowsc - 1]);
3105   lives_widget_destroy(conxwp->del_button[conxwp->trowsc - 1]);
3106 
3107   // destroy (empty) last row parent widgets
3108   lives_widget_destroy(hbox[0]);
3109   lives_widget_destroy(hbox[1]);
3110   lives_widget_destroy(hbox[2]);
3111 
3112   lives_table_resize(LIVES_TABLE(conxwp->tablec), conxwp->trowsc, 6);
3113 #else
3114   lives_grid_remove_row(LIVES_GRID(conxwp->tablec), ours + 1);
3115 #endif
3116 
3117   if (ours == totchans) {
3118     if (lives_widget_get_sensitive(conxwp->del_button[ours - 1]))
3119       lives_widget_set_sensitive(conxwp->add_button[ours - 1], TRUE);
3120   }
3121 
3122   conxwp->pclabel = (LiVESWidget **)lives_realloc(conxwp->pclabel, (totchans + totparams) * sizeof(LiVESWidget *));
3123 
3124   conxwp->add_button = (LiVESWidget **)lives_realloc(conxwp->add_button, (totchans + totparams) * sizeof(LiVESWidget *));
3125   conxwp->del_button = (LiVESWidget **)lives_realloc(conxwp->del_button, (totchans + totparams) * sizeof(LiVESWidget *));
3126   conxwp->clabel = (LiVESWidget **)lives_realloc(conxwp->clabel, (totchans + totparams) * sizeof(LiVESWidget *));
3127 
3128   conxwp->ikeys = (int *)lives_realloc(conxwp->ikeys, (totchans + totparams) * sizint);
3129   conxwp->imodes = (int *)lives_realloc(conxwp->imodes, (totchans + totparams) * sizint);
3130   conxwp->idx = (int *)lives_realloc(conxwp->idx, (totchans + totparams) * sizint);
3131 
3132   conxwp->cfxcombo = (LiVESWidget **)lives_realloc(conxwp->cfxcombo, totchans * sizeof(LiVESWidget *));
3133   conxwp->ccombo = (LiVESWidget **)lives_realloc(conxwp->ccombo, totchans * sizeof(LiVESWidget *));
3134 
3135   conxwp->dpc_func = (ulong *)lives_realloc(conxwp->dpc_func, totchans * sizeof(ulong));
3136 }
3137 
3138 
dfxc_changed(LiVESWidget * combo,livespointer user_data)3139 static void dfxc_changed(LiVESWidget * combo, livespointer user_data) {
3140   lives_conx_w *conxw = (lives_conx_w *)user_data;
3141 
3142   LiVESTreeIter iter;
3143   LiVESTreeModel *model;
3144 
3145   weed_plant_t **ichans;
3146   weed_plant_t *filter, *chan;
3147 
3148   LiVESList *clist = NULL;
3149 
3150   char *channame;
3151 
3152   int fidx, cidx, key, mode;
3153   int nichans, nchans, ours = -1;
3154 
3155   int i, j = 0;
3156 
3157   if (!lives_combo_get_active_iter(LIVES_COMBO(combo), &iter)) return;
3158   model = lives_combo_get_model(LIVES_COMBO(combo));
3159 
3160   lives_tree_model_get(model, &iter, KEYVAL_COLUMN, &key, MODEVAL_COLUMN, &mode, -1);
3161   fidx = rte_keymode_get_filter_idx(key, mode);
3162 
3163   nchans = cconx_get_numcons(conxw, FX_DATA_WILDCARD);
3164 
3165   for (i = 0; i < nchans; i++) {
3166     if (conxw->cfxcombo[i] == combo) {
3167       ours = i;
3168       break;
3169     }
3170   }
3171 
3172   cidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(combo), "cidx"));
3173 
3174   if (fidx == -1) {
3175     lives_combo_set_active_string(LIVES_COMBO(conxw->ccombo[ours]), "");
3176     lives_signal_handler_block(conxw->ccombo[ours], conxw->dpc_func[ours]);
3177     lives_combo_populate(LIVES_COMBO(conxw->ccombo[ours]), NULL);
3178     lives_signal_handler_unblock(conxw->ccombo[ours], conxw->dpc_func[ours]);
3179     if (conxw->acbutton && cconx_get_nconns(conxw->cconx, 0) == 0 && cidx == 0)
3180       lives_widget_set_sensitive(conxw->acbutton, FALSE);
3181     lives_widget_set_sensitive(conxw->ccombo[ours], FALSE);
3182     return;
3183   }
3184 
3185   // find the receiving filter/instance
3186   filter = get_weed_filter(fidx);
3187 
3188   ichans = weed_get_plantptr_array(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, NULL);
3189   nichans = weed_leaf_num_elements(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES);
3190 
3191   // populate all ccombo with channels
3192   for (i = 0; i < nichans; i++) {
3193     chan = ichans[j++];
3194 
3195     if (!has_alpha_palette(chan, filter)) continue;
3196 
3197     channame = get_chan_name(chan, i, TRUE);
3198     clist = lives_list_append(clist, channame);
3199   }
3200 
3201   lives_free(ichans);
3202 
3203   lives_combo_set_active_string(LIVES_COMBO(conxw->ccombo[ours]), "");
3204   lives_signal_handler_block(conxw->ccombo[ours], conxw->dpc_func[ours]);
3205   lives_combo_populate(LIVES_COMBO(conxw->ccombo[ours]), clist);
3206   lives_signal_handler_unblock(conxw->ccombo[ours], conxw->dpc_func[ours]);
3207 
3208   if (cidx == 0) if (conxw->acbutton) lives_widget_set_sensitive(conxw->acbutton, TRUE);
3209   lives_widget_set_sensitive(conxw->ccombo[ours], TRUE);
3210 
3211   lives_list_free_all(&clist);
3212 }
3213 
3214 
dfxp_changed(LiVESWidget * combo,livespointer user_data)3215 static void dfxp_changed(LiVESWidget * combo, livespointer user_data) {
3216   // filter was changed
3217 
3218   lives_conx_w *conxwp = (lives_conx_w *)user_data;
3219 
3220   LiVESTreeIter iter;
3221   LiVESTreeModel *model;
3222 
3223   weed_plant_t **iparams = NULL;
3224   weed_plant_t *filter, *param;
3225 
3226   LiVESList *plist = NULL;
3227 
3228   char *paramname;
3229 
3230   char *ptype, *range;
3231   char *array_type, *text;
3232 
3233   int defelems, pflags, stype;
3234   int fidx, key, mode, pidx;
3235   int niparams = 0, nparams;
3236   int ours = -1, i, j = 0;
3237 
3238   if (!lives_combo_get_active_iter(LIVES_COMBO(combo), &iter)) {
3239     return;
3240   }
3241 
3242   nparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3243 
3244   for (i = 0; i < nparams; i++) {
3245     if (conxwp->pfxcombo[i] == combo) {
3246       ours = i;
3247       break;
3248     }
3249   }
3250 
3251   model = lives_combo_get_model(LIVES_COMBO(combo));
3252 
3253   lives_tree_model_get(model, &iter, KEYVAL_COLUMN, &key, MODEVAL_COLUMN, &mode, -1);
3254 
3255   if (key == FX_DATA_KEY_OMC_MACRO) {
3256     // match with OMC macro
3257     int nmatchparams = 0;
3258     weed_plant_t *ofilter;
3259     LiVESWidget *fxcombo = conxwp->pcombo[ours];
3260 
3261     // get type for out_param
3262     pidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(fxcombo), "pidx"));
3263     ofilter = conxwp->filter;
3264 
3265     if (pidx >= 0) {
3266       weed_plant_t **oparams = weed_get_plantptr_array(ofilter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, NULL);
3267       weed_plant_t *oparam = oparams[pidx];
3268       lives_free(oparams);
3269       stype = weed_leaf_seed_type(oparam, WEED_LEAF_DEFAULT);
3270     } else {
3271       // invent an "ACTIVATED" param
3272       stype = WEED_SEED_BOOLEAN;
3273     }
3274 
3275     // check omc_macros[mode]; show all params with matching type
3276 
3277     // TODO...
3278 
3279     // if nothing matched, then
3280 
3281     // if out is boolean, we can link to ACTIVATE, we can choose FALSE -> TRUE (TODO: or TRUE -> FALSE)
3282     //
3283     if (nmatchparams == 0 && stype == WEED_SEED_BOOLEAN) {
3284       text = lives_strdup_printf(_("False to True -> TRIGGER (%s)"), weed_seed_type_to_text(WEED_SEED_BOOLEAN));
3285       plist = lives_list_append(plist, text);
3286     }
3287   } else {
3288     fidx = rte_keymode_get_filter_idx(key, mode);
3289 
3290     pidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(combo), "pidx"));
3291 
3292     if (fidx == -1) {
3293       // connection deleted
3294       LiVESWidget *acheck = conxwp->acheck[ours];
3295       if (acheck) {
3296         lives_signal_handler_block(acheck, conxwp->acheck_func[ours]);
3297         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(acheck), FALSE);
3298         lives_widget_set_sensitive(acheck, FALSE);
3299         lives_signal_handler_unblock(acheck, conxwp->acheck_func[ours]);
3300       }
3301 
3302       lives_combo_set_active_string(LIVES_COMBO(conxwp->pcombo[ours]), "");
3303       lives_signal_handler_block(conxwp->pcombo[ours], conxwp->dpp_func[ours]);
3304       lives_combo_populate(LIVES_COMBO(conxwp->pcombo[ours]), NULL);
3305       lives_signal_handler_unblock(conxwp->pcombo[ours], conxwp->dpp_func[ours]);
3306 
3307       if (conxwp->apbutton && pconx_get_nconns(conxwp->pconx, 0) == 0 && pidx == 0)
3308         lives_widget_set_sensitive(conxwp->apbutton, FALSE);
3309 
3310       lives_widget_set_sensitive(conxwp->pcombo[ours], FALSE);
3311 
3312       return;
3313     }
3314 
3315     // find the receiving filter/instance
3316     filter = get_weed_filter(fidx);
3317 
3318     if (weed_plant_has_leaf(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES)) {
3319       iparams = weed_get_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, NULL);
3320       niparams = weed_leaf_num_elements(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES);
3321 #ifdef COMBO_LIST_LIMIT
3322       if (niparams + EXTRA_PARAMS_IN > COMBO_LIST_LIMIT) {
3323         niparams = COMBO_LIST_LIMIT - EXTRA_PARAMS_IN;
3324       }
3325 #endif
3326     }
3327 
3328     // populate pcombo with all in params
3329     for (i = -EXTRA_PARAMS_IN; i < niparams; i++) {
3330       if (i == FX_DATA_PARAM_ACTIVE) {
3331         ptype = weed_seed_type_to_text(WEED_SEED_BOOLEAN);
3332         text = lives_strdup_printf(_("ACTIVATE (%s)"), ptype);
3333       } else {
3334         param = iparams[j++];
3335 
3336         if (weed_plant_has_leaf(param, WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
3337 
3338         if (weed_plant_has_leaf(param, WEED_LEAF_GROUP) && weed_get_int_value(param, WEED_LEAF_GROUP, NULL) != 0) continue;
3339 
3340         paramname = get_param_name(param, j - 1, TRUE);
3341 
3342         ptype = weed_seed_type_to_text((stype = weed_leaf_seed_type(param, WEED_LEAF_DEFAULT)));
3343 
3344         pflags = weed_get_int_value(param, WEED_LEAF_FLAGS, NULL);
3345 
3346         if (pflags & WEED_PARAMETER_VARIABLE_SIZE) array_type = lives_strdup("[]");
3347         else if ((defelems = weed_leaf_num_elements(param, WEED_LEAF_DEFAULT)) > 1)
3348           array_type = lives_strdup_printf("[%d]", defelems);
3349         else array_type = lives_strdup("");
3350 
3351         if (weed_plant_has_leaf(param, WEED_LEAF_MAX) && weed_plant_has_leaf(param, WEED_LEAF_MIN)) {
3352           if (stype == WEED_SEED_INT) {
3353             range = lives_strdup_printf("Range: %d to %d",
3354                                         weed_get_int_value(param, WEED_LEAF_MIN, NULL), weed_get_int_value(param,
3355                                             WEED_LEAF_MAX, NULL));
3356           } else if (stype == WEED_SEED_DOUBLE) {
3357             range = lives_strdup_printf("Range: %f to %f",
3358                                         weed_get_double_value(param, WEED_LEAF_MIN, NULL), weed_get_double_value(param,
3359                                             WEED_LEAF_MAX, NULL));
3360           } else range = lives_strdup("");
3361         } else range = lives_strdup("");
3362 
3363         text = lives_strdup_printf("%s\n (%s%s) %s", paramname, ptype, array_type, range);
3364         lives_free(paramname);
3365         lives_free(array_type);
3366         lives_free(range);
3367       }
3368       lives_free(ptype);
3369       plist = lives_list_append(plist, text);
3370     }
3371 
3372     if (iparams) lives_free(iparams);
3373   }
3374 
3375   lives_combo_set_active_string(LIVES_COMBO(conxwp->pcombo[ours]), "");
3376   lives_signal_handler_block(conxwp->pcombo[ours], conxwp->dpp_func[ours]);
3377   lives_combo_populate(LIVES_COMBO(conxwp->pcombo[ours]), plist);
3378   lives_signal_handler_unblock(conxwp->pcombo[ours], conxwp->dpp_func[ours]);
3379 
3380   //lives_combo_set_active_string(LIVES_COMBO(conxwp->pcombo[ours]), "");
3381 
3382   if (!pidx) if (conxwp->apbutton) lives_widget_set_sensitive(conxwp->apbutton, TRUE);
3383 
3384   lives_widget_set_sensitive(conxwp->pcombo[ours], TRUE);
3385 
3386   lives_list_free_all(&plist);
3387 }
3388 
3389 
pconx_check_connection(weed_plant_t * ofilter,int opnum,int ikey,int imode,int ipnum,boolean setup,weed_plant_t ** iparam_ret,int * idx_ret,int * okey,int * omode,int * oopnum)3390 int pconx_check_connection(weed_plant_t *ofilter, int opnum, int ikey, int imode, int ipnum, boolean setup,
3391                            weed_plant_t **iparam_ret, int *idx_ret, int *okey, int *omode, int *oopnum) {
3392   weed_plant_t **oparams = NULL, **iparams;
3393   weed_plant_t *oparam, *iparam = NULL;
3394 
3395   int niparams, idx, i, j = 0;
3396 
3397   // TODO :: key FX_DATA_KEY_OMC_MACRO == OMC Macro
3398 
3399   if (opnum >= 0) {
3400     oparams = weed_get_plantptr_array(ofilter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, NULL);
3401     oparam = oparams[opnum];
3402     lives_free(oparams);
3403   } else {
3404     // invent an "ACTIVATED" param
3405     pthread_mutex_lock(&mainw->fxd_active_mutex);
3406     if (active_dummy && WEED_PLANT_IS_PARAMETER(active_dummy)) {
3407       weed_plant_free(active_dummy);
3408       active_dummy = NULL;
3409     }
3410     if (!active_dummy) {
3411       active_dummy = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
3412       weed_set_boolean_value(active_dummy, WEED_LEAF_DEFAULT, WEED_FALSE);
3413     }
3414     oparam = active_dummy;
3415     pthread_mutex_unlock(&mainw->fxd_active_mutex);
3416   }
3417 
3418   if (ipnum == FX_DATA_PARAM_ACTIVE) {
3419     // invent an "ACTIVATE" param
3420     pthread_mutex_lock(&mainw->fxd_active_mutex);
3421     if (active_dummy && WEED_PLANT_IS_PARAMETER(active_dummy)) {
3422       weed_plant_free(active_dummy);
3423       active_dummy = NULL;
3424     }
3425     if (!active_dummy) {
3426       active_dummy = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
3427       weed_set_boolean_value(active_dummy, WEED_LEAF_DEFAULT, WEED_FALSE);
3428     }
3429     idx = ipnum;
3430     iparam = active_dummy;
3431     pthread_mutex_unlock(&mainw->fxd_active_mutex);
3432   } else {
3433     // find the receiving filter/instance
3434     int fidx = rte_keymode_get_filter_idx(ikey, imode);
3435     weed_plant_t *filter = get_weed_filter(fidx);
3436 
3437     iparams = weed_get_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, NULL);
3438     niparams = weed_leaf_num_elements(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES);
3439 
3440     for (i = 0; i < niparams; i++) {
3441       iparam = iparams[i];
3442       if (weed_plant_has_leaf(iparam, WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
3443       if (weed_plant_has_leaf(iparam, WEED_LEAF_GROUP) && weed_get_int_value(iparam, WEED_LEAF_GROUP, NULL) != 0) continue;
3444       if (j == ipnum) break;
3445       j++;
3446     }
3447     idx = i;
3448     lives_free(iparams);
3449   }
3450 
3451   if (iparam_ret) *iparam_ret = iparam;
3452   if (idx_ret) *idx_ret = idx;
3453 
3454   if (!setup) {
3455     if (pconx_get_out_param(TRUE, ikey, imode, ipnum, okey, omode, oopnum, NULL)) {
3456       // dest param already has a connection
3457       return -1;
3458     }
3459   }
3460 
3461   if (!params_compatible(oparam, iparam)) {
3462     return -2;
3463   }
3464 
3465   return 0;
3466 }
3467 
3468 
dpp_changed(LiVESWidget * combo,livespointer user_data)3469 static void dpp_changed(LiVESWidget * combo, livespointer user_data) {
3470   // receiver param was set
3471 
3472   // 1) check if compatible
3473 
3474   // 2) maybe enable autoscale
3475 
3476   // 3) set text to just param name
3477   weed_plant_t *iparam;
3478 
3479   lives_conx_w *conxwp = (lives_conx_w *)user_data;
3480 
3481   LiVESWidget *acheck = NULL;
3482   LiVESWidget *fxcombo;
3483 
3484   LiVESTreeModel *model;
3485 
3486   LiVESTreeIter iter;
3487 
3488   char *paramname;
3489 
3490   boolean hasone = FALSE;
3491   boolean setup = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(combo), "setup"));
3492 
3493   int nparams, nchans;
3494   int okey, omode, opnum, ikey;
3495 
3496   int pidx, key, mode, ours = -1, ret;
3497   int idx = lives_combo_get_active_index(LIVES_COMBO(combo));
3498   int i, j;
3499 
3500   nparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3501   nchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3502 
3503   for (i = 0; i < nparams; i++) {
3504     if (conxwp->pcombo[i] == combo) {
3505       ours = i;
3506       break;
3507     }
3508   }
3509 
3510   pidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(combo), "pidx"));
3511 
3512   if (idx == -1) {
3513     // connection deleted
3514     if (setup) return;
3515     for (i = 0; i < nchans; i++) {
3516       if (lives_combo_get_active_index(LIVES_COMBO(conxwp->ccombo[i])) > -1) {
3517         hasone = TRUE;
3518         break;
3519       }
3520     }
3521     if (!hasone) {
3522       for (i = 0; i < nparams; i++) {
3523         if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[i]), "idx")) > -1) {
3524           hasone = TRUE;
3525           break;
3526         }
3527       }
3528     }
3529 
3530     if (!hasone) lives_widget_set_sensitive(conxwp->disconbutton, FALSE);
3531 
3532     ikey = conxwp->ikeys[nchans + ours];
3533 
3534     if (ikey >= 0) {
3535       //if (ikey > 0) ikey--;
3536       pconx_delete(conxwp->okey, conxwp->omode, pidx, ikey, conxwp->imodes[nchans + ours],
3537                    conxwp->idx[nchans + ours]);
3538 
3539       conxwp->pconx = pconx_find(conxwp->okey, conxwp->omode);
3540     }
3541     conxwp->ikeys[nchans + ours] = -1;
3542     conxwp->imodes[nchans + ours] = 0;
3543     conxwp->idx[nchans + ours] = 0;
3544 
3545     lives_widget_set_sensitive(conxwp->del_button[nchans + ours], FALSE);
3546     lives_widget_set_sensitive(conxwp->add_button[nchans + ours], FALSE);
3547 
3548     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(combo), "idx", LIVES_INT_TO_POINTER(idx));
3549 
3550     return;
3551   }
3552 
3553   fxcombo = conxwp->pfxcombo[ours];
3554 
3555   if (!lives_combo_get_active_iter(LIVES_COMBO(fxcombo), &iter)) return;
3556 
3557   model = lives_combo_get_model(LIVES_COMBO(fxcombo));
3558 
3559   lives_tree_model_get(model, &iter, KEYVAL_COLUMN, &key, MODEVAL_COLUMN, &mode, -1);
3560 
3561   idx -= EXTRA_PARAMS_IN;
3562 
3563   //// check if connection may be made
3564   ret = pconx_check_connection(conxwp->filter, pidx, key, mode, idx, setup, &iparam, &j, &okey, &omode, &opnum);
3565 
3566   if (ret == -2) {
3567     do_param_incompatible_error(conxwp);
3568     lives_combo_set_active_string(LIVES_COMBO(combo), "");
3569     return;
3570   }
3571   if (ret == -1) {
3572     if (!do_param_connected_query(conxwp, okey, omode, opnum, conxwp->okey == okey)) {
3573       lives_combo_set_active_string(LIVES_COMBO(combo), "");
3574       return;
3575     } else {
3576       pconx_delete(okey, omode, opnum, key, mode, idx);
3577     }
3578   }
3579 
3580   ///////////////////////////////////////////////////////////////////////////
3581 
3582   idx += EXTRA_PARAMS_IN;
3583 
3584   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(combo), "idx", LIVES_INT_TO_POINTER(idx));
3585 
3586   acheck = conxwp->acheck[ours];
3587 
3588   if (acheck) {
3589     boolean hasrange = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(acheck), "available"));
3590 
3591     lives_signal_handler_block(acheck, conxwp->acheck_func[ours]);
3592     lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(acheck), FALSE);
3593     lives_signal_handler_unblock(acheck, conxwp->acheck_func[ours]);
3594 
3595     if (hasrange) {
3596       lives_widget_set_sensitive(acheck, TRUE);
3597       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(conxwp->allcheckc))) {
3598         lives_signal_handler_block(acheck, conxwp->acheck_func[ours]);
3599         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(acheck), TRUE);
3600         lives_signal_handler_unblock(acheck, conxwp->acheck_func[ours]);
3601       }
3602     }
3603   }
3604 
3605   if (iparam == active_dummy) {
3606     if (key == FX_DATA_KEY_OMC_MACRO) paramname = (_("TRIGGER"));
3607     else paramname = (_("ACTIVATE"));
3608   } else paramname = get_param_name(iparam, idx - EXTRA_PARAMS_IN, TRUE);
3609 
3610   lives_signal_handler_block(combo, conxwp->dpp_func[ours]);
3611   lives_combo_set_active_string(LIVES_COMBO(combo), paramname);
3612   lives_signal_handler_unblock(combo, conxwp->dpp_func[ours]);
3613 
3614   lives_free(paramname);
3615 
3616   lives_widget_set_sensitive(conxwp->del_button[nchans + ours], TRUE);
3617 
3618   if (setup) return;
3619 
3620   lives_widget_set_sensitive(conxwp->add_button[nchans + ours], TRUE);
3621 
3622   conxwp->pconx = pconx_add_connection_private(conxwp->okey, conxwp->omode, pidx, key - 1, mode, j,
3623                   acheck ? lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(acheck)) : FALSE);
3624 
3625   conxwp->ikeys[nchans + ours] = key;
3626   conxwp->imodes[nchans + ours] = mode;
3627   conxwp->idx[nchans + ours] = j;
3628 
3629   lives_widget_set_sensitive(conxwp->disconbutton, TRUE);
3630 }
3631 
3632 
cconx_check_connection(int ikey,int imode,int icnum,boolean setup,weed_plant_t ** ichan_ret,int * idx_ret,int * okey,int * omode,int * ocnum)3633 int cconx_check_connection(int ikey, int imode, int icnum, boolean setup, weed_plant_t **ichan_ret, int *idx_ret,
3634                            int *okey, int *omode, int *ocnum) {
3635   weed_plant_t **ichans;
3636   weed_plant_t *filter, *ichan = NULL;
3637 
3638   int fidx, idx, nichans, i, j = 0;
3639 
3640   fidx = rte_keymode_get_filter_idx(ikey + 1, imode);
3641 
3642   // find the receiving filter/instance
3643   filter = get_weed_filter(fidx);
3644 
3645   ichans = weed_get_plantptr_array(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, NULL);
3646   nichans = weed_leaf_num_elements(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES);
3647 
3648   // find actual in channel number from list of alpha channels
3649   for (i = 0; i < nichans; i++) {
3650     ichan = ichans[i];
3651     if (!has_alpha_palette(ichan, filter)) continue;
3652     if (j == icnum) break;
3653     j++;
3654   }
3655 
3656   lives_free(ichans);
3657 
3658   idx = i;
3659 
3660   if (ichan_ret) *ichan_ret = ichan;
3661   if (idx_ret) *idx_ret = idx;
3662 
3663   if (!setup) {
3664     if (cconx_get_out_alpha(TRUE, ikey, imode, i, okey, omode, ocnum)) {
3665       // dest chan already has a connection
3666       return -1;
3667     }
3668   }
3669   return 0;
3670 }
3671 
3672 
dpc_changed(LiVESWidget * combo,livespointer user_data)3673 static void dpc_changed(LiVESWidget * combo, livespointer user_data) {
3674   lives_conx_w *conxwp = (lives_conx_w *)user_data;
3675   weed_plant_t *ichan;
3676   LiVESTreeModel *model;
3677   LiVESTreeIter iter;
3678   LiVESWidget *fxcombo;
3679 
3680   char *channame;
3681 
3682   boolean hasone = FALSE;
3683   boolean setup = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(combo), "setup"));
3684 
3685   int nchans, nparams;
3686   int key, mode, cidx, ours = -1, ret, j;
3687   int okey, omode, ocnum;
3688   int idx = lives_combo_get_active_index(LIVES_COMBO(combo));
3689   int i;
3690 
3691   nchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3692   nparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3693 
3694   for (i = 0; i < nchans; i++) {
3695     if (conxwp->ccombo[i] == combo) {
3696       ours = i;
3697       break;
3698     }
3699   }
3700 
3701   cidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(combo), "cidx"));
3702 
3703   if (idx == -1) {
3704     for (i = 0; i < nchans; i++) {
3705       if (lives_combo_get_active_index(LIVES_COMBO(conxwp->ccombo[i])) > -1) {
3706         hasone = TRUE;
3707         break;
3708       }
3709     }
3710     if (!hasone) for (i = 0; i < nparams; i++) {
3711         if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[i]), "idx")) > -1) {
3712           hasone = TRUE;
3713           break;
3714         }
3715       }
3716 
3717     if (!hasone) lives_widget_set_sensitive(conxwp->disconbutton, FALSE);
3718 
3719     if (conxwp->ikeys[ours] >= 0) cconx_delete(conxwp->okey, conxwp->omode, cidx,
3720           conxwp->ikeys[ours], conxwp->imodes[ours], conxwp->idx[ours]);
3721     conxwp->cconx = cconx_find(conxwp->okey, conxwp->omode);
3722 
3723     conxwp->ikeys[ours] = -1;
3724     conxwp->imodes[ours] = 0;
3725     conxwp->idx[ours] = 0;
3726 
3727     lives_widget_set_sensitive(conxwp->del_button[ours], FALSE);
3728 
3729     return;
3730   }
3731 
3732   fxcombo = conxwp->cfxcombo[ours];
3733 
3734   if (!lives_combo_get_active_iter(LIVES_COMBO(fxcombo), &iter)) return;
3735 
3736   model = lives_combo_get_model(LIVES_COMBO(fxcombo));
3737   lives_tree_model_get(model, &iter, KEYVAL_COLUMN, &key, MODEVAL_COLUMN, &mode, -1);
3738 
3739   // check if connection can be made
3740   ret = cconx_check_connection(key, mode, idx, setup, &ichan, &j, &okey, &omode, &ocnum);
3741 
3742   if (ret == -1) {
3743     // dest chan already has a connection
3744     if (!do_chan_connected_query(conxwp, okey, omode, ocnum, conxwp->okey == okey)) {
3745       lives_combo_set_active_string(LIVES_COMBO(combo), "");
3746       return;
3747     } else {
3748       cconx_delete(okey, omode, ocnum, key, mode, idx);
3749     }
3750   }
3751 
3752   lives_signal_handler_block(combo, conxwp->dpc_func[ours]);
3753   channame = get_chan_name(ichan, idx, TRUE);
3754   lives_combo_set_active_string(LIVES_COMBO(combo), channame);
3755   lives_signal_handler_unblock(combo, conxwp->dpc_func[ours]);
3756 
3757   lives_free(channame);
3758 
3759   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(combo), "idx", LIVES_INT_TO_POINTER(idx));
3760 
3761   lives_widget_set_sensitive(conxwp->del_button[ours], TRUE);
3762 
3763   if (setup) return;
3764 
3765   conxwp->cconx = cconx_add_connection_private(conxwp->okey, conxwp->omode, cidx, key - 1, mode, j);
3766 
3767   conxwp->ikeys[ours] = key;
3768   conxwp->imodes[ours] = mode;
3769   conxwp->idx[ours] = j;
3770 
3771   lives_widget_set_sensitive(conxwp->disconbutton, TRUE);
3772 }
3773 
3774 
on_allcheck_toggled(LiVESToggleButton * button,livespointer user_data)3775 static void on_allcheck_toggled(LiVESToggleButton * button, livespointer user_data) {
3776   lives_conx_w *conxwp = (lives_conx_w *)user_data;
3777   boolean on = lives_toggle_button_get_active(button);
3778   int nparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3779 
3780   for (int i = EXTRA_PARAMS_OUT; i < nparams; i++) {
3781     if (conxwp->acheck[i]) {
3782       if (lives_widget_is_sensitive(conxwp->acheck[i]))
3783         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(conxwp->acheck[i]), on);
3784     }
3785   }
3786 }
3787 
3788 
on_acheck_toggled(LiVESToggleButton * acheck,livespointer user_data)3789 static void on_acheck_toggled(LiVESToggleButton * acheck, livespointer user_data) {
3790   lives_conx_w *conxwp = (lives_conx_w *)user_data;
3791   weed_plant_t **iparams;
3792   weed_plant_t *param, *filter;
3793   LiVESTreeModel *model;
3794   LiVESTreeIter iter;
3795   LiVESWidget *fxcombo;
3796 
3797   boolean on = lives_toggle_button_get_active(acheck);
3798   int ours = -1, fidx, key, mode, niparams, nparams, nchans, nx = 0;
3799   int idx, pidx, i, j = 0;
3800 
3801   if (EXTRA_PARAMS_OUT > 0) nx = pconx_get_numcons(conxwp, -EXTRA_PARAMS_OUT);
3802 
3803   nparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3804   nchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3805 
3806   for (i = nx; i < nparams; i++) {
3807     if (conxwp->acheck[i] == (LiVESWidget *)acheck) {
3808       ours = i;
3809       break;
3810     }
3811   }
3812 
3813   idx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[ours]), "idx"));
3814 
3815   fxcombo = conxwp->pfxcombo[ours];
3816 
3817   model = lives_combo_get_model(LIVES_COMBO(fxcombo));
3818   lives_combo_get_active_iter(LIVES_COMBO(fxcombo), &iter);
3819 
3820   lives_tree_model_get(model, &iter, KEYVAL_COLUMN, &key, MODEVAL_COLUMN, &mode, -1);
3821 
3822   if (key < 0) return;
3823 
3824   if (key == FX_DATA_KEY_OMC_MACRO) {
3825     // match with OMC macro
3826 
3827 
3828     return;
3829   } else {
3830     if (idx >= EXTRA_PARAMS_IN) {
3831       fidx = rte_keymode_get_filter_idx(key, mode);
3832 
3833       // find the receiving filter/instance
3834       filter = get_weed_filter(fidx);
3835 
3836       iparams = weed_get_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, NULL);
3837       niparams = weed_leaf_num_elements(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES);
3838 
3839 #ifdef COMBO_LIST_LIMIT
3840       if (niparams + EXTRA_PARAMS_IN > COMBO_LIST_LIMIT) {
3841         niparams = COMBO_LIST_LIMIT - EXTRA_PARAMS_IN;
3842       }
3843 #endif
3844 
3845       for (i = 0; i < niparams; i++) {
3846         param = iparams[j];
3847         if (weed_plant_has_leaf(param, WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
3848         if (j == idx) break;
3849         j++;
3850       }
3851 
3852       j = i;
3853 
3854       lives_free(iparams);
3855     } else j = idx - EXTRA_PARAMS_IN;
3856   }
3857 
3858   pidx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(acheck), "pidx"));
3859   conxwp->pconx = pconx_add_connection_private(conxwp->okey, conxwp->omode, pidx, key - 1, mode, j, on);
3860 
3861   conxwp->ikeys[nchans + ours] = key;
3862   conxwp->imodes[nchans + ours] = mode;
3863   conxwp->idx[nchans + ours] = j;
3864 }
3865 
3866 
inparam_fx_model(boolean is_chans,int key)3867 static LiVESTreeModel *inparam_fx_model(boolean is_chans, int key) {
3868   LiVESTreeStore *tstore;
3869   LiVESTreeIter iter1, iter2;
3870   weed_plant_t *filter;
3871   char *fxname, *keystr, *text;
3872   boolean key_added;
3873   int idx, nmodes = rte_getmodespk(), i, j;
3874 
3875   tstore = lives_tree_store_new(EFD_NUM_COLUMNS, LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_INT,
3876                                 LIVES_COL_TYPE_INT);
3877 
3878   lives_tree_store_append(tstore, &iter1, NULL);   /* Acquire an iterator */
3879   lives_tree_store_set(tstore, &iter1,
3880                        KEY_COLUMN, mainw->string_constants[LIVES_STRING_CONSTANT_NONE],
3881                        NAME_COLUMN, mainw->string_constants[LIVES_STRING_CONSTANT_NONE],
3882                        KEYVAL_COLUMN, 0, MODEVAL_COLUMN, 0, -1);
3883 
3884   if (!is_chans) {
3885     // add OMC Macros
3886     keystr = (_("OMC Macros"));
3887     lives_tree_store_append(tstore, &iter1, NULL);   /* Acquire an iterator */
3888     lives_tree_store_set(tstore, &iter1, KEY_COLUMN, keystr, NAME_COLUMN, keystr, KEYVAL_COLUMN, 0, MODEVAL_COLUMN, 0, -1);
3889 
3890     for (i = 0; get_omc_macro(i); i++) {
3891       if (i != OSC_NOTIFY) continue; // TODO
3892       text = get_omc_macro(i)->macro_text;
3893       lives_tree_store_append(tstore, &iter2, &iter1);
3894       lives_tree_store_set(tstore, &iter2, KEY_COLUMN, text, NAME_COLUMN, text,
3895                            KEYVAL_COLUMN, FX_DATA_KEY_OMC_MACRO, MODEVAL_COLUMN, i, -1);
3896     }
3897 
3898     lives_free(keystr);
3899   }
3900 
3901   // go through all keys
3902   for (i = 1; i <= FX_KEYS_MAX_VIRTUAL; i++) {
3903     if (i == key + 1) continue;
3904 
3905     key_added = FALSE;
3906     keystr = lives_strdup_printf(_("Key slot %d"), i);
3907 
3908     for (j = 0; j < nmodes; j++) {
3909       if ((idx = rte_keymode_get_filter_idx(i, j)) == -1) continue;
3910 
3911       filter = get_weed_filter(idx);
3912 
3913       if (is_chans)
3914         if (num_alpha_channels(filter, FALSE) == 0) continue;
3915 
3916       fxname = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
3917       text = lives_strdup_printf("(%d,%d) %s", i, j + 1, fxname);
3918 
3919       if (!key_added) {
3920         // add key
3921         lives_tree_store_append(tstore, &iter1, NULL);   /* Acquire an iterator */
3922         lives_tree_store_set(tstore, &iter1, KEY_COLUMN, keystr, NAME_COLUMN, keystr, KEYVAL_COLUMN, 0, MODEVAL_COLUMN, 0, -1);
3923         key_added = TRUE;
3924       }
3925       lives_tree_store_append(tstore, &iter2, &iter1);
3926       lives_tree_store_set(tstore, &iter2, KEY_COLUMN, text, NAME_COLUMN, text, KEYVAL_COLUMN, i, MODEVAL_COLUMN, j, -1);
3927 
3928       lives_free(fxname);
3929       lives_free(text);
3930     }
3931 
3932     lives_free(keystr);
3933   }
3934 
3935   return (LiVESTreeModel *)tstore;
3936 }
3937 
3938 
ptable_row_add_variable_widgets(lives_conx_w * conxwp,int idx,int row,int pidx)3939 static void ptable_row_add_variable_widgets(lives_conx_w * conxwp, int idx, int row, int pidx) {
3940   weed_plant_t **oparams, *param;
3941 
3942   LiVESWidget *hbox, *hbox2;
3943   LiVESWidget *fx_entry;
3944 
3945   boolean hasrange = FALSE;
3946 
3947   int stype, totchans;
3948 
3949   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
3950 
3951   hbox = lives_hbox_new(FALSE, 0);
3952 
3953   lives_table_attach(LIVES_TABLE(conxwp->tablep), hbox, 0, 1, row, row + 1,
3954                      (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
3955                      (LiVESAttachOptions)(0), 0, 0);
3956 
3957   conxwp->pclabel[idx + totchans] = lives_standard_label_new(NULL);
3958   lives_box_pack_start(LIVES_BOX(hbox), conxwp->pclabel[idx + totchans], FALSE, FALSE, widget_opts.packing_width);
3959 
3960   hbox = lives_hbox_new(FALSE, 0);
3961 
3962   conxwp->pfxcombo[idx] = lives_standard_combo_new_with_model(pmodel, LIVES_BOX(hbox));
3963 
3964   lives_combo_set_entry_text_column(LIVES_COMBO(conxwp->pfxcombo[idx]), NAME_COLUMN);
3965 
3966   lives_table_attach(LIVES_TABLE(conxwp->tablep), hbox, 2, 3, row, row + 1,
3967                      (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
3968                      (LiVESAttachOptions)(0), 0, 0);
3969 
3970   fx_entry = lives_combo_get_entry(LIVES_COMBO(conxwp->pfxcombo[idx]));
3971   lives_entry_set_width_chars(LIVES_ENTRY(fx_entry), SHORTER_ENTRY_WIDTH);
3972   lives_entry_set_text(LIVES_ENTRY(fx_entry), mainw->string_constants[LIVES_STRING_CONSTANT_NONE]);
3973   lives_entry_set_editable(LIVES_ENTRY(fx_entry), FALSE);
3974 
3975   hbox = lives_hbox_new(FALSE, 0);
3976 
3977   hbox2 = lives_hbox_new(FALSE, 0);
3978   lives_box_pack_start(LIVES_BOX(hbox), hbox2, FALSE, FALSE, 0);
3979 
3980   conxwp->pcombo[idx] = lives_standard_combo_new("", NULL, LIVES_BOX(hbox2), NULL);
3981   lives_entry_set_width_chars(LIVES_ENTRY(lives_combo_get_entry(LIVES_COMBO(conxwp->pcombo[idx]))), SHORTER_ENTRY_WIDTH);
3982   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[idx]), "idx", LIVES_INT_TO_POINTER(-1));
3983   lives_widget_set_sensitive(conxwp->pcombo[idx], FALSE);
3984 
3985   lives_table_attach(LIVES_TABLE(conxwp->tablep), hbox, 3, 4, row, row + 1,
3986                      (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
3987                      (LiVESAttachOptions)(0), 0, 0);
3988 
3989   lives_signal_sync_connect(LIVES_GUI_OBJECT(conxwp->pfxcombo[idx]), LIVES_WIDGET_CHANGED_SIGNAL,
3990                             LIVES_GUI_CALLBACK(dfxp_changed), (livespointer)conxwp);
3991 
3992   conxwp->dpp_func[idx] = lives_signal_sync_connect(LIVES_GUI_OBJECT(conxwp->pcombo[idx]), LIVES_WIDGET_CHANGED_SIGNAL,
3993                           LIVES_GUI_CALLBACK(dpp_changed), (livespointer)conxwp);
3994 
3995   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(conxwp->pcombo[idx]), "pidx", LIVES_INT_TO_POINTER(pidx));
3996 
3997   if (pidx < 0) {
3998     conxwp->acheck[idx] = NULL;
3999   } else {
4000     if (weed_plant_has_leaf(conxwp->filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES)) {
4001       oparams = weed_get_plantptr_array(conxwp->filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, NULL);
4002 
4003       param = oparams[pidx];
4004       stype = weed_leaf_seed_type(param, WEED_LEAF_DEFAULT);
4005 
4006       if (weed_plant_has_leaf(param, WEED_LEAF_MAX) && weed_plant_has_leaf(param, WEED_LEAF_MIN) && (stype == WEED_SEED_INT ||
4007           stype == WEED_SEED_DOUBLE))
4008         hasrange = TRUE;
4009 
4010       lives_free(oparams);
4011     }
4012 
4013     hbox = lives_hbox_new(FALSE, 0);
4014 
4015     lives_table_attach(LIVES_TABLE(conxwp->tablep), hbox, 4, 5, row, row + 1,
4016                        (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
4017                        (LiVESAttachOptions)(0), 0, 0);
4018 
4019     hbox2 = lives_hbox_new(FALSE, 0);
4020     lives_box_pack_start(LIVES_BOX(hbox), hbox2, FALSE, FALSE, 0);
4021 
4022     conxwp->acheck[idx] = lives_standard_check_button_new(_("Autoscale"), FALSE, LIVES_BOX(hbox2), NULL);
4023 
4024     lives_widget_set_sensitive(conxwp->acheck[idx], FALSE);
4025     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(conxwp->acheck[idx]), "available", LIVES_INT_TO_POINTER(hasrange));
4026 
4027     conxwp->acheck_func[idx] = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(conxwp->acheck[idx]), LIVES_WIDGET_TOGGLED_SIGNAL,
4028                                LIVES_GUI_CALLBACK(on_acheck_toggled), (livespointer)conxwp);
4029 
4030     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(conxwp->acheck[idx]), "pidx", LIVES_INT_TO_POINTER(pidx));
4031   }
4032 }
4033 
4034 
ctable_row_add_variable_widgets(lives_conx_w * conxwp,int idx,int row,int cidx)4035 static void ctable_row_add_variable_widgets(lives_conx_w * conxwp, int idx, int row, int cidx) {
4036   LiVESWidget *hbox, *hbox2;
4037   LiVESWidget *fx_entry;
4038 
4039   hbox = lives_hbox_new(FALSE, 0);
4040 
4041   lives_table_attach(LIVES_TABLE(conxwp->tablec), hbox, 0, 1, row, row + 1,
4042                      (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
4043                      (LiVESAttachOptions)(0), 0, 0);
4044 
4045   conxwp->pclabel[idx] = lives_standard_label_new(NULL);
4046   lives_box_pack_start(LIVES_BOX(hbox), conxwp->pclabel[idx], FALSE, FALSE, widget_opts.packing_width);
4047 
4048   hbox = lives_hbox_new(FALSE, 0);
4049 
4050   conxwp->cfxcombo[idx] = lives_standard_combo_new_with_model(cmodel, LIVES_BOX(hbox));
4051 
4052   lives_combo_set_entry_text_column(LIVES_COMBO(conxwp->cfxcombo[idx]), NAME_COLUMN);
4053 
4054   lives_table_attach(LIVES_TABLE(conxwp->tablec), hbox, 2, 3, row, row + 1,
4055                      (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
4056                      (LiVESAttachOptions)(0), 0, 0);
4057 
4058   fx_entry = lives_combo_get_entry(LIVES_COMBO(conxwp->cfxcombo[idx]));
4059   lives_entry_set_width_chars(LIVES_ENTRY(fx_entry), SHORTER_ENTRY_WIDTH);
4060   lives_entry_set_text(LIVES_ENTRY(fx_entry), mainw->string_constants[LIVES_STRING_CONSTANT_NONE]);
4061   lives_entry_set_editable(LIVES_ENTRY(fx_entry), FALSE);
4062 
4063   hbox = lives_hbox_new(FALSE, 0);
4064 
4065   hbox2 = lives_hbox_new(FALSE, 0);
4066   lives_box_pack_start(LIVES_BOX(hbox), hbox2, FALSE, FALSE, 0);
4067 
4068   conxwp->ccombo[idx] = lives_standard_combo_new("", NULL, LIVES_BOX(hbox2), NULL);
4069   lives_entry_set_width_chars(LIVES_ENTRY(lives_combo_get_entry(LIVES_COMBO(conxwp->ccombo[idx]))), SHORTER_ENTRY_WIDTH);
4070   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(conxwp->ccombo[idx]), "idx", LIVES_INT_TO_POINTER(-1));
4071   lives_widget_set_sensitive(conxwp->ccombo[idx], FALSE);
4072 
4073   lives_table_attach(LIVES_TABLE(conxwp->tablec), hbox, 3, 4, row, row + 1,
4074                      (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
4075                      (LiVESAttachOptions)(0), 0, 0);
4076 
4077   lives_signal_sync_connect(LIVES_GUI_OBJECT(conxwp->cfxcombo[idx]), LIVES_WIDGET_CHANGED_SIGNAL,
4078                             LIVES_GUI_CALLBACK(dfxc_changed), (livespointer)conxwp);
4079 
4080   conxwp->dpc_func[idx] = lives_signal_sync_connect(LIVES_GUI_OBJECT(conxwp->ccombo[idx]), LIVES_WIDGET_CHANGED_SIGNAL,
4081                           LIVES_GUI_CALLBACK(dpc_changed), (livespointer)conxwp);
4082 
4083   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(conxwp->ccombo[idx]), "cidx", LIVES_INT_TO_POINTER(cidx));
4084 }
4085 
4086 
ptable_row_add_standard_widgets(lives_conx_w * conxwp,int idx)4087 static void ptable_row_add_standard_widgets(lives_conx_w * conxwp, int idx) {
4088   LiVESWidget *hbox;
4089 
4090   hbox = lives_hbox_new(FALSE, 0);
4091 
4092   lives_table_attach(LIVES_TABLE(conxwp->tablep), hbox, 1, 2, conxwp->trowsp - 1, conxwp->trowsp,
4093                      (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND), (LiVESAttachOptions)(0), 0, 0);
4094 
4095   conxwp->clabel[idx] = lives_standard_label_new(lctext);
4096   lives_box_pack_start(LIVES_BOX(hbox), conxwp->clabel[idx], FALSE, FALSE, widget_opts.packing_width);
4097 
4098   conxwp->add_button[idx] = lives_standard_button_new_from_stock(LIVES_STOCK_ADD, NULL, BW, BH);
4099   lives_widget_set_tooltip_text(conxwp->add_button[idx],
4100                                 _("Add another connection for this output parameter"));
4101   lives_widget_set_sensitive(conxwp->add_button[idx], FALSE);
4102 
4103   lives_table_attach(LIVES_TABLE(conxwp->tablep), conxwp->add_button[idx], 6, 7, conxwp->trowsp - 1,
4104                      conxwp->trowsp, (LiVESAttachOptions)(0), (LiVESAttachOptions)(0), 0, 0);
4105 
4106   lives_signal_sync_connect(LIVES_GUI_OBJECT(conxwp->add_button[idx]), LIVES_WIDGET_CLICKED_SIGNAL,
4107                             LIVES_GUI_CALLBACK(padd_clicked), (livespointer)conxwp);
4108 
4109   conxwp->del_button[idx] = lives_standard_button_new_from_stock(LIVES_STOCK_REMOVE, NULL, BW, BH);
4110   lives_widget_set_tooltip_text(conxwp->del_button[idx], _("Delete this connection"));
4111 
4112   hbox = lives_hbox_new(FALSE, 0);
4113 
4114   lives_box_pack_start(LIVES_BOX(hbox), conxwp->del_button[idx], FALSE, FALSE, 0);
4115 
4116   lives_table_attach(LIVES_TABLE(conxwp->tablep), hbox, 5, 6, conxwp->trowsp - 1, conxwp->trowsp,
4117                      (LiVESAttachOptions)(0), (LiVESAttachOptions)(0), 0, 0);
4118 
4119   lives_signal_sync_connect(LIVES_GUI_OBJECT(conxwp->del_button[idx]), LIVES_WIDGET_CLICKED_SIGNAL,
4120                             LIVES_GUI_CALLBACK(pdel_clicked), (livespointer)conxwp);
4121 
4122   lives_widget_set_sensitive(conxwp->del_button[idx], FALSE);
4123 }
4124 
4125 
ctable_row_add_standard_widgets(lives_conx_w * conxwp,int idx)4126 static void ctable_row_add_standard_widgets(lives_conx_w * conxwp, int idx) {
4127   LiVESWidget *hbox = lives_hbox_new(FALSE, 0);
4128 
4129   lives_table_attach(LIVES_TABLE(conxwp->tablec), hbox, 1, 2, conxwp->trowsc - 1, conxwp->trowsc,
4130                      (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND), (LiVESAttachOptions)(0), 0, 0);
4131 
4132   conxwp->clabel[idx] = lives_standard_label_new(lctext);
4133   lives_box_pack_start(LIVES_BOX(hbox), conxwp->clabel[idx], FALSE, FALSE, widget_opts.packing_width);
4134 
4135   conxwp->add_button[idx] = lives_standard_button_new_from_stock(LIVES_STOCK_ADD, NULL, BW, BH);
4136   lives_widget_set_tooltip_text(conxwp->add_button[idx], _("Add another connection for this output channel"));
4137 
4138   lives_table_attach(LIVES_TABLE(conxwp->tablec), conxwp->add_button[idx], 5, 6, conxwp->trowsc - 1, conxwp->trowsc,
4139                      (LiVESAttachOptions)(0), (LiVESAttachOptions)(0), 0, 0);
4140 
4141   lives_signal_sync_connect(LIVES_GUI_OBJECT(conxwp->add_button[idx]), LIVES_WIDGET_CLICKED_SIGNAL,
4142                             LIVES_GUI_CALLBACK(cadd_clicked), (livespointer)conxwp);
4143 
4144   conxwp->del_button[idx] = lives_standard_button_new_from_stock(LIVES_STOCK_REMOVE, NULL, BW, BH);
4145   lives_widget_set_tooltip_text(conxwp->del_button[idx], _("Delete this connection"));
4146 
4147   hbox = lives_hbox_new(FALSE, 0);
4148 
4149   lives_box_pack_start(LIVES_BOX(hbox), conxwp->del_button[idx], FALSE, FALSE, 0);
4150 
4151   lives_table_attach(LIVES_TABLE(conxwp->tablec), hbox, 4, 5, conxwp->trowsc - 1, conxwp->trowsc,
4152                      (LiVESAttachOptions)(0),
4153                      (LiVESAttachOptions)(0), 0, 0);
4154 
4155   lives_signal_sync_connect(LIVES_GUI_OBJECT(conxwp->del_button[idx]), LIVES_WIDGET_CLICKED_SIGNAL,
4156                             LIVES_GUI_CALLBACK(cdel_clicked), (livespointer)conxwp);
4157 
4158   lives_widget_set_sensitive(conxwp->del_button[idx], FALSE);
4159 }
4160 
4161 
conx_scroll_new(lives_conx_w * conxwp)4162 static LiVESWidget *conx_scroll_new(lives_conx_w * conxwp) {
4163   weed_plant_t *chan, *param;
4164 
4165   LiVESWidget *label;
4166   LiVESWidget *top_vbox;
4167   LiVESWidget *hbox;
4168   LiVESWidget *scrolledwindow;
4169 
4170   char *channame, *pname, *fname;
4171   char *ptype, *range;
4172   char *array_type, *text, *tmp;
4173 
4174   boolean isfirst;
4175 
4176   int defelems, pflags, stype;
4177 
4178   int totchans, totparams, nconns;
4179 
4180   register int i, j = 0, x = 0;
4181 
4182   for (i = 0; i < conxwp->num_alpha; i++) {
4183     nconns = cconx_get_nconns(conxwp->cconx, i);
4184     if (nconns == 0) nconns = 1;
4185     conxwp->dispc[i] = nconns;
4186   }
4187 
4188   for (i = -EXTRA_PARAMS_OUT; i < conxwp->num_params - EXTRA_PARAMS_OUT; i++) {
4189     nconns = pconx_get_nconns(conxwp->pconx, i);
4190     if (nconns == 0) nconns = 1;
4191     conxwp->dispp[i + EXTRA_PARAMS_OUT] = nconns;
4192   }
4193 
4194   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
4195   totparams = pconx_get_numcons(conxwp, FX_DATA_WILDCARD);
4196 
4197   conxwp->add_button = (LiVESWidget **)lives_malloc((totchans + totparams) * sizeof(LiVESWidget *));
4198   conxwp->del_button = (LiVESWidget **)lives_malloc((totchans + totparams) * sizeof(LiVESWidget *));
4199   conxwp->clabel = (LiVESWidget **)lives_malloc((totchans + totparams) * sizeof(LiVESWidget *));
4200   conxwp->pclabel = (LiVESWidget **)lives_malloc((totchans + totparams) * sizeof(LiVESWidget *));
4201 
4202   conxwp->cfxcombo = conxwp->ccombo = conxwp->pcombo = conxwp->pfxcombo = conxwp->acheck = NULL;
4203   conxwp->dpp_func = conxwp->dpc_func = conxwp->acheck_func = NULL;
4204 
4205   conxwp->ikeys = (int *)lives_malloc((totchans + totparams) * sizint);
4206   conxwp->imodes = (int *)lives_malloc((totchans + totparams) * sizint);
4207   conxwp->idx = (int *)lives_malloc((totchans + totparams) * sizint);
4208 
4209   for (i = 0; i < totchans + totparams; i++) {
4210     conxwp->ikeys[i] = -1;
4211     conxwp->imodes[i] = conxwp->idx[i] = 0;
4212   }
4213 
4214   lctext = (_("Connected to -->"));
4215 
4216   top_vbox = lives_vbox_new(FALSE, 0);
4217 
4218   scrolledwindow = lives_standard_scrolled_window_new(-1, -1, top_vbox);
4219 
4220   conxwp->trowsc = conxwp->trowsp = 0;
4221 
4222   fname = weed_get_string_value(conxwp->filter, WEED_LEAF_NAME, NULL);
4223 
4224   if (conxwp->num_alpha > 0) {
4225     weed_plant_t **ochans = weed_get_plantptr_array(conxwp->filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, NULL);
4226 
4227     conxwp->dpc_func = (ulong *)lives_malloc(totchans * sizeof(ulong));
4228 
4229     conxwp->cfxcombo = (LiVESWidget **)lives_malloc(totchans * sizeof(LiVESWidget *));
4230 
4231     conxwp->ccombo = (LiVESWidget **)lives_malloc(totchans * sizeof(LiVESWidget *));
4232 
4233     tmp = lives_big_and_bold("%s - Alpha Channel Connections", fname);
4234 
4235     widget_opts.use_markup = TRUE;
4236     widget_opts.justify = LIVES_JUSTIFY_CENTER;
4237     label = lives_standard_label_new(tmp);
4238     widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
4239     widget_opts.use_markup = FALSE;
4240     lives_free(tmp);
4241 
4242     lives_box_pack_start(LIVES_BOX(top_vbox), label, FALSE, FALSE, widget_opts.packing_height);
4243 
4244     conxwp->tablec = lives_table_new(0, 6, FALSE);
4245     lives_table_set_row_spacings(LIVES_TABLE(conxwp->tablec), widget_opts.packing_height);
4246     lives_table_set_col_spacings(LIVES_TABLE(conxwp->tablec), widget_opts.packing_width);
4247     lives_box_pack_start(LIVES_BOX(top_vbox), conxwp->tablec, FALSE, FALSE, widget_opts.packing_height);
4248 
4249     conxwp->trowsc = 1;
4250 
4251     cmodel = inparam_fx_model(TRUE, conxwp->okey);
4252 
4253     for (i = 0; i < conxwp->num_alpha; i++) {
4254       chan = ochans[j++];
4255 
4256       if (!has_alpha_palette(chan, conxwp->filter)) {
4257         i--;
4258         continue;
4259       }
4260 
4261       lives_table_resize(LIVES_TABLE(conxwp->tablec), ++conxwp->trowsc, 6);
4262 
4263       nconns = conxwp->dispc[i];
4264 
4265       isfirst = TRUE;
4266 
4267       do {
4268         ctable_row_add_variable_widgets(conxwp, x, conxwp->trowsc - 1, i);
4269         ctable_row_add_standard_widgets(conxwp, x);
4270 
4271         if (isfirst) {
4272           channame = get_chan_name(chan, j - 1, FALSE);
4273           widget_opts.mnemonic_label = FALSE;
4274           lives_label_set_text(LIVES_LABEL(conxwp->pclabel[x]), channame);
4275           widget_opts.mnemonic_label = TRUE;
4276           lives_free(channame);
4277           isfirst = FALSE;
4278         }
4279 
4280         x++;
4281 
4282         if (nconns > 1) {
4283           lives_table_resize(LIVES_TABLE(conxwp->tablec), ++conxwp->trowsc, 6);
4284         }
4285 
4286       } while (--nconns > 0);
4287     }
4288 
4289     lives_free(ochans);
4290   }
4291 
4292   if (conxwp->num_alpha > 0 && conxwp->num_params > 0) {
4293     add_hsep_to_box(LIVES_BOX(top_vbox));
4294   }
4295 
4296   if (conxwp->num_params > 0) {
4297     weed_plant_t **oparams = NULL;
4298 
4299     pmodel = inparam_fx_model(FALSE, conxwp->okey);
4300 
4301     if (weed_plant_has_leaf(conxwp->filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES))
4302       oparams = weed_get_plantptr_array(conxwp->filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, NULL);
4303 
4304     conxwp->pfxcombo = (LiVESWidget **)lives_malloc(totparams * sizeof(LiVESWidget *));
4305     conxwp->pcombo = (LiVESWidget **)lives_malloc(totparams * sizeof(LiVESWidget *));
4306 
4307     conxwp->dpp_func = (ulong *)lives_malloc(totparams * sizeof(ulong));
4308     conxwp->acheck_func = (ulong *)lives_malloc(totparams * sizeof(ulong));
4309 
4310     conxwp->acheck = (LiVESWidget **)lives_malloc(totparams * sizeof(LiVESWidget *));
4311 
4312     tmp = lives_big_and_bold("%s - Parameter Data Connections", fname);
4313 
4314     widget_opts.use_markup = TRUE;
4315     widget_opts.justify = LIVES_JUSTIFY_CENTER;
4316     label = lives_standard_label_new(tmp);
4317     widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
4318     widget_opts.use_markup = FALSE;
4319     lives_free(tmp);
4320 
4321     lives_box_pack_start(LIVES_BOX(top_vbox), label, FALSE, FALSE, widget_opts.packing_height);
4322 
4323     conxwp->tablep = lives_table_new(1, 7, FALSE);
4324     lives_table_set_row_spacings(LIVES_TABLE(conxwp->tablep), widget_opts.packing_height);
4325     lives_table_set_col_spacings(LIVES_TABLE(conxwp->tablep), widget_opts.packing_width);
4326     lives_box_pack_start(LIVES_BOX(top_vbox), conxwp->tablep, FALSE, FALSE, widget_opts.packing_height);
4327 
4328     conxwp->trowsp = 1;
4329     x = 0;
4330 
4331     hbox = lives_hbox_new(FALSE, 0);
4332 
4333     lives_table_attach(LIVES_TABLE(conxwp->tablep), hbox, 4, 5, 0, 1,
4334                        (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
4335                        (LiVESAttachOptions)(0), 0, 0);
4336 
4337     conxwp->allcheckc = lives_standard_check_button_new(_("Autoscale All"), TRUE, LIVES_BOX(hbox), NULL);
4338     conxwp->allcheck_label = widget_opts.last_label;
4339 
4340     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(conxwp->allcheckc), LIVES_WIDGET_TOGGLED_SIGNAL,
4341                                     LIVES_GUI_CALLBACK(on_allcheck_toggled),
4342                                     (livespointer)conxwp);
4343 
4344     if (EXTRA_PARAMS_OUT > 0) {
4345       lives_table_resize(LIVES_TABLE(conxwp->tablep), ++conxwp->trowsp, 7);
4346 
4347       nconns = conxwp->dispp[0];
4348 
4349       isfirst = TRUE;
4350 
4351       do {
4352         // TODO: for OMC, adjust the pmodel for each oparam type
4353 
4354         ptable_row_add_variable_widgets(conxwp, x, conxwp->trowsp - 1, -EXTRA_PARAMS_OUT);
4355         ptable_row_add_standard_widgets(conxwp, x + totchans);
4356 
4357         //lives_widget_set_sensitive(conxwp->add_button[x + totchans], FALSE);
4358 
4359         if (isfirst) {
4360           /* TRANSLATORS - as in "Effect ACTIVATED" */
4361           pname = (_("ACTIVATED"));
4362           ptype = weed_seed_type_to_text(WEED_SEED_BOOLEAN);
4363 
4364           text = lives_strdup_printf("%s (%s)", pname, ptype);
4365           widget_opts.mnemonic_label = FALSE;
4366           lives_label_set_text(LIVES_LABEL(conxwp->pclabel[x + totchans]), text);
4367           widget_opts.mnemonic_label = TRUE;
4368           lives_free(text);
4369           lives_free(pname);
4370           lives_free(ptype);
4371 
4372           isfirst = FALSE;
4373         }
4374 
4375         x++;
4376 
4377         if (nconns > 1) {
4378           lives_table_resize(LIVES_TABLE(conxwp->tablep), ++conxwp->trowsp, 7);
4379         }
4380       } while (--nconns > 0);
4381     }
4382 
4383     for (i = 0; i < conxwp->num_params - EXTRA_PARAMS_OUT; i++) {
4384       lives_table_resize(LIVES_TABLE(conxwp->tablep), ++conxwp->trowsp, 7);
4385 
4386       nconns = conxwp->dispp[i + EXTRA_PARAMS_OUT];
4387 
4388       isfirst = TRUE;
4389 
4390       do {
4391         ptable_row_add_variable_widgets(conxwp, x, conxwp->trowsp - 1, i);
4392         ptable_row_add_standard_widgets(conxwp, x + totchans);
4393 
4394         if (isfirst) {
4395           param = oparams[i];
4396 
4397           pname = get_param_name(param, i, FALSE);
4398 
4399           ptype = weed_seed_type_to_text((stype = weed_leaf_seed_type(param, WEED_LEAF_DEFAULT)));
4400 
4401           pflags = weed_get_int_value(param, WEED_LEAF_FLAGS, NULL);
4402 
4403           if (pflags & WEED_PARAMETER_VARIABLE_SIZE) array_type = lives_strdup("[]");
4404           else if ((defelems = weed_leaf_num_elements(param, WEED_LEAF_DEFAULT)) > 1)
4405             array_type = lives_strdup_printf("[%d]", defelems);
4406           else array_type = lives_strdup("");
4407 
4408           if (weed_plant_has_leaf(param, WEED_LEAF_MAX) && weed_plant_has_leaf(param, WEED_LEAF_MIN)) {
4409             if (stype == WEED_SEED_INT) {
4410               range = lives_strdup_printf("Range: %d to %d", weed_get_int_value(param, WEED_LEAF_MIN, NULL),
4411                                           weed_get_int_value(param,
4412                                               WEED_LEAF_MAX,
4413                                               NULL));
4414             } else if (stype == WEED_SEED_DOUBLE) {
4415               range = lives_strdup_printf("Range: %f to %f", weed_get_double_value(param, WEED_LEAF_MIN, NULL),
4416                                           weed_get_double_value(param,
4417                                               WEED_LEAF_MAX,
4418                                               NULL));
4419             } else range = lives_strdup("");
4420           } else range = lives_strdup("");
4421 
4422           text = lives_strdup_printf("%s\n (%s%s) %s", pname, ptype, array_type, range);
4423           widget_opts.mnemonic_label = FALSE;
4424           lives_label_set_text(LIVES_LABEL(conxwp->pclabel[x + totchans]), text);
4425           widget_opts.mnemonic_label = TRUE;
4426           lives_free(text);
4427           lives_free(pname);
4428           lives_free(ptype);
4429 
4430           isfirst = FALSE;
4431         }
4432 
4433         x++;
4434 
4435         if (nconns > 1) {
4436           lives_table_resize(LIVES_TABLE(conxwp->tablep), ++conxwp->trowsp, 7);
4437         }
4438       } while (--nconns > 0);
4439     }
4440 
4441     lives_free(oparams);
4442   }
4443 
4444   lives_free(fname);
4445 
4446   return scrolledwindow;
4447 }
4448 
4449 
conxw_cancel_clicked(LiVESWidget * button,livespointer user_data)4450 static void conxw_cancel_clicked(LiVESWidget * button, livespointer user_data) {
4451   lives_conx_w *conxwp = (lives_conx_w *)user_data;
4452 
4453   /// TODO - consider the benefits of using LSD
4454   if (conxwp->pclabel) lives_free(conxwp->pclabel);
4455   if (conxwp->clabel) lives_free(conxwp->clabel);
4456   if (conxwp->cfxcombo) lives_free(conxwp->cfxcombo);
4457   if (conxwp->ccombo) lives_free(conxwp->ccombo);
4458   if (conxwp->pfxcombo) lives_free(conxwp->pfxcombo);
4459   if (conxwp->pcombo) lives_free(conxwp->pcombo);
4460   if (conxwp->acheck) lives_free(conxwp->acheck);
4461   if (conxwp->add_button) lives_free(conxwp->add_button);
4462   if (conxwp->del_button) lives_free(conxwp->del_button);
4463   if (conxwp->dpp_func) lives_free(conxwp->dpp_func);
4464   if (conxwp->dpc_func) lives_free(conxwp->dpc_func);
4465   if (conxwp->acheck_func) lives_free(conxwp->acheck_func);
4466 
4467   if (conxwp->dispp) lives_free(conxwp->dispp);
4468   if (conxwp->dispc) lives_free(conxwp->dispc);
4469 
4470   lives_free(conxwp->ikeys);
4471   lives_free(conxwp->imodes);
4472   lives_free(conxwp->idx);
4473 
4474   lives_free(lctext);
4475 
4476   pconx_delete_all();
4477   cconx_delete_all();
4478 
4479   if (!button) return;
4480 
4481   // restore old values
4482   mainw->pconx = spconx;
4483   mainw->cconx = scconx;
4484 
4485   lives_general_button_clicked(LIVES_BUTTON(button), NULL);
4486 }
4487 
4488 
conxw_ok_clicked(LiVESWidget * button,livespointer user_data)4489 static void conxw_ok_clicked(LiVESWidget * button, livespointer user_data) {
4490   lives_cconnect_t *cconx_bak = mainw->cconx;
4491   lives_pconnect_t *pconx_bak = mainw->pconx;
4492 
4493   // let backup copy be freed
4494   mainw->pconx = spconx;
4495   mainw->cconx = scconx;
4496 
4497   conxw_cancel_clicked(NULL, user_data);
4498 
4499   mainw->cconx = cconx_bak;
4500   mainw->pconx = pconx_bak;
4501 
4502   lives_general_button_clicked(LIVES_BUTTON(button), NULL);
4503 }
4504 
4505 
set_to_keymode_vals(LiVESCombo * combo,int xkey,int xmode)4506 static void set_to_keymode_vals(LiVESCombo * combo, int xkey, int xmode) {
4507   LiVESTreeIter iter, piter;
4508   LiVESTreeModel *model;
4509 
4510   int key, mode;
4511 
4512   model = lives_combo_get_model(combo);
4513   if (!lives_tree_model_get_iter_first(model, &piter)) return;
4514 
4515   do {
4516     if (lives_tree_model_iter_children(model, &iter, &piter)) {
4517       do {
4518         lives_tree_model_get(model, &iter, KEYVAL_COLUMN, &key, MODEVAL_COLUMN, &mode, -1);
4519         if (key == (key > -0 ? xkey + 1 : key) && mode == xmode) goto iter_found;
4520       } while (lives_tree_model_iter_next(model, &iter));
4521     }
4522   } while (lives_tree_model_iter_next(model, &piter));
4523 
4524 iter_found:
4525   lives_combo_set_active_iter(combo, &iter);
4526 
4527 }
4528 
4529 
show_existing(lives_conx_w * conxwp)4530 static boolean show_existing(lives_conx_w * conxwp) {
4531   lives_cconnect_t *cconx = conxwp->cconx;
4532   lives_pconnect_t *pconx = conxwp->pconx;
4533 
4534   LiVESWidget *cfxcombo, *ccombo;
4535   LiVESWidget *pfxcombo, *pcombo;
4536   LiVESWidget *acheck;
4537 
4538   weed_plant_t **ochans, **ichans;
4539   weed_plant_t **iparams;
4540   weed_plant_t *ofilter = conxwp->filter, *filter;
4541   weed_plant_t *chan, *param;
4542 
4543   int ikey, imode, icnum, ipnum, nichans, niparams;
4544   int posn = 0, cidx, pidx, totchans = 0;
4545   int i, j, k, l;
4546 
4547   if (!cconx) goto show_ex_params;
4548 
4549   totchans = cconx_get_numcons(conxwp, FX_DATA_WILDCARD);
4550 
4551   ochans = weed_get_plantptr_array(ofilter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, NULL);
4552 
4553   for (i = 0; i < cconx->nchans; i++) {
4554     // find the row
4555     l = 0;
4556 
4557     // total out channel connections (display order) up to here
4558     for (k = 0; k < i; k++) {
4559       chan = ochans[j++];
4560       if (!has_alpha_palette(chan, ofilter)) {
4561         k--;
4562         continue;
4563       }
4564       l += cconx_get_numcons(conxwp, k);
4565     }
4566 
4567     cidx = cconx->chans[i];
4568     for (j = posn; j < posn + cconx->nconns[i]; j++) {
4569       ikey = cconx->ikey[j];
4570       imode = cconx->imode[j];
4571 
4572       // row is l
4573       cfxcombo = conxwp->cfxcombo[l];
4574 
4575       // set it to the value which has ikey/imode
4576       set_to_keymode_vals(LIVES_COMBO(cfxcombo), ikey, imode);
4577 
4578       // set channel
4579       ccombo = conxwp->ccombo[l];
4580       icnum = cconx->icnum[j];
4581 
4582       filter = rte_keymode_get_filter(ikey + 1, imode);
4583       ichans = weed_get_plantptr_array(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, NULL);
4584       nichans = weed_leaf_num_elements(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES);
4585 
4586       cidx = 0;
4587 
4588       // find combo list index for ichan
4589       for (k = 0; k < nichans; k++) {
4590         chan = ichans[k];
4591         if (!has_alpha_palette(chan, filter)) continue;
4592         if (k == icnum) break;
4593         cidx++;
4594       }
4595 
4596       lives_free(ichans);
4597 
4598       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ccombo), "setup", LIVES_INT_TO_POINTER(TRUE));
4599 
4600       lives_signal_handler_block(ccombo, conxwp->dpc_func[l]);
4601       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ccombo), "idx", LIVES_INT_TO_POINTER(cidx));
4602       lives_combo_set_active_index(LIVES_COMBO(ccombo), cidx);
4603       lives_signal_handler_unblock(ccombo, conxwp->dpc_func[l]);
4604 
4605       conxwp->ikeys[l] = ikey;
4606       conxwp->imodes[l] = imode;
4607       conxwp->idx[l] = icnum;
4608 
4609       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ccombo), "setup", LIVES_INT_TO_POINTER(FALSE));
4610       lives_widget_set_sensitive(conxwp->disconbutton, TRUE);
4611       lives_widget_set_sensitive(ccombo, TRUE);
4612 
4613       l++;
4614     }
4615 
4616     posn += cconx->nconns[i];
4617   }
4618 
4619   lives_free(ochans);
4620 
4621 show_ex_params:
4622 
4623   if (!pconx) goto show_ex_done;
4624 
4625   posn = 0;
4626 
4627   for (i = 0; i < pconx->nparams; i++) {
4628     pidx = pconx->params[i];
4629 
4630     // find the row
4631     l = 0;
4632 
4633     // total out param connections (display order) up to here
4634     for (k = -EXTRA_PARAMS_OUT; k < pidx; k++) {
4635       l += pconx_get_numcons(conxwp, k);
4636     }
4637 
4638     for (j = posn; j < posn + pconx->nconns[i]; j++) {
4639       ikey = pconx->ikey[j];
4640       imode = pconx->imode[j];
4641 
4642       // row is l
4643       pfxcombo = conxwp->pfxcombo[l];
4644 
4645       // set it to the value which has ikey/imode
4646       set_to_keymode_vals(LIVES_COMBO(pfxcombo), ikey, imode);
4647 
4648       // set parameter
4649       pcombo = conxwp->pcombo[l];
4650       acheck = conxwp->acheck[l];
4651 
4652       if (j == posn + pconx->nconns[i] - 1)
4653         lives_widget_set_sensitive(conxwp->add_button[l], TRUE);
4654 
4655       if (acheck) {
4656         boolean hasrange = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(acheck), "available"));
4657         if (hasrange) {
4658           lives_widget_set_sensitive(acheck, TRUE);
4659           lives_signal_handler_block(acheck, conxwp->acheck_func[l]);
4660           lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(acheck), pconx->autoscale[j]);
4661           lives_signal_handler_unblock(acheck, conxwp->acheck_func[l]);
4662         }
4663       }
4664 
4665       ipnum = pconx->ipnum[j];
4666 
4667       // TODO ****: OMC
4668 
4669       if (ipnum == FX_DATA_PARAM_ACTIVE) {
4670         pidx = FX_DATA_PARAM_ACTIVE;
4671       } else {
4672         filter = rte_keymode_get_filter(ikey + 1, imode);
4673         iparams = weed_get_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, NULL);
4674         niparams = weed_leaf_num_elements(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES);
4675 
4676 #ifdef COMBO_LIST_LIMIT
4677         if (niparams + EXTRA_PARAMS_IN > COMBO_LIST_LIMIT) {
4678           niparams = COMBO_LIST_LIMIT - EXTRA_PARAMS_IN;
4679         }
4680 #endif
4681         pidx = 0;
4682 
4683         // find combo list index for iparam
4684         for (k = 0; k < niparams; k++) {
4685           param = iparams[k];
4686           if (weed_plant_has_leaf(param, WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
4687           if (k == ipnum) break;
4688           pidx++;
4689         }
4690 
4691         lives_free(iparams);
4692       }
4693 
4694       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(pcombo), "setup", LIVES_INT_TO_POINTER(TRUE));
4695 
4696       conxwp->ikeys[totchans + l] = ikey;
4697       conxwp->imodes[totchans + l] = imode;
4698       conxwp->idx[totchans + l] = ipnum;
4699 
4700       //lives_signal_handler_block(pcombo,conxwp->dpp_func[pidx]);
4701       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(pcombo), "idx", LIVES_INT_TO_POINTER(pidx + EXTRA_PARAMS_IN));
4702       lives_combo_set_active_index(LIVES_COMBO(pcombo), pidx + EXTRA_PARAMS_IN);
4703       //lives_signal_handler_unblock(pcombo,conxwp->dpp_func[pidx]);
4704 
4705       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(pcombo), "setup", LIVES_INT_TO_POINTER(FALSE));
4706 
4707       lives_widget_set_sensitive(conxwp->disconbutton, TRUE);
4708       lives_widget_set_sensitive(pcombo, TRUE);
4709 
4710       l++;
4711     }
4712 
4713     posn += pconx->nconns[i];
4714   }
4715 
4716 show_ex_done:
4717 
4718   return FALSE;
4719 }
4720 
4721 
make_datacon_window(int key,int mode)4722 LiVESWidget *make_datacon_window(int key, int mode) {
4723   static lives_conx_w conxw;
4724 
4725   LiVESWidget *cbox, *abox;
4726   LiVESWidget *scrolledwindow;
4727 
4728   LiVESWidget *cancelbutton;
4729   LiVESWidget *okbutton;
4730 
4731   LiVESAccelGroup *accel_group;
4732 
4733   int winsize_h;
4734   int winsize_v;
4735 
4736   conxw.filter = rte_keymode_get_filter(key + 1, mode);
4737 
4738   if (!conxw.filter) return NULL;
4739 
4740   conxw.acbutton = conxw.apbutton = NULL;
4741 
4742   conxw.dispp = conxw.dispc = NULL;
4743 
4744   // save unaltered values
4745   spconx = pconx_copy(mainw->pconx);
4746   scconx = cconx_copy(mainw->cconx);
4747 
4748   conxw.cconx = cconx_find(key, mode);
4749   conxw.pconx = pconx_find(key, mode);
4750 
4751   conxw.okey = key;
4752   conxw.omode = mode;
4753 
4754   conxw.num_alpha = num_alpha_channels(conxw.filter, TRUE);
4755   conxw.num_params = weed_leaf_num_elements(conxw.filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES);
4756 
4757   conxw.num_params += EXTRA_PARAMS_OUT;
4758 
4759   if (conxw.num_params > 0)
4760     conxw.dispp = (int *)lives_malloc(conxw.num_params * sizint);
4761 
4762   conxw.ntabs = 0;
4763 
4764   winsize_h = GUI_SCREEN_WIDTH - SCR_WIDTH_SAFETY * 2;
4765   winsize_v = GUI_SCREEN_HEIGHT - SCR_HEIGHT_SAFETY;
4766 
4767   conxw.conx_dialog = lives_standard_dialog_new(_("Parameter and Alpha Channel Connections"), FALSE, winsize_h, winsize_v);
4768 
4769   accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
4770   lives_window_add_accel_group(LIVES_WINDOW(conxw.conx_dialog), accel_group);
4771 
4772   if (conxw.num_alpha > 0) {
4773     conxw.dispc = (int *)lives_malloc(conxw.num_alpha * sizint);
4774 
4775     conxw.acbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(conxw.conx_dialog), NULL, _("Auto Connect Channels"),
4776                      LIVES_RESPONSE_NONE);
4777     lives_widget_set_sensitive(conxw.acbutton, FALSE);
4778     lives_signal_sync_connect(LIVES_GUI_OBJECT(conxw.acbutton), LIVES_WIDGET_CLICKED_SIGNAL,
4779                               LIVES_GUI_CALLBACK(acbutton_clicked), (livespointer)&conxw);
4780   }
4781 
4782   if (conxw.num_params > EXTRA_PARAMS_OUT) {
4783     conxw.apbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(conxw.conx_dialog), NULL, _("Auto Connect Parameters"),
4784                      LIVES_RESPONSE_NONE);
4785     lives_widget_set_sensitive(conxw.apbutton, FALSE);
4786     lives_signal_sync_connect(LIVES_GUI_OBJECT(conxw.apbutton), LIVES_WIDGET_CLICKED_SIGNAL,
4787                               LIVES_GUI_CALLBACK(apbutton_clicked), (livespointer)&conxw);
4788   }
4789 
4790   conxw.disconbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(conxw.conx_dialog), NULL, _("_Disconnect All"),
4791                        LIVES_RESPONSE_NONE);
4792   //lives_container_set_border_width(LIVES_CONTAINER(conxw.disconbutton), widget_opts.border_width);
4793   //!! dont - causes other buttons to exp. vert in gtk2
4794   lives_widget_set_sensitive(conxw.disconbutton, FALSE);
4795 
4796   lives_signal_sync_connect(LIVES_GUI_OBJECT(conxw.disconbutton), LIVES_WIDGET_CLICKED_SIGNAL,
4797                             LIVES_GUI_CALLBACK(disconbutton_clicked), (livespointer)&conxw);
4798 
4799   abox = lives_dialog_get_action_area(LIVES_DIALOG(conxw.conx_dialog));
4800   if (LIVES_IS_BOX(abox) && (conxw.num_alpha > 0 || conxw.num_params > 0)) add_fill_to_box(LIVES_BOX(abox));
4801 
4802   cbox = lives_dialog_get_content_area(LIVES_DIALOG(conxw.conx_dialog));
4803 
4804   scrolledwindow = conx_scroll_new(&conxw);
4805   show_existing(&conxw);
4806 
4807   lives_box_pack_start(LIVES_BOX(cbox), scrolledwindow, TRUE, TRUE, 0);
4808 
4809   if (conxw.num_params > EXTRA_PARAMS_OUT) {
4810     if (conxw.apbutton && pconx_get_nconns(conxw.pconx, 0) > 0)
4811       lives_widget_set_sensitive(conxw.apbutton, TRUE);
4812   }
4813   if (conxw.num_alpha > 0) {
4814     if (conxw.acbutton && cconx_get_nconns(conxw.cconx, 0) > 0)
4815       lives_widget_set_sensitive(conxw.acbutton, TRUE);
4816   }
4817 
4818   cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(conxw.conx_dialog), LIVES_STOCK_CANCEL, NULL,
4819                  LIVES_RESPONSE_CANCEL);
4820 
4821   okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(conxw.conx_dialog), LIVES_STOCK_OK, NULL,
4822              LIVES_RESPONSE_OK);
4823 
4824   lives_button_grab_default_special(okbutton);
4825 
4826   lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
4827                                LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
4828 
4829   lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
4830                             LIVES_GUI_CALLBACK(conxw_cancel_clicked),
4831                             (livespointer)&conxw);
4832 
4833   lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
4834                             LIVES_GUI_CALLBACK(conxw_ok_clicked),
4835                             (livespointer)&conxw);
4836 
4837   lives_widget_show_all(conxw.conx_dialog);
4838 
4839   if (conxw.num_params == EXTRA_PARAMS_OUT && EXTRA_PARAMS_OUT > 0) {
4840     lives_widget_set_no_show_all(conxw.allcheckc, TRUE);
4841     lives_widget_hide(conxw.allcheckc);
4842     lives_widget_set_no_show_all(conxw.allcheck_label, TRUE);
4843     lives_widget_hide(conxw.allcheck_label);
4844   }
4845 
4846   return conxw.conx_dialog;
4847 }
4848 
4849 
do_chan_connected_query(lives_conx_w * conxwp,int key,int mode,int cnum,boolean is_same)4850 static boolean do_chan_connected_query(lives_conx_w * conxwp, int key, int mode, int cnum, boolean is_same) {
4851   weed_plant_t *filter = rte_keymode_get_filter(key + 1, mode);
4852   weed_plant_t *ctmpl, **ochans;
4853   char *cname, *msg, *fname = weed_filter_get_name(filter);
4854   boolean resp = FALSE;
4855   ochans = weed_get_plantptr_array(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, NULL);
4856   ctmpl = ochans[cnum];
4857   lives_free(ochans);
4858   cname = get_chan_name(ctmpl, cnum, TRUE);
4859   widget_opts.transient = LIVES_WINDOW(conxwp->conx_dialog);
4860   msg = lives_strdup_printf(_("Input channel is already connected from key %d, mode %d\n\n(%s:%s)\n\n"),
4861                             key + 1, mode, fname, cname);
4862   if (!is_same)
4863     resp = do_yesno_dialogf(_("%sWould you like to replace the existing connection ?\n\n"), msg);
4864 
4865   else
4866     do_error_dialogf(_("%sYou need to remove this connection first before adding another.\n\n"), msg);
4867   widget_opts.transient = NULL;
4868   lives_free(msg); lives_free(cname); lives_free(fname);
4869   return resp;
4870 }
4871 
4872 
do_param_connected_query(lives_conx_w * conxwp,int key,int mode,int pnum,boolean is_same)4873 static boolean do_param_connected_query(lives_conx_w * conxwp, int key, int mode, int pnum, boolean is_same) {
4874   weed_plant_t *filter = rte_keymode_get_filter(key + 1, mode);
4875   char *pname, *msg, *fname = weed_filter_get_name(filter);
4876   boolean resp = FALSE;
4877   if (pnum >= 0) {
4878     weed_plant_t *ptmpl = weed_filter_out_paramtmpl(filter, pnum);
4879     pname = get_param_name(ptmpl, pnum, TRUE);
4880   } else pname = (_("ACTIVATED"));
4881   widget_opts.transient = LIVES_WINDOW(conxwp->conx_dialog);
4882   msg = lives_strdup_printf(_("Input parameter is already connected from key %d, mode %d\n\n(%s:%s)\n\n"),
4883                             key + 1, mode, fname, pname);
4884   if (!is_same)
4885     resp = do_yesno_dialogf(_("%sWould you like to replace the existing connection ?\n\n"), msg);
4886 
4887   else
4888     do_error_dialogf(_("%sYou need to remove this connection first before adding another.\n\n"), msg);
4889   widget_opts.transient = NULL;
4890   lives_free(msg); lives_free(pname); lives_free(fname);
4891   return resp;
4892 }
4893 
4894 
do_param_incompatible_error(lives_conx_w * conxwp)4895 static void do_param_incompatible_error(lives_conx_w * conxwp) {
4896   widget_opts.transient = LIVES_WINDOW(conxwp->conx_dialog);
4897   do_error_dialog(_("Input and output parameters are not compatible"));
4898   widget_opts.transient = NULL;
4899 }
4900 
4901