1 // multitrack.c
2 // LiVES
3 // (c) G. Finch 2005 - 2020 <salsaman+lives@gmail.com>
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING for licensing details
6 
7 // multitrack window
8 
9 // layout is:
10 
11 // play | clips/params | ctx menu
12 // ------------------------------
13 //            timeline
14 // ------------------------------
15 //            messages
16 
17 // the multitrack window is designed to be more-or-less standalone
18 // it relies on functions in other files for applying effects and rendering
19 // we use a Weed event list to store our timeline
20 // (see weed events extension in weed-docs directory)
21 
22 // future plans include timeline plugins, which would generate event lists
23 // or adjust the currently playing one
24 // and it would be nice to be able to read/write event lists in other formats than the default
25 
26 
27 // mainw->playarea is reparented:
28 // (mt->hbox -> preview_frame -> preview_eventbox -> (playarea -> plug -> play_image)
29 // for gtk+ 3.x the frame_pixbuf is drawn into play_image in expose_pb when not playing
30 
31 // MAP:
32 
33 // top_vbox
34 //         - menu_hbox
35 //                    - menubar
36 //                            - menuitems
37 //         - xtravbox
38 //                    - top_eventbox
39 //                            - hbox
40 //                                 - btoolbar2
41 //                    - hseparator
42 //                    - mt_hbox
43 //                             - preview_frame
44 //                                     - preview_eventbox
45 //                                              - mainw->playarea
46 //                                                      - plug
47 //                                                             - play_image
48 ////                             - hpaned
49 //                                    - mt->nb (notebook)
50 //                                    - context_box
51 //                    - hseparator
52 //                    - sepimage
53 //                    - hbox
54 //                            - amixb_eventbox
55 //                                            - btoolbar
56 //                                                    - amixer_button
57 //
58 //         - hseparator2
59 //         - vpaned
60 //                    - mt->tlx_vbox
61 //                    - message_box
62 
63 //#define DEBUG_TTABLE
64 
65 #include "main.h"
66 #include "events.h"
67 #include "callbacks.h"
68 #include "effects.h"
69 #include "resample.h"
70 #include "paramwindow.h"
71 #include "interface.h"
72 #include "audio.h"
73 #include "startup.h"
74 #include "framedraw.h"
75 #include "cvirtual.h"
76 #include "pangotext.h"
77 #include "rte_window.h"
78 
79 #ifdef ENABLE_GIW
80 #include "giw/giwvslider.h"
81 #include "giw/giwled.h"
82 #endif
83 
84 #ifdef ENABLE_GIW_3
85 #include "giw/giwtimeline.h"
86 #endif
87 
88 #ifdef HAVE_LDVGRAB
89 #include "ldvgrab.h"
90 #endif
91 
92 #ifndef WEED_AUDIO_LITTLE_ENDIAN
93 #define WEED_AUDIO_LITTLE_ENDIAN 0
94 #define WEED_AUDIO_BIG_ENDIAN 1
95 #endif
96 
97 /// TODO: use uint64_t
98 static uint32_t event_list_get_byte_size(lives_mt *mt, weed_plant_t *event_list, boolean nxprev, int *num_events);
99 
100 static EXPOSE_FN_PROTOTYPE(expose_timeline_reg_event)
101 static EXPOSE_FN_PROTOTYPE(mt_expose_audtrack_event)
102 
103 static boolean mt_add_block_effect_idle(livespointer mt);
104 static boolean mt_add_region_effect_idle(livespointer mt);
105 static boolean mt_fx_edit_idle(livespointer mt);
106 
107 static void paint_lines(lives_mt *mt, double currtime, boolean unpaint, lives_painter_t *cr);
108 
109 static int *update_layout_map(weed_plant_t *event_list);
110 static double *update_layout_map_audio(weed_plant_t *event_list);
111 
112 static boolean check_can_resetp(lives_mt *mt);
113 
114 /// used to match clips from the event recorder with renumbered clips (without gaps)
115 static int renumbered_clips[MAX_FILES + 1];
116 
117 static double lfps[MAX_FILES + 1]; ///< table of layout fps
118 static void **pchain; ///< param chain for currently being edited filter
119 
120 static int xachans, xarate, xasamps, xse;
121 static boolean ptaud;
122 static int btaud;
123 
124 static int aofile;
125 static int afd;
126 
127 static int dclick_time = 0;
128 
129 static boolean force_pertrack_audio;
130 static int force_backing_tracks;
131 
132 static int clips_to_files[MAX_FILES];
133 
134 static boolean pb_audio_needs_prerender;
135 static weed_plant_t *pb_loop_event, *pb_filter_map, *pb_afilter_map;
136 
137 static boolean nb_ignore = FALSE;
138 
139 static LiVESWidget *dummy_menuitem;
140 
141 static boolean doubleclick = FALSE;
142 
143 static uint32_t last_press_time = 0;
144 static int last_x = 0;
145 static int last_y = 0;
146 
147 static boolean needs_clear;
148 
149 static LiVESList *pkg_list;
150 
151 static LiVESWidget *mainw_message_box;
152 static LiVESWidget *mainw_msg_area;
153 static LiVESWidget *mainw_msg_scrollbar;
154 static LiVESAdjustment *mainw_msg_adj;
155 //static ulong mainw_sw_func;
156 
157 ////////////////////////////
158 
159 // menuitem callbacks
160 static void multitrack_adj_start_end(LiVESMenuItem *, livespointer mt);
161 boolean multitrack_audio_insert(LiVESMenuItem *, livespointer mt);
162 static void multitrack_view_events(LiVESMenuItem *, livespointer mt);
163 static void multitrack_view_sel_events(LiVESMenuItem *, livespointer mt);
164 static void on_prerender_aud_activate(LiVESMenuItem *, livespointer mt);
165 static void on_jumpnext_activate(LiVESMenuItem *, livespointer mt);
166 static void on_jumpback_activate(LiVESMenuItem *, livespointer mt);
167 static void on_jumpnext_mark_activate(LiVESMenuItem *, livespointer mt);
168 static void on_jumpback_mark_activate(LiVESMenuItem *, livespointer mt);
169 static void on_delblock_activate(LiVESMenuItem *, livespointer mt);
170 static void on_seltrack_activate(LiVESMenuItem *, livespointer mt);
171 static void multitrack_view_details(LiVESMenuItem *, livespointer mt);
172 static void mt_add_region_effect(LiVESMenuItem *, livespointer mt);
173 static void mt_add_block_effect(LiVESMenuItem *, livespointer mt);
174 static void on_clear_event_list_activate(LiVESMenuItem *, livespointer mt);
175 static void show_frame_events_activate(LiVESMenuItem *, livespointer);
176 static void mt_load_vals_toggled(LiVESMenuItem *, livespointer mt);
177 static void mt_load_vals_toggled(LiVESMenuItem *, livespointer mt);
178 static void mt_render_vid_toggled(LiVESMenuItem *, livespointer mt);
179 static void mt_render_aud_toggled(LiVESMenuItem *, livespointer mt);
180 static void mt_norm_aud_toggled(LiVESMenuItem *, livespointer mt);
181 static void mt_fplay_toggled(LiVESMenuItem *, livespointer mt);
182 static void mt_change_vals_activate(LiVESMenuItem *, livespointer mt);
183 static void on_set_pvals_clicked(LiVESWidget *button, livespointer mt);
184 static void on_move_fx_changed(LiVESMenuItem *, livespointer mt);
185 static void select_all_time(LiVESMenuItem *, livespointer mt);
186 static void select_from_zero_time(LiVESMenuItem *, livespointer mt);
187 static void select_to_end_time(LiVESMenuItem *, livespointer mt);
188 static void select_all_vid(LiVESMenuItem *, livespointer mt);
189 static void select_no_vid(LiVESMenuItem *, livespointer mt);
190 static void on_split_sel_activate(LiVESMenuItem *, livespointer mt);
191 static void on_split_curr_activate(LiVESMenuItem *, livespointer mt);
192 static void multitrack_undo(LiVESMenuItem *, livespointer mt);
193 static void multitrack_redo(LiVESMenuItem *, livespointer mt);
194 static void on_mt_showkeys_activate(LiVESMenuItem *, livespointer);
195 static void on_mt_list_fx_activate(LiVESMenuItem *, livespointer mt);
196 static void on_mt_delfx_activate(LiVESMenuItem *, livespointer mt);
197 static void on_mt_fx_edit_activate(LiVESMenuItem *, livespointer mt);
198 static void mt_view_audio_toggled(LiVESMenuItem *, livespointer mt);
199 static void mt_ign_ins_sel_toggled(LiVESMenuItem *, livespointer mt);
200 static void mt_change_max_disp_tracks(LiVESMenuItem *, livespointer mt);
201 
202 static void mt_ac_audio_toggled(LiVESMenuItem *, livespointer mt);
203 
204 ///////////////////////////////////////////////////////////////////
205 
206 #define LIVES_AVOL_SCALE ((double)1000000.)
207 
maybe_add_mt_idlefunc(void)208 void maybe_add_mt_idlefunc(void) {
209   if (mainw->multitrack && mainw->mt_needs_idlefunc) {
210     if (!mainw->is_exiting)  mainw->multitrack->idlefunc = mt_idle_add(mainw->multitrack);
211     mainw->mt_needs_idlefunc = FALSE;
212   }
213 }
214 
215 
mt_file_from_clip(lives_mt * mt,int clip)216 LIVES_INLINE int mt_file_from_clip(lives_mt *mt, int clip) {
217   return clips_to_files[clip];
218 }
219 
220 
mt_clip_from_file(lives_mt * mt,int file)221 LIVES_INLINE int mt_clip_from_file(lives_mt *mt, int file) {
222   register int i;
223   for (i = 0; i < MAX_FILES; i++) {
224     if (clips_to_files[i] == file) return i;
225   }
226   return -1;
227 }
228 
229 
230 /// return track number for a given block
get_track_for_block(track_rect * block)231 int get_track_for_block(track_rect *block) {
232   return LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
233 }
234 
235 
is_empty_track(LiVESWidgetObject * track)236 LIVES_INLINE boolean is_empty_track(LiVESWidgetObject *track) {
237   return (lives_widget_object_get_data(track, "blocks") == NULL);
238 }
239 
get_mixer_track_vol(lives_mt * mt,int trackno)240 double get_mixer_track_vol(lives_mt *mt, int trackno) {
241   double vol = (double)(LIVES_POINTER_TO_INT(lives_list_nth_data(mt->audio_vols, trackno)));
242   return vol / LIVES_AVOL_SCALE;
243 }
244 
245 
set_mixer_track_vol(lives_mt * mt,int trackno,double vol)246 void set_mixer_track_vol(lives_mt *mt, int trackno, double vol) {
247   int x = vol * LIVES_AVOL_SCALE;
248   lives_list_nth(mt->audio_vols, trackno)->data = LIVES_INT_TO_POINTER(x);
249 }
250 
251 
save_event_list_inner(lives_mt * mt,int fd,weed_plant_t * event_list,unsigned char ** mem)252 boolean save_event_list_inner(lives_mt *mt, int fd, weed_plant_t *event_list, unsigned char **mem) {
253   weed_plant_t *event;
254 
255   void **ievs = NULL;
256   void *next, *prev;
257 
258   int64_t *uievs;
259   int64_t iev = 0l;
260 
261   int count = 0;
262   int nivs = 0;
263 
264   int i;
265 
266   if (!event_list) return TRUE;
267 
268   event_list = lives_event_list_new(event_list, NULL);
269   event = get_first_event(event_list);
270 
271   threaded_dialog_spin(0.);
272 
273   if (mt) {
274     weed_set_int_value(event_list, WEED_LEAF_WIDTH, mainw->files[mt->render_file]->hsize);
275     weed_set_int_value(event_list, WEED_LEAF_HEIGHT, mainw->files[mt->render_file]->vsize);
276     weed_set_int_value(event_list, WEED_LEAF_AUDIO_CHANNELS, mainw->files[mt->render_file]->achans);
277     weed_set_int_value(event_list, WEED_LEAF_AUDIO_RATE, mainw->files[mt->render_file]->arate);
278     weed_set_int_value(event_list, WEED_LEAF_AUDIO_SAMPLE_SIZE, mainw->files[mt->render_file]->asampsize);
279 
280     if (mainw->files[mt->render_file]->signed_endian & AFORM_UNSIGNED)
281       weed_set_boolean_value(event_list, WEED_LEAF_AUDIO_SIGNED, WEED_FALSE);
282     else weed_set_boolean_value(event_list, WEED_LEAF_AUDIO_SIGNED, WEED_TRUE);
283 
284     if (mainw->files[mt->render_file]->signed_endian & AFORM_BIG_ENDIAN)
285       weed_set_int_value(event_list, WEED_LEAF_AUDIO_ENDIAN, WEED_AUDIO_BIG_ENDIAN);
286     else weed_set_int_value(event_list, WEED_LEAF_AUDIO_ENDIAN, WEED_AUDIO_LITTLE_ENDIAN);
287 
288     if (prefs->letterbox_mt) weed_set_boolean_value(event_list, WEED_LEAF_KEEP_ASPECT, WEED_TRUE);
289     else weed_set_boolean_value(event_list, WEED_LEAF_KEEP_ASPECT, WEED_FALSE);
290   }
291   if (mt && mt->audio_vols && mt->audio_draws) {
292     int natracks = lives_list_length(mt->audio_draws);
293 
294     int *atracks = (int *)lives_malloc(natracks * sizint);
295     double *avols;
296 
297     int navols;
298 
299     for (i = 0; i < natracks; i++) {
300       atracks[i] = i - mt->opts.back_audio_tracks;
301     }
302     weed_set_int_array(event_list, WEED_LEAF_AUDIO_VOLUME_TRACKS, natracks, atracks);
303     lives_free(atracks);
304 
305     if (mt->opts.gang_audio) navols = 1 + mt->opts.back_audio_tracks;
306     else navols = natracks;
307 
308     avols = (double *)lives_malloc(navols * sizeof(double));
309     for (i = 0; i < navols; i++) {
310       avols[i] = get_mixer_track_vol(mt, i);
311     }
312     weed_set_double_array(event_list, WEED_LEAF_AUDIO_VOLUME_VALUES, navols, avols);
313     lives_free(avols);
314   }
315 
316   if (mt) {
317     int nvtracks = lives_list_length(mt->video_draws);
318 
319     int *vtracks = (int *)lives_malloc(nvtracks * sizint);
320     char **const labels = (char **)lives_malloc(nvtracks * sizeof(char *));
321 
322     for (i = 0; i < nvtracks; i++) {
323       LiVESWidget *ebox = get_eventbox_for_track(mt, i);
324       const char *tname = (const char *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(ebox), "track_name");
325       labels[i] = (char *)tname;
326       vtracks[i] = i;
327     }
328 
329     weed_set_int_array(event_list, WEED_LEAF_TRACK_LABEL_TRACKS, nvtracks, vtracks);
330     lives_free(vtracks);
331 
332     weed_set_string_array(event_list, WEED_LEAF_TRACK_LABEL_VALUES, nvtracks, labels);
333 
334     lives_free(labels);
335   }
336 
337   if (!mem && fd < 0) return TRUE;
338 
339   threaded_dialog_spin(0.);
340 
341   THREADVAR(write_failed) = FALSE;
342   weed_plant_serialise(fd, event_list, mem);
343 
344   while (!THREADVAR(write_failed) && event) {
345     next = weed_get_voidptr_value(event, WEED_LEAF_NEXT, NULL);
346     weed_leaf_delete(event, WEED_LEAF_NEXT);
347 
348     prev = weed_get_voidptr_value(event, WEED_LEAF_PREVIOUS, NULL);
349     weed_leaf_delete(event, WEED_LEAF_PREVIOUS);
350 
351     if (WEED_EVENT_IS_FILTER_INIT(event)) {
352       weed_leaf_delete(event, WEED_LEAF_EVENT_ID);
353       weed_set_int64_value(event, WEED_LEAF_EVENT_ID, (int64_t)(uint64_t)((void *)event));
354     } else if (WEED_EVENT_IS_FILTER_DEINIT(event) || WEED_EVENT_IS_PARAM_CHANGE(event)) {
355       iev = (int64_t)(uint64_t)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
356       weed_leaf_delete(event, WEED_LEAF_INIT_EVENT);
357       weed_set_int64_value(event, WEED_LEAF_INIT_EVENT, iev);
358     } else if (WEED_EVENT_IS_FILTER_MAP(event)) {
359       ievs = weed_get_voidptr_array_counted(event, WEED_LEAF_INIT_EVENTS, &nivs);
360       uievs = (int64_t *)lives_malloc(nivs * 8);
361       for (i = 0; i < nivs; i++) {
362         uievs[i] = (int64_t)(uint64_t)ievs[i];
363       }
364       weed_leaf_delete(event, WEED_LEAF_INIT_EVENTS);
365       weed_set_int64_array(event, WEED_LEAF_INIT_EVENTS, nivs, uievs);
366       lives_free(uievs);
367     }
368 
369     if (!mem && prefs->back_compat) {
370       // create a "hint" leaf as a service to older versions of LiVES
371       // TODO: prompt user if they need backwards compat or not
372       weed_leaf_copy(event, WEED_LEAF_HINT, event, WEED_LEAF_EVENT_TYPE);
373     }
374 
375     weed_plant_serialise(fd, event, mem);
376 
377     weed_leaf_delete(event, WEED_LEAF_HINT);
378 
379     if (WEED_EVENT_IS_FILTER_INIT(event)) {
380       weed_leaf_delete(event, WEED_LEAF_EVENT_ID);
381     }
382     if (WEED_EVENT_IS_FILTER_DEINIT(event) || WEED_EVENT_IS_PARAM_CHANGE(event)) {
383       weed_leaf_delete(event, WEED_LEAF_INIT_EVENT);
384       weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENT, (void *)iev);
385     } else if (WEED_EVENT_IS_FILTER_MAP(event)) {
386       weed_leaf_delete(event, WEED_LEAF_INIT_EVENTS);
387       weed_set_voidptr_array(event, WEED_LEAF_INIT_EVENTS, nivs, ievs);
388       lives_free(ievs);
389     }
390 
391     weed_set_voidptr_value(event, WEED_LEAF_NEXT, next);
392     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, prev);
393 
394     event = get_next_event(event);
395     if (++count == 100) {
396       count = 0;
397       threaded_dialog_spin(0.);
398     }
399   }
400   if (THREADVAR(write_failed)) return FALSE;
401   return TRUE;
402 }
403 
404 
make_thumb(lives_mt * mt,int file,int width,int height,frames_t frame,LiVESInterpType interp,boolean noblanks)405 LiVESPixbuf *make_thumb(lives_mt *mt, int file, int width, int height, frames_t frame, LiVESInterpType interp,
406                         boolean noblanks) {
407   LiVESPixbuf *thumbnail = NULL, *pixbuf;
408   LiVESError *error = NULL;
409 
410   lives_clip_t *sfile = mainw->files[file];
411 
412   boolean tried_all = FALSE;
413   boolean needs_idlefunc = FALSE;
414 
415   boolean did_backup = FALSE;
416 
417   int nframe, oframe = frame;
418 
419   if (file < 1) {
420     LIVES_WARN("Warning - make thumb for file -1");
421     return NULL;
422   }
423 
424   if (width < 4 || height < 4) return NULL;
425 
426   if (mt) did_backup = mt->did_backup;
427 
428   if (mt && mt->idlefunc > 0) {
429     needs_idlefunc = TRUE;
430     lives_source_remove(mt->idlefunc);
431     mt->idlefunc = 0;
432   }
433 
434   do {
435     if (sfile->frames > 0) {
436       weed_timecode_t tc = (frame - 1.) / sfile->fps * TICKS_PER_SECOND;
437       if (sfile->frames > 0 && sfile->clip_type == CLIP_TYPE_FILE) {
438         lives_clip_data_t *cdata = ((lives_decoder_t *)sfile->ext_src)->cdata;
439         if (cdata && !(cdata->seek_flag & LIVES_SEEK_FAST) &&
440             is_virtual_frame(file, frame)) {
441           virtual_to_images(file, frame, frame, FALSE, NULL);
442         }
443       }
444       thumbnail = pull_lives_pixbuf_at_size(file, frame, get_image_ext_for_type(sfile->img_type), tc,
445                                             width, height, LIVES_INTERP_FAST, TRUE);
446     } else {
447       pixbuf = lives_pixbuf_new_from_stock_at_size(LIVES_LIVES_STOCK_AUDIO, LIVES_ICON_SIZE_CUSTOM, width, height);
448       if (error || !pixbuf) {
449         lives_error_free(error);
450         if (mt && (needs_idlefunc || (!did_backup && mt->auto_changed))) {
451           mt->idlefunc = mt_idle_add(mt);
452         }
453         return NULL;
454       }
455 
456       if (lives_pixbuf_get_width(pixbuf) != width || lives_pixbuf_get_height(pixbuf) != height) {
457         // ...at_scale is inaccurate
458         thumbnail = lives_pixbuf_scale_simple(pixbuf, width, height, LIVES_INTERP_FAST);
459         lives_widget_object_unref(pixbuf);
460       } else thumbnail = pixbuf;
461     }
462 
463     if (tried_all) noblanks = FALSE;
464 
465     if (noblanks && thumbnail && !lives_pixbuf_is_all_black(thumbnail)) noblanks = FALSE;
466     if (noblanks) {
467       nframe = frame + sfile->frames / 10.;
468       if (nframe == frame) nframe++;
469       if (nframe > sfile->frames) {
470         nframe = oframe;
471         tried_all = TRUE;
472       }
473       frame = nframe;
474       if (thumbnail) lives_widget_object_unref(thumbnail);
475       thumbnail = NULL;
476     }
477   } while (noblanks);
478 
479   if (mt) {
480     if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
481       mt->idlefunc = mt_idle_add(mt);
482     }
483   }
484 
485   return thumbnail;
486 }
487 
488 
make_thumb_fast_between(lives_mt * mt,int fileno,int width,int height,int tframe,int range)489 LiVESPixbuf *make_thumb_fast_between(lives_mt *mt, int fileno, int width, int height, int tframe, int range) {
490   int nvframe = -1;
491   register int i;
492 
493   if (fileno < 1) {
494     LIVES_WARN("Warning - make thumb for file -1");
495     return NULL;
496   }
497 
498   if (width < 2 || height < 2) return NULL;
499 
500   for (i = 1; i <= range; i++) {
501     if (tframe - i > 0 && !is_virtual_frame(fileno, tframe - i)) {
502       nvframe = tframe - i;
503       break;
504     }
505     if (tframe + i <= mainw->files[fileno]->frames && !is_virtual_frame(fileno, tframe + i)) {
506       nvframe = tframe + i;
507       break;
508     }
509   }
510 
511   if (nvframe != -1) {
512     return make_thumb(mt, fileno, width, height, nvframe, LIVES_INTERP_FAST, FALSE);
513   }
514 
515   return NULL;
516 }
517 
reset_mt_play_sizes(lives_mt * mt)518 LIVES_LOCAL_INLINE void reset_mt_play_sizes(lives_mt *mt) {
519   lives_widget_set_size_request(mt->preview_eventbox, GUI_SCREEN_WIDTH / PEB_WRATIO,
520                                 GUI_SCREEN_HEIGHT / PEB_HRATIO);
521   lives_widget_set_size_request(mainw->play_image, GUI_SCREEN_WIDTH / PEB_WRATIO,
522                                 GUI_SCREEN_HEIGHT / PEB_HRATIO);
523 }
524 
525 
mt_set_cursor_style(lives_mt * mt,lives_cursor_t cstyle,int width,int height,int clip,int hsx,int hsy)526 static void mt_set_cursor_style(lives_mt *mt, lives_cursor_t cstyle, int width, int height, int clip, int hsx, int hsy) {
527   LiVESXCursor *cursor;
528   LiVESXDisplay *disp;
529 
530   LiVESPixbuf *pixbuf = NULL;
531   LiVESPixbuf *thumbnail = NULL;
532 
533   uint8_t *cpixels, *tpixels;
534 
535   lives_clip_t *sfile = mainw->files[clip];
536 
537   double frames_width;
538 
539   unsigned int cwidth, cheight;
540 
541   int twidth = 0, twidth3, twidth4, trow;
542   int frame_start;
543 
544   register int i, j, k;
545 
546   disp = lives_widget_get_display(LIVES_MAIN_WINDOW_WIDGET);
547 
548 #ifdef GUI_GTK
549   /* screen = gdk_display_get_default_screen (display); */
550   /* window = gdk_screen_get_root_window (screen); */
551   /* XQueryBestCursor (GDK_DISPLAY_XDISPLAY (display), */
552   /* 		    GDK_WINDOW_XID (window), */
553   /* 		    width, height, &cwidth, &cheight); */
554   gdk_display_get_maximal_cursor_size(disp, &cwidth, &cheight);
555 #endif
556 #ifdef GUI_QT
557   cwidth = MAX_CURSOR_WIDTH;
558 #endif
559 
560   if (width > cwidth) width = cwidth;
561 
562   mt->cursor_style = cstyle;
563   switch (cstyle) {
564   case LIVES_CURSOR_BLOCK:
565     if (sfile && sfile->frames > 0) {
566       frame_start = mt->opts.ign_ins_sel ? 1 : sfile->start;
567       frames_width = (double)(mt->opts.ign_ins_sel ? sfile->frames : sfile->end - sfile->start + 1.);
568 
569       pixbuf = lives_pixbuf_new(TRUE, width, height);
570 
571       for (i = 0; i < width; i += BLOCK_THUMB_WIDTH) {
572         // create a small thumb
573         twidth = BLOCK_THUMB_WIDTH;
574         if ((i + twidth) > width) twidth = width - i;
575         if (twidth >= 4) {
576           thumbnail = make_thumb(mt, clip, twidth, height, frame_start + (int)((double)i / (double)width * frames_width),
577                                  LIVES_INTERP_NORMAL, FALSE);
578           // render it in the cursor
579           if (thumbnail) {
580             trow = lives_pixbuf_get_rowstride(thumbnail);
581             twidth = lives_pixbuf_get_width(thumbnail);
582             cpixels = lives_pixbuf_get_pixels(pixbuf) + (i * 4);
583             tpixels = lives_pixbuf_get_pixels(thumbnail);
584 
585             if (!lives_pixbuf_get_has_alpha(thumbnail)) {
586               twidth3 = twidth * 3;
587               for (j = 0; j < height; j++) {
588                 for (k = 0; k < twidth3; k += 3) {
589                   lives_memcpy(cpixels, &tpixels[k], 3);
590                   lives_memset(cpixels + 3, 0xFF, 1);
591                   cpixels += 4;
592                 }
593                 tpixels += trow;
594                 cpixels += (width - twidth) << 2;
595               }
596             } else {
597               twidth4 = twidth * 4;
598               for (j = 0; j < height; j++) {
599                 lives_memcpy(cpixels, tpixels, twidth4);
600                 tpixels += trow;
601                 cpixels += width << 2;
602               }
603             }
604             lives_widget_object_unref(thumbnail);
605           }
606         }
607       }
608       break;
609     }
610   // fallthrough
611   case LIVES_CURSOR_AUDIO_BLOCK:
612     pixbuf = lives_pixbuf_new(TRUE, width, height);
613     trow = lives_pixbuf_get_rowstride(pixbuf);
614     cpixels = lives_pixbuf_get_pixels(pixbuf);
615     for (j = 0; j < height; j++) {
616       for (k = 0; k < width; k++) {
617         cpixels[0] = palette->audcol.red >> 8;
618         cpixels[1] = palette->audcol.green >> 8;
619         cpixels[2] = palette->audcol.blue >> 8;
620         cpixels[3] = palette->audcol.alpha >> 8;
621         cpixels += 4;
622       }
623       cpixels += (trow - width * 4);
624     }
625     break;
626   case LIVES_CURSOR_VIDEO_BLOCK:
627     pixbuf = lives_pixbuf_new(TRUE, width, height);
628     trow = lives_pixbuf_get_rowstride(pixbuf);
629     cpixels = lives_pixbuf_get_pixels(pixbuf);
630     for (j = 0; j < height; j++) {
631       for (k = 0; k < width; k++) {
632         cpixels[0] = palette->vidcol.red >> 8;
633         cpixels[1] = palette->vidcol.green >> 8;
634         cpixels[2] = palette->vidcol.blue >> 8;
635         cpixels[3] = palette->vidcol.alpha >> 8;
636         cpixels += 4;
637       }
638       cpixels += (trow - width * 4);
639     }
640     break;
641   case LIVES_CURSOR_FX_BLOCK:
642     pixbuf = lives_pixbuf_new(TRUE, width, height);
643     trow = lives_pixbuf_get_rowstride(pixbuf);
644     cpixels = lives_pixbuf_get_pixels(pixbuf);
645     for (j = 0; j < height; j++) {
646       for (k = 0; k < width; k++) {
647         cpixels[0] = palette->fxcol.red >> 8;
648         cpixels[1] = palette->fxcol.green >> 8;
649         cpixels[2] = palette->fxcol.blue >> 8;
650         cpixels[3] = palette->fxcol.alpha >> 8;
651         cpixels += 4;
652       }
653       cpixels += (trow - width * 4);
654     }
655     break;
656   default:
657     return;
658   }
659 
660   cursor = lives_cursor_new_from_pixbuf(disp, pixbuf, hsx, hsy);
661   lives_xwindow_set_cursor(lives_widget_get_xwindow(LIVES_MAIN_WINDOW_WIDGET), cursor);
662 
663   if (pixbuf) lives_widget_object_unref(pixbuf);
664   if (cursor) lives_cursor_unref(cursor);
665 }
666 
667 
write_backup_layout_numbering(lives_mt * mt)668 boolean write_backup_layout_numbering(lives_mt *mt) {
669   // link clip numbers in the auto save event_list to actual clip numbers
670   lives_clip_t *sfile;
671   double vald;
672   int fd, i, vali, hdlsize;
673   char *asave_file = lives_strdup_printf("%s/%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, lives_getuid(),
674                                          lives_getgid(),
675                                          capable->mainpid);
676   LiVESList *clist = mainw->cliplist;
677 
678   fd = lives_create_buffered(asave_file, DEF_FILE_PERMS);
679   lives_free(asave_file);
680 
681   THREADVAR(write_failed) = FALSE;
682 
683   if (fd != -1) {
684     for (clist = mainw->cliplist; !THREADVAR(write_failed) && clist; clist = clist->next) {
685       i = LIVES_POINTER_TO_INT(clist->data);
686       if (i < 1 || !IS_NORMAL_CLIP(i)) continue;
687       sfile = mainw->files[i];
688       if (mt) vali = i;
689       else vali = sfile->stored_layout_idx;
690       lives_write_le_buffered(fd, &vali, 4, TRUE);
691       vald = sfile->fps;
692       lives_write_le_buffered(fd, &vald, 8, TRUE);
693       hdlsize = strlen(sfile->handle);
694       lives_write_le_buffered(fd, &hdlsize, 4, TRUE);
695       lives_write_buffered(fd, sfile->handle, hdlsize, TRUE);
696     }
697     lives_close_buffered(fd);
698   }
699 
700   if (THREADVAR(write_failed)) return FALSE;
701   return TRUE;
702 }
703 
704 
upd_layout_maps(weed_plant_t * event_list)705 static void upd_layout_maps(weed_plant_t *event_list) {
706   int *layout_map;
707   double *layout_map_audio;
708 
709   // update layout maps for files from global layout map
710   layout_map = update_layout_map(event_list);
711   layout_map_audio = update_layout_map_audio(event_list);
712 
713   save_layout_map(layout_map, layout_map_audio, NULL, NULL);
714 
715   lives_freep((void **)&layout_map);
716   lives_freep((void **)&layout_map_audio);
717 }
718 
719 
renumber_from_backup_layout_numbering(lives_mt * mt)720 static void renumber_from_backup_layout_numbering(lives_mt *mt) {
721   // this is used only for crash recovery
722 
723   // layout_numbering simply maps our clip handle to clip numbers in the current layout
724   // (it would have been better to use the unique_id, but for backwards compatibility that is not possible)
725   // we assume the order hasnt changed (it cant), but there may be gaps in the numbering
726   // the numbering may have changed (for example we started last time in mt mode, this time in ce mode)
727 
728   double vard;
729   char buf[512];
730   char *aload_file;
731   boolean gotvalid = FALSE;
732   int fd, vari, clipn, offs, restart = 0;
733 
734   if (mt) {
735     aload_file = lives_strdup_printf("%s/%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME,
736                                      lives_getuid(), lives_getgid(),
737                                      capable->mainpid);
738     // ensure file layouts are updated
739     upd_layout_maps(NULL);
740   } else {
741     aload_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME,
742                                      lives_getuid(), lives_getgid(),
743                                      capable->mainpid);
744   }
745 
746   fd = lives_open_buffered_rdonly(aload_file);
747   if (fd != -1) {
748     while (1) {
749       offs = restart;
750       if (lives_read_le_buffered(fd, &clipn, 4, TRUE) < 4) break;
751       if (lives_read_le_buffered(fd, &vard, 8, TRUE) < 8) break;
752       if (lives_read_le_buffered(fd, &vari, 4, TRUE) < 4) break;
753       if (vari > 511) vari = 511;
754       if (lives_read_buffered(fd, buf, vari, TRUE) < vari) break;
755       lives_memset(buf + vari, 0, 1);
756       if (clipn < 0 || clipn > MAX_FILES) continue;
757       gotvalid = FALSE;
758       while (++offs < MAX_FILES) {
759         if (!IS_VALID_CLIP(offs)) {
760           if (!gotvalid) restart = offs;
761           continue;
762         }
763         gotvalid = TRUE;
764 
765         // dont increase restart, in case we cant match this clip
766         if (strcmp(mainw->files[offs]->handle, buf)) continue;
767 
768         // got a match - index the current clip order -> clip order in layout
769         renumbered_clips[clipn] = offs;
770         // lfps contains the fps at the time of the crash
771         lfps[offs] = vard;
772         restart = offs;
773         break;
774       }
775     }
776     lives_close_buffered(fd);
777   }
778   lives_free(aload_file);
779 }
780 
781 
save_mt_autoback(lives_mt * mt)782 static void save_mt_autoback(lives_mt *mt) {
783   // auto backup of the current layout
784 
785   // this is called from an idle funtion - if the specified amount of time has passed and
786   // the clip has been altered
787 
788   struct timeval otv;
789 
790   char *fname = lives_strdup_printf("%s.%d.%d.%d.%s", LAYOUT_FILENAME, lives_getuid(), lives_getgid(),
791                                     capable->mainpid, LIVES_FILE_EXT_LAYOUT);
792   char *asave_file = lives_build_filename(prefs->workdir, fname, NULL);
793   char *tmp;
794 
795   boolean retval = TRUE;
796   int retval2;
797   int fd;
798 
799   lives_free(fname);
800 
801   mt->auto_changed = FALSE;
802   lives_widget_set_sensitive(mt->backup, FALSE);
803   mt_desensitise(mt);
804 
805   // flush any pending events
806   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
807 
808   do {
809     retval2 = 0;
810     THREADVAR(write_failed) = FALSE;
811 
812     fd = lives_create_buffered(asave_file, DEF_FILE_PERMS);
813     if (fd >= 0) {
814       add_markers(mt, mt->event_list, FALSE);
815       do_threaded_dialog(_("Auto backup"), FALSE);
816 
817       set_signal_handlers((SignalHandlerPointer)defer_sigint);
818 
819       retval = save_event_list_inner(mt, fd, mt->event_list, NULL);
820       if (retval) retval = write_backup_layout_numbering(mt);
821 
822       if (mainw->signal_caught) catch_sigint(mainw->signal_caught);
823 
824       set_signal_handlers((SignalHandlerPointer)catch_sigint);
825 
826       end_threaded_dialog();
827       paint_lines(mt, mt->ptr_time, FALSE, NULL);
828 
829       remove_markers(mt->event_list);
830       lives_close_buffered(fd);
831     } else THREADVAR(write_failed) = TRUE;
832 
833     mt_sensitise(mt);
834 
835     if (!retval || THREADVAR(write_failed)) {
836       THREADVAR(write_failed) = FALSE;
837       retval2 = do_write_failed_error_s_with_retry(asave_file, NULL);
838     }
839   } while (retval2 == LIVES_RESPONSE_RETRY);
840 
841   lives_free(asave_file);
842 
843   mt->auto_back_time = lives_get_current_ticks();
844 
845   gettimeofday(&otv, NULL);
846   tmp = lives_datetime(otv.tv_sec, TRUE);
847   d_print("Auto backup of timeline at %s\n", tmp);
848   lives_free(tmp);
849 }
850 
851 
on_mt_backup_activate(LiVESMenuItem * menuitem,livespointer user_data)852 static void on_mt_backup_activate(LiVESMenuItem *menuitem, livespointer user_data) {
853   lives_mt *mt = (lives_mt *)user_data;
854   if (!mt->auto_changed) return;
855   if (mt->idlefunc > 0) {
856     lives_source_remove(mt->idlefunc);
857     mt->idlefunc = 0;
858   }
859   save_mt_autoback(mt);
860 }
861 
862 
mt_auto_backup(livespointer user_data)863 boolean mt_auto_backup(livespointer user_data) {
864   ticks_t stime, diff;
865 
866   lives_mt *mt = (lives_mt *)user_data;
867 
868   if (!mt->idlefunc) return FALSE;
869   if (!mainw->multitrack) return FALSE;
870 
871   if (prefs->mt_auto_back == 0) mt->auto_changed = TRUE;
872 
873   if (!mt->auto_changed && mt->did_backup) {
874     LIVES_WARN("Error in mt backup");
875     return TRUE;
876   }
877 
878   mt->idlefunc = 0;
879 
880   if (!mt->auto_changed || !mt->event_list || prefs->mt_auto_back < 0) {
881     return FALSE;
882   }
883 
884   stime = lives_get_current_ticks();
885   if (mt->auto_back_time == 0) mt->auto_back_time = stime;
886 
887   diff = stime - mt->auto_back_time;
888   if (diff >= prefs->mt_auto_back * TICKS_PER_SECOND) {
889     // time to back up the event_list
890     // resets mt->auto_changed
891     save_mt_autoback(mt);
892     return FALSE;
893   }
894 
895   // re-add the idlefunc (in case we came here via a timer)
896   mt->idlefunc = mt_idle_add(mt);
897   return FALSE;
898 }
899 
900 
mt_idle_add(lives_mt * mt)901 uint32_t mt_idle_add(lives_mt *mt) {
902   uint32_t retval;
903 
904   if (LIVES_IS_PLAYING || mt->is_rendering) return 0;
905 
906   if (prefs->mt_auto_back <= 0) return 0;
907 
908   if (mt->idlefunc > 0) return mt->idlefunc;
909 
910   set_signal_handlers((SignalHandlerPointer)defer_sigint);
911 
912   // TODO: last param is a destroy notify, so we can check if something re-adds it or removes it when it shouldnt
913   retval = lives_timer_add_simple(1001, mt_auto_backup, mt);
914 
915   if (mainw->signal_caught) catch_sigint(mainw->signal_caught);
916 
917   set_signal_handlers((SignalHandlerPointer)catch_sigint);
918 
919   return retval;
920 }
921 
922 
recover_layout_cancelled(boolean is_startup)923 void recover_layout_cancelled(boolean is_startup) {
924   char *eload_file = lives_strdup_printf("%s/%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, lives_getuid(), lives_getgid(),
925                                          capable->mainpid, LIVES_FILE_EXT_LAYOUT);
926 
927   if (is_startup) mainw->recoverable_layout = FALSE;
928 
929   lives_rm(eload_file);
930   lives_free(eload_file);
931 
932   eload_file = lives_strdup_printf("%s/%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, lives_getuid(), lives_getgid(),
933                                    capable->mainpid);
934   lives_rm(eload_file);
935   lives_free(eload_file);
936 
937   if (is_startup) do_after_crash_warning();
938 }
939 
940 
mt_load_recovery_layout(lives_mt * mt)941 boolean mt_load_recovery_layout(lives_mt *mt) {
942   boolean recovered = TRUE;
943   char *aload_file = NULL, *eload_file;
944 
945   if (mt) {
946     aload_file = lives_strdup_printf("%s/%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, lives_getuid(), lives_getgid(),
947                                      capable->mainpid);
948     eload_file = lives_strdup_printf("%s/%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, lives_getuid(), lives_getgid(),
949                                      capable->mainpid, LIVES_FILE_EXT_LAYOUT);
950     mt->auto_reloading = TRUE;
951     mt->event_list = mainw->event_list = load_event_list(mt, eload_file);
952     mt->auto_reloading = FALSE;
953     if (mt->event_list) {
954       lives_rm(eload_file);
955       lives_rm(aload_file);
956       mt_init_tracks(mt, TRUE);
957       remove_markers(mt->event_list);
958       save_mt_autoback(mt);
959     }
960   } else {
961     // recover recording
962     int fd;
963     aload_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, lives_getuid(),
964                                      lives_getgid(),
965                                      capable->mainpid);
966     eload_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, lives_getuid(), lives_getgid(),
967                                      capable->mainpid, LIVES_FILE_EXT_LAYOUT);
968 
969     if ((fd = lives_open_buffered_rdonly(eload_file)) < 0) {
970       lives_close_buffered(fd);
971     } else {
972       mainw->event_list = load_event_list(NULL, eload_file);
973       if (mainw->event_list) {
974         lives_rm(eload_file);
975         lives_rm(aload_file);
976       }
977     }
978   }
979 
980   if (!mainw->event_list) {
981     // failed to load
982     // keep the faulty layout for forensic purposes
983     char *uldir = lives_build_path(prefs->workdir, UNREC_LAYOUTS_DIR, NULL);
984     lives_mkdir_with_parents(uldir, capable->umask);
985     if (lives_file_test(uldir, LIVES_FILE_TEST_IS_DIR)) {
986       char *norem = lives_build_filename(uldir, LIVES_FILENAME_NOREMOVE, NULL);
987       lives_touch(norem);
988       lives_free(norem);
989       if (lives_file_test(eload_file, LIVES_FILE_TEST_EXISTS)) {
990         lives_mv(eload_file, uldir);
991       }
992       if (lives_file_test(aload_file, LIVES_FILE_TEST_EXISTS)) {
993         lives_mv(aload_file, uldir);
994       }
995       // this works very well on layout files
996       compress_files_in_dir(uldir, 0, NULL);
997     }
998     lives_free(uldir);
999 
1000     if (mt) mt->fps = prefs->mt_def_fps;
1001     recovered = FALSE;
1002   }
1003 
1004   lives_free(eload_file);
1005   lives_freep((void **)&aload_file);
1006   return recovered;
1007 }
1008 
1009 
recover_layout(void)1010 boolean recover_layout(void) {
1011   boolean loaded = TRUE;
1012 
1013   if (prefs->startup_interface == STARTUP_CE) {
1014     if (!on_multitrack_activate(NULL, NULL)) {
1015       loaded = FALSE;
1016       multitrack_delete(mainw->multitrack, FALSE);
1017       do_bad_layout_error();
1018     }
1019   } else {
1020     mainw->multitrack->auto_reloading = TRUE;
1021     set_string_pref(PREF_AR_LAYOUT, ""); // in case we crash...
1022     loaded = mt_load_recovery_layout(mainw->multitrack);
1023     mainw->multitrack->auto_reloading = FALSE;
1024     mt_sensitise(mainw->multitrack);
1025     reset_mt_play_sizes(mainw->multitrack);
1026   }
1027   mainw->recoverable_layout = FALSE;
1028   do_after_crash_warning();
1029   return loaded;
1030 }
1031 
1032 
mt_get_pchain(void)1033 void **mt_get_pchain(void) {
1034   return pchain;
1035 }
1036 
1037 
get_track_name(lives_mt * mt,int track_num,boolean is_audio)1038 char *get_track_name(lives_mt *mt, int track_num, boolean is_audio) {
1039   // not const return because of translation issues
1040   LiVESWidget *xeventbox;
1041   if (track_num < 0) return (_("Backing audio"));
1042   if (!is_audio) xeventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, track_num);
1043   else xeventbox = (LiVESWidget *)lives_list_nth_data(mt->audio_draws, track_num + mt->opts.back_audio_tracks);
1044   return lives_strdup((char *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "track_name"));
1045 }
1046 
1047 
get_time_from_x(lives_mt * mt,int x)1048 LIVES_INLINE double get_time_from_x(lives_mt *mt, int x) {
1049   double time = (double)x / (double)(lives_widget_get_allocation_width(mt->timeline) - 1) *
1050                 (mt->tl_max - mt->tl_min) + mt->tl_min;
1051   if (time < 0.) time = 0.;
1052   else if (time > mt->end_secs + 1. / mt->fps) time = mt->end_secs + 1. / mt->fps;
1053   return q_dbl(time, mt->fps) / TICKS_PER_SECOND_DBL;
1054 }
1055 
1056 
set_params_unchanged(lives_mt * mt,lives_rfx_t * rfx)1057 LIVES_INLINE void set_params_unchanged(lives_mt *mt, lives_rfx_t *rfx) {
1058   weed_plant_t *wparam;
1059   weed_plant_t *inst = rfx->source;
1060   int i, j;
1061 
1062   for (i = 0; i < rfx->num_params; i++) {
1063     rfx->params[i].changed = FALSE;
1064     if ((wparam = weed_inst_in_param(inst, i, FALSE, FALSE))) {
1065       if (is_perchannel_multiw(wparam)) {
1066         int nvals = weed_leaf_num_elements(mt->init_event, WEED_LEAF_IN_TRACKS);
1067         int *ign_array = (int *)lives_calloc(nvals, sizint);
1068         for (j = 0; j < nvals; j++) {
1069           ign_array[j] = WEED_TRUE;
1070         }
1071         weed_set_boolean_array(wparam, WEED_LEAF_IGNORE, nvals, ign_array);
1072         lives_free(ign_array);
1073 	// *INDENT-OFF*
1074       }}}
1075   // *INDENT-ON*
1076 
1077   lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
1078   lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
1079 }
1080 
1081 
get_track_height(lives_mt * mt)1082 static int get_track_height(lives_mt * mt) {
1083   LiVESWidget *eventbox;
1084   LiVESList *list = mt->video_draws;
1085 
1086   while (list) {
1087     eventbox = (LiVESWidget *)list->data;
1088     if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY)) == 0)
1089       return lives_widget_get_allocation_height(eventbox);
1090     list = list->next;
1091   }
1092 
1093   return 0;
1094 }
1095 
1096 
is_audio_eventbox(LiVESWidget * ebox)1097 static boolean is_audio_eventbox(LiVESWidget * ebox) {
1098   return LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(ebox), "is_audio"));
1099 }
1100 
1101 
add_to_thumb_cache(int fnum,frames_t frame,frames_t range,int height,LiVESPixbuf * pixbuf)1102 static boolean add_to_thumb_cache(int fnum, frames_t frame, frames_t range, int height,
1103                                   LiVESPixbuf * pixbuf) {
1104   LiVESList *list = mainw->files[fnum]->tcache, *xlist, *llist = NULL;
1105   lives_tcache_entry_t *tce;
1106 
1107   for (; list; list = list->next) {
1108     tce = (lives_tcache_entry_t *)list->data;
1109     llist = list;
1110     if (tce->frame <= frame - range) continue;
1111     if (tce->frame <= frame + range) return FALSE;
1112     break;
1113   }
1114   tce = (lives_tcache_entry_t *)lives_calloc(1, sizeof(lives_tcache_entry_t));
1115   tce->frame = frame;
1116   tce->pixbuf = pixbuf;
1117   xlist = lives_list_append(NULL, tce);
1118   xlist->data = (livespointer)tce;
1119   if (list) {
1120     xlist->prev = list->prev;
1121     if (list->prev) list->prev->next = xlist;
1122     else  mainw->files[fnum]->tcache = xlist;
1123     xlist->next = list;
1124     list->prev = xlist;
1125   } else {
1126     if (llist) {
1127       llist->next = xlist;
1128       xlist->prev = llist;
1129     } else {
1130       mainw->files[fnum]->tcache = xlist;
1131       mainw->files[fnum]->tcache_height = height;
1132     }
1133   }
1134   return TRUE;
1135 }
1136 
get_from_thumb_cache(int fnum,frames_t frame,frames_t range)1137 static LiVESPixbuf *get_from_thumb_cache(int fnum, frames_t frame, frames_t range) {
1138   LiVESList *list = mainw->files[fnum]->tcache;
1139   for (; list; list = list->next) {
1140     lives_tcache_entry_t *tcentry = (lives_tcache_entry_t *)list->data;
1141     if (tcentry->frame <= frame - range) continue;
1142     if (tcentry->frame >= frame + range) return NULL;
1143     return tcentry->pixbuf;
1144   }
1145   return NULL;
1146 }
1147 
free_thumb_cache(int fnum,frames_t fromframe)1148 void free_thumb_cache(int fnum, frames_t fromframe) {
1149   LiVESList *list = mainw->files[fnum]->tcache, *freestart = NULL;
1150   boolean has_some = FALSE;
1151   for (; list; list = list->next) {
1152     lives_tcache_entry_t *tcentry = (lives_tcache_entry_t *)list->data;
1153     if (tcentry) {
1154       if (tcentry->frame < fromframe) {
1155         has_some = TRUE;
1156         continue;
1157       }
1158       if (has_some) {
1159         list->prev->next = NULL;
1160         list->prev = NULL;
1161         freestart = list;
1162         has_some = FALSE;
1163       } else freestart = mainw->files[fnum]->tcache;
1164       lives_widget_object_unref(tcentry->pixbuf);
1165     }
1166   }
1167   if (freestart) lives_list_free(freestart);
1168   if (freestart == mainw->files[fnum]->tcache) {
1169     mainw->files[fnum]->tcache = NULL;
1170     mainw->files[fnum]->tcache_height = 0;
1171   }
1172   mainw->files[fnum]->tcache_dubious_from = 0;
1173 }
1174 
1175 
draw_block(lives_mt * mt,lives_painter_t * cairo,lives_painter_surface_t * surf,track_rect * block,int x1,int x2)1176 static void draw_block(lives_mt * mt, lives_painter_t *cairo,
1177                        lives_painter_surface_t *surf, track_rect * block, int x1, int x2) {
1178   // x1 is start point of drawing area (in pixels), x2 is width of drawing area (in pixels)
1179   lives_painter_t *cr;
1180   weed_event_t *event = block->start_event, *nxevent = NULL;
1181   weed_timecode_t tc = get_event_timecode(event);
1182   LiVESWidget *eventbox = block->eventbox;
1183   LiVESPixbuf *thumbnail = NULL;
1184 
1185   double tl_span = mt->tl_max - mt->tl_min;
1186   double offset_startd = (double)tc / TICKS_PER_SECOND_DBL, offset_endd;
1187 
1188   frames_t framenum, last_framenum = -1, range = 0;
1189 
1190   boolean needs_text = TRUE;
1191   boolean is_audio = FALSE;
1192 
1193   int offset_start, offset_end;
1194   int filenum, track;
1195   int width = BLOCK_THUMB_WIDTH;
1196   int hidden = (int)LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY));
1197 
1198   register int i;
1199 
1200   if (mt->no_expose) return;
1201 
1202   if (hidden) return;
1203 
1204   // block to right of screen
1205   if (offset_startd >= mt->tl_max) return;
1206 
1207   // block to right of drawing area
1208   offset_start = (int)((offset_startd - mt->tl_min) / tl_span * lives_widget_get_allocation_width(eventbox) + .5);
1209   if ((x1 > 0 || x2 > 0) && offset_start > (x1 + x2)) return;
1210 
1211   offset_endd = get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL + (!is_audio_eventbox(eventbox))
1212                 / mainw->files[mt->render_file]->fps;
1213   offset_end = (offset_endd - mt->tl_min) / tl_span * lives_widget_get_allocation_width(eventbox);
1214 
1215   // end of block before drawing area
1216   if (offset_end < x1) return;
1217 
1218   if (!surf) cr = cairo;
1219   else cr = lives_painter_create_from_surface(surf);
1220 
1221   if (!cr) return;
1222 
1223   lives_painter_set_line_width(cr, 1.);
1224 
1225   track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number"));
1226   is_audio = is_audio_eventbox(eventbox);
1227   if (track < 0) is_audio = TRUE;
1228 
1229   if (!is_audio) filenum = get_frame_event_clip(block->start_event, track);
1230   else filenum = get_audio_frame_clip(block->start_event, track);
1231   if (!IS_VALID_CLIP(filenum)) return;
1232 
1233   switch (block->state) {
1234   case BLOCK_UNSELECTED:
1235     if (BLOCK_DRAW_TYPE == BLOCK_DRAW_SIMPLE) {
1236       lives_painter_set_source_rgb_from_lives_rgba(cr, &palette->vidcol);
1237       lives_painter_new_path(cr);
1238       lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(eventbox));
1239 
1240       lives_painter_move_to(cr, offset_start, 0);
1241       lives_painter_line_to(cr, offset_end, lives_widget_get_allocation_height(eventbox));
1242 
1243       lives_painter_move_to(cr, offset_end, 0);
1244       lives_painter_line_to(cr, offset_start, lives_widget_get_allocation_height(eventbox));
1245 
1246       lives_painter_stroke(cr);
1247     } else {
1248       if (!is_audio && track > -1) {
1249         boolean in_cache = FALSE;
1250         int height = lives_widget_get_allocation_height(eventbox);
1251         for (i = offset_start; i < offset_end; i += BLOCK_THUMB_WIDTH) {
1252           if (i > x2 - x1) break;
1253           tc += tl_span / lives_widget_get_allocation_width(eventbox) * width * TICKS_PER_SECOND_DBL;
1254           if (i + BLOCK_THUMB_WIDTH < x1) continue;
1255           if (!nxevent) event = get_frame_event_at(mt->event_list, tc, event, FALSE);
1256           else {
1257             event = nxevent;
1258             nxevent = NULL;
1259           }
1260           if (last_framenum == -1) {
1261             frames_t xframenum;
1262             weed_timecode_t xtc = tc + tl_span / lives_widget_get_allocation_width(eventbox) * width
1263                                   * TICKS_PER_SECOND_DBL;
1264             framenum = get_frame_event_frame(event, track);
1265             if ((nxevent = get_frame_event_at(mt->event_list, xtc, event, FALSE))) {
1266               if ((xframenum = get_frame_event_frame(nxevent, track)) > framenum)
1267                 range = (xframenum - framenum) / 2;
1268             } else {
1269               xtc = tc - tl_span / lives_widget_get_allocation_width(eventbox) * width
1270                     * TICKS_PER_SECOND_DBL;
1271               if (xtc >= 0) {
1272                 if ((nxevent = get_frame_event_at(mt->event_list, xtc, NULL, FALSE))) {
1273                   if ((xframenum = get_frame_event_frame(nxevent, track)) < framenum)
1274                     range = (framenum - xframenum) / 2;
1275                   nxevent = NULL;
1276 		  // *INDENT-OFF*
1277                 }}}}
1278 	  // *INDENT-ON*
1279 
1280           if (i + width >= 0) {
1281             // create a small thumb
1282             framenum = get_frame_event_frame(event, track);
1283             if (framenum < 1 || framenum > mainw->files[filenum]->frames) continue;
1284 
1285             if (!in_cache && thumbnail) lives_widget_object_unref(thumbnail);
1286             thumbnail = NULL;
1287             in_cache = FALSE;
1288 
1289             if (IS_VALID_CLIP(filenum) && filenum != mainw->scrap_file && framenum != last_framenum) {
1290               if (height != mainw->files[filenum]->tcache_height && mainw->files[filenum]->tcache) {
1291                 free_thumb_cache(filenum, 0);
1292               }
1293               if (!(thumbnail = get_from_thumb_cache(filenum, framenum, range))) {
1294                 if (mainw->files[filenum]->frames > 0 && mainw->files[filenum]->clip_type == CLIP_TYPE_FILE) {
1295                   lives_clip_data_t *cdata = ((lives_decoder_t *)mainw->files[filenum]->ext_src)->cdata;
1296                   if (cdata && !(cdata->seek_flag & LIVES_SEEK_FAST) &&
1297                       is_virtual_frame(filenum, framenum)) {
1298                     thumbnail = make_thumb_fast_between(mt, filenum, width, height,
1299                                                         framenum, last_framenum == -1 ? 0
1300                                                         : framenum - last_framenum);
1301                   } else {
1302                     thumbnail = make_thumb(mt, filenum, width, height,
1303                                            framenum, LIVES_INTERP_FAST, FALSE);
1304                   }
1305                 } else {
1306                   thumbnail = make_thumb(mt, filenum, width, height,
1307                                          framenum, LIVES_INTERP_FAST, FALSE);
1308                 }
1309                 in_cache = add_to_thumb_cache(filenum, framenum, range, height, thumbnail);
1310               } else {
1311                 in_cache = TRUE;
1312               }
1313             }
1314             last_framenum = framenum;
1315             // render it in the eventbox
1316             if (thumbnail) {
1317               lives_painter_set_source_pixbuf(cr, thumbnail, i, 0);
1318               if (i + width > offset_end) {
1319                 width = offset_end - i;
1320                 // crop to width
1321                 lives_painter_new_path(cr);
1322                 lives_painter_rectangle(cr, i, 0, width, lives_widget_get_allocation_height(eventbox));
1323                 lives_painter_clip(cr);
1324               }
1325               lives_painter_paint(cr);
1326             } else {
1327               if (i + width > offset_end) width = offset_end - i;
1328               lives_painter_set_source_rgb_from_lives_rgba(cr, &palette->vidcol);
1329               lives_painter_new_path(cr);
1330               lives_painter_rectangle(cr, i, 0, width, lives_widget_get_allocation_height(eventbox));
1331               lives_painter_fill(cr);
1332             }
1333             if (LIVES_IS_PLAYING) {
1334               mt->no_expose = TRUE;
1335               // expose is not re-entrant due to bgimg refs
1336               unpaint_lines(mt);
1337               mt->no_expose = FALSE;
1338             }
1339           }
1340         }
1341         if (!in_cache && thumbnail) lives_widget_object_unref(thumbnail);
1342       } else {
1343         lives_painter_set_source_rgb_from_lives_rgba(cr, &palette->audcol);
1344         lives_painter_new_path(cr);
1345         lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(eventbox));
1346         lives_painter_fill(cr);
1347       }
1348       lives_painter_set_source_rgb_from_lives_widget_color(cr, &palette->black);
1349       lives_painter_new_path(cr);
1350       lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(eventbox));
1351       lives_painter_stroke(cr);
1352 
1353       if (needs_text) {
1354         const char *sfont = "Sans";
1355         char *fname = get_menu_name(mainw->files[filenum], FALSE);
1356         lives_colRGBA64_t col_white, col_black, *colr;
1357         LingoLayout *layout;
1358         lives_painter_surface_t *surface;
1359         double top = 0.2;
1360         int height = lives_widget_get_allocation_height(eventbox);
1361         int text_start = offset_start + 2, text_end = offset_end;
1362 
1363         if (text_start < 2) text_start = 2;
1364 
1365         surface = lives_painter_get_target(cr);
1366         lives_painter_surface_flush(surface);
1367 
1368         col_white.red = col_white.green = col_white.blue = col_white.alpha = col_black.alpha = 65535;
1369         col_black.red = col_black.green = col_black.blue = 0;
1370         colr = &col_white;
1371 
1372         if (is_audio) {
1373           double luma = get_luma16(palette->audcol.red, palette->audcol.green, palette->audcol.blue);
1374           if (luma > 0.2) colr = &col_black;
1375         }
1376 
1377         layout = render_text_to_cr(NULL, cr, fname, sfont, 10.,
1378                                    LIVES_TEXT_MODE_FOREGROUND_ONLY, colr,
1379                                    colr, FALSE, FALSE, &top, &text_start,
1380                                    text_end - text_start, &height);
1381 
1382         lingo_painter_show_layout(cr, layout);
1383 
1384         if (layout) lives_widget_object_unref(layout);
1385 
1386         lives_free(fname);
1387 
1388         lives_painter_fill(cr);
1389       }
1390 
1391       if (LIVES_IS_PLAYING) {
1392         mt->no_expose = TRUE; // expose is not re-entrant due to bgimg refs.
1393         unpaint_lines(mt);
1394         mt->no_expose = FALSE;
1395       }
1396     }
1397     break;
1398   case BLOCK_SELECTED:
1399     lives_painter_new_path(cr);
1400 
1401     lives_painter_set_source_rgba(cr, 0., 0., 0., SELBLOCK_ALPHA);
1402 
1403     lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(eventbox));
1404     lives_painter_fill(cr);
1405 
1406     lives_painter_new_path(cr);
1407     lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(eventbox));
1408 
1409     lives_painter_move_to(cr, offset_start, 0);
1410     lives_painter_line_to(cr, offset_end, lives_widget_get_allocation_height(eventbox));
1411 
1412     lives_painter_move_to(cr, offset_end, 0);
1413     lives_painter_line_to(cr, offset_start, lives_widget_get_allocation_height(eventbox));
1414 
1415     lives_painter_stroke(cr);
1416 
1417     break;
1418   }
1419 
1420   if (surf) lives_painter_destroy(cr);
1421 
1422 }
1423 
1424 
draw_aparams(lives_mt * mt,LiVESWidget * eventbox,lives_painter_t * cr,LiVESList * param_list,weed_plant_t * init_event,int startx,int width)1425 static void draw_aparams(lives_mt * mt, LiVESWidget * eventbox, lives_painter_t *cr, LiVESList * param_list,
1426                          weed_plant_t *init_event,
1427                          int startx, int width) {
1428   // draw audio parameters : currently we overlay coloured lines on the audio track to show the level of
1429   // parameters in the audio_volume plugin
1430   // we only display whichever parameters the user has elected to show
1431 
1432   LiVESList *plist;
1433 
1434   weed_plant_t **in_params, *param, *ptmpl;
1435   weed_plant_t *filter, *inst, *deinit_event;
1436 
1437   weed_timecode_t tc, start_tc, end_tc;
1438 
1439   double tl_span = mt->tl_max - mt->tl_min;
1440   double dtime;
1441   double ratio;
1442   double vald, mind, maxd, *valds;
1443 
1444   double y;
1445 
1446   int vali, mini, maxi, *valis;
1447   int i, pnum, ptype;
1448   int offset_start, offset_end, startpos;
1449   int track;
1450 
1451   char *fhash;
1452 
1453   void **pchainx = NULL;
1454 
1455   fhash = weed_get_string_value(init_event, WEED_LEAF_FILTER, NULL);
1456 
1457   if (!fhash) return;
1458 
1459   filter = get_weed_filter(weed_get_idx_for_hashname(fhash, TRUE));
1460   lives_free(fhash);
1461 
1462   inst = weed_instance_from_filter(filter);
1463   in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
1464 
1465   deinit_event = weed_get_plantptr_value(init_event, WEED_LEAF_DEINIT_EVENT, NULL);
1466 
1467   start_tc = get_event_timecode(init_event);
1468   end_tc = get_event_timecode(deinit_event);
1469 
1470   offset_start = (int)((start_tc / TICKS_PER_SECOND_DBL - mt->tl_min) / tl_span * lives_widget_get_allocation_width(
1471                          eventbox) + .5);
1472   offset_end = (int)((end_tc / TICKS_PER_SECOND_DBL - mt->tl_min + 1. / mt->fps) / tl_span * lives_widget_get_allocation_width(
1473                        eventbox) + .5);
1474 
1475   if (offset_end < 0 || offset_start > lives_widget_get_allocation_width(eventbox)) {
1476     lives_free(in_params);
1477     weed_instance_unref(inst);
1478     return;
1479   }
1480 
1481   if (offset_start > startx) startpos = offset_start;
1482   else startpos = startx;
1483 
1484   track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox),
1485                                "layer_number")) + mt->opts.back_audio_tracks;
1486 
1487   lives_painter_set_line_width(cr, 1.);
1488   lives_painter_set_source_rgb_from_lives_widget_color(cr, &palette->black);
1489 
1490   pchainx = weed_get_voidptr_array(init_event, WEED_LEAF_IN_PARAMETERS, NULL);
1491 
1492   //lives_painter_set_operator (cr, LIVES_PAINTER_OPERATOR_DEST_OVER);
1493   for (i = startpos; i < startx + width; i++) {
1494     dtime = get_time_from_x(mt, i);
1495     tc = dtime * TICKS_PER_SECOND_DBL;
1496     if (tc >= end_tc) break;
1497 
1498     if (pchainx) interpolate_params(inst, pchainx, tc);
1499 
1500     plist = param_list;
1501     while (plist) {
1502       pnum = LIVES_POINTER_TO_INT(plist->data);
1503       param = in_params[pnum];
1504       ptmpl = weed_param_get_template(param);
1505       ptype = weed_paramtmpl_get_type(ptmpl);
1506       switch (ptype) {
1507       case WEED_PARAM_INTEGER:
1508         valis = weed_get_int_array(param, WEED_LEAF_VALUE, NULL);
1509         if (is_perchannel_multiw(in_params[pnum])) vali = valis[track];
1510         else vali = valis[0];
1511         mini = weed_get_int_value(ptmpl, WEED_LEAF_MIN, NULL);
1512         maxi = weed_get_int_value(ptmpl, WEED_LEAF_MAX, NULL);
1513         ratio = (double)(vali - mini) / (double)(maxi - mini);
1514         lives_free(valis);
1515         break;
1516       case WEED_PARAM_FLOAT:
1517         valds = weed_get_double_array(param, WEED_LEAF_VALUE, NULL);
1518         if (is_perchannel_multiw(in_params[pnum])) vald = valds[track];
1519         else vald = valds[0];
1520         mind = weed_get_double_value(ptmpl, WEED_LEAF_MIN, NULL);
1521         maxd = weed_get_double_value(ptmpl, WEED_LEAF_MAX, NULL);
1522         ratio = (vald - mind) / (maxd - mind);
1523         lives_free(valds);
1524         break;
1525       default:
1526         continue;
1527       }
1528 
1529       y = (1. - ratio) * (double)lives_widget_get_allocation_height(eventbox);
1530 
1531       lives_painter_move_to(cr, i - 1, y - 1);
1532       lives_painter_line_to(cr, i, y);
1533 
1534       plist = plist->next;
1535     }
1536   }
1537 
1538   weed_instance_unref(inst);
1539   weed_instance_unref(inst);
1540 
1541   lives_painter_stroke(cr);
1542   lives_painter_surface_flush(lives_painter_get_target(cr));
1543 
1544   lives_free(pchainx);
1545   lives_free(in_params);
1546 }
1547 
1548 
redraw_eventbox(lives_mt * mt,LiVESWidget * eventbox)1549 static void redraw_eventbox(lives_mt * mt, LiVESWidget * eventbox) {
1550   if (!LIVES_IS_WIDGET_OBJECT(eventbox)) return;
1551 
1552   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "drawn", LIVES_INT_TO_POINTER(FALSE));
1553   lives_widget_queue_draw(eventbox);  // redraw the track
1554 
1555   if (is_audio_eventbox(eventbox)) {
1556     // handle expanded audio
1557     LiVESWidget *xeventbox;
1558     if (mainw->files[mt->render_file]->achans > 0) {
1559       xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "achan0");
1560       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "drawn", LIVES_INT_TO_POINTER(FALSE));
1561       lives_widget_queue_draw(xeventbox);  // redraw the track
1562       if (mainw->files[mt->render_file]->achans > 1) {
1563         xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "achan1");
1564         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "drawn", LIVES_INT_TO_POINTER(FALSE));
1565         lives_widget_queue_draw(xeventbox);  // redraw the track
1566       }
1567     }
1568   }
1569 }
1570 
1571 
EXPOSE_FN_DECL(expose_track_event,eventbox,user_data)1572 static EXPOSE_FN_DECL(expose_track_event, eventbox, user_data) {
1573   lives_painter_t *cr;
1574 
1575   lives_mt *mt = (lives_mt *)user_data;
1576 
1577   track_rect *block;
1578   track_rect *sblock = NULL;
1579 
1580   ulong idlefunc;
1581 
1582   lives_painter_surface_t *bgimage;
1583 
1584   int startx, starty, width, height;
1585   int hidden;
1586 
1587   if (mt->no_expose) return TRUE;
1588 
1589   if (event) {
1590     if (event->count > 0) {
1591       return TRUE;
1592     }
1593     startx = event->area.x;
1594     starty = event->area.y;
1595     width = event->area.width;
1596     height = event->area.height;
1597   } else {
1598     startx = starty = 0;
1599     width = lives_widget_get_allocation_width(eventbox);
1600     height = lives_widget_get_allocation_height(eventbox);
1601   }
1602 
1603   if (width == 0) return FALSE;
1604 
1605   hidden = (int)LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY));
1606   if (hidden != 0) {
1607     LiVESWidget *label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label");
1608     lives_widget_hide(eventbox);
1609     lives_widget_hide(lives_widget_get_parent(label));
1610     return FALSE;
1611   }
1612 
1613   idlefunc = mt->idlefunc;
1614   if (mt->idlefunc > 0) {
1615     mt->idlefunc = 0;
1616     lives_source_remove(idlefunc);
1617   }
1618 
1619   if (width > lives_widget_get_allocation_width(eventbox) - startx) width = lives_widget_get_allocation_width(eventbox) - startx;
1620 
1621   bgimage = (lives_painter_surface_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "bgimg");
1622 
1623 draw1:
1624 #if !GTK_CHECK_VERSION(3, 22, 0)
1625   if (!cairo) cr = lives_painter_create_from_surface(bgimage);
1626   else cr = cairo;
1627 #else
1628   cr = cairo;
1629 #endif
1630 
1631   if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "drawn"))) {
1632     if (bgimage) {
1633       lives_painter_set_source_surface(cr, bgimage, startx, starty);
1634       lives_painter_rectangle(cr, startx, starty, width, height);
1635       lives_painter_fill(cr);
1636 
1637       if (mt->block_selected && mt->block_selected->eventbox == eventbox) {
1638         draw_block(mt, cr, NULL, mt->block_selected, -1, -1);
1639       }
1640 
1641       if (is_audio_eventbox(eventbox) && mt->avol_init_event && mt->opts.aparam_view_list)
1642         draw_aparams(mt, eventbox, cr, mt->opts.aparam_view_list, mt->avol_init_event, startx, width);
1643 
1644       if (idlefunc > 0) {
1645         mt->idlefunc = mt_idle_add(mt);
1646       }
1647       if (!cairo) lives_painter_destroy(cr);
1648 
1649       return TRUE;
1650     }
1651   }
1652 
1653   width = lives_widget_get_allocation_width(eventbox);
1654   height = lives_widget_get_allocation_height(eventbox);
1655   if (!cairo) lives_painter_destroy(cr);
1656 
1657   if (bgimage) lives_painter_surface_destroy(bgimage);
1658 
1659   bgimage = lives_widget_create_painter_surface(eventbox);
1660 
1661   if (bgimage) {
1662     lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
1663 
1664     if (palette->style & STYLE_1) {
1665       lives_painter_t *crx = lives_painter_create_from_surface(bgimage);
1666       lives_painter_set_source_rgb_from_lives_rgba(crx, &palette->mt_evbox);
1667       lives_painter_rectangle(crx, 0., 0., width, height);
1668       lives_painter_fill(crx);
1669       lives_painter_paint(crx);
1670       lives_painter_destroy(crx);
1671     } else clear_widget_bg(eventbox, bgimage);
1672 
1673     if (mt->block_selected) {
1674       sblock = mt->block_selected;
1675       sblock->state = BLOCK_UNSELECTED;
1676     }
1677 
1678     block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
1679 
1680     while (block) {
1681       draw_block(mt, NULL, bgimage, block, startx, width);
1682       block = block->next;
1683     }
1684 
1685     if (sblock) {
1686       mt->block_selected = sblock;
1687       sblock->state = BLOCK_SELECTED;
1688     }
1689 
1690     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1691   } else if (bgimage) {
1692     lives_painter_surface_destroy(bgimage);
1693     bgimage = NULL;
1694   }
1695 
1696   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "bgimg", (livespointer)bgimage);
1697   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "drawn", LIVES_INT_TO_POINTER(bgimage != NULL));
1698 
1699   if (bgimage) goto draw1;
1700 
1701   if (idlefunc > 0) {
1702     mt->idlefunc = mt_idle_add(mt);
1703   }
1704 
1705   return TRUE;
1706 }
1707 EXPOSE_FN_END
1708 
1709 
mt_params_label(lives_mt * mt)1710 static char *mt_params_label(lives_mt * mt) {
1711   char *fname = weed_filter_idx_get_name(mt->current_fx, FALSE, FALSE);
1712   char *layer_name;
1713   char *ltext, *tmp;
1714 
1715   if (has_perchannel_multiw(get_weed_filter(mt->current_fx))) {
1716     layer_name = get_track_name(mt, mt->current_track, mt->aud_track_selected);
1717     tmp = lives_strdup_printf(_("%s : parameters for %s"), fname, layer_name);
1718     ltext = lives_markup_escape_text(tmp, -1);
1719     lives_free(layer_name); lives_free(tmp);
1720   } else ltext = lives_strdup(fname);
1721   lives_free(fname);
1722 
1723   if (mt->framedraw) {
1724     char *someparms = lives_big_and_bold(_("<--- Some parameters can be altered by clicking / dragging in the Preview window"));
1725     char *tmp2 = lives_markup_escape_text(ltext, -1);
1726     tmp = lives_strdup_printf("%s\n%s",  tmp2, someparms);
1727     lives_free(ltext); lives_free(tmp2); lives_free(someparms);
1728     ltext = tmp;
1729   }
1730 
1731   return ltext;
1732 }
1733 
1734 
mt_get_effect_time(lives_mt * mt)1735 LIVES_GLOBAL_INLINE double mt_get_effect_time(lives_mt * mt) {
1736   return QUANT_TIME(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)));
1737 }
1738 
1739 
add_mt_param_box(lives_mt * mt)1740 boolean add_mt_param_box(lives_mt * mt) {
1741   // here we add a GUI box which will hold effect parameters
1742 
1743   // if we set keep_scale to TRUE, the current time slider is kept
1744   // this is necessary in case we need to update the parameters without resetting the current timeline value
1745 
1746   // returns TRUE if we have any parameters
1747 
1748   weed_plant_t *deinit_event;
1749 
1750   weed_timecode_t tc;
1751 
1752   double fx_start_time, fx_end_time;
1753   double cur_time = mt->ptr_time;
1754 
1755   char *ltext;
1756 
1757   boolean res = FALSE;
1758   int dph = widget_opts.packing_height;
1759   int dbw = widget_opts.border_width;
1760 
1761   tc = get_event_timecode((weed_plant_t *)mt->init_event);
1762   deinit_event = weed_get_plantptr_value(mt->init_event, WEED_LEAF_DEINIT_EVENT, NULL);
1763 
1764   fx_start_time = tc / TICKS_PER_SECOND_DBL;
1765   fx_end_time = get_event_timecode(deinit_event) / TICKS_PER_SECOND_DBL;
1766 
1767   if (mt->fx_box) {
1768     lives_widget_destroy(mt->fx_box);
1769   }
1770 
1771   mt->fx_box = lives_vbox_new(FALSE, 0);
1772 
1773   if (mt->fx_params_label) {
1774     lives_widget_destroy(mt->fx_params_label);
1775   }
1776 
1777   mt->fx_params_label = lives_label_new("");
1778   lives_widget_apply_theme2(mt->fx_params_label, LIVES_WIDGET_STATE_NORMAL, TRUE);
1779   lives_box_pack_start(LIVES_BOX(mt->fx_base_box), mt->fx_params_label, FALSE, TRUE, widget_opts.packing_height);
1780 
1781   lives_box_pack_end(LIVES_BOX(mt->fx_base_box), mt->fx_box, TRUE, TRUE, 0);
1782 
1783   lives_signal_handlers_block_by_func(mt->node_spinbutton, (livespointer)on_node_spin_value_changed, (livespointer)mt);
1784   lives_spin_button_configure(LIVES_SPIN_BUTTON(mt->node_spinbutton), cur_time - fx_start_time, 0.,
1785                               fx_end_time - fx_start_time, 1. / mt->fps, 10. / mt->fps);
1786 
1787   lives_signal_handlers_unblock_by_func(mt->node_spinbutton, (livespointer)on_node_spin_value_changed, (livespointer)mt);
1788 
1789   widget_opts.packing_height = 2. * widget_opts.scale;
1790   widget_opts.border_width = 2. * widget_opts.scale;
1791   res = make_param_box(LIVES_VBOX(mt->fx_box), mt->current_rfx);
1792   widget_opts.packing_height = dph;
1793   widget_opts.border_width = dbw;
1794 
1795   ltext = mt_params_label(mt);
1796 
1797   widget_opts.mnemonic_label = FALSE;
1798   widget_opts.justify = LIVES_JUSTIFY_CENTER;
1799   widget_opts.use_markup = TRUE;
1800   lives_label_set_text(LIVES_LABEL(mt->fx_params_label), ltext);
1801   widget_opts.use_markup = FALSE;
1802   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
1803   widget_opts.mnemonic_label = TRUE;
1804 
1805   lives_free(ltext);
1806 
1807   lives_widget_show_all(mt->fx_base_box);
1808 
1809   if (!res) {
1810     lives_widget_hide(mt->apply_fx_button);
1811     lives_widget_hide(mt->resetp_button);
1812     lives_widget_hide(mt->del_node_button);
1813     lives_widget_hide(mt->prev_node_button);
1814     lives_widget_hide(mt->next_node_button);
1815   }
1816 
1817   mt->prev_fx_time = mt_get_effect_time(mt);
1818 
1819   if (!mt->sel_locked) {
1820     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_start), fx_start_time);
1821     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_end), fx_end_time);
1822   }
1823   return res;
1824 }
1825 
1826 
get_block_from_time(LiVESWidget * eventbox,double time,lives_mt * mt)1827 static track_rect *get_block_from_time(LiVESWidget * eventbox, double time, lives_mt * mt) {
1828   // return block (track_rect) at seconds time in eventbox
1829   weed_timecode_t tc = time * TICKS_PER_SECOND;
1830   track_rect *block;
1831 
1832   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
1833   tc = q_gint64(tc, mt->fps);
1834 
1835   while (block) {
1836     if (get_event_timecode(block->start_event) > tc) return NULL;
1837     if (q_gint64(get_event_timecode(block->end_event) + (!is_audio_eventbox(eventbox))*TICKS_PER_SECOND_DBL / mt->fps,
1838                  mt->fps) > tc) break;
1839     block = block->next;
1840   }
1841   return block;
1842 }
1843 
1844 
track_to_channel(weed_plant_t * ievent,int track)1845 static int track_to_channel(weed_plant_t *ievent, int track) {
1846   // given an init_event and a track, we check to see which (if any) channel the track is mapped to
1847 
1848   // if track is not mapped, we return -1
1849 
1850   // note that a track could be mapped to multiple channels; we return only the first instance we find
1851 
1852   int *in_tracks;
1853   int *carray = NULL;
1854   int nc = 0, rpts, ntracks;
1855 
1856   register int i;
1857 
1858   in_tracks = weed_get_int_array_counted(ievent, WEED_LEAF_IN_TRACKS, &ntracks);
1859   if (ntracks == 0) return -1;
1860 
1861   carray = weed_get_int_array_counted(ievent, WEED_LEAF_IN_COUNT, &nc);
1862 
1863   for (i = 0; i < ntracks; i++) {
1864     rpts = nc < i ? 1 : carray[i];
1865     if (in_tracks[i] + rpts > track) {
1866       lives_free(in_tracks);
1867       lives_freep((void **)&carray);
1868       return i;
1869     }
1870     track -= rpts - 1;
1871   }
1872   lives_free(in_tracks);
1873   lives_freep((void **)&carray);
1874   return -1;
1875 }
1876 
1877 
get_track_index(lives_mt * mt,weed_timecode_t tc)1878 static boolean get_track_index(lives_mt * mt, weed_timecode_t tc) {
1879   // set mt->track_index to the in_channel index of mt->current_track in WEED_LEAF_IN_TRACKS in mt->init_event
1880   // set -1 if there is no frame for that in_channel, or if mt->current_track lies outside the WEED_LEAF_IN_TRACKS of mt->init_event
1881 
1882   // return TRUE if mt->fx_box is redrawn
1883 
1884   int *clips, *in_tracks, numtracks;
1885   weed_plant_t *event = get_frame_event_at(mt->event_list, tc, NULL, TRUE);
1886 
1887   boolean retval = FALSE;
1888 
1889   int num_in_tracks;
1890   int opwidth, opheight;
1891   int track_index = mt->track_index;
1892   int chindx, i;
1893 
1894 
1895   mt->track_index = -1;
1896 
1897   if (!event || !mt->play_width || !mt->play_height) return retval;
1898 
1899   opwidth = mainw->files[mt->render_file]->hsize;
1900   opheight = mainw->files[mt->render_file]->vsize;
1901   calc_maxspect(mt->play_width, mt->play_height, &opwidth, &opheight);
1902 
1903   clips = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &numtracks);
1904 
1905   chindx = track_to_channel(mt->init_event, mt->current_track);
1906 
1907   if (mt->current_track < numtracks && clips[mt->current_track] < 1 &&
1908       (!mt->current_rfx || !mt->init_event || !mt->current_rfx->source || chindx == -1 ||
1909        !is_audio_channel_in((weed_plant_t *)mt->current_rfx->source, chindx))) {
1910     if (track_index != -1 && mt->fx_box) {
1911       add_mt_param_box(mt);
1912       retval = TRUE;
1913     }
1914     lives_free(clips);
1915     return retval;
1916   }
1917 
1918   in_tracks = weed_get_int_array_counted(mt->init_event, WEED_LEAF_IN_TRACKS, &num_in_tracks);
1919   if (num_in_tracks > 0) {
1920     for (i = 0; i < num_in_tracks; i++) {
1921       if (in_tracks[i] == mt->current_track) {
1922         mt->track_index = i;
1923         if (track_index == -1 && mt->fx_box) {
1924           add_mt_param_box(mt);
1925           retval = TRUE;
1926         }
1927         break;
1928       }
1929     }
1930     lives_free(in_tracks);
1931   }
1932   lives_free(clips);
1933   if (track_index != -1 && mt->track_index == -1 && mt->fx_box) {
1934     add_mt_param_box(mt);
1935     retval = TRUE;
1936   }
1937   return retval;
1938 }
1939 
1940 
track_select(lives_mt * mt)1941 void track_select(lives_mt * mt) {
1942   LiVESWidget *labelbox, *label, *hbox, *dummy, *ahbox, *arrow, *eventbox, *oeventbox, *checkbutton = NULL;
1943   LiVESList *list;
1944   weed_timecode_t tc;
1945   int hidden = 0;
1946   register int i;
1947 
1948   if (!prefs->show_gui) return;
1949 
1950   if (mt->current_track < 0) {
1951     // back aud sel
1952     lives_widget_set_sensitive(mt->select_track, FALSE);
1953     lives_widget_set_sensitive(mt->rename_track, FALSE);
1954     lives_widget_set_sensitive(mt->insert, FALSE);
1955 
1956     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->select_track), FALSE);
1957 
1958     lives_widget_set_sensitive(mt->cback_audio, FALSE);
1959     lives_widget_set_sensitive(mt->audio_insert, mt->file_selected > 0 &&
1960                                mainw->files[mt->file_selected]->achans > 0 &&
1961                                mainw->files[mt->file_selected]->laudio_time > 0.);
1962   } else {
1963     // vid sel
1964     lives_widget_set_sensitive(mt->select_track, TRUE);
1965     lives_widget_set_sensitive(mt->rename_track, TRUE);
1966     lives_widget_set_sensitive(mt->cback_audio, TRUE);
1967 
1968     lives_widget_set_sensitive(mt->insert, mt->file_selected > 0 && mainw->files[mt->file_selected]->frames > 0);
1969     lives_widget_set_sensitive(mt->adjust_start_end, mt->file_selected > 0);
1970     lives_widget_set_sensitive(mt->audio_insert, FALSE);
1971   }
1972 
1973   if (palette->style & STYLE_1) {
1974     if (CURRENT_CLIP_HAS_AUDIO) {
1975       i = 0;
1976       for (list = mt->audio_draws; list; list = list->next, i++) {
1977         eventbox = (LiVESWidget *)list->data;
1978         if ((oeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "owner")))
1979           hidden = !LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(oeventbox), "expanded"));
1980         if (hidden == 0) hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY));
1981         if (hidden == 0) {
1982           labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "labelbox");
1983           label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label");
1984           dummy = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "dummy");
1985           ahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "ahbox");
1986           hbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "hbox");
1987           arrow = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "arrow");
1988           if (mt->current_track == i - mt->opts.back_audio_tracks && (mt->current_track < 0 || mt->aud_track_selected)) {
1989             // audio track is selected
1990             if (labelbox) {
1991               lives_widget_set_bg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
1992               lives_widget_set_fg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
1993             }
1994             if (ahbox) {
1995               lives_widget_set_bg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
1996               lives_widget_set_fg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
1997             }
1998             lives_widget_set_bg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
1999             lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
2000             lives_widget_set_bg_color(dummy, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
2001             lives_widget_set_fg_color(dummy, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
2002             lives_widget_set_bg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
2003             lives_widget_set_fg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
2004 
2005             lives_widget_set_sensitive(mt->jumpback,
2006                                        lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks") != NULL);
2007             lives_widget_set_sensitive(mt->jumpnext,
2008                                        lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks") != NULL);
2009           } else {
2010             if (labelbox) {
2011               lives_widget_set_bg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2012               lives_widget_set_fg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2013             }
2014             if (ahbox) {
2015               lives_widget_set_bg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2016               lives_widget_set_fg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2017             }
2018             lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2019             lives_widget_set_bg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2020             lives_widget_set_fg_color(dummy, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2021             lives_widget_set_bg_color(dummy, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2022             lives_widget_set_fg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2023             lives_widget_set_bg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2024 	    // *INDENT-OFF*
2025           }}}}}
2026   // *INDENT-ON*
2027   i = 0;
2028   for (list = mt->video_draws; list; list = list->next, i++) {
2029     eventbox = (LiVESWidget *)list->data;
2030     hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY));
2031     if (hidden == 0) {
2032       labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "labelbox");
2033       label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label");
2034       hbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "hbox");
2035       ahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "ahbox");
2036       arrow = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "arrow");
2037       checkbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "checkbutton");
2038       if (i == mt->current_track) {
2039         if (palette->style & STYLE_1) {
2040           if (!mt->aud_track_selected) {
2041             if (labelbox) {
2042               lives_widget_set_bg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
2043               lives_widget_set_fg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
2044             }
2045             if (ahbox) {
2046               lives_widget_set_bg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
2047               lives_widget_set_fg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
2048             }
2049             lives_widget_set_bg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
2050             lives_widget_set_bg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
2051             lives_widget_set_bg_color(checkbutton, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
2052             lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
2053             lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
2054             lives_widget_set_fg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
2055 
2056             lives_widget_set_sensitive(mt->jumpback,
2057                                        lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks") != NULL);
2058             lives_widget_set_sensitive(mt->jumpnext,
2059                                        lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks") != NULL);
2060           } else {
2061             if (labelbox) {
2062               lives_widget_set_bg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2063               lives_widget_set_fg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2064             }
2065             if (ahbox) {
2066               lives_widget_set_bg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2067               lives_widget_set_fg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2068             }
2069             lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2070             lives_widget_set_fg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2071             lives_widget_set_bg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2072             lives_widget_set_bg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2073             lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2074             lives_widget_set_bg_color(checkbutton, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2075           }
2076         }
2077 
2078 #ifdef ENABLE_GIW
2079         if ((prefs->lamp_buttons && !giw_led_get_mode(GIW_LED(checkbutton))) || (!prefs->lamp_buttons &&
2080 #else
2081         if (
2082 #endif
2083             !lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(checkbutton)))
2084 #ifdef ENABLE_GIW
2085            )
2086 #endif
2087 #if 0
2088         }
2089 #endif
2090       {
2091         // set other widgets
2092         if (lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(mt->select_track))) {
2093           lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->select_track), FALSE);
2094         } else on_seltrack_activate(LIVES_MENU_ITEM(mt->select_track), mt);
2095       } else {
2096         if (!lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(mt->select_track)))
2097           lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->select_track), TRUE);
2098         else on_seltrack_activate(LIVES_MENU_ITEM(mt->select_track), mt);
2099       }
2100     } else {
2101       if (palette->style & STYLE_1) {
2102         if (labelbox) {
2103           lives_widget_set_bg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2104           lives_widget_set_fg_color(labelbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2105         }
2106         if (ahbox) {
2107           lives_widget_set_bg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2108           lives_widget_set_fg_color(ahbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2109         }
2110         lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2111         lives_widget_set_fg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2112         lives_widget_set_bg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2113         lives_widget_set_bg_color(arrow, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2114         lives_widget_set_bg_color(checkbutton, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2115         lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
2116 	  // *INDENT-OFF*
2117         }}}}
2118   // *INDENT-ON*
2119 
2120 if (mt->poly_state == POLY_FX_STACK) polymorph(mt, POLY_FX_STACK);
2121 else if (mt->current_rfx && mt->init_event && mt->poly_state == POLY_PARAMS &&
2122          weed_plant_has_leaf(mt->init_event, WEED_LEAF_IN_TRACKS)) {
2123   boolean xx;
2124   boolean interp = TRUE;
2125   weed_timecode_t init_tc = get_event_timecode(mt->init_event);
2126   tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) * TICKS_PER_SECOND_DBL + init_tc, mt->fps);
2127 
2128   // must be done in this order: interpolate, update, preview
2129   xx = get_track_index(mt, tc);
2130   if (mt->track_index != -1) {
2131     for (i = 0; i < mt->current_rfx->num_params; i++) {
2132       // if we are just switching tracks within the same effect, without changing the time,
2133       // and we have unapplied changes, we don't want to interpolate
2134       // otherwise we will lose those changes
2135       if (mt->current_rfx->params[i].changed) {
2136         interp = FALSE;
2137         break;
2138       }
2139     }
2140     if (mt->current_track >= 0) {
2141       // interpolate values ONLY if there are no unapplied changes (e.g. the time was altered)
2142       if (interp) interpolate_params((weed_plant_t *)mt->current_rfx->source, pchain, tc);
2143     }
2144     if (!xx) {
2145       // the param box was redrawn
2146       boolean aprev = mt->opts.fx_auto_preview;
2147       //mt->opts.fx_auto_preview = FALSE;
2148       mainw->block_param_updates = TRUE;
2149       mt->current_rfx->needs_reinit = FALSE;
2150       mt->current_rfx->flags |= RFX_FLAGS_NO_RESET;
2151       update_visual_params(mt->current_rfx, FALSE);
2152       mainw->block_param_updates = FALSE;
2153       if (mt->current_rfx->needs_reinit) {
2154         weed_reinit_effect(mt->current_rfx->source, TRUE);
2155         mt->current_rfx->needs_reinit = FALSE;
2156       }
2157       mt->current_rfx->flags ^= RFX_FLAGS_NO_RESET;
2158       mt->opts.fx_auto_preview = aprev;
2159       activate_mt_preview(mt);
2160     } else mt_show_current_frame(mt, FALSE);
2161   } else polymorph(mt, POLY_FX_STACK);
2162 }
2163 }
2164 
2165 
show_track_info(lives_mt * mt,LiVESWidget * eventbox,int track,double timesecs)2166 static void show_track_info(lives_mt * mt, LiVESWidget * eventbox, int track, double timesecs) {
2167   char *tmp, *tmp1;
2168   track_rect *block = get_block_from_time(eventbox, timesecs, mt);
2169   int filenum;
2170 
2171   clear_context(mt);
2172   if (!is_audio_eventbox(eventbox)) add_context_label
2173     (mt, (tmp = lives_markup_printf_escaped
2174                 (_("Current track: %s (layer %d)\n"),
2175                  lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox),
2176                      "track_name"), track)));
2177   else {
2178     if (track == -1) add_context_label(mt, (tmp = (_("Current track: Backing audio\n"))));
2179     else add_context_label(mt, (tmp =
2180                                     lives_markup_printf_escaped(_("Current track: Layer %d audio\n"), track)));
2181   }
2182   lives_free(tmp);
2183   add_context_label(mt, (tmp = lives_strdup_printf(_("%.2f sec.\n"), timesecs)));
2184   lives_free(tmp);
2185   if (block) {
2186     if (!is_audio_eventbox(eventbox)) filenum = get_frame_event_clip(block->start_event, track);
2187     else filenum = get_audio_frame_clip(block->start_event, track);
2188     add_context_label(mt, (tmp = lives_markup_printf_escaped(_("Source: %s"),
2189                                  (tmp1 = get_menu_name(mainw->files[filenum], FALSE)))));
2190     lives_free(tmp);
2191     lives_free(tmp1);
2192     add_context_label(mt, (_("Right click for context menu.\n")));
2193   }
2194   add_context_label(mt, (_("Double click on a block\nto select it.")));
2195 }
2196 
2197 
atrack_ebox_pressed(LiVESWidget * labelbox,LiVESXEventButton * event,livespointer user_data)2198 static boolean atrack_ebox_pressed(LiVESWidget * labelbox, LiVESXEventButton * event, livespointer user_data) {
2199   lives_mt *mt = (lives_mt *)user_data;
2200   int current_track = mt->current_track;
2201   if (!LIVES_IS_INTERACTIVE) return FALSE;
2202   mt->current_track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(labelbox), "layer_number"));
2203   if (current_track != mt->current_track) mt->fm_edit_event = NULL;
2204   mt->aud_track_selected = TRUE;
2205   track_select(mt);
2206   show_track_info(mt, (LiVESWidget *)lives_list_nth_data(mt->audio_draws, mt->current_track + mt->opts.back_audio_tracks),
2207                   mt->current_track, mt->ptr_time);
2208   return FALSE;
2209 }
2210 
2211 
track_ebox_pressed(LiVESWidget * labelbox,LiVESXEventButton * event,livespointer user_data)2212 static boolean track_ebox_pressed(LiVESWidget * labelbox, LiVESXEventButton * event, livespointer user_data) {
2213   lives_mt *mt = (lives_mt *)user_data;
2214   int current_track = mt->current_track;
2215   if (!LIVES_IS_INTERACTIVE) return FALSE;
2216   mt->current_track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(labelbox), "layer_number"));
2217   if (current_track != mt->current_track) mt->fm_edit_event = NULL;
2218   mt->aud_track_selected = FALSE;
2219   track_select(mt);
2220   show_track_info(mt, (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track), mt->current_track, mt->ptr_time);
2221   return FALSE;
2222 }
2223 
2224 
on_mt_timeline_scroll(LiVESWidget * widget,LiVESXEventScroll * event,livespointer user_data)2225 static boolean on_mt_timeline_scroll(LiVESWidget * widget, LiVESXEventScroll * event, livespointer user_data) {
2226   // scroll timeline up/down with mouse wheel
2227   lives_mt *mt = (lives_mt *)user_data;
2228 
2229   int cval;
2230 
2231   //if (!lives_window_has_toplevel_focus(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET))) return FALSE;
2232 
2233   LiVESXModifierType kstate = (LiVESXModifierType)event->state;
2234   if ((kstate & LIVES_DEFAULT_MOD_MASK) == LIVES_CONTROL_MASK) return on_mouse_scroll(widget, event, user_data);
2235 
2236   cval = lives_adjustment_get_value(lives_range_get_adjustment(LIVES_RANGE(mt->scrollbar)));
2237 
2238   if (lives_get_scroll_direction(event) == LIVES_SCROLL_UP) {
2239     if (--cval < 0) return FALSE;
2240   } else if (lives_get_scroll_direction(event) == LIVES_SCROLL_DOWN) {
2241     if (++cval >= mt->num_video_tracks) return FALSE;
2242   }
2243 
2244   lives_range_set_value(LIVES_RANGE(mt->scrollbar), cval);
2245 
2246   return FALSE;
2247 }
2248 
2249 
get_top_track_for(lives_mt * mt,int track)2250 static int get_top_track_for(lives_mt * mt, int track) {
2251   // find top track such that all of track fits at the bottom
2252 
2253   LiVESWidget *eventbox;
2254   LiVESList *vdraw;
2255   int extras = prefs->max_disp_vtracks - 1;
2256   int hidden, expanded;
2257 
2258   if (mt->opts.back_audio_tracks > 0 && !mt->audio_draws) mt->opts.back_audio_tracks = 0;
2259   if (mainw->files[mt->render_file]->achans > 0 && mt->opts.back_audio_tracks > 0) {
2260     eventbox = (LiVESWidget *)mt->audio_draws->data;
2261     hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY));
2262     if (!hidden) {
2263       extras--;
2264       expanded = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"));
2265       if (expanded) {
2266         extras -= mainw->files[mt->render_file]->achans;
2267       }
2268     }
2269   }
2270 
2271   if (extras < 0) return track;
2272 
2273   vdraw = lives_list_nth(mt->video_draws, track);
2274   eventbox = (LiVESWidget *)vdraw->data;
2275   expanded = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"));
2276   if (expanded) {
2277     eventbox = (LiVESWidget *)(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "atrack"));
2278     extras--;
2279     expanded = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"));
2280     if (expanded) {
2281       extras -= mainw->files[mt->render_file]->achans;
2282     }
2283   }
2284 
2285   if (extras < 0) return track;
2286 
2287   vdraw = vdraw->prev;
2288 
2289   while (vdraw) {
2290     eventbox = (LiVESWidget *)vdraw->data;
2291     hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY))&TRACK_I_HIDDEN_USER;
2292     expanded = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"));
2293     extras--;
2294     if (expanded) {
2295       eventbox = (LiVESWidget *)(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "atrack"));
2296       extras--;
2297       expanded = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"));
2298       if (expanded) {
2299         extras -= mainw->files[mt->render_file]->achans;
2300       }
2301     }
2302     if (extras < 0) break;
2303     vdraw = vdraw->prev;
2304     track--;
2305   }
2306 
2307   if (track < 0) track = 0;
2308   return track;
2309 }
2310 
2311 
redraw_all_event_boxes(lives_mt * mt)2312 static void redraw_all_event_boxes(lives_mt * mt) {
2313   LiVESList *slist;
2314 
2315   slist = mt->audio_draws;
2316   while (slist) {
2317     redraw_eventbox(mt, (LiVESWidget *)slist->data);
2318     slist = slist->next;
2319   }
2320 
2321   slist = mt->video_draws;
2322   while (slist) {
2323     redraw_eventbox(mt, (LiVESWidget *)slist->data);
2324     slist = slist->next;
2325   }
2326   paint_lines(mt, mt->ptr_time, TRUE, NULL);
2327 }
2328 
2329 
expose_paintlines(LiVESWidget * widget,lives_painter_t * cr,livespointer data)2330 static boolean expose_paintlines(LiVESWidget * widget, lives_painter_t *cr, livespointer data) {
2331   int offset = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(widget),
2332                                     "has_line"));
2333 
2334   if (lives_painter_set_operator(cr, LIVES_PAINTER_OPERATOR_DIFFERENCE))
2335     lives_painter_set_source_rgb(cr, 1., 1., 1.);
2336   else
2337     lives_painter_set_source_rgb(cr, 0., 0., 0.);
2338 
2339   lives_painter_set_line_width(cr, 1.);
2340   lives_painter_rectangle(cr, offset, 0., 1., lives_widget_get_allocation_height(widget));
2341   lives_painter_fill(cr);
2342 
2343   return FALSE;
2344 }
2345 
2346 
scroll_tracks(lives_mt * mt,int top_track,boolean set_value)2347 void scroll_tracks(lives_mt * mt, int top_track, boolean set_value) {
2348   LiVESList *vdraws = mt->video_draws;
2349   LiVESList *table_children, *xlist;
2350 
2351   LiVESWidget *eventbox;
2352   LiVESWidget *label;
2353   LiVESWidget *dummy;
2354   LiVESWidget *arrow;
2355   LiVESWidget *checkbutton;
2356   LiVESWidget *labelbox;
2357   LiVESWidget *hbox;
2358   LiVESWidget *ahbox;
2359   LiVESWidget *xeventbox, *aeventbox;
2360 
2361   LiVESWidgetColor col;
2362 
2363   boolean expanded;
2364 
2365   int rows = 0;
2366   int aud_tracks = 0;
2367   int hidden;
2368 
2369   // only for gtk+ 2 (I think)
2370   lives_rgba_to_widget_color(&col, &palette->mt_evbox);
2371 
2372   lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->vadjustment), (double)prefs->max_disp_vtracks);
2373   lives_adjustment_set_upper(LIVES_ADJUSTMENT(mt->vadjustment), (double)(mt->num_video_tracks * 2 - 1));
2374 
2375   if (set_value)
2376     lives_adjustment_set_value(LIVES_ADJUSTMENT(mt->vadjustment), (double)top_track);
2377 
2378   if (top_track < 0) top_track = 0;
2379   if (top_track >= mt->num_video_tracks) top_track = mt->num_video_tracks - 1;
2380 
2381   mt->top_track = top_track;
2382 
2383   // first set all tracks to hidden
2384   while (vdraws) {
2385     eventbox = (LiVESWidget *)vdraws->data;
2386     hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY));
2387     hidden |= TRACK_I_HIDDEN_SCROLLED;
2388     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(hidden));
2389 
2390     aeventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "atrack"));
2391 
2392     if (aeventbox) {
2393       hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), HIDDEN_KEY));
2394       hidden |= TRACK_I_HIDDEN_SCROLLED;
2395       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(hidden));
2396 
2397       xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "achan0");
2398 
2399       if (xeventbox) {
2400         hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY));
2401         hidden |= TRACK_I_HIDDEN_SCROLLED;
2402         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(hidden));
2403       }
2404 
2405       xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "achan1");
2406 
2407       if (xeventbox) {
2408         hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY));
2409         hidden |= TRACK_I_HIDDEN_SCROLLED;
2410         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(hidden));
2411       }
2412     }
2413 
2414     vdraws = vdraws->next;
2415   }
2416 
2417   if (mt->timeline_table) {
2418     lives_widget_destroy(mt->timeline_table);
2419   }
2420 
2421   mt->timeline_table = lives_table_new(prefs->max_disp_vtracks, TIMELINE_TABLE_COLUMNS, TRUE);
2422   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(mt->timeline_table), "has_line", LIVES_INT_TO_POINTER(-1));
2423 
2424   lives_container_add(LIVES_CONTAINER(mt->tl_eventbox), mt->timeline_table);
2425 
2426   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(mt->timeline_table), LIVES_WIDGET_EXPOSE_EVENT,
2427                                   LIVES_GUI_CALLBACK(expose_paintlines),
2428                                   (livespointer)mt);
2429 
2430   lives_table_set_row_spacings(LIVES_TABLE(mt->timeline_table), widget_opts.packing_height * widget_opts.scale);
2431   lives_table_set_col_spacings(LIVES_TABLE(mt->timeline_table), 0);
2432 
2433   lives_widget_set_vexpand(mt->timeline_table, FALSE);
2434 
2435   if (mt->opts.back_audio_tracks > 0 && !mt->audio_draws) mt->opts.back_audio_tracks = 0;
2436 
2437   if (mainw->files[mt->render_file]->achans > 0 && mt->opts.back_audio_tracks > 0) {
2438     // show our float audio
2439     if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), HIDDEN_KEY)) == 0) {
2440       aud_tracks++;
2441 
2442       expanded = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "expanded"));
2443 
2444       label = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "label")));
2445       dummy = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "dummy")));
2446       arrow = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "arrow")));
2447 
2448       labelbox = lives_event_box_new();
2449       hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
2450       ahbox = lives_event_box_new();
2451 
2452       lives_container_add(LIVES_CONTAINER(labelbox), hbox);
2453       lives_box_pack_start(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
2454       lives_container_add(LIVES_CONTAINER(ahbox), arrow);
2455 
2456       lives_table_attach(LIVES_TABLE(mt->timeline_table), dummy, 0, 2, 0, 1, LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2457       lives_table_attach(LIVES_TABLE(mt->timeline_table), labelbox, 2, 6, 0, 1, LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2458       lives_table_attach(LIVES_TABLE(mt->timeline_table), ahbox, 6, 7, 0, 1, LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2459 
2460       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "labelbox", labelbox);
2461       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "ahbox", ahbox);
2462       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ahbox), "eventbox", (livespointer)mt->audio_draws->data);
2463       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(labelbox), "layer_number", LIVES_INT_TO_POINTER(-1));
2464 
2465       lives_signal_connect(LIVES_GUI_OBJECT(labelbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2466                            LIVES_GUI_CALLBACK(atrack_ebox_pressed), (livespointer)mt);
2467 
2468       lives_signal_connect(LIVES_GUI_OBJECT(ahbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2469                            LIVES_GUI_CALLBACK(track_arrow_pressed), (livespointer)mt);
2470 
2471       lives_table_attach(LIVES_TABLE(mt->timeline_table), (LiVESWidget *)mt->audio_draws->data, 7, TIMELINE_TABLE_COLUMNS, 0, 1,
2472                          (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL), (LiVESAttachOptions)(0), 0, 0);
2473 
2474       lives_widget_set_valign((LiVESWidget *)mt->audio_draws->data, LIVES_ALIGN_CENTER);
2475 
2476       lives_signal_connect(LIVES_GUI_OBJECT(mt->audio_draws->data), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2477                            LIVES_GUI_CALLBACK(on_track_click), (livespointer)mt);
2478       lives_signal_connect(LIVES_GUI_OBJECT(mt->audio_draws->data), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
2479                            LIVES_GUI_CALLBACK(on_track_release), (livespointer)mt);
2480 
2481       lives_widget_set_bg_color(LIVES_WIDGET(mt->audio_draws->data), LIVES_WIDGET_STATE_NORMAL, &col);
2482       lives_widget_set_app_paintable(LIVES_WIDGET(mt->audio_draws->data), TRUE);
2483       lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->audio_draws->data), LIVES_WIDGET_EXPOSE_EVENT,
2484                                 LIVES_GUI_CALLBACK(expose_track_event), (livespointer)mt);
2485 
2486       if (expanded) {
2487         xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "achan0");
2488 
2489         label = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "label")));
2490         labelbox = lives_event_box_new();
2491         hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
2492         lives_container_add(LIVES_CONTAINER(labelbox), hbox);
2493         lives_box_pack_end(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
2494 
2495         lives_table_attach(LIVES_TABLE(mt->timeline_table), labelbox, 2, 6, 1, 2, LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2496         lives_table_attach(LIVES_TABLE(mt->timeline_table), xeventbox, 7, TIMELINE_TABLE_COLUMNS, 1, 2,
2497                            (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL),
2498                            (LiVESAttachOptions)(0), 0, 0);
2499 
2500         lives_widget_set_valign(xeventbox, LIVES_ALIGN_CENTER);
2501 
2502         lives_widget_set_bg_color(xeventbox, LIVES_WIDGET_STATE_NORMAL, &col);
2503         lives_widget_set_app_paintable(xeventbox, TRUE);
2504         lives_signal_connect(LIVES_GUI_OBJECT(xeventbox), LIVES_WIDGET_EXPOSE_EVENT,
2505                              LIVES_GUI_CALLBACK(mt_expose_audtrack_event),
2506                              (livespointer)mt);
2507 
2508         if (mainw->files[mt->render_file]->achans > 1) {
2509           xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "achan1");
2510 
2511           label = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "label")));
2512           labelbox = lives_event_box_new();
2513           hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
2514           lives_container_add(LIVES_CONTAINER(labelbox), hbox);
2515           lives_box_pack_end(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
2516 
2517           lives_table_attach(LIVES_TABLE(mt->timeline_table), labelbox, 2, 6, 2, 3, LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2518           lives_table_attach(LIVES_TABLE(mt->timeline_table), xeventbox, 7, TIMELINE_TABLE_COLUMNS, 2, 3,
2519                              (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL),
2520                              (LiVESAttachOptions)(0), 0, 0);
2521 
2522           lives_widget_set_valign(xeventbox, LIVES_ALIGN_CENTER);
2523 
2524           lives_widget_set_bg_color(xeventbox, LIVES_WIDGET_STATE_NORMAL, &col);
2525           lives_widget_set_app_paintable(xeventbox, TRUE);
2526           lives_signal_connect(LIVES_GUI_OBJECT(xeventbox), LIVES_WIDGET_EXPOSE_EVENT,
2527                                LIVES_GUI_CALLBACK(mt_expose_audtrack_event),
2528                                (livespointer)mt);
2529 	  // *INDENT-OFF*
2530         }
2531         aud_tracks += mainw->files[mt->render_file]->achans;
2532       }}}
2533   // *INDENT-ON*
2534 
2535   lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->vadjustment),
2536                                  (double)((int)(lives_adjustment_get_page_size(LIVES_ADJUSTMENT(mt->vadjustment))) - aud_tracks));
2537 
2538   vdraws = lives_list_nth(mt->video_draws, top_track);
2539 
2540   rows += aud_tracks;
2541 
2542   while (vdraws && rows < prefs->max_disp_vtracks) {
2543     eventbox = (LiVESWidget *)vdraws->data;
2544 
2545     hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY))&TRACK_I_HIDDEN_USER;
2546     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(hidden));
2547 
2548     if (hidden == 0) {
2549       label = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label")));
2550       arrow = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "arrow")));
2551       checkbutton = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "checkbutton")));
2552       labelbox = lives_event_box_new();
2553       hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
2554       ahbox = lives_event_box_new();
2555 
2556       lives_widget_set_bg_color(LIVES_WIDGET(eventbox), LIVES_WIDGET_STATE_NORMAL, &col);
2557 
2558 #ifdef ENABLE_GIW
2559       if (prefs->lamp_buttons) {
2560 #if GTK_CHECK_VERSION(3, 0, 0)
2561         giw_led_set_rgba(GIW_LED(checkbutton), palette->light_green, palette->dark_red);
2562 #else
2563         giw_led_set_colors(GIW_LED(checkbutton), palette->light_green, palette->dark_red);
2564 #endif
2565       }
2566 #endif
2567 
2568       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(labelbox), "layer_number",
2569                                    LIVES_INT_TO_POINTER(LIVES_POINTER_TO_INT
2570                                        (lives_widget_object_get_data
2571                                         (LIVES_WIDGET_OBJECT(eventbox), "layer_number"))));
2572 
2573       lives_container_add(LIVES_CONTAINER(labelbox), hbox);
2574       lives_box_pack_start(LIVES_BOX(hbox), checkbutton, FALSE, FALSE, widget_opts.border_width);
2575       lives_box_pack_start(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
2576       lives_container_add(LIVES_CONTAINER(ahbox), arrow);
2577 
2578       lives_table_attach(LIVES_TABLE(mt->timeline_table), labelbox, 0, 6,
2579                          rows, rows + 1, LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2580       lives_table_attach(LIVES_TABLE(mt->timeline_table), ahbox, 6, 7, rows, rows + 1, LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2581 
2582       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "labelbox", labelbox);
2583       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "hbox", hbox);
2584       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "ahbox", ahbox);
2585       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ahbox), "eventbox", eventbox);
2586 
2587       lives_table_attach(LIVES_TABLE(mt->timeline_table), eventbox, 7, TIMELINE_TABLE_COLUMNS, rows, rows + 1,
2588                          (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL), (LiVESAttachOptions)(0), 0, 0);
2589 
2590       lives_widget_set_valign(eventbox, LIVES_ALIGN_CENTER);
2591 
2592       if (!prefs->lamp_buttons) {
2593         lives_signal_sync_connect_after(LIVES_GUI_OBJECT(checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2594                                         LIVES_GUI_CALLBACK(on_seltrack_toggled), mt);
2595       } else {
2596         lives_signal_sync_connect_after(LIVES_GUI_OBJECT(checkbutton), LIVES_WIDGET_MODE_CHANGED_SIGNAL,
2597                                         LIVES_GUI_CALLBACK(on_seltrack_toggled), mt);
2598       }
2599 
2600       lives_signal_connect(LIVES_GUI_OBJECT(labelbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2601                            LIVES_GUI_CALLBACK(track_ebox_pressed), (livespointer)mt);
2602 
2603       lives_widget_set_bg_color(eventbox, LIVES_WIDGET_STATE_NORMAL, &col);
2604       lives_widget_set_app_paintable(eventbox, TRUE);
2605       lives_signal_sync_connect(LIVES_GUI_OBJECT(eventbox), LIVES_WIDGET_EXPOSE_EVENT,
2606                                 LIVES_GUI_CALLBACK(expose_track_event), (livespointer)mt);
2607 
2608       lives_signal_connect(LIVES_GUI_OBJECT(eventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2609                            LIVES_GUI_CALLBACK(on_track_click), (livespointer)mt);
2610       lives_signal_connect(LIVES_GUI_OBJECT(eventbox), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
2611                            LIVES_GUI_CALLBACK(on_track_release), (livespointer)mt);
2612 
2613       lives_signal_connect(LIVES_GUI_OBJECT(ahbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2614                            LIVES_GUI_CALLBACK(track_arrow_pressed), (livespointer)mt);
2615       rows++;
2616 
2617       if (rows == prefs->max_disp_vtracks) break;
2618 
2619       if (mt->opts.pertrack_audio && lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded")) {
2620 
2621         aeventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "atrack"));
2622 
2623         hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox),
2624                                       HIDDEN_KEY))&TRACK_I_HIDDEN_USER;
2625         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(hidden));
2626 
2627         if (hidden == 0) {
2628           lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->vadjustment),
2629                                          (double)((int)lives_adjustment_get_page_size(LIVES_ADJUSTMENT(mt->vadjustment)) - 1));
2630           expanded = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "expanded"));
2631 
2632           label = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "label")));
2633           dummy = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "dummy")));
2634           arrow = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "arrow")));
2635 
2636           labelbox = lives_event_box_new();
2637           hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
2638           ahbox = lives_event_box_new();
2639 
2640           lives_widget_set_bg_color(LIVES_WIDGET(aeventbox), LIVES_WIDGET_STATE_NORMAL, &col);
2641 
2642           lives_container_add(LIVES_CONTAINER(labelbox), hbox);
2643           lives_box_pack_start(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
2644           lives_container_add(LIVES_CONTAINER(ahbox), arrow);
2645 
2646           // for gtk+2.x have 0,2...5,7 ?
2647           lives_table_attach(LIVES_TABLE(mt->timeline_table), dummy, 0, 2, rows, rows + 1,
2648                              LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2649           lives_table_attach(LIVES_TABLE(mt->timeline_table), labelbox, 2, 6, rows, rows + 1,
2650                              LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2651           lives_table_attach(LIVES_TABLE(mt->timeline_table), ahbox, 6, 7, rows, rows + 1,
2652                              LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2653 
2654           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), "labelbox", labelbox);
2655           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), "hbox", hbox);
2656           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), "ahbox", ahbox);
2657           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ahbox), "eventbox", aeventbox);
2658           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(labelbox), "layer_number",
2659                                        LIVES_INT_TO_POINTER(LIVES_POINTER_TO_INT
2660                                            (lives_widget_object_get_data
2661                                             (LIVES_WIDGET_OBJECT(eventbox), "layer_number"))));
2662 
2663           lives_signal_connect(LIVES_GUI_OBJECT(labelbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2664                                LIVES_GUI_CALLBACK(atrack_ebox_pressed),
2665                                (livespointer)mt);
2666 
2667           lives_signal_connect(LIVES_GUI_OBJECT(ahbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2668                                LIVES_GUI_CALLBACK(track_arrow_pressed),
2669                                (livespointer)mt);
2670 
2671           lives_table_attach(LIVES_TABLE(mt->timeline_table), aeventbox, 7, TIMELINE_TABLE_COLUMNS, rows, rows + 1,
2672                              (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL), (LiVESAttachOptions)(0), 0, 0);
2673 
2674           lives_widget_set_valign(aeventbox, LIVES_ALIGN_CENTER);
2675 
2676           lives_signal_connect(LIVES_GUI_OBJECT(aeventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
2677                                LIVES_GUI_CALLBACK(on_track_click), (livespointer)mt);
2678           lives_signal_connect(LIVES_GUI_OBJECT(aeventbox), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
2679                                LIVES_GUI_CALLBACK(on_track_release), (livespointer)mt);
2680 
2681           lives_widget_set_app_paintable(aeventbox, TRUE);
2682           lives_signal_sync_connect(LIVES_GUI_OBJECT(aeventbox), LIVES_WIDGET_EXPOSE_EVENT,
2683                                     LIVES_GUI_CALLBACK(expose_track_event), (livespointer)mt);
2684           rows++;
2685 
2686           if (rows == prefs->max_disp_vtracks) break;
2687 
2688           if (expanded) {
2689             xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "achan0");
2690             hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY))
2691                      & TRACK_I_HIDDEN_USER;
2692             lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(hidden));
2693 
2694             if (hidden == 0) {
2695               label = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "label")));
2696               labelbox = lives_event_box_new();
2697               hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
2698               lives_widget_apply_theme(label, LIVES_WIDGET_STATE_NORMAL);
2699               lives_widget_apply_theme(labelbox, LIVES_WIDGET_STATE_NORMAL);
2700               lives_widget_apply_theme(hbox, LIVES_WIDGET_STATE_NORMAL);
2701               lives_container_add(LIVES_CONTAINER(labelbox), hbox);
2702               lives_box_pack_end(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
2703 
2704               lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->vadjustment),
2705                                              (double)((int)lives_adjustment_get_page_size(LIVES_ADJUSTMENT(mt->vadjustment))
2706                                                       - 1));
2707 
2708               lives_table_attach(LIVES_TABLE(mt->timeline_table),
2709                                  labelbox, 2, 6, rows, rows + 1, LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2710               lives_table_attach(LIVES_TABLE(mt->timeline_table), xeventbox, 7, TIMELINE_TABLE_COLUMNS, rows, rows + 1,
2711                                  (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL),
2712                                  (LiVESAttachOptions)(LIVES_FILL), 0, 0);
2713 
2714               lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "labelbox", labelbox);
2715               lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "hbox", hbox);
2716 
2717               lives_widget_set_bg_color(xeventbox, LIVES_WIDGET_STATE_NORMAL, &col);
2718               lives_widget_set_app_paintable(xeventbox, TRUE);
2719               lives_signal_connect(LIVES_GUI_OBJECT(xeventbox), LIVES_WIDGET_EXPOSE_EVENT,
2720                                    LIVES_GUI_CALLBACK(mt_expose_audtrack_event),
2721                                    (livespointer)mt);
2722 
2723               rows++;
2724               if (rows == prefs->max_disp_vtracks) break;
2725             }
2726 
2727             if (mainw->files[mt->render_file]->achans > 1) {
2728               xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "achan1");
2729               hidden = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY))
2730                        & TRACK_I_HIDDEN_USER;
2731               lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(hidden));
2732 
2733               if (hidden == 0) {
2734                 label = (LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "label")));
2735                 labelbox = lives_event_box_new();
2736                 hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
2737                 lives_widget_apply_theme(label, LIVES_WIDGET_STATE_NORMAL);
2738                 lives_widget_apply_theme(labelbox, LIVES_WIDGET_STATE_NORMAL);
2739                 lives_widget_apply_theme(hbox, LIVES_WIDGET_STATE_NORMAL);
2740                 lives_container_add(LIVES_CONTAINER(labelbox), hbox);
2741                 lives_box_pack_end(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
2742 
2743                 lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->vadjustment),
2744                                                (double)((int)lives_adjustment_get_page_size(LIVES_ADJUSTMENT(mt->vadjustment))
2745                                                         - 1));
2746 
2747                 lives_table_attach(LIVES_TABLE(mt->timeline_table), labelbox, 2, 6, rows, rows + 1,
2748                                    LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
2749                 lives_table_attach(LIVES_TABLE(mt->timeline_table), xeventbox, 7, TIMELINE_TABLE_COLUMNS, rows, rows + 1,
2750                                    (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL),
2751                                    (LiVESAttachOptions)(0), 0, 0);
2752 
2753                 lives_widget_set_valign(xeventbox, LIVES_ALIGN_CENTER);
2754 
2755                 lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "labelbox", labelbox);
2756                 lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "hbox", hbox);
2757 
2758                 lives_widget_set_bg_color(xeventbox, LIVES_WIDGET_STATE_NORMAL, &col);
2759                 lives_widget_set_app_paintable(xeventbox, TRUE);
2760                 lives_signal_connect(LIVES_GUI_OBJECT(xeventbox), LIVES_WIDGET_EXPOSE_EVENT,
2761                                      LIVES_GUI_CALLBACK(mt_expose_audtrack_event),
2762                                      (livespointer)mt);
2763 
2764                 rows++;
2765                 if (rows == prefs->max_disp_vtracks) break;
2766 		// *INDENT-OFF*
2767               }}}}}}
2768     // *INDENT-ON*
2769 
2770     vdraws = vdraws->next;
2771   }
2772 
2773   if (lives_adjustment_get_page_size(LIVES_ADJUSTMENT(mt->vadjustment)) < 1.)
2774     lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->vadjustment), 1.);
2775 
2776   lives_adjustment_set_upper(LIVES_ADJUSTMENT(mt->vadjustment),
2777                              (double)(get_top_track_for(mt, mt->num_video_tracks - 1) +
2778                                       (int)lives_adjustment_get_page_size(LIVES_ADJUSTMENT(mt->vadjustment))));
2779 
2780   if (lives_adjustment_get_value(LIVES_ADJUSTMENT(mt->vadjustment)) + lives_adjustment_get_page_size(LIVES_ADJUSTMENT(
2781         mt->vadjustment)) >
2782       lives_adjustment_get_upper(LIVES_ADJUSTMENT(mt->vadjustment)))
2783     lives_adjustment_set_upper(LIVES_ADJUSTMENT(mt->vadjustment), lives_adjustment_get_value(LIVES_ADJUSTMENT(mt->vadjustment)) +
2784                                lives_adjustment_get_page_size(LIVES_ADJUSTMENT(mt->vadjustment)));
2785 
2786   xlist = table_children = lives_container_get_children(LIVES_CONTAINER(mt->timeline_table));
2787 
2788   while (table_children) {
2789     LiVESWidget *child = (LiVESWidget *)table_children->data;
2790     lives_widget_set_size_request(child, -1, MT_TRACK_HEIGHT);
2791     table_children = table_children->next;
2792   }
2793 
2794   if (xlist) lives_list_free(xlist);
2795 
2796   paint_lines(mt, mt->ptr_time, TRUE, NULL);
2797 
2798   lives_widget_show_all(mt->timeline_table);
2799 
2800   if (mt->is_ready) {
2801     mt->no_expose = FALSE;
2802     //lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
2803   }
2804 }
2805 
2806 
track_arrow_pressed(LiVESWidget * ebox,LiVESXEventButton * event,livespointer user_data)2807 boolean track_arrow_pressed(LiVESWidget * ebox, LiVESXEventButton * event, livespointer user_data) {
2808   LiVESWidget *eventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(ebox), "eventbox");
2809   LiVESWidget *arrow = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "arrow"), *new_arrow;
2810   lives_mt *mt = (lives_mt *)user_data;
2811   boolean expanded = !(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"));
2812 
2813   if (!LIVES_IS_INTERACTIVE) return FALSE;
2814 
2815   if (!mt->audio_draws || (!mt->opts.pertrack_audio && (mt->opts.back_audio_tracks == 0 ||
2816                            eventbox != mt->audio_draws->data))) {
2817     track_ebox_pressed(eventbox, NULL, mt);
2818     return FALSE;
2819   }
2820 
2821   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "expanded", LIVES_INT_TO_POINTER(expanded));
2822 
2823   if (!expanded) {
2824     new_arrow = lives_arrow_new(LIVES_ARROW_RIGHT, LIVES_SHADOW_OUT);
2825   } else {
2826     new_arrow = lives_arrow_new(LIVES_ARROW_DOWN, LIVES_SHADOW_OUT);
2827   }
2828 
2829   lives_widget_object_ref(new_arrow);
2830 
2831   lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(eventbox), "arrow", new_arrow);
2832 
2833   lives_tooltips_copy(new_arrow, arrow);
2834 
2835   // must do this after we update object data, to avoid a race condition
2836   lives_widget_destroy(arrow);
2837 
2838   scroll_tracks(mt, mt->top_track, FALSE);
2839   track_select(mt);
2840   return FALSE;
2841 }
2842 
2843 
multitrack_view_clips(LiVESMenuItem * menuitem,livespointer user_data)2844 void multitrack_view_clips(LiVESMenuItem * menuitem, livespointer user_data) {
2845   lives_mt *mt = (lives_mt *)user_data;
2846   polymorph(mt, POLY_CLIPS);
2847 }
2848 
2849 
multitrack_view_in_out(LiVESMenuItem * menuitem,livespointer user_data)2850 void multitrack_view_in_out(LiVESMenuItem * menuitem, livespointer user_data) {
2851   lives_mt *mt = (lives_mt *)user_data;
2852   if (!mt->block_selected) return;
2853   if (!nb_ignore) {
2854     polymorph(mt, POLY_IN_OUT);
2855   }
2856 }
2857 
2858 
time_to_string(double secs)2859 static char *time_to_string(double secs) {
2860   int hours, mins, rest;
2861   char timestring[TIMECODE_LENGTH];
2862 
2863   hours = secs / 3600;
2864   secs -= hours * 3600.;
2865   mins = secs / 60;
2866   secs -= mins * 60.;
2867   rest = (secs - ((int)secs) * 1.) * 100. + .5;
2868   secs = (int)secs * 1.;
2869   lives_snprintf(timestring, TIMECODE_LENGTH, "%02d:%02d:%02d.%02d", hours, mins, (int)secs, rest);
2870   return lives_strdup(timestring);
2871 }
2872 
2873 
update_timecodes(lives_mt * mt,double dtime)2874 static void update_timecodes(lives_mt * mt, double dtime) {
2875   char *timestring = time_to_string(QUANT_TIME(dtime));
2876   lives_snprintf(mt->timestring, TIMECODE_LENGTH, "%s", timestring);
2877   widget_opts.justify = LIVES_JUSTIFY_CENTER;
2878   lives_entry_set_text(LIVES_ENTRY(mt->timecode), timestring);
2879   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
2880   lives_free(timestring);
2881 }
2882 
2883 
set_fxlist_label(lives_mt * mt)2884 static void set_fxlist_label(lives_mt * mt) {
2885   char *tname = get_track_name(mt, mt->current_track, mt->aud_track_selected);
2886   char *text = lives_strdup_printf(_("Effects stack for %s at time %s"), tname, mt->timestring);
2887   lives_label_set_text(LIVES_LABEL(mt->fx_list_label), text);
2888   lives_free(tname);
2889   lives_free(text);
2890 }
2891 
2892 
renumber_clips(void)2893 static void renumber_clips(void) {
2894   // remove gaps in our mainw->files array - caused when clips are closed
2895   // we also ensure each clip has a (non-zero) 64 bit unique_id to help with later id of the clips
2896 
2897   // called once when we enter multitrack mode
2898 
2899   int cclip;
2900   int i = 1, j;
2901 
2902   LiVESList *clist;
2903 
2904   boolean bad_header = FALSE;
2905 
2906   renumbered_clips[0] = 0;
2907 
2908   // walk through files mainw->files[cclip]
2909   // mainw->files[i] points to next non-NULL clip
2910 
2911   // if we find a gap we move i to cclip
2912 
2913   for (cclip = 1; i <= MAX_FILES; cclip++) {
2914     if (!mainw->files[cclip]) {
2915       if (i != cclip) {
2916         mainw->files[cclip] = mainw->files[i];
2917 
2918         for (j = 0; j < FN_KEYS - 1; j++) {
2919           if (mainw->clipstore[j][0] == i) mainw->clipstore[j][0] = cclip;
2920         }
2921 
2922         // we need to change the entries in mainw->cliplist
2923         clist = mainw->cliplist;
2924         while (clist) {
2925           if (LIVES_POINTER_TO_INT(clist->data) == i) {
2926             clist->data = LIVES_INT_TO_POINTER(cclip);
2927             break;
2928           }
2929           clist = clist->next;
2930         }
2931 
2932         mainw->files[i] = NULL;
2933 
2934         if (mainw->scrap_file == i) mainw->scrap_file = cclip;
2935         if (mainw->ascrap_file == i) mainw->ascrap_file = cclip;
2936         if (mainw->current_file == i) mainw->current_file = cclip;
2937 
2938         if (mainw->first_free_file == cclip) mainw->first_free_file++;
2939 
2940         renumbered_clips[i] = cclip;
2941       }
2942       // process this clip again
2943       else cclip--;
2944     } else {
2945       renumbered_clips[cclip] = cclip;
2946       if (i == cclip) i++;
2947     }
2948 
2949     if (mainw->files[cclip] && (cclip == mainw->scrap_file || cclip == mainw->ascrap_file ||
2950                                 IS_NORMAL_CLIP(cclip)) && mainw->files[cclip]->unique_id == 0l) {
2951       mainw->files[cclip]->unique_id = gen_unique_id();
2952       save_clip_value(cclip, CLIP_DETAILS_UNIQUE_ID, &mainw->files[cclip]->unique_id);
2953       if (THREADVAR(com_failed) || THREADVAR(write_failed)) bad_header = TRUE;
2954 
2955       if (bad_header) do_header_write_error(cclip);
2956     }
2957 
2958     for (; i <= MAX_FILES; i++) {
2959       if (mainw->files[i]) break;
2960     }
2961   }
2962 }
2963 
2964 
rerenumber_clips(const char * lfile,weed_plant_t * event_list)2965 static void rerenumber_clips(const char *lfile, weed_plant_t *event_list) {
2966   // we loaded an event_list, now we match clip numbers in event_list with our current clips, using the layout map file
2967   // the renumbering is used for translations in event_list_rectify
2968   // in mt_init_tracks we alter the clip numbers in the event_list
2969 
2970   // this means if we save again, the clip numbers in the disk event list (*.lay file) may be updated
2971   // however, since we also have a layout map file (*.map) for the set, this should not be too big an issue
2972 
2973   LiVESList *lmap;
2974   char **array;
2975   int rnc;
2976   register int i;
2977 
2978   // ensure file layouts are updated
2979   upd_layout_maps(event_list);
2980 
2981   renumbered_clips[0] = 0;
2982 
2983   for (i = 1; i <= MAX_FILES && mainw->files[i]; i++) {
2984     renumbered_clips[i] = 0;
2985     if (mainw->files[i]) lfps[i] = mainw->files[i]->fps;
2986     else lfps[i] = cfile->fps;
2987   }
2988 
2989   if (lfile) {
2990     // lfile is supplied layout file name
2991     for (i = 1; i <= MAX_FILES && mainw->files[i]; i++) {
2992       for (lmap = mainw->files[i]->layout_map; lmap; lmap = lmap->next) {
2993         // lmap->data starts with layout name
2994         if (!lives_strncmp((char *)lmap->data, lfile, strlen(lfile))) {
2995           threaded_dialog_spin(0.);
2996           array = lives_strsplit((char *)lmap->data, "|", -1);
2997           threaded_dialog_spin(0.);
2998 
2999           // piece 2 is the clip number
3000           rnc = atoi(array[1]);
3001           if (rnc < 0 || rnc > MAX_FILES) continue;
3002           renumbered_clips[rnc] = i;
3003 
3004           // original fps
3005           lfps[i] = strtod(array[3], NULL);
3006           threaded_dialog_spin(0.);
3007           lives_strfreev(array);
3008           threaded_dialog_spin(0.);
3009         }
3010       }
3011     }
3012   } else {
3013     // current event_list
3014     for (i = 1; i <= MAX_FILES && mainw->files[i]; i++) {
3015       if (mainw->files[i]->stored_layout_idx != -1) {
3016         renumbered_clips[mainw->files[i]->stored_layout_idx] = i;
3017       }
3018       lfps[i] = mainw->files[i]->stored_layout_fps;
3019     }
3020   }
3021 }
3022 
3023 
mt_clip_select(lives_mt * mt,boolean scroll)3024 void mt_clip_select(lives_mt * mt, boolean scroll) {
3025   LiVESList *list = lives_container_get_children(LIVES_CONTAINER(mt->clip_inner_box));
3026   LiVESWidget *clipbox = NULL;
3027   boolean was_neg = FALSE;
3028   int len;
3029 
3030   mt->file_selected = -1;
3031 
3032   if (!list) return;
3033 
3034   if (mt->poly_state == POLY_FX_STACK && mt->event_list) {
3035     if (!mt->was_undo_redo) {
3036       polymorph(mt, POLY_FX_STACK);
3037     }
3038   } else polymorph(mt, POLY_CLIPS);
3039   if (mt->clip_selected < 0) {
3040     was_neg = TRUE;
3041     mt->clip_selected = -mt->clip_selected;
3042   }
3043 
3044   if (mt->clip_selected >= (len = lives_list_length(list)) && !was_neg) mt->clip_selected = 0;
3045 
3046   if (was_neg) mt->clip_selected--;
3047 
3048   if (mt->clip_selected < 0 || (was_neg && mt->clip_selected == 0)) mt->clip_selected = len - 1;
3049 
3050   if (mt->clip_selected < 0) {
3051     mt->file_selected = -1;
3052     lives_list_free(list);
3053     return;
3054   }
3055 
3056   mt->file_selected = mt_file_from_clip(mt, mt->clip_selected);
3057 
3058   if (scroll) {
3059     LiVESAdjustment *adj = lives_scrolled_window_get_hadjustment(LIVES_SCROLLED_WINDOW(mt->clip_scroll));
3060     if (adj) {
3061       double value = lives_adjustment_get_upper(adj) * (mt->clip_selected + .5) / (double)len;
3062       lives_adjustment_clamp_page(adj, value - lives_adjustment_get_page_size(adj) / 2.,
3063                                   value + lives_adjustment_get_page_size(adj) / 2.);
3064     }
3065   }
3066 
3067   for (int i = 0; i < len; i++) {
3068     clipbox = (LiVESWidget *)lives_list_nth_data(list, i);
3069     if (i == mt->clip_selected) {
3070       if (palette->style & STYLE_1) {
3071         lives_widget_set_bg_color(clipbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
3072         lives_widget_set_fg_color(clipbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
3073         set_child_alt_colour(clipbox, TRUE);
3074       }
3075 
3076       lives_widget_set_sensitive(mt->adjust_start_end, mainw->files[mt->file_selected]->frames > 0);
3077       if (mt->current_track > -1) {
3078         lives_widget_set_sensitive(mt->insert, mainw->files[mt->file_selected]->frames > 0);
3079         lives_widget_set_sensitive(mt->audio_insert, FALSE);
3080       } else {
3081         lives_widget_set_sensitive(mt->audio_insert, mainw->files[mt->file_selected]->achans > 0);
3082         lives_widget_set_sensitive(mt->insert, FALSE);
3083       }
3084     } else {
3085       if (palette->style & STYLE_1) {
3086         lives_widget_set_bg_color(clipbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
3087         lives_widget_set_fg_color(clipbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
3088         set_child_colour(clipbox, TRUE);
3089       }
3090     }
3091   }
3092   lives_list_free(list);
3093 }
3094 
3095 
mt_prevclip(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)3096 boolean mt_prevclip(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
3097                     livespointer user_data) {
3098   lives_mt *mt = (lives_mt *)user_data;
3099   if (!LIVES_IS_INTERACTIVE) return TRUE;
3100   mt->clip_selected--;
3101   polymorph(mt, POLY_CLIPS);
3102   mt_clip_select(mt, TRUE);
3103   return TRUE;
3104 }
3105 
3106 
mt_nextclip(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)3107 boolean mt_nextclip(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
3108                     livespointer user_data) {
3109   lives_mt *mt = (lives_mt *)user_data;
3110   if (!LIVES_IS_INTERACTIVE) return TRUE;
3111   mt->clip_selected++;
3112   polymorph(mt, POLY_CLIPS);
3113   mt_clip_select(mt, TRUE);
3114   return TRUE;
3115 }
3116 
3117 
set_time_scrollbar(lives_mt * mt)3118 static void set_time_scrollbar(lives_mt * mt) {
3119   double page = mt->tl_max - mt->tl_min;
3120   if (mt->end_secs == 0.) mt->end_secs = DEF_TIME;
3121 
3122   if (mt->tl_max > mt->end_secs) mt->end_secs = mt->tl_max;
3123 
3124   lives_widget_object_freeze_notify(LIVES_WIDGET_OBJECT(mt->hadjustment));
3125   lives_range_set_range(LIVES_RANGE(mt->time_scrollbar), 0., mt->end_secs);
3126   lives_range_set_increments(LIVES_RANGE(mt->time_scrollbar), page / 4., page);
3127   lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->hadjustment), page);
3128   lives_adjustment_set_value(LIVES_ADJUSTMENT(mt->hadjustment), mt->tl_min);
3129   lives_widget_object_thaw_notify(LIVES_WIDGET_OBJECT(mt->hadjustment));
3130   lives_widget_queue_draw(mt->time_scrollbar);
3131 }
3132 
3133 
set_timeline_end_secs(lives_mt * mt,double secs)3134 void set_timeline_end_secs(lives_mt * mt, double secs) {
3135   double pos = mt->ptr_time;
3136 
3137   mt->end_secs = secs;
3138 
3139 #ifdef ENABLE_GIW
3140   giw_timeline_set_max_size(GIW_TIMELINE(mt->timeline), mt->end_secs);
3141   lives_ruler_set_upper(LIVES_RULER(mt->timeline), mt->tl_max);
3142   lives_ruler_set_lower(LIVES_RULER(mt->timeline), mt->tl_min);
3143 #endif
3144 
3145   lives_ruler_set_range(LIVES_RULER(mt->timeline), mt->tl_min, mt->tl_max, mt->tl_min, mt->end_secs + 1. / mt->fps);
3146   lives_widget_queue_draw(mt->timeline);
3147   lives_widget_queue_draw(mt->timeline_table);
3148   if (!mt->sel_locked || mt->region_end < mt->end_secs + 1. / mt->fps) {
3149     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_start), 0., mt->end_secs);
3150     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_end), 0., mt->end_secs + 1. / mt->fps);
3151   }
3152   set_time_scrollbar(mt);
3153 
3154   lives_ruler_set_value(LIVES_RULER(mt->timeline), pos);
3155 
3156   redraw_all_event_boxes(mt);
3157 }
3158 
3159 
set_play_position(lives_mt * mt)3160 static weed_timecode_t set_play_position(lives_mt * mt) {
3161   // get start event
3162   boolean has_pb_loop_event = FALSE;
3163   weed_timecode_t tc;
3164 #ifdef ENABLE_JACK_TRANSPORT
3165   weed_timecode_t end_tc = event_list_get_end_tc(mt->event_list);
3166 #endif
3167 
3168   mainw->cancelled = CANCEL_NONE;
3169 
3170 #ifdef ENABLE_JACK_TRANSPORT
3171   // if we have jack transport enabled, we get our playback start time from there
3172 
3173   if (mainw->jack_can_stop && (prefs->jack_opts & JACK_OPTS_TIMEBASE_START) && (prefs->jack_opts & JACK_OPTS_TRANSPORT_CLIENT)) {
3174     mt->pb_loop_event = get_first_frame_event(mt->event_list);
3175     has_pb_loop_event = TRUE;
3176     tc = q_gint64(jack_transport_get_current_ticks(), mainw->files[mt->render_file]->fps);
3177     if (!mainw->loop_cont) {
3178       if (tc > end_tc) {
3179         mainw->cancelled = CANCEL_VID_END;
3180         return 0;
3181       }
3182     }
3183     if (end_tc > 0) tc %= end_tc;
3184     mt->is_paused = FALSE;
3185   } else {
3186 #endif
3187     //////////////////////////////////////////
3188 
3189     // set actual playback start time, from mt->ptr_time
3190     tc = q_gint64(mt->ptr_time * TICKS_PER_SECOND_DBL, mainw->files[mt->render_file]->fps);
3191 
3192     if (!mt->is_paused)
3193       mt->pb_unpaused_start_time = mt->ptr_time;
3194 
3195     mt->pb_start_time = mt->ptr_time;
3196 
3197     //////////////////////////////////
3198 #ifdef ENABLE_JACK_TRANSPORT
3199   }
3200 #endif
3201   // get the start event to play from
3202   if (tc > event_list_get_end_tc(mt->event_list) || tc == 0) mt->pb_start_event = get_first_frame_event(mt->event_list);
3203   else {
3204     mt->pb_start_event = get_frame_event_at(mt->event_list, tc, NULL, TRUE);
3205   }
3206 
3207   if (!has_pb_loop_event) mt->pb_loop_event = mt->pb_start_event;
3208 
3209   // return timecode of start event
3210   return get_event_timecode(mt->pb_start_event);
3211 }
3212 
3213 
mt_show_current_frame(lives_mt * mt,boolean return_layer)3214 void mt_show_current_frame(lives_mt * mt, boolean return_layer) {
3215   // show preview of current frame in preview_eventbox and/or play_
3216   // or, if return_layer is TRUE, we just set mainw->frame_layer (used when we want to save the frame, e.g right click context)
3217 
3218   /// NOTE: this will show the current frame WITHOUT any currently unapplied effects
3219   // to show WITH unapplied effects, call activate_mt_preview() instead
3220 
3221   weed_timecode_t curr_tc;
3222 
3223   double ptr_time = mt->ptr_time;
3224 
3225   weed_plant_t *frame_layer = mainw->frame_layer;
3226 
3227   int current_file;
3228   int actual_frame;
3229 
3230   boolean is_rendering = mainw->is_rendering;
3231   boolean internal_messaging = mainw->internal_messaging;
3232   boolean needs_idlefunc = FALSE;
3233   boolean did_backup = mt->did_backup;
3234   static boolean lastblank = TRUE;
3235 
3236   //if (mt->play_width == 0 || mt->play_height == 0) return;
3237   if (mt->no_frame_update) return;
3238 
3239   set_mt_play_sizes_cfg(mt);
3240 
3241   if (mt->idlefunc > 0) {
3242     lives_source_remove(mt->idlefunc);
3243     mt->idlefunc = 0;
3244     needs_idlefunc = TRUE;
3245   }
3246 
3247   if (!return_layer) {
3248     // show frame image in window
3249     if (!mt->mt_frame_preview) {
3250       boolean sep_win = mainw->sep_win;
3251       mt->mt_frame_preview = TRUE;
3252 
3253       if (mainw->plug) {
3254         lives_container_remove(LIVES_CONTAINER(mainw->plug), mainw->play_image);
3255         lives_widget_destroy(mainw->plug);
3256         mainw->plug = NULL;
3257       }
3258 
3259       if (LIVES_IS_WIDGET(mainw->playarea)) lives_widget_destroy(mainw->playarea);
3260 
3261       mainw->playarea = lives_hbox_new(FALSE, 0);
3262       lives_container_add(LIVES_CONTAINER(mt->preview_eventbox), mainw->playarea);
3263       lives_widget_set_bg_color(mainw->playarea, LIVES_WIDGET_STATE_NORMAL, &palette->black);
3264       mainw->sep_win = FALSE;
3265       add_to_playframe();
3266       lives_widget_show_all(mainw->playarea);
3267       set_mt_play_sizes_cfg(mt);
3268       mainw->sep_win = sep_win;
3269     }
3270   }
3271 
3272   if (LIVES_IS_PLAYING) {
3273     if (mainw->play_window && LIVES_IS_XWINDOW(lives_widget_get_xwindow(mainw->play_window))) {
3274 #if GTK_CHECK_VERSION(3, 0, 0)
3275       if (!mt->frame_pixbuf || mt->frame_pixbuf != mainw->imframe) {
3276         if (mt->frame_pixbuf) lives_widget_object_unref(mt->frame_pixbuf);
3277         // set frame_pixbuf, this gets painted in in expose_event
3278         mt->frame_pixbuf = mainw->imframe;
3279         set_drawing_area_from_pixbuf(mainw->play_image, mt->frame_pixbuf, mainw->play_surface);
3280       }
3281 #else
3282       set_drawing_area_from_pixbuf(mainw->play_image, mainw->imframe, mainw->play_surface);
3283 #endif
3284     } else {
3285 #if GTK_CHECK_VERSION(3, 0, 0)
3286       if (mt->frame_pixbuf != mainw->imframe) {
3287         if (mt->frame_pixbuf) lives_widget_object_unref(mt->frame_pixbuf);
3288         mt->frame_pixbuf = NULL;
3289         set_drawing_area_from_pixbuf(mainw->play_image, mt->frame_pixbuf, mainw->play_surface);
3290       }
3291 #else
3292       set_drawing_area_from_pixbuf(mainw->play_image, NULL, mainw->play_surface);
3293 #endif
3294     }
3295     lives_widget_queue_draw(mt->preview_eventbox);
3296     if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
3297       mt->idlefunc = mt_idle_add(mt);
3298     }
3299     return;
3300   }
3301 
3302   // start "playback" at mt->ptr_time; we just "render" one frame
3303   curr_tc = set_play_position(mt);
3304   actual_frame = (int)((double)curr_tc / TICKS_PER_SECOND_DBL * mainw->files[mt->render_file]->fps + 1.4999);
3305   mainw->frame_layer = NULL;
3306 
3307   if (mt->is_rendering && actual_frame <= mainw->files[mt->render_file]->frames) {
3308     // get the actual frame if it has already been rendered
3309     mainw->frame_layer = lives_layer_new_for_frame(mainw->current_file, actual_frame);
3310     pull_frame(mainw->frame_layer, get_image_ext_for_type(mainw->files[mt->render_file]->img_type), curr_tc);
3311   } else {
3312     weed_plant_t *live_inst = NULL;
3313     mainw->is_rendering = TRUE;
3314 
3315     if (mt->event_list) {
3316       if (mt->pb_start_event) {
3317         cfile->next_event = mt->pb_start_event;
3318       } else {
3319         cfile->next_event = get_first_event(mt->event_list);
3320       }
3321       // "play" a single frame
3322       current_file = mainw->current_file;
3323       mainw->internal_messaging = TRUE; // stop load_frame from showing image
3324       mainw->files[mt->render_file]->next_event = mt->pb_start_event;
3325       if (is_rendering) {
3326         backup_weed_instances();
3327         backup_host_tags(mt->event_list, curr_tc);
3328       }
3329 
3330       // pass quickly through events_list, switching on and off effects and interpolating at current time
3331       get_audio_and_effects_state_at(mt->event_list, mt->pb_start_event, 0, LIVES_PREVIEW_TYPE_VIDEO_ONLY, mt->exact_preview);
3332 
3333       // if we are previewing a specific effect we also need to init it
3334       if (mt->current_rfx && mt->init_event) {
3335         if (mt->current_rfx->source_type == LIVES_RFX_SOURCE_WEED && mt->current_rfx->source) {
3336           live_inst = (weed_plant_t *)mt->current_rfx->source;
3337 
3338           // we want to get hold of the instance which the renderer will use, and then set the in_parameters
3339           // with the currently unapplied values from the live instance
3340 
3341           // the renderer's instance will be freed in deinit_render_effects(),
3342           // so we need to keep the live instance around for when we return
3343 
3344           if (weed_plant_has_leaf(mt->init_event, WEED_LEAF_HOST_TAG)) {
3345             char *keystr = weed_get_string_value(mt->init_event, WEED_LEAF_HOST_TAG, NULL);
3346             int key = atoi(keystr) + 1;
3347             lives_freep((void **)&keystr);
3348 
3349             // get the rendering version:
3350             mt->current_rfx->source = (void *)rte_keymode_get_instance(key, 0); // adds a ref
3351 
3352             // for the preview we will use a copy of the current in_params from the live instance
3353             // interpolation is OFF here so we will see exactly the current values
3354             if (mt->current_rfx->source) {
3355               int nparams;
3356               weed_plant_t **src_params = weed_instance_get_in_params(live_inst, &nparams);
3357               weed_plant_t **dst_params = weed_instance_get_in_params((weed_plant_t *)mt->current_rfx->source, NULL);
3358               for (int i = 0; i < nparams; i++) {
3359                 weed_leaf_dup(dst_params[i], src_params[i], WEED_LEAF_VALUE);
3360               }
3361               if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(mt->solo_check)))
3362                 mt->solo_inst = mt->current_rfx->source;
3363               else
3364                 mt->solo_inst = NULL;
3365 
3366               /// we want to preview the (first) output layer
3367               mt->preview_layer = weed_get_int_value(mt->init_event, WEED_LEAF_OUT_TRACKS, NULL);
3368 
3369               /// release the ref from rte_keymode_get_instance()
3370               weed_instance_unref((weed_plant_t *)mt->current_rfx->source);
3371               lives_free(src_params);
3372               lives_free(dst_params);
3373 	      // *INDENT-OFF*
3374             }}}}
3375       // *INDENT-ON*
3376 
3377       mainw->last_display_ticks = 0;
3378 
3379       // start decoder plugins, one per track
3380       init_track_decoders();
3381 
3382       // render one frame
3383       process_events(mt->pb_start_event, FALSE, 0);
3384       free_track_decoders();
3385       mt->preview_layer = -100000;
3386       mt->solo_inst = NULL;
3387       mainw->internal_messaging = internal_messaging;
3388       mainw->current_file = current_file;
3389       deinit_render_effects();
3390 
3391       // if we are previewing an effect we now need to restore the live inst
3392       if (live_inst) mt->current_rfx->source = (void *)live_inst;
3393 
3394       if (is_rendering) {
3395         restore_weed_instances();
3396         restore_host_tags(mt->event_list, curr_tc);
3397       }
3398       mainw->files[mt->render_file]->next_event = NULL;
3399       mainw->is_rendering = is_rendering;
3400     }
3401   }
3402 
3403   if (return_layer) {
3404     if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
3405       mt->idlefunc = mt_idle_add(mt);
3406     }
3407     return;
3408   }
3409 
3410 #if GTK_CHECK_VERSION(3, 0, 0)
3411   if (mt->frame_pixbuf && mt->frame_pixbuf != mainw->imframe) {
3412     lives_widget_object_unref(mt->frame_pixbuf);
3413     mt->frame_pixbuf = NULL;
3414   }
3415 #endif
3416 
3417   if (mt->frame_pixbuf && mt->frame_pixbuf == mainw->imframe) {
3418     // size_request, reset play frame size
3419     // try to expand / shrink
3420     lives_widget_set_size_request(mt->preview_eventbox, GUI_SCREEN_WIDTH / PEB_WRATIO,
3421                                   GUI_SCREEN_HEIGHT / PEB_HRATIO);
3422   }
3423 
3424   if (mainw->frame_layer) {
3425     LiVESPixbuf *pixbuf = NULL;
3426     int pwidth, pheight, lb_width, lb_height;
3427     int cpal = WEED_PALETTE_RGB24, layer_palette;
3428     boolean was_letterboxed = FALSE;
3429 
3430     check_layer_ready(mainw->frame_layer);
3431     layer_palette = weed_layer_get_palette(mainw->frame_layer);
3432     if (weed_palette_has_alpha(layer_palette)) cpal = WEED_PALETTE_RGBA32;
3433 
3434     pwidth = mt->play_width;
3435     pheight = mt->play_height;
3436 
3437     if (weed_get_boolean_value(mainw->frame_layer, "letterboxed", NULL) == WEED_FALSE) {
3438 
3439       if (prefs->letterbox_mt) {
3440         lb_width = weed_layer_get_width_pixels(mainw->frame_layer);
3441         lb_height = weed_layer_get_height(mainw->frame_layer);
3442         calc_maxspect(pwidth, pheight, &lb_width, &lb_height);
3443         pwidth = lb_width;
3444         pheight = lb_height;
3445         if (!letterbox_layer(mainw->frame_layer, pwidth, pheight, lb_width, lb_height,
3446                              LIVES_INTERP_BEST, cpal, 0))
3447           return;
3448         was_letterboxed = TRUE;
3449       }
3450     }
3451 
3452     if (!was_letterboxed) resize_layer(mainw->frame_layer, pwidth, pheight, LIVES_INTERP_BEST, cpal, 0);
3453 
3454     convert_layer_palette_full(mainw->frame_layer, cpal, 0, 0, 0, WEED_GAMMA_SRGB);
3455 
3456     if (prefs->use_screen_gamma)
3457       gamma_convert_layer(WEED_GAMMA_MONITOR, mainw->frame_layer);
3458 
3459     if (mt->framedraw) mt_framedraw(mt, mainw->frame_layer); // framedraw will free the frame_layer itself
3460     else {
3461       if (lastblank) {
3462         clear_widget_bg(mainw->play_image, mainw->play_surface);
3463         lastblank = FALSE;
3464       }
3465       if (!pixbuf) {
3466         pixbuf = layer_to_pixbuf(mainw->frame_layer, TRUE, TRUE);
3467       }
3468 #if GTK_CHECK_VERSION(3, 0, 0)
3469       // set frame_pixbuf, this gets painted in in expose_event
3470       mt->frame_pixbuf = pixbuf;
3471       set_drawing_area_from_pixbuf(mainw->play_image, mt->frame_pixbuf, mainw->play_surface);
3472 #endif
3473       lives_widget_queue_draw(mt->preview_eventbox);
3474       weed_plant_free(mainw->frame_layer);
3475       mainw->frame_layer = NULL;
3476     }
3477   } else {
3478     // no frame - show blank
3479     if (!lastblank) {
3480       clear_widget_bg(mainw->play_image, mainw->play_surface);
3481       lastblank = TRUE;
3482     }
3483 
3484 #if GTK_CHECK_VERSION(3, 0, 0)
3485     // set frame_pixbuf, this gets painted in in expose_event
3486     mt->frame_pixbuf = mainw->imframe;
3487     set_drawing_area_from_pixbuf(mainw->play_image, mt->frame_pixbuf, mainw->play_surface);
3488 #else
3489     set_drawing_area_from_pixbuf(mainw->play_image, mainw->imframe, mainw->play_surface);
3490 #endif
3491     lives_widget_queue_draw(mt->preview_eventbox);
3492   }
3493   if (mt->frame_pixbuf && mt->frame_pixbuf != mainw->imframe) {
3494     lives_widget_object_unref(mt->frame_pixbuf);
3495   }
3496   mt->frame_pixbuf = NULL;
3497 
3498   /// restore original mainw->frame_layer
3499   mainw->frame_layer = frame_layer;
3500 
3501   lives_ruler_set_value(LIVES_RULER(mt->timeline), ptr_time);
3502   lives_widget_queue_draw(mt->timeline);
3503 
3504   if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
3505     mt->idlefunc = mt_idle_add(mt);
3506   }
3507 }
3508 
3509 
tc_to_rs(LiVESMenuItem * menuitem,livespointer user_data)3510 static void tc_to_rs(LiVESMenuItem * menuitem, livespointer user_data) {
3511   lives_mt *mt = (lives_mt *)user_data;
3512   mt->region_start = mt->ptr_time;
3513   on_timeline_release(mt->timeline_reg, NULL, mt);
3514 }
3515 
3516 
tc_to_re(LiVESMenuItem * menuitem,livespointer user_data)3517 static void tc_to_re(LiVESMenuItem * menuitem, livespointer user_data) {
3518   lives_mt *mt = (lives_mt *)user_data;
3519   mt->region_end = mt->ptr_time;
3520   on_timeline_release(mt->timeline_reg, NULL, mt);
3521 }
3522 
3523 
rs_to_tc(LiVESMenuItem * menuitem,livespointer user_data)3524 static void rs_to_tc(LiVESMenuItem * menuitem, livespointer user_data) {
3525   lives_mt *mt = (lives_mt *)user_data;
3526   mt_tl_move(mt, mt->region_start);
3527 }
3528 
3529 
re_to_tc(LiVESMenuItem * menuitem,livespointer user_data)3530 static void re_to_tc(LiVESMenuItem * menuitem, livespointer user_data) {
3531   lives_mt *mt = (lives_mt *)user_data;
3532   mt_tl_move(mt, mt->region_end);
3533 }
3534 
3535 
3536 //////////////////////////////////////////////////
3537 
_mt_tl_move(lives_mt * mt,double pos)3538 static void _mt_tl_move(lives_mt * mt, double pos) {
3539   pos = q_dbl(pos, mt->fps) / TICKS_PER_SECOND_DBL;
3540   if (pos < 0.) pos = 0.;
3541 
3542   // after this, we need to reference ONLY mt->ptr_time, since it may become outside the range of mt->timeline
3543   // thus we cannot rely on reading the value from mt->timeline
3544   mt->ptr_time = lives_ruler_set_value(LIVES_RULER(mt->timeline), pos);
3545 
3546   if (mt->is_ready) unpaint_lines(mt);
3547 
3548   if (pos > 0.) {
3549     lives_widget_set_sensitive(mt->rewind, TRUE);
3550     lives_widget_set_sensitive(mainw->m_rewindbutton, TRUE);
3551   } else {
3552     lives_widget_set_sensitive(mt->rewind, FALSE);
3553     lives_widget_set_sensitive(mainw->m_rewindbutton, FALSE);
3554   }
3555 
3556   if (mt->is_paused) {
3557     mt->is_paused = FALSE;
3558     lives_widget_set_sensitive(mainw->stop, FALSE);
3559     lives_widget_set_sensitive(mainw->m_stopbutton, FALSE);
3560   }
3561 
3562   lives_widget_queue_draw(mt->timeline);
3563   if (mt->init_event && mt->poly_state == POLY_PARAMS && !mt->block_node_spin) {
3564     mt->block_tl_move = TRUE;
3565     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->node_spinbutton),
3566                                 pos - get_event_timecode(mt->init_event) / TICKS_PER_SECOND_DBL);
3567     mt->block_tl_move = FALSE;
3568   }
3569 
3570   update_timecodes(mt, pos);
3571 
3572   if (pos > mt->region_end - 1. / mt->fps) lives_widget_set_sensitive(mt->tc_to_rs, FALSE);
3573   else lives_widget_set_sensitive(mt->tc_to_rs, TRUE);
3574   if (pos < mt->region_start + 1. / mt->fps) lives_widget_set_sensitive(mt->tc_to_re, FALSE);
3575   else lives_widget_set_sensitive(mt->tc_to_re, TRUE);
3576 
3577   mt->fx_order = FX_ORD_NONE;
3578   if (mt->init_event) {
3579     weed_timecode_t tc = q_gint64(pos * TICKS_PER_SECOND_DBL, mt->fps);
3580     weed_plant_t *deinit_event = weed_get_plantptr_value(mt->init_event, WEED_LEAF_DEINIT_EVENT, NULL);
3581     if (tc < get_event_timecode(mt->init_event) || tc > get_event_timecode(deinit_event)) {
3582       mt->init_event = NULL;
3583       if (mt->poly_state == POLY_PARAMS) polymorph(mt, POLY_FX_STACK);
3584     }
3585   }
3586 
3587   if (mt->poly_state == POLY_FX_STACK) polymorph(mt, POLY_FX_STACK);
3588   if (mt->is_ready) {
3589     mt_show_current_frame(mt, FALSE);
3590     paint_lines(mt, pos, TRUE, NULL);
3591   }
3592 }
3593 
3594 
mt_tl_move(lives_mt * mt,double pos)3595 void mt_tl_move(lives_mt * mt, double pos) {
3596   if (LIVES_IS_PLAYING) return;
3597   main_thread_execute((lives_funcptr_t)_mt_tl_move, -1, NULL, "vd", mt, pos);
3598 }
3599 
mt_tl_move_relative(lives_mt * mt,double pos_rel)3600 LIVES_INLINE void mt_tl_move_relative(lives_mt * mt, double pos_rel) {
3601   mt_tl_move(mt, mt->ptr_time + pos_rel);
3602 }
3603 
3604 
mt_tlfor(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)3605 boolean mt_tlfor(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
3606                  livespointer user_data) {
3607   lives_mt *mt = (lives_mt *)user_data;
3608   if (!LIVES_IS_INTERACTIVE) return TRUE;
3609   mt->fm_edit_event = NULL;
3610   mt_tl_move_relative(mt, 1.);
3611   return TRUE;
3612 }
3613 
3614 
mt_tlfor_frame(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)3615 boolean mt_tlfor_frame(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
3616                        livespointer user_data) {
3617   lives_mt *mt = (lives_mt *)user_data;
3618   if (!LIVES_IS_INTERACTIVE) return TRUE;
3619   mt->fm_edit_event = NULL;
3620   mt_tl_move_relative(mt, 1. / mt->fps);
3621   return TRUE;
3622 }
3623 
3624 
mt_tlback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)3625 boolean mt_tlback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
3626                   livespointer user_data) {
3627   lives_mt *mt = (lives_mt *)user_data;
3628   if (!LIVES_IS_INTERACTIVE) return TRUE;
3629   mt->fm_edit_event = NULL;
3630   mt_tl_move_relative(mt, -1.);
3631   return TRUE;
3632 }
3633 
mt_tlback_frame(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)3634 boolean mt_tlback_frame(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
3635                         livespointer user_data) {
3636   lives_mt *mt = (lives_mt *)user_data;
3637   if (!LIVES_IS_INTERACTIVE) return TRUE;
3638   mt->fm_edit_event = NULL;
3639   mt_tl_move_relative(mt, -1. / mt->fps);
3640   return TRUE;
3641 }
3642 
3643 
scroll_track_on_screen(lives_mt * mt,int track)3644 static void scroll_track_on_screen(lives_mt * mt, int track) {
3645   if (track > mt->top_track) track = get_top_track_for(mt, track);
3646   scroll_tracks(mt, track, track != mt->top_track);
3647 }
3648 
3649 
scroll_track_by_scrollbar(LiVESScrollbar * sbar,livespointer user_data)3650 void scroll_track_by_scrollbar(LiVESScrollbar * sbar, livespointer user_data) {
3651   lives_mt *mt = (lives_mt *)user_data;
3652   scroll_tracks(mt, lives_adjustment_get_value(lives_range_get_adjustment(LIVES_RANGE(sbar))), FALSE);
3653   track_select(mt);
3654 }
3655 
3656 
mt_zoom(lives_mt * mt,double scale)3657 static void mt_zoom(lives_mt * mt, double scale) {
3658   // ABS(scale) < 1.0 == zoom in
3659 
3660   // scale < 0.0 = center on screen middle
3661   // scale > 0.0 = center on cursor
3662 
3663   double tl_span = (mt->tl_max - mt->tl_min) / 2.;
3664   double tl_cur;
3665 
3666   if (scale > 0.) {
3667     tl_cur = mt->ptr_time;
3668   } else {
3669     tl_cur = mt->tl_min + tl_span; // center on middle of screen
3670     scale = -scale;
3671   }
3672 
3673   mt->tl_min = tl_cur - tl_span * scale; // new min
3674   mt->tl_max = tl_cur + tl_span * scale; // new max
3675 
3676   if (mt->tl_min < 0.) {
3677     mt->tl_max -= mt->tl_min;
3678     mt->tl_min = 0.;
3679   }
3680 
3681   mt->tl_min = q_gint64(mt->tl_min * TICKS_PER_SECOND_DBL, mt->fps) / TICKS_PER_SECOND_DBL;
3682   mt->tl_max = q_gint64(mt->tl_max * TICKS_PER_SECOND_DBL, mt->fps) / TICKS_PER_SECOND_DBL;
3683 
3684   if (mt->tl_min == mt->tl_max) mt->tl_max = mt->tl_min + 1. / mt->fps;
3685 
3686 #ifdef ENABLE_GIW
3687   giw_timeline_set_max_size(GIW_TIMELINE(mt->timeline), mt->tl_max);
3688 #endif
3689   lives_ruler_set_upper(LIVES_RULER(mt->timeline), mt->tl_max);
3690   lives_ruler_set_lower(LIVES_RULER(mt->timeline), mt->tl_min);
3691 
3692   set_time_scrollbar(mt);
3693 
3694   lives_widget_queue_draw(mt->timeline);
3695 
3696   redraw_all_event_boxes(mt);
3697 }
3698 
3699 
scroll_time_by_scrollbar(LiVESHScrollbar * sbar,livespointer user_data)3700 static void scroll_time_by_scrollbar(LiVESHScrollbar * sbar, livespointer user_data) {
3701   lives_mt *mt = (lives_mt *)user_data;
3702   mt->tl_min = lives_adjustment_get_value(lives_range_get_adjustment(LIVES_RANGE(sbar)));
3703   mt->tl_max = lives_adjustment_get_value(lives_range_get_adjustment(LIVES_RANGE(sbar)))
3704                + lives_adjustment_get_page_size(lives_range_get_adjustment(LIVES_RANGE(sbar)));
3705   mt_zoom(mt, -1.);
3706   paint_lines(mt, mt->ptr_time, TRUE, NULL);
3707 }
3708 
3709 
mt_trdown(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)3710 boolean mt_trdown(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
3711                   livespointer user_data) {
3712   lives_mt *mt = (lives_mt *)user_data;
3713   if (!LIVES_IS_INTERACTIVE) return TRUE;
3714 
3715   if (mt->current_track >= 0 && mt->opts.pertrack_audio && !mt->aud_track_selected) {
3716     LiVESWidget *eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
3717     mt->aud_track_selected = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"));
3718     if (!mt->aud_track_selected && mt->current_track == mt->num_video_tracks - 1) return TRUE;
3719   } else {
3720     if (mt->current_track == mt->num_video_tracks - 1) return TRUE;
3721     mt->aud_track_selected = FALSE;
3722   }
3723 
3724   if (!mt->aud_track_selected || mt->current_track == -1) {
3725     if (mt->current_track > -1) mt->current_track++;
3726     else {
3727       int i = 0;
3728       LiVESList *llist = mt->video_draws;
3729       while (llist) {
3730         if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(llist->data), HIDDEN_KEY)) == 0) {
3731           mt->current_track = i;
3732           break;
3733         }
3734         llist = llist->next;
3735         i++;
3736       }
3737       mt->current_track = i;
3738     }
3739   }
3740   mt->selected_init_event = NULL;
3741   scroll_track_on_screen(mt, mt->current_track);
3742   track_select(mt);
3743 
3744   return TRUE;
3745 }
3746 
3747 
mt_trup(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)3748 boolean mt_trup(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
3749                 livespointer user_data) {
3750   lives_mt *mt = (lives_mt *)user_data;
3751   if (mt->current_track == -1 || (mt->current_track == 0 && !mt->aud_track_selected && !mt->opts.show_audio)) return TRUE;
3752   if (!LIVES_IS_INTERACTIVE) return TRUE;
3753 
3754   if (mt->aud_track_selected) mt->aud_track_selected = FALSE;
3755   else {
3756     mt->current_track--;
3757     if (mt->current_track >= 0 && mt->opts.pertrack_audio) {
3758       LiVESWidget *eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
3759       mt->aud_track_selected = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"));
3760     }
3761   }
3762   mt->selected_init_event = NULL;
3763   if (mt->current_track != -1) scroll_track_on_screen(mt, mt->current_track);
3764   track_select(mt);
3765 
3766   return TRUE;
3767 }
3768 
3769 
poly_page_to_tab(uint32_t page)3770 LIVES_INLINE int poly_page_to_tab(uint32_t page) {
3771   return ++page;
3772 }
3773 
3774 
poly_tab_to_page(uint32_t tab)3775 LIVES_INLINE int poly_tab_to_page(uint32_t tab) {
3776   return --tab;
3777 }
3778 
3779 
get_poly_state_from_page(lives_mt * mt)3780 LIVES_INLINE lives_mt_poly_state_t get_poly_state_from_page(lives_mt * mt) {
3781   return (lives_mt_poly_state_t)poly_page_to_tab(lives_notebook_get_current_page(LIVES_NOTEBOOK(mt->nb)));
3782 }
3783 
3784 
notebook_error(LiVESNotebook * nb,uint32_t tab,lives_mt_nb_error_t err,lives_mt * mt)3785 static void notebook_error(LiVESNotebook * nb, uint32_t tab, lives_mt_nb_error_t err, lives_mt * mt) {
3786   uint32_t page = poly_tab_to_page(tab);
3787 
3788   if (mt->nb_label) lives_widget_destroy(mt->nb_label);
3789   mt->nb_label = NULL;
3790 
3791   widget_opts.justify = LIVES_JUSTIFY_CENTER;
3792 
3793   switch (err) {
3794   case NB_ERROR_SEL:
3795     mt->nb_label = lives_standard_label_new(_("\n\nPlease select a block\nin the timeline by\nright or double clicking on it.\n"));
3796     break;
3797   case NB_ERROR_NOEFFECT:
3798     mt->nb_label = lives_standard_label_new(
3799                      _("\n\nNo effect selected.\nSelect an effect in FX stack first to view its parameters.\n"));
3800     break;
3801   case NB_ERROR_NOCLIP:
3802     mt->nb_label = lives_standard_label_new(_("\n\nNo clips loaded.\n"));
3803     break;
3804   case NB_ERROR_NOTRANS:
3805     mt->nb_label = lives_standard_label_new(
3806                      _("You must select two video tracks\nand a time region\nto apply transitions.\n\n"
3807                        "Alternately, you can enable Autotransitions from the Effects menu\nbefore inserting clips into the timeline."));
3808     break;
3809   case NB_ERROR_NOCOMP:
3810     mt->nb_label = lives_standard_label_new(
3811                      _("\n\nYou must select at least one video track\nand a time region\nto apply compositors.\n"));
3812     break;
3813   }
3814 
3815   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
3816 
3817   lives_widget_set_hexpand(mt->nb_label, TRUE);
3818 
3819   // add label to notebook page
3820   lives_container_add(LIVES_CONTAINER(lives_notebook_get_nth_page(LIVES_NOTEBOOK(nb), page)), mt->nb_label);
3821   lives_container_set_border_width(LIVES_CONTAINER(lives_notebook_get_nth_page(LIVES_NOTEBOOK(nb), page)),
3822                                    widget_opts.border_width);
3823   lives_widget_show(mt->nb_label);
3824 
3825   // hide the poly box
3826   lives_widget_hide(mt->poly_box);
3827   lives_widget_set_no_show_all(mt->poly_box, TRUE);
3828 
3829   lives_widget_queue_resize(mt->nb_label);
3830 }
3831 
3832 
fubar(lives_mt * mt)3833 static void fubar(lives_mt * mt) {
3834   int npch, i;
3835   int num_in_tracks;
3836   int *in_tracks;
3837   void **pchainx;
3838   char *fhash;
3839 
3840   mt->init_event = mt->selected_init_event;
3841 
3842   mt->track_index = -1;
3843 
3844   in_tracks = weed_get_int_array_counted(mt->init_event, WEED_LEAF_IN_TRACKS, &num_in_tracks);
3845   if (num_in_tracks > 0) {
3846     // set track_index (for special widgets)
3847     for (i = 0; i < num_in_tracks; i++) {
3848       if (mt->current_track == in_tracks[i]) {
3849         mt->track_index = i;
3850         break;
3851       }
3852     }
3853     lives_free(in_tracks);
3854   }
3855 
3856   fhash = weed_get_string_value(mt->init_event, WEED_LEAF_FILTER, NULL);
3857   mt->current_fx = weed_get_idx_for_hashname(fhash, TRUE);
3858   lives_free(fhash);
3859 
3860   pchainx = weed_get_voidptr_array_counted(mt->init_event, WEED_LEAF_IN_PARAMETERS, &npch);
3861   if (npch > 0) {
3862     pchain = (void **)lives_malloc((npch + 1) * sizeof(void *));
3863     for (i = 0; i < npch; i++) pchain[i] = pchainx[i];
3864     pchain[i] = NULL;
3865     lives_free(pchainx);
3866   }
3867 }
3868 
3869 
notebook_page(LiVESWidget * nb,LiVESWidget * nbp,uint32_t tab,livespointer user_data)3870 static boolean notebook_page(LiVESWidget * nb, LiVESWidget * nbp, uint32_t tab, livespointer user_data) {
3871   // this is called once or twice: - once when the user clicks on a tab, and a second time from polymorph
3872   // (via set_poly_tab(), lives_notebook_set_current_page() )
3873 
3874   uint32_t page;
3875   lives_mt *mt = (lives_mt *)user_data;
3876 
3877   if (nbp) {
3878     page = tab;
3879     tab = poly_page_to_tab(page);
3880   } else {
3881     // should never be NULL i think
3882     page = poly_tab_to_page(tab);
3883   }
3884 
3885   // destroy the label that was in the page
3886   if (mt->nb_label) lives_widget_destroy(mt->nb_label);
3887   mt->nb_label = NULL;
3888 
3889   lives_widget_show_all(mt->poly_box);
3890   lives_widget_set_no_show_all(mt->poly_box, TRUE);
3891   lives_widget_show(lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3892 
3893   // we reparent the poly_box in the current tab
3894 
3895   switch (tab) {
3896   case POLY_CLIPS:
3897     if (!mt->clip_labels) {
3898       notebook_error(LIVES_NOTEBOOK(mt->nb), tab, NB_ERROR_NOCLIP, mt);
3899       return FALSE;
3900     }
3901     if (mt->poly_state != POLY_CLIPS && nb) polymorph(mt, POLY_CLIPS);
3902     else lives_widget_reparent(mt->poly_box, lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3903     break;
3904   case POLY_IN_OUT:
3905     if (!mt->block_selected && mt->poly_state != POLY_IN_OUT) {
3906       notebook_error(LIVES_NOTEBOOK(mt->nb), tab, NB_ERROR_SEL, mt);
3907       return FALSE;
3908     }
3909     if (mt->poly_state != POLY_IN_OUT) polymorph(mt, POLY_IN_OUT);
3910     else lives_widget_reparent(mt->poly_box, lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3911     break;
3912   case POLY_FX_STACK:
3913     if (mt->poly_state != POLY_FX_STACK) polymorph(mt, POLY_FX_STACK);
3914     else lives_widget_reparent(mt->poly_box, lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3915     break;
3916   case POLY_EFFECTS:
3917     if (!mt->block_selected && mt->poly_state != POLY_EFFECTS) {
3918       notebook_error(LIVES_NOTEBOOK(mt->nb), tab, NB_ERROR_SEL, mt);
3919       return FALSE;
3920     }
3921     if (mt->poly_state != POLY_EFFECTS) polymorph(mt, POLY_EFFECTS);
3922     else lives_widget_reparent(mt->poly_box, lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3923     break;
3924   case POLY_TRANS:
3925     if (lives_list_length(mt->selected_tracks) != 2 || mt->region_start == mt->region_end) {
3926       notebook_error(LIVES_NOTEBOOK(mt->nb), tab, NB_ERROR_NOTRANS, mt);
3927       return FALSE;
3928     }
3929     if (mt->poly_state != POLY_TRANS) polymorph(mt, POLY_TRANS);
3930     else {
3931       lives_widget_reparent(mt->poly_box, lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3932     }
3933     break;
3934   case POLY_COMP:
3935     if (!mt->selected_tracks || mt->region_start == mt->region_end) {
3936       notebook_error(LIVES_NOTEBOOK(mt->nb), tab, NB_ERROR_NOCOMP, mt);
3937       return FALSE;
3938     }
3939     if (mt->poly_state != POLY_COMP) polymorph(mt, POLY_COMP);
3940     else lives_widget_reparent(mt->poly_box, lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3941     break;
3942   case POLY_PARAMS:
3943     if (mt->poly_state != POLY_PARAMS && !mt->selected_init_event) {
3944       notebook_error(LIVES_NOTEBOOK(mt->nb), tab, NB_ERROR_NOEFFECT, mt);
3945       return FALSE;
3946     }
3947     lives_widget_reparent(mt->poly_box, lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3948     if (mt->selected_init_event && mt->poly_state != POLY_PARAMS) {
3949       fubar(mt);
3950       polymorph(mt, POLY_PARAMS);
3951     }
3952     break;
3953   default:
3954     break;
3955   }
3956   lives_widget_set_no_show_all(mt->poly_box, FALSE);
3957   lives_widget_show_all(mt->poly_box);
3958   return TRUE;
3959 }
3960 
3961 
set_poly_tab(lives_mt * mt,uint32_t tab)3962 void set_poly_tab(lives_mt * mt, uint32_t tab) {
3963   int page = poly_tab_to_page(tab);
3964   lives_widget_show(lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page));
3965   if (page != lives_notebook_get_current_page(LIVES_NOTEBOOK(mt->nb))) {
3966     lives_notebook_set_current_page(LIVES_NOTEBOOK(mt->nb), page);
3967   } else {
3968     notebook_page(mt->nb, lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), page), page, mt);
3969   }
3970 }
3971 
3972 
select_block(lives_mt * mt)3973 static void select_block(lives_mt * mt) {
3974   track_rect *block = mt->putative_block;
3975   int track;
3976   int filenum;
3977   char *tmp, *tmp2;
3978 
3979   if (block) {
3980     LiVESWidget *eventbox = block->eventbox;
3981 
3982     if (!mainw->files[mt->render_file]->achans || !mt->audio_draws || (mt->opts.back_audio_tracks == 0 ||
3983         eventbox != mt->audio_draws->data))
3984       track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number"));
3985     else track = -1;
3986 
3987     if (!is_audio_eventbox(eventbox)) filenum = get_frame_event_clip(block->start_event, track);
3988     else filenum = get_audio_frame_clip(block->start_event, track);
3989     block->state = BLOCK_SELECTED;
3990     mt->block_selected = block;
3991 
3992     clear_context(mt);
3993 
3994     if (mainw->files[mt->render_file]->achans == 0 || !mt->audio_draws || (mt->opts.back_audio_tracks == 0 ||
3995         eventbox != mt->audio_draws->data))
3996       add_context_label(mt, (tmp2 = lives_markup_printf_escaped(_("Current track: %s (layer %d)\n"),
3997                                     lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "track_name"), track)));
3998     else add_context_label(mt, (tmp2 = (_("Current track: Backing audio\n"))));
3999     lives_free(tmp2);
4000 
4001     add_context_label(mt, (tmp2 = lives_strdup_printf(_("%.2f sec. to %.2f sec.\n"),
4002                                   get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL,
4003                                   get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL + 1. / mt->fps)));
4004     lives_free(tmp2);
4005     add_context_label(mt, (tmp2 = lives_markup_printf_escaped(_("Source: %s"),
4006                                   (tmp = get_menu_name(mainw->files[filenum], FALSE)))));
4007     lives_free(tmp);
4008     lives_free(tmp2);
4009     add_context_label(mt, (_("Right click for context menu.\n")));
4010     add_context_label(mt, (_("Single click on timeline\nto select a frame.\n")));
4011 
4012     lives_widget_set_sensitive(mt->view_in_out, TRUE);
4013     lives_widget_set_sensitive(mt->fx_block, TRUE);
4014 
4015     lives_widget_set_sensitive(mt->fx_blockv, TRUE);
4016     if (mainw->files[mt->render_file]->achans > 0) lives_widget_set_sensitive(mt->fx_blocka, TRUE);
4017 
4018     redraw_eventbox(mt, eventbox);
4019 
4020     multitrack_view_in_out(NULL, mt);
4021     paint_lines(mt, mt->ptr_time, TRUE, NULL);
4022   }
4023 
4024   mt->context_time = -1.;
4025 }
4026 
4027 
pkg_in_list(char * pkgstring)4028 LIVES_LOCAL_INLINE int pkg_in_list(char *pkgstring) {
4029   return lives_list_strcmp_index(pkg_list, pkgstring, FALSE) + 1;
4030 }
4031 
4032 
add_to_pkg_list(char * pkgstring)4033 LIVES_LOCAL_INLINE int add_to_pkg_list(char *pkgstring) {
4034   pkg_list = lives_list_append(pkg_list, pkgstring);
4035   return lives_list_length(pkg_list);
4036 }
4037 
get_pkg_name(int pkgnum)4038 LIVES_LOCAL_INLINE char *get_pkg_name(int pkgnum) {
4039   return strdup(lives_list_nth_data(pkg_list, pkgnum - 1));
4040 }
4041 
4042 
free_pkg_list(void)4043 LIVES_LOCAL_INLINE void free_pkg_list(void) {
4044   lives_list_free_all((LiVESList **)&pkg_list);
4045 }
4046 
4047 static void populate_filter_box(int ninchans, lives_mt * mt, int pkgnum);
4048 
filter_ebox_pressed(LiVESWidget * eventbox,LiVESXEventButton * event,livespointer user_data)4049 static boolean filter_ebox_pressed(LiVESWidget * eventbox, LiVESXEventButton * event, livespointer user_data) {
4050   lives_mt *mt = (lives_mt *)user_data;
4051 
4052   int pkgnum;
4053 
4054   if (mt->is_rendering) return FALSE;
4055 
4056   if ((pkgnum = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "pkgnum"))) != 0) {
4057     populate_filter_box(0, mt, pkgnum);
4058     return FALSE;
4059   }
4060 
4061   mt->selected_filter = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "fxid"));
4062 
4063   if (event->type != LIVES_BUTTON_PRESS) {
4064     // double click
4065     return FALSE;
4066   }
4067 
4068   if (!LIVES_IS_PLAYING) {
4069     // change cursor to mini block
4070     if (!mt->video_draws && !mt->audio_draws) {
4071       return FALSE;
4072     } else {
4073       mt_set_cursor_style(mt, LIVES_CURSOR_FX_BLOCK, FX_BLOCK_WIDTH, FX_BLOCK_HEIGHT, 0, 0, FX_BLOCK_HEIGHT / 2);
4074       mt->hotspot_x = mt->hotspot_y = 0;
4075     }
4076   }
4077 
4078   return FALSE;
4079 }
4080 
4081 
on_drag_filter_end(LiVESWidget * widget,LiVESXEventButton * event,livespointer user_data)4082 static boolean on_drag_filter_end(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
4083   LiVESXWindow *window;
4084   LiVESWidget *eventbox = NULL, *oeventbox;
4085   LiVESWidget *labelbox;
4086   LiVESWidget *ahbox;
4087   LiVESList *list;
4088   lives_mt *mt = (lives_mt *)user_data;
4089   double timesecs = 0.;
4090   int win_x, win_y, nins;
4091   int tchan = 0;
4092   boolean ok = FALSE;
4093 
4094   if (mt->cursor_style != LIVES_CURSOR_FX_BLOCK) {
4095     mt->selected_filter = -1;
4096     return FALSE;
4097   }
4098 
4099   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
4100 
4101   if (mt->is_rendering || LIVES_IS_PLAYING || mt->selected_filter == -1) {
4102     mt->selected_filter = -1;
4103     return FALSE;
4104   }
4105 
4106   window = lives_display_get_window_at_pointer
4107            ((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
4108             mt->display, &win_x, &win_y);
4109 
4110   if (mainw->files[mt->render_file]->achans > 0 && enabled_in_channels(get_weed_filter(mt->selected_filter), TRUE) == 1) {
4111     for (list = mt->audio_draws; list; list = list->next) {
4112       eventbox = (LiVESWidget *)list->data;
4113       if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY)) != 0) continue;
4114       labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "labelbox");
4115       if (lives_widget_get_xwindow(eventbox) == window || lives_widget_get_xwindow(labelbox) == window) {
4116         if (lives_widget_get_xwindow(labelbox) != window) {
4117           lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
4118                                    mt->timeline, &mt->sel_x, &mt->sel_y);
4119           timesecs = get_time_from_x(mt, mt->sel_x);
4120         }
4121         tchan = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number"));
4122         if ((oeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "owner")) != NULL) {
4123           eventbox = oeventbox;
4124         }
4125         ok = TRUE;
4126         break;
4127       }
4128     }
4129   }
4130 
4131   if (!ok) {
4132     for (list = mt->video_draws; list; list = list->next) {
4133       eventbox = (LiVESWidget *)list->data;
4134       if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY)) != 0) continue;
4135       labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "labelbox");
4136       ahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "ahbox");
4137       if (lives_widget_get_xwindow(eventbox) == window || lives_widget_get_xwindow(labelbox) == window ||
4138           lives_widget_get_xwindow(ahbox) == window) {
4139         if (lives_widget_get_xwindow(labelbox) != window) {
4140           lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
4141                                    mt->timeline, &mt->sel_x, &mt->sel_y);
4142           timesecs = get_time_from_x(mt, mt->sel_x);
4143         }
4144         tchan = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number"));
4145         ok = TRUE;
4146         break;
4147       }
4148     }
4149   }
4150 
4151   if (!ok) {
4152     mt->selected_filter = -1;
4153     return FALSE;
4154   }
4155 
4156   mt->current_fx = mt->selected_filter;
4157   mt->selected_filter = -1;
4158 
4159   // create dummy menuitem, needed in order to pass idx value
4160   dummy_menuitem = lives_standard_menu_item_new();
4161   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(dummy_menuitem), "idx", LIVES_INT_TO_POINTER(mt->current_fx));
4162   lives_widget_object_ref_sink(dummy_menuitem);
4163 
4164   nins = enabled_in_channels(get_weed_filter(mt->current_fx), TRUE);
4165   if (nins == 1) {
4166     /*    // filter - either we drop on a region or on a block
4167       if (lives_list_length(mt->selected_tracks)==1&&mt->region_start!=mt->region_end) {
4168       // apply to region
4169       mt_add_region_effect(LIVES_MENU_ITEM(menuitem),mt);
4170       }
4171       else {*/
4172     // always apply to block
4173     track_rect *block;
4174 
4175     if (tchan == -1 && !is_pure_audio(get_weed_filter(mt->current_fx), FALSE)) {
4176       // can only apply audio filters to backing audio
4177       lives_widget_object_unref(dummy_menuitem);
4178       return FALSE;
4179     }
4180 
4181     block = get_block_from_time(eventbox, timesecs, mt);
4182     if (!block) {
4183       lives_widget_object_unref(dummy_menuitem);
4184       return FALSE;
4185     }
4186     nb_ignore = TRUE;
4187     unselect_all(mt);
4188     mt->putative_block = block;
4189 
4190     mt->current_track = get_track_for_block(mt->putative_block);
4191     if (mt->current_track < 0) mt->aud_track_selected = TRUE;
4192     else mt->aud_track_selected = FALSE;
4193     track_select(mt);
4194 
4195     select_block(mt);
4196     nb_ignore = FALSE;
4197     // apply to block
4198     mt->putative_block = NULL;
4199     lives_timer_add(0, mt_add_block_effect_idle, mt); // work around issue in gtk+
4200   } else if (nins == 2) {
4201     // transition
4202     if (lives_list_length(mt->selected_tracks) == 2 && mt->region_start != mt->region_end) {
4203       // apply to region
4204       lives_timer_add_simple(0, mt_add_region_effect_idle, mt);
4205     }
4206   } else if (nins >= 1000000) {
4207     // compositor
4208     if (mt->selected_tracks && mt->region_start != mt->region_end) {
4209       // apply to region
4210       lives_timer_add_simple(0, mt_add_region_effect_idle, mt);
4211     }
4212   }
4213 
4214   //lives_widget_destroy(menuitem);
4215   return FALSE;
4216 }
4217 
4218 
add_to_listbox(lives_mt * mt,LiVESWidget * xeventbox,char * fname,boolean add_top)4219 static void add_to_listbox(lives_mt * mt, LiVESWidget * xeventbox, char *fname, boolean add_top) {
4220   LiVESWidget *label;
4221   int offswidth = lives_widget_get_allocation_width(mt->nb) / 3.;
4222 
4223   lives_widget_add_events(xeventbox, LIVES_BUTTON_RELEASE_MASK | LIVES_BUTTON_PRESS_MASK);
4224   if (palette->style & STYLE_1) {
4225     lives_widget_set_bg_color(xeventbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
4226   }
4227 
4228   lives_container_set_border_width(LIVES_CONTAINER(xeventbox), widget_opts.border_width >> 1);
4229   widget_opts.mnemonic_label = FALSE;
4230   label = lives_standard_label_new(fname);
4231   widget_opts.mnemonic_label = TRUE;
4232 
4233   if (palette->style & STYLE_1) {
4234     lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
4235     lives_widget_set_fg_color(xeventbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
4236     lives_widget_set_can_focus(xeventbox, TRUE);
4237   }
4238 
4239   lives_container_add(LIVES_CONTAINER(xeventbox), label);
4240   lives_widget_set_margin_left(label, offswidth);
4241 
4242   // pack pkgs and a/v transitions first
4243 
4244   if (add_top) lives_box_pack_top(LIVES_BOX(mt->fx_list_vbox), xeventbox, FALSE, FALSE, 0);
4245   else lives_box_pack_start(LIVES_BOX(mt->fx_list_vbox), xeventbox, TRUE, FALSE, 0);
4246 
4247   lives_signal_sync_connect(LIVES_GUI_OBJECT(xeventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
4248                             LIVES_GUI_CALLBACK(filter_ebox_pressed), (livespointer)mt);
4249   lives_signal_sync_connect(LIVES_GUI_OBJECT(xeventbox), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
4250                             LIVES_GUI_CALLBACK(on_drag_filter_end), (livespointer)mt);
4251 }
4252 
4253 
populate_filter_box(int ninchans,lives_mt * mt,int pkgnum)4254 static void populate_filter_box(int ninchans, lives_mt * mt, int pkgnum) {
4255   static int oxninchans = 0;
4256 
4257   LiVESWidget *eventbox = NULL, *xeventbox;
4258   char *fname, *pkgstring = NULL, *pkg_name = NULL, *catstring;
4259 
4260   int nfilts = rte_get_numfilters();
4261   int nins;
4262 
4263   register int i, j;
4264 
4265   if (mt->fx_list_scroll) lives_widget_destroy(mt->fx_list_scroll);
4266   mt->fx_list_scroll = lives_scrolled_window_new(NULL, NULL);
4267   lives_scrolled_window_set_policy(LIVES_SCROLLED_WINDOW(mt->fx_list_scroll),
4268                                    LIVES_POLICY_AUTOMATIC, LIVES_POLICY_AUTOMATIC);
4269   lives_box_pack_start(LIVES_BOX(mt->fx_list_box), mt->fx_list_scroll, TRUE, TRUE, 0);
4270 
4271   mt->fx_list_vbox = lives_vbox_new(FALSE, widget_opts.packing_height);
4272   lives_container_set_border_width(LIVES_CONTAINER(mt->fx_list_vbox), widget_opts.border_width);
4273   lives_scrolled_window_add_with_viewport(LIVES_SCROLLED_WINDOW(mt->fx_list_scroll), mt->fx_list_vbox);
4274   lives_widget_set_bg_color(lives_bin_get_child(LIVES_BIN(mt->fx_list_scroll)),
4275                             LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
4276   lives_widget_set_fg_color(mt->fx_list_vbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
4277   lives_widget_show_all(mt->fx_list_scroll);
4278 
4279   if (!mt->block_selected && ninchans == 1) return;
4280 
4281   if (mt->block_selected) eventbox = mt->block_selected->eventbox;
4282 
4283   if (pkgnum != 0) {
4284     ninchans = oxninchans;
4285     if (pkgnum == -1) pkgnum = 0;
4286     else pkg_name = get_pkg_name(pkgnum);
4287   }
4288 
4289   if (pkgnum == 0) free_pkg_list();
4290 
4291   oxninchans = ninchans;
4292 
4293   catstring = (ninchans == 1 ? lives_fx_cat_to_text(LIVES_FX_CAT_EFFECT, TRUE) :
4294                ninchans == 2 ? lives_fx_cat_to_text(LIVES_FX_CAT_TRANSITION, TRUE) :
4295                lives_fx_cat_to_text(LIVES_FX_CAT_COMPOSITOR, TRUE));
4296 
4297   for (i = 0; i < nfilts; i++) {
4298     int sorted = weed_get_sorted_filter(i);
4299     weed_plant_t *filter = get_weed_filter(sorted);
4300     if (filter && !weed_plant_has_leaf(filter, WEED_LEAF_HOST_MENU_HIDE)) {
4301       int mandouts = 0, nouts = 0;
4302       weed_plant_t **ctmpls;
4303 
4304       if ((is_pure_audio(filter, FALSE) && (!eventbox || !is_audio_eventbox(eventbox))) ||
4305           (!is_pure_audio(filter, FALSE) && eventbox && is_audio_eventbox(eventbox))) continue;
4306 
4307       if (weed_filter_hints_unstable(filter) && !prefs->unstable_fx) continue;
4308 
4309       nins = enabled_in_channels(filter, TRUE);
4310 
4311       ctmpls = weed_filter_get_out_chantmpls(filter, &nouts);
4312       for (j = 0; j < nouts; j++) {
4313         if (!weed_chantmpl_is_optional(ctmpls[j])) {
4314           if (!has_non_alpha_palette(ctmpls[j], filter)) break;
4315           mandouts++;
4316         }
4317       }
4318       lives_freep((void **)&ctmpls);
4319       if (j < nouts) continue;
4320 
4321       if ((nins == ninchans || (ninchans == 1000000 && nins >= ninchans)) && mandouts == 1) {
4322         pkgnum = 0;
4323         fname = weed_filter_idx_get_name(sorted, TRUE, TRUE);
4324 
4325         if ((pkgstring = weed_filter_idx_get_package_name(sorted))) {
4326           // filter is in package
4327           if (pkg_name && strcmp(pkgstring, pkg_name)) {
4328             // but wrong one
4329             lives_free(fname);
4330             lives_freep((void **)&pkgstring);
4331             continue;
4332           }
4333           if (!pkg_name || !(*pkg_name)) {
4334             // no pkg requested
4335             if (!(pkgnum = pkg_in_list(pkgstring))) {
4336               // if this is the first for this package, add to list and show it
4337               lives_free(fname);
4338               fname = lives_strdup_printf(_("%s from %s package (CLICK TO SHOW)     ---->"), catstring, pkgstring);
4339               pkgnum = add_to_pkg_list(pkgstring); // list will free the string later
4340               pkgstring = NULL;
4341             } else {
4342               // pkg already in list, skip
4343               lives_free(fname);
4344               lives_freep((void **)&pkgstring);
4345               continue;
4346             }
4347           }
4348           // pkg matched
4349         }
4350 
4351         if (!pkgnum) {
4352           // filter is in no package
4353           if (pkg_name && *pkg_name && !pkgstring) {
4354             // skip if pkg was requested
4355             lives_free(fname);
4356             continue;
4357           }
4358           lives_free(fname);
4359           fname = weed_filter_idx_get_name(sorted, TRUE, TRUE);
4360         }
4361 
4362         xeventbox = lives_event_box_new();
4363         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "fxid", LIVES_INT_TO_POINTER(sorted));
4364         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "pkgnum", LIVES_INT_TO_POINTER(pkgnum));
4365 
4366         //add_to_listbox(mt, xeventbox, fname, (pkgnum != 0 || get_transition_param(filter, FALSE) == -1 || !has_video_chans_in(filter, FALSE)));
4367 
4368         add_to_listbox(mt, xeventbox, fname, FALSE);
4369         lives_free(fname);
4370       }
4371     }
4372   }
4373   if (pkg_name) {
4374     char *tmp;
4375     xeventbox = lives_event_box_new();
4376     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "fxid", LIVES_INT_TO_POINTER(0));
4377     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "pkgnum", LIVES_INT_TO_POINTER(-1));
4378     fname = lives_strdup_printf(_("<----     SHOW ALL %s"), (tmp = lives_utf8_strup(catstring, -1)));
4379     lives_free(tmp);
4380     add_to_listbox(mt, xeventbox, fname, TRUE);
4381   }
4382   lives_free(catstring);
4383   lives_freep((void **)&pkg_name);
4384   lives_widget_show_all(mt->fx_list_box);
4385 }
4386 
4387 
mt_selblock(LiVESMenuItem * menuitem,livespointer user_data)4388 static track_rect *mt_selblock(LiVESMenuItem * menuitem, livespointer user_data) {
4389   // ctrl-Enter - select block at current time/track
4390   lives_mt *mt = (lives_mt *)user_data;
4391   LiVESWidget *eventbox;
4392   double timesecs = mt->ptr_time;
4393   boolean desel = TRUE;
4394 
4395   if (mt->current_track == -1 || mt->aud_track_selected)
4396     eventbox = (LiVESWidget *)lives_list_nth_data(mt->audio_draws, mt->current_track + mt->opts.back_audio_tracks);
4397   else eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
4398 
4399   if (!menuitem) return get_block_from_time(eventbox, timesecs, mt);
4400 
4401   mt->putative_block = get_block_from_time(eventbox, timesecs, mt);
4402 
4403   if (mt->putative_block && mt->putative_block->state == BLOCK_UNSELECTED) desel = FALSE;
4404 
4405   unselect_all(mt);
4406   if (!desel) select_block((lives_mt *)user_data);
4407 
4408   return mt->putative_block;
4409 }
4410 
4411 
mt_center_on_cursor(LiVESMenuItem * menuitem,livespointer user_data)4412 void mt_center_on_cursor(LiVESMenuItem * menuitem, livespointer user_data) {
4413   lives_mt *mt = (lives_mt *)user_data;
4414   mt_zoom(mt, 1.);
4415   paint_lines(mt, mt->ptr_time, TRUE, NULL);
4416 }
4417 
4418 
mt_zoom_in(LiVESMenuItem * menuitem,livespointer user_data)4419 void mt_zoom_in(LiVESMenuItem * menuitem, livespointer user_data) {
4420   lives_mt *mt = (lives_mt *)user_data;
4421   mt_zoom(mt, 0.5);
4422   if (LIVES_IS_PLAYING && mt->opts.follow_playback) {
4423     mt_zoom(mt, 1.);
4424   }
4425   paint_lines(mt, mt->ptr_time, TRUE, NULL);
4426 }
4427 
4428 
mt_zoom_out(LiVESMenuItem * menuitem,livespointer user_data)4429 void mt_zoom_out(LiVESMenuItem * menuitem, livespointer user_data) {
4430   lives_mt *mt = (lives_mt *)user_data;
4431   mt_zoom(mt, 2.);
4432   if (LIVES_IS_PLAYING && mt->opts.follow_playback) {
4433     mt_zoom(mt, 1.);
4434   }
4435   paint_lines(mt, mt->ptr_time, TRUE, NULL);
4436 }
4437 
4438 
hpaned_position(LiVESWidgetObject * object,livespointer pspec,livespointer user_data)4439 static void hpaned_position(LiVESWidgetObject * object, livespointer pspec, livespointer user_data) {
4440   lives_mt *mt = (lives_mt *)user_data;
4441   mt->opts.hpaned_pos = lives_paned_get_position(LIVES_PANED(object));
4442 }
4443 
4444 
no_time_selected(lives_mt * mt)4445 static void no_time_selected(lives_mt * mt) {
4446   clear_context(mt);
4447   add_context_label(mt, _("You can click and drag\nbelow the timeline"));
4448   add_context_label(mt, _("to select a time region.\n"));
4449 }
4450 
4451 
mt_spin_start_value_changed(LiVESSpinButton * spinbutton,livespointer user_data)4452 void mt_spin_start_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
4453   lives_mt *mt = (lives_mt *)user_data;
4454   boolean has_region = (mt->region_start != mt->region_end);
4455 
4456   if (!LIVES_IS_INTERACTIVE) return;
4457 
4458   lives_signal_handler_block(mt->spinbutton_start, mt->spin_start_func);
4459   mt->region_start = q_dbl(lives_spin_button_get_value(spinbutton), mt->fps) / TICKS_PER_SECOND_DBL;
4460   lives_spin_button_set_value(spinbutton, mt->region_start);
4461   lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_end), mt->region_start, mt->end_secs);
4462   lives_widget_queue_draw(mt->timeline_reg);
4463   draw_region(mt);
4464   do_sel_context(mt);
4465 
4466   if ((((mt->region_start != mt->region_end && !has_region) || (mt->region_start == mt->region_end && has_region))) &&
4467       mt->event_list && get_first_event(mt->event_list)) {
4468     lives_mt_poly_state_t statep = get_poly_state_from_page(mt);
4469     if (mt->selected_tracks) {
4470       lives_widget_set_sensitive(mt->split_sel, mt_selblock(NULL, (livespointer)mt) != NULL);
4471       if (mt->region_start != mt->region_end) {
4472         lives_widget_set_sensitive(mt->playsel, TRUE);
4473         lives_widget_set_sensitive(mt->ins_gap_sel, TRUE);
4474         lives_widget_set_sensitive(mt->remove_first_gaps, TRUE);
4475         lives_widget_set_sensitive(mt->fx_region, TRUE);
4476         switch (lives_list_length(mt->selected_tracks)) {
4477         case 1:
4478           lives_widget_set_sensitive(mt->fx_region_2av, FALSE);
4479           lives_widget_set_sensitive(mt->fx_region_2v, FALSE);
4480           lives_widget_set_sensitive(mt->fx_region_2a, FALSE);
4481           break;
4482         case 2:
4483           lives_widget_set_sensitive(mt->fx_region_v, FALSE);
4484           lives_widget_set_sensitive(mt->fx_region_a, FALSE);
4485           if (!mt->opts.pertrack_audio)
4486             lives_widget_set_sensitive(mt->fx_region_2a, FALSE);
4487           break;
4488         default:
4489           break;
4490         }
4491       }
4492       // update labels
4493       if (statep == POLY_TRANS || statep == POLY_COMP) {
4494         polymorph(mt, POLY_NONE);
4495         polymorph(mt, statep);
4496       }
4497     }
4498   }
4499 
4500   if (mt->region_start == mt->region_end) no_time_selected(mt);
4501 
4502   lives_signal_handler_unblock(mt->spinbutton_start, mt->spin_start_func);
4503 }
4504 
4505 
mt_spin_end_value_changed(LiVESSpinButton * spinbutton,livespointer user_data)4506 void mt_spin_end_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
4507   lives_mt *mt = (lives_mt *)user_data;
4508   boolean has_region = (mt->region_start != mt->region_end);
4509 
4510   if (!LIVES_IS_INTERACTIVE) return;
4511 
4512   lives_signal_handler_block(mt->spinbutton_end, mt->spin_end_func);
4513   mt->region_end = q_dbl(lives_spin_button_get_value(spinbutton), mt->fps) / TICKS_PER_SECOND_DBL;
4514   lives_spin_button_set_value(spinbutton, mt->region_end);
4515   lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_start), 0., mt->region_end);
4516   lives_widget_queue_draw(mt->timeline_reg);
4517   draw_region(mt);
4518   do_sel_context(mt);
4519 
4520   if ((((mt->region_start != mt->region_end && !has_region) || (mt->region_start == mt->region_end && has_region))) &&
4521       mt->event_list && get_first_event(mt->event_list)) {
4522     lives_mt_poly_state_t statep = get_poly_state_from_page(mt);
4523     if (mt->selected_tracks) {
4524       lives_widget_set_sensitive(mt->split_sel, TRUE);
4525       if (mt->region_start != mt->region_end) {
4526         lives_widget_set_sensitive(mt->playsel, TRUE);
4527         lives_widget_set_sensitive(mt->ins_gap_sel, TRUE);
4528         lives_widget_set_sensitive(mt->remove_gaps, TRUE);
4529         lives_widget_set_sensitive(mt->remove_first_gaps, TRUE);
4530         lives_widget_set_sensitive(mt->fx_region, TRUE);
4531         switch (lives_list_length(mt->selected_tracks)) {
4532         case 1:
4533           if (mainw->files[mt->render_file]->achans == 0) lives_widget_set_sensitive(mt->fx_region_a, FALSE);
4534           lives_widget_set_sensitive(mt->fx_region_2a, FALSE);
4535           lives_widget_set_sensitive(mt->fx_region_2v, FALSE);
4536           lives_widget_set_sensitive(mt->fx_region_2av, FALSE);
4537           break;
4538         case 2:
4539           lives_widget_set_sensitive(mt->fx_region_v, FALSE);
4540           lives_widget_set_sensitive(mt->fx_region_a, FALSE);
4541           if (!mt->opts.pertrack_audio)
4542             lives_widget_set_sensitive(mt->fx_region_2a, FALSE);
4543           break;
4544         default:
4545           break;
4546         }
4547       }
4548       // update labels
4549       if (statep == POLY_TRANS || statep == POLY_COMP) {
4550         polymorph(mt, POLY_NONE);
4551         polymorph(mt, statep);
4552       }
4553     }
4554   }
4555 
4556   if (mt->region_start == mt->region_end) no_time_selected(mt);
4557 
4558   lives_signal_handler_unblock(mt->spinbutton_end, mt->spin_end_func);
4559 }
4560 
4561 
in_out_ebox_pressed(LiVESWidget * eventbox,LiVESXEventButton * event,livespointer user_data)4562 static boolean in_out_ebox_pressed(LiVESWidget * eventbox, LiVESXEventButton * event, livespointer user_data) {
4563   int height;
4564   double width;
4565   int ebwidth;
4566   lives_clip_t *sfile;
4567   int file;
4568   lives_mt *mt = (lives_mt *)user_data;
4569 
4570   if (!LIVES_IS_INTERACTIVE) return FALSE;
4571 
4572   if (mt->block_selected) return FALSE;
4573 
4574   ebwidth = lives_widget_get_allocation_width(mt->timeline);
4575   file = mt_file_from_clip(mt, mt->clip_selected);
4576   sfile = mainw->files[file];
4577 
4578   // change cursor to block
4579   if (!mt->video_draws && !mt->audio_draws) {
4580     return FALSE;
4581   } else {
4582     if (sfile->frames > 0) {
4583       if (!mt->opts.ign_ins_sel) {
4584         width = (sfile->end - sfile->start + 1.) / sfile->fps;
4585       } else {
4586         width = sfile->frames / sfile->fps;
4587       }
4588     } else width = sfile->laudio_time;
4589     if (width == 0) return FALSE;
4590     width = width / (mt->tl_max - mt->tl_min) * (double)ebwidth;
4591     if (width > ebwidth) width = ebwidth;
4592     if (width < 2) width = 2;
4593     height = get_track_height(mt);
4594 
4595     lives_set_cursor_style(LIVES_CURSOR_NORMAL, eventbox);
4596     mt_set_cursor_style(mt, LIVES_CURSOR_BLOCK, width, height, file, 0, height / 2);
4597 
4598     mt->hotspot_x = mt->hotspot_y = 0;
4599   }
4600 
4601   return FALSE;
4602 }
4603 
4604 
do_clip_context(lives_mt * mt,LiVESXEventButton * event,lives_clip_t * sfile)4605 static void do_clip_context(lives_mt * mt, LiVESXEventButton * event, lives_clip_t *sfile) {
4606   // pop up a context menu when clip is right clicked on
4607 
4608   LiVESWidget *edit_start_end, *edit_clipedit, *close_clip, *show_clipinfo;
4609   LiVESWidget *menu = lives_menu_new();
4610 
4611   if (!LIVES_IS_INTERACTIVE) return;
4612 
4613   lives_menu_set_title(LIVES_MENU(menu), _("Selected Clip"));
4614 
4615   if (sfile->frames > 0) {
4616     edit_start_end = lives_standard_menu_item_new_with_label(_("_Adjust Start and End Points"));
4617     lives_signal_connect(LIVES_GUI_OBJECT(edit_start_end), LIVES_WIDGET_ACTIVATE_SIGNAL,
4618                          LIVES_GUI_CALLBACK(edit_start_end_cb),
4619                          (livespointer)mt);
4620 
4621     lives_container_add(LIVES_CONTAINER(menu), edit_start_end);
4622   }
4623 
4624   edit_clipedit = lives_standard_menu_item_new_with_label(_("_Edit/Encode in Clip Editor"));
4625   lives_signal_sync_connect(LIVES_GUI_OBJECT(edit_clipedit), LIVES_WIDGET_ACTIVATE_SIGNAL,
4626                             LIVES_GUI_CALLBACK(multitrack_end_cb),
4627                             (livespointer)mt);
4628 
4629   lives_container_add(LIVES_CONTAINER(menu), edit_clipedit);
4630 
4631   show_clipinfo = lives_standard_menu_item_new_with_label(_("_Show Clip Information"));
4632   lives_signal_connect(LIVES_GUI_OBJECT(show_clipinfo), LIVES_WIDGET_ACTIVATE_SIGNAL,
4633                        LIVES_GUI_CALLBACK(show_clipinfo_cb),
4634                        (livespointer)mt);
4635 
4636   lives_container_add(LIVES_CONTAINER(menu), show_clipinfo);
4637 
4638   close_clip = lives_standard_menu_item_new_with_label(_("_Close this Clip"));
4639   lives_signal_connect(LIVES_GUI_OBJECT(close_clip), LIVES_WIDGET_ACTIVATE_SIGNAL,
4640                        LIVES_GUI_CALLBACK(close_clip_cb),
4641                        (livespointer)mt);
4642 
4643   lives_container_add(LIVES_CONTAINER(menu), close_clip);
4644 
4645   if (palette->style & STYLE_1) {
4646     set_child_alt_colour(menu, TRUE);
4647     lives_widget_set_bg_color(menu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
4648     lives_widget_set_fg_color(menu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
4649   }
4650 
4651   lives_widget_show_all(menu);
4652   lives_menu_popup(LIVES_MENU(menu), event);
4653 }
4654 
4655 
clip_ebox_pressed(LiVESWidget * eventbox,LiVESXEventButton * event,livespointer user_data)4656 static boolean clip_ebox_pressed(LiVESWidget * eventbox, LiVESXEventButton * event, livespointer user_data) {
4657   lives_mt *mt = (lives_mt *)user_data;
4658   lives_clip_t *sfile;
4659   double width;
4660   int height;
4661   int ebwidth;
4662   int file;
4663 
4664   if (!mt->is_ready) return FALSE;
4665 
4666   if (!LIVES_IS_INTERACTIVE) return FALSE;
4667 
4668   if (event->type != LIVES_BUTTON_PRESS && !mt->is_rendering) {
4669     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
4670     // double click, open up in clip editor
4671     if (!LIVES_IS_PLAYING) multitrack_delete(mt, !(prefs->warning_mask & WARN_MASK_EXIT_MT));
4672     return FALSE;
4673   }
4674 
4675   mt->clip_selected = get_box_child_index(LIVES_BOX(mt->clip_inner_box), eventbox);
4676   mt_clip_select(mt, FALSE);
4677 
4678   ebwidth = lives_widget_get_allocation_width(mt->timeline);
4679   file = mt_file_from_clip(mt, mt->clip_selected);
4680   sfile = mainw->files[file];
4681 
4682   if (event->button == 3) {
4683     double timesecs;
4684     lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
4685                              mt->timeline, &mt->sel_x, &mt->sel_y);
4686     timesecs = get_time_from_x(mt, mt->sel_x);
4687     lives_ruler_set_value(LIVES_RULER(mt->timeline), timesecs);
4688     lives_widget_queue_draw(mt->timeline);
4689     do_clip_context(mt, event, sfile);
4690     return FALSE;
4691   }
4692 
4693   // change cursor to block
4694   if (!mt->video_draws && !mt->audio_draws) {
4695     return FALSE;
4696   } else {
4697     if (sfile->frames > 0) {
4698       if (!mt->opts.ign_ins_sel) {
4699         width = (sfile->end - sfile->start + 1.) / sfile->fps;
4700       } else {
4701         width = sfile->frames / sfile->fps;
4702       }
4703     } else width = sfile->laudio_time;
4704     if (width == 0) return FALSE;
4705     width = width / (mt->tl_max - mt->tl_min) * (double)ebwidth;
4706     if (width > ebwidth) width = ebwidth;
4707     if (width < 2) width = 2;
4708     height = get_track_height(mt);
4709     lives_set_cursor_style(LIVES_CURSOR_NORMAL, eventbox);
4710     mt_set_cursor_style(mt, LIVES_CURSOR_BLOCK, width, height, file, 0, height / 2);
4711     mt->hotspot_x = mt->hotspot_y = 0;
4712   }
4713 
4714   return FALSE;
4715 }
4716 
4717 
on_drag_clip_end(LiVESWidget * widget,LiVESXEventButton * event,livespointer user_data)4718 static boolean on_drag_clip_end(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
4719   lives_mt *mt = (lives_mt *)user_data;
4720 
4721   LiVESXWindow *window;
4722   LiVESWidget *eventbox;
4723   LiVESWidget *labelbox;
4724   LiVESWidget *ahbox;
4725 
4726   double timesecs, osecs;
4727 
4728   int win_x, win_y;
4729 
4730   if (!LIVES_IS_INTERACTIVE) {
4731     g_print("booo\n");
4732     return FALSE;
4733   }
4734 
4735   if (mt->is_rendering) return FALSE;
4736 
4737   if (mt->cursor_style != LIVES_CURSOR_BLOCK) return FALSE;
4738 
4739   osecs = mt->ptr_time;
4740 
4741   lives_xwindow_set_cursor(lives_widget_get_xwindow(LIVES_MAIN_WINDOW_WIDGET), NULL);
4742   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
4743   //lives_widget_context_update();
4744 
4745   window = lives_display_get_window_at_pointer
4746            ((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
4747             mt->display, &win_x, &win_y);
4748 
4749   if (mainw->files[mt->render_file]->achans > 0 && mt->opts.back_audio_tracks > 0 &&
4750       LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), HIDDEN_KEY)) == 0) {
4751     labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "labelbox");
4752     ahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), "ahbox");
4753 
4754     if (lives_widget_get_xwindow(LIVES_WIDGET(mt->audio_draws->data)) == window ||
4755         lives_widget_get_xwindow(labelbox) == window || lives_widget_get_xwindow(ahbox) == window) {
4756 
4757       // insert in backing audio
4758       if (lives_widget_get_xwindow(labelbox) == window || lives_widget_get_xwindow(ahbox) == window) timesecs = 0.;
4759       else {
4760         lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
4761                                  mt->timeline, &mt->sel_x, &mt->sel_y);
4762         timesecs = get_time_from_x(mt, mt->sel_x);
4763       }
4764       mt->current_track = -1;
4765       track_select(mt);
4766 
4767       if (!LIVES_IS_PLAYING) {
4768         mt->ptr_time = lives_ruler_set_value(LIVES_RULER(mt->timeline), timesecs);
4769         if (!mt->is_paused) {
4770           if (mt->poly_state == POLY_FX_STACK) {
4771             polymorph(mt, POLY_FX_STACK);
4772           }
4773           mt_show_current_frame(mt, FALSE);
4774           if (timesecs > 0.) {
4775             lives_widget_set_sensitive(mt->rewind, TRUE);
4776             lives_widget_set_sensitive(mainw->m_rewindbutton, TRUE);
4777           }
4778         }
4779         lives_widget_queue_draw(mt->timeline);
4780       }
4781 
4782       if (!LIVES_IS_PLAYING && (mainw->files[mt->file_selected]->laudio_time >
4783                                 ((mainw->files[mt->file_selected]->start - 1.) / mainw->files[mt->file_selected]->fps) ||
4784                                 (mainw->files[mt->file_selected]->laudio_time > 0. && mt->opts.ign_ins_sel)))
4785         insert_audio_here_cb(NULL, (livespointer)mt);
4786       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
4787       if (mt->is_paused) mt->ptr_time = lives_ruler_set_value(LIVES_RULER(mt->timeline), osecs);
4788       return FALSE;
4789     }
4790   }
4791 
4792   for (LiVESList *list = mt->video_draws; list; list = list->next) {
4793     eventbox = (LiVESWidget *)list->data;
4794 
4795     if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY)) != 0) continue;
4796 
4797     labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "labelbox");
4798     ahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "ahbox");
4799     if (lives_widget_get_xwindow(eventbox) == window || lives_widget_get_xwindow(labelbox) == window ||
4800         lives_widget_get_xwindow(ahbox) == window) {
4801       if (lives_widget_get_xwindow(labelbox) == window || lives_widget_get_xwindow(ahbox) == window) timesecs = 0.;
4802       else {
4803         lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
4804                                  mt->timeline, &mt->sel_x, &mt->sel_y);
4805         timesecs = get_time_from_x(mt, mt->sel_x);
4806       }
4807       mt->current_track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number"));
4808       mt->aud_track_selected = FALSE;
4809 
4810       track_select(mt);
4811 
4812       if (!LIVES_IS_PLAYING) {
4813         mt->ptr_time = lives_ruler_set_value(LIVES_RULER(mt->timeline), timesecs);
4814         if (!mt->is_paused) {
4815           mt_show_current_frame(mt, FALSE);
4816           if (timesecs > 0.) {
4817             lives_widget_set_sensitive(mt->rewind, TRUE);
4818             lives_widget_set_sensitive(mainw->m_rewindbutton, TRUE);
4819           }
4820         }
4821         lives_widget_queue_draw(mt->timeline);
4822       }
4823       if (!LIVES_IS_PLAYING && mainw->files[mt->file_selected]->frames > 0) {
4824         insert_here_cb(NULL, mt);
4825       }
4826       break;
4827     }
4828   }
4829 
4830   if (mt->is_paused) mt->ptr_time = lives_ruler_set_value(LIVES_RULER(mt->timeline), osecs);
4831   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
4832   return FALSE;
4833 }
4834 
4835 
on_clipbox_enter(LiVESWidget * widget,LiVESXEventCrossing * event,livespointer user_data)4836 static boolean on_clipbox_enter(LiVESWidget * widget, LiVESXEventCrossing * event, livespointer user_data) {
4837   lives_mt *mt = (lives_mt *)user_data;
4838   if (mt->cursor_style != LIVES_CURSOR_NORMAL) return FALSE;
4839   lives_set_cursor_style(LIVES_CURSOR_HAND2, widget);
4840   return FALSE;
4841 }
4842 
4843 
mt_init_start_end_spins(lives_mt * mt)4844 void mt_init_start_end_spins(lives_mt * mt) {
4845   LiVESWidget *hbox;
4846   char *tmp, *tmp2;
4847   int dpw = widget_opts.packing_width;
4848   int wois = widget_opts.icon_size;
4849 
4850   hbox = lives_hbox_new(FALSE, 0);
4851   lives_widget_apply_theme(hbox, LIVES_WIDGET_STATE_NORMAL);
4852   lives_box_pack_start(LIVES_BOX(mt->tlx_vbox), hbox, FALSE, FALSE, 6);
4853 
4854   mt->amixer_button = lives_standard_button_new_full(_("Open Audio _Mixer"), DEF_BUTTON_WIDTH,
4855                       DEF_BUTTON_HEIGHT, LIVES_BOX(hbox), TRUE, NULL);
4856 
4857   mt->amix_label = widget_opts.last_label;
4858 
4859   lives_widget_add_accelerator(mt->amixer_button, LIVES_WIDGET_CLICKED_SIGNAL, mt->accel_group,
4860                                LIVES_KEY_m, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
4861 
4862   if (!mainw->files[mt->render_file]->achans || !mt->opts.pertrack_audio) lives_widget_set_sensitive(mt->amixer_button, FALSE);
4863 
4864   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->amixer_button), LIVES_WIDGET_CLICKED_SIGNAL,
4865                             LIVES_GUI_CALLBACK(amixer_show), (livespointer)mt);
4866   widget_opts.icon_size = LIVES_ICON_SIZE_SMALL_TOOLBAR;
4867   mt->bleedthru = lives_glowing_check_button_new((tmp = _("  Bleedthru  ")), LIVES_BOX(hbox),
4868                   (tmp2 = H_("When active, all layers will be audible regardless of visibility")),
4869                   &mt->opts.audio_bleedthru);
4870   widget_opts.icon_size = wois;
4871   lives_free(tmp); lives_free(tmp2);
4872 
4873   widget_opts.packing_width = MAIN_SPIN_SPACER;
4874   mt->spinbutton_start = lives_standard_spin_button_new(NULL, 0., 0., 10000000., 1. / mt->fps, 1. / mt->fps, 3,
4875                          NULL, NULL);
4876   lives_spin_button_set_snap_to_ticks(LIVES_SPIN_BUTTON(mt->spinbutton_start), TRUE);
4877   widget_opts.packing_width = dpw;
4878   lives_widget_set_valign(mt->spinbutton_start, LIVES_ALIGN_CENTER);
4879 
4880   lives_box_pack_start(LIVES_BOX(hbox), mt->spinbutton_start, TRUE, FALSE, MAIN_SPIN_SPACER);
4881 
4882   mt->l_sel_arrow = lives_arrow_new(LIVES_ARROW_LEFT, LIVES_SHADOW_OUT);
4883   lives_box_pack_start(LIVES_BOX(hbox), mt->l_sel_arrow, FALSE, FALSE, 0);
4884 
4885   lives_entry_set_width_chars(LIVES_ENTRY(mt->spinbutton_start), COMBOWIDTHCHARS);
4886   mt->sel_label = lives_standard_label_new(NULL);
4887 
4888   set_sel_label(mt->sel_label);
4889   lives_box_pack_start(LIVES_BOX(hbox), mt->sel_label, FALSE, FALSE, 0);
4890 
4891   mt->r_sel_arrow = lives_arrow_new(LIVES_ARROW_RIGHT, LIVES_SHADOW_OUT);
4892   lives_box_pack_start(LIVES_BOX(hbox), mt->r_sel_arrow, FALSE, FALSE, 3);
4893 
4894   widget_opts.packing_width = MAIN_SPIN_SPACER;
4895   mt->spinbutton_end = lives_standard_spin_button_new(NULL, 0., 0., 10000000., 1. / mt->fps, 1. / mt->fps, 3,
4896                        NULL, NULL);
4897   lives_spin_button_set_snap_to_ticks(LIVES_SPIN_BUTTON(mt->spinbutton_end), TRUE);
4898   lives_widget_set_valign(mt->spinbutton_end, LIVES_ALIGN_CENTER);
4899 
4900   widget_opts.packing_width = dpw;
4901 
4902   lives_entry_set_width_chars(LIVES_ENTRY(mt->spinbutton_end), COMBOWIDTHCHARS);
4903 
4904   lives_box_pack_start(LIVES_BOX(hbox), mt->spinbutton_end, TRUE, FALSE, MAIN_SPIN_SPACER);
4905 
4906   mt->spin_start_func = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(mt->spinbutton_start), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
4907                         LIVES_GUI_CALLBACK(mt_spin_start_value_changed),
4908                         (livespointer)mt);
4909 
4910   mt->spin_end_func = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(mt->spinbutton_end), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
4911                       LIVES_GUI_CALLBACK(mt_spin_end_value_changed),
4912                       (livespointer)mt);
4913 }
4914 
4915 
mouse_mode_context(lives_mt * mt)4916 void mouse_mode_context(lives_mt * mt) {
4917   char *fname, *text, *tmp;
4918   clear_context(mt);
4919 
4920   if (mt->opts.mouse_mode == MOUSE_MODE_MOVE) {
4921     add_context_label(mt, (_("Single click on timeline")));
4922     add_context_label(mt, (_("to select a frame.")));
4923     add_context_label(mt, (_("Double click or right click on timeline")));
4924     add_context_label(mt, (_("to select a block.")));
4925     add_context_label(mt, (_("\nClips can be dragged")));
4926     add_context_label(mt, (_("onto the timeline.")));
4927 
4928     add_context_label(mt, (_("Mouse mode is: Move")));
4929     add_context_label(mt, (_("clips can be moved around.")));
4930   } else if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) {
4931     add_context_label(mt, (_("Mouse mode is: Select.")));
4932     add_context_label(mt, (_("Drag with mouse on timeline")));
4933     add_context_label(mt, (_("to select tracks and time.")));
4934   }
4935   if (prefs->atrans_fx != -1) fname = weed_filter_idx_get_name(prefs->atrans_fx, FALSE, FALSE);
4936   else fname = lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_NONE]);
4937   add_context_label(mt, (_("\nAutotransition effect is:")));
4938   text = lives_strdup_printf("<b>%s</b>", (tmp = lives_markup_escape_text(fname, -1)));
4939   add_context_label(mt, (text));
4940   lives_free(tmp);
4941   lives_free(text);
4942   lives_free(fname);
4943 }
4944 
4945 
update_insert_mode(lives_mt * mt)4946 void update_insert_mode(lives_mt * mt) {
4947   const char *mtext = NULL;
4948   if (mt->opts.insert_mode == INSERT_MODE_NORMAL) {
4949     mtext = lives_menu_item_get_text(mt->ins_normal);
4950   }
4951 
4952   if (!mt->ins_label) {
4953     lives_menu_item_set_text(mt->ins_menuitem, mtext, TRUE);
4954   } else {
4955     widget_opts.mnemonic_label = FALSE;
4956     lives_label_set_text(LIVES_LABEL(mt->ins_label), mtext);
4957     widget_opts.mnemonic_label = TRUE;
4958   }
4959 
4960   lives_signal_handler_block(mt->ins_normal, mt->ins_normal_func);
4961   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->ins_normal), mt->opts.insert_mode == INSERT_MODE_NORMAL);
4962   lives_signal_handler_unblock(mt->ins_normal, mt->ins_normal_func);
4963 }
4964 
4965 
on_insert_mode_changed(LiVESMenuItem * menuitem,livespointer user_data)4966 static void on_insert_mode_changed(LiVESMenuItem * menuitem, livespointer user_data) {
4967   lives_mt *mt = (lives_mt *)user_data;
4968 
4969   if (!LIVES_IS_INTERACTIVE) return;
4970 
4971   if (menuitem == (LiVESMenuItem *)mt->ins_normal) {
4972     mt->opts.insert_mode = INSERT_MODE_NORMAL;
4973   }
4974   update_insert_mode(mt);
4975 }
4976 
4977 
on_mouse_mode_changed(LiVESMenuItem * menuitem,livespointer user_data)4978 static void on_mouse_mode_changed(LiVESMenuItem * menuitem, livespointer user_data) {
4979   lives_mt *mt = (lives_mt *)user_data;
4980   const char *text;
4981 
4982   if (!LIVES_IS_INTERACTIVE) return;
4983 
4984   if (menuitem == (LiVESMenuItem *)mt->mm_move) {
4985     mt->opts.mouse_mode = MOUSE_MODE_MOVE;
4986   } else if (menuitem == (LiVESMenuItem *)mt->mm_select) {
4987     mt->opts.mouse_mode = MOUSE_MODE_SELECT;
4988   }
4989 
4990   text = lives_menu_item_get_text(LIVES_WIDGET(menuitem));
4991 
4992   if (!mt->ins_label) {
4993     lives_menu_item_set_text(mt->mm_menuitem, text, TRUE);
4994   } else {
4995     widget_opts.mnemonic_label = FALSE;
4996     lives_label_set_text(LIVES_LABEL(mt->mm_label), text);
4997     widget_opts.mnemonic_label = TRUE;
4998   }
4999 
5000   mouse_mode_context(mt);
5001 
5002   lives_signal_handler_block(mt->mm_move, mt->mm_move_func);
5003   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->mm_move), mt->opts.mouse_mode == MOUSE_MODE_MOVE);
5004   lives_signal_handler_unblock(mt->mm_move, mt->mm_move_func);
5005 
5006   lives_signal_handler_block(mt->mm_select, mt->mm_select_func);
5007   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->mm_select), mt->opts.mouse_mode == MOUSE_MODE_SELECT);
5008   lives_signal_handler_unblock(mt->mm_select, mt->mm_select_func);
5009 }
5010 
5011 
update_grav_mode(lives_mt * mt)5012 void update_grav_mode(lives_mt * mt) {
5013   // update GUI after grav mode change
5014   const char *mtext = NULL;
5015 
5016   if (mt->opts.grav_mode == GRAV_MODE_NORMAL) {
5017     mtext = lives_menu_item_get_text(mt->grav_normal);
5018   } else if (mt->opts.grav_mode == GRAV_MODE_LEFT) {
5019     mtext = lives_menu_item_get_text(mt->grav_left);
5020   }
5021 
5022   if (mt->opts.grav_mode == GRAV_MODE_RIGHT) {
5023     mtext = lives_menu_item_get_text(mt->grav_right);
5024     lives_menu_item_set_text(mt->remove_first_gaps, _("Close _last gap(s) in selected tracks/time"), TRUE);
5025   } else {
5026     lives_menu_item_set_text(mt->remove_first_gaps, _("Close _first gap(s) in selected tracks/time"), TRUE);
5027   }
5028 
5029   widget_opts.mnemonic_label = FALSE;
5030   lives_label_set_text(LIVES_LABEL(mt->grav_label), mtext);
5031   widget_opts.mnemonic_label = TRUE;
5032 
5033   lives_signal_handler_block(mt->grav_normal, mt->grav_normal_func);
5034   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->grav_normal), mt->opts.grav_mode == GRAV_MODE_NORMAL);
5035   lives_signal_handler_unblock(mt->grav_normal, mt->grav_normal_func);
5036 
5037   lives_signal_handler_block(mt->grav_left, mt->grav_left_func);
5038   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->grav_left), mt->opts.grav_mode == GRAV_MODE_LEFT);
5039   lives_signal_handler_unblock(mt->grav_left, mt->grav_left_func);
5040 
5041   lives_signal_handler_block(mt->grav_right, mt->grav_right_func);
5042   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->grav_right), mt->opts.grav_mode == GRAV_MODE_RIGHT);
5043   lives_signal_handler_unblock(mt->grav_right, mt->grav_right_func);
5044 }
5045 
5046 
on_grav_mode_changed(LiVESMenuItem * menuitem,livespointer user_data)5047 static void on_grav_mode_changed(LiVESMenuItem * menuitem, livespointer user_data) {
5048   lives_mt *mt = (lives_mt *)user_data;
5049 
5050   if (!LIVES_IS_INTERACTIVE) return;
5051 
5052   if (menuitem == (LiVESMenuItem *)mt->grav_normal) {
5053     mt->opts.grav_mode = GRAV_MODE_NORMAL;
5054   } else if (menuitem == (LiVESMenuItem *)mt->grav_left) {
5055     mt->opts.grav_mode = GRAV_MODE_LEFT;
5056   } else if (menuitem == (LiVESMenuItem *)mt->grav_right) {
5057     mt->opts.grav_mode = GRAV_MODE_RIGHT;
5058   }
5059   update_grav_mode(mt);
5060 }
5061 
5062 
estimate_space(lives_mt * mt,int undo_type)5063 static size_t estimate_space(lives_mt * mt, int undo_type) {
5064   size_t needed = sizeof(mt_undo);
5065 
5066   switch (undo_type) {
5067   case MT_UNDO_NONE:
5068     break;
5069   default:
5070     needed += event_list_get_byte_size(mt, mt->event_list, FALSE, NULL);
5071     break;
5072   }
5073   return needed;
5074 }
5075 
5076 
get_undo_text(int action,void * extra)5077 static char *get_undo_text(int action, void *extra) {
5078   char *filtname, *ret;
5079 
5080   switch (action) {
5081   case MT_UNDO_REMOVE_GAPS:
5082     return (_("Close gaps"));
5083   case MT_UNDO_MOVE_BLOCK:
5084     return (_("Move block"));
5085   case MT_UNDO_MOVE_AUDIO_BLOCK:
5086     return (_("Move audio block"));
5087   case MT_UNDO_DELETE_BLOCK:
5088     return (_("Delete block"));
5089   case MT_UNDO_DELETE_AUDIO_BLOCK:
5090     return (_("Delete audio block"));
5091   case MT_UNDO_SPLIT_MULTI:
5092     return (_("Split tracks"));
5093   case MT_UNDO_SPLIT:
5094     return (_("Split block"));
5095   case MT_UNDO_APPLY_FILTER:
5096     filtname = weed_get_string_value((weed_plant_t *)extra, WEED_LEAF_NAME, NULL);
5097     ret = lives_strdup_printf(_("Apply %s"), filtname);
5098     lives_free(filtname);
5099     return ret;
5100   case MT_UNDO_DELETE_FILTER:
5101     filtname = weed_get_string_value((weed_plant_t *)extra, WEED_LEAF_NAME, NULL);
5102     ret = lives_strdup_printf(_("Delete %s"), filtname);
5103     lives_free(filtname);
5104     return ret;
5105   case MT_UNDO_INSERT_BLOCK:
5106     return (_("Insert block"));
5107   case MT_UNDO_INSERT_GAP:
5108     return (_("Insert gap"));
5109   case MT_UNDO_INSERT_AUDIO_BLOCK:
5110     return (_("Insert audio block"));
5111   case MT_UNDO_FILTER_MAP_CHANGE:
5112     return (_("Effect order change"));
5113   }
5114   return lives_strdup("");
5115 }
5116 
5117 
mt_set_undoable(lives_mt * mt,int what,void * extra,boolean sensitive)5118 static void mt_set_undoable(lives_mt * mt, int what, void *extra, boolean sensitive) {
5119   mt->undoable = sensitive;
5120   if (what != MT_UNDO_NONE) {
5121     char *what_safe;
5122     char *text = get_undo_text(what, extra);
5123     what_safe = lives_strdelimit(lives_strdup(text), "_", ' ');
5124     lives_snprintf(mt->undo_text, 32, _("_Undo %s"), what_safe);
5125 
5126     lives_free(what_safe);
5127     lives_free(text);
5128   } else {
5129     mt->undoable = FALSE;
5130     lives_snprintf(mt->undo_text, 32, "%s", _("_Undo"));
5131   }
5132   lives_menu_item_set_text(mt->undo, mt->undo_text, TRUE);
5133 
5134   lives_widget_set_sensitive(mt->undo, sensitive);
5135 }
5136 
5137 
mt_set_redoable(lives_mt * mt,int what,void * extra,boolean sensitive)5138 static void mt_set_redoable(lives_mt * mt, int what, void *extra, boolean sensitive) {
5139   mt->redoable = sensitive;
5140   if (what != MT_UNDO_NONE) {
5141     char *what_safe;
5142     char *text = get_undo_text(what, extra);
5143     what_safe = lives_strdelimit(lives_strdup(text), "_", ' ');
5144     lives_snprintf(mt->redo_text, 32, _("_Redo %s"), what_safe);
5145     lives_free(what_safe);
5146     lives_free(text);
5147   } else {
5148     mt->redoable = FALSE;
5149     lives_snprintf(mt->redo_text, 32, "%s", _("_Redo"));
5150   }
5151   lives_menu_item_set_text(mt->redo, mt->redo_text, TRUE);
5152 
5153   lives_widget_set_sensitive(mt->redo, sensitive);
5154 }
5155 
5156 
make_backup_space(lives_mt * mt,size_t space_needed)5157 boolean make_backup_space(lives_mt * mt, size_t space_needed) {
5158   // read thru mt->undos and eliminate that space until we have space_needed
5159   size_t space_avail = (size_t)(prefs->mt_undo_buf * 1024 * 1024) - mt->undo_buffer_used;
5160   size_t space_freed = 0;
5161   size_t len;
5162   LiVESList *xundo = mt->undos, *ulist;
5163   int count = 0;
5164   mt_undo *undo;
5165 
5166   while (xundo) {
5167     count++;
5168     undo = (mt_undo *)(xundo->data);
5169     space_freed += undo->data_len;
5170     if ((space_avail + space_freed) >= space_needed) {
5171       lives_memmove(mt->undo_mem, mt->undo_mem + space_freed, mt->undo_buffer_used - space_freed);
5172       ulist = lives_list_copy(lives_list_nth(mt->undos, count));
5173       if (ulist) ulist->prev = NULL;
5174       lives_list_free(mt->undos);
5175       mt->undos = ulist;
5176       while (ulist) {
5177         ulist->data = (unsigned char *)(ulist->data) - space_freed;
5178         ulist = ulist->next;
5179       }
5180       mt->undo_buffer_used -= space_freed;
5181       if (mt->undo_offset > (len = lives_list_length(mt->undos))) {
5182         mt->undo_offset = len;
5183         mt_set_undoable(mt, MT_UNDO_NONE, NULL, FALSE);
5184         mt_set_redoable(mt, ((mt_undo *)(mt->undos->data))->action, NULL, TRUE);
5185       }
5186       return TRUE;
5187     }
5188     xundo = xundo->next;
5189   }
5190   mt->undo_buffer_used = 0;
5191   lives_list_free(mt->undos);
5192   mt->undos = NULL;
5193   mt->undo_offset = 0;
5194   mt_set_undoable(mt, MT_UNDO_NONE, NULL, FALSE);
5195   mt_set_redoable(mt, MT_UNDO_NONE, NULL, FALSE);
5196   return FALSE;
5197 }
5198 
5199 
mt_backup(lives_mt * mt,int undo_type,weed_timecode_t tc)5200 void mt_backup(lives_mt * mt, int undo_type, weed_timecode_t tc) {
5201   // backup an operation in the undo/redo list
5202 
5203   size_t space_needed = 0;
5204   mt_undo *undo;
5205   mt_undo *last_valid_undo;
5206 
5207   unsigned char *memblock, *omemblock;
5208 
5209   if (mt->did_backup) return;
5210 
5211   // top level caller MUST reset this to FALSE !
5212   mt->did_backup = mt->changed = TRUE;
5213   lives_widget_set_sensitive(mt->backup, TRUE);
5214 
5215   // ask caller to add the idle func
5216   if (prefs->mt_auto_back > 0) mt->auto_changed = TRUE;
5217 
5218   if (!mt->undo_mem) return;
5219 
5220   if (mt->undos && mt->undo_offset != 0) {
5221     // invalidate redo's - we are backing up, so we can't redo any more
5222     // invalidate from lives_list_length-undo_offset onwards
5223     if ((lives_list_length(mt->undos)) == mt->undo_offset) {
5224       mt->undos = NULL;
5225       mt->undo_buffer_used = 0;
5226     } else {
5227       int i = 0;
5228       LiVESList *ulist = mt->undos;
5229       for (i = (int)lives_list_length(mt->undos) - mt->undo_offset; --i > 0; ulist = ulist->next);
5230       if (ulist) {
5231         memblock = (unsigned char *)ulist->data;
5232         last_valid_undo = (mt_undo *)memblock;
5233         memblock += last_valid_undo->data_len;
5234         mt->undo_buffer_used = memblock - mt->undo_mem;
5235         if (ulist->next) {
5236           ulist = ulist->next;
5237           ulist->prev->next = NULL;
5238           lives_list_free(ulist);
5239         }
5240       }
5241     }
5242     mt_set_redoable(mt, MT_UNDO_NONE, NULL, FALSE);
5243     mt->undo_offset = 0;
5244   }
5245 
5246   undo = (mt_undo *)lives_calloc(1, sizeof(mt_undo));
5247   undo->action = (lives_mt_undo_t)undo_type;
5248   undo->extra = NULL;
5249 
5250   switch (undo_type) {
5251   case MT_UNDO_NONE:
5252     break;
5253   case MT_UNDO_APPLY_FILTER:
5254   case MT_UNDO_DELETE_FILTER:
5255     undo->extra = get_weed_filter(mt->current_fx);
5256     break;
5257   case MT_UNDO_FILTER_MAP_CHANGE:
5258     undo->tc = tc;
5259     break;
5260   default:
5261     break;
5262   }
5263 
5264   add_markers(mt, mt->event_list, TRUE);
5265   if ((space_needed = estimate_space(mt, undo_type) + sizeof(mt_undo)) >
5266       ((size_t)(prefs->mt_undo_buf * 1024 * 1024) - mt->undo_buffer_used)) {
5267     if (!make_backup_space(mt, space_needed)) {
5268       remove_markers(mt->event_list);
5269       do_mt_backup_space_error(mt, (int)((space_needed * 3) >> 20));
5270       return;
5271     }
5272   }
5273 
5274   omemblock = memblock = (unsigned char *)(mt->undo_mem + mt->undo_buffer_used + sizeof(mt_undo));
5275   save_event_list_inner(NULL, 0, mt->event_list, &memblock);
5276   undo->data_len = memblock - omemblock;
5277   space_needed = undo->data_len + sizeof(mt_undo);
5278 
5279   remove_markers(mt->event_list);
5280 
5281   lives_memcpy(mt->undo_mem + mt->undo_buffer_used, undo, sizeof(mt_undo));
5282   mt->undos = lives_list_append(mt->undos, mt->undo_mem + mt->undo_buffer_used);
5283   mt->undo_buffer_used += space_needed;
5284   mt_set_undoable(mt, undo->action, undo->extra, TRUE);
5285   lives_free(undo);
5286 }
5287 
5288 
mt_aparam_view_toggled(LiVESMenuItem * menuitem,livespointer user_data)5289 void mt_aparam_view_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
5290   lives_mt *mt = (lives_mt *)user_data;
5291   LiVESList *list;
5292   int which = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(menuitem), "pnum"));
5293 
5294   if (lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(menuitem)))
5295     mt->opts.aparam_view_list = lives_list_append(mt->opts.aparam_view_list, LIVES_INT_TO_POINTER(which));
5296   else mt->opts.aparam_view_list = lives_list_remove(mt->opts.aparam_view_list, LIVES_INT_TO_POINTER(which));
5297   for (list = mt->audio_draws; list; list = list->next) {
5298     lives_widget_queue_draw((LiVESWidget *)list->data);
5299   }
5300 }
5301 
5302 
destroy_widget(LiVESWidget * widget,livespointer user_data)5303 static void destroy_widget(LiVESWidget * widget, livespointer user_data) {
5304   lives_widget_destroy(widget);
5305 }
5306 
5307 
add_aparam_menuitems(lives_mt * mt)5308 void add_aparam_menuitems(lives_mt * mt) {
5309   // add menuitems for avol_fx to the View/Audio parameters submenu
5310   LiVESWidget *menuitem;
5311   weed_plant_t *filter;
5312   lives_rfx_t *rfx;
5313   register int i;
5314 
5315   lives_container_foreach(LIVES_CONTAINER(mt->aparam_submenu), destroy_widget, NULL);
5316 
5317   if (mt->avol_fx == -1 || !mt->audio_draws) {
5318     lives_widget_hide(mt->insa_checkbutton);
5319     lives_widget_hide(mt->aparam_separator);
5320     lives_widget_hide(mt->aparam_menuitem);
5321     lives_widget_hide(mt->aparam_submenu);
5322 
5323     lives_widget_hide(mt->render_aud);
5324     lives_widget_hide(mt->normalise_aud);
5325     lives_widget_hide(mt->render_vid);
5326     lives_widget_hide(mt->render_sep);
5327 
5328     if (mt->opts.aparam_view_list && !mainw->multi_opts.aparam_view_list) {
5329       lives_list_free(mt->opts.aparam_view_list);
5330       mt->opts.aparam_view_list = NULL;
5331     }
5332     return;
5333   }
5334   if (mt->opts.pertrack_audio) {
5335     lives_widget_show(mt->insa_checkbutton);
5336   }
5337 
5338   lives_widget_show(mt->render_aud);
5339   lives_widget_show(mt->normalise_aud);
5340   lives_widget_show(mt->render_vid);
5341   lives_widget_show(mt->render_sep);
5342 
5343   //  lives_widget_show(mt->aparam_separator);
5344   lives_widget_show(mt->aparam_menuitem);
5345   lives_widget_show(mt->aparam_submenu);
5346 
5347   filter = get_weed_filter(mt->avol_fx);
5348   rfx = weed_to_rfx(filter, FALSE);
5349   widget_opts.mnemonic_label = FALSE;
5350 
5351   for (i = 0; i < rfx->num_params; i++) {
5352     // TODO - check rfx->params[i].multi
5353     if ((rfx->params[i].hidden | HIDDEN_MULTI) == HIDDEN_MULTI && rfx->params[i].type == LIVES_PARAM_NUM) {
5354       menuitem = lives_standard_check_menu_item_new_with_label(rfx->params[i].name,
5355                  mt->opts.aparam_view_list && lives_list_find(mt->opts.aparam_view_list, LIVES_INT_TO_POINTER(i)));
5356       lives_container_add(LIVES_CONTAINER(mt->aparam_submenu), menuitem);
5357       lives_widget_show(menuitem);
5358       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), "pnum", LIVES_INT_TO_POINTER(i));
5359       lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
5360                            LIVES_GUI_CALLBACK(mt_aparam_view_toggled), (livespointer)mt);
5361     }
5362   }
5363   widget_opts.mnemonic_label = TRUE;
5364   rfx_free(rfx);
5365   lives_free(rfx);
5366 }
5367 
5368 
apply_avol_filter(lives_mt * mt)5369 static void apply_avol_filter(lives_mt * mt) {
5370   // apply audio volume effect from 0 to last frame event
5371   // since audio off occurs on a frame event this should cover the whole timeline
5372 
5373   weed_plant_t *init_event = mt->avol_init_event, *new_end_event;
5374   weed_plant_t *deinit_event;
5375   weed_timecode_t new_tc;
5376   LiVESList *list;
5377 
5378   register int i;
5379 
5380   if (mt->opts.back_audio_tracks == 0 && !mt->opts.pertrack_audio) return;
5381 
5382   new_end_event = get_last_frame_event(mt->event_list);
5383 
5384   if (!new_end_event) {
5385     if (init_event) {
5386       remove_filter_from_event_list(mt->event_list, init_event);
5387       if (mt->opts.aparam_view_list) {
5388         for (list = mt->audio_draws; list; list = list->next) {
5389           lives_widget_queue_draw((LiVESWidget *)list->data);
5390 	  // *INDENT-OFF*
5391         }}}
5392     // *INDENT-ON*
5393     return;
5394   }
5395 
5396   if (mt->opts.pertrack_audio) lives_widget_set_sensitive(mt->prerender_aud, TRUE);
5397 
5398   if (!init_event) {
5399     LiVESList *slist = lives_list_copy(mt->selected_tracks);
5400 
5401     weed_plant_t *old_mt_init = mt->init_event;
5402 
5403     double region_start = mt->region_start;
5404     double region_end = mt->region_end;
5405 
5406     boolean did_backup = mt->did_backup;
5407     int current_fx = mt->current_fx;
5408 
5409     mt->region_start = 0.;
5410     mt->region_end = (get_event_timecode(new_end_event) + TICKS_PER_SECOND_DBL / mt->fps) / TICKS_PER_SECOND_DBL;
5411     if (mt->selected_tracks) {
5412       lives_list_free(mt->selected_tracks);
5413       mt->selected_tracks = NULL;
5414     }
5415     if (mt->opts.back_audio_tracks > 0) mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(-1));
5416     if (mt->opts.pertrack_audio) {
5417       for (i = 0; i < mt->num_video_tracks; i++) {
5418         mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(i));
5419       }
5420     }
5421     mt->current_fx = mt->avol_fx;
5422 
5423     mt->did_backup = TRUE; // this is a special internal event that we don't want to backup
5424     mt_add_region_effect(NULL, mt);
5425     mt->did_backup = did_backup;
5426     mt->avol_init_event = mt->init_event;
5427 
5428     mt->region_start = region_start;
5429     mt->region_end = region_end;
5430     lives_list_free(mt->selected_tracks);
5431     mt->selected_tracks = lives_list_copy(slist);
5432     if (slist) lives_list_free(slist);
5433     mt->current_fx = current_fx;
5434     mt->init_event = old_mt_init;
5435 
5436     if (mt->opts.aparam_view_list) {
5437       for (list = mt->audio_draws; list; list = list->next) {
5438         lives_widget_queue_draw((LiVESWidget *)list->data);
5439       }
5440     }
5441     return;
5442   }
5443 
5444   // init event is already there - we will move the deinit event to tc==new_end event
5445   deinit_event = weed_get_plantptr_value(init_event, WEED_LEAF_DEINIT_EVENT, NULL);
5446   new_tc = get_event_timecode(new_end_event);
5447 
5448   move_filter_deinit_event(mt->event_list, new_tc, deinit_event, mt->fps, FALSE);
5449 
5450   if (mt->opts.aparam_view_list) {
5451     for (list = mt->audio_draws; list; list = list->next) {
5452       lives_widget_queue_draw((LiVESWidget *)list->data);
5453     }
5454   }
5455 }
5456 
5457 
set_audio_filter_channel_values(lives_mt * mt)5458 static void set_audio_filter_channel_values(lives_mt * mt) {
5459   // audio values may have changed
5460   // we need to reinit the filters if they are being edited
5461   // for now we just have avol_fx
5462 
5463   // TODO - in future we may have more audio filters
5464 
5465   weed_plant_t *inst;
5466   weed_plant_t **in_channels, **out_channels;
5467 
5468   int num_in, num_out;
5469   int i;
5470 
5471   add_aparam_menuitems(mt);
5472 
5473   if (!mt->current_rfx || mt->current_fx == -1 || mt->current_fx != mt->avol_fx) return;
5474 
5475   inst = (weed_plant_t *)mt->current_rfx->source;
5476   in_channels = weed_get_plantptr_array_counted(inst, WEED_LEAF_IN_CHANNELS, &num_in);
5477   if (num_in > 0) {
5478     for (i = 0; i < num_in; i++) {
5479       weed_set_int_value(in_channels[i], WEED_LEAF_AUDIO_CHANNELS, mainw->files[mt->render_file]->achans);
5480       weed_set_int_value(in_channels[i], WEED_LEAF_AUDIO_RATE, mainw->files[mt->render_file]->arate);
5481     }
5482     lives_free(in_channels);
5483   }
5484   out_channels = weed_get_plantptr_array_counted(inst, WEED_LEAF_OUT_CHANNELS, &num_out);
5485   if (num_out > 0) {
5486     for (i = 0; i < num_out; i++) {
5487       weed_set_int_value(out_channels[i], WEED_LEAF_AUDIO_CHANNELS, mainw->files[mt->render_file]->achans);
5488       weed_set_int_value(out_channels[i], WEED_LEAF_AUDIO_RATE, mainw->files[mt->render_file]->arate);
5489     }
5490     lives_free(out_channels);
5491   }
5492 
5493   mt->changed = TRUE;
5494 
5495   weed_reinit_effect(inst, TRUE);
5496   polymorph(mt, POLY_PARAMS);
5497 }
5498 
5499 
mt_set_vals_string(void)5500 static char *mt_set_vals_string(void) {
5501   char sendian[128];
5502 
5503   if (cfile->signed_endian & AFORM_UNSIGNED) lives_snprintf(sendian, 128, "%s", _("unsigned "));
5504   else lives_snprintf(sendian, 128, "%s", _("signed "));
5505 
5506   if (cfile->signed_endian & AFORM_BIG_ENDIAN) lives_strappend(sendian, 128, _("big endian"));
5507   else lives_strappend(sendian, 128, _("little endian"));
5508 
5509   return lives_strdup_printf(
5510            _("Multitrack values set to %.3f fps, frame size %d x %d, audio channels %d, "
5511              "audio rate %d, audio sample size %d, %s.\n"),
5512            cfile->fps, cfile->hsize, cfile->vsize, cfile->achans, cfile->arate, cfile->asampsize, sendian);
5513 }
5514 
5515 
set_mt_play_sizes_cfg(lives_mt * mt)5516 void set_mt_play_sizes_cfg(lives_mt * mt) {
5517   if (!CURRENT_CLIP_IS_VALID) return;
5518   else {
5519     int rwidth = lives_widget_get_allocation_width(mt->preview_eventbox);
5520     int rheight = lives_widget_get_allocation_height(mt->preview_eventbox);
5521     int width = mainw->files[mt->render_file]->hsize;
5522     int height = mainw->files[mt->render_file]->vsize;
5523 
5524     if (rwidth * rheight < 64) {
5525       rwidth = GUI_SCREEN_WIDTH / PEB_WRATIO;
5526       rheight = GUI_SCREEN_HEIGHT / PEB_HRATIO;
5527     }
5528 
5529     calc_maxspect(rwidth, rheight, &width, &height);
5530 
5531     width = (width >> 2) << 2;
5532     height = (height >> 2) << 2;
5533 
5534     mt->play_width = width;
5535     mt->play_height = height;
5536   }
5537 }
5538 
5539 
load_event_list_inner(lives_mt * mt,int fd,boolean show_errors,int * num_events,unsigned char ** mem,unsigned char * mem_end)5540 static weed_plant_t *load_event_list_inner(lives_mt * mt, int fd, boolean show_errors, int *num_events,
5541     unsigned char **mem, unsigned char *mem_end) {
5542   weed_plant_t *event, *eventprev = NULL;
5543   weed_plant_t *event_list;
5544 
5545   double fps = -1.;
5546 
5547   char *msg, *err;
5548 
5549   if (fd > 0 || mem) event_list = weed_plant_deserialise(fd, mem, NULL);
5550   else event_list = mainw->stored_event_list;
5551 
5552   if (mt) mt->layout_set_properties = FALSE;
5553 
5554   if (!event_list || !WEED_PLANT_IS_EVENT_LIST(event_list)) {
5555     if (show_errors) d_print(_("invalid event list. Failed.\n"));
5556     return NULL;
5557   }
5558 
5559   if (show_errors && (!weed_plant_has_leaf(event_list, WEED_LEAF_FPS) ||
5560                       (fps = weed_get_double_value(event_list, WEED_LEAF_FPS, NULL)) < 1. ||
5561                       fps > FPS_MAX)) {
5562     d_print(_("event list has invalid fps. Failed.\n"));
5563     return NULL;
5564   }
5565 
5566   if (mt && show_errors && !(prefs->warning_mask & WARN_MASK_LAYOUT_LB)
5567       && weed_plant_has_leaf(event_list, WEED_LEAF_KEEP_ASPECT)) {
5568     boolean letterbox = weed_get_boolean_value(event_list, WEED_LEAF_KEEP_ASPECT, NULL);
5569     if ((letterbox == WEED_TRUE && !prefs->letterbox_mt)
5570         || (letterbox == WEED_FALSE && prefs->letterbox_mt)) {
5571       if (do_mt_lb_warn(letterbox == WEED_TRUE)) {
5572         pref_factory_bool(PREF_LETTERBOXMT, letterbox == WEED_TRUE, FALSE);
5573       }
5574     }
5575   }
5576 
5577   if (weed_plant_has_leaf(event_list, WEED_LEAF_NEEDS_SET)) {
5578     if (show_errors) {
5579       char *set_needed = weed_get_string_value(event_list, WEED_LEAF_NEEDS_SET, NULL);
5580       char *tmp = NULL;
5581       if (!mainw->was_set || strcmp((tmp = U82F(set_needed)), mainw->set_name)) {
5582         if (tmp) lives_free(tmp);
5583         err = lives_strdup_printf(
5584                 _("\nThis layout requires the set \"%s\"\nIn order to load it you must return to the Clip Editor, \n"
5585                   "close the current set,\nthen load in the new set from the File menu.\n"),
5586                 set_needed);
5587         d_print(err);
5588         do_error_dialog(err);
5589         lives_free(err);
5590         lives_free(set_needed);
5591         return NULL;
5592       }
5593       if (tmp) lives_free(tmp);
5594       lives_free(set_needed);
5595     }
5596   } else if (mt && !show_errors && !mem) return NULL; // no change needed
5597 
5598   if (mt) {
5599     if (event_list == mainw->stored_event_list || (mt && !mt->ignore_load_vals)) {
5600       if (fps > -1) {
5601         mainw->files[mt->render_file]->fps = mainw->files[mt->render_file]->pb_fps = fps;
5602         if (mt) mt->fps = mainw->files[mt->render_file]->fps;
5603         mainw->files[mt->render_file]->ratio_fps = check_for_ratio_fps(mainw->files[mt->render_file]->fps);
5604       }
5605 
5606       // check for optional leaves
5607       if (weed_plant_has_leaf(event_list, WEED_LEAF_WIDTH)) {
5608         int width = weed_get_int_value(event_list, WEED_LEAF_WIDTH, NULL);
5609         if (width > 0) {
5610           mainw->files[mt->render_file]->hsize = width;
5611           if (mt) mt->layout_set_properties = TRUE;
5612         }
5613       }
5614 
5615       if (weed_plant_has_leaf(event_list, WEED_LEAF_HEIGHT)) {
5616         int height = weed_get_int_value(event_list, WEED_LEAF_HEIGHT, NULL);
5617         if (height > 0) {
5618           mainw->files[mt->render_file]->vsize = height;
5619           if (mt) mt->layout_set_properties = TRUE;
5620         }
5621       }
5622 
5623       if (weed_plant_has_leaf(event_list, WEED_LEAF_AUDIO_CHANNELS)) {
5624         int achans = weed_get_int_value(event_list, WEED_LEAF_AUDIO_CHANNELS, NULL);
5625         if (achans >= 0 && mt) {
5626           if (achans > 2) {
5627             char *err = lives_strdup_printf(
5628                           _("\nThis layout has an invalid number of audio channels (%d) for LiVES.\n"
5629                             "It cannot be loaded.\n"), achans);
5630             d_print(err);
5631             do_error_dialog(err);
5632             lives_free(err);
5633             return NULL;
5634           }
5635           mainw->files[mt->render_file]->achans = achans;
5636           if (mt) mt->layout_set_properties = TRUE;
5637         }
5638       }
5639 
5640       if (weed_plant_has_leaf(event_list, WEED_LEAF_AUDIO_RATE)) {
5641         int arate = weed_get_int_value(event_list, WEED_LEAF_AUDIO_RATE, NULL);
5642         if (arate > 0) {
5643           mainw->files[mt->render_file]->arate = mainw->files[mt->render_file]->arps = arate;
5644           if (mt) mt->layout_set_properties = TRUE;
5645         }
5646       }
5647 
5648       if (weed_plant_has_leaf(event_list, WEED_LEAF_AUDIO_SAMPLE_SIZE)) {
5649         int asamps = weed_get_int_value(event_list, WEED_LEAF_AUDIO_SAMPLE_SIZE, NULL);
5650         if (asamps == 8 || asamps == 16) {
5651           mainw->files[mt->render_file]->asampsize = asamps;
5652           if (mt) mt->layout_set_properties = TRUE;
5653         } else if (mainw->files[mt->render_file]->achans > 0) {
5654           msg = lives_strdup_printf("Layout has invalid sample size %d\n", asamps);
5655           LIVES_ERROR(msg);
5656           lives_free(msg);
5657         }
5658       }
5659 
5660       if (weed_plant_has_leaf(event_list, WEED_LEAF_AUDIO_SIGNED)) {
5661         int asigned = weed_get_boolean_value(event_list, WEED_LEAF_AUDIO_SIGNED, NULL);
5662         if (asigned == WEED_TRUE) {
5663           if (mainw->files[mt->render_file]->signed_endian & AFORM_UNSIGNED)
5664             mainw->files[mt->render_file]->signed_endian ^= AFORM_UNSIGNED;
5665         } else {
5666           if (!(mainw->files[mt->render_file]->signed_endian & AFORM_UNSIGNED))
5667             mainw->files[mt->render_file]->signed_endian |= AFORM_UNSIGNED;
5668         }
5669         if (mt) mt->layout_set_properties = TRUE;
5670       }
5671 
5672       if (weed_plant_has_leaf(event_list, WEED_LEAF_AUDIO_ENDIAN)) {
5673         int aendian = weed_get_int_value(event_list, WEED_LEAF_AUDIO_ENDIAN, NULL);
5674         if (aendian == WEED_AUDIO_LITTLE_ENDIAN) {
5675           if (mainw->files[mt->render_file]->signed_endian & AFORM_BIG_ENDIAN)
5676             mainw->files[mt->render_file]->signed_endian ^= AFORM_BIG_ENDIAN;
5677         } else {
5678           if (!(mainw->files[mt->render_file]->signed_endian & AFORM_BIG_ENDIAN))
5679             mainw->files[mt->render_file]->signed_endian |= AFORM_BIG_ENDIAN;
5680         }
5681         if (mt) mt->layout_set_properties = TRUE;
5682       }
5683     } else {
5684       if (mt) {
5685         msg = set_values_from_defs(mt, FALSE);
5686         if (msg) {
5687           if (mt) mt->layout_set_properties = TRUE;
5688           lives_free(msg);
5689         }
5690         mainw->files[mt->render_file]->fps = mainw->files[mt->render_file]->pb_fps;
5691         if (mt) mt->fps = mainw->files[mt->render_file]->fps;
5692         mainw->files[mt->render_file]->ratio_fps = check_for_ratio_fps(mainw->files[mt->render_file]->fps);
5693       }
5694     }
5695 
5696     if (event_list == mainw->stored_event_list) return event_list;
5697     // force 64 bit ptrs when reading layouts (for compatibility)
5698     prefs->force64bit = FALSE;
5699 
5700     if (weed_plant_has_leaf(event_list, WEED_LEAF_WEED_EVENT_API_VERSION)) {
5701       if (weed_get_int_value(event_list, WEED_LEAF_WEED_EVENT_API_VERSION, NULL) >= 110) prefs->force64bit = TRUE;
5702     } else {
5703       if (weed_plant_has_leaf(event_list, WEED_LEAF_PTRSIZE)) {
5704         if (weed_get_int_value(event_list, WEED_LEAF_PTRSIZE, NULL) == 8) prefs->force64bit = TRUE;
5705       }
5706     }
5707   }
5708 
5709   if (weed_plant_has_leaf(event_list, WEED_LEAF_FIRST)) weed_leaf_delete(event_list, WEED_LEAF_FIRST);
5710   if (weed_plant_has_leaf(event_list, WEED_LEAF_LAST)) weed_leaf_delete(event_list, WEED_LEAF_LAST);
5711 
5712   weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, NULL);
5713   weed_set_voidptr_value(event_list, WEED_LEAF_LAST, NULL);
5714 
5715   do {
5716     if (mem && *mem >= mem_end) break;
5717     event = weed_plant_deserialise(fd, mem, NULL);
5718     if (event) {
5719 #ifdef DEBUG_TTABLE
5720       uint64_t event_id;
5721       if (weed_plant_has_leaf(event, WEED_LEAF_INIT_EVENT)) {
5722         if (weed_leaf_seed_type(event, WEED_LEAF_INIT_EVENT) == WEED_SEED_INT64)
5723           event_id = (uint64_t)(weed_get_int64_value(event, WEED_LEAF_INIT_EVENT, NULL));
5724         else
5725           event_id = (uint64_t)((weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL));
5726       }
5727 #endif
5728 
5729       if (weed_plant_has_leaf(event, WEED_LEAF_PREVIOUS)) weed_leaf_delete(event, WEED_LEAF_PREVIOUS);
5730       if (weed_plant_has_leaf(event, WEED_LEAF_NEXT)) weed_leaf_delete(event, WEED_LEAF_NEXT);
5731       if (eventprev) weed_set_voidptr_value(eventprev, WEED_LEAF_NEXT, event);
5732       weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, eventprev);
5733       weed_set_voidptr_value(event, WEED_LEAF_NEXT, NULL);
5734       if (!get_first_event(event_list)) {
5735         weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
5736       }
5737       weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
5738       //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN);
5739       eventprev = event;
5740       if (num_events)(*num_events)++;
5741     }
5742   } while (event);
5743 
5744   if (!mt) return event_list;
5745 
5746   //weed_add_plant_flags(event_list, WEED_LEAF_READONLY_PLUGIN);
5747 
5748   if (weed_plant_has_leaf(event_list, WEED_LEAF_GAMMA_ENABLED)) {
5749     err = NULL;
5750     if (weed_get_boolean_value(event_list, WEED_LEAF_GAMMA_ENABLED, NULL) == WEED_TRUE) {
5751       if (!prefs->apply_gamma) {
5752         err = (_("This layout was created using automatic gamma correction.\nFor compatibility, you may wish to"
5753                  "enable this feature in Tools -> Preferences -> Effects, whilst editing and rendering the layout."));
5754       }
5755     } else if (prefs->apply_gamma) {
5756       err = (_("This layout was created without automatic gamma correction.\nFor compatibility, you may wish to"
5757                "disable this feature in Tools -> Preferences -> Effects, whilst editing and rendering the layout."));
5758     }
5759     if (err) {
5760       do_error_dialog(err);
5761       lives_free(err);
5762     }
5763   }
5764   return event_list;
5765 }
5766 
5767 
set_values_from_defs(lives_mt * mt,boolean from_prefs)5768 char *set_values_from_defs(lives_mt * mt, boolean from_prefs) {
5769   // set various multitrack state flags from either defaults or user preferences
5770 
5771   char *retval = NULL;
5772 
5773   int hsize = mainw->files[mt->render_file]->hsize;
5774   int vsize = mainw->files[mt->render_file]->vsize;
5775   int arate = mainw->files[mt->render_file]->arate;
5776   int achans = mainw->files[mt->render_file]->achans;
5777   int asamps = mainw->files[mt->render_file]->asampsize;
5778   int ase = mainw->files[mt->render_file]->signed_endian;
5779 
5780   if (mainw->stored_event_list) {
5781     load_event_list_inner(mt, -1, TRUE, NULL, NULL, NULL);
5782     mt->user_width = mainw->files[mt->render_file]->hsize;
5783     mt->user_height = mainw->files[mt->render_file]->vsize;
5784     mainw->files[mt->render_file]->pb_fps = mt->fps = mt->user_fps = mainw->files[mt->render_file]->fps;
5785     mainw->files[mt->render_file]->arps = mt->user_arate = mainw->files[mt->render_file]->arate;
5786     mt->user_achans = mainw->files[mt->render_file]->achans;
5787     mt->user_asamps = mainw->files[mt->render_file]->asampsize;
5788     mt->user_signed_endian = mainw->files[mt->render_file]->signed_endian;
5789   } else {
5790     if (!from_prefs) {
5791       mainw->files[mt->render_file]->hsize = mt->user_width;
5792       mainw->files[mt->render_file]->vsize = mt->user_height;
5793       mainw->files[mt->render_file]->pb_fps = mainw->files[mt->render_file]->fps = mt->fps = mt->user_fps;
5794       mainw->files[mt->render_file]->arps = mainw->files[mt->render_file]->arate = mt->user_arate;
5795       mainw->files[mt->render_file]->achans = mt->user_achans;
5796       mainw->files[mt->render_file]->asampsize = mt->user_asamps;
5797       mainw->files[mt->render_file]->signed_endian = mt->user_signed_endian;
5798     } else {
5799       mt->user_width = mainw->files[mt->render_file]->hsize = prefs->mt_def_width;
5800       mt->user_height = mainw->files[mt->render_file]->vsize = prefs->mt_def_height;
5801       mt->user_fps = mainw->files[mt->render_file]->pb_fps = mainw->files[mt->render_file]->fps = mt->fps = prefs->mt_def_fps;
5802       mt->user_arate = mainw->files[mt->render_file]->arate = mainw->files[mt->render_file]->arps = prefs->mt_def_arate;
5803       mt->user_achans = mainw->files[mt->render_file]->achans = prefs->mt_def_achans;
5804       mt->user_asamps = mainw->files[mt->render_file]->asampsize = prefs->mt_def_asamps;
5805       mt->user_signed_endian = mainw->files[mt->render_file]->signed_endian = prefs->mt_def_signed_endian;
5806     }
5807   }
5808   mainw->files[mt->render_file]->ratio_fps = check_for_ratio_fps(mainw->files[mt->render_file]->fps);
5809 
5810   if (mainw->files[mt->render_file]->hsize != hsize || mainw->files[mt->render_file]->vsize != vsize ||
5811       mainw->files[mt->render_file]->arate != arate || mainw->files[mt->render_file]->achans != achans ||
5812       mainw->files[mt->render_file]->asampsize != asamps || mainw->files[mt->render_file]->signed_endian != ase) {
5813     retval = mt_set_vals_string();
5814   }
5815 
5816   if (mt->is_ready) scroll_tracks(mt, 0, TRUE);
5817 
5818   if (mainw->files[mt->render_file]->achans == 0) {
5819     mt->avol_fx = -1;
5820     mt->avol_init_event = NULL;
5821   } else set_audio_filter_channel_values(mt);
5822 
5823   return retval;
5824 }
5825 
5826 
event_list_free_undos(lives_mt * mt)5827 void event_list_free_undos(lives_mt * mt) {
5828   if (!mt) return;
5829 
5830   if (mt->undos) lives_list_free(mt->undos);
5831   mt->undos = NULL;
5832   mt->undo_buffer_used = 0;
5833   mt->undo_offset = 0;
5834 
5835   if (mainw->is_exiting) return;
5836 
5837   mt_set_undoable(mt, MT_UNDO_NONE, NULL, FALSE);
5838   mt_set_redoable(mt, MT_UNDO_NONE, NULL, FALSE);
5839 }
5840 
5841 
stored_event_list_free_undos(void)5842 void stored_event_list_free_undos(void) {
5843   if (mainw->stored_layout_undos) lives_list_free(mainw->stored_layout_undos);
5844   mainw->stored_layout_undos = NULL;
5845   lives_freep((void **)&mainw->sl_undo_mem);
5846   mainw->sl_undo_buffer_used = 0;
5847   mainw->sl_undo_offset = 0;
5848 }
5849 
5850 
remove_current_from_affected_layouts(lives_mt * mt)5851 void remove_current_from_affected_layouts(lives_mt * mt) {
5852   // remove from affected layouts map
5853   if (mainw->affected_layouts_map) {
5854     LiVESList *found = lives_list_find_custom(mainw->affected_layouts_map, mainw->string_constants[LIVES_STRING_CONSTANT_CL],
5855                        (LiVESCompareFunc)strcmp);
5856     if (found) {
5857       lives_free((livespointer)found->data);
5858       mainw->affected_layouts_map = lives_list_delete_link(mainw->affected_layouts_map, found);
5859     }
5860   }
5861 
5862   if (!mainw->affected_layouts_map) {
5863     lives_widget_set_sensitive(mainw->show_layout_errors, FALSE);
5864     if (mt) lives_widget_set_sensitive(mt->show_layout_errors, FALSE);
5865   }
5866 
5867   recover_layout_cancelled(FALSE);
5868 
5869   if (mt) {
5870     if (mt->event_list) {
5871       event_list_free(mt->event_list);
5872       mt->event_list = NULL;
5873     }
5874     mt_clear_timeline(mt);
5875   }
5876 
5877   // remove some text
5878 
5879   if (mainw->layout_textbuffer) {
5880     LiVESTextIter iter1, iter2;
5881     LiVESList *markmap = mainw->affected_layout_marks;
5882     while (markmap) {
5883       lives_text_buffer_get_iter_at_mark(LIVES_TEXT_BUFFER(mainw->layout_textbuffer), &iter1, (LiVESTextMark *)markmap->data);
5884       lives_text_buffer_get_iter_at_mark(LIVES_TEXT_BUFFER(mainw->layout_textbuffer), &iter2,
5885                                          (LiVESTextMark *)markmap->next->data);
5886       lives_text_buffer_delete(LIVES_TEXT_BUFFER(mainw->layout_textbuffer), &iter1, &iter2);
5887 
5888       lives_text_buffer_delete_mark(LIVES_TEXT_BUFFER(mainw->layout_textbuffer), (LiVESTextMark *)markmap->data);
5889       lives_text_buffer_delete_mark(LIVES_TEXT_BUFFER(mainw->layout_textbuffer), (LiVESTextMark *)markmap->next->data);
5890       markmap = markmap->next->next;
5891     }
5892     mainw->affected_layout_marks = NULL;
5893   }
5894 }
5895 
5896 
stored_event_list_free_all(boolean wiped)5897 void stored_event_list_free_all(boolean wiped) {
5898   register int i;
5899 
5900   for (i = 0; i < MAX_FILES; i++) {
5901     if (mainw->files[i]) {
5902       mainw->files[i]->stored_layout_frame = 0;
5903       mainw->files[i]->stored_layout_audio = 0.;
5904       mainw->files[i]->stored_layout_fps = 0.;
5905       mainw->files[i]->stored_layout_idx = -1;
5906     }
5907   }
5908 
5909   stored_event_list_free_undos();
5910 
5911   if (mainw->stored_event_list) event_list_free(mainw->stored_event_list);
5912   mainw->stored_event_list = NULL;
5913 
5914   if (wiped) {
5915     remove_current_from_affected_layouts(NULL);
5916     mainw->stored_event_list_changed = FALSE;
5917   }
5918 }
5919 
5920 
print_layout_wiped(void)5921 LIVES_INLINE void print_layout_wiped(void) {d_print(_("Layout was wiped.\n"));}
5922 
5923 
check_for_layout_del(lives_mt * mt,boolean exiting)5924 boolean check_for_layout_del(lives_mt * mt, boolean exiting) {
5925   // save or wipe event_list
5926   // returns FALSE if cancelled
5927   int resp = 2;
5928 
5929   if ((!mt || !mt->event_list || !get_first_event(mt->event_list)) &&
5930       (!mainw->stored_event_list || !get_first_event(mainw->stored_event_list))) return TRUE;
5931 
5932   if (((mt && (mt->changed || mainw->scrap_file != -1 || mainw->ascrap_file != -1)) ||
5933        (mainw->stored_event_list &&
5934         mainw->stored_event_list_changed))) {
5935     int type = ((mainw->scrap_file == -1 && mainw->ascrap_file == -1) || !mt) ? 3 * (!exiting) : 4;
5936     _entryw *cdsw = create_cds_dialog(type);
5937 
5938     do {
5939       resp = lives_dialog_run(LIVES_DIALOG(cdsw->dialog));
5940       if (resp == 2) {
5941         // save
5942         mainw->cancelled = CANCEL_NONE;
5943         on_save_event_list_activate(NULL, mt);
5944         if (mainw->cancelled == CANCEL_NONE) {
5945           break;
5946         } else mainw->cancelled = CANCEL_NONE;
5947       }
5948     } while (resp == 2);
5949 
5950     lives_widget_destroy(cdsw->dialog);
5951     lives_free(cdsw);
5952 
5953     if (resp == LIVES_RESPONSE_CANCEL) {
5954       // cancel
5955       return FALSE;
5956     }
5957 
5958     recover_layout_cancelled(FALSE);
5959 
5960     if (resp == 1 && !exiting) {
5961       // wipe
5962       prefs->ar_layout = FALSE;
5963       set_string_pref(PREF_AR_LAYOUT, "");
5964       lives_memset(prefs->ar_layout_name, 0, 1);
5965     }
5966   }
5967 
5968   if (mainw->stored_event_list || mainw->sl_undo_mem) {
5969     stored_event_list_free_all(TRUE);
5970     print_layout_wiped();
5971   } else if (mt && mt->event_list && (exiting || resp == 1)) {
5972     event_list_free(mt->event_list);
5973     event_list_free_undos(mt);
5974     mt->event_list = NULL;
5975     mt_clear_timeline(mt);
5976     close_scrap_file(TRUE);
5977     close_ascrap_file(TRUE);
5978     mainw->recording_recovered = FALSE;
5979     print_layout_wiped();
5980   }
5981   prefs->letterbox_mt = future_prefs->letterbox_mt;
5982   return TRUE;
5983 }
5984 
5985 
delete_audio_tracks(lives_mt * mt,LiVESList * list,boolean full)5986 void delete_audio_tracks(lives_mt * mt, LiVESList * list, boolean full) {
5987   LiVESList *slist = list;
5988   while (slist) {
5989     delete_audio_track(mt, (LiVESWidget *)slist->data, full);
5990     slist = slist->next;
5991   }
5992   lives_list_free(list);
5993 }
5994 
5995 
mt_quit_activate(LiVESMenuItem * menuitem,livespointer user_data)5996 void mt_quit_activate(LiVESMenuItem * menuitem, livespointer user_data) {
5997   lives_mt *mt = (lives_mt *)user_data;
5998 
5999   if (!check_for_layout_del(mt, FALSE)) return;
6000 
6001   if (mt->idlefunc > 0) lives_source_remove(mt->idlefunc);
6002   mt->idlefunc = 0;
6003 
6004   on_quit_activate(menuitem, NULL);
6005 }
6006 
6007 
set_mt_title(lives_mt * mt)6008 static void set_mt_title(lives_mt * mt) {
6009   char *wtxt = lives_strdup_printf(_("Multitrack %dx%d : %d bpp %.3f fps"), mainw->files[mt->render_file]->hsize,
6010                                    mainw->files[mt->render_file]->vsize, mainw->files[mt->render_file]->bpp,
6011                                    mainw->files[mt->render_file]->fps);
6012   lives_window_set_title(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), wtxt);
6013   lives_free(wtxt);
6014 }
6015 
6016 
timecode_string_validate(LiVESEntry * entry,lives_mt * mt)6017 static boolean timecode_string_validate(LiVESEntry * entry, lives_mt * mt) {
6018   const char *etext = lives_entry_get_text(entry);
6019   char **array;
6020 
6021   double secs;
6022   double tl_range, pos;
6023 
6024   int hrs, mins;
6025 
6026   if (get_token_count((char *)etext, ':') != 3) return FALSE;
6027 
6028   array = lives_strsplit(etext, ":", 3);
6029 
6030   if (get_token_count(array[2], '.') != 2) {
6031     lives_strfreev(array);
6032     return FALSE;
6033   }
6034 
6035   hrs = atoi(array[0]);
6036   mins = atoi(array[1]);
6037   if (mins > 59) mins = 59;
6038   secs = lives_strtod(array[2], NULL);
6039 
6040   lives_strfreev(array);
6041 
6042   secs = secs + mins * 60. + hrs * 3600.;
6043 
6044   if (secs > mt->end_secs) {
6045     tl_range = mt->tl_max - mt->tl_min;
6046     set_timeline_end_secs(mt, secs);
6047 
6048     mt->tl_min = secs - tl_range / 2;
6049     mt->tl_max = secs + tl_range / 2;
6050 
6051     if (mt->tl_max > mt->end_secs) {
6052       mt->tl_min -= (mt->tl_max - mt->end_secs);
6053       mt->tl_max = mt->end_secs;
6054     }
6055   }
6056 
6057   mt_tl_move(mt, secs);
6058 
6059   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
6060 
6061   pos = mt->ptr_time;
6062 
6063   pos = q_dbl(pos, mt->fps) / TICKS_PER_SECOND_DBL;
6064   if (pos < 0.) pos = 0.;
6065 
6066   update_timecodes(mt, pos);
6067 
6068   return TRUE;
6069 }
6070 
6071 
cmi_set_inactive(LiVESWidget * widget,livespointer data)6072 static void cmi_set_inactive(LiVESWidget * widget, livespointer data) {
6073   if (widget == data) return;
6074   lives_widget_object_freeze_notify(LIVES_WIDGET_OBJECT(widget));
6075   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(widget), FALSE);
6076   lives_widget_object_thaw_notify(LIVES_WIDGET_OBJECT(widget));
6077 }
6078 
6079 
mt_set_autotrans(int idx)6080 void mt_set_autotrans(int idx) {
6081   prefs->atrans_fx = idx;
6082   if (idx == -1) set_string_pref(PREF_ACTIVE_AUTOTRANS, "none");
6083   else {
6084     char *atrans_hash = make_weed_hashname(prefs->atrans_fx, TRUE, FALSE, '|', FALSE);
6085     set_string_pref(PREF_ACTIVE_AUTOTRANS, atrans_hash);
6086     lives_free(atrans_hash);
6087   }
6088   mouse_mode_context(mainw->multitrack);
6089 }
6090 
6091 
mt_set_atrans_effect(LiVESMenuItem * menuitem,livespointer user_data)6092 static void mt_set_atrans_effect(LiVESMenuItem * menuitem, livespointer user_data) {
6093   lives_mt *mt = (lives_mt *)user_data;
6094 
6095   if (!lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(menuitem))) return;
6096   lives_container_foreach(LIVES_CONTAINER(mt->submenu_atransfx), cmi_set_inactive, menuitem);
6097 
6098   mt_set_autotrans(LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(menuitem), "idx")));
6099 }
6100 
6101 
after_timecode_changed(LiVESWidget * entry,LiVESXEventFocus * dir,livespointer user_data)6102 static void after_timecode_changed(LiVESWidget * entry, LiVESXEventFocus * dir, livespointer user_data) {
6103   lives_mt *mt = (lives_mt *)user_data;
6104   double pos;
6105 
6106   if (!timecode_string_validate(LIVES_ENTRY(entry), mt)) {
6107     pos = mt->ptr_time;
6108     pos = q_dbl(pos, mt->fps) / TICKS_PER_SECOND_DBL;
6109     if (pos < 0.) pos = 0.;
6110     update_timecodes(mt, pos);
6111   }
6112 }
6113 
6114 
get_tab_name(uint32_t tab)6115 static char *get_tab_name(uint32_t tab) {
6116   switch (tab) {
6117   case POLY_CLIPS:
6118     return (_("Clips"));
6119   case POLY_IN_OUT:
6120     return (_("In/out"));
6121   case POLY_FX_STACK:
6122     return (_("FX stack"));
6123   case POLY_EFFECTS:
6124     return lives_fx_cat_to_text(LIVES_FX_CAT_EFFECT, TRUE); // effects
6125   case POLY_TRANS:
6126     return lives_fx_cat_to_text(LIVES_FX_CAT_TRANSITION, TRUE); // transitions
6127   case POLY_COMP:
6128     return lives_fx_cat_to_text(LIVES_FX_CAT_COMPOSITOR, TRUE); // compositors
6129   case POLY_PARAMS:
6130     return (_("Params."));
6131   default:
6132     break;
6133   }
6134   return lives_strdup("");
6135 }
6136 
6137 
set_child_grad(LiVESWidget * widget,livespointer data)6138 static void set_child_grad(LiVESWidget * widget, livespointer data) {
6139   char *tmp = (char *)data;
6140   if (widget == mainw->multitrack->timecode) return;
6141   set_css_value_direct(widget, LIVES_WIDGET_STATE_NORMAL, "",
6142                        "background-image", tmp);
6143   if (LIVES_IS_CONTAINER(widget))
6144     lives_container_foreach(LIVES_CONTAINER(widget), set_child_grad, tmp);
6145 }
6146 
6147 
set_mt_colours(lives_mt * mt)6148 void set_mt_colours(lives_mt * mt) {
6149   lives_widget_set_bg_color(mt->timecode, LIVES_WIDGET_STATE_NORMAL, &palette->mt_timecode_bg);
6150   lives_widget_set_base_color(mt->timecode, LIVES_WIDGET_STATE_NORMAL, &palette->mt_timecode_bg);
6151   lives_widget_set_text_color(mt->timecode, LIVES_WIDGET_STATE_NORMAL, &palette->mt_timecode_fg);
6152 
6153   lives_widget_set_bg_color(mt->timecode, LIVES_WIDGET_STATE_INSENSITIVE, &palette->mt_timecode_bg);
6154   lives_widget_set_base_color(mt->timecode, LIVES_WIDGET_STATE_INSENSITIVE, &palette->mt_timecode_bg);
6155   lives_widget_set_text_color(mt->timecode, LIVES_WIDGET_STATE_INSENSITIVE, &palette->mt_timecode_fg);
6156 
6157   lives_widget_set_bg_color(LIVES_WIDGET(mt->timeline_table), LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6158 
6159   lives_widget_set_bg_color(LIVES_WIDGET(mt->timeline), LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6160   lives_widget_set_fg_color(LIVES_WIDGET(mt->timeline), LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6161 
6162 #ifdef ENABLE_GIW_3
6163   // need to set this even if theme is none
6164   if (mt->timeline) {
6165     int woat = widget_opts.apply_theme;
6166     widget_opts.apply_theme = 1;
6167     lives_widget_apply_theme(mt->timeline, LIVES_WIDGET_STATE_NORMAL);
6168     widget_opts.apply_theme = woat;
6169   }
6170 #endif
6171 
6172   mt_clip_select(mt, FALSE);
6173 
6174   lives_widget_apply_theme(mt->top_vbox, LIVES_WIDGET_STATE_NORMAL);
6175   lives_widget_apply_theme(mt->tl_hbox, LIVES_WIDGET_STATE_NORMAL);
6176 
6177   lives_widget_apply_theme(mt->clip_inner_box, LIVES_WIDGET_STATE_NORMAL);
6178 
6179   if (palette->style & STYLE_1) {
6180     lives_widget_apply_theme2(mt->menubar, LIVES_WIDGET_STATE_NORMAL, TRUE);
6181     lives_widget_apply_theme2(mt->menu_hbox, LIVES_WIDGET_STATE_NORMAL, TRUE);
6182   } else {
6183     lives_widget_apply_theme(mt->menubar, LIVES_WIDGET_STATE_NORMAL);
6184     lives_widget_apply_theme(mt->menu_hbox, LIVES_WIDGET_STATE_NORMAL);
6185   }
6186 
6187   lives_widget_apply_theme(mt->eventbox, LIVES_WIDGET_STATE_NORMAL);
6188   lives_widget_apply_theme(mt->scroll_label, LIVES_WIDGET_STATE_NORMAL);
6189 
6190   if (mt->dumlabel1)
6191     lives_widget_set_bg_color(mt->dumlabel1, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6192   if (mt->dumlabel2)
6193     lives_widget_set_bg_color(mt->dumlabel2, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6194 
6195   lives_widget_set_fg_color(lives_frame_get_label_widget(LIVES_FRAME(mt->preview_frame)), LIVES_WIDGET_STATE_NORMAL,
6196                             &palette->normal_fore);
6197 
6198   lives_widget_apply_theme2(mt->top_eventbox, LIVES_WIDGET_STATE_NORMAL, TRUE);
6199 
6200   if (palette->style & STYLE_1) {
6201     set_submenu_colours(LIVES_MENU(mt->files_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6202     set_submenu_colours(LIVES_MENU(mt->edit_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6203     set_submenu_colours(LIVES_MENU(mt->play_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6204     set_submenu_colours(LIVES_MENU(mt->effects_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6205     set_submenu_colours(LIVES_MENU(mt->tracks_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6206     set_submenu_colours(LIVES_MENU(mt->selection_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6207     set_submenu_colours(LIVES_MENU(mt->tools_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6208     set_submenu_colours(LIVES_MENU(mt->render_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6209     set_submenu_colours(LIVES_MENU(mt->view_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6210     set_submenu_colours(LIVES_MENU(mt->help_menu), &palette->menu_and_bars_fore, &palette->menu_and_bars);
6211   }
6212   lives_widget_set_bg_color(mt->grav_submenu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
6213 #if GTK_CHECK_VERSION(3, 0, 0)
6214   lives_widget_set_bg_color(mt->grav_submenu, LIVES_WIDGET_STATE_BACKDROP, &palette->menu_and_bars);
6215 #endif
6216   lives_widget_set_fg_color(mt->grav_submenu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
6217 
6218   lives_tool_button_set_border_color(mt->grav_menuitem, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
6219   set_child_alt_colour(mt->grav_menuitem, TRUE);
6220 
6221   lives_widget_set_bg_color(mt->mm_submenu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
6222 #if GTK_CHECK_VERSION(3, 0, 0)
6223   lives_widget_set_bg_color(mt->mm_submenu, LIVES_WIDGET_STATE_BACKDROP, &palette->menu_and_bars);
6224 #endif
6225   lives_widget_set_fg_color(mt->mm_submenu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
6226   lives_tool_button_set_border_color(mt->mm_menuitem, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
6227   set_child_alt_colour(mt->mm_menuitem, TRUE);
6228 
6229   lives_widget_set_bg_color(mt->ins_submenu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
6230 #if GTK_CHECK_VERSION(3, 0, 0)
6231   lives_widget_set_bg_color(mt->ins_submenu, LIVES_WIDGET_STATE_BACKDROP, &palette->menu_and_bars);
6232 #endif
6233   lives_widget_set_fg_color(mt->ins_submenu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
6234   lives_tool_button_set_border_color(mt->ins_menuitem, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
6235   set_child_alt_colour(mt->ins_menuitem, TRUE);
6236 
6237   lives_widget_set_fg_color(mt->l_sel_arrow, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6238   lives_widget_set_fg_color(mt->r_sel_arrow, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6239 
6240   lives_widget_apply_theme2(mt->amixer_button, LIVES_WIDGET_STATE_NORMAL, TRUE);
6241   set_child_alt_colour(mt->amixer_button, TRUE);
6242 
6243   if (palette->style & STYLE_1) {
6244     lives_widget_apply_theme2(mainw->vol_toolitem, LIVES_WIDGET_STATE_NORMAL, FALSE);
6245     if (mainw->vol_label) lives_widget_apply_theme2(mainw->vol_label, LIVES_WIDGET_STATE_NORMAL, FALSE);
6246     lives_widget_apply_theme2(mainw->volume_scale, LIVES_WIDGET_STATE_NORMAL, FALSE);
6247 
6248     set_css_value_direct(mainw->volume_scale, LIVES_WIDGET_STATE_NORMAL, "",
6249                          "background-image",  "none");
6250   }
6251 
6252   lives_widget_apply_theme(mt->in_out_box, LIVES_WIDGET_STATE_NORMAL);
6253   set_child_colour(mt->in_out_box, TRUE);
6254 
6255   if (palette->style & STYLE_4) {
6256     lives_widget_show(mt->hseparator);
6257     lives_widget_apply_theme2(mt->hseparator, LIVES_WIDGET_STATE_NORMAL, TRUE);
6258     if (mt->hseparator2) {
6259       lives_widget_show(mt->hseparator2);
6260       lives_widget_apply_theme2(mt->hseparator2, LIVES_WIDGET_STATE_NORMAL, TRUE);
6261     }
6262   } else {
6263     lives_widget_hide(mt->hseparator);
6264     if (mt->hseparator2) {
6265       lives_widget_hide(mt->hseparator2);
6266     }
6267   }
6268 
6269   lives_widget_set_bg_color(mt->in_image, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6270   lives_widget_set_bg_color(mt->out_image, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6271 
6272   lives_widget_apply_theme2(mt->btoolbarx, LIVES_WIDGET_STATE_NORMAL, TRUE);
6273 
6274   lives_widget_apply_theme2(mt->btoolbary, LIVES_WIDGET_STATE_NORMAL, TRUE);
6275   set_child_alt_colour(mt->btoolbary, TRUE);
6276 
6277   lives_widget_set_fg_color(mt->tlx_eventbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6278   lives_widget_set_bg_color(mt->tlx_eventbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6279 
6280   if (mt->fx_params_label) lives_widget_apply_theme2(mt->fx_params_label, LIVES_WIDGET_STATE_NORMAL, TRUE);
6281   lives_widget_apply_theme(mt->time_label, LIVES_WIDGET_STATE_NORMAL);
6282 
6283   if (mt->tl_label)
6284     lives_widget_set_fg_color(mt->tl_label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6285 
6286   // needed for gtk+ 2.x
6287   lives_widget_apply_theme2(lives_widget_get_parent(mt->insa_label), LIVES_WIDGET_STATE_NORMAL, TRUE);
6288   lives_widget_apply_theme2(mt->insa_label, LIVES_WIDGET_STATE_NORMAL, TRUE);
6289 
6290   lives_widget_apply_theme2(lives_widget_get_parent(mt->overlap_label), LIVES_WIDGET_STATE_NORMAL, TRUE);
6291   lives_widget_apply_theme2(mt->overlap_label, LIVES_WIDGET_STATE_NORMAL, TRUE);
6292 
6293   lives_widget_set_bg_color(mt->preview_eventbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6294 
6295   lives_widget_apply_theme2(mt->btoolbar2, LIVES_WIDGET_STATE_NORMAL, TRUE);
6296   lives_widget_apply_theme2(mt->btoolbar3, LIVES_WIDGET_STATE_NORMAL, TRUE);
6297 
6298   lives_widget_apply_theme2(lives_widget_get_parent(mt->grav_label), LIVES_WIDGET_STATE_NORMAL, TRUE);
6299   lives_widget_apply_theme2(mt->grav_label, LIVES_WIDGET_STATE_NORMAL, TRUE);
6300 
6301   if (mt->ins_label) {
6302     lives_widget_apply_theme2(lives_widget_get_parent(mt->ins_label), LIVES_WIDGET_STATE_NORMAL, TRUE);
6303     lives_widget_apply_theme2(mt->ins_label, LIVES_WIDGET_STATE_NORMAL, TRUE);
6304   }
6305 
6306   if (mt->mm_label) {
6307     lives_widget_apply_theme2(lives_widget_get_parent(mt->mm_label), LIVES_WIDGET_STATE_NORMAL, TRUE);
6308     lives_widget_apply_theme2(mt->mm_label, LIVES_WIDGET_STATE_NORMAL, TRUE);
6309   }
6310 
6311   lives_widget_apply_theme2(lives_bin_get_child(LIVES_BIN(mt->grav_normal)), LIVES_WIDGET_STATE_NORMAL, TRUE);
6312   lives_widget_apply_theme2(lives_bin_get_child(LIVES_BIN(mt->grav_left)), LIVES_WIDGET_STATE_NORMAL, TRUE);
6313   lives_widget_apply_theme2(lives_bin_get_child(LIVES_BIN(mt->grav_right)), LIVES_WIDGET_STATE_NORMAL, TRUE);
6314 
6315   lives_widget_set_bg_color(mt->hpaned, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6316 
6317   lives_widget_set_bg_color(mt->nb, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6318   lives_widget_set_base_color(mt->nb, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6319 
6320   lives_widget_set_bg_color(mt->nb, LIVES_WIDGET_STATE_ACTIVE, &palette->menu_and_bars);
6321   lives_widget_set_base_color(mt->nb, LIVES_WIDGET_STATE_ACTIVE, &palette->menu_and_bars);
6322 
6323   lives_widget_set_fg_color(mt->nb, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6324   lives_widget_set_text_color(mt->nb, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6325 
6326   lives_widget_set_bg_color(lives_bin_get_child(LIVES_BIN(mt->clip_scroll)), LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6327 
6328   lives_widget_apply_theme2(mt->fx_base_box, LIVES_WIDGET_STATE_NORMAL, TRUE);
6329 
6330   lives_widget_apply_theme(mt->context_frame, LIVES_WIDGET_STATE_NORMAL);
6331 
6332   lives_widget_set_fg_color(lives_frame_get_label_widget(LIVES_FRAME(mt->context_frame)), LIVES_WIDGET_STATE_NORMAL,
6333                             &palette->normal_fore);
6334 
6335   // gtk+ 2.x
6336   if ((mt->poly_state == POLY_FX_STACK || mt->poly_state == POLY_EFFECTS || mt->poly_state == POLY_TRANS ||
6337        mt->poly_state == POLY_COMP) \
6338       && LIVES_IS_BIN(mt->fx_list_scroll) && lives_bin_get_child(LIVES_BIN(mt->fx_list_scroll)))
6339     lives_widget_set_bg_color(lives_bin_get_child(LIVES_BIN(mt->fx_list_scroll)), LIVES_WIDGET_STATE_NORMAL,
6340                               &palette->normal_back);
6341   if (prefs->mt_show_ctx) {
6342     lives_widget_apply_theme(lives_bin_get_child(LIVES_BIN(mt->context_scroll)), LIVES_WIDGET_STATE_NORMAL);
6343   }
6344 
6345   if (mt->context_box && LIVES_IS_WIDGET_OBJECT(mt->context_box)) {
6346     lives_widget_apply_theme(mt->context_box, LIVES_WIDGET_STATE_NORMAL);
6347     set_child_colour(mt->context_box, TRUE);
6348   }
6349 
6350   lives_widget_apply_theme(mt->vpaned, LIVES_WIDGET_STATE_NORMAL);
6351 
6352   lives_widget_set_bg_color(mt->tl_eventbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6353 
6354   set_child_alt_colour(mt->btoolbar2, TRUE);
6355 
6356 #if GTK_CHECK_VERSION(3, 16, 0)
6357   if (mainw->pretty_colours) {
6358     char *colref2 = gdk_rgba_to_string(&palette->menu_and_bars);
6359     char *colref = gdk_rgba_to_string(&palette->normal_back);
6360     char *tmp = lives_strdup_printf("linear-gradient(%s, %s)", colref2, colref);
6361     set_css_value_direct(lives_widget_get_parent(mt->btoolbar2), LIVES_WIDGET_STATE_NORMAL, "",
6362                          "background-image", tmp);
6363     set_child_grad(lives_widget_get_parent(mt->btoolbar2), tmp);
6364     lives_free(colref); lives_free(colref2);
6365     lives_free(tmp);
6366   }
6367 #endif
6368 
6369   lives_widget_set_size_request(mainw->volume_scale, -1, -1);
6370   lives_widget_apply_theme(LIVES_WIDGET(mt->timeline_table_header), LIVES_WIDGET_STATE_NORMAL);
6371 
6372   if (mt->timeline) {
6373     lives_widget_apply_theme(mt->timeline, LIVES_WIDGET_STATE_NORMAL);
6374   }
6375 
6376   if (palette->style & STYLE_3) {
6377     if (mt->timeline_eb) {
6378       lives_widget_apply_theme2(mt->timeline_eb, LIVES_WIDGET_STATE_NORMAL, TRUE);
6379     }
6380     if (mt->timeline_reg) {
6381       lives_widget_apply_theme2(mt->timeline_reg, LIVES_WIDGET_STATE_NORMAL, TRUE);
6382     }
6383   } else {
6384     if (mt->timeline_reg) {
6385       lives_widget_set_bg_color(mt->timeline_reg, LIVES_WIDGET_STATE_NORMAL, &palette->white);
6386       lives_widget_set_fg_color(mt->timeline_reg, LIVES_WIDGET_STATE_NORMAL, &palette->black);
6387     }
6388   }
6389 
6390   // BG color is set by eventbox (gtk+ 2.x), this is for gtk+3.x (?)
6391   lives_widget_apply_theme(mt->amix_label, LIVES_WIDGET_STATE_NORMAL);
6392 
6393   // for gtk+2.x (At least) this sets the amixer button (?)
6394   lives_widget_apply_theme2(mt->amixer_button, LIVES_WIDGET_STATE_NORMAL, TRUE);
6395 
6396   if (palette->style & STYLE_2) {
6397 #if !GTK_CHECK_VERSION(3, 0, 0)
6398     lives_widget_set_base_color(mt->spinbutton_start, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6399     lives_widget_set_base_color(mt->spinbutton_start, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_back);
6400     lives_widget_set_base_color(mt->spinbutton_end, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
6401     lives_widget_set_base_color(mt->spinbutton_end, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_back);
6402     lives_widget_set_text_color(mt->spinbutton_start, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6403     lives_widget_set_text_color(mt->spinbutton_start, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_fore);
6404     lives_widget_set_text_color(mt->spinbutton_end, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
6405     lives_widget_set_text_color(mt->spinbutton_end, LIVES_WIDGET_STATE_INSENSITIVE, &palette->normal_fore);
6406 #endif
6407   }
6408 
6409   lives_widget_apply_theme(mt->sel_label, LIVES_WIDGET_STATE_NORMAL);
6410 
6411   /// TODO: clearly an array would be better
6412   lives_widget_apply_theme2(mt->nb_label1, LIVES_WIDGET_STATE_NORMAL, TRUE);
6413   lives_widget_apply_theme2(mt->nb_label1, LIVES_WIDGET_STATE_ACTIVE, TRUE);
6414   lives_widget_apply_theme2(mt->nb_label2, LIVES_WIDGET_STATE_NORMAL, TRUE);
6415   lives_widget_apply_theme2(mt->nb_label2, LIVES_WIDGET_STATE_ACTIVE, TRUE);
6416   lives_widget_apply_theme2(mt->nb_label3, LIVES_WIDGET_STATE_NORMAL, TRUE);
6417   lives_widget_apply_theme2(mt->nb_label3, LIVES_WIDGET_STATE_ACTIVE, TRUE);
6418   lives_widget_apply_theme2(mt->nb_label4, LIVES_WIDGET_STATE_NORMAL, TRUE);
6419   lives_widget_apply_theme2(mt->nb_label4, LIVES_WIDGET_STATE_ACTIVE, TRUE);
6420   lives_widget_apply_theme2(mt->nb_label5, LIVES_WIDGET_STATE_NORMAL, TRUE);
6421   lives_widget_apply_theme2(mt->nb_label5, LIVES_WIDGET_STATE_ACTIVE, TRUE);
6422   lives_widget_apply_theme2(mt->nb_label6, LIVES_WIDGET_STATE_NORMAL, TRUE);
6423   lives_widget_apply_theme2(mt->nb_label6, LIVES_WIDGET_STATE_ACTIVE, TRUE);
6424   lives_widget_apply_theme2(mt->nb_label7, LIVES_WIDGET_STATE_NORMAL, TRUE);
6425   lives_widget_apply_theme2(mt->nb_label7, LIVES_WIDGET_STATE_ACTIVE, TRUE);
6426 
6427   redraw_all_event_boxes(mt);
6428 }
6429 
6430 
on_solo_check_toggled(LiVESWidget * widg,livespointer user_data)6431 static void on_solo_check_toggled(LiVESWidget * widg, livespointer user_data) {
6432   lives_mt *mt = (lives_mt *)user_data;
6433   activate_mt_preview(mt);
6434 }
6435 
6436 
multitrack_clear_marks(LiVESMenuItem * menuitem,livespointer user_data)6437 static void multitrack_clear_marks(LiVESMenuItem * menuitem, livespointer user_data) {
6438   lives_mt *mt = (lives_mt *)user_data;
6439   lives_list_free(mt->tl_marks);
6440   mt->tl_marks = NULL;
6441   lives_widget_set_sensitive(mt->clear_marks, FALSE);
6442   lives_widget_set_sensitive(mt->mark_jumpback, FALSE);
6443   lives_widget_set_sensitive(mt->mark_jumpnext, FALSE);
6444   lives_widget_queue_draw(mt->timeline_reg);
6445 }
6446 
6447 
multitrack(weed_plant_t * event_list,int orig_file,double fps)6448 lives_mt *multitrack(weed_plant_t *event_list, int orig_file, double fps) {
6449   LiVESWidget *hseparator;
6450   LiVESWidget *menuitem;
6451   LiVESWidget *menuitem2;
6452   LiVESWidget *menuitem_menu;
6453   LiVESWidget *selcopy_menu;
6454 #if LIVES_HAS_IMAGE_MENU_ITEM
6455   LiVESWidget *image;
6456 #endif
6457   LiVESWidget *full_screen;
6458   LiVESWidget *about;
6459   LiVESWidget *show_mt_keys;
6460   LiVESWidget *view_mt_details;
6461   LiVESWidget *zoom_in;
6462   LiVESWidget *zoom_out;
6463   LiVESWidget *show_messages;
6464   LiVESWidget *scrollbar;
6465   LiVESWidget *hbox;
6466   LiVESWidget *vbox;
6467   LiVESWidget *ign_ins_sel;
6468   LiVESWidget *recent_submenu;
6469 #ifdef ENABLE_DVD_GRAB
6470   LiVESWidget *vcd_dvd_submenu;
6471 #endif
6472 #ifdef HAVE_LDVGRAB
6473   LiVESWidget *device_submenu;
6474 #endif
6475 #ifdef HAVE_WEBM
6476   LiVESWidget *open_loc_submenu;
6477 #endif
6478   LiVESWidget *submenu;
6479   LiVESWidget *submenu2;
6480 
6481   // block fx submenus
6482   LiVESWidget *submenu_menu;
6483   LiVESWidget *submenu_menuv;
6484   LiVESWidget *submenu_menua;
6485   LiVESWidget *submenu_menuvp = NULL;
6486   LiVESWidget *submenu_menuap = NULL;
6487 
6488   // region fx submenus
6489   LiVESWidget *submenu_menurv;
6490   LiVESWidget *submenu_menura;
6491   LiVESWidget *submenu_menurvp = NULL;
6492   LiVESWidget *submenu_menurap = NULL;
6493   LiVESWidget *submenu_menu2av;
6494   LiVESWidget *submenu_menu2v;
6495   LiVESWidget *submenu_menu2a;
6496   LiVESWidget *submenu_menu3;
6497 
6498   LiVESWidget *show_frame_events;
6499   LiVESWidget *ccursor;
6500   LiVESWidget *sep;
6501   LiVESWidget *show_manual;
6502   LiVESWidget *donate;
6503   LiVESWidget *email_author;
6504   LiVESWidget *report_bug;
6505   LiVESWidget *suggest_feature;
6506   LiVESWidget *help_translate;
6507   LiVESWidget *label;
6508 
6509   LiVESWidgetObject *vadjustment;
6510   LiVESAdjustment *spinbutton_adj;
6511 
6512   boolean in_menubar = TRUE;
6513 
6514   char *cname, *tname, *msg;
6515   char *tmp, *tmp2;
6516   char *pkg, *xpkgv = NULL, *xpkga = NULL;
6517   const char *mtext = NULL;
6518 
6519   int dph;
6520   int num_filters;
6521   int woat = widget_opts.apply_theme;
6522   int i;
6523 
6524   lives_mt *mt = (lives_mt *)lives_calloc(1, sizeof(lives_mt));
6525 
6526   if (rte_window) {
6527     on_rtew_delete_event(NULL, NULL, NULL);
6528     lives_widget_destroy(rte_window);
6529     rte_window = NULL;
6530   }
6531 
6532   mainw->multitrack = mt;
6533 
6534   mt->frame_pixbuf = NULL;
6535 
6536   mt->is_ready = FALSE;
6537   mt->tl_marks = NULL;
6538 
6539   mt->timestring[0] = 0;
6540 
6541   mt->idlefunc = 0; // idle function for auto backup
6542   mt->auto_back_time = 0;
6543 
6544   mt->playing_sel = FALSE;
6545 
6546   mt->in_sensitise = FALSE;
6547 
6548   mt->render_file = mainw->current_file;
6549 
6550   if (!mainw->sl_undo_mem) {
6551     mt->undo_mem = (uint8_t *)lives_malloc(prefs->mt_undo_buf * 1024 * 1024);
6552     if (!mt->undo_mem) {
6553       do_mt_undo_mem_error();
6554     }
6555     mt->undo_buffer_used = 0;
6556     mt->undos = NULL;
6557     mt->undo_offset = 0;
6558   } else {
6559     mt->undo_mem = mainw->sl_undo_mem;
6560     mt->undo_buffer_used = mainw->sl_undo_buffer_used;
6561     mt->undos = mainw->stored_layout_undos;
6562     mt->undo_offset = mainw->sl_undo_offset;
6563   }
6564 
6565   mt->preview_layer = -1000000;
6566 
6567   mt->solo_inst = NULL;
6568 
6569   mt->apply_fx_button = NULL;
6570   mt->resetp_button = NULL;
6571 
6572   mt->cursor_style = LIVES_CURSOR_NORMAL;
6573 
6574   mt->file_selected = orig_file;
6575 
6576   mt->auto_changed = mt->changed = FALSE;
6577 
6578   mt->was_undo_redo = FALSE;
6579 
6580   mt->tl_mouse = FALSE;
6581 
6582   mt->sel_locked = FALSE;
6583 
6584   mt->clip_labels = NULL;
6585 
6586   mt->force_load_name = NULL;
6587 
6588   mt->dumlabel1 = mt->dumlabel2 = mt->tl_label = mt->timeline = mt->timeline_eb =
6589                                     mt->timeline_reg = NULL;
6590 
6591   mt->tl_surf = mt->tl_ev_surf = mt->tl_reg_surf = NULL;
6592 
6593   mt->play_width = mt->play_height = 0;
6594 
6595   // init .opts =
6596   if (mainw->multi_opts.set) {
6597     lives_memcpy(&mt->opts, &mainw->multi_opts, sizeof(mt->opts));
6598     mt->opts.aparam_view_list = lives_list_copy(mainw->multi_opts.aparam_view_list);
6599   } else {
6600     mt->opts.move_effects = TRUE;
6601     mt->opts.fx_auto_preview = TRUE;
6602     mt->opts.snap_over = FALSE;
6603     mt->opts.mouse_mode = MOUSE_MODE_MOVE;
6604     mt->opts.show_audio = TRUE;
6605     mt->opts.ign_ins_sel = FALSE;
6606     mt->opts.follow_playback = TRUE;
6607     mt->opts.grav_mode = GRAV_MODE_NORMAL;
6608     mt->opts.insert_mode = INSERT_MODE_NORMAL;
6609     mt->opts.autocross_audio = TRUE;
6610     mt->opts.render_vidp = TRUE;
6611     mt->opts.render_audp = TRUE;
6612     mt->opts.normalise_audp = TRUE;
6613     mt->opts.aparam_view_list = NULL;
6614     mt->opts.hpaned_pos = mt->opts.vpaned_pos = -1;
6615     mt->opts.overlay_timecode = TRUE;
6616     mt->opts.ptr_time = 0.;
6617   }
6618 
6619   mt->opts.insert_audio = TRUE;
6620 
6621   mt->opts.pertrack_audio = prefs->mt_pertrack_audio;
6622   mt->opts.audio_bleedthru = FALSE;
6623   mt->opts.gang_audio = TRUE;
6624   mt->opts.back_audio_tracks = 1;
6625 
6626   if (force_pertrack_audio) mt->opts.pertrack_audio = TRUE;
6627   force_pertrack_audio = FALSE;
6628 
6629   mt->tl_fixed_length = 0.;
6630   mt->ptr_time = 0.;
6631   mt->video_draws = NULL;
6632   mt->block_selected = NULL;
6633   mt->event_list = event_list;
6634   mt->accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
6635   lives_window_remove_accel_group(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), mainw->accel_group);
6636   lives_window_add_accel_group(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), mt->accel_group);
6637 
6638   mt->fps = fps;
6639   mt->hotspot_x = mt->hotspot_y = 0;
6640   mt->redraw_block = FALSE;
6641 
6642   mt->region_start = mt->region_end = 0.;
6643   mt->region_updating = FALSE;
6644   mt->is_rendering = FALSE;
6645   mt->pr_audio = FALSE;
6646   mt->selected_tracks = NULL;
6647   mt->mt_frame_preview = FALSE;
6648   mt->current_rfx = NULL;
6649   mt->current_fx = -1;
6650   mt->putative_block = NULL;
6651   mt->specific_event = NULL;
6652 
6653   mt->block_tl_move = FALSE;
6654   mt->block_node_spin = FALSE;
6655 
6656   mt->is_atrans = FALSE;
6657 
6658   mt->last_fx_type = MT_LAST_FX_NONE;
6659 
6660   mt->display = mainw->mgeom[widget_opts.monitor].disp;
6661 
6662   mt->moving_block = FALSE;
6663 
6664   mt->insert_start = mt->insert_end = -1;
6665   mt->insert_avel = 1.;
6666 
6667   mt->selected_init_event = mt->init_event = NULL;
6668 
6669   mt->auto_reloading = FALSE;
6670   mt->fm_edit_event = NULL;
6671 
6672   mt->nb_label = NULL;
6673   mt->fx_list_box = NULL;
6674   mt->fx_list_scroll = NULL;
6675   mt->fx_list_label = NULL;
6676 
6677   mt->moving_fx = NULL;
6678   mt->fx_order = FX_ORD_NONE;
6679 
6680   lives_memset(mt->layout_name, 0, 1);
6681 
6682   mt->did_backup = FALSE;
6683   mt->framedraw = NULL;
6684 
6685   mt->audio_draws = NULL;
6686   mt->audio_vols = mt->audio_vols_back = NULL;
6687   mt->amixer = NULL;
6688   mt->ignore_load_vals = FALSE;
6689 
6690   mt->exact_preview = 0;
6691 
6692   mt->context_time = -1.;
6693   mt->use_context = FALSE;
6694 
6695   mt->no_expose = mt->no_expose_frame = TRUE;
6696 
6697   mt->is_paused = FALSE;
6698 
6699   mt->pb_start_event = NULL;
6700 
6701   mt->aud_track_selected = FALSE;
6702 
6703   mt->has_audio_file = FALSE;
6704 
6705   mt->fx_params_label = NULL;
6706   mt->fx_box = NULL;
6707 
6708   mt->selected_filter = -1;
6709 
6710   mt->top_track = 0;
6711 
6712   mt->cb_list = NULL;
6713 
6714   mt->no_frame_update = FALSE;
6715 
6716   if (mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].delegate != -1) {
6717     // user (or system) has delegated an audio volume filter from the candidates
6718     mt->avol_fx = LIVES_POINTER_TO_INT(lives_list_nth_data(mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].list,
6719                                        mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].delegate));
6720   } else mt->avol_fx = -1;
6721   mt->avol_init_event = NULL;
6722 
6723   if (prefs->mt_enter_prompt && rdet) {
6724     mt->user_width = rdet->width;
6725     mt->user_height = rdet->height;
6726     mt->user_fps = rdet->fps;
6727     mt->user_arate = xarate;
6728     mt->user_achans = xachans;
6729     mt->user_asamps = xasamps;
6730     mt->user_signed_endian = xse;
6731     mt->opts.pertrack_audio = ptaud;
6732     mt->opts.back_audio_tracks = btaud;
6733   }
6734 
6735   if (rdet) {
6736     lives_free(rdet->encoder_name);
6737     lives_freep((void **)&rdet);
6738     lives_freep((void **)&resaudw);
6739   }
6740 
6741   if (force_backing_tracks > mt->opts.back_audio_tracks) mt->opts.back_audio_tracks = force_backing_tracks;
6742   force_backing_tracks = 0;
6743 
6744   mt->top_vbox = lives_vbox_new(FALSE, 0);
6745   lives_widget_set_vexpand(mt->top_vbox, TRUE);
6746 
6747   mt->menu_hbox = lives_hbox_new(FALSE, 0);
6748   lives_box_pack_start(LIVES_BOX(mt->top_vbox), mt->menu_hbox, FALSE, FALSE, 0);
6749 
6750   mt->top_vpaned = lives_standard_vpaned_new();
6751   lives_box_pack_start(LIVES_BOX(mt->top_vbox), mt->top_vpaned, TRUE, TRUE, 0);
6752   lives_widget_set_vexpand(mt->top_vpaned, TRUE);
6753 
6754   mt->xtravbox = lives_vbox_new(FALSE, 0);
6755   lives_widget_set_vexpand(mt->xtravbox, TRUE);
6756 
6757   mt->menubar = lives_menu_bar_new();
6758   lives_box_pack_start(LIVES_BOX(mt->menu_hbox), mt->menubar, FALSE, FALSE, 0);
6759 
6760   // File
6761   menuitem = lives_standard_menu_item_new_with_label(_("_File"));
6762   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
6763 
6764   mt->files_menu = lives_menu_new();
6765   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->files_menu);
6766 
6767   mt->open_menu = lives_standard_menu_item_new_with_label(_("_Open..."));
6768   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->open_menu);
6769 
6770   menuitem_menu = lives_menu_new();
6771   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->open_menu), menuitem_menu);
6772 
6773   menuitem = lives_standard_menu_item_new_with_label(_("_Open File/Directory"));
6774   lives_container_add(LIVES_CONTAINER(menuitem_menu), menuitem);
6775 
6776   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
6777                        LIVES_GUI_CALLBACK(on_open_activate), NULL);
6778 
6779   menuitem = lives_standard_menu_item_new_with_label(_("O_pen File Selection..."));
6780   lives_container_add(LIVES_CONTAINER(menuitem_menu), menuitem);
6781 
6782   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
6783                        LIVES_GUI_CALLBACK(on_open_sel_activate), NULL);
6784 
6785   // TODO, show these options but show error if no mplayer / mplayer2
6786 
6787 #ifdef HAVE_WEBM
6788   mt->open_loc_menu = lives_standard_menu_item_new_with_label(_("Open _Location/Stream..."));
6789   lives_container_add(LIVES_CONTAINER(menuitem_menu), mt->open_loc_menu);
6790 
6791   open_loc_submenu = lives_menu_new();
6792   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->open_loc_menu), open_loc_submenu);
6793 
6794   menuitem = lives_standard_menu_item_new_with_label(_("Open _Youtube Clip..."));
6795   lives_container_add(LIVES_CONTAINER(open_loc_submenu), menuitem);
6796 
6797   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
6798                        LIVES_GUI_CALLBACK(on_open_utube_activate), NULL);
6799 
6800   menuitem = lives_standard_menu_item_new_with_label(_("Open _Location/Stream..."));
6801   lives_container_add(LIVES_CONTAINER(open_loc_submenu), menuitem);
6802 
6803 #else
6804 
6805   menuitem = lives_standard_menu_item_new_with_label(_("Open _Location/Stream..."));
6806   lives_container_add(LIVES_CONTAINER(menuitem_menu), menuitem);
6807 
6808 #endif
6809 
6810   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
6811                        LIVES_GUI_CALLBACK(on_open_loc_activate), NULL);
6812 
6813 #ifdef ENABLE_DVD_GRAB
6814   mt->vcd_dvd_menu = lives_standard_menu_item_new_with_label(_("Import Selection from _dvd/vcd..."));
6815   lives_container_add(LIVES_CONTAINER(menuitem_menu), mt->vcd_dvd_menu);
6816   vcd_dvd_submenu = lives_menu_new();
6817   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->vcd_dvd_menu), vcd_dvd_submenu);
6818 
6819   menuitem = lives_standard_menu_item_new_with_label(_("Import Selection from _dvd"));
6820   lives_container_add(LIVES_CONTAINER(vcd_dvd_submenu), menuitem);
6821 
6822   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
6823                        LIVES_GUI_CALLBACK(on_open_vcd_activate),
6824                        LIVES_INT_TO_POINTER(1));
6825 # endif
6826 
6827   menuitem = lives_standard_menu_item_new_with_label(_("Import Selection from _vcd"));
6828 
6829 #ifdef ENABLE_DVD_GRAB
6830   lives_container_add(LIVES_CONTAINER(vcd_dvd_submenu), menuitem);
6831 #else
6832   lives_container_add(LIVES_CONTAINER(menuitem_menu), menuitem);
6833 #endif
6834 
6835   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
6836                        LIVES_GUI_CALLBACK(on_open_vcd_activate),
6837                        LIVES_INT_TO_POINTER(2));
6838 
6839 #ifdef HAVE_LDVGRAB
6840   mt->device_menu = lives_standard_menu_item_new_with_label(_("_Import from Device"));
6841   lives_container_add(LIVES_CONTAINER(menuitem_menu), mt->device_menu);
6842   device_submenu = lives_menu_new();
6843 
6844   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->device_menu), device_submenu);
6845 
6846   if (capable->has_mplayer || capable->has_mplayer2) {
6847 
6848     menuitem = lives_standard_menu_item_new_with_label(_("Import from _Firewire Device (dv)"));
6849     lives_container_add(LIVES_CONTAINER(device_submenu), menuitem);
6850 
6851     lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
6852                          LIVES_GUI_CALLBACK(on_open_fw_activate),
6853                          LIVES_INT_TO_POINTER(CAM_FORMAT_DV));
6854 
6855     menuitem = lives_standard_menu_item_new_with_label(_("Import from _Firewire Device (hdv)"));
6856     lives_container_add(LIVES_CONTAINER(device_submenu), menuitem);
6857 
6858     lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
6859                          LIVES_GUI_CALLBACK(on_open_fw_activate),
6860                          LIVES_INT_TO_POINTER(CAM_FORMAT_HDV));
6861   }
6862 #endif
6863 
6864   mt->close = lives_standard_menu_item_new_with_label(_("_Close the Selected Clip"));
6865   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->close);
6866 
6867   lives_signal_connect(LIVES_GUI_OBJECT(mt->close), LIVES_WIDGET_ACTIVATE_SIGNAL,
6868                        LIVES_GUI_CALLBACK(on_close_activate), NULL);
6869 
6870   lives_widget_set_sensitive(mt->close, FALSE);
6871 
6872   lives_widget_add_accelerator(mt->close, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
6873                                LIVES_KEY_w, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
6874 
6875   mt->recent_menu = lives_standard_menu_item_new_with_label(_("_Recent Files..."));
6876   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->recent_menu);
6877   recent_submenu = lives_menu_new();
6878   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->recent_menu), recent_submenu);
6879 
6880   for (i = 0; i < N_RECENT_FILES; i++) {
6881     lives_widget_reparent(mainw->recent[i], recent_submenu);
6882   }
6883 
6884   lives_menu_add_separator(LIVES_MENU(mt->files_menu));
6885 
6886   mt->load_set = lives_standard_menu_item_new_with_label(_("_Reload Clip Set..."));
6887   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->load_set);
6888 
6889   lives_signal_connect(LIVES_GUI_OBJECT(mt->load_set), LIVES_WIDGET_ACTIVATE_SIGNAL,
6890                        LIVES_GUI_CALLBACK(on_load_set_activate), NULL);
6891 
6892   mt->save_set = lives_standard_menu_item_new_with_label(_("Close/Sa_ve All Clips"));
6893   lives_widget_set_sensitive(mt->save_set, FALSE);
6894   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->save_set);
6895 
6896   lives_signal_connect(LIVES_GUI_OBJECT(mt->save_set), LIVES_WIDGET_ACTIVATE_SIGNAL,
6897                        LIVES_GUI_CALLBACK(on_quit_activate), LIVES_INT_TO_POINTER(1));
6898 
6899   lives_menu_add_separator(LIVES_MENU(mt->files_menu));
6900 
6901   mt->save_event_list = lives_standard_image_menu_item_new_with_label(_("_Save Layout as..."));
6902   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->save_event_list);
6903   lives_widget_set_sensitive(mt->save_event_list, FALSE);
6904 
6905   lives_widget_add_accelerator(mt->save_event_list, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
6906                                LIVES_KEY_s, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
6907 
6908   mt->load_event_list = lives_standard_image_menu_item_new_with_label(_("_Load Layout..."));
6909   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->load_event_list);
6910   lives_widget_set_sensitive(mt->load_event_list, *mainw->set_name != 0);
6911 
6912   mt->clear_event_list = lives_standard_image_menu_item_new_with_label(_("_Wipe/Delete Layout..."));
6913   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->clear_event_list);
6914 
6915   lives_widget_add_accelerator(mt->clear_event_list, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
6916                                LIVES_KEY_d, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
6917 
6918   lives_widget_set_sensitive(mt->clear_event_list, mt->event_list != NULL);
6919 
6920   lives_menu_add_separator(LIVES_MENU(mt->files_menu));
6921 
6922   mt->clear_ds = lives_standard_menu_item_new_with_label(_("Clean _up Diskspace"));
6923   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->clear_ds);
6924 
6925   lives_signal_connect(LIVES_GUI_OBJECT(mt->clear_ds), LIVES_WIDGET_ACTIVATE_SIGNAL,
6926                        LIVES_GUI_CALLBACK(on_cleardisk_activate), NULL);
6927 
6928   lives_menu_add_separator(LIVES_MENU(mt->files_menu));
6929 
6930   mt->load_vals = lives_standard_check_menu_item_new_with_label(_("_Ignore Width, Height and Audio Values from Loaded Layouts"),
6931                   mt->ignore_load_vals);
6932   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->load_vals);
6933 
6934   mt->aload_subs = lives_standard_check_menu_item_new_with_label(_("Auto Load _Subtitles with Clips"), prefs->autoload_subs);
6935   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->aload_subs);
6936 
6937   lives_menu_add_separator(LIVES_MENU(mt->files_menu));
6938 
6939   mt->quit = lives_standard_image_menu_item_new_from_stock(LIVES_STOCK_LABEL_QUIT, mt->accel_group);
6940   lives_container_add(LIVES_CONTAINER(mt->files_menu), mt->quit);
6941 
6942   lives_widget_add_accelerator(mt->quit, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
6943                                LIVES_KEY_q, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
6944 
6945   // Edit
6946 
6947   menuitem = lives_standard_menu_item_new_with_label(_("_Edit"));
6948   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
6949 
6950   mt->edit_menu = lives_menu_new();
6951   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->edit_menu);
6952 
6953   mt->undo = lives_standard_image_menu_item_new_with_label(_("_Undo"));
6954   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->undo);
6955   lives_widget_set_sensitive(mt->undo, FALSE);
6956 
6957   lives_widget_add_accelerator(mt->undo, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
6958                                LIVES_KEY_u, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
6959 
6960 #if LIVES_HAS_IMAGE_MENU_ITEM
6961   image = lives_image_new_from_stock(LIVES_STOCK_UNDO, LIVES_ICON_SIZE_MENU);
6962   lives_image_menu_item_set_image(LIVES_IMAGE_MENU_ITEM(mt->undo), image);
6963 #endif
6964 
6965   if (mt->undo_offset == lives_list_length(mt->undos)) mt_set_undoable(mt, MT_UNDO_NONE, NULL, FALSE);
6966   else {
6967     mt_undo *undo = (mt_undo *)(lives_list_nth_data(mt->undos, lives_list_length(mt->undos) - mt->undo_offset - 1));
6968     mt_set_undoable(mt, undo->action, undo->extra, TRUE);
6969   }
6970 
6971   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->undo), LIVES_WIDGET_ACTIVATE_SIGNAL,
6972                             LIVES_GUI_CALLBACK(multitrack_undo), (livespointer)mt);
6973 
6974   mt->redo = lives_standard_image_menu_item_new_with_label(_("_Redo"));
6975   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->redo);
6976   lives_widget_set_sensitive(mt->redo, FALSE);
6977 
6978   lives_widget_add_accelerator(mt->redo, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
6979                                LIVES_KEY_z, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
6980 
6981 #if LIVES_HAS_IMAGE_MENU_ITEM
6982   image = lives_image_new_from_stock(LIVES_STOCK_REDO, LIVES_ICON_SIZE_MENU);
6983   lives_image_menu_item_set_image(LIVES_IMAGE_MENU_ITEM(mt->redo), image);
6984 #endif
6985 
6986   if (mt->undo_offset <= 1) mt_set_redoable(mt, MT_UNDO_NONE, NULL, FALSE);
6987   else {
6988     mt_undo *redo = (mt_undo *)(lives_list_nth_data(mt->undos, lives_list_length(mt->undos) - mt->undo_offset));
6989     mt_set_redoable(mt, redo->action, redo->extra, TRUE);
6990   }
6991 
6992   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->redo), LIVES_WIDGET_ACTIVATE_SIGNAL,
6993                             LIVES_GUI_CALLBACK(multitrack_redo), (livespointer)mt);
6994 
6995   lives_menu_add_separator(LIVES_MENU(mt->edit_menu));
6996 
6997   mt->clipedit = lives_standard_image_menu_item_new_with_label(_("_CLIP EDITOR"));
6998   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->clipedit);
6999 
7000   lives_widget_add_accelerator(mt->clipedit, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7001                                LIVES_KEY_e, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7002 
7003   lives_menu_add_separator(LIVES_MENU(mt->edit_menu));
7004 
7005   mt->adjust_start_end = lives_standard_image_menu_item_new_with_label(_("_Adjust Selected Clip Start/End Points"));
7006   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->adjust_start_end);
7007 
7008   lives_widget_add_accelerator(mt->adjust_start_end, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7009                                LIVES_KEY_x, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7010   lives_widget_set_sensitive(mt->adjust_start_end, FALSE);
7011 
7012   mt->insert = lives_standard_image_menu_item_new_with_label(_("_Insert selected clip"));
7013   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->insert);
7014 
7015   lives_widget_add_accelerator(mt->insert, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7016                                LIVES_KEY_i, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7017   lives_widget_add_accelerator(mt->insert, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7018                                LIVES_KEY_i, LIVES_CONTROL_MASK, (LiVESAccelFlags)0);
7019   lives_widget_set_sensitive(mt->insert, FALSE);
7020 
7021   mt->audio_insert = lives_standard_image_menu_item_new_with_label(_("_Insert Selected Clip Audio"));
7022   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->audio_insert);
7023 
7024   lives_widget_add_accelerator(mt->audio_insert, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7025                                LIVES_KEY_i, LIVES_CONTROL_MASK,
7026                                LIVES_ACCEL_VISIBLE);
7027   lives_widget_set_sensitive(mt->audio_insert, FALSE);
7028 
7029   mt->delblock = lives_standard_image_menu_item_new_with_label(_("_Delete Selected Block"));
7030   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->delblock);
7031   lives_widget_set_sensitive(mt->delblock, FALSE);
7032 
7033   // TODO
7034   /*
7035     lives_widget_add_accelerator (mt->delblock, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7036     LIVES_KEY_d, LIVES_CONTROL_MASK,
7037     LIVES_ACCEL_VISIBLE);
7038   */
7039 
7040   mt->jumpback = lives_standard_image_menu_item_new_with_label(_("_Jump to Previous Block Boundary"));
7041   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->jumpback);
7042 
7043   lives_widget_add_accelerator(mt->jumpback, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7044                                LIVES_KEY_j, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7045 
7046   lives_widget_set_sensitive(mt->jumpback, FALSE);
7047 
7048   mt->jumpnext = lives_standard_image_menu_item_new_with_label(_("_Jump to Next Block Boundary"));
7049   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->jumpnext);
7050 
7051   lives_widget_add_accelerator(mt->jumpnext, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7052                                LIVES_KEY_l, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7053 
7054   lives_widget_set_sensitive(mt->jumpnext, FALSE);
7055 
7056   lives_menu_add_separator(LIVES_MENU(mt->edit_menu));
7057 
7058   mt->mark_jumpback = lives_standard_image_menu_item_new_with_label(_("_Jump to Previous Timeline Mark"));
7059   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->mark_jumpback);
7060 
7061   lives_widget_set_sensitive(mt->mark_jumpback, FALSE);
7062 
7063   lives_widget_add_accelerator(mt->mark_jumpback, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7064                                LIVES_KEY_j, (LIVES_CONTROL_MASK | LIVES_SHIFT_MASK),
7065                                LIVES_ACCEL_VISIBLE);
7066 
7067   mt->mark_jumpnext = lives_standard_image_menu_item_new_with_label(_("_Jump to Next Timeline Mark"));
7068   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->mark_jumpnext);
7069 
7070   lives_widget_set_sensitive(mt->mark_jumpnext, FALSE);
7071 
7072   lives_widget_add_accelerator(mt->mark_jumpnext, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7073                                LIVES_KEY_l, (LIVES_CONTROL_MASK | LIVES_SHIFT_MASK),
7074                                LIVES_ACCEL_VISIBLE);
7075 
7076   mt->clear_marks = lives_standard_image_menu_item_new_with_label(_("Clear _Marks from Timeline"));
7077   lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->clear_marks);
7078   lives_widget_set_sensitive(mt->clear_marks, FALSE);
7079 
7080   lives_menu_add_separator(LIVES_MENU(mt->edit_menu));
7081 
7082   ign_ins_sel = lives_standard_check_menu_item_new_with_label(_("Ignore Selection Limits when Inserting"), mt->opts.ign_ins_sel);
7083   lives_container_add(LIVES_CONTAINER(mt->edit_menu), ign_ins_sel);
7084 
7085   // Play
7086 
7087   menuitem = lives_standard_menu_item_new_with_label(_("_Play"));
7088   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
7089 
7090   mt->play_menu = lives_menu_new();
7091   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->play_menu);
7092 
7093   mt->playall = lives_standard_image_menu_item_new_with_label(_("_Play from Timeline Position"));
7094   lives_widget_add_accelerator(mt->playall, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7095                                LIVES_KEY_p, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7096   lives_widget_set_sensitive(mt->playall, FALSE);
7097 
7098   lives_container_add(LIVES_CONTAINER(mt->play_menu), mt->playall);
7099 
7100 #if LIVES_HAS_IMAGE_MENU_ITEM
7101   image = lives_image_new_from_stock(LIVES_STOCK_REFRESH, LIVES_ICON_SIZE_MENU);
7102   lives_image_menu_item_set_image(LIVES_IMAGE_MENU_ITEM(mt->playall), image);
7103 #endif
7104 
7105   mt->playsel = lives_standard_image_menu_item_new_with_label(_("Pla_y Selected Time Only"));
7106   lives_widget_add_accelerator(mt->playsel, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7107                                LIVES_KEY_y, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7108   lives_container_add(LIVES_CONTAINER(mt->play_menu), mt->playsel);
7109   lives_widget_set_sensitive(mt->playsel, FALSE);
7110 
7111   mt->stop = lives_standard_image_menu_item_new_with_label(_("_Stop"));
7112   lives_container_add(LIVES_CONTAINER(mt->play_menu), mt->stop);
7113   lives_widget_set_sensitive(mt->stop, FALSE);
7114   lives_widget_add_accelerator(mt->stop, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7115                                LIVES_KEY_q, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7116 
7117 #if LIVES_HAS_IMAGE_MENU_ITEM
7118   image = lives_image_new_from_stock(LIVES_STOCK_MEDIA_STOP, LIVES_ICON_SIZE_MENU);
7119   lives_image_menu_item_set_image(LIVES_IMAGE_MENU_ITEM(mt->stop), image);
7120 #endif
7121 
7122   mt->rewind = lives_standard_image_menu_item_new_with_label(_("Re_wind"));
7123   lives_container_add(LIVES_CONTAINER(mt->play_menu), mt->rewind);
7124 
7125 #if LIVES_HAS_IMAGE_MENU_ITEM
7126   image = lives_image_new_from_stock(LIVES_STOCK_MEDIA_REWIND, LIVES_ICON_SIZE_MENU);
7127   lives_image_menu_item_set_image(LIVES_IMAGE_MENU_ITEM(mt->rewind), image);
7128 #endif
7129 
7130   lives_widget_set_sensitive(mt->rewind, FALSE);
7131 
7132   lives_widget_add_accelerator(mt->rewind, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7133                                LIVES_KEY_w, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7134 
7135   lives_menu_add_separator(LIVES_MENU(mt->play_menu));
7136 
7137   full_screen = lives_standard_check_menu_item_new_with_label(_("_Full Screen"), mainw->fs);
7138   lives_container_add(LIVES_CONTAINER(mt->play_menu), full_screen);
7139 
7140   lives_widget_add_accelerator(full_screen, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7141                                LIVES_KEY_f, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7142 
7143   mt->sepwin = lives_standard_check_menu_item_new_with_label(_("Play in _Separate Window"), mainw->sep_win);
7144   lives_container_add(LIVES_CONTAINER(mt->play_menu), mt->sepwin);
7145 
7146   lives_widget_add_accelerator(mt->sepwin, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7147                                LIVES_KEY_s, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7148 
7149   mt->loop_continue = lives_standard_check_menu_item_new_with_label(_("L_oop Continuously"), mainw->loop_cont);
7150   lives_container_add(LIVES_CONTAINER(mt->play_menu), mt->loop_continue);
7151 
7152   lives_widget_add_accelerator(mt->loop_continue, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7153                                LIVES_KEY_o, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7154 
7155   mt->mute_audio = lives_standard_check_menu_item_new_with_label(_("_Mute"), mainw->mute);
7156   lives_container_add(LIVES_CONTAINER(mt->play_menu), mt->mute_audio);
7157 
7158   lives_widget_add_accelerator(mt->mute_audio, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7159                                LIVES_KEY_z, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7160 
7161   // Effects
7162 
7163   menuitem = lives_standard_menu_item_new_with_label(_("Effect_s"));
7164   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
7165 
7166   mt->effects_menu = lives_menu_new();
7167   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->effects_menu);
7168 
7169   mt->move_fx = lives_standard_check_menu_item_new_with_label(_("_Move Effects with Blocks"), mt->opts.move_effects);
7170   lives_container_add(LIVES_CONTAINER(mt->effects_menu), mt->move_fx);
7171 
7172   lives_signal_connect_after(LIVES_GUI_OBJECT(mt->move_fx), LIVES_WIDGET_TOGGLED_SIGNAL,
7173                              LIVES_GUI_CALLBACK(on_move_fx_changed),
7174                              (livespointer)mt);
7175 
7176   lives_menu_add_separator(LIVES_MENU(mt->effects_menu));
7177 
7178   mt->atrans_menuitem = lives_standard_menu_item_new_with_label(_("Select _Autotransition Effect..."));
7179   lives_container_add(LIVES_CONTAINER(mt->effects_menu), mt->atrans_menuitem);
7180 
7181   mt->submenu_atransfx = lives_menu_new();
7182   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->atrans_menuitem), mt->submenu_atransfx);
7183 
7184   mt->ac_audio_check = lives_standard_check_menu_item_new_with_label(_("Crossfade Audio with Autotransition"),
7185                        mt->opts.autocross_audio);
7186   lives_container_add(LIVES_CONTAINER(mt->effects_menu), mt->ac_audio_check);
7187 
7188   lives_menu_add_separator(LIVES_MENU(mt->effects_menu));
7189 
7190   mt->fx_edit = lives_standard_menu_item_new_with_label(_("View/_Edit Selected Effect"));
7191   lives_container_add(LIVES_CONTAINER(mt->effects_menu), mt->fx_edit);
7192   lives_widget_set_sensitive(mt->fx_edit, FALSE);
7193 
7194   mt->fx_delete = lives_standard_menu_item_new_with_label(_("_Delete Selected Effect"));
7195   lives_container_add(LIVES_CONTAINER(mt->effects_menu), mt->fx_delete);
7196   lives_widget_set_sensitive(mt->fx_delete, FALSE);
7197 
7198   lives_menu_add_separator(LIVES_MENU(mt->effects_menu));
7199 
7200   //// block effects (video / audio) /////
7201 
7202   mt->fx_block = lives_standard_menu_item_new_with_label(_("Apply Effect to _Block..."));
7203   lives_container_add(LIVES_CONTAINER(mt->effects_menu), mt->fx_block);
7204 
7205   submenu_menu = lives_menu_new();
7206   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_block), submenu_menu);
7207 
7208   tname = lives_fx_cat_to_text(LIVES_FX_CAT_VIDEO_EFFECT, TRUE); // video effects
7209   cname = lives_strdup_printf("_%s...", tname);
7210   lives_free(tname);
7211 
7212   mt->fx_blockv = lives_standard_menu_item_new_with_label(cname);
7213   mt->fx_region_v = lives_standard_menu_item_new_with_label(cname);
7214 
7215   lives_free(cname);
7216 
7217   lives_container_add(LIVES_CONTAINER(submenu_menu), mt->fx_blockv);
7218 
7219   submenu_menuv = lives_menu_new();
7220   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_blockv), submenu_menuv);
7221 
7222   tname = lives_fx_cat_to_text(LIVES_FX_CAT_AUDIO_EFFECT, TRUE); // audio effects
7223   cname = lives_strdup_printf("_%s...", tname);
7224   lives_free(tname);
7225 
7226   mt->fx_blocka = lives_standard_menu_item_new_with_label(cname);
7227   mt->fx_region_a = lives_standard_menu_item_new_with_label(cname);
7228 
7229   lives_free(cname);
7230 
7231   lives_container_add(LIVES_CONTAINER(submenu_menu), mt->fx_blocka);
7232 
7233   submenu_menua = lives_menu_new();
7234   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_blocka), submenu_menua);
7235 
7236   lives_widget_set_sensitive(mt->fx_blockv, FALSE);
7237   lives_widget_set_sensitive(mt->fx_blocka, FALSE);
7238   lives_widget_set_sensitive(mt->fx_block, FALSE);
7239 
7240   /////// region effects (video, audio, va trans, v transitions, a transitions, c)
7241 
7242   mt->fx_region = lives_standard_menu_item_new_with_label(_("Apply Effect to _Region..."));
7243   lives_container_add(LIVES_CONTAINER(mt->effects_menu), mt->fx_region);
7244 
7245   submenu_menu = lives_menu_new();
7246   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_region), submenu_menu);
7247   lives_container_add(LIVES_CONTAINER(submenu_menu), mt->fx_region_v);
7248   lives_container_add(LIVES_CONTAINER(submenu_menu), mt->fx_region_a);
7249 
7250   submenu_menurv = lives_menu_new();
7251   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_region_v), submenu_menurv);
7252 
7253   submenu_menura = lives_menu_new();
7254   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_region_a), submenu_menura);
7255 
7256   tname = lives_fx_cat_to_text(LIVES_FX_CAT_AV_TRANSITION, TRUE); //audio/video transitions
7257   cname = lives_strdup_printf("_%s...", tname);
7258   lives_free(tname);
7259 
7260   mt->fx_region_2av = lives_standard_menu_item_new_with_label(cname);
7261   lives_free(cname);
7262   lives_container_add(LIVES_CONTAINER(submenu_menu), mt->fx_region_2av);
7263 
7264   submenu_menu2av = lives_menu_new();
7265   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_region_2av), submenu_menu2av);
7266 
7267   tname = lives_fx_cat_to_text(LIVES_FX_CAT_VIDEO_TRANSITION, TRUE); //video only transitions
7268   cname = lives_strdup_printf("_%s...", tname);
7269   lives_free(tname);
7270 
7271   mt->fx_region_2v = lives_standard_menu_item_new_with_label(cname);
7272   lives_free(cname);
7273   lives_container_add(LIVES_CONTAINER(submenu_menu), mt->fx_region_2v);
7274 
7275   submenu_menu2v = lives_menu_new();
7276   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_region_2v), submenu_menu2v);
7277 
7278   tname = lives_fx_cat_to_text(LIVES_FX_CAT_AUDIO_TRANSITION, TRUE); //audio only transitions
7279   cname = lives_strdup_printf("_%s...", tname);
7280   lives_free(tname);
7281 
7282   mt->fx_region_2a = lives_standard_menu_item_new_with_label(cname);
7283   lives_free(cname);
7284   lives_container_add(LIVES_CONTAINER(submenu_menu), mt->fx_region_2a);
7285 
7286   submenu_menu2a = lives_menu_new();
7287   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_region_2a), submenu_menu2a);
7288 
7289   tname = lives_fx_cat_to_text(LIVES_FX_CAT_COMPOSITOR, TRUE); // compositors
7290   cname = lives_strdup_printf("_%s...", tname);
7291   lives_free(tname);
7292 
7293   mt->fx_region_3 = lives_standard_menu_item_new_with_label(cname);
7294   lives_free(cname);
7295   lives_container_add(LIVES_CONTAINER(submenu_menu), mt->fx_region_3);
7296 
7297   submenu_menu3 = lives_menu_new();
7298   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->fx_region_3), submenu_menu3);
7299 
7300   num_filters = rte_get_numfilters();
7301   widget_opts.mnemonic_label = FALSE;
7302   for (i = 0; i < num_filters; i++) {
7303     int sorted = weed_get_sorted_filter(i);
7304     weed_plant_t *filter = get_weed_filter(sorted);
7305     if (filter && !weed_plant_has_leaf(filter, WEED_LEAF_HOST_MENU_HIDE)) {
7306       LiVESWidget *menuitem;
7307       char *fxname = NULL;
7308 
7309       if (weed_filter_hints_unstable(filter) && !prefs->unstable_fx) continue;
7310       pkg = weed_filter_get_package_name(filter);
7311 
7312       if (enabled_in_channels(filter, TRUE) >= 1000000 && enabled_out_channels(filter, FALSE) == 1) {
7313         fxname = weed_filter_idx_get_name(sorted, TRUE, TRUE);
7314         menuitem = lives_standard_image_menu_item_new_with_label(fxname);
7315         lives_container_add(LIVES_CONTAINER(submenu_menu3), menuitem);
7316         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), "idx", LIVES_INT_TO_POINTER(sorted));
7317         lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7318                              LIVES_GUI_CALLBACK(mt_add_region_effect),
7319                              (livespointer)mt);
7320       } else if (enabled_in_channels(filter, FALSE) == 1 && enabled_out_channels(filter, FALSE) == 1) {
7321         fxname = weed_filter_idx_get_name(sorted, FALSE, TRUE);
7322         // add all filter effects to submenus
7323         if (!is_pure_audio(filter, FALSE)) {
7324           if (pkg) {
7325             if (!xpkgv || strcmp(pkg, xpkgv)) {
7326               // create new submenu for pkg
7327               tname = lives_fx_cat_to_text(LIVES_FX_CAT_VIDEO_EFFECT, TRUE); // video effects
7328               cname = lives_strdup_printf("%s from package %s...", tname, pkg);
7329               lives_free(tname);
7330 
7331               menuitem2 = lives_standard_menu_item_new_with_label(cname);
7332 
7333               lives_container_add(LIVES_CONTAINER(submenu_menuv), menuitem2);
7334 
7335               submenu_menuvp = lives_menu_new();
7336               lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem2), submenu_menuvp);
7337 
7338               menuitem2 = lives_standard_menu_item_new_with_label(cname);
7339               lives_free(cname);
7340 
7341               lives_container_add(LIVES_CONTAINER(submenu_menurv), menuitem2);
7342 
7343               submenu_menurvp = lives_menu_new();
7344               lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem2), submenu_menurvp);
7345             }
7346             submenu = submenu_menuvp;
7347             submenu2 = submenu_menurvp;
7348             lives_freep((void **)&xpkgv);
7349             xpkgv = lives_strdup(pkg);
7350           } else {
7351             submenu = submenu_menuv;
7352             submenu2 = submenu_menurv;
7353           }
7354 
7355           menuitem = lives_standard_image_menu_item_new_with_label(fxname);
7356           lives_container_add(LIVES_CONTAINER(submenu), menuitem);
7357           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), "idx", LIVES_INT_TO_POINTER(sorted));
7358           lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7359                                LIVES_GUI_CALLBACK(mt_add_block_effect),
7360                                (livespointer)mt);
7361 
7362           menuitem = lives_standard_image_menu_item_new_with_label(fxname);
7363           lives_container_add(LIVES_CONTAINER(submenu2), menuitem);
7364           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), "idx", LIVES_INT_TO_POINTER(sorted));
7365           lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7366                                LIVES_GUI_CALLBACK(mt_add_region_effect),
7367                                (livespointer)mt);
7368         } else {
7369           if (pkg) {
7370             if (!xpkga || strcmp(pkg, xpkga)) {
7371               // create new submenu for pkg
7372               tname = lives_fx_cat_to_text(LIVES_FX_CAT_AUDIO_EFFECT, TRUE); // audio effects
7373               cname = lives_strdup_printf("%s from package %s...", tname, pkg);
7374               lives_free(tname);
7375 
7376               menuitem2 = lives_standard_menu_item_new_with_label(cname);
7377 
7378               lives_container_add(LIVES_CONTAINER(submenu_menua), menuitem2);
7379 
7380               submenu_menuap = lives_menu_new();
7381               lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem2), submenu_menuap);
7382 
7383               menuitem2 = lives_standard_menu_item_new_with_label(cname);
7384               lives_free(cname);
7385 
7386               lives_container_add(LIVES_CONTAINER(submenu_menura), menuitem2);
7387 
7388               submenu_menurap = lives_menu_new();
7389               lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem2), submenu_menurap);
7390             }
7391             submenu = submenu_menuap;
7392             submenu2 = submenu_menurap;
7393             lives_freep((void **)&xpkga);
7394             xpkga = lives_strdup(pkg);
7395           } else {
7396             submenu = submenu_menua;
7397             submenu2 = submenu_menura;
7398           }
7399 
7400           menuitem = lives_standard_image_menu_item_new_with_label(fxname);
7401           lives_container_add(LIVES_CONTAINER(submenu), menuitem);
7402           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), "idx", LIVES_INT_TO_POINTER(sorted));
7403           lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7404                                LIVES_GUI_CALLBACK(mt_add_block_effect), (livespointer)mt);
7405 
7406           menuitem = lives_standard_image_menu_item_new_with_label(fxname);
7407           lives_container_add(LIVES_CONTAINER(submenu2), menuitem);
7408           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), "idx", LIVES_INT_TO_POINTER(sorted));
7409           lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7410                                LIVES_GUI_CALLBACK(mt_add_region_effect), (livespointer)mt);
7411         }
7412       } else if (enabled_in_channels(filter, FALSE) == 2 && enabled_out_channels(filter, FALSE) == 1) {
7413         fxname = weed_filter_idx_get_name(sorted, FALSE, TRUE);
7414         // add all transitions to submenus
7415         menuitem = lives_standard_image_menu_item_new_with_label(fxname);
7416         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), "idx", LIVES_INT_TO_POINTER(sorted));
7417         if (get_transition_param(filter, FALSE) == -1) lives_container_add(LIVES_CONTAINER(submenu_menu2v), menuitem);
7418         else {
7419           if (has_video_chans_in(filter, FALSE)) {
7420             /// the autotransitions menu
7421             menuitem2 = lives_standard_check_menu_item_new_with_label(fxname, prefs->atrans_fx == sorted);
7422             lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem2), "idx", LIVES_INT_TO_POINTER(sorted));
7423 
7424             lives_signal_sync_connect(LIVES_GUI_OBJECT(menuitem2), LIVES_WIDGET_ACTIVATE_SIGNAL,
7425                                       LIVES_GUI_CALLBACK(mt_set_atrans_effect), (livespointer)mt);
7426             if (sorted == mainw->def_trans_idx) {
7427               lives_menu_shell_prepend(LIVES_MENU_SHELL(mt->submenu_atransfx), menuitem2);
7428             } else lives_menu_shell_append(LIVES_MENU_SHELL(mt->submenu_atransfx), menuitem2);
7429             /// apply block effect menu
7430             lives_container_add(LIVES_CONTAINER(submenu_menu2av), menuitem);
7431           } else lives_container_add(LIVES_CONTAINER(submenu_menu2a), menuitem);
7432         }
7433         lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7434                              LIVES_GUI_CALLBACK(mt_add_region_effect), (livespointer)mt);
7435       }
7436 
7437       if (pkg) lives_free(pkg);
7438       if (fxname) lives_free(fxname);
7439     }
7440   }
7441 
7442   lives_freep((void **)&xpkgv);
7443   lives_freep((void **)&xpkga);
7444   widget_opts.mnemonic_label = TRUE;
7445 
7446   /// None autotransition
7447   menuitem2 = lives_standard_check_menu_item_new_with_label(mainw->string_constants[LIVES_STRING_CONSTANT_NONE],
7448               prefs->atrans_fx == -1);
7449   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem2), "idx", LIVES_INT_TO_POINTER(-1));
7450   lives_menu_shell_prepend(LIVES_MENU_SHELL(mt->submenu_atransfx), menuitem2);
7451 
7452   lives_signal_sync_connect(LIVES_GUI_OBJECT(menuitem2), LIVES_WIDGET_ACTIVATE_SIGNAL,
7453                             LIVES_GUI_CALLBACK(mt_set_atrans_effect),
7454                             (livespointer)mt);
7455 
7456   lives_widget_set_sensitive(mt->fx_block, FALSE);
7457   lives_widget_set_sensitive(mt->fx_region, FALSE);
7458 
7459   // Tracks
7460   menuitem = lives_standard_menu_item_new_with_label(_("_Tracks"));
7461   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
7462 
7463   mt->tracks_menu = lives_menu_new();
7464   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->tracks_menu);
7465 
7466   mt->rename_track = lives_standard_image_menu_item_new_with_label(_("Rename Current Track"));
7467   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->rename_track);
7468 
7469   lives_menu_add_separator(LIVES_MENU(mt->tracks_menu));
7470 
7471   mt->cback_audio = lives_standard_image_menu_item_new_with_label(_("Make _Backing Audio Current Track"));
7472   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->cback_audio);
7473 
7474   lives_widget_add_accelerator(mt->cback_audio, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7475                                LIVES_KEY_b, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7476 
7477   lives_menu_add_separator(LIVES_MENU(mt->tracks_menu));
7478 
7479   mt->add_vid_behind = lives_standard_image_menu_item_new_with_label(_("Add Video Track at _Rear"));
7480   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->add_vid_behind);
7481 
7482   lives_widget_add_accelerator(mt->add_vid_behind, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7483                                LIVES_KEY_t, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7484 
7485   mt->add_vid_front = lives_standard_image_menu_item_new_with_label(_("Add Video Track at _Front"));
7486   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->add_vid_front);
7487 
7488   lives_widget_add_accelerator(mt->add_vid_front, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7489                                LIVES_KEY_t, (LiVESXModifierType)(LIVES_CONTROL_MASK | LIVES_SHIFT_MASK),
7490                                LIVES_ACCEL_VISIBLE);
7491 
7492   lives_menu_add_separator(LIVES_MENU(mt->tracks_menu));
7493 
7494   menuitem = lives_standard_menu_item_new_with_label(_("_Split Current Track at Cursor"));
7495   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), menuitem);
7496 
7497   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7498                        LIVES_GUI_CALLBACK(on_split_curr_activate), (livespointer)mt);
7499 
7500   lives_widget_add_accelerator(menuitem, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7501                                LIVES_KEY_s, (LiVESXModifierType)LIVES_CONTROL_MASK,
7502                                LIVES_ACCEL_VISIBLE);
7503 
7504   mt->split_sel = lives_standard_menu_item_new_with_label(_("_Split Selected Video Tracks"));
7505   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->split_sel);
7506   lives_widget_set_sensitive(mt->split_sel, FALSE);
7507 
7508   lives_signal_connect(LIVES_GUI_OBJECT(mt->split_sel), LIVES_WIDGET_ACTIVATE_SIGNAL,
7509                        LIVES_GUI_CALLBACK(on_split_sel_activate), (livespointer)mt);
7510 
7511   lives_menu_add_separator(LIVES_MENU(mt->tracks_menu));
7512 
7513   mt->ins_gap_sel = lives_standard_image_menu_item_new_with_label(_("Insert Gap in Selected Tracks/Time"));
7514   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->ins_gap_sel);
7515   lives_widget_set_sensitive(mt->ins_gap_sel, FALSE);
7516 
7517   lives_signal_connect(LIVES_GUI_OBJECT(mt->ins_gap_sel), LIVES_WIDGET_ACTIVATE_SIGNAL,
7518                        LIVES_GUI_CALLBACK(on_insgap_sel_activate), (livespointer)mt);
7519 
7520   mt->ins_gap_cur = lives_standard_image_menu_item_new_with_label(_("Insert Gap in Current Track/Selected Time"));
7521   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->ins_gap_cur);
7522   lives_widget_set_sensitive(mt->ins_gap_cur, FALSE);
7523 
7524   lives_signal_connect(LIVES_GUI_OBJECT(mt->ins_gap_cur), LIVES_WIDGET_ACTIVATE_SIGNAL,
7525                        LIVES_GUI_CALLBACK(on_insgap_cur_activate), (livespointer)mt);
7526 
7527   lives_menu_add_separator(LIVES_MENU(mt->tracks_menu));
7528 
7529   mt->remove_gaps = lives_standard_menu_item_new_with_label(_("Close All _Gaps in Selected Tracks/Time"));
7530   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->remove_gaps);
7531 
7532   lives_signal_connect(LIVES_GUI_OBJECT(mt->remove_gaps), LIVES_WIDGET_ACTIVATE_SIGNAL,
7533                        LIVES_GUI_CALLBACK(remove_gaps), (livespointer)mt);
7534 
7535   lives_widget_add_accelerator(mt->remove_gaps, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7536                                LIVES_KEY_g, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7537 
7538   mt->remove_first_gaps = lives_standard_menu_item_new_with_label("");
7539   lives_container_add(LIVES_CONTAINER(mt->tracks_menu), mt->remove_first_gaps);
7540 
7541   lives_signal_connect(LIVES_GUI_OBJECT(mt->remove_first_gaps), LIVES_WIDGET_ACTIVATE_SIGNAL,
7542                        LIVES_GUI_CALLBACK(remove_first_gaps), (livespointer)mt);
7543 
7544   lives_widget_add_accelerator(mt->remove_first_gaps, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7545                                LIVES_KEY_f, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7546 
7547   // Selection
7548 
7549   menuitem = lives_standard_menu_item_new_with_label(_("Se_lection"));
7550   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
7551 
7552   mt->selection_menu = lives_menu_new();
7553   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->selection_menu);
7554 
7555   menuitem = lives_standard_check_menu_item_new_with_label(_("_Lock Time Selection"), mt->sel_locked);
7556   lives_container_add(LIVES_CONTAINER(mt->selection_menu), menuitem);
7557 
7558   lives_signal_sync_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7559                             LIVES_GUI_CALLBACK(mt_selection_lock), (livespointer)mt);
7560 
7561   mt->select_track = lives_standard_check_menu_item_new_with_label(_("_Select Current Track"), FALSE);
7562   lives_container_add(LIVES_CONTAINER(mt->selection_menu), mt->select_track);
7563 
7564   lives_widget_add_accelerator(mt->select_track, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7565                                LIVES_KEY_Space, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7566 
7567   menuitem = lives_standard_menu_item_new_with_label(_("Select _All Video Tracks"));
7568   lives_container_add(LIVES_CONTAINER(mt->selection_menu), menuitem);
7569 
7570   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7571                        LIVES_GUI_CALLBACK(select_all_vid), (livespointer)mt);
7572 
7573   menuitem = lives_standard_menu_item_new_with_label(_("Select _No Video Tracks"));
7574   lives_container_add(LIVES_CONTAINER(mt->selection_menu), menuitem);
7575 
7576   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7577                        LIVES_GUI_CALLBACK(select_no_vid), (livespointer)mt);
7578 
7579   menuitem = lives_standard_menu_item_new_with_label(_("Select All _Time"));
7580   lives_container_add(LIVES_CONTAINER(mt->selection_menu), menuitem);
7581 
7582   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7583                        LIVES_GUI_CALLBACK(select_all_time), (livespointer)mt);
7584 
7585   lives_widget_add_accelerator(menuitem, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7586                                LIVES_KEY_a, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7587 
7588   menuitem = lives_standard_menu_item_new_with_label(_("Select from _Zero Time"));
7589   lives_container_add(LIVES_CONTAINER(mt->selection_menu), menuitem);
7590 
7591   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7592                        LIVES_GUI_CALLBACK(select_from_zero_time), (livespointer)mt);
7593 
7594   menuitem = lives_standard_menu_item_new_with_label(_("Select to _End Time"));
7595   lives_container_add(LIVES_CONTAINER(mt->selection_menu), menuitem);
7596 
7597   lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7598                        LIVES_GUI_CALLBACK(select_to_end_time), (livespointer)mt);
7599 
7600   menuitem = lives_standard_menu_item_new_with_label(_("_Copy..."));
7601   lives_container_add(LIVES_CONTAINER(mt->selection_menu), menuitem);
7602 
7603   selcopy_menu = lives_menu_new();
7604   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), selcopy_menu);
7605 
7606   mt->tc_to_rs = lives_standard_menu_item_new_with_label(_("_Timecode to Region Start"));
7607   lives_container_add(LIVES_CONTAINER(selcopy_menu), mt->tc_to_rs);
7608 
7609   lives_signal_connect(LIVES_GUI_OBJECT(mt->tc_to_rs), LIVES_WIDGET_ACTIVATE_SIGNAL,
7610                        LIVES_GUI_CALLBACK(tc_to_rs), (livespointer)mt);
7611 
7612   mt->tc_to_re = lives_standard_menu_item_new_with_label(_("_Timecode to Region End"));
7613   lives_container_add(LIVES_CONTAINER(selcopy_menu), mt->tc_to_re);
7614 
7615   lives_signal_connect(LIVES_GUI_OBJECT(mt->tc_to_re), LIVES_WIDGET_ACTIVATE_SIGNAL,
7616                        LIVES_GUI_CALLBACK(tc_to_re), (livespointer)mt);
7617 
7618   mt->rs_to_tc = lives_standard_menu_item_new_with_label(_("_Region Start to Timecode"));
7619   lives_container_add(LIVES_CONTAINER(selcopy_menu), mt->rs_to_tc);
7620 
7621   lives_signal_connect(LIVES_GUI_OBJECT(mt->rs_to_tc), LIVES_WIDGET_ACTIVATE_SIGNAL,
7622                        LIVES_GUI_CALLBACK(rs_to_tc), (livespointer)mt);
7623 
7624   mt->re_to_tc = lives_standard_menu_item_new_with_label(_("_Region End to Timecode"));
7625   lives_container_add(LIVES_CONTAINER(selcopy_menu), mt->re_to_tc);
7626 
7627   lives_signal_connect(LIVES_GUI_OBJECT(mt->re_to_tc), LIVES_WIDGET_ACTIVATE_SIGNAL,
7628                        LIVES_GUI_CALLBACK(re_to_tc), (livespointer)mt);
7629 
7630   lives_widget_set_sensitive(mt->rs_to_tc, FALSE);
7631   lives_widget_set_sensitive(mt->re_to_tc, FALSE);
7632 
7633   lives_menu_add_separator(LIVES_MENU(mt->selection_menu));
7634 
7635   mt->seldesel_menuitem = lives_standard_menu_item_new_with_label(_("Select/Deselect Block at Current Track/Time"));
7636   lives_container_add(LIVES_CONTAINER(mt->selection_menu), mt->seldesel_menuitem);
7637 
7638   lives_signal_connect(LIVES_GUI_OBJECT(mt->seldesel_menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7639                        LIVES_GUI_CALLBACK(mt_selblock), (livespointer)mt);
7640 
7641   lives_widget_add_accelerator(mt->seldesel_menuitem, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7642                                LIVES_KEY_Return, LIVES_CONTROL_MASK,
7643                                LIVES_ACCEL_VISIBLE);
7644 
7645   // Tools
7646 
7647   menuitem = lives_standard_menu_item_new_with_label(_("_Tools"));
7648   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
7649 
7650   mt->tools_menu = lives_menu_new();
7651   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->tools_menu);
7652 
7653   mt->change_vals = lives_standard_image_menu_item_new_with_label(_("_Change Width, Height and Audio Values..."));
7654   lives_container_add(LIVES_CONTAINER(mt->tools_menu), mt->change_vals);
7655 
7656   lives_signal_connect(LIVES_GUI_OBJECT(mt->change_vals), LIVES_WIDGET_ACTIVATE_SIGNAL,
7657                        LIVES_GUI_CALLBACK(mt_change_vals_activate), (livespointer)mt);
7658 
7659   lives_menu_add_separator(LIVES_MENU(mt->tools_menu));
7660 
7661   mt->gens_submenu = lives_standard_menu_item_new_with_label(_("_Generate"));
7662   lives_container_add(LIVES_CONTAINER(mt->tools_menu), mt->gens_submenu);
7663 
7664   if (mainw->gens_menu) {
7665     lives_widget_object_ref(mainw->gens_menu);
7666     lives_menu_detach(LIVES_MENU(mainw->gens_menu));
7667     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->gens_submenu), mainw->gens_menu);
7668   }
7669 
7670   mt->capture = lives_standard_menu_item_new_with_label(_("Capture _External Window... "));
7671   lives_container_add(LIVES_CONTAINER(mt->tools_menu), mt->capture);
7672 
7673   lives_signal_connect(LIVES_GUI_OBJECT(mt->capture), LIVES_WIDGET_ACTIVATE_SIGNAL,
7674                        LIVES_GUI_CALLBACK(on_capture_activate), NULL);
7675 
7676   lives_menu_add_separator(LIVES_MENU(mt->tools_menu));
7677 
7678   mt->backup = lives_standard_image_menu_item_new_with_label(_("_Backup timeline now"));
7679   lives_container_add(LIVES_CONTAINER(mt->tools_menu), mt->backup);
7680   lives_widget_set_sensitive(mt->backup, FALSE);
7681 
7682   lives_signal_connect(LIVES_GUI_OBJECT(mt->backup), LIVES_WIDGET_ACTIVATE_SIGNAL,
7683                        LIVES_GUI_CALLBACK(on_mt_backup_activate), (livespointer)mt);
7684 
7685   lives_menu_add_separator(LIVES_MENU(mt->tools_menu));
7686 
7687   menuitem = lives_standard_image_menu_item_new_with_label(_("_Preferences..."));
7688   lives_container_add(LIVES_CONTAINER(mt->tools_menu), menuitem);
7689   lives_widget_add_accelerator(menuitem, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7690                                LIVES_KEY_p, LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7691 
7692 #if LIVES_HAS_IMAGE_MENU_ITEM
7693   image = lives_image_new_from_stock(LIVES_STOCK_PREFERENCES, LIVES_ICON_SIZE_MENU);
7694   lives_image_menu_item_set_image(LIVES_IMAGE_MENU_ITEM(menuitem), image);
7695 #endif
7696 
7697   lives_signal_sync_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
7698                             LIVES_GUI_CALLBACK(on_preferences_activate), NULL);
7699 
7700   // Render
7701 
7702   menuitem = lives_standard_menu_item_new_with_label(_("_Render"));
7703   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
7704 
7705   mt->render_menu = lives_menu_new();
7706   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->render_menu);
7707 
7708   mt->render = lives_standard_image_menu_item_new_with_label(_("_Render All to New Clip"));
7709   lives_container_add(LIVES_CONTAINER(mt->render_menu), mt->render);
7710   lives_widget_set_sensitive(mt->render, FALSE);
7711 
7712   lives_widget_add_accelerator(mt->render, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7713                                LIVES_KEY_r, LIVES_CONTROL_MASK,
7714                                LIVES_ACCEL_VISIBLE);
7715 
7716   // TODO - render selected time
7717 
7718   mt->render_sep = lives_standard_menu_item_new();
7719   lives_container_add(LIVES_CONTAINER(mt->render_menu), mt->render_sep);
7720   lives_widget_set_sensitive(mt->render_sep, FALSE);
7721 
7722   mt->render_vid = lives_standard_check_menu_item_new_with_label(_("Render _Video"), mt->opts.render_vidp);
7723   lives_widget_set_sensitive(mt->render_vid, mt->opts.render_audp);
7724 
7725   lives_container_add(LIVES_CONTAINER(mt->render_menu), mt->render_vid);
7726 
7727   mt->render_aud = lives_standard_check_menu_item_new_with_label(_("Render _Audio"), mt->opts.render_audp);
7728 
7729   lives_container_add(LIVES_CONTAINER(mt->render_menu), mt->render_aud);
7730 
7731   sep = lives_standard_menu_item_new();
7732 
7733   lives_container_add(LIVES_CONTAINER(mt->render_menu), sep);
7734   lives_widget_set_sensitive(sep, FALSE);
7735 
7736   mt->normalise_aud = lives_standard_check_menu_item_new_with_label(_("_Normalise Rendered Audio"), mt->opts.normalise_audp);
7737   lives_widget_set_sensitive(mt->normalise_aud, mt->opts.render_audp);
7738 
7739   lives_container_add(LIVES_CONTAINER(mt->render_menu), mt->normalise_aud);
7740 
7741   mt->prerender_aud = lives_standard_menu_item_new_with_label(_("_Pre-render Audio"));
7742   lives_widget_set_sensitive(mt->prerender_aud, FALSE);
7743 
7744   //lives_container_add (LIVES_CONTAINER (mt->render_menu), mt->prerender_aud);
7745 
7746   // View
7747 
7748   menuitem = lives_standard_menu_item_new_with_label(_("_View"));
7749   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
7750 
7751   mt->view_menu = lives_menu_new();
7752   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->view_menu);
7753 
7754   mt->show_info = lives_standard_check_menu_item_new_with_label(_("Show Info Box"), prefs->mt_show_ctx);
7755   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->show_info), LIVES_WIDGET_ACTIVATE_SIGNAL,
7756                             LIVES_GUI_CALLBACK(toggle_sets_pref), (livespointer)PREF_MT_SHOW_CTX);
7757 
7758   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->show_info);
7759   lives_menu_add_separator(LIVES_MENU(mt->view_menu));
7760 
7761   mt->view_clips = lives_standard_menu_item_new_with_label(_("_Clips"));
7762   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->view_clips);
7763 
7764   lives_widget_add_accelerator(mt->view_clips, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7765                                LIVES_KEY_c, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7766 
7767   mt->view_in_out = lives_standard_menu_item_new_with_label(_("Block _In/Out Points"));
7768   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->view_in_out);
7769 
7770   lives_widget_add_accelerator(mt->view_in_out, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7771                                LIVES_KEY_n, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7772 
7773   lives_widget_set_sensitive(mt->view_in_out, FALSE);
7774 
7775   mt->view_effects = lives_standard_menu_item_new_with_label(_("_Effects at Current"));
7776   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->view_effects);
7777 
7778   lives_widget_add_accelerator(mt->view_effects, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7779                                LIVES_KEY_e, (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
7780 
7781   show_messages = lives_standard_image_menu_item_new_with_label(_("Show _Messages"));
7782   lives_container_add(LIVES_CONTAINER(mt->view_menu), show_messages);
7783 
7784   mt->show_quota = lives_standard_image_menu_item_new_with_label(_("Show / Edit Disk _Quota Settings"));
7785   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->show_quota);
7786 
7787   mt->aparam_separator = lives_standard_menu_item_new();
7788   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->aparam_separator);
7789   lives_widget_set_sensitive(mt->aparam_separator, FALSE);
7790 
7791   mt->aparam_menuitem = lives_standard_menu_item_new_with_label(_("Audio Parameters"));
7792   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->aparam_menuitem);
7793 
7794   mt->aparam_submenu = lives_menu_new();
7795   lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->aparam_menuitem), mt->aparam_submenu);
7796 
7797   mt->view_audio = lives_standard_check_menu_item_new_with_label(_("Show Backing _Audio Track"), mt->opts.show_audio);
7798   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->view_audio);
7799 
7800   mt->change_max_disp = lives_standard_menu_item_new_with_label(_("Maximum Tracks to Display..."));
7801   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->change_max_disp);
7802 
7803   lives_menu_add_separator(LIVES_MENU(mt->view_menu));
7804 
7805   mt->follow_play = lives_standard_check_menu_item_new_with_label(_("Scroll to Follow Playback"), mt->opts.follow_playback);
7806   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->follow_play);
7807 
7808   ccursor = lives_standard_menu_item_new_with_label(_("_Center on Cursor"));
7809   lives_container_add(LIVES_CONTAINER(mt->view_menu), ccursor);
7810 
7811   lives_widget_add_accelerator(ccursor, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7812                                LIVES_KEY_c, (LiVESXModifierType)LIVES_CONTROL_MASK, LIVES_ACCEL_VISIBLE);
7813 
7814   zoom_in = lives_standard_menu_item_new_with_label(_("_Zoom In"));
7815   lives_container_add(LIVES_CONTAINER(mt->view_menu), zoom_in);
7816 
7817   lives_widget_add_accelerator(zoom_in, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7818                                LIVES_KEY_Plus, (LiVESXModifierType)LIVES_CONTROL_MASK,
7819                                LIVES_ACCEL_VISIBLE);
7820 
7821   lives_widget_add_accelerator(zoom_in, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7822                                LIVES_KEY_Equal, (LiVESXModifierType)LIVES_CONTROL_MASK,
7823                                (LiVESAccelFlags)0);
7824 
7825   zoom_out = lives_standard_menu_item_new_with_label(_("_Zoom Out"));
7826   lives_container_add(LIVES_CONTAINER(mt->view_menu), zoom_out);
7827 
7828   lives_widget_add_accelerator(zoom_out, LIVES_WIDGET_ACTIVATE_SIGNAL, mt->accel_group,
7829                                LIVES_KEY_Minus, (LiVESXModifierType)LIVES_CONTROL_MASK,
7830                                LIVES_ACCEL_VISIBLE);
7831 
7832   lives_menu_add_separator(LIVES_MENU(mt->view_menu));
7833 
7834   view_mt_details = lives_standard_menu_item_new_with_label(_("Multitrack _Details"));
7835   lives_container_add(LIVES_CONTAINER(mt->view_menu), view_mt_details);
7836 
7837   mt->show_layout_errors = lives_standard_image_menu_item_new_with_label(_("Show _Layout Errors"));
7838   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->show_layout_errors);
7839   lives_widget_set_sensitive(mt->show_layout_errors, mainw->affected_layouts_map != NULL);
7840 
7841   lives_menu_add_separator(LIVES_MENU(mt->view_menu));
7842 
7843   mt->view_events = lives_standard_image_menu_item_new_with_label(_("_Event Window"));
7844   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->view_events);
7845   lives_widget_set_sensitive(mt->view_events, FALSE);
7846 
7847   mt->view_sel_events = lives_standard_image_menu_item_new_with_label(_("_Event Window (selected time only)"));
7848   lives_container_add(LIVES_CONTAINER(mt->view_menu), mt->view_sel_events);
7849   lives_widget_set_sensitive(mt->view_sel_events, FALSE);
7850 
7851   show_frame_events = lives_standard_check_menu_item_new_with_label(_("_Show FRAME Events in Event Window"),
7852                       prefs->event_window_show_frame_events);
7853   lives_container_add(LIVES_CONTAINER(mt->view_menu), show_frame_events);
7854 
7855   // help
7856   menuitem = lives_standard_menu_item_new_with_label(_("_Help"));
7857   lives_container_add(LIVES_CONTAINER(mt->menubar), menuitem);
7858 
7859   mt->help_menu = lives_menu_new();
7860   lives_menu_item_set_submenu(LIVES_MENU_ITEM(menuitem), mt->help_menu);
7861 
7862   show_mt_keys = lives_standard_menu_item_new_with_label(_("_Show Multitrack Keys"));
7863   lives_container_add(LIVES_CONTAINER(mt->help_menu), show_mt_keys);
7864 
7865   lives_menu_add_separator(LIVES_MENU(mt->help_menu));
7866 
7867   show_manual = lives_standard_menu_item_new_with_label(_("_Manual (opens in browser)"));
7868   lives_container_add(LIVES_CONTAINER(mt->help_menu), show_manual);
7869 
7870   lives_menu_add_separator(LIVES_MENU(mt->help_menu));
7871 
7872   donate = lives_standard_menu_item_new_with_label(_("_Donate to the Project !"));
7873   lives_container_add(LIVES_CONTAINER(mt->help_menu), donate);
7874 
7875   email_author = lives_standard_menu_item_new_with_label(_("_Email the Author"));
7876   lives_container_add(LIVES_CONTAINER(mt->help_menu), email_author);
7877 
7878   report_bug = lives_standard_menu_item_new_with_label(_("Report a _bug"));
7879   lives_container_add(LIVES_CONTAINER(mt->help_menu), report_bug);
7880 
7881   suggest_feature = lives_standard_menu_item_new_with_label(_("Suggest a _Feature"));
7882   lives_container_add(LIVES_CONTAINER(mt->help_menu), suggest_feature);
7883 
7884   help_translate = lives_standard_menu_item_new_with_label(_("Assist with _Translating"));
7885   lives_container_add(LIVES_CONTAINER(mt->help_menu), help_translate);
7886 
7887   lives_menu_add_separator(LIVES_MENU(mt->help_menu));
7888 
7889   mt->show_devopts = lives_standard_check_menu_item_new_with_label(_("Enable Developer Options"), prefs->show_dev_opts);
7890   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->show_devopts),  prefs->show_dev_opts);
7891 
7892   lives_container_add(LIVES_CONTAINER(mt->help_menu), mt->show_devopts);
7893   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->show_devopts), LIVES_WIDGET_ACTIVATE_SIGNAL,
7894                             LIVES_GUI_CALLBACK(toggle_sets_pref), (livespointer)PREF_SHOW_DEVOPTS);
7895 
7896   lives_menu_add_separator(LIVES_MENU(mt->help_menu));
7897 
7898   mt->troubleshoot = lives_standard_menu_item_new_with_label(_("_Troubleshoot"));
7899   lives_container_add(LIVES_CONTAINER(mt->help_menu), mt->troubleshoot);
7900 
7901   mt->expl_missing = lives_standard_menu_item_new_with_label(_("Check for Missing Features"));
7902   if (!prefs->vj_mode) lives_container_add(LIVES_CONTAINER(mainw->help_menu), mt->expl_missing);
7903 
7904   lives_menu_add_separator(LIVES_MENU(mt->help_menu));
7905 
7906   about = lives_standard_menu_item_new_with_label(_("_About"));
7907   lives_container_add(LIVES_CONTAINER(mt->help_menu), about);
7908 
7909   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->quit), LIVES_WIDGET_ACTIVATE_SIGNAL,
7910                             LIVES_GUI_CALLBACK(mt_quit_activate), (livespointer)mt);
7911   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->load_vals), LIVES_WIDGET_ACTIVATE_SIGNAL,
7912                             LIVES_GUI_CALLBACK(mt_load_vals_toggled), (livespointer)mt);
7913   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->ac_audio_check), LIVES_WIDGET_ACTIVATE_SIGNAL,
7914                             LIVES_GUI_CALLBACK(mt_ac_audio_toggled), (livespointer)mt);
7915   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->aload_subs), LIVES_WIDGET_ACTIVATE_SIGNAL,
7916                             LIVES_GUI_CALLBACK(on_boolean_toggled), &prefs->autoload_subs);
7917   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->clipedit), LIVES_WIDGET_ACTIVATE_SIGNAL,
7918                             LIVES_GUI_CALLBACK(multitrack_end_cb), (livespointer)mt);
7919   lives_signal_connect(LIVES_GUI_OBJECT(mt->playall), LIVES_WIDGET_ACTIVATE_SIGNAL,
7920                        LIVES_GUI_CALLBACK(on_playall_activate), NULL);
7921   lives_signal_connect(LIVES_GUI_OBJECT(mt->playsel), LIVES_WIDGET_ACTIVATE_SIGNAL,
7922                        LIVES_GUI_CALLBACK(multitrack_play_sel), (livespointer)mt);
7923   lives_signal_connect(LIVES_GUI_OBJECT(mt->insert), LIVES_WIDGET_ACTIVATE_SIGNAL,
7924                        LIVES_GUI_CALLBACK(multitrack_insert), (livespointer)mt);
7925   lives_signal_connect(LIVES_GUI_OBJECT(mt->audio_insert), LIVES_WIDGET_ACTIVATE_SIGNAL,
7926                        LIVES_GUI_CALLBACK(multitrack_audio_insert), (livespointer)mt);
7927   lives_signal_connect(LIVES_GUI_OBJECT(mt->adjust_start_end), LIVES_WIDGET_ACTIVATE_SIGNAL,
7928                        LIVES_GUI_CALLBACK(multitrack_adj_start_end), (livespointer)mt);
7929   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->view_events), LIVES_WIDGET_ACTIVATE_SIGNAL,
7930                             LIVES_GUI_CALLBACK(multitrack_view_events), (livespointer)mt);
7931   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->view_sel_events), LIVES_WIDGET_ACTIVATE_SIGNAL,
7932                             LIVES_GUI_CALLBACK(multitrack_view_sel_events), (livespointer)mt);
7933   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->clear_marks), LIVES_WIDGET_ACTIVATE_SIGNAL,
7934                             LIVES_GUI_CALLBACK(multitrack_clear_marks), (livespointer)mt);
7935   lives_signal_sync_connect(LIVES_GUI_OBJECT(view_mt_details), LIVES_WIDGET_ACTIVATE_SIGNAL,
7936                             LIVES_GUI_CALLBACK(multitrack_view_details), (livespointer)mt);
7937   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->show_layout_errors), LIVES_WIDGET_ACTIVATE_SIGNAL,
7938                             LIVES_GUI_CALLBACK(popup_lmap_errors), NULL);
7939   lives_signal_connect(LIVES_GUI_OBJECT(mt->view_clips), LIVES_WIDGET_ACTIVATE_SIGNAL,
7940                        LIVES_GUI_CALLBACK(multitrack_view_clips), (livespointer)mt);
7941   lives_signal_connect(LIVES_GUI_OBJECT(mt->view_in_out), LIVES_WIDGET_ACTIVATE_SIGNAL,
7942                        LIVES_GUI_CALLBACK(multitrack_view_in_out), (livespointer)mt);
7943   lives_signal_sync_connect(LIVES_GUI_OBJECT(show_messages), LIVES_WIDGET_ACTIVATE_SIGNAL,
7944                             LIVES_GUI_CALLBACK(on_show_messages_activate), NULL);
7945   lives_signal_connect(LIVES_GUI_OBJECT(mt->show_quota), LIVES_WIDGET_ACTIVATE_SIGNAL,
7946                        LIVES_GUI_CALLBACK(run_diskspace_dialog_cb), NULL);
7947   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->stop), LIVES_WIDGET_ACTIVATE_SIGNAL,
7948                             LIVES_GUI_CALLBACK(on_stop_activate), NULL);
7949   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->rewind), LIVES_WIDGET_ACTIVATE_SIGNAL,
7950                             LIVES_GUI_CALLBACK(on_rewind_activate), NULL);
7951   mt->sepwin_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->sepwin), LIVES_WIDGET_ACTIVATE_SIGNAL,
7952                     LIVES_GUI_CALLBACK(on_sepwin_activate), NULL);
7953   lives_signal_sync_connect(LIVES_GUI_OBJECT(full_screen), LIVES_WIDGET_ACTIVATE_SIGNAL,
7954                             LIVES_GUI_CALLBACK(on_full_screen_activate), NULL);
7955   mt->loop_cont_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->loop_continue), LIVES_WIDGET_ACTIVATE_SIGNAL,
7956                        LIVES_GUI_CALLBACK(on_loop_cont_activate), NULL);
7957   mt->mute_audio_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->mute_audio), LIVES_WIDGET_ACTIVATE_SIGNAL,
7958                         LIVES_GUI_CALLBACK(on_mute_activate), NULL);
7959   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->rename_track), LIVES_WIDGET_ACTIVATE_SIGNAL,
7960                             LIVES_GUI_CALLBACK(on_rename_track_activate), (livespointer)mt);
7961   lives_signal_connect(LIVES_GUI_OBJECT(mt->cback_audio), LIVES_WIDGET_ACTIVATE_SIGNAL,
7962                        LIVES_GUI_CALLBACK(on_cback_audio_activate), (livespointer)mt);
7963   lives_signal_connect(LIVES_GUI_OBJECT(mt->add_vid_behind), LIVES_WIDGET_ACTIVATE_SIGNAL,
7964                        LIVES_GUI_CALLBACK(add_video_track_behind), (livespointer)mt);
7965   lives_signal_connect(LIVES_GUI_OBJECT(mt->add_vid_front), LIVES_WIDGET_ACTIVATE_SIGNAL,
7966                        LIVES_GUI_CALLBACK(add_video_track_front), (livespointer)mt);
7967   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->render), LIVES_WIDGET_ACTIVATE_SIGNAL,
7968                             LIVES_GUI_CALLBACK(on_render_activate), (livespointer)mt);
7969   lives_signal_connect(LIVES_GUI_OBJECT(mt->prerender_aud), LIVES_WIDGET_ACTIVATE_SIGNAL,
7970                        LIVES_GUI_CALLBACK(on_prerender_aud_activate), (livespointer)mt);
7971   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->jumpback), LIVES_WIDGET_ACTIVATE_SIGNAL,
7972                             LIVES_GUI_CALLBACK(on_jumpback_activate), (livespointer)mt);
7973   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->jumpnext), LIVES_WIDGET_ACTIVATE_SIGNAL,
7974                             LIVES_GUI_CALLBACK(on_jumpnext_activate), (livespointer)mt);
7975   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->mark_jumpback), LIVES_WIDGET_ACTIVATE_SIGNAL,
7976                             LIVES_GUI_CALLBACK(on_jumpback_mark_activate), (livespointer)mt);
7977   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->mark_jumpnext), LIVES_WIDGET_ACTIVATE_SIGNAL,
7978                             LIVES_GUI_CALLBACK(on_jumpnext_mark_activate), (livespointer)mt);
7979   lives_signal_connect(LIVES_GUI_OBJECT(mt->delblock), LIVES_WIDGET_ACTIVATE_SIGNAL,
7980                        LIVES_GUI_CALLBACK(on_delblock_activate), (livespointer)mt);
7981   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->save_event_list), LIVES_WIDGET_ACTIVATE_SIGNAL,
7982                             LIVES_GUI_CALLBACK(on_save_event_list_activate), (livespointer)mt);
7983   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->load_event_list), LIVES_WIDGET_ACTIVATE_SIGNAL,
7984                             LIVES_GUI_CALLBACK(on_load_event_list_activate), (livespointer)mt);
7985   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->clear_event_list), LIVES_WIDGET_ACTIVATE_SIGNAL,
7986                             LIVES_GUI_CALLBACK(on_clear_event_list_activate), (livespointer)mt);
7987   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->view_audio), LIVES_WIDGET_ACTIVATE_SIGNAL,
7988                             LIVES_GUI_CALLBACK(mt_view_audio_toggled), (livespointer)mt);
7989   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->change_max_disp), LIVES_WIDGET_ACTIVATE_SIGNAL,
7990                             LIVES_GUI_CALLBACK(mt_change_max_disp_tracks), (livespointer)mt);
7991   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->render_vid), LIVES_WIDGET_ACTIVATE_SIGNAL,
7992                             LIVES_GUI_CALLBACK(mt_render_vid_toggled), (livespointer)mt);
7993   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->render_aud), LIVES_WIDGET_ACTIVATE_SIGNAL,
7994                             LIVES_GUI_CALLBACK(mt_render_aud_toggled), (livespointer)mt);
7995   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->normalise_aud), LIVES_WIDGET_ACTIVATE_SIGNAL,
7996                             LIVES_GUI_CALLBACK(mt_norm_aud_toggled), (livespointer)mt);
7997   lives_signal_sync_connect(LIVES_GUI_OBJECT(ign_ins_sel), LIVES_WIDGET_ACTIVATE_SIGNAL,
7998                             LIVES_GUI_CALLBACK(mt_ign_ins_sel_toggled), (livespointer)mt);
7999   lives_signal_sync_connect(LIVES_GUI_OBJECT(show_frame_events), LIVES_WIDGET_ACTIVATE_SIGNAL,
8000                             LIVES_GUI_CALLBACK(show_frame_events_activate), NULL);
8001   lives_signal_sync_connect(LIVES_GUI_OBJECT(ccursor), LIVES_WIDGET_ACTIVATE_SIGNAL,
8002                             LIVES_GUI_CALLBACK(mt_center_on_cursor), (livespointer)mt);
8003   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->follow_play), LIVES_WIDGET_ACTIVATE_SIGNAL,
8004                             LIVES_GUI_CALLBACK(mt_fplay_toggled), (livespointer)mt);
8005   lives_signal_connect(LIVES_GUI_OBJECT(zoom_in), LIVES_WIDGET_ACTIVATE_SIGNAL,
8006                        LIVES_GUI_CALLBACK(mt_zoom_in), (livespointer)mt);
8007   lives_signal_connect(LIVES_GUI_OBJECT(zoom_out), LIVES_WIDGET_ACTIVATE_SIGNAL,
8008                        LIVES_GUI_CALLBACK(mt_zoom_out), (livespointer)mt);
8009   mt->seltrack_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->select_track), LIVES_WIDGET_ACTIVATE_SIGNAL,
8010                       LIVES_GUI_CALLBACK(on_seltrack_activate), (livespointer)mt);
8011 
8012   lives_signal_sync_connect(LIVES_GUI_OBJECT(show_manual), LIVES_WIDGET_ACTIVATE_SIGNAL,
8013                             LIVES_GUI_CALLBACK(show_manual_activate), NULL);
8014 
8015   lives_signal_sync_connect(LIVES_GUI_OBJECT(email_author), LIVES_WIDGET_ACTIVATE_SIGNAL,
8016                             LIVES_GUI_CALLBACK(email_author_activate), NULL);
8017 
8018   lives_signal_sync_connect(LIVES_GUI_OBJECT(donate), LIVES_WIDGET_ACTIVATE_SIGNAL,
8019                             LIVES_GUI_CALLBACK(donate_activate), NULL);
8020 
8021   lives_signal_sync_connect(LIVES_GUI_OBJECT(report_bug), LIVES_WIDGET_ACTIVATE_SIGNAL,
8022                             LIVES_GUI_CALLBACK(report_bug_activate), NULL);
8023 
8024   lives_signal_sync_connect(LIVES_GUI_OBJECT(suggest_feature), LIVES_WIDGET_ACTIVATE_SIGNAL,
8025                             LIVES_GUI_CALLBACK(suggest_feature_activate), NULL);
8026 
8027   lives_signal_sync_connect(LIVES_GUI_OBJECT(help_translate), LIVES_WIDGET_ACTIVATE_SIGNAL,
8028                             LIVES_GUI_CALLBACK(help_translate_activate), NULL);
8029 
8030   lives_signal_sync_connect(LIVES_GUI_OBJECT(about), LIVES_WIDGET_ACTIVATE_SIGNAL,
8031                             LIVES_GUI_CALLBACK(on_about_activate), NULL);
8032 
8033   lives_signal_connect(LIVES_GUI_OBJECT(mt->troubleshoot), LIVES_WIDGET_ACTIVATE_SIGNAL,
8034                        LIVES_GUI_CALLBACK(on_troubleshoot_activate), NULL);
8035 
8036   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->expl_missing), LIVES_WIDGET_ACTIVATE_SIGNAL,
8037                             LIVES_GUI_CALLBACK(explain_missing_activate), NULL);
8038 
8039   lives_signal_sync_connect(LIVES_GUI_OBJECT(show_mt_keys), LIVES_WIDGET_ACTIVATE_SIGNAL,
8040                             LIVES_GUI_CALLBACK(on_mt_showkeys_activate), NULL);
8041   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->fx_delete), LIVES_WIDGET_ACTIVATE_SIGNAL,
8042                             LIVES_GUI_CALLBACK(on_mt_delfx_activate), (livespointer)mt);
8043   lives_signal_connect(LIVES_GUI_OBJECT(mt->fx_edit), LIVES_WIDGET_ACTIVATE_SIGNAL,
8044                        LIVES_GUI_CALLBACK(on_mt_fx_edit_activate), (livespointer)mt);
8045   lives_signal_connect(LIVES_GUI_OBJECT(mt->view_effects), LIVES_WIDGET_ACTIVATE_SIGNAL,
8046                        LIVES_GUI_CALLBACK(on_mt_list_fx_activate), (livespointer)mt);
8047 
8048   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_m, (LiVESXModifierType)0, (LiVESAccelFlags)0,
8049                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_mark_callback), (livespointer)mt, NULL));
8050 
8051   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_t, (LiVESXModifierType)0, (LiVESAccelFlags)0,
8052                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_tcoverlay_callback), (livespointer)mt, NULL));
8053 
8054   mt->top_eventbox = lives_event_box_new();
8055   lives_box_pack_start(LIVES_BOX(mt->xtravbox), mt->top_eventbox, FALSE, FALSE, 0);
8056 
8057   lives_widget_add_events(mt->top_eventbox, LIVES_BUTTON1_MOTION_MASK | LIVES_BUTTON_RELEASE_MASK
8058                           | LIVES_BUTTON_PRESS_MASK | LIVES_SCROLL_MASK | LIVES_SMOOTH_SCROLL_MASK);
8059 
8060   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->top_eventbox), LIVES_WIDGET_SCROLL_EVENT,
8061                             LIVES_GUI_CALLBACK(on_mouse_scroll), mt);
8062 
8063   hbox = lives_hbox_new(FALSE, 0);
8064   lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
8065   lives_widget_set_fg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
8066 
8067   lives_container_add(LIVES_CONTAINER(mt->top_eventbox), hbox);
8068 
8069   mt->btoolbar2 = lives_toolbar_new();
8070   lives_box_pack_start(LIVES_BOX(hbox), mt->btoolbar2, FALSE, FALSE, 0);
8071 
8072   // play buttons
8073 
8074   lives_toolbar_set_show_arrow(LIVES_TOOLBAR(mt->btoolbar2), FALSE);
8075 
8076   lives_toolbar_set_style(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOLBAR_ICONS);
8077   lives_toolbar_set_icon_size(LIVES_TOOLBAR(mt->btoolbar2), LIVES_ICON_SIZE_LARGE_TOOLBAR);
8078 
8079   lives_widget_object_ref(mainw->m_sepwinbutton);
8080   lives_widget_unparent(mainw->m_sepwinbutton);
8081   lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_sepwinbutton), -1);
8082   lives_widget_object_unref(mainw->m_sepwinbutton);
8083 
8084   lives_widget_object_ref(mainw->m_rewindbutton);
8085   lives_widget_unparent(mainw->m_rewindbutton);
8086   lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_rewindbutton), -1);
8087   lives_widget_object_unref(mainw->m_rewindbutton);
8088   lives_widget_set_sensitive(mainw->m_rewindbutton, (mt->event_list && get_first_event(mt->event_list)));
8089 
8090   lives_widget_object_ref(mainw->m_playbutton);
8091   lives_widget_unparent(mainw->m_playbutton);
8092   lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_playbutton), -1);
8093   lives_widget_object_unref(mainw->m_playbutton);
8094   lives_widget_set_sensitive(mainw->m_playbutton, (mt->event_list && get_first_event(mt->event_list)));
8095 
8096   lives_widget_object_ref(mainw->m_stopbutton);
8097   lives_widget_unparent(mainw->m_stopbutton);
8098   lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_stopbutton), -1);
8099   lives_widget_object_unref(mainw->m_stopbutton);
8100   lives_widget_set_sensitive(mainw->m_stopbutton, FALSE);
8101 
8102   lives_widget_object_ref(mainw->m_loopbutton);
8103   lives_widget_unparent(mainw->m_loopbutton);
8104   lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_loopbutton), -1);
8105   lives_widget_object_unref(mainw->m_loopbutton);
8106 
8107   widget_opts.expand = LIVES_EXPAND_EXTRA_HEIGHT | LIVES_EXPAND_DEFAULT_WIDTH;
8108   widget_opts.justify = LIVES_JUSTIFY_CENTER;
8109   widget_opts.apply_theme = FALSE;
8110   widget_opts.text_size = LIVES_TEXT_SIZE_LARGE;
8111   mt->timecode = lives_standard_entry_new(NULL, NULL, TIMECODE_LENGTH, TIMECODE_LENGTH, LIVES_BOX(hbox), NULL);
8112   widget_opts.text_size = LIVES_TEXT_SIZE_MEDIUM;
8113   lives_widget_set_valign(mt->timecode, LIVES_ALIGN_CENTER);
8114   widget_opts.apply_theme = woat;
8115   widget_opts.expand = LIVES_EXPAND_DEFAULT;
8116   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
8117 
8118 #if GTK_CHECK_VERSION(3, 16, 0)
8119   if (mainw->pretty_colours) {
8120     set_css_value_direct(LIVES_WIDGET(mt->timecode), LIVES_WIDGET_STATE_NORMAL, "", "border-top-left-radius", "20px");
8121     set_css_value_direct(LIVES_WIDGET(mt->timecode), LIVES_WIDGET_STATE_NORMAL, "", "border-top-right-radius", "20px");
8122     set_css_value_direct(LIVES_WIDGET(mt->timecode), LIVES_WIDGET_STATE_NORMAL, "", "border-bottom-right-radius", "20px");
8123     set_css_value_direct(LIVES_WIDGET(mt->timecode), LIVES_WIDGET_STATE_NORMAL, "", "border-bottom-left-radius", "20px");
8124   }
8125 #endif
8126 
8127   update_timecodes(mt, 0.);
8128 
8129   lives_widget_add_events(mt->timecode, LIVES_FOCUS_CHANGE_MASK);
8130   lives_widget_set_sensitive(mt->timecode, FALSE);
8131 
8132   mt->tc_func = lives_signal_sync_connect_after(LIVES_WIDGET_OBJECT(mt->timecode), LIVES_WIDGET_FOCUS_OUT_EVENT,
8133                 LIVES_GUI_CALLBACK(after_timecode_changed), (livespointer) mt);
8134 
8135   label = add_fill_to_box(LIVES_BOX(hbox));
8136   lives_widget_set_bg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
8137   lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
8138 
8139   widget_opts.expand = LIVES_EXPAND_DEFAULT_HEIGHT;
8140 
8141   mt->insa_checkbutton = lives_glowing_check_button_new((tmp = (_("  Insert with _Audio  "))), LIVES_BOX(hbox),
8142                          (tmp2 = (_("Select whether video clips are inserted and moved with their audio or not"))),
8143                          &mt->opts.insert_audio);
8144   lives_free(tmp);
8145   lives_free(tmp2);
8146   lives_widget_apply_theme2(widget_opts.last_container, LIVES_WIDGET_STATE_NORMAL, TRUE);
8147 
8148   widget_opts.expand = LIVES_EXPAND_DEFAULT;
8149 
8150   mt->insa_label = widget_opts.last_label;
8151 
8152   label = add_fill_to_box(LIVES_BOX(hbox));
8153   lives_widget_set_bg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
8154   lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
8155 
8156   widget_opts.expand = LIVES_EXPAND_DEFAULT_HEIGHT;
8157 
8158   mt->snapo_checkbutton = lives_glowing_check_button_new((tmp = (_("  Select _Overlap  "))), LIVES_BOX(hbox),
8159                           (tmp2 = (_("Select whether timeline selection snaps to overlap between selected tracks or not"))),
8160                           &mt->opts.snap_over);
8161   lives_free(tmp);
8162   lives_free(tmp2);
8163   lives_widget_apply_theme2(widget_opts.last_container, LIVES_WIDGET_STATE_NORMAL, TRUE);
8164 
8165   widget_opts.expand = LIVES_EXPAND_DEFAULT;
8166 
8167   mt->overlap_label = widget_opts.last_label;
8168 
8169   // TODO - add a vbox with two hboxes
8170   // in each hbox we have 16 images
8171   // light for audio - in animate_multitrack
8172   // divide by out volume - then we have a volume gauge
8173 
8174   // add toolbar
8175 
8176   /*  volind=LIVES_WIDGET(gtk_tool_item_new());
8177       mainw->volind_hbox=lives_hbox_new(TRUE,0);
8178       lives_container_add(LIVES_CONTAINER(volind),mainw->volind_hbox);
8179       lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar),LIVES_TOOL_ITEM(mainw->vol_label),7);
8180   */
8181 
8182   mt->btoolbarx = lives_toolbar_new();
8183   lives_box_pack_start(LIVES_BOX(hbox), mt->btoolbarx, TRUE, TRUE,
8184                        widget_opts.packing_width * 2);
8185 
8186   lives_toolbar_set_show_arrow(LIVES_TOOLBAR(mt->btoolbarx), FALSE);
8187 
8188   lives_toolbar_set_style(LIVES_TOOLBAR(mt->btoolbarx), LIVES_TOOLBAR_TEXT);
8189 
8190   mt->btoolbar3 = lives_toolbar_new();
8191   lives_box_pack_end(LIVES_BOX(mt->menu_hbox), mt->btoolbar3, FALSE, FALSE, 0);
8192 
8193   lives_toolbar_set_show_arrow(LIVES_TOOLBAR(mt->btoolbar3), FALSE);
8194 
8195   lives_toolbar_set_style(LIVES_TOOLBAR(mt->btoolbar3), LIVES_TOOLBAR_TEXT);
8196 
8197   mt->grav_menuitem = LIVES_WIDGET(lives_standard_menu_tool_button_new(NULL, NULL));
8198   lives_tool_button_set_use_underline(LIVES_TOOL_BUTTON(mt->grav_menuitem), TRUE);
8199 
8200   mt->grav_normal = lives_standard_check_menu_item_new_with_label(_("Gravity: _Normal"),
8201                     mt->opts.grav_mode == GRAV_MODE_NORMAL);
8202   mtext = lives_menu_item_get_text(mt->grav_normal);
8203 
8204   mt->grav_label = lives_label_new(mtext);
8205   lives_tool_button_set_label_widget(LIVES_TOOL_BUTTON(mt->grav_menuitem), mt->grav_label);
8206 
8207   lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar3), LIVES_TOOL_ITEM(mt->grav_menuitem), -1);
8208 
8209   mt->grav_submenu = lives_menu_new();
8210 
8211   lives_menu_tool_button_set_menu(LIVES_MENU_TOOL_BUTTON(mt->grav_menuitem), mt->grav_submenu);
8212 
8213   lives_container_add(LIVES_CONTAINER(mt->grav_submenu), mt->grav_normal);
8214 
8215   mt->grav_normal_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->grav_normal), LIVES_WIDGET_TOGGLED_SIGNAL,
8216                          LIVES_GUI_CALLBACK(on_grav_mode_changed), (livespointer)mt);
8217 
8218   mt->grav_left = lives_standard_check_menu_item_new_with_label(_("Gravity: _Left"), mt->opts.grav_mode == GRAV_MODE_LEFT);
8219   lives_container_add(LIVES_CONTAINER(mt->grav_submenu), mt->grav_left);
8220 
8221   mt->grav_left_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->grav_left), LIVES_WIDGET_TOGGLED_SIGNAL,
8222                        LIVES_GUI_CALLBACK(on_grav_mode_changed), (livespointer)mt);
8223 
8224   mt->grav_right = lives_standard_check_menu_item_new_with_label(_("Gravity: _Right"), mt->opts.grav_mode == GRAV_MODE_RIGHT);
8225   lives_container_add(LIVES_CONTAINER(mt->grav_submenu), mt->grav_right);
8226 
8227   mt->grav_right_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->grav_right), LIVES_WIDGET_TOGGLED_SIGNAL,
8228                         LIVES_GUI_CALLBACK(on_grav_mode_changed), (livespointer)mt);
8229 
8230   lives_widget_show_all(mt->grav_submenu); // needed
8231 
8232   if (mainw->mgeom[widget_opts.monitor].width > MENUBAR_MIN) in_menubar = FALSE;
8233 
8234   mt->mm_submenu = lives_menu_new();
8235   mt->mm_move = lives_standard_check_menu_item_new_with_label(_("Mouse Mode: _Move"), mt->opts.mouse_mode == MOUSE_MODE_MOVE);
8236   mt->mm_label = NULL;
8237 
8238   if (in_menubar) {
8239     mt->mm_menuitem = lives_standard_menu_item_new_with_label("");
8240     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->mm_menuitem), mt->mm_submenu);
8241     lives_container_add(LIVES_CONTAINER(mt->menubar), mt->mm_menuitem);
8242   } else {
8243     mt->mm_menuitem = LIVES_WIDGET(lives_standard_menu_tool_button_new(NULL, NULL));
8244     lives_tool_button_set_use_underline(LIVES_TOOL_BUTTON(mt->mm_menuitem), TRUE);
8245 
8246     mtext = lives_menu_item_get_text(mt->mm_move);
8247     mt->mm_label = lives_label_new(mtext);
8248     lives_tool_button_set_label_widget(LIVES_TOOL_BUTTON(mt->mm_menuitem), mt->mm_label);
8249     lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar3), LIVES_TOOL_ITEM(mt->mm_menuitem), -1);
8250     lives_menu_tool_button_set_menu(LIVES_MENU_TOOL_BUTTON(mt->mm_menuitem), mt->mm_submenu);
8251   }
8252 
8253   lives_container_add(LIVES_CONTAINER(mt->mm_submenu), mt->mm_move);
8254 
8255   mt->mm_move_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->mm_move), LIVES_WIDGET_TOGGLED_SIGNAL,
8256                      LIVES_GUI_CALLBACK(on_mouse_mode_changed), (livespointer)mt);
8257 
8258   mt->mm_select = lives_standard_check_menu_item_new_with_label(_("Mouse Mode: _Select"),
8259                   mt->opts.mouse_mode == MOUSE_MODE_SELECT);
8260   lives_container_add(LIVES_CONTAINER(mt->mm_submenu), mt->mm_select);
8261 
8262   mt->mm_select_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->mm_select), LIVES_WIDGET_TOGGLED_SIGNAL,
8263                        LIVES_GUI_CALLBACK(on_mouse_mode_changed), (livespointer)mt);
8264 
8265   lives_widget_show_all(mt->mm_submenu); // needed
8266 
8267   mt->ins_submenu = lives_menu_new();
8268   mt->ins_normal = lives_standard_check_menu_item_new_with_label(_("Insert Mode: _Normal"),
8269                    mt->opts.insert_mode == INSERT_MODE_NORMAL);
8270 
8271   if (in_menubar) {
8272     mt->ins_menuitem = lives_standard_menu_item_new_with_label("");
8273     mt->ins_label = NULL;
8274     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mt->ins_menuitem), mt->ins_submenu);
8275     lives_container_add(LIVES_CONTAINER(mt->menubar), mt->ins_menuitem);
8276   } else {
8277     mt->ins_menuitem = LIVES_WIDGET(lives_standard_menu_tool_button_new(NULL, NULL));
8278     lives_tool_button_set_use_underline(LIVES_TOOL_BUTTON(mt->ins_menuitem), TRUE);
8279     mtext = lives_menu_item_get_text(mt->ins_normal);
8280     mt->ins_label = lives_label_new(mtext);
8281     lives_tool_button_set_label_widget(LIVES_TOOL_BUTTON(mt->ins_menuitem), mt->ins_label);
8282     lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar3), LIVES_TOOL_ITEM(mt->ins_menuitem), -1);
8283     lives_menu_tool_button_set_menu(LIVES_MENU_TOOL_BUTTON(mt->ins_menuitem), mt->ins_submenu);
8284   }
8285 
8286   lives_container_add(LIVES_CONTAINER(mt->ins_submenu), mt->ins_normal);
8287 
8288   mt->ins_normal_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->ins_normal), LIVES_WIDGET_TOGGLED_SIGNAL,
8289                         LIVES_GUI_CALLBACK(on_insert_mode_changed),
8290                         (livespointer)mt);
8291 
8292   lives_widget_show_all(mt->ins_submenu); // needed
8293 
8294   if (!in_menubar) {
8295     mt->sep4 = lives_toolbar_insert_space(LIVES_TOOLBAR(mt->btoolbar3));
8296   } else mt->sep4 = NULL;
8297 
8298   mt->btoolbary = lives_toolbar_new();
8299   lives_box_pack_start(LIVES_BOX(hbox), mt->btoolbary, TRUE, TRUE, 0);
8300 
8301   lives_toolbar_set_show_arrow(LIVES_TOOLBAR(mt->btoolbary), FALSE);
8302 
8303   lives_toolbar_set_style(LIVES_TOOLBAR(mt->btoolbary), LIVES_TOOLBAR_ICONS);
8304   lives_toolbar_set_icon_size(LIVES_TOOLBAR(mt->btoolbary), LIVES_ICON_SIZE_SMALL_TOOLBAR);
8305 
8306   lives_widget_object_ref(mainw->m_mutebutton);
8307   lives_widget_unparent(mainw->m_mutebutton);
8308   lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbary), LIVES_TOOL_ITEM(mainw->m_mutebutton), -1);
8309   lives_widget_object_unref(mainw->m_mutebutton);
8310 
8311   if (!lives_scale_button_set_orientation(LIVES_SCALE_BUTTON(mainw->volume_scale),
8312                                           LIVES_ORIENTATION_HORIZONTAL)) {
8313     if (mainw->vol_label) {
8314       lives_widget_object_ref(mainw->vol_label);
8315       lives_widget_unparent(mainw->vol_label);
8316       lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbary), LIVES_TOOL_ITEM(mainw->vol_label), -1);
8317       lives_widget_object_unref(mainw->vol_label);
8318     }
8319   }
8320 
8321   lives_widget_object_ref(mainw->vol_toolitem);
8322   lives_widget_unparent(mainw->vol_toolitem);
8323   lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbary), LIVES_TOOL_ITEM(mainw->vol_toolitem), -1);
8324   lives_widget_object_unref(mainw->vol_toolitem);
8325 
8326   lives_widget_apply_theme2(mainw->vol_toolitem, LIVES_WIDGET_STATE_NORMAL, FALSE);
8327   if (mainw->vol_label) lives_widget_apply_theme2(mainw->vol_label, LIVES_WIDGET_STATE_NORMAL, FALSE);
8328   lives_widget_apply_theme2(mainw->volume_scale, LIVES_WIDGET_STATE_NORMAL, FALSE);
8329 
8330   hseparator = lives_hseparator_new();
8331   lives_box_pack_start(LIVES_BOX(mt->xtravbox), hseparator, FALSE, FALSE, 0);
8332 
8333   mt->hbox = lives_hbox_new(FALSE, 0);
8334   lives_box_pack_start(LIVES_BOX(mt->xtravbox), mt->hbox, FALSE, TRUE, 0);
8335   lives_widget_set_vexpand(mt->hbox, TRUE);
8336 
8337   mt->play_blank = lives_image_new_from_pixbuf(mainw->imframe);
8338   mt->preview_frame = lives_standard_frame_new(_("Preview"), 0.5, FALSE);
8339   lives_container_set_border_width(LIVES_CONTAINER(mt->preview_frame), 0);
8340 
8341   lives_box_pack_start(LIVES_BOX(mt->hbox), mt->preview_frame, FALSE, FALSE, 0);
8342   mt->fd_frame = mt->preview_frame;
8343 
8344   mt->preview_eventbox = lives_event_box_new();
8345   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(mt->preview_eventbox), LIVES_WIDGET_CONFIGURE_EVENT,
8346                                   LIVES_GUI_CALLBACK(config_event), NULL);
8347   lives_widget_queue_resize(mt->preview_eventbox);
8348 
8349   lives_widget_set_hexpand(mt->preview_frame, FALSE);
8350   lives_widget_set_vexpand(mt->preview_frame, FALSE);
8351 
8352   lives_widget_set_vexpand(mt->preview_eventbox, TRUE);
8353 
8354   // must do this here to set mainw->files[mt->render_file]->hsize, mainw->files[mt->render_file]->vsize;
8355   //and we must have created aparam_submenu and insa_eventbox and insa_checkbutton
8356   msg = set_values_from_defs(mt, !prefs->mt_enter_prompt || (mainw->recoverable_layout &&
8357                              prefs->startup_interface == STARTUP_CE));
8358   lives_freep((void **)&msg);
8359 
8360   lives_container_add(LIVES_CONTAINER(mt->preview_frame), mt->preview_eventbox);
8361 
8362   lives_widget_add_events(mt->preview_eventbox, LIVES_BUTTON1_MOTION_MASK | LIVES_BUTTON_RELEASE_MASK
8363                           | LIVES_BUTTON_PRESS_MASK | LIVES_ENTER_NOTIFY_MASK | LIVES_SMOOTH_SCROLL_MASK | LIVES_SCROLL_MASK);
8364 
8365   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->preview_eventbox), LIVES_WIDGET_MOTION_NOTIFY_EVENT,
8366                             LIVES_GUI_CALLBACK(on_framedraw_mouse_update), NULL);
8367   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->preview_eventbox), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
8368                             LIVES_GUI_CALLBACK(on_framedraw_mouse_reset), NULL);
8369   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->preview_eventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
8370                             LIVES_GUI_CALLBACK(on_framedraw_mouse_start), NULL);
8371   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->preview_eventbox), LIVES_WIDGET_ENTER_EVENT,
8372                             LIVES_GUI_CALLBACK(on_framedraw_enter), NULL);
8373   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->preview_eventbox), LIVES_WIDGET_LEAVE_NOTIFY_EVENT,
8374                             LIVES_GUI_CALLBACK(on_framedraw_leave), NULL);
8375   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->preview_eventbox), LIVES_WIDGET_SCROLL_EVENT,
8376                             LIVES_GUI_CALLBACK(on_framedraw_scroll), NULL);
8377 
8378   mt->hpaned = lives_hpaned_new();
8379   lives_box_pack_start(LIVES_BOX(mt->hbox), mt->hpaned, TRUE, TRUE, 0);
8380 
8381   mt->nb = lives_standard_notebook_new(&palette->normal_back, &palette->menu_and_bars);
8382   lives_container_set_border_width(LIVES_CONTAINER(mt->nb), widget_opts.border_width);
8383 
8384   hbox = lives_hbox_new(FALSE, 0);
8385 
8386   // add a page
8387   lives_container_add(LIVES_CONTAINER(mt->nb), hbox);
8388 
8389   tname = get_tab_name(POLY_CLIPS);
8390   mt->nb_label1 = lives_standard_label_new(tname);
8391   lives_free(tname);
8392   lives_widget_set_hexpand(mt->nb_label1, TRUE);
8393   lives_widget_set_halign(mt->nb_label1, LIVES_ALIGN_CENTER);
8394   lives_widget_apply_theme(mt->nb_label1, LIVES_WIDGET_STATE_NORMAL);
8395 
8396   // prepare polymorph box
8397   mt->poly_box = lives_vbox_new(FALSE, 0);
8398 
8399   lives_widget_set_vexpand(mt->poly_box, FALSE);
8400   lives_widget_set_hexpand(mt->poly_box, TRUE);
8401 
8402   lives_container_add(LIVES_CONTAINER(hbox), mt->poly_box);
8403 
8404   lives_notebook_set_tab_label(LIVES_NOTEBOOK(mt->nb), lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), 0), mt->nb_label1);
8405 
8406   // poly box is first page in notebook
8407 
8408   // notebook goes in paned: so we have paned -> nb-> poly_box
8409   lives_paned_pack(1, LIVES_PANED(mt->hpaned), mt->nb, FALSE, FALSE);
8410 
8411   // poly clip scroll
8412   mt->clip_scroll = lives_scrolled_window_new(NULL, NULL);
8413   lives_widget_object_ref(mt->clip_scroll);
8414   lives_widget_set_events(mt->clip_scroll, LIVES_SCROLL_MASK | LIVES_SMOOTH_SCROLL_MASK);
8415   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->clip_scroll), LIVES_WIDGET_SCROLL_EVENT, LIVES_GUI_CALLBACK(on_mouse_scroll),
8416                             mt);
8417 
8418   lives_scrolled_window_set_policy(LIVES_SCROLLED_WINDOW(mt->clip_scroll), LIVES_POLICY_AUTOMATIC, LIVES_POLICY_NEVER);
8419   lives_widget_set_hexpand(mt->clip_scroll, TRUE);
8420 
8421   mt->clip_inner_box = lives_hbox_new(FALSE, widget_opts.packing_width);
8422 
8423   lives_scrolled_window_add_with_viewport(LIVES_SCROLLED_WINDOW(mt->clip_scroll), mt->clip_inner_box);
8424 
8425   // add a dummy hbox to nb (adds a tab with a label)
8426 
8427   tname = get_tab_name(POLY_IN_OUT);
8428   mt->nb_label2 = lives_label_new(tname);
8429   lives_free(tname);
8430   lives_widget_set_hexpand(mt->nb_label2, TRUE);
8431   lives_widget_set_halign(mt->nb_label2, LIVES_ALIGN_CENTER);
8432   lives_widget_apply_theme(mt->nb_label2, LIVES_WIDGET_STATE_NORMAL);
8433 
8434   hbox = lives_hbox_new(FALSE, 0);
8435 
8436   lives_container_add(LIVES_CONTAINER(mt->nb), hbox);
8437   lives_notebook_set_tab_label(LIVES_NOTEBOOK(mt->nb), lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), 1), mt->nb_label2);
8438 
8439   // add a dummy hbox to nb (adds a tab with label)
8440 
8441   tname = get_tab_name(POLY_FX_STACK);
8442   mt->nb_label3 = lives_label_new(tname);
8443   lives_free(tname);
8444   lives_widget_set_hexpand(mt->nb_label3, TRUE);
8445   lives_widget_set_halign(mt->nb_label3, LIVES_ALIGN_CENTER);
8446   lives_widget_apply_theme(mt->nb_label3, LIVES_WIDGET_STATE_NORMAL);
8447 
8448   hbox = lives_hbox_new(FALSE, 0);
8449 
8450   lives_container_add(LIVES_CONTAINER(mt->nb), hbox);
8451   lives_notebook_set_tab_label(LIVES_NOTEBOOK(mt->nb), lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), 2), mt->nb_label3);
8452 
8453   // add a dummy hbox to nb
8454 
8455   tname = get_tab_name(POLY_PARAMS);
8456   mt->nb_label7 = lives_label_new(tname);
8457   lives_free(tname);
8458   lives_widget_set_hexpand(mt->nb_label7, TRUE);
8459   lives_widget_set_halign(mt->nb_label7, LIVES_ALIGN_CENTER);
8460   lives_widget_apply_theme(mt->nb_label7, LIVES_WIDGET_STATE_NORMAL);
8461 
8462   hbox = lives_hbox_new(FALSE, 0);
8463 
8464   lives_container_add(LIVES_CONTAINER(mt->nb), hbox);
8465   lives_notebook_set_tab_label(LIVES_NOTEBOOK(mt->nb), lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), 3), mt->nb_label7);
8466 
8467   // params contents
8468 
8469   mt->fx_base_box = lives_vbox_new(FALSE, 0);
8470   lives_widget_object_ref(mt->fx_base_box);
8471 
8472   lives_widget_set_bg_color(mt->fx_base_box, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
8473   lives_widget_set_fg_color(mt->fx_base_box, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
8474 
8475   mt->fx_contents_box = lives_vbox_new(FALSE, 2);
8476 
8477   lives_widget_set_bg_color(mt->fx_contents_box, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
8478   lives_widget_set_fg_color(mt->fx_contents_box, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
8479 
8480   dph = widget_opts.packing_height;
8481   widget_opts.packing_height >>= 1;
8482   add_hsep_to_box(LIVES_BOX(mt->fx_contents_box));
8483   widget_opts.packing_height = dph;
8484 
8485   lives_box_pack_end(LIVES_BOX(mt->fx_base_box), mt->fx_contents_box, FALSE, FALSE, 0);
8486 
8487   hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
8488   lives_box_pack_end(LIVES_BOX(mt->fx_contents_box), hbox, FALSE, FALSE, 0);
8489 
8490   lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
8491   lives_widget_set_fg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
8492 
8493   mt->apply_fx_button = lives_standard_button_new_full(_("_Apply"), DEF_BUTTON_WIDTH >> 1,
8494                         DEF_BUTTON_HEIGHT, LIVES_BOX(hbox), TRUE, NULL);
8495 
8496   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->apply_fx_button), LIVES_WIDGET_CLICKED_SIGNAL,
8497                             LIVES_GUI_CALLBACK(on_set_pvals_clicked), (livespointer)mt);
8498 
8499   mt->node_adj = (LiVESWidgetObject *)lives_adjustment_new(0., 0., 0., 1. / mt->fps, 10. / mt->fps, 0.);
8500 
8501   mt->node_scale = lives_standard_hscale_new(LIVES_ADJUSTMENT(mt->node_adj));
8502 
8503   mt->node_spinbutton = lives_standard_spin_button_new(NULL, 0., 0., 0., 1. / mt->fps, 1. / mt->fps,
8504                         3, LIVES_BOX(hbox), NULL);
8505 
8506   lives_spin_button_set_adjustment(LIVES_SPIN_BUTTON(mt->node_spinbutton), LIVES_ADJUSTMENT(mt->node_adj));
8507 
8508   lives_signal_connect_after(LIVES_GUI_OBJECT(mt->node_spinbutton), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
8509                              LIVES_GUI_CALLBACK(on_node_spin_value_changed), (livespointer)mt);
8510 
8511   mt->time_label = lives_standard_label_new(_("Time"));
8512   lives_box_pack_start(LIVES_BOX(hbox), mt->time_label, FALSE, TRUE, 0);
8513 
8514   lives_box_pack_start(LIVES_BOX(hbox), mt->node_scale, TRUE, TRUE, widget_opts.packing_width);
8515 
8516   hbox = lives_hbox_new(FALSE, widget_opts.packing_width);
8517   lives_box_pack_end(LIVES_BOX(mt->fx_contents_box), hbox, FALSE, FALSE, 0);
8518 
8519   mt->solo_check = lives_standard_check_button_new(_("Preview _Solo"), TRUE, LIVES_BOX(hbox),
8520                    (tmp = (_("Preview only the selected effect"))));
8521   lives_free(tmp);
8522 
8523   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(mt->solo_check), LIVES_WIDGET_TOGGLED_SIGNAL,
8524                                   LIVES_GUI_CALLBACK(on_solo_check_toggled), (livespointer)mt);
8525 
8526   mt->resetp_button = lives_standard_button_new_with_label(_("_Reset  To Defaults"),
8527                       DEF_BUTTON_WIDTH, -1);
8528   lives_box_pack_start(LIVES_BOX(hbox), mt->resetp_button, FALSE, FALSE, 0);
8529 
8530   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->resetp_button), LIVES_WIDGET_CLICKED_SIGNAL,
8531                             LIVES_GUI_CALLBACK(on_resetp_clicked), (livespointer)mt);
8532 
8533 
8534   widget_opts.expand = LIVES_EXPAND_DEFAULT_HEIGHT;
8535   widget_opts.pack_end = TRUE;
8536   mt->del_node_button = lives_standard_button_new_full(_("_Del. node"), DEF_BUTTON_WIDTH >> 1,
8537                         -1, LIVES_BOX(hbox), TRUE, NULL);
8538   widget_opts.pack_end = FALSE;
8539 
8540   lives_widget_set_sensitive(mt->del_node_button, FALSE);
8541 
8542   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->del_node_button), LIVES_WIDGET_CLICKED_SIGNAL,
8543                             LIVES_GUI_CALLBACK(on_del_node_clicked), (livespointer)mt);
8544 
8545   widget_opts.pack_end = TRUE;
8546   mt->next_node_button = lives_standard_button_new_full(_("_Next node"), DEF_BUTTON_WIDTH >> 1,
8547                          -1, LIVES_BOX(hbox), TRUE, NULL);
8548   widget_opts.pack_end = FALSE;
8549 
8550   lives_widget_set_sensitive(mt->next_node_button, FALSE);
8551 
8552   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->next_node_button), LIVES_WIDGET_CLICKED_SIGNAL,
8553                             LIVES_GUI_CALLBACK(on_next_node_clicked), (livespointer)mt);
8554 
8555   widget_opts.pack_end = TRUE;
8556   mt->prev_node_button = lives_standard_button_new_full(_("_Prev node"), DEF_BUTTON_WIDTH >> 1,
8557                          -1, LIVES_BOX(hbox), TRUE, NULL);
8558   widget_opts.pack_end = FALSE;
8559   widget_opts.expand = LIVES_EXPAND_DEFAULT;
8560 
8561   lives_widget_set_sensitive(mt->prev_node_button, FALSE);
8562 
8563   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->prev_node_button), LIVES_WIDGET_CLICKED_SIGNAL,
8564                             LIVES_GUI_CALLBACK(on_prev_node_clicked), (livespointer)mt);
8565 
8566   widget_opts.justify = LIVES_JUSTIFY_CENTER;
8567   mt->fx_label = lives_standard_label_new("");
8568   lives_box_pack_end(LIVES_BOX(hbox), mt->fx_label, FALSE, FALSE, widget_opts.packing_width * 2);
8569   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
8570 
8571   // add a dummy hbox to nb
8572 
8573   tname = get_tab_name(POLY_EFFECTS);
8574   mt->nb_label4 = lives_label_new(tname);
8575   lives_free(tname);
8576   lives_widget_set_hexpand(mt->nb_label4, TRUE);
8577   lives_widget_set_halign(mt->nb_label4, LIVES_ALIGN_CENTER);
8578   lives_widget_apply_theme(mt->nb_label4, LIVES_WIDGET_STATE_NORMAL);
8579 
8580   hbox = lives_hbox_new(FALSE, 0);
8581 
8582   lives_container_add(LIVES_CONTAINER(mt->nb), hbox);
8583   lives_notebook_set_tab_label(LIVES_NOTEBOOK(mt->nb),
8584                                lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), 4), mt->nb_label4);
8585 
8586   // add a dummy hbox to nb
8587 
8588   tname = get_tab_name(POLY_TRANS);
8589   mt->nb_label5 = lives_label_new(tname);
8590   lives_free(tname);
8591   lives_widget_set_hexpand(mt->nb_label5, TRUE);
8592   lives_widget_set_halign(mt->nb_label5, LIVES_ALIGN_CENTER);
8593   lives_widget_apply_theme(mt->nb_label5, LIVES_WIDGET_STATE_NORMAL);
8594 
8595   hbox = lives_hbox_new(FALSE, 0);
8596 
8597   lives_container_add(LIVES_CONTAINER(mt->nb), hbox);
8598   lives_notebook_set_tab_label(LIVES_NOTEBOOK(mt->nb), lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), 5), mt->nb_label5);
8599 
8600   // add a dummy hbox to nb
8601 
8602   tname = get_tab_name(POLY_COMP);
8603   mt->nb_label6 = lives_label_new(tname);
8604   lives_free(tname);
8605   lives_widget_set_hexpand(mt->nb_label6, TRUE);
8606   lives_widget_set_halign(mt->nb_label6, LIVES_ALIGN_CENTER);
8607   lives_widget_apply_theme(mt->nb_label6, LIVES_WIDGET_STATE_NORMAL);
8608 
8609   hbox = lives_hbox_new(FALSE, 0);
8610 
8611   lives_container_add(LIVES_CONTAINER(mt->nb), hbox);
8612   lives_notebook_set_tab_label(LIVES_NOTEBOOK(mt->nb), lives_notebook_get_nth_page(LIVES_NOTEBOOK(mt->nb), 6), mt->nb_label6);
8613 
8614   set_mt_title(mt);
8615 
8616   mt_init_clips(mt, orig_file, FALSE);
8617 
8618   // poly audio velocity
8619   mt->avel_box = lives_vbox_new(FALSE, 0);
8620   lives_widget_object_ref(mt->avel_box);
8621 
8622   hbox = lives_hbox_new(FALSE, 0);
8623   lives_box_pack_start(LIVES_BOX(mt->avel_box), hbox, FALSE, FALSE, widget_opts.packing_height >> 1);
8624 
8625   mt->checkbutton_avel_reverse = lives_standard_check_button_new(_("_Reverse playback  "), FALSE, LIVES_BOX(hbox), NULL);
8626 
8627   if (palette->style & STYLE_1) {
8628     lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
8629     lives_widget_set_fg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
8630   }
8631 
8632   mt->check_avel_rev_func = lives_signal_connect_after(LIVES_GUI_OBJECT(mt->checkbutton_avel_reverse),
8633                             LIVES_WIDGET_TOGGLED_SIGNAL,  LIVES_GUI_CALLBACK(avel_reverse_toggled), mt);
8634 
8635   hbox = lives_hbox_new(FALSE, 8);
8636   lives_box_pack_start(LIVES_BOX(mt->avel_box), hbox, FALSE, FALSE, widget_opts.packing_height);
8637 
8638   mt->spinbutton_avel = lives_standard_spin_button_new(_("_Velocity  "), 1., 0.5, 2., .1, 1., 2,
8639                         LIVES_BOX(hbox), NULL);
8640 
8641   mt->spin_avel_func = lives_signal_connect_after(LIVES_GUI_OBJECT(mt->spinbutton_avel), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
8642                        LIVES_GUI_CALLBACK(avel_spin_changed), mt);
8643 
8644   spinbutton_adj = lives_spin_button_get_adjustment(LIVES_SPIN_BUTTON(mt->spinbutton_avel));
8645 
8646   mt->avel_scale = lives_standard_hscale_new(LIVES_ADJUSTMENT(spinbutton_adj));
8647   lives_box_pack_start(LIVES_BOX(hbox), mt->avel_scale, TRUE, TRUE, widget_opts.packing_width);
8648 
8649   // poly in_out_box
8650   mt->in_out_box = lives_hbox_new(TRUE, 0);
8651   lives_container_set_border_width(LIVES_CONTAINER(mt->in_out_box), 2);
8652   lives_widget_object_ref(mt->in_out_box);
8653   lives_widget_apply_theme(mt->in_out_box, LIVES_WIDGET_STATE_NORMAL);
8654 
8655   vbox = lives_vbox_new(FALSE, widget_opts.packing_height);
8656   lives_box_pack_start(LIVES_BOX(mt->in_out_box), vbox, TRUE, TRUE, widget_opts.packing_width);
8657 
8658   mt->in_image = lives_standard_drawing_area_new(LIVES_GUI_CALLBACK(all_expose), &mt->insurface);
8659   mt->in_frame = lives_frame_new(NULL);
8660   lives_container_set_border_width(LIVES_CONTAINER(mt->in_frame), 0);
8661 
8662   lives_container_add(LIVES_CONTAINER(mt->in_frame), mt->in_image);
8663   lives_box_pack_start(LIVES_BOX(vbox), mt->in_frame, TRUE, TRUE, 0);
8664 
8665   lives_signal_connect(LIVES_GUI_OBJECT(mt->in_frame), LIVES_WIDGET_BUTTON_PRESS_EVENT,
8666                        LIVES_GUI_CALLBACK(in_out_ebox_pressed), (livespointer)mt);
8667   lives_signal_connect(LIVES_GUI_OBJECT(mt->in_frame), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
8668                        LIVES_GUI_CALLBACK(on_drag_clip_end), (livespointer)mt);
8669 
8670   if (palette->style & STYLE_1) {
8671     lives_widget_set_fg_color(mt->in_frame, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
8672     lives_widget_set_bg_color(mt->in_frame, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
8673   }
8674 
8675   mt->in_hbox = lives_hbox_new(FALSE, 0);
8676   lives_box_pack_start(LIVES_BOX(vbox), mt->in_hbox, FALSE, FALSE, 0);
8677 
8678   add_spring_to_box(LIVES_BOX(mt->in_hbox), 0);
8679 
8680   mt->spinbutton_in = lives_standard_spin_button_new(NULL, 0., 0., 1000000., 1. / mt->fps, 1., 2,
8681                       LIVES_BOX(mt->in_hbox), NULL);
8682   lives_spin_button_set_snap_to_ticks(LIVES_SPIN_BUTTON(mt->spinbutton_in), TRUE);
8683 
8684   mt->checkbutton_start_anchored = lives_standard_check_button_new((tmp = (_("Anchor _start"))), FALSE,
8685                                    LIVES_BOX(mt->in_hbox),
8686                                    (tmp2 = (_("Anchor the start point to the timeline"))));
8687   lives_free(tmp);
8688   lives_free(tmp2);
8689 
8690   add_spring_to_box(LIVES_BOX(mt->in_hbox), 0);
8691 
8692   mt->spin_in_func = lives_signal_connect_after(LIVES_GUI_OBJECT(mt->spinbutton_in), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
8693                      LIVES_GUI_CALLBACK(in_out_start_changed), mt);
8694 
8695   mt->check_start_func = lives_signal_connect_after(LIVES_GUI_OBJECT(mt->checkbutton_start_anchored),
8696                          LIVES_WIDGET_TOGGLED_SIGNAL, LIVES_GUI_CALLBACK(in_anchor_toggled), mt);
8697 
8698   vbox = lives_vbox_new(FALSE, widget_opts.packing_height);
8699 
8700   lives_box_pack_end(LIVES_BOX(mt->in_out_box), vbox, TRUE, TRUE, widget_opts.packing_width);
8701 
8702   mt->out_image = lives_standard_drawing_area_new(LIVES_GUI_CALLBACK(all_expose), &mt->outsurface);
8703 
8704   mt->out_frame = lives_frame_new(NULL);
8705   lives_container_set_border_width(LIVES_CONTAINER(mt->out_frame), 0);
8706 
8707   lives_container_add(LIVES_CONTAINER(mt->out_frame), mt->out_image);
8708   lives_box_pack_start(LIVES_BOX(vbox), mt->out_frame, TRUE, TRUE, 0);
8709 
8710   lives_signal_connect(LIVES_GUI_OBJECT(mt->out_frame), LIVES_WIDGET_BUTTON_PRESS_EVENT,
8711                        LIVES_GUI_CALLBACK(in_out_ebox_pressed), (livespointer)mt);
8712   lives_signal_connect(LIVES_GUI_OBJECT(mt->out_frame), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
8713                        LIVES_GUI_CALLBACK(on_drag_clip_end), (livespointer)mt);
8714 
8715   mt->out_hbox = lives_hbox_new(FALSE, 0);
8716   lives_box_pack_start(LIVES_BOX(vbox), mt->out_hbox, FALSE, FALSE, 0);
8717 
8718   add_spring_to_box(LIVES_BOX(mt->out_hbox), 0);
8719 
8720   mt->spinbutton_out = lives_standard_spin_button_new(NULL, 0., 0., 1000000., 1. / mt->fps, 1., 2,
8721                        LIVES_BOX(mt->out_hbox), NULL);
8722   lives_spin_button_set_snap_to_ticks(LIVES_SPIN_BUTTON(mt->spinbutton_out), TRUE);
8723 
8724   mt->checkbutton_end_anchored = lives_standard_check_button_new((tmp = (_("Anchor _end"))), FALSE,
8725                                  LIVES_BOX(mt->out_hbox),
8726                                  (tmp2 = (_("Anchor the end point to the timeline"))));
8727 
8728   add_spring_to_box(LIVES_BOX(mt->out_hbox), 0);
8729 
8730   lives_free(tmp);
8731   lives_free(tmp2);
8732 
8733   mt->spin_out_func = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(mt->spinbutton_out), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
8734                       LIVES_GUI_CALLBACK(in_out_end_changed), mt);
8735 
8736   mt->check_end_func = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(mt->checkbutton_end_anchored),
8737                        LIVES_WIDGET_TOGGLED_SIGNAL, LIVES_GUI_CALLBACK(out_anchor_toggled), mt);
8738 
8739   lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
8740   lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
8741 
8742   lives_signal_connect(LIVES_GUI_OBJECT(mt->nb), LIVES_WIDGET_SWITCH_PAGE_SIGNAL,
8743                        LIVES_GUI_CALLBACK(notebook_page), (livespointer)mt);
8744   ///////////////////////////////////////////////
8745 
8746   mt->poly_state = POLY_NONE;
8747   polymorph(mt, POLY_CLIPS);
8748 
8749   mt->context_frame = lives_standard_frame_new(_("Info"), 0.5, FALSE);
8750 
8751   lives_paned_pack(2, LIVES_PANED(mt->hpaned), mt->context_frame, TRUE, FALSE);
8752 
8753   mt->context_scroll = NULL;
8754 
8755   clear_context(mt);
8756 
8757   add_hsep_to_box(LIVES_BOX(mt->xtravbox));
8758 
8759   mt->hseparator = lives_hseparator_new();
8760 
8761   if (!mainw->imsep) {
8762     lives_box_pack_start(LIVES_BOX(mt->xtravbox), mt->hseparator, FALSE, FALSE, widget_opts.packing_height);
8763     mt->sep_image = NULL;
8764     mt->hseparator2 = NULL;
8765   } else {
8766     lives_box_pack_start(LIVES_BOX(mt->xtravbox), mt->hseparator, FALSE, FALSE, 0);
8767     mt->sep_image = lives_image_new_from_pixbuf(mainw->imsep);
8768     if (!palette || !(palette->style & STYLE_LIGHT)) {
8769       lives_widget_set_opacity(mt->sep_image, 0.4);
8770     } else {
8771       lives_widget_set_opacity(mt->sep_image, 0.4);
8772     }
8773     lives_box_pack_start(LIVES_BOX(mt->xtravbox), mt->sep_image, FALSE, FALSE, 0);
8774     mt->hseparator2 = lives_hseparator_new();
8775     lives_box_pack_start(LIVES_BOX(mt->xtravbox), mt->hseparator2, FALSE, FALSE, 0);
8776   }
8777 
8778   mt->tlx_vbox = lives_vbox_new(FALSE, 0);
8779 
8780   mt_init_start_end_spins(mt);
8781 
8782   mt->vpaned = lives_vbox_new(FALSE, 0);
8783 
8784   lives_widget_set_hexpand(mt->vpaned, FALSE);
8785 
8786   lives_container_set_border_width(LIVES_CONTAINER(mt->tlx_vbox), 0);
8787   lives_widget_set_valign(mt->tlx_vbox, LIVES_ALIGN_START);
8788   lives_box_pack_start(LIVES_BOX(mt->vpaned), mt->tlx_vbox, FALSE, TRUE, 0);
8789 
8790   mt->timeline_table_header = lives_table_new(2, TIMELINE_TABLE_COLUMNS, TRUE);
8791   lives_table_set_row_spacings(LIVES_TABLE(mt->timeline_table_header), 0);
8792   lives_table_set_row_homogeneous(LIVES_TABLE(mt->timeline_table_header), FALSE);
8793 
8794   mt->tlx_eventbox = lives_event_box_new();
8795   lives_box_pack_start(LIVES_BOX(mt->tlx_vbox), mt->tlx_eventbox, FALSE, FALSE, 0);
8796 
8797   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->tlx_eventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
8798                             LIVES_GUI_CALLBACK(on_track_header_click), (livespointer)mt);
8799 
8800   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->tlx_eventbox), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
8801                             LIVES_GUI_CALLBACK(on_track_header_release), (livespointer)mt);
8802 
8803   lives_widget_add_events(mt->tlx_eventbox, LIVES_BUTTON1_MOTION_MASK | LIVES_BUTTON_RELEASE_MASK
8804                           | LIVES_BUTTON_PRESS_MASK);
8805   mt->mouse_mot1 = lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->tlx_eventbox), LIVES_WIDGET_MOTION_NOTIFY_EVENT,
8806                    LIVES_GUI_CALLBACK(on_track_header_move), (livespointer)mt);
8807 
8808   lives_signal_handler_block(mt->tlx_eventbox, mt->mouse_mot1);
8809 
8810   hbox = lives_hbox_new(FALSE, 0);
8811   lives_container_add(LIVES_CONTAINER(mt->tlx_eventbox), hbox);
8812 
8813   vadjustment = (LiVESWidgetObject *)lives_adjustment_new(1.0, 1.0, 1.0, 1.0, 1.0, 1.0);
8814   scrollbar = lives_vscrollbar_new(LIVES_ADJUSTMENT(vadjustment));
8815   lives_widget_set_sensitive(scrollbar, FALSE);
8816   lives_box_pack_start(LIVES_BOX(hbox), mt->timeline_table_header, TRUE, TRUE, 0);
8817   lives_box_pack_end(LIVES_BOX(hbox), scrollbar, FALSE, FALSE, widget_opts.packing_width);
8818 #if GTK_CHECK_VERSION(3, 8, 0)
8819   gtk_widget_set_opacity(scrollbar, 0.);
8820 #endif
8821 
8822   mt->tl_hbox = lives_hbox_new(FALSE, 0);
8823   lives_container_set_border_width(LIVES_CONTAINER(mt->tl_hbox), 0);
8824 
8825   lives_box_pack_start(LIVES_BOX(mt->tlx_vbox), mt->tl_hbox, TRUE, TRUE, widget_opts.packing_height >> 1);
8826 
8827   mt->vadjustment = (LiVESWidgetObject *)lives_adjustment_new(0.0, 0.0, 1.0, 1.0, prefs->max_disp_vtracks, 1.0);
8828   mt->scrollbar = lives_vscrollbar_new(LIVES_ADJUSTMENT(mt->vadjustment));
8829 
8830   lives_signal_connect_after(LIVES_GUI_OBJECT(mt->scrollbar), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
8831                              LIVES_GUI_CALLBACK(scroll_track_by_scrollbar), (livespointer)mt);
8832 
8833   mt->tl_eventbox = lives_event_box_new();
8834   lives_box_pack_start(LIVES_BOX(mt->tl_hbox), mt->tl_eventbox, TRUE, TRUE, 0);
8835 
8836   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->tl_eventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
8837                             LIVES_GUI_CALLBACK(on_track_between_click), (livespointer)mt);
8838 
8839   lives_signal_connect(LIVES_GUI_OBJECT(mt->tl_eventbox), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
8840                        LIVES_GUI_CALLBACK(on_track_between_release), (livespointer)mt);
8841 
8842   lives_widget_add_events(mt->tl_eventbox, LIVES_BUTTON1_MOTION_MASK | LIVES_BUTTON_RELEASE_MASK
8843                           | LIVES_BUTTON_PRESS_MASK | LIVES_SCROLL_MASK | LIVES_SMOOTH_SCROLL_MASK);
8844   mt->mouse_mot2 = lives_signal_connect(LIVES_GUI_OBJECT(mt->tl_eventbox), LIVES_WIDGET_MOTION_NOTIFY_EVENT,
8845                                         LIVES_GUI_CALLBACK(on_track_move), (livespointer)mt);
8846 
8847   lives_signal_handler_block(mt->tl_eventbox, mt->mouse_mot2);
8848 
8849   lives_signal_connect(LIVES_GUI_OBJECT(mt->tl_eventbox), LIVES_WIDGET_SCROLL_EVENT,
8850                        LIVES_GUI_CALLBACK(on_mt_timeline_scroll), (livespointer)mt);
8851 
8852   lives_box_pack_end(LIVES_BOX(mt->tl_hbox), mt->scrollbar, FALSE, FALSE, widget_opts.packing_width);
8853 
8854   mt->eventbox = lives_event_box_new();
8855   hbox = lives_hbox_new(FALSE, 0);
8856 
8857   lives_box_pack_start(LIVES_BOX(mt->tlx_vbox), mt->eventbox, FALSE, FALSE, 4.*widget_opts.scale);
8858   lives_container_add(LIVES_CONTAINER(mt->eventbox), hbox);
8859 
8860   mt->scroll_label = lives_standard_label_new(_("Scroll"));
8861 
8862   lives_box_pack_start(LIVES_BOX(hbox), mt->scroll_label, FALSE, FALSE, widget_opts.packing_width);
8863 
8864   mt->hadjustment = (LiVESWidgetObject *)lives_adjustment_new(0.0, 0.0, 1., 0.25, 1., 1.);
8865   mt->time_scrollbar = lives_hscrollbar_new(LIVES_ADJUSTMENT(mt->hadjustment));
8866 
8867   lives_box_pack_start(LIVES_BOX(hbox), mt->time_scrollbar, TRUE, TRUE, widget_opts.packing_width);
8868 
8869   lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->time_scrollbar), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
8870                             LIVES_GUI_CALLBACK(scroll_time_by_scrollbar), (livespointer)mt);
8871 
8872   mt->num_video_tracks = 0;
8873 
8874   mt->timeline_table = NULL;
8875   mt->timeline_eb = NULL;
8876 
8877   if (prefs->ar_layout && !mt->event_list && !mainw->recoverable_layout) {
8878     char *eload_file = lives_build_filename(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME, prefs->ar_layout_name, NULL);
8879     mt->auto_reloading = TRUE;
8880     set_string_pref(PREF_AR_LAYOUT, ""); // in case we crash...
8881     mainw->event_list = mt->event_list = load_event_list(mt, eload_file);
8882     mt->auto_reloading = FALSE;
8883     lives_free(eload_file);
8884     if (mt->event_list) {
8885       mt_init_tracks(mt, TRUE);
8886       remove_markers(mt->event_list);
8887       set_string_pref(PREF_AR_LAYOUT, prefs->ar_layout_name);
8888     } else {
8889       prefs->ar_layout = FALSE;
8890       lives_memset(prefs->ar_layout_name, 0, 1);
8891       mt_init_tracks(mt, TRUE);
8892       mainw->unordered_blocks = FALSE;
8893     }
8894   } else if (mainw->recoverable_layout) {
8895     mt_load_recovery_layout(mt);
8896   } else {
8897     mt_init_tracks(mt, TRUE);
8898     mainw->unordered_blocks = FALSE;
8899   }
8900 
8901   if (prefs->show_msg_area) {
8902     mainw_message_box = mainw->message_box;
8903     mainw_msg_area = mainw->msg_area;
8904     mainw_msg_adj = mainw->msg_adj;
8905     mainw_msg_scrollbar = mainw->msg_scrollbar;
8906 
8907     mt->message_box = mainw->message_box = lives_hbox_new(FALSE, 0);
8908 
8909     mt->msg_area = mainw->msg_area =
8910                      lives_standard_drawing_area_new(LIVES_GUI_CALLBACK(reshow_msg_area),
8911                          &mainw->msg_surface);
8912 
8913     lives_widget_set_events(mt->msg_area, LIVES_SMOOTH_SCROLL_MASK | LIVES_SCROLL_MASK);
8914 
8915     lives_container_set_border_width(LIVES_CONTAINER(mt->message_box), 0);
8916     lives_box_pack_start(LIVES_BOX(mt->message_box), mt->msg_area, TRUE, TRUE, 0);
8917     mt->msg_scrollbar = mainw->msg_scrollbar = lives_vscrollbar_new(NULL);
8918 
8919     lives_box_pack_start(LIVES_BOX(mt->message_box), mt->msg_scrollbar, FALSE, TRUE, 0);
8920     mt->msg_adj = mainw->msg_adj = lives_range_get_adjustment(LIVES_RANGE(mt->msg_scrollbar));
8921 
8922     lives_signal_connect_after(LIVES_GUI_OBJECT(mt->msg_adj),
8923                                LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
8924                                LIVES_GUI_CALLBACK(msg_area_scroll),
8925                                (livespointer)mt->msg_area);
8926 
8927     lives_signal_connect(LIVES_GUI_OBJECT(mt->msg_area), LIVES_WIDGET_SCROLL_EVENT,
8928                          LIVES_GUI_CALLBACK(on_msg_area_scroll),
8929                          (livespointer)mt->msg_adj);
8930 
8931     lives_widget_set_size_request(mainw->message_box, -1, MIN_MSGBAR_HEIGHT);
8932   } else {
8933     mt->msg_area = mt->msg_scrollbar = NULL;
8934     mt->msg_adj = NULL;
8935   }
8936 
8937   if (prefs->show_msg_area) lives_box_pack_start(LIVES_BOX(mt->vpaned), mt->message_box, TRUE, TRUE, 0);
8938 
8939   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_Page_Up, LIVES_CONTROL_MASK, (LiVESAccelFlags)0,
8940                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_prevclip), mt, NULL));
8941   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_Page_Down, LIVES_CONTROL_MASK, (LiVESAccelFlags)0,
8942                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_nextclip), mt, NULL));
8943 
8944   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_Left, LIVES_CONTROL_MASK, (LiVESAccelFlags)0,
8945                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_tlback), mt, NULL));
8946   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_Right, LIVES_CONTROL_MASK, (LiVESAccelFlags)0,
8947                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_tlfor), mt, NULL));
8948 
8949   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_Left, LIVES_SHIFT_MASK, (LiVESAccelFlags)0,
8950                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_tlback_frame), mt, NULL));
8951   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_Right, LIVES_SHIFT_MASK, (LiVESAccelFlags)0,
8952                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_tlfor_frame), mt, NULL));
8953 
8954   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_Up, LIVES_CONTROL_MASK, (LiVESAccelFlags)0,
8955                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_trup), mt, NULL));
8956   lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_Down, LIVES_CONTROL_MASK, (LiVESAccelFlags)0,
8957                             lives_cclosure_new(LIVES_GUI_CALLBACK(mt_trdown), mt, NULL));
8958 
8959   mt->last_direction = LIVES_DIRECTION_FORWARD;
8960 
8961   // set check menuitems
8962   if (mt->opts.mouse_mode == MOUSE_MODE_MOVE) on_mouse_mode_changed(LIVES_MENU_ITEM(mt->mm_move), (livespointer)mt);
8963   else if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) on_mouse_mode_changed(LIVES_MENU_ITEM(mt->mm_select), (livespointer)mt);
8964 
8965   if (mt->opts.insert_mode == INSERT_MODE_NORMAL) on_insert_mode_changed(LIVES_MENU_ITEM(mt->ins_normal), (livespointer)mt);
8966 
8967   if (mt->opts.grav_mode == GRAV_MODE_NORMAL) on_grav_mode_changed(LIVES_MENU_ITEM(mt->grav_normal), (livespointer)mt);
8968   else if (mt->opts.grav_mode == GRAV_MODE_LEFT) on_grav_mode_changed(LIVES_MENU_ITEM(mt->grav_left), (livespointer)mt);
8969   else if (mt->opts.grav_mode == GRAV_MODE_RIGHT) on_grav_mode_changed(LIVES_MENU_ITEM(mt->grav_right), (livespointer)mt);
8970 
8971   set_mt_colours(mt);
8972   lives_widget_show_all(mt->poly_box);
8973   lives_widget_set_no_show_all(mt->poly_box, TRUE);
8974 
8975   menu_sets_visible(LIVES_CHECK_MENU_ITEM(mt->show_info), mt->context_frame, FALSE);
8976 
8977   mt_sensitise(mt);
8978   mt->is_ready = TRUE;
8979 
8980   if (prefs->show_msg_area) {
8981     lives_widget_set_can_focus(mt->message_box, TRUE);
8982     lives_widget_grab_focus(mt->message_box);
8983   }
8984 
8985   lives_paned_pack(1, LIVES_PANED(mt->top_vpaned), mt->xtravbox, FALSE, FALSE);
8986   lives_paned_pack(2, LIVES_PANED(mt->top_vpaned), mt->vpaned, TRUE, FALSE);
8987   lives_paned_set_position(LIVES_PANED(mt->top_vpaned),
8988                            (double)GUI_SCREEN_HEIGHT * 2. / 3. * widget_opts.scale);
8989   return mt;
8990 }
8991 
8992 
delete_audio_track(lives_mt * mt,LiVESWidget * eventbox,boolean full)8993 void delete_audio_track(lives_mt * mt, LiVESWidget * eventbox, boolean full) {
8994   // WARNING - does not yet delete events from event_list
8995   // only deletes visually
8996 
8997   track_rect *block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks"), *blocknext;
8998 
8999   LiVESWidget *label, *labelbox, *arrow, *ahbox, *xeventbox;
9000   lives_painter_surface_t *bgimg;
9001 
9002   while (block) {
9003     blocknext = block->next;
9004     if (mt->block_selected == block) mt->block_selected = NULL;
9005     lives_free(block);
9006     block = blocknext;
9007   }
9008 
9009   if ((bgimg = (lives_painter_surface_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "bgimg")) != NULL) {
9010     lives_painter_surface_destroy(bgimg);
9011   }
9012 
9013   label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label");
9014   arrow = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "arrow");
9015   lives_widget_destroy(label);
9016   lives_widget_destroy(arrow);
9017   if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY)) == 0) {
9018     labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "labelbox");
9019     ahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "ahbox");
9020     if (labelbox) lives_widget_destroy(labelbox);
9021     if (ahbox) lives_widget_destroy(ahbox);
9022   }
9023 
9024   xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "achan0");
9025   if (xeventbox) {
9026     label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "label");
9027     lives_widget_destroy(label);
9028     if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY)) == 0) {
9029       labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "labelbox");
9030       if (labelbox) lives_widget_destroy(labelbox);
9031     }
9032     if ((bgimg = (lives_painter_surface_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "bgimg")) != NULL) {
9033       lives_painter_surface_destroy(bgimg);
9034     }
9035 
9036     lives_widget_destroy(xeventbox);
9037   }
9038   if (mainw->files[mt->render_file]->achans > 1) {
9039     xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "achan1");
9040     if (xeventbox) {
9041       label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "label");
9042       lives_widget_destroy(label);
9043       if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY)) == 0) {
9044         labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "labelbox");
9045         if (labelbox) lives_widget_destroy(labelbox);
9046       }
9047       if ((bgimg = (lives_painter_surface_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "bgimg")) != NULL) {
9048         lives_painter_surface_destroy(bgimg);
9049       }
9050 
9051       lives_widget_destroy(xeventbox);
9052     }
9053   }
9054 
9055   lives_free(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "track_name"));
9056   lives_widget_destroy(eventbox);
9057 }
9058 
9059 
update_layout_map(weed_plant_t * event_list)9060 static int *update_layout_map(weed_plant_t *event_list) {
9061   // update our current layout map with the current layout
9062   // returns an int * of maximum frame used for each clip that exists, 0 means unused
9063   int *used_clips;
9064   weed_plant_t *event;
9065   int i;
9066 
9067   used_clips = (int *)lives_malloc((MAX_FILES + 1) * sizint);
9068   for (i = 1; i <= MAX_FILES; i++) used_clips[i] = 0;
9069 
9070   if (!event_list) return used_clips;
9071 
9072   event = get_first_event(event_list);
9073 
9074   while (event) {
9075     if (WEED_EVENT_IS_FRAME(event)) {
9076       int numtracks;
9077       int *clip_index = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &numtracks);
9078       if (numtracks > 0) {
9079         int64_t *frame_index = weed_get_int64_array(event, WEED_LEAF_FRAMES, NULL);
9080         for (int i = 0; i < numtracks; i++) {
9081           if (clip_index[i] > 0 && (frame_index[i] > used_clips[clip_index[i]])) used_clips[clip_index[i]] = (int)frame_index[i];
9082         }
9083         lives_free(clip_index);
9084         lives_free(frame_index);
9085       }
9086     }
9087     event = get_next_event(event);
9088   }
9089   return used_clips;
9090 }
9091 
9092 
update_layout_map_audio(weed_plant_t * event_list)9093 static double *update_layout_map_audio(weed_plant_t *event_list) {
9094   // update our current layout map with the current layout
9095   // returns a double * of maximum audio in seconds used for each clip that exists, 0. means unused
9096   double *used_clips;
9097   weed_plant_t *event;
9098   int i;
9099 
9100   // TODO - use linked lists
9101   double aseek[MAX_AUDIO_TRACKS];
9102   double avel[MAX_AUDIO_TRACKS];
9103   weed_timecode_t atc[MAX_AUDIO_TRACKS];
9104   int last_aclips[MAX_AUDIO_TRACKS];
9105 
9106   double neg_aseek[MAX_AUDIO_TRACKS];
9107   double neg_avel[MAX_AUDIO_TRACKS];
9108   weed_timecode_t neg_atc[MAX_AUDIO_TRACKS];
9109   int neg_last_aclips[MAX_AUDIO_TRACKS];
9110 
9111   int atrack;
9112   double aval;
9113   weed_timecode_t tc;
9114   int last_aclip;
9115 
9116   used_clips = (double *)lives_calloc((MAX_FILES + 1), sizdbl);
9117   if (!event_list) return used_clips;
9118 
9119   event = get_first_event(event_list);
9120 
9121   for (i = 0; i < MAX_AUDIO_TRACKS; i++) {
9122     avel[i] = neg_avel[i] = 0.;
9123   }
9124 
9125   while (event) {
9126     if (WEED_EVENT_IS_FRAME(event)) {
9127       if (WEED_EVENT_IS_AUDIO_FRAME(event)) {
9128         int numatracks;
9129         int *aclip_index;
9130         double *aseek_index;
9131         register int i;
9132         numatracks = weed_frame_event_get_audio_tracks(event, &aclip_index, &aseek_index);
9133         for (i = 0; i < numatracks; i += 2) {
9134           if (aclip_index[i + 1] > 0) {
9135             atrack = aclip_index[i];
9136             tc = get_event_timecode(event);
9137             if (atrack >= 0) {
9138               if (atrack >= MAX_AUDIO_TRACKS) {
9139                 LIVES_ERROR("invalid atrack");
9140               } else {
9141                 if (avel[atrack] != 0.) {
9142                   aval = aseek[atrack] + (tc - atc[atrack]) / TICKS_PER_SECOND_DBL * avel[atrack];
9143                   last_aclip = last_aclips[atrack];
9144                   if (aval > used_clips[last_aclip]) used_clips[last_aclip] = aval;
9145                 }
9146                 aseek[atrack] = aseek_index[i];
9147                 avel[atrack] = aseek_index[i + 1];
9148                 atc[atrack] = tc;
9149                 last_aclips[atrack] = aclip_index[i + 1];
9150               }
9151             } else {
9152               atrack = -atrack;
9153               if (atrack > MAX_AUDIO_TRACKS) {
9154                 LIVES_ERROR("invalid back atrack");
9155               } else {
9156                 if (neg_avel[atrack] != 0.) {
9157                   aval = neg_aseek[atrack] + (tc - neg_atc[atrack]) / TICKS_PER_SECOND_DBL * neg_avel[atrack];
9158                   last_aclip = neg_last_aclips[atrack];
9159                   if (aval > used_clips[last_aclip]) used_clips[last_aclip] = aval;
9160                 }
9161                 neg_aseek[atrack] = aseek_index[i];
9162                 neg_avel[atrack] = aseek_index[i + 1];
9163                 neg_atc[atrack] = tc;
9164                 neg_last_aclips[atrack] = aclip_index[i + 1];
9165 		// *INDENT-OFF*
9166               }}}}
9167 	// *INDENT-ON*
9168 
9169         lives_free(aclip_index);
9170         lives_free(aseek_index);
9171       }
9172     }
9173     event = get_next_event(event);
9174   }
9175 
9176   return used_clips;
9177 }
9178 
9179 
used_in_current_layout(lives_mt * mt,int file)9180 boolean used_in_current_layout(lives_mt * mt, int file) {
9181   // see if <file> is used in current layout
9182   int *layout_map;
9183   double *layout_map_audio;
9184   boolean retval = FALSE;
9185 
9186   if (mainw->stored_event_list) {
9187     return (mainw->files[file]->stored_layout_frame > 0 || mainw->files[file]->stored_layout_audio > 0.);
9188   }
9189 
9190   if (mt && mt->event_list) {
9191     layout_map = update_layout_map(mt->event_list);
9192     layout_map_audio = update_layout_map_audio(mt->event_list);
9193 
9194     if (layout_map[file] > 0 || layout_map_audio[file] > 0.) retval = TRUE;
9195 
9196     lives_freep((void **)&layout_map);
9197     lives_freep((void **)&layout_map_audio);
9198   }
9199 
9200   return retval;
9201 }
9202 
9203 
multitrack_delete(lives_mt * mt,boolean save_layout)9204 boolean multitrack_delete(lives_mt * mt, boolean save_layout) {
9205   // free lives_mt struct
9206   int *layout_map;
9207   double *layout_map_audio = NULL;
9208 
9209   boolean needs_idlefunc = FALSE;
9210   boolean did_backup = mt->did_backup;
9211 
9212   int i;
9213 
9214   mainw->cancelled = CANCEL_NONE;
9215 
9216   if (mt->idlefunc > 0) {
9217     needs_idlefunc = TRUE;
9218     lives_source_remove(mt->idlefunc);
9219     mt->idlefunc = 0;
9220   }
9221 
9222   if (mt->frame_pixbuf && mt->frame_pixbuf != mainw->imframe) {
9223     lives_widget_object_unref(mt->frame_pixbuf);
9224     mt->frame_pixbuf = NULL;
9225   }
9226 
9227   if (save_layout || mainw->scrap_file != -1 || mainw->ascrap_file != -1) {
9228     if (!mainw->recording_recovered) {
9229       int file_selected = mt->file_selected;
9230       if (!check_for_layout_del(mt, TRUE)) {
9231         if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
9232         return FALSE;
9233       }
9234       mt->file_selected = file_selected; // because init_clips will reset this
9235     } else mainw->recording_recovered = FALSE;
9236   } else {
9237     if (mt->event_list) {
9238       save_event_list_inner(mt, -1, mt->event_list, NULL); // set width, height, fps etc.
9239       add_markers(mt, mt->event_list, FALSE);
9240 
9241       mainw->stored_event_list = mt->event_list;
9242 
9243 #ifdef DEBUG_TTABLE
9244       int error;
9245       weed_plant_t *tevent = get_first_event(mt->event_list);
9246       tevent = get_next_event(tevent);
9247       tevent = get_next_event(tevent);
9248       g_print("VALXX is %p\n", weed_get_voidptr_value(tevent, WEED_LEAF_INIT_EVENT, NULL));
9249 #endif
9250 
9251       mt->event_list = NULL;
9252       mainw->stored_event_list_changed = mt->changed;
9253       mainw->stored_event_list_auto_changed = mt->auto_changed;
9254       lives_snprintf(mainw->stored_layout_name, 256, "%s", mt->layout_name);
9255       mainw->stored_layout_undos = mt->undos;
9256       mainw->sl_undo_mem = mt->undo_mem;
9257       mainw->sl_undo_buffer_used = mt->undo_buffer_used;
9258       mainw->sl_undo_offset = mt->undo_offset;
9259       mt->undos = NULL;
9260       mt->undo_mem = NULL;
9261 
9262       // update layout maps (kind of) with the stored_event_list
9263 
9264       layout_map = update_layout_map(mainw->stored_event_list);
9265       layout_map_audio = update_layout_map_audio(mainw->stored_event_list);
9266 
9267       for (i = 1; i < MAX_FILES; i++) {
9268         if (mainw->files[i] && (layout_map[i] != 0 || (layout_map_audio && layout_map_audio[i] != 0.))) {
9269           mainw->files[i]->stored_layout_frame = layout_map[i];
9270           if (layout_map_audio)
9271             mainw->files[i]->stored_layout_audio = layout_map_audio[i];
9272           else
9273             mainw->files[i]->stored_layout_audio = 0.;
9274           mainw->files[i]->stored_layout_fps = mainw->files[i]->fps;
9275           mainw->files[i]->stored_layout_idx = i;
9276         }
9277       }
9278 
9279       lives_freep((void **)&layout_map);
9280       lives_freep((void **)&layout_map_audio);
9281     }
9282   }
9283 
9284   if (mt->amixer) on_amixer_close_clicked(NULL, mt);
9285 
9286   mt->no_expose = mt->no_expose_frame = TRUE;
9287   mt->is_ready = FALSE;
9288 
9289   lives_memcpy(&mainw->multi_opts, &mt->opts, sizeof(mainw->multi_opts));
9290   mainw->multi_opts.aparam_view_list = lives_list_copy(mt->opts.aparam_view_list);
9291   mainw->multi_opts.ptr_time = mt->ptr_time;
9292 
9293   mainw->multi_opts.set = TRUE;
9294 
9295   if (mt->insurface) lives_painter_surface_destroy(mt->insurface);
9296   if (mt->outsurface) lives_painter_surface_destroy(mt->outsurface);
9297 
9298   if (mt->poly_state == POLY_PARAMS) polymorph(mt, POLY_CLIPS);
9299 
9300   lives_freep((void **)&mt->undo_mem);
9301 
9302   if (mt->undos) lives_list_free(mt->undos);
9303 
9304   if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
9305 
9306   if (mainw->event_list == mt->event_list) mainw->event_list = NULL;
9307   if (mt->event_list) event_list_free(mt->event_list);
9308   mt->event_list = NULL;
9309 
9310   if (mt->clip_selected >= 0 && mainw->files[mt_file_from_clip(mt, mt->clip_selected)])
9311     mt_file_from_clip(mt, mt->clip_selected);
9312 
9313   if (mt->clip_labels) lives_list_free(mt->clip_labels);
9314 
9315   lives_signal_handler_block(mainw->full_screen, mainw->fullscreen_cb_func);
9316   lives_signal_handler_block(mainw->sepwin, mainw->sepwin_cb_func);
9317   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->full_screen), mainw->fs);
9318   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->sepwin), mainw->sep_win);
9319   lives_signal_handler_unblock(mainw->full_screen, mainw->fullscreen_cb_func);
9320   lives_signal_handler_unblock(mainw->sepwin, mainw->sepwin_cb_func);
9321 
9322   if (mainw->play_window) {
9323     lives_window_remove_accel_group(LIVES_WINDOW(mainw->play_window), mt->accel_group);
9324     lives_window_add_accel_group(LIVES_WINDOW(mainw->play_window), mainw->accel_group);
9325   }
9326 
9327   // put buttons back in mainw->menubar
9328   mt_swap_play_pause(mt, FALSE);
9329 
9330   lives_signal_handler_block(mainw->loop_continue, mainw->loop_cont_func);
9331   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->loop_continue), mainw->loop_cont);
9332   lives_signal_handler_unblock(mainw->loop_continue, mainw->loop_cont_func);
9333 
9334   lives_signal_handler_block(mainw->mute_audio, mainw->mute_audio_func);
9335   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->mute_audio), mainw->mute);
9336   lives_signal_handler_unblock(mainw->mute_audio, mainw->mute_audio_func);
9337 
9338   lives_widget_object_ref(mainw->m_sepwinbutton);
9339   lives_widget_unparent(mainw->m_sepwinbutton);
9340   lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_sepwinbutton), 0);
9341   lives_widget_object_unref(mainw->m_sepwinbutton);
9342 
9343   lives_widget_object_ref(mainw->m_rewindbutton);
9344   lives_widget_unparent(mainw->m_rewindbutton);
9345   lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_rewindbutton), 1);
9346   lives_widget_object_unref(mainw->m_rewindbutton);
9347 
9348   lives_widget_object_ref(mainw->m_playbutton);
9349   lives_widget_unparent(mainw->m_playbutton);
9350   lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_playbutton), 2);
9351   lives_widget_object_unref(mainw->m_playbutton);
9352 
9353   lives_widget_object_ref(mainw->m_stopbutton);
9354   lives_widget_unparent(mainw->m_stopbutton);
9355   lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_stopbutton), 3);
9356   lives_widget_object_unref(mainw->m_stopbutton);
9357 
9358   /*  lives_widget_object_ref(mainw->m_playselbutton);
9359       lives_widget_unparent(mainw->m_playselbutton);
9360       lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar),LIVES_TOOL_ITEM(mainw->m_playselbutton),4);
9361       lives_widget_object_unref(mainw->m_playselbutton);*/
9362 
9363   lives_widget_object_ref(mainw->m_loopbutton);
9364   lives_widget_unparent(mainw->m_loopbutton);
9365   lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_loopbutton), 5);
9366   lives_widget_object_unref(mainw->m_loopbutton);
9367 
9368   lives_widget_object_ref(mainw->m_mutebutton);
9369   lives_widget_unparent(mainw->m_mutebutton);
9370   lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_mutebutton), 6);
9371   lives_widget_object_unref(mainw->m_mutebutton);
9372 
9373   if (!lives_scale_button_set_orientation(LIVES_SCALE_BUTTON(mainw->volume_scale),
9374                                           LIVES_ORIENTATION_HORIZONTAL)) {
9375     if (mainw->vol_label) {
9376       lives_widget_object_ref(mainw->vol_label);
9377       lives_widget_unparent(mainw->vol_label);
9378       lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->vol_label), 7);
9379       lives_widget_object_unref(mainw->vol_label);
9380     }
9381   }
9382 
9383   lives_widget_object_ref(mainw->vol_toolitem);
9384   lives_widget_unparent(mainw->vol_toolitem);
9385   lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->vol_toolitem), -1);
9386   lives_widget_object_unref(mainw->vol_toolitem);
9387 
9388   if (mainw->gens_menu) {
9389     lives_widget_object_ref(mainw->gens_menu);
9390     lives_menu_detach(LIVES_MENU(mainw->gens_menu));
9391     lives_menu_item_set_submenu(LIVES_MENU_ITEM(mainw->gens_submenu), mainw->gens_menu);
9392   }
9393 
9394   if (mt->mt_frame_preview) {
9395     if (mainw->plug) {
9396       lives_container_remove(LIVES_CONTAINER(mainw->plug), mainw->play_image);
9397       lives_widget_destroy(mainw->plug);
9398       mainw->plug = NULL;
9399     }
9400 
9401     mainw->playarea = lives_hbox_new(FALSE, 0);
9402 
9403     lives_container_add(LIVES_CONTAINER(mainw->pl_eventbox), mainw->playarea);
9404     lives_widget_set_app_paintable(mainw->playarea, TRUE);
9405     lives_widget_set_app_paintable(mainw->pl_eventbox, TRUE);
9406 
9407     if (palette->style & STYLE_1) {
9408       lives_widget_set_bg_color(mainw->playframe, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
9409     }
9410     lives_widget_show(mainw->playarea);
9411   }
9412 
9413   // free our track_rects
9414   if (mainw->files[mt->render_file]->achans > 0) {
9415     delete_audio_tracks(mt, mt->audio_draws, FALSE);
9416     if (mt->audio_vols) lives_list_free(mt->audio_vols);
9417   }
9418 
9419   if (CURRENT_CLIP_IS_VALID && CLIP_TOTAL_TIME(mainw->current_file) == 0.) {
9420     mainw->files[mt->render_file]->laudio_drawable = mainw->files[mt->render_file]->raudio_drawable = NULL;
9421     close_current_file(mt->file_selected);
9422   }
9423 
9424   if (mt->video_draws) {
9425     for (i = 0; i < mt->num_video_tracks; i++) {
9426       delete_video_track(mt, i, FALSE);
9427     }
9428     lives_list_free(mt->video_draws);
9429   }
9430 
9431   lives_widget_destroy(mt->in_out_box);
9432   lives_widget_destroy(mt->clip_scroll);
9433   lives_widget_destroy(mt->fx_base_box);
9434 
9435   lives_list_free(mt->tl_marks);
9436 
9437   mainw->multitrack = NULL;
9438   mainw->event_list = NULL;
9439 
9440   lives_window_remove_accel_group(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), mt->accel_group);
9441   lives_window_add_accel_group(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), mainw->accel_group);
9442 
9443   for (i = 1; i < MAX_FILES; i++) {
9444     if (mainw->files[i]) {
9445       if (mainw->files[i]->event_list) {
9446         event_list_free(mainw->files[i]->event_list);
9447       }
9448       mainw->files[i]->event_list = NULL;
9449     }
9450   }
9451 
9452   for (i = 0; i < N_RECENT_FILES; i++) {
9453     lives_widget_reparent(mainw->recent[i], mainw->recent_submenu);
9454   }
9455 
9456   if (prefs->show_gui) {
9457     if (lives_widget_get_parent(mt->top_vbox)) {
9458       lives_widget_object_ref(mt->top_vbox);
9459       lives_widget_unparent(mt->top_vbox);
9460       lives_container_add(LIVES_CONTAINER(LIVES_MAIN_WINDOW_WIDGET), mainw->top_vbox);
9461       if (prefs->show_msg_area) {
9462         mainw->message_box = mainw_message_box;
9463         mainw->msg_area = mainw_msg_area;
9464         mainw->msg_adj = mainw_msg_adj;
9465         mainw->msg_scrollbar = mainw_msg_scrollbar;
9466       }
9467       if (mainw->reconfig) return TRUE;
9468       show_lives();
9469       resize(1.);
9470     }
9471   }
9472 
9473   if (mainw->reconfig) return TRUE;
9474 
9475   if (future_prefs->audio_src == AUDIO_SRC_EXT) {
9476     // switch back to external audio
9477     pref_factory_bool(PREF_REC_EXT_AUDIO, TRUE, FALSE);
9478   }
9479 
9480   lives_widget_set_opacity(mainw->playframe, 0.);
9481 
9482   mainw->is_rendering = FALSE;
9483 
9484   reset_clipmenu();
9485   mainw->last_dprint_file = -1;
9486 
9487   if (prefs->show_gui && prefs->open_maximised) {
9488     int bx, by;
9489     get_border_size(LIVES_MAIN_WINDOW_WIDGET, &bx, &by);
9490     if (by > MENU_HIDE_LIM)
9491       lives_window_set_hide_titlebar_when_maximized(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), TRUE);
9492     lives_window_maximize(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET));
9493   }
9494 
9495   desensitize();
9496 
9497   // force the message area to get its correct space, in case we started in STARTUP_MT and haven't show it yet
9498   // also, clears out any events for widgets we are going to destroy in switch_fo_file
9499   lives_widget_context_update(); // IMPORTANT !!!
9500   /////////////////////////////////
9501 
9502   if (mt->file_selected != -1) {
9503     switch_to_file((mainw->current_file = 0), mt->file_selected);
9504   } else {
9505     if (mainw->laudio_drawable) lives_painter_surface_destroy(mainw->laudio_drawable);
9506     if (mainw->raudio_drawable) lives_painter_surface_destroy(mainw->raudio_drawable);
9507     mainw->laudio_drawable = mainw->raudio_drawable = NULL;
9508     mainw->drawsrc = -1;
9509     resize(1);
9510     lives_widget_context_update();
9511     load_start_image(0);
9512     load_end_image(0);
9513   }
9514 
9515   pref_factory_int(PREF_SEPWIN_TYPE, future_prefs->sepwin_type, FALSE);
9516 
9517   lives_free(mt);
9518 
9519   if (prefs->sepwin_type == SEPWIN_TYPE_STICKY && mainw->sep_win) {
9520     make_play_window();
9521   }
9522 
9523   if (!mainw->recoverable_layout) sensitize();
9524 
9525   lives_widget_set_vexpand(mainw->play_image, FALSE);
9526 
9527   lives_idle_add_simple(redraw_tl_idle, NULL);
9528 
9529   if (prefs->show_msg_area) {
9530     prefs->msg_textsize = future_prefs->msg_textsize;
9531     if (mainw->idlemax == 0)
9532       lives_idle_add_simple(resize_message_area, NULL);
9533     mainw->idlemax = DEF_IDLE_MAX;
9534   }
9535 
9536   d_print(_("====== Switched to Clip Edit mode ======\n"));
9537 
9538   lives_notify_int(LIVES_OSC_NOTIFY_MODE_CHANGED, STARTUP_CE);
9539 
9540   return TRUE;
9541 }
9542 
9543 
locate_avol_init_event(lives_mt * mt,weed_plant_t * event_list,int avol_fx)9544 static void locate_avol_init_event(lives_mt * mt, weed_plant_t *event_list, int avol_fx) {
9545   // once we have detected or assigned our audio volume effect, we search for a FILTER_INIT event for it
9546   // this becomes our mt->avol_init_event
9547   char *filter_hash;
9548   weed_plant_t *event = get_first_event(event_list);
9549 
9550   while (event) {
9551     if (WEED_EVENT_IS_FILTER_INIT(event)) {
9552       filter_hash = weed_get_string_value(event, WEED_LEAF_FILTER, NULL);
9553 
9554       if (avol_fx == weed_get_idx_for_hashname(filter_hash, TRUE)) {
9555         lives_free(filter_hash);
9556         mt->avol_init_event = event;
9557         return;
9558       }
9559       lives_free(filter_hash);
9560     }
9561     event = get_next_event(event);
9562   }
9563 }
9564 
9565 
add_block_start_point(LiVESWidget * eventbox,weed_timecode_t tc,int filenum,weed_timecode_t offset_start,weed_plant_t * event,boolean ordered)9566 static track_rect *add_block_start_point(LiVESWidget * eventbox, weed_timecode_t tc, int filenum,
9567     weed_timecode_t offset_start, weed_plant_t *event, boolean ordered) {
9568   // each mt->video_draw (eventbox) has a ulong data which points to a linked list of track_rect
9569   // here we create a new linked list item and set the start timecode in the timeline,
9570   // offset in the source file, and start event
9571   // then append it to our list
9572 
9573   // "block_last" points to the last block added - not the last block in the track !!
9574 
9575   // note: filenum is unused and may be removed in future
9576 
9577   track_rect *block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
9578   track_rect *new_block;
9579 
9580   for (; block && get_event_timecode(block->start_event) <= tc; block = block->next) {
9581     if (block->start_event == event) return NULL;
9582     if (!block->next) break;
9583   }
9584 
9585   new_block = (track_rect *)lives_malloc(sizeof(track_rect));
9586 
9587   new_block->uid = lives_random();
9588   new_block->next = new_block->prev = NULL;
9589   new_block->state = BLOCK_UNSELECTED;
9590   new_block->start_anchored = new_block->end_anchored = FALSE;
9591   new_block->start_event = event;
9592   new_block->ordered = ordered;
9593   new_block->eventbox = eventbox;
9594   new_block->offset_start = offset_start;
9595 
9596   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "block_last", (livespointer)new_block);
9597 
9598   if (block) {
9599     if (get_event_timecode(block->start_event) > tc) {
9600       // found a block after insertion point
9601       if (block->prev) {
9602         block->prev->next = new_block;
9603         new_block->prev = block->prev;
9604       }
9605       // add as first block
9606       else lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "blocks", (livespointer)new_block);
9607       new_block->next = block;
9608       block->prev = new_block;
9609     } else {
9610       // add as last block
9611       block->next = new_block;
9612       new_block->prev = block;
9613     }
9614   }
9615 
9616   // there were no blocks there
9617   if (!block) lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "blocks", (livespointer)new_block);
9618 
9619   return new_block;
9620 }
9621 
9622 
add_block_end_point(LiVESWidget * eventbox,weed_plant_t * event)9623 static track_rect *add_block_end_point(LiVESWidget * eventbox, weed_plant_t *event) {
9624   // here we add the end point to our last track_rect
9625   track_rect *block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
9626   if (block) block->end_event = event;
9627   return block;
9628 }
9629 
on_tlreg_enter(LiVESWidget * widget,LiVESXEventCrossing * event,livespointer user_data)9630 static boolean on_tlreg_enter(LiVESWidget * widget, LiVESXEventCrossing * event, livespointer user_data) {
9631   lives_mt *mt = (lives_mt *)user_data;
9632   if (mt->cursor_style != LIVES_CURSOR_NORMAL) return FALSE;
9633   lives_set_cursor_style(LIVES_CURSOR_SB_H_DOUBLE_ARROW, widget);
9634   return FALSE;
9635 }
9636 
9637 
on_tleb_enter(LiVESWidget * widget,LiVESXEventCrossing * event,livespointer user_data)9638 static boolean on_tleb_enter(LiVESWidget * widget, LiVESXEventCrossing * event, livespointer user_data) {
9639   lives_mt *mt = (lives_mt *)user_data;
9640   if (mt->cursor_style != LIVES_CURSOR_NORMAL) return FALSE;
9641   lives_set_cursor_style(LIVES_CURSOR_CENTER_PTR, widget);
9642   return FALSE;
9643 }
9644 
9645 
reset_renumbering(void)9646 void reset_renumbering(void) {
9647   for (int i = 1; i <= MAX_FILES; i++) {
9648     if (mainw->files[i]) {
9649       renumbered_clips[i] = i;
9650     } else renumbered_clips[i] = 0;
9651   }
9652 }
9653 
9654 
set_track_labels(lives_mt * mt)9655 static void set_track_labels(lives_mt * mt) {
9656   register int i;
9657 
9658   if (weed_plant_has_leaf(mt->event_list, WEED_LEAF_TRACK_LABEL_TRACKS)) {
9659     int navs;
9660     int *navals = weed_get_int_array_counted(mt->event_list, WEED_LEAF_TRACK_LABEL_TRACKS, &navs);
9661 
9662     int nlabs;
9663     char **labs = weed_get_string_array_counted(mt->event_list, WEED_LEAF_TRACK_LABEL_VALUES, &nlabs);
9664 
9665     if (nlabs < navs) navs = nlabs;
9666 
9667     for (i = 0; i < navs; i++) {
9668       int nt = navals[i];
9669       if (nt < mt->num_video_tracks) {
9670         set_track_label_string(mt, nt, labs[i]);
9671       }
9672     }
9673     lives_free(labs);
9674     lives_free(navals);
9675   }
9676 }
9677 
9678 
mt_init_tracks(lives_mt * mt,boolean set_min_max)9679 void mt_init_tracks(lives_mt * mt, boolean set_min_max) {
9680   LiVESList *tlist;
9681 
9682   mt->avol_init_event = NULL;
9683 
9684   tlist = mt->audio_draws;
9685 
9686   while (mt->audio_draws) {
9687     if (mt->audio_draws->data) lives_widget_destroy((LiVESWidget *)mt->audio_draws->data);
9688     mt->audio_draws = mt->audio_draws->next;
9689   }
9690 
9691   lives_list_free(tlist);
9692 
9693   tlist = mt->video_draws;
9694 
9695   while (mt->video_draws) {
9696     if (mt->video_draws->data) lives_widget_destroy((LiVESWidget *)mt->video_draws->data);
9697     mt->video_draws->data = NULL;
9698     mt->video_draws = mt->video_draws->next;
9699   }
9700 
9701   lives_list_free(tlist);
9702   mt->num_video_tracks = 0;
9703 
9704   mt->tl_label = NULL;
9705 
9706 #ifndef ENABLE_GIW_3
9707   if (!mt->timeline_table) {
9708     mt->tl_label = lives_standard_label_new(_("Timeline (seconds)"));
9709     lives_table_attach(LIVES_TABLE(mt->timeline_table_header), mt->tl_label, 0, 7, 0, 2,
9710                        LIVES_FILL, (LiVESAttachOptions)0, 0, 0);
9711   }
9712 #endif
9713 
9714   mt->current_track = 0;
9715 
9716   mt->clip_selected = mt_clip_from_file(mt, mt->file_selected);
9717   mt_clip_select(mt, TRUE);
9718 
9719   if (mainw->files[mt->render_file]->achans > 0 && mt->opts.back_audio_tracks > 0) {
9720     // start with 1 audio track
9721     add_audio_track(mt, -1, FALSE);
9722   }
9723 
9724   // start with 2 video tracks
9725   add_video_track_behind(NULL, mt);
9726   add_video_track_behind(NULL, mt);
9727 
9728   mt->current_track = 0;
9729   mt->block_selected = NULL;
9730 
9731   if (!mt->timeline_eb) {
9732 #ifdef ENABLE_GIW_3
9733     mt->timeline = giw_timeline_new_with_adjustment(LIVES_ORIENTATION_HORIZONTAL, 0., 0., 1000000., 1000000.);
9734     giw_timeline_set_unit(GIW_TIMELINE(mt->timeline), GIW_TIME_UNIT_SMH);
9735     giw_timeline_set_mouse_policy(GIW_TIMELINE(mt->timeline), GIW_TIMELINE_MOUSE_DISABLED);
9736 #else
9737     mt->timeline = lives_standard_hruler_new();
9738 #endif
9739 
9740     mt->timeline_reg = lives_standard_drawing_area_new(LIVES_GUI_CALLBACK(all_expose),
9741                        &mt->tl_reg_surf);
9742     mt->timeline_eb = lives_event_box_new();
9743 
9744     lives_widget_add_events(mt->timeline_eb, LIVES_POINTER_MOTION_MASK | LIVES_BUTTON1_MOTION_MASK |
9745                             LIVES_BUTTON_RELEASE_MASK | LIVES_BUTTON_PRESS_MASK | LIVES_ENTER_NOTIFY_MASK);
9746     lives_widget_add_events(mt->timeline, LIVES_BUTTON_RELEASE_MASK | LIVES_BUTTON_PRESS_MASK);
9747     lives_widget_add_events(mt->timeline_reg, LIVES_POINTER_MOTION_MASK | LIVES_BUTTON1_MOTION_MASK |
9748                             LIVES_BUTTON_RELEASE_MASK | LIVES_BUTTON_PRESS_MASK | LIVES_ENTER_NOTIFY_MASK);
9749     lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->timeline_eb), LIVES_WIDGET_ENTER_EVENT, LIVES_GUI_CALLBACK(on_tleb_enter),
9750                               (livespointer)mt);
9751     lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->timeline_reg), LIVES_WIDGET_ENTER_EVENT, LIVES_GUI_CALLBACK(on_tlreg_enter),
9752                               (livespointer)mt);
9753 
9754     lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->timeline), LIVES_WIDGET_MOTION_NOTIFY_EVENT,
9755                               LIVES_GUI_CALLBACK(return_true), NULL);
9756 
9757     lives_signal_connect(LIVES_GUI_OBJECT(mt->timeline_eb), LIVES_WIDGET_MOTION_NOTIFY_EVENT,
9758                          LIVES_GUI_CALLBACK(on_timeline_update), (livespointer)mt);
9759 
9760     lives_signal_connect(LIVES_GUI_OBJECT(mt->timeline_eb), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
9761                          LIVES_GUI_CALLBACK(on_timeline_release), (livespointer)mt);
9762 
9763     lives_signal_connect(LIVES_GUI_OBJECT(mt->timeline_eb), LIVES_WIDGET_BUTTON_PRESS_EVENT,
9764                          LIVES_GUI_CALLBACK(on_timeline_press), (livespointer)mt);
9765 
9766     lives_signal_connect(LIVES_GUI_OBJECT(mt->timeline), LIVES_WIDGET_BUTTON_PRESS_EVENT,
9767                          LIVES_GUI_CALLBACK(on_timeline_press), (livespointer)mt);
9768 
9769     lives_signal_connect(LIVES_GUI_OBJECT(mt->timeline), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
9770                          LIVES_GUI_CALLBACK(on_timeline_release), (livespointer)mt);
9771 
9772     lives_signal_connect(LIVES_GUI_OBJECT(mt->timeline_reg), LIVES_WIDGET_MOTION_NOTIFY_EVENT,
9773                          LIVES_GUI_CALLBACK(on_timeline_update), (livespointer)mt);
9774 
9775     lives_signal_connect(LIVES_GUI_OBJECT(mt->timeline_reg), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
9776                          LIVES_GUI_CALLBACK(on_timeline_release), (livespointer)mt);
9777 
9778     lives_signal_connect(LIVES_GUI_OBJECT(mt->timeline_reg), LIVES_WIDGET_BUTTON_PRESS_EVENT,
9779                          LIVES_GUI_CALLBACK(on_timeline_press), (livespointer)mt);
9780 
9781     lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->timeline_reg), LIVES_WIDGET_EXPOSE_EVENT,
9782                               LIVES_GUI_CALLBACK(expose_timeline_reg_event), (livespointer)mt);
9783 
9784     lives_container_add(LIVES_CONTAINER(mt->timeline_eb), mt->timeline);
9785 
9786     mt->dumlabel1 = lives_standard_label_new(""); // dummy label
9787 
9788     lives_table_attach(LIVES_TABLE(mt->timeline_table_header), mt->dumlabel1, 0, 7, 0, 1,
9789                        (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL),
9790                        (LiVESAttachOptions)(LIVES_FILL), 0, 0);
9791 
9792     lives_table_attach(LIVES_TABLE(mt->timeline_table_header), mt->timeline_eb, 7, TIMELINE_TABLE_COLUMNS, 0, 1,
9793                        (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL),
9794                        (LiVESAttachOptions)(LIVES_FILL), 0, 0);
9795 
9796     mt->dumlabel2 = lives_standard_label_new(""); // dummy label
9797 
9798     lives_table_attach(LIVES_TABLE(mt->timeline_table_header), mt->dumlabel2, 0, 7, 1, 2,
9799                        (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL),
9800                        (LiVESAttachOptions)(LIVES_FILL), 0, 0);
9801 
9802     lives_table_attach(LIVES_TABLE(mt->timeline_table_header), mt->timeline_reg, 7, TIMELINE_TABLE_COLUMNS, 1, 2,
9803                        (LiVESAttachOptions)(LIVES_EXPAND | LIVES_FILL),
9804                        (LiVESAttachOptions)(LIVES_FILL), 0, 0);
9805   }
9806 
9807   if (mt->event_list) {
9808     LiVESWidget *audio_draw;
9809     LiVESList *slist;
9810     track_rect *block;
9811     weed_plant_t *event, *last_event = NULL, *next_frame_event;
9812     weed_timecode_t tc, last_tc;
9813     weed_timecode_t offset_start;
9814     weed_timecode_t block_marker_tc = -1;
9815     weed_timecode_t block_marker_uo_tc = -1;
9816     double *aseeks;
9817     double avels[MAX_AUDIO_TRACKS];
9818     int64_t *frame_index, *new_frame_index;
9819     int *clip_index, *new_clip_index;
9820     int *block_marker_uo_tracks = NULL;
9821     int *aclips, *navals;
9822     int *block_marker_tracks = NULL;
9823     int tracks[MAX_VIDEO_TRACKS]; // TODO - use linked list
9824 
9825     boolean forced_end = FALSE;
9826     boolean ordered = TRUE;
9827     boolean shown_audio_warn = FALSE;
9828 
9829     int block_marker_uo_num_tracks = 0;
9830     int num_aclips, i;
9831     int navs, maxval;
9832     int last_valid_frame;
9833     int block_marker_num_tracks = 0;
9834     int last_tracks = 1; // number of video tracks on timeline
9835     int num_tracks;
9836     int j;
9837 
9838     if (mainw->reconfig) mt->was_undo_redo = TRUE;
9839 
9840     for (j = 0; j < MAX_TRACKS; j++) {
9841       tracks[j] = 0;
9842       avels[j] = 0.;
9843     }
9844 
9845     navals = weed_get_int_array_counted(mt->event_list, WEED_LEAF_AUDIO_VOLUME_TRACKS, &navs);
9846     if (navs > 0) {
9847       maxval = mt->num_video_tracks - 1;
9848 
9849       for (j = 0; j < navs; j++) {
9850         if (navals[j] > maxval) maxval = navals[j];
9851       }
9852       lives_free(navals);
9853       num_tracks = maxval + 1;
9854 
9855       if (num_tracks > mt->num_video_tracks) {
9856         for (j = mt->num_video_tracks; j < num_tracks; j++) {
9857           add_video_track_behind(NULL, mt);
9858         }
9859       }
9860     }
9861 
9862     // draw coloured blocks to represent the FRAME events
9863     event = get_first_event(mt->event_list);
9864     while (event) {
9865       if (WEED_EVENT_IS_MARKER(event)) {
9866         if (weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, NULL) == EVENT_MARKER_BLOCK_START) {
9867           block_marker_tc = get_event_timecode(event);
9868           lives_freep((void **)&block_marker_tracks);
9869           block_marker_tracks = weed_get_int_array(event, WEED_LEAF_TRACKS, NULL);
9870         } else if (weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, NULL) == EVENT_MARKER_BLOCK_UNORDERED) {
9871           block_marker_uo_tc = get_event_timecode(event);
9872           lives_freep((void **)&block_marker_uo_tracks);
9873           block_marker_uo_tracks = weed_get_int_array_counted(event, WEED_LEAF_TRACKS, &block_marker_uo_num_tracks);
9874         }
9875       } else if (WEED_EVENT_IS_FILTER_INIT(event)) {
9876         if (weed_plant_has_leaf(event, WEED_LEAF_IN_TRACKS)) {
9877           navals = weed_get_int_array_counted(event, WEED_LEAF_IN_TRACKS, &navs);
9878           maxval = mt->num_video_tracks - 1;
9879 
9880           for (j = 0; j < navs; j++) {
9881             if (navals[j] > maxval) maxval = navals[j];
9882           }
9883           lives_free(navals);
9884           num_tracks = maxval + 1;
9885 
9886           if (num_tracks > mt->num_video_tracks) {
9887             for (j = mt->num_video_tracks; j < num_tracks; j++) {
9888               add_video_track_behind(NULL, mt);
9889             }
9890           }
9891         }
9892       } else if (WEED_EVENT_IS_FRAME(event)) {
9893         tc = get_event_timecode(event);
9894         clip_index = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &num_tracks);
9895         frame_index = weed_get_int64_array(event, WEED_LEAF_FRAMES, NULL);
9896 
9897         if (num_tracks < last_tracks) {
9898           for (j = num_tracks; j < last_tracks; j++) {
9899             // TODO - tracks should be linked list
9900             if (tracks[j] > 0) {
9901               add_block_end_point(LIVES_WIDGET(lives_list_nth_data(mt->video_draws, j)), last_event); // end of previous rectangle
9902               tracks[j] = 0;
9903             }
9904           }
9905         }
9906 
9907         if (num_tracks > mt->num_video_tracks) {
9908           for (j = mt->num_video_tracks; j < num_tracks; j++) {
9909             add_video_track_behind(NULL, mt);
9910           }
9911         }
9912 
9913         last_tracks = num_tracks;
9914         new_clip_index = (int *)lives_malloc(num_tracks * sizint);
9915         new_frame_index = (int64_t *)lives_malloc(num_tracks * 8);
9916         last_valid_frame = 0;
9917 
9918         for (j = 0; j < num_tracks; j++) {
9919           // TODO - tracks should be linked list
9920           if (clip_index[j] > 0 && frame_index[j] > -1 && clip_index[j] <= MAX_FILES &&
9921               renumbered_clips[clip_index[j]] > 0 && (frame_index[j] <=
9922                   (int64_t)mainw->files[renumbered_clips[clip_index[j]]]->frames
9923                   || renumbered_clips[clip_index[j]] == mainw->scrap_file)) {
9924             forced_end = FALSE;
9925             if (tc == block_marker_tc && int_array_contains_value(block_marker_tracks, block_marker_num_tracks, j))
9926               forced_end = TRUE;
9927             if ((tracks[j] != renumbered_clips[clip_index[j]]) || forced_end) {
9928               // handling for block end or split blocks
9929               if (tracks[j] > 0) {
9930                 // end of previous rectangle
9931                 add_block_end_point(LIVES_WIDGET(lives_list_nth_data(mt->video_draws, j)), last_event);
9932               }
9933               if (clip_index[j] > 0) {
9934                 ordered = !mainw->unordered_blocks;
9935                 if (tc == block_marker_uo_tc && int_array_contains_value(block_marker_uo_tracks, block_marker_uo_num_tracks, j))
9936                   ordered = FALSE;
9937                 // start a new rectangle
9938                 offset_start = calc_time_from_frame(renumbered_clips[clip_index[j]], (int)frame_index[j]) * TICKS_PER_SECOND_DBL;
9939                 add_block_start_point(LIVES_WIDGET(lives_list_nth_data(mt->video_draws, j)), tc,
9940                                       renumbered_clips[clip_index[j]], offset_start, event, ordered);
9941               }
9942               tracks[j] = renumbered_clips[clip_index[j]];
9943             }
9944             new_clip_index[j] = renumbered_clips[clip_index[j]];
9945             new_frame_index[j] = frame_index[j];
9946             last_valid_frame = j + 1;
9947           } else {
9948             // clip has probably been closed, so we remove its frames
9949 
9950             // TODO - do similar check for audio
9951             new_clip_index[j] = -1;
9952             new_frame_index[j] = 0;
9953             if (tracks[j] > 0) {
9954               // end of previous rectangle
9955               add_block_end_point(LIVES_WIDGET(lives_list_nth_data(mt->video_draws, j)), last_event);
9956               tracks[j] = 0;
9957             }
9958           }
9959         }
9960 
9961         if (last_valid_frame == 0) {
9962           lives_free(new_clip_index);
9963           lives_free(new_frame_index);
9964           new_clip_index = (int *)lives_malloc(sizint);
9965           new_frame_index = (int64_t *)lives_malloc(8);
9966           *new_clip_index = -1;
9967           *new_frame_index = 0;
9968           num_tracks = 1;
9969         } else {
9970           if (last_valid_frame < num_tracks) {
9971             lives_free(new_clip_index);
9972             lives_free(new_frame_index);
9973             new_clip_index = (int *)lives_malloc(last_valid_frame * sizint);
9974             new_frame_index = (int64_t *)lives_malloc(last_valid_frame * 8);
9975             for (j = 0; j < last_valid_frame; j++) {
9976               new_clip_index[j] = clip_index[j];
9977               new_frame_index[j] = frame_index[j];
9978             }
9979             num_tracks = last_valid_frame;
9980           }
9981         }
9982 
9983         weed_set_int_array(event, WEED_LEAF_CLIPS, num_tracks, new_clip_index);
9984         weed_set_int64_array(event, WEED_LEAF_FRAMES, num_tracks, new_frame_index);
9985 
9986         lives_free(clip_index);
9987         lives_free(frame_index);
9988 
9989         next_frame_event = get_next_frame_event(event);
9990 
9991         if (WEED_EVENT_IS_AUDIO_FRAME(event)) {
9992           // audio starts or stops here
9993           aclips = weed_get_int_array_counted(event, WEED_LEAF_AUDIO_CLIPS, &num_aclips);
9994           aseeks = weed_get_double_array(event, WEED_LEAF_AUDIO_SEEKS, NULL);
9995           for (i = 0; i < num_aclips; i += 2) {
9996             if (aclips[i + 1] > 0) {
9997               if (mainw->files[mt->render_file]->achans == 0) {
9998                 if (!shown_audio_warn) {
9999                   shown_audio_warn = TRUE;
10000                   do_mt_audchan_error(WARN_MASK_MT_ACHANS);
10001                 }
10002               } else {
10003                 if (ordered) {
10004                   if (aclips[i] == -1) audio_draw = (LiVESWidget *)mt->audio_draws->data;
10005                   else audio_draw = (LiVESWidget *)lives_list_nth_data(mt->audio_draws, aclips[i] + mt->opts.back_audio_tracks);
10006                   if (avels[aclips[i] + 1] != 0.) {
10007                     add_block_end_point(audio_draw, event);
10008                     if (!forced_end && tracks[aclips[i]] > 0 && next_frame_event && get_next_frame_event(next_frame_event)) {
10009                       add_block_end_point(LIVES_WIDGET(lives_list_nth_data(mt->video_draws, aclips[i])), last_event);
10010                     }
10011                   }
10012                   //if (renumbered_clips[clip_index[aclips[i+1]]]>0) {
10013                   avels[aclips[i] + 1] = aseeks[i + 1];
10014                   //}
10015                   if (ordered) {
10016                     if (avels[aclips[i] + 1] != 0.) {
10017                       add_block_start_point(audio_draw, tc, renumbered_clips[aclips[i + 1]],
10018                                             aseeks[i]*TICKS_PER_SECOND_DBL, event, TRUE);
10019                       if (!forced_end && tracks[aclips[i]] > 0 && next_frame_event && get_next_frame_event(next_frame_event)) {
10020                         offset_start = calc_time_from_frame(new_clip_index[aclips[i]],
10021                                                             (int)new_frame_index[aclips[i]]) * TICKS_PER_SECOND_DBL;
10022                         add_block_start_point(LIVES_WIDGET(lives_list_nth_data(mt->video_draws, aclips[i])), tc,
10023                                               new_clip_index[aclips[i]], offset_start, event, ordered);
10024 			// *INDENT-OFF*
10025 		      }}}}}}
10026 	    // *INDENT-ON*
10027 
10028             if (aclips[i + 1] > 0) aclips[i + 1] = renumbered_clips[aclips[i + 1]];
10029           }
10030           weed_set_int_array(event, WEED_LEAF_AUDIO_CLIPS, num_aclips, aclips);
10031           lives_free(aseeks);
10032           lives_free(aclips);
10033         }
10034 
10035         lives_free(new_clip_index);
10036         lives_free(new_frame_index);
10037 
10038         slist = mt->audio_draws;
10039         for (i = mt->opts.back_audio_tracks + 1; --i > 0; slist = slist->next);
10040         for (; slist; slist = slist->next, i++) {
10041           // handling for split blocks
10042           if (tc == block_marker_tc && int_array_contains_value(block_marker_tracks, block_marker_num_tracks, -i)) {
10043             audio_draw = (LiVESWidget *)slist->data;
10044             if (avels[i] != 0.) {
10045               // end the current block and add a new one
10046               // note we only add markers here, when drawing the block audio events will be added
10047               block = add_block_end_point(audio_draw, event);
10048               if (block) {
10049                 last_tc = get_event_timecode(block->start_event);
10050                 offset_start = block->offset_start + (weed_timecode_t)((double)(tc - last_tc) * avels[i] + .5);
10051                 add_block_start_point(audio_draw, tc, -1, offset_start, event, TRUE);
10052 		// *INDENT-OFF*
10053               }}}}
10054 	// *INDENT-ON*
10055 
10056         if (!next_frame_event) {
10057           // this is the last FRAME event, so close all our rectangles
10058           j = 0;
10059           for (slist = mt->video_draws; slist; slist = slist->next) {
10060             if (tracks[j++] > 0) {
10061               add_block_end_point(LIVES_WIDGET(slist->data), event);
10062             }
10063           }
10064           j = 0;
10065           for (slist = mt->audio_draws; slist; slist = slist->next) {
10066             if (mainw->files[mt->render_file]->achans > 0 && avels[j++] != 0.)
10067               add_block_end_point((LiVESWidget *)slist->data, event);
10068           }
10069         }
10070         last_event = event;
10071       }
10072       event = get_next_event(event);
10073     }
10074     if (!mt->was_undo_redo) remove_end_blank_frames(mt->event_list, TRUE);
10075     lives_freep((void **)&block_marker_tracks);
10076     lives_freep((void **)&block_marker_uo_tracks);
10077 
10078     if (mainw->files[mt->render_file]->achans > 0 && mt->opts.back_audio_tracks > 0) lives_widget_show(mt->view_audio);
10079 
10080     if (mt->avol_fx != -1) locate_avol_init_event(mt, mt->event_list, mt->avol_fx);
10081 
10082     if (!mt->was_undo_redo && mt->avol_fx != -1 && mt->audio_draws) {
10083       apply_avol_filter(mt);
10084     }
10085   }
10086 
10087   mt->end_secs = 0.;
10088   if (mt->event_list) {
10089     mt->end_secs = event_list_get_end_secs(mt->event_list) * 2.;
10090     if (mt->end_secs == 0.) LIVES_WARN("got zero length event_list");
10091   }
10092   if (mt->end_secs == 0.) mt->end_secs = DEF_TIME;
10093 
10094   if (set_min_max) {
10095     mt->tl_min = 0.;
10096     mt->tl_max = mt->end_secs;
10097   }
10098 
10099   set_timeline_end_secs(mt, mt->end_secs);
10100 
10101   if (!mt->was_undo_redo) {
10102     set_track_labels(mt);
10103 
10104     if (mt->is_ready) {
10105       if (mt->current_track != 0) {
10106         mt->current_track = 0;
10107         track_select(mt);
10108       }
10109       if (mt->region_start != mt->region_end || mt->region_start != 0.) {
10110         mt->region_start = mt->region_end = 0.;
10111         draw_region(mt);
10112       }
10113     }
10114     mt_tl_move(mt, 0.);
10115   } else mt->was_undo_redo = FALSE;
10116 
10117   reset_renumbering();
10118 }
10119 
10120 
delete_video_track(lives_mt * mt,int layer,boolean full)10121 void delete_video_track(lives_mt * mt, int layer, boolean full) {
10122   // WARNING - does not yet delete events from event_list
10123   // only deletes visually
10124 
10125   LiVESWidget *eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, layer);
10126   track_rect *block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks"), *blocknext;
10127 
10128   LiVESWidget *checkbutton;
10129   LiVESWidget *label, *labelbox, *ahbox, *arrow;
10130   lives_painter_surface_t *bgimg;
10131 
10132   while (block) {
10133     blocknext = block->next;
10134     if (mt->block_selected == block) mt->block_selected = NULL;
10135     lives_free(block);
10136     block = blocknext;
10137   }
10138 
10139   if ((bgimg = (lives_painter_surface_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "bgimg")) != NULL) {
10140     lives_painter_surface_destroy(bgimg);
10141   }
10142 
10143   checkbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "checkbutton");
10144   label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label");
10145   arrow = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "arrow");
10146 
10147   mt->cb_list = lives_list_remove(mt->cb_list, (livespointer)checkbutton);
10148 
10149   lives_widget_destroy(checkbutton);
10150   lives_widget_destroy(label);
10151   lives_widget_destroy(arrow);
10152   if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY)) == 0) {
10153     labelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "labelbox");
10154     ahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "ahbox");
10155     if (labelbox) lives_widget_destroy(labelbox);
10156     if (ahbox) lives_widget_destroy(ahbox);
10157   }
10158   lives_free(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "track_name"));
10159 
10160   // corresponding audio track will be deleted in delete_audio_track(s)
10161 
10162   lives_widget_destroy(eventbox);
10163 }
10164 
10165 
add_audio_track(lives_mt * mt,int track,boolean behind)10166 LiVESWidget *add_audio_track(lives_mt * mt, int track, boolean behind) {
10167   // add float or pertrack audio track to our timeline_table
10168   LiVESWidgetObject *adj;
10169   LiVESWidget *label, *dummy;
10170   LiVESWidget *arrow;
10171   LiVESWidget *eventbox;
10172   LiVESWidget *vbox;
10173   LiVESWidget *audio_draw = lives_event_box_new();
10174   char *pname, *tname;
10175   int max_disp_vtracks = prefs->max_disp_vtracks - 1;
10176   int llen, vol = 0;
10177   int nachans = 0;
10178   int i;
10179 
10180   lives_widget_object_ref(audio_draw);
10181 
10182   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(audio_draw), "blocks", (livespointer)NULL);
10183   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(audio_draw), "block_last", (livespointer)NULL);
10184   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(audio_draw), HIDDEN_KEY, LIVES_INT_TO_POINTER(0));
10185   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(audio_draw), "expanded", LIVES_INT_TO_POINTER(FALSE));
10186   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(audio_draw), "bgimg", NULL);
10187   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(audio_draw), "is_audio", LIVES_INT_TO_POINTER(TRUE));
10188 
10189   lives_adjustment_set_upper(LIVES_ADJUSTMENT(mt->vadjustment), (double)mt->num_video_tracks);
10190   lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->vadjustment), (double)(max_disp_vtracks > mt->num_video_tracks ?
10191                                  mt->num_video_tracks : max_disp_vtracks));
10192 
10193   dummy = lives_event_box_new();
10194   lives_widget_object_ref(dummy);
10195 
10196   widget_opts.justify = LIVES_JUSTIFY_START;
10197   if (track == -1) {
10198     label = lives_label_new(_(" Backing audio"));
10199   } else {
10200     char *tmp = lives_strdup_printf(_(" Layer %d audio"), track);
10201     label = lives_label_new(tmp);
10202     lives_free(tmp);
10203   }
10204 
10205   lives_widget_set_halign(label, LIVES_ALIGN_START);
10206 
10207   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
10208   lives_widget_object_ref(label);
10209 
10210   arrow = lives_arrow_new(LIVES_ARROW_RIGHT, LIVES_SHADOW_OUT);
10211   lives_widget_set_tooltip_text(arrow, _("Show/hide audio details"));
10212   lives_widget_object_ref(arrow);
10213 
10214   lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(audio_draw), "label", label);
10215   lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(audio_draw), "dummy", dummy);
10216   lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(audio_draw), "arrow", arrow);
10217 
10218   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(arrow), "layer_number", LIVES_INT_TO_POINTER(track));
10219   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(audio_draw), "layer_number", LIVES_INT_TO_POINTER(track));
10220   lives_widget_object_set_data_auto(LIVES_WIDGET_OBJECT(audio_draw), "track_name", lives_strdup_printf(_("Layer %d audio"),
10221                                     track));
10222 
10223   // add channel subtracks
10224   for (i = 0; i < mainw->files[mt->render_file]->achans; i++) {
10225     eventbox = lives_event_box_new();
10226     lives_widget_object_ref(eventbox);
10227     pname = lives_strdup_printf("achan%d", i);
10228     lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(audio_draw), pname, eventbox);
10229     lives_free(pname);
10230 
10231     widget_opts.justify = LIVES_JUSTIFY_END;
10232     tname = get_achannel_name(mainw->files[mt->render_file]->achans, i);
10233     label = lives_label_new(tname);
10234     lives_free(tname);
10235 
10236     lives_widget_set_halign(label, LIVES_ALIGN_END);
10237 
10238     widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
10239     lives_widget_object_ref(label);
10240 
10241     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "owner", audio_draw);
10242     lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(eventbox), "label", label);
10243     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(0));
10244     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "bgimg", NULL);
10245     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "channel", LIVES_INT_TO_POINTER(i));
10246   }
10247 
10248   lives_widget_queue_draw(mt->vpaned);
10249 
10250   if (!mt->was_undo_redo) {
10251     // calc layer volume value
10252     llen = lives_list_length(mt->audio_draws);
10253     if (llen == 0) {
10254       // set vol to 1.0
10255       vol = LIVES_AVOL_SCALE;
10256     } else if (llen == 1) {
10257       if (mt->opts.back_audio_tracks > 0) {
10258         vol = LIVES_AVOL_SCALE / 2.;
10259         mt->audio_vols->data = LIVES_INT_TO_POINTER(vol);
10260       } else {
10261         if (mt->opts.gang_audio) {
10262           vol = LIVES_POINTER_TO_INT(lives_list_nth_data(mt->audio_vols, 0));
10263         } else vol = LIVES_AVOL_SCALE;
10264       }
10265     } else {
10266       if (mt->opts.gang_audio) {
10267         vol = LIVES_POINTER_TO_INT(lives_list_nth_data(mt->audio_vols, mt->opts.back_audio_tracks));
10268       } else {
10269         if (mt->opts.back_audio_tracks > 0) {
10270           vol = LIVES_AVOL_SCALE / 2.;
10271         } else {
10272           vol = LIVES_AVOL_SCALE;
10273         }
10274       }
10275     }
10276   }
10277 
10278   if (!mt->was_undo_redo && mt->amixer && track >= 0) {
10279     // if mixer is open add space for another slider
10280     LiVESWidget **ch_sliders;
10281     ulong *ch_slider_fns;
10282 
10283     int j = 0;
10284 
10285     nachans = lives_list_length(mt->audio_vols) + 1;
10286 
10287     ch_sliders = (LiVESWidget **)lives_malloc(nachans * sizeof(LiVESWidget *));
10288     ch_slider_fns = (ulong *)lives_malloc(nachans * sizeof(ulong));
10289 
10290     // make a gap
10291     for (i = 0; i < nachans - 1; i++) {
10292       if (!behind && i == mt->opts.back_audio_tracks) j++;
10293       ch_sliders[j] = mt->amixer->ch_sliders[i];
10294       ch_slider_fns[j] = mt->amixer->ch_slider_fns[i];
10295       j++;
10296     }
10297 
10298     lives_free(mt->amixer->ch_sliders);
10299     lives_free(mt->amixer->ch_slider_fns);
10300 
10301     mt->amixer->ch_sliders = ch_sliders;
10302     mt->amixer->ch_slider_fns = ch_slider_fns;
10303   }
10304 
10305   if (track == -1) {
10306     mt->audio_draws = lives_list_prepend(mt->audio_draws, (livespointer)audio_draw);
10307     if (!mt->was_undo_redo) mt->audio_vols = lives_list_prepend(mt->audio_vols, LIVES_INT_TO_POINTER(vol));
10308   } else if (behind) {
10309     mt->audio_draws = lives_list_append(mt->audio_draws, (livespointer)audio_draw);
10310 
10311     if (!mt->was_undo_redo) {
10312       if (mt->amixer) {
10313         // if mixer is open add a new track at end
10314         vbox = amixer_add_channel_slider(mt, nachans - 1 - mt->opts.back_audio_tracks);
10315         lives_box_pack_start(LIVES_BOX(mt->amixer->main_hbox), vbox, FALSE, FALSE, widget_opts.packing_width);
10316         lives_widget_show_all(vbox);
10317       }
10318 
10319       mt->audio_vols = lives_list_append(mt->audio_vols, LIVES_INT_TO_POINTER(vol));
10320     }
10321   } else {
10322     mt->audio_draws = lives_list_insert(mt->audio_draws, (livespointer)audio_draw, mt->opts.back_audio_tracks);
10323     if (!mt->was_undo_redo) {
10324       mt->audio_vols = lives_list_insert(mt->audio_vols, LIVES_INT_TO_POINTER(vol), mt->opts.back_audio_tracks);
10325 
10326       if (mt->amixer) {
10327         // if mixer is open add a new track at posn 0 and update all labels and layer numbers
10328         vbox = amixer_add_channel_slider(mt, 0);
10329 
10330         // pack at posn 2
10331         lives_box_pack_start(LIVES_BOX(mt->amixer->main_hbox), vbox, FALSE, FALSE, widget_opts.packing_width);
10332         lives_box_reorder_child(LIVES_BOX(mt->amixer->main_hbox), vbox, 2);
10333         lives_widget_show_all(vbox);
10334 
10335         // update labels and layer numbers
10336 
10337         for (i = mt->opts.back_audio_tracks + 1; i < nachans; i++) {
10338           label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->amixer->ch_sliders[i]), "label");
10339           tname = get_track_name(mt, i - mt->opts.back_audio_tracks, TRUE);
10340           widget_opts.mnemonic_label = FALSE;
10341           lives_label_set_text(LIVES_LABEL(label), tname);
10342           widget_opts.mnemonic_label = TRUE;
10343           lives_free(tname);
10344 
10345           adj = (LiVESWidgetObject *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->amixer->ch_sliders[i]), "adj");
10346           lives_widget_object_set_data(LIVES_WIDGET_OBJECT(adj), "layer", LIVES_INT_TO_POINTER(i));
10347 	  // *INDENT-OFF*
10348         }}}}
10349   // *INDENT-ON*
10350 
10351   return audio_draw;
10352 }
10353 
10354 
set_track_label(LiVESEventBox * xeventbox,int tnum)10355 static void set_track_label(LiVESEventBox * xeventbox, int tnum) {
10356   LiVESWidget *label = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "label"));
10357   const char *tname = (const char *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "track_name");
10358   char *newtext = lives_strdup_printf(_("%s (layer %d)"), tname, tnum);
10359   widget_opts.mnemonic_label = FALSE;
10360   lives_label_set_text(LIVES_LABEL(label), newtext);
10361   widget_opts.mnemonic_label = TRUE;
10362   lives_free(newtext);
10363 }
10364 
10365 
set_track_label_string(lives_mt * mt,int track,const char * label)10366 void set_track_label_string(lives_mt * mt, int track, const char *label) {
10367   LiVESWidget *ebox = get_eventbox_for_track(mt, track);
10368   if (!ebox) return;
10369   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ebox), "track_name", (livespointer)label);
10370   set_track_label(LIVES_EVENT_BOX(ebox), track);
10371 }
10372 
10373 
add_video_track(lives_mt * mt,boolean behind)10374 static int add_video_track(lives_mt * mt, boolean behind) {
10375   // add another video track to our timeline_table
10376   LiVESWidget *label;
10377   LiVESWidget *checkbutton;
10378   LiVESWidget *arrow;
10379   LiVESWidget *eventbox;  // each track has an eventbox, which we store in LiVESList *video_draws
10380   LiVESWidget *aeventbox; // each track has optionally an associated audio track, which we store in LiVESList *audio_draws
10381   LiVESList *liste;
10382   int max_disp_vtracks = prefs->max_disp_vtracks;
10383   int i;
10384   char *tmp;
10385 
10386   if (mt->audio_draws && mt->audio_draws->data && mt->opts.back_audio_tracks > 0 &&
10387       LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), HIDDEN_KEY)) == 0) {
10388     max_disp_vtracks--;
10389     if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data),
10390                              "expanded"))) max_disp_vtracks -= mainw->files[mt->render_file]->achans;
10391   }
10392 
10393   mt->num_video_tracks++;
10394 
10395 #ifdef ENABLE_GIW
10396   if (!prefs->lamp_buttons) {
10397 #endif
10398     checkbutton = lives_check_button_new();
10399 #ifdef ENABLE_GIW
10400   } else {
10401     checkbutton = giw_led_new();
10402     giw_led_enable_mouse(GIW_LED(checkbutton), TRUE);
10403   }
10404 #endif
10405   lives_widget_object_ref(checkbutton);
10406 
10407   mt->cb_list = lives_list_append(mt->cb_list, checkbutton);
10408 
10409   if (LIVES_IS_WIDGET(checkbutton)) {
10410     lives_widget_set_tooltip_text(checkbutton, _("Select track"));
10411   }
10412 
10413   arrow = lives_arrow_new(LIVES_ARROW_RIGHT, LIVES_SHADOW_OUT);
10414   lives_widget_set_tooltip_text(arrow, _("Show/hide audio"));
10415   lives_widget_object_ref(arrow);
10416 
10417   eventbox = lives_event_box_new();
10418   lives_widget_object_ref(eventbox);
10419 
10420   lives_widget_object_set_data_auto(LIVES_WIDGET_OBJECT(eventbox), "track_name", lives_strdup_printf(_("Video %d"),
10421                                     mt->num_video_tracks));
10422   lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(eventbox), "checkbutton", (livespointer)checkbutton);
10423   lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(eventbox), "arrow", (livespointer)arrow);
10424   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "expanded", LIVES_INT_TO_POINTER(FALSE));
10425 
10426   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "blocks", (livespointer)NULL);
10427   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "block_last", (livespointer)NULL);
10428   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), HIDDEN_KEY, LIVES_INT_TO_POINTER(0));
10429   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "bgimg", NULL);
10430 
10431   lives_adjustment_set_page_size(LIVES_ADJUSTMENT(mt->vadjustment), (double)(max_disp_vtracks > mt->num_video_tracks ?
10432                                  mt->num_video_tracks : max_disp_vtracks));
10433   lives_adjustment_set_upper(LIVES_ADJUSTMENT(mt->vadjustment), (double)(mt->num_video_tracks +
10434                              (int)lives_adjustment_get_page_size(LIVES_ADJUSTMENT(mt->vadjustment))));
10435 
10436   if (!behind) {
10437     // track in front of (above) stack
10438     // shift all rows down
10439     // increment "layer_number"s, change labels
10440     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number", LIVES_INT_TO_POINTER(0));
10441     for (i = 0; i < mt->num_video_tracks - 1; i++) {
10442       char *newtext;
10443       LiVESWidget *xeventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, i);
10444       LiVESWidget *xcheckbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "checkbutton");
10445       LiVESWidget *xarrow = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "arrow");
10446       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "layer_number", LIVES_INT_TO_POINTER(i + 1));
10447       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xcheckbutton), "layer_number", LIVES_INT_TO_POINTER(i + 1));
10448       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xarrow), "layer_number", LIVES_INT_TO_POINTER(i + 1));
10449       lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "is_audio", LIVES_INT_TO_POINTER(FALSE));
10450 
10451       set_track_label(LIVES_EVENT_BOX(xeventbox), i + 1);
10452 
10453       if (mt->opts.pertrack_audio) {
10454         LiVESWidget *aeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "atrack");
10455         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), "layer_number", LIVES_INT_TO_POINTER(i + 1));
10456         xarrow = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "arrow");
10457         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xarrow), "layer_number", LIVES_INT_TO_POINTER(i + 1));
10458         label = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "label");
10459         lives_widget_object_set_data_auto(LIVES_WIDGET_OBJECT(aeventbox), "track_name", lives_strdup_printf(_("Layer %d audio"),
10460                                           i + 1));
10461         newtext = lives_strdup_printf(_(" %s"), lives_widget_object_get_data(LIVES_WIDGET_OBJECT(aeventbox), "track_name"));
10462         widget_opts.mnemonic_label = FALSE;
10463         lives_label_set_text(LIVES_LABEL(label), newtext);
10464         widget_opts.mnemonic_label = TRUE;
10465         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), "is_audio", LIVES_INT_TO_POINTER(TRUE));
10466       }
10467     }
10468     // add a -1,0 in all frame events
10469     // renumber "in_tracks", "out_tracks" in effect_init events
10470     event_list_add_track(mt->event_list, 0);
10471 
10472     mt->video_draws = lives_list_prepend(mt->video_draws, (livespointer)eventbox);
10473     mt->current_track = 0;
10474 
10475     //renumber all tracks in mt->selected_tracks
10476     liste = mt->selected_tracks;
10477     while (liste) {
10478       liste->data = LIVES_INT_TO_POINTER(LIVES_POINTER_TO_INT(liste->data) + 1);
10479       liste = liste->next;
10480     }
10481   } else {
10482     // add track behind (below) stack
10483     mt->video_draws = lives_list_append(mt->video_draws, (livespointer)eventbox);
10484     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number", LIVES_INT_TO_POINTER(mt->num_video_tracks - 1));
10485     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(checkbutton), "layer_number",
10486                                  LIVES_INT_TO_POINTER(mt->num_video_tracks - 1));
10487     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(arrow), "layer_number", LIVES_INT_TO_POINTER(mt->num_video_tracks - 1));
10488     mt->current_track = mt->num_video_tracks - 1;
10489   }
10490 
10491   widget_opts.justify = LIVES_JUSTIFY_START;
10492   label = lives_label_new((tmp = lives_strdup_printf(_("%s (layer %d)"),
10493                                  lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "track_name"),
10494                                  LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox),
10495                                      "layer_number")))));
10496   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
10497 
10498   lives_free(tmp);
10499   lives_widget_object_ref(label);
10500 
10501   lives_widget_object_set_data_widget_object(LIVES_WIDGET_OBJECT(eventbox), "label", label);
10502 
10503   if (mt->opts.pertrack_audio) {
10504     aeventbox = add_audio_track(mt, mt->current_track, behind);
10505     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), "owner", eventbox);
10506     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "atrack", aeventbox);
10507 
10508     if (mt->avol_init_event) {
10509       weed_plant_t *filter = get_weed_filter(mt->avol_fx);
10510       add_track_to_avol_init(filter, mt->avol_init_event, mt->opts.back_audio_tracks, behind);
10511     }
10512   }
10513   if (!behind) scroll_track_on_screen(mt, 0);
10514   else scroll_track_on_screen(mt, mt->num_video_tracks - 1);
10515 
10516   lives_widget_queue_draw(mt->vpaned);
10517 
10518   if (!behind) return 0;
10519   else return mt->num_video_tracks - 1;
10520 }
10521 
10522 
add_video_track_behind(LiVESMenuItem * menuitem,livespointer user_data)10523 int add_video_track_behind(LiVESMenuItem * menuitem, livespointer user_data) {
10524   int tnum;
10525   lives_mt *mt = (lives_mt *)user_data;
10526   if (menuitem) mt_desensitise(mt);
10527   tnum = add_video_track(mt, TRUE);
10528   if (menuitem) mt_sensitise(mt);
10529   return tnum;
10530 }
10531 
10532 
add_video_track_front(LiVESMenuItem * menuitem,livespointer user_data)10533 int add_video_track_front(LiVESMenuItem * menuitem, livespointer user_data) {
10534   int tnum;
10535   lives_mt *mt = (lives_mt *)user_data;
10536   if (menuitem) mt_desensitise(mt);
10537   tnum = add_video_track(mt, FALSE);
10538   if (menuitem) mt_sensitise(mt);
10539   return tnum;
10540 }
10541 
10542 
on_mt_fx_edit_activate(LiVESMenuItem * menuitem,livespointer user_data)10543 void on_mt_fx_edit_activate(LiVESMenuItem * menuitem, livespointer user_data) {
10544   lives_mt *mt = (lives_mt *)user_data;
10545   if (!mt->selected_init_event) return;
10546   fubar(mt);
10547   polymorph(mt, POLY_PARAMS);
10548   mt_show_current_frame(mt, FALSE);
10549   lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
10550   lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
10551 }
10552 
10553 
mt_fx_edit_idle(livespointer user_data)10554 static boolean mt_fx_edit_idle(livespointer user_data) {
10555   on_mt_fx_edit_activate(NULL, user_data);
10556   return FALSE;
10557 }
10558 
10559 
mt_avol_quick(LiVESMenuItem * menuitem,livespointer user_data)10560 static void mt_avol_quick(LiVESMenuItem * menuitem, livespointer user_data) {
10561   lives_mt *mt = (lives_mt *)user_data;
10562   mt->selected_init_event = mt->avol_init_event;
10563   on_mt_fx_edit_activate(menuitem, user_data);
10564 }
10565 
10566 
rdrw_cb(LiVESMenuItem * menuitem,livespointer user_data)10567 static void rdrw_cb(LiVESMenuItem * menuitem, livespointer user_data) {
10568   lives_mt *mt = (lives_mt *)user_data;
10569   redraw_all_event_boxes(mt);
10570 }
10571 
10572 
do_effect_context(lives_mt * mt,LiVESXEventButton * event)10573 void do_effect_context(lives_mt * mt, LiVESXEventButton * event) {
10574   // pop up a context menu when a selected block is right clicked on
10575 
10576   LiVESWidget *edit_effect;
10577   LiVESWidget *delete_effect;
10578   LiVESWidget *menu = lives_menu_new();
10579 
10580   weed_plant_t *filter;
10581   char *fhash;
10582 
10583   lives_menu_set_title(LIVES_MENU(menu), _("Selected Effect"));
10584 
10585   fhash = weed_get_string_value(mt->selected_init_event, WEED_LEAF_FILTER, NULL);
10586   filter = get_weed_filter(weed_get_idx_for_hashname(fhash, TRUE));
10587   lives_free(fhash);
10588 
10589   if (num_in_params(filter, TRUE, TRUE) > 0) {
10590     edit_effect = lives_standard_menu_item_new_with_label(_("_View/Edit this Effect"));
10591   } else {
10592     edit_effect = lives_standard_menu_item_new_with_label(_("_View this Effect"));
10593   }
10594   lives_container_add(LIVES_CONTAINER(menu), edit_effect);
10595 
10596   lives_signal_connect(LIVES_GUI_OBJECT(edit_effect), LIVES_WIDGET_ACTIVATE_SIGNAL,
10597                        LIVES_GUI_CALLBACK(on_mt_fx_edit_activate),
10598                        (livespointer)mt);
10599 
10600   delete_effect = lives_standard_menu_item_new_with_label(_("_Delete this Effect"));
10601   if (mt->selected_init_event != mt->avol_init_event) {
10602     lives_container_add(LIVES_CONTAINER(menu), delete_effect);
10603 
10604     lives_signal_connect(LIVES_GUI_OBJECT(delete_effect), LIVES_WIDGET_ACTIVATE_SIGNAL,
10605                          LIVES_GUI_CALLBACK(on_mt_delfx_activate),
10606                          (livespointer)mt);
10607   }
10608 
10609   if (palette->style & STYLE_1) {
10610     set_child_alt_colour(menu, TRUE);
10611     lives_widget_apply_theme2(menu, LIVES_WIDGET_STATE_NORMAL, TRUE);
10612   }
10613 
10614   lives_widget_show_all(menu);
10615   lives_menu_popup(LIVES_MENU(menu), event);
10616 }
10617 
10618 
fx_ebox_pressed(LiVESWidget * eventbox,LiVESXEventButton * event,livespointer user_data)10619 static boolean fx_ebox_pressed(LiVESWidget * eventbox, LiVESXEventButton * event, livespointer user_data) {
10620   lives_mt *mt = (lives_mt *)user_data;
10621   LiVESList *children, *xlist;
10622   weed_plant_t *osel = mt->selected_init_event;
10623 
10624   if (mt->is_rendering) return FALSE;
10625 
10626   mt->selected_init_event = (weed_plant_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "init_event");
10627 
10628   if (event->type == LIVES_BUTTON2_PRESS) {
10629     // double click
10630     mt->moving_fx = NULL;
10631     if (!LIVES_IS_PLAYING) {
10632       lives_timer_add_simple(0, mt_fx_edit_idle, mt); // work around issue in gtk+
10633     }
10634     return FALSE;
10635   }
10636 
10637   if (!LIVES_IS_PLAYING) {
10638     if (mt->fx_order != FX_ORD_NONE) {
10639       if (osel != mt->selected_init_event && osel != mt->avol_init_event) {
10640         switch (mt->fx_order) {
10641         case FX_ORD_BEFORE:
10642           /// backup events and note timecode of filter map
10643           mt_backup(mt, MT_UNDO_FILTER_MAP_CHANGE, get_event_timecode(mt->fm_edit_event));
10644 
10645           /// move the init event in the filter map
10646           move_init_in_filter_map(mt, mt->event_list, mt->fm_edit_event, osel, mt->selected_init_event,
10647                                   mt->current_track, FALSE);
10648           break;
10649         case FX_ORD_AFTER:
10650           if (init_event_is_process_last(mt->selected_init_event)) {
10651             // cannot insert after a process_last effect
10652             clear_context(mt);
10653             add_context_label(mt, _("Cannot insert after this effect"));
10654             mt->selected_init_event = osel;
10655             mt->fx_order = FX_ORD_NONE;
10656             return FALSE;
10657           }
10658 
10659           /// backup events and note timecode of filter map
10660           mt_backup(mt, MT_UNDO_FILTER_MAP_CHANGE, get_event_timecode(mt->fm_edit_event));
10661 
10662           /// move the init event in the filter map
10663           move_init_in_filter_map(mt, mt->event_list, mt->fm_edit_event, osel, mt->selected_init_event,
10664                                   mt->current_track, TRUE);
10665           break;
10666 
10667         default:
10668           break;
10669         }
10670       }
10671 
10672       mt->did_backup = FALSE;
10673       mt->selected_init_event = osel;
10674       mt->fx_order = FX_ORD_NONE;
10675       mt->selected_init_event = NULL;
10676       polymorph(mt, POLY_FX_STACK);
10677       mt_show_current_frame(mt, FALSE); ///< show updated preview
10678       return FALSE;
10679     }
10680     if (mt->fx_order == FX_ORD_NONE && WEED_EVENT_IS_FILTER_MAP(mt->fm_edit_event)) {
10681       if (init_event_is_process_last(mt->selected_init_event)) {
10682         clear_context(mt);
10683         add_context_label(mt, _("This effect cannot be moved"));
10684         if (mt->fx_ibefore_button) lives_widget_set_sensitive(mt->fx_ibefore_button, FALSE);
10685         if (mt->fx_iafter_button) lives_widget_set_sensitive(mt->fx_iafter_button, FALSE);
10686       } else {
10687         do_fx_move_context(mt);
10688         if (mt->fx_ibefore_button) lives_widget_set_sensitive(mt->fx_ibefore_button, TRUE);
10689         if (mt->fx_iafter_button) lives_widget_set_sensitive(mt->fx_iafter_button, TRUE);
10690       }
10691     }
10692     lives_widget_set_sensitive(mt->fx_edit, TRUE);
10693     if (mt->selected_init_event != mt->avol_init_event) lives_widget_set_sensitive(mt->fx_delete, TRUE);
10694   }
10695 
10696   if (widget_opts.apply_theme) {
10697     // set clicked-on widget to selected state and reset all others
10698     xlist = children = lives_container_get_children(LIVES_CONTAINER(mt->fx_list_vbox));
10699     while (children) {
10700       LiVESWidget *child = (LiVESWidget *)children->data;
10701       if (child != eventbox) {
10702         lives_widget_apply_theme(child, LIVES_WIDGET_STATE_NORMAL);
10703         set_child_colour(child, TRUE);
10704       } else {
10705         lives_widget_apply_theme2(child, LIVES_WIDGET_STATE_NORMAL, TRUE);
10706         set_child_alt_colour(child, TRUE);
10707       }
10708       children = children->next;
10709     }
10710     if (xlist) lives_list_free(xlist);
10711   }
10712   if (event->button == 3 && !LIVES_IS_PLAYING) {
10713     do_effect_context(mt, event);
10714   }
10715 
10716   return FALSE;
10717 }
10718 
10719 
set_clip_labels_variable(lives_mt * mt,int i)10720 static void set_clip_labels_variable(lives_mt * mt, int i) {
10721   char *tmp;
10722   LiVESLabel *label1, *label2;
10723   lives_clip_t *sfile = mainw->files[i];
10724 
10725   if (!mt->clip_labels) return;
10726 
10727   i = mt_clip_from_file(mt, i);
10728   i *= 2;
10729 
10730   label1 = (LiVESLabel *)lives_list_nth_data(mt->clip_labels, i);
10731   label2 = (LiVESLabel *)lives_list_nth_data(mt->clip_labels, ++i);
10732 
10733   // sets the width of the box
10734   lives_label_set_text(label1, (tmp = lives_strdup_printf(_("%d to %d selected"), sfile->start, sfile->end)));
10735   lives_free(tmp);
10736 
10737   lives_label_set_text(label2, (tmp = lives_strdup_printf(_("%.2f sec."), (sfile->end - sfile->start + 1.) / sfile->fps)));
10738   lives_free(tmp);
10739 }
10740 
10741 
mt_clear_timeline(lives_mt * mt)10742 void mt_clear_timeline(lives_mt * mt) {
10743   int i;
10744   char *msg;
10745 
10746   mainw->no_configs = TRUE;
10747 
10748   for (i = 0; i < mt->num_video_tracks; i++) {
10749     delete_video_track(mt, i, FALSE);
10750   }
10751   lives_list_free(mt->video_draws);
10752   mt->video_draws = NULL;
10753   mt->num_video_tracks = 0;
10754   mainw->event_list = mt->event_list = NULL;
10755 
10756   if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
10757   mt->selected_tracks = NULL;
10758 
10759   if (mt->amixer) on_amixer_close_clicked(NULL, mt);
10760 
10761   delete_audio_tracks(mt, mt->audio_draws, FALSE);
10762   mt->audio_draws = NULL;
10763   if (mt->audio_vols) lives_list_free(mt->audio_vols);
10764   mt->audio_vols = NULL;
10765 
10766   mt_init_tracks(mt, TRUE);
10767 
10768   unselect_all(mt);
10769   mt->changed = FALSE;
10770 
10771   msg = set_values_from_defs(mt, FALSE);
10772 
10773   if (mainw->files[mt->render_file]->achans == 0) mt->opts.pertrack_audio = FALSE;
10774 
10775   if (msg) {
10776     d_print(msg);
10777     lives_free(msg);
10778     set_mt_title(mt);
10779   }
10780 
10781   // reset avol_fx
10782   if (mainw->files[mt->render_file]->achans > 0 && mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].delegate != -1) {
10783     // user (or system) has delegated an audio volume filter from the candidates
10784     mt->avol_fx = LIVES_POINTER_TO_INT(lives_list_nth_data(mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].list,
10785                                        mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].delegate));
10786   } else mt->avol_fx = -1;
10787   mt->avol_init_event = NULL;
10788 
10789   add_aparam_menuitems(mt);
10790 
10791   lives_memset(mt->layout_name, 0, 1);
10792 
10793   clear_widget_bg(mainw->play_image, mainw->play_surface);
10794 
10795   mt_show_current_frame(mt, FALSE);
10796   mainw->no_configs = FALSE;
10797 }
10798 
10799 
mt_delete_clips(lives_mt * mt,int file)10800 void mt_delete_clips(lives_mt * mt, int file) {
10801   // close eventbox(es) for a given file
10802   LiVESList *list = lives_container_get_children(LIVES_CONTAINER(mt->clip_inner_box)), *list_next, *olist = list;
10803   LiVESList *clist = mt->clip_labels, *clist_nnext;
10804 
10805   boolean removed = FALSE;
10806   int neg = 0, i = 0;
10807 
10808   for (; list; i++) {
10809     list_next = list->next;
10810 
10811     // each clip has 2 labels
10812     clist_nnext = clist->next->next;
10813     if (clips_to_files[i] == file) {
10814       removed = TRUE;
10815 
10816       lives_widget_destroy((LiVESWidget *)list->data);
10817 
10818       if (clist_nnext) clist_nnext->prev = clist->prev;
10819       if (clist->prev) clist->prev->next = clist_nnext;
10820       else mt->clip_labels = clist_nnext;
10821       clist->prev = clist->next->next = NULL;
10822       lives_list_free(clist);
10823 
10824       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
10825 
10826       neg++;
10827     }
10828     clips_to_files[i] = clips_to_files[i + neg];
10829     list = list_next;
10830     clist = clist_nnext;
10831     if (mt->file_selected == file) {
10832       mt_prevclip(NULL, NULL, 0, 0, mt);
10833     }
10834   }
10835 
10836   if (olist) lives_list_free(olist);
10837 
10838   if (mt->event_list && removed && used_in_current_layout(mt, file)) {
10839     int current_file = mainw->current_file;
10840 
10841     if (!event_list_rectify(mt, mt->event_list) || !get_first_event(mt->event_list)) {
10842       // delete the current layout
10843       mainw->current_file = mt->render_file;
10844       remove_current_from_affected_layouts(mt);
10845       mainw->current_file = current_file;
10846     } else {
10847       mainw->current_file = mt->render_file;
10848       mt_init_tracks(mt, FALSE);
10849       mainw->current_file = current_file;
10850     }
10851   }
10852 
10853   if (CURRENT_CLIP_IS_VALID) {
10854     lives_widget_set_sensitive(mt->adjust_start_end, FALSE);
10855   }
10856 }
10857 
10858 
mt_init_clips(lives_mt * mt,int orig_file,boolean add)10859 void mt_init_clips(lives_mt * mt, int orig_file, boolean add) {
10860   // setup clip boxes in the poly window. if add is TRUE then we are just adding a new clip
10861   // orig_file is the file we want to select
10862 
10863   // mt_clip_select() should be called after this
10864 
10865   LiVESWidget *thumb_image = NULL;
10866   LiVESWidget *vbox, *hbox, *label;
10867   LiVESWidget *eventbox;
10868 
10869   LiVESPixbuf *thumbnail;
10870 
10871   LiVESList *cliplist = mainw->cliplist;
10872 
10873   char filename[PATH_MAX];
10874   char clip_name[CLIP_LABEL_LENGTH];
10875   char *tmp;
10876 
10877   int i = 1;
10878   int width = CLIP_THUMB_WIDTH, height = CLIP_THUMB_HEIGHT;
10879   int count = lives_list_length(mt->clip_labels) / 2;
10880 
10881   mt->clip_selected = -1;
10882 
10883   if (add) i = orig_file;
10884 
10885   while (add || cliplist) {
10886     if (add) i = orig_file;
10887     else i = LIVES_POINTER_TO_INT(cliplist->data);
10888     if (0 && !IS_NORMAL_CLIP(i)) {
10889       if (cliplist) {
10890         cliplist = cliplist->next;
10891         continue;
10892       } else break;
10893     }
10894     if (i != mainw->scrap_file && i != mainw->ascrap_file) {
10895       if (i == orig_file || (mt->clip_selected == -1 && i == mainw->pre_src_file)) {
10896         if (!add) mt->clip_selected = mt_clip_from_file(mt, i);
10897         else {
10898           mt->file_selected = i;
10899           mt->clip_selected = count;
10900           renumbered_clips[i] = i;
10901         }
10902         if (mainw->files[i]->tcache_dubious_from > 0)
10903           free_thumb_cache(i, mainw->files[i]->tcache_dubious_from);
10904       }
10905       // remove the "no clips" label
10906       if (mt->nb_label) lives_widget_destroy(mt->nb_label);
10907       mt->nb_label = NULL;
10908 
10909       // make a small thumbnail, add it to the clips box
10910       thumbnail = make_thumb(mt, i, width, height, mainw->files[i]->start, LIVES_INTERP_BEST, TRUE);
10911 
10912       eventbox = lives_event_box_new();
10913       lives_widget_add_events(eventbox, LIVES_BUTTON_RELEASE_MASK | LIVES_BUTTON_PRESS_MASK | LIVES_ENTER_NOTIFY_MASK);
10914       lives_signal_sync_connect(LIVES_GUI_OBJECT(eventbox), LIVES_WIDGET_ENTER_EVENT, LIVES_GUI_CALLBACK(on_clipbox_enter),
10915                                 (livespointer)mt);
10916 
10917       clips_to_files[count] = i;
10918 
10919       vbox = lives_vbox_new(FALSE, widget_opts.packing_height);
10920 
10921       thumb_image = lives_image_new();
10922       lives_image_set_from_pixbuf(LIVES_IMAGE(thumb_image), thumbnail);
10923       if (thumbnail) lives_widget_object_unref(thumbnail);
10924       lives_container_add(LIVES_CONTAINER(eventbox), vbox);
10925       lives_box_pack_start(LIVES_BOX(mt->clip_inner_box), eventbox, FALSE, FALSE, 0);
10926 
10927       lives_snprintf(filename, PATH_MAX, "%s", (tmp = get_menu_name(mainw->files[i], FALSE)));
10928       lives_free(tmp);
10929       get_basename(filename);
10930       lives_snprintf(clip_name, CLIP_LABEL_LENGTH, "%s", filename);
10931 
10932       widget_opts.justify = LIVES_JUSTIFY_CENTER;
10933       label = lives_standard_label_new(clip_name);
10934       lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
10935       lives_box_pack_start(LIVES_BOX(vbox), thumb_image, FALSE, FALSE, widget_opts.packing_height);
10936 
10937       if (mainw->files[i]->frames > 0) {
10938         label = lives_standard_label_new((tmp = lives_strdup_printf(_("%d frames"), mainw->files[i]->frames)));
10939         lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
10940 
10941         label = lives_standard_label_new("");
10942         mt->clip_labels = lives_list_append(mt->clip_labels, label);
10943 
10944         hbox = lives_hbox_new(FALSE, 0);
10945         lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
10946         lives_box_pack_start(LIVES_BOX(hbox), label, FALSE, TRUE, widget_opts.border_width);
10947 
10948         label = lives_standard_label_new("");
10949         mt->clip_labels = lives_list_append(mt->clip_labels, label);
10950 
10951         lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
10952 
10953         set_clip_labels_variable(mt, i);
10954       } else {
10955         label = lives_standard_label_new(_("audio only"));
10956         mt->clip_labels = lives_list_append(mt->clip_labels, label);
10957 
10958         hbox = lives_hbox_new(FALSE, 0);
10959         lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
10960         lives_box_pack_start(LIVES_BOX(hbox), label, FALSE, TRUE, widget_opts.border_width);
10961 
10962         label = lives_standard_label_new((tmp = lives_strdup_printf(_("%.2f sec."), mainw->files[i]->laudio_time)));
10963         lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
10964         mt->clip_labels = lives_list_append(mt->clip_labels, label);
10965       }
10966       lives_free(tmp);
10967       widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
10968 
10969       count++;
10970 
10971       lives_signal_sync_connect(LIVES_GUI_OBJECT(eventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
10972                                 LIVES_GUI_CALLBACK(clip_ebox_pressed), (livespointer)mt);
10973       lives_signal_sync_connect(LIVES_GUI_OBJECT(eventbox), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
10974                                 LIVES_GUI_CALLBACK(on_drag_clip_end), (livespointer)mt);
10975       if (add) {
10976         lives_widget_set_no_show_all(mt->poly_box, FALSE);
10977         lives_widget_show_all(mt->poly_box);
10978         lives_widget_show_all(eventbox);
10979         break;
10980       }
10981     }
10982     if (cliplist) cliplist = cliplist->next;
10983   }
10984 }
10985 
10986 
set_audio_mixer_vols(lives_mt * mt,weed_plant_t * elist)10987 static void set_audio_mixer_vols(lives_mt * mt, weed_plant_t *elist) {
10988   int *atracks;
10989   double *avols;
10990   int catracks, xtrack, xavol, natracks, navols;
10991 
10992   atracks = weed_get_int_array_counted(elist, WEED_LEAF_AUDIO_VOLUME_TRACKS, &natracks);
10993   if (!atracks) return;
10994   avols = weed_get_double_array_counted(elist, WEED_LEAF_AUDIO_VOLUME_VALUES, &navols);
10995   if (!avols) return;
10996 
10997   catracks = lives_list_length(mt->audio_vols);
10998   for (int i = 0; i < natracks; i++) {
10999     xtrack = atracks[i];
11000     if (xtrack < -mt->opts.back_audio_tracks) continue;
11001     if (xtrack >= catracks - mt->opts.back_audio_tracks) continue;
11002 
11003     xavol = i;
11004     if (xavol >= navols) {
11005       mt->opts.gang_audio = TRUE;
11006       xavol = navols - 1;
11007     }
11008     set_mixer_track_vol(mt, xtrack + mt->opts.back_audio_tracks, avols[xavol]);
11009   }
11010 
11011   lives_free(atracks);
11012   lives_free(avols);
11013 }
11014 
mt_idle_show_current_frame(livespointer data)11015 boolean mt_idle_show_current_frame(livespointer data) {
11016   lives_mt *mt = (lives_mt *)data;
11017   if (!mainw->multitrack) return FALSE;
11018   mt_tl_move(mt, mt->opts.ptr_time);
11019   lives_widget_queue_draw(mainw->play_image);
11020   return FALSE;
11021 }
11022 
11023 
on_multitrack_activate(LiVESMenuItem * menuitem,weed_plant_t * event_list)11024 boolean on_multitrack_activate(LiVESMenuItem * menuitem, weed_plant_t *event_list) {
11025   //returns TRUE if we go into mt mode
11026   lives_mt *multi;
11027   boolean response;
11028   int orig_file;
11029 
11030   xachans = xarate = xasamps = xse = 0;
11031   ptaud = prefs->mt_pertrack_audio;
11032   btaud = prefs->mt_backaudio;
11033 
11034   if (mainw->frame_layer) weed_layer_free(mainw->frame_layer);
11035   mainw->frame_layer = NULL;
11036 
11037   if (prefs->mt_enter_prompt && !mainw->stored_event_list && prefs->show_gui && !(mainw->recoverable_layout &&
11038       prefs->startup_interface == STARTUP_CE)) {
11039     // WARNING:
11040     rdet = create_render_details(3); // WARNING !! - rdet is global in events.h
11041     rdet->enc_changed = FALSE;
11042     if (prefs->startup_interface == STARTUP_MT && !mainw->is_ready) {
11043       if (prefs->show_gui) {
11044         lives_widget_show_now(rdet->dialog);
11045         lives_window_present(LIVES_WINDOW(rdet->dialog));
11046         lives_widget_grab_focus(rdet->okbutton);
11047       }
11048     }
11049     do {
11050       rdet->suggestion_followed = FALSE;
11051       if ((response = lives_dialog_run(LIVES_DIALOG(rdet->dialog))) == LIVES_RESPONSE_OK) {
11052         if (rdet->enc_changed) {
11053           check_encoder_restrictions(FALSE, lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton)), TRUE);
11054         }
11055       }
11056     } while (rdet->suggestion_followed || response == LIVES_RESPONSE_RESET);
11057 
11058     if (response == LIVES_RESPONSE_CANCEL) {
11059       lives_widget_destroy(rdet->dialog);
11060       lives_free(rdet->encoder_name);
11061       lives_freep((void **)&rdet);
11062       lives_freep((void **)&resaudw);
11063       return FALSE;
11064     }
11065 
11066     lives_set_cursor_style(LIVES_CURSOR_BUSY, LIVES_MAIN_WINDOW_WIDGET);
11067 
11068     if (resaudw) {
11069       xarate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
11070       xachans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
11071       xasamps = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
11072 
11073       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned))) {
11074         xse = AFORM_UNSIGNED;
11075       }
11076 
11077       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))) {
11078         xse |= AFORM_BIG_ENDIAN;
11079       }
11080 
11081       if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton))) {
11082         xachans = 0;
11083       }
11084 
11085       ptaud = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rdet->pertrack_checkbutton));
11086       btaud = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rdet->backaudio_checkbutton));
11087     } else {
11088       xarate = xachans = xasamps = 0;
11089       xse = cfile->signed_endian;
11090     }
11091 
11092     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rdet->always_checkbutton))) {
11093       prefs->mt_enter_prompt = FALSE;
11094       set_boolean_pref(PREF_MT_ENTER_PROMPT, prefs->mt_enter_prompt);
11095       prefs->mt_def_width = rdet->width;
11096       set_int_pref(PREF_MT_DEF_WIDTH, prefs->mt_def_width);
11097       prefs->mt_def_height = rdet->height;
11098       set_int_pref(PREF_MT_DEF_HEIGHT, prefs->mt_def_height);
11099       prefs->mt_def_fps = rdet->fps;
11100       set_double_pref(PREF_MT_DEF_FPS, prefs->mt_def_fps);
11101       prefs->mt_def_arate = xarate;
11102       set_int_pref(PREF_MT_DEF_ARATE, prefs->mt_def_arate);
11103       prefs->mt_def_achans = xachans;
11104       set_int_pref(PREF_MT_DEF_ACHANS, prefs->mt_def_achans);
11105       prefs->mt_def_asamps = xasamps;
11106       set_int_pref(PREF_MT_DEF_ASAMPS, prefs->mt_def_asamps);
11107       prefs->mt_def_signed_endian = xse;
11108       set_int_pref(PREF_MT_DEF_SIGNED_ENDIAN, prefs->mt_def_signed_endian);
11109       prefs->mt_pertrack_audio = ptaud;
11110       set_boolean_pref(PREF_MT_PERTRACK_AUDIO, prefs->mt_pertrack_audio);
11111       prefs->mt_backaudio = btaud;
11112       set_int_pref(PREF_MT_BACKAUDIO, prefs->mt_backaudio);
11113     } else {
11114       if (!prefs->mt_enter_prompt) {
11115         prefs->mt_enter_prompt = TRUE;
11116         set_boolean_pref(PREF_MT_ENTER_PROMPT, prefs->mt_enter_prompt);
11117       }
11118     }
11119 
11120     lives_widget_destroy(rdet->dialog);
11121   }
11122 
11123   if (CURRENT_CLIP_IS_VALID && cfile->clip_type == CLIP_TYPE_GENERATOR) {
11124     // shouldn't be playing, so OK to just call this
11125     weed_generator_end((weed_plant_t *)cfile->ext_src);
11126   }
11127 
11128   /* if (prefs->show_gui) { */
11129   /*   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET); // force showing of transient window */
11130   /* } */
11131 
11132   // create new file for rendering to
11133   renumber_clips();
11134   orig_file = mainw->current_file;
11135   mainw->current_file = mainw->first_free_file;
11136 
11137   if (!get_new_handle(mainw->current_file, NULL)) {
11138     mainw->current_file = orig_file;
11139     if (rdet) {
11140       lives_free(rdet->encoder_name);
11141       lives_freep((void **)&rdet);
11142       lives_freep((void **)&resaudw);
11143     }
11144     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
11145     return FALSE; // show dialog again
11146   }
11147 
11148   cfile->img_type = IMG_TYPE_BEST; // override the pref
11149 
11150   cfile->bpp = cfile->img_type == IMG_TYPE_JPEG ? 24 : 32;
11151   cfile->changed = TRUE;
11152   cfile->is_loaded = TRUE;
11153 
11154   cfile->old_frames = cfile->frames;
11155 
11156   force_pertrack_audio = FALSE;
11157   force_backing_tracks = 0;
11158 
11159   if (mainw->stored_event_list) {
11160     event_list = mainw->stored_event_list;
11161     rerenumber_clips(NULL, event_list);
11162   }
11163 
11164   // if we have an existing event list, we will quantise it to the selected fps
11165   if (event_list) {
11166     weed_plant_t *qevent_list = quantise_events(event_list, cfile->fps, FALSE);
11167     if (!qevent_list) {
11168       if (rdet) {
11169         lives_free(rdet->encoder_name);
11170         lives_freep((void **)&rdet);
11171         lives_freep((void **)&resaudw);
11172       }
11173       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
11174       return FALSE; // memory error
11175     }
11176     event_list_replace_events(event_list, qevent_list);
11177     weed_set_double_value(event_list, WEED_LEAF_FPS, cfile->fps);
11178     event_list_rectify(NULL, event_list);
11179   } else prefs->letterbox_mt = future_prefs->letterbox_mt;
11180 
11181   prefs->msg_textsize = future_prefs->msg_textsize;
11182 
11183   ///////// CREATE MULTITRACK CONTENTS ////////////
11184   multi = multitrack(event_list, orig_file, cfile->fps); // also frees rdet
11185   ////////////////////////////////////////////////
11186 
11187   pref_factory_int(PREF_SEPWIN_TYPE, SEPWIN_TYPE_NON_STICKY, FALSE);
11188 
11189   if (mainw->stored_event_list) {
11190     mainw->stored_event_list = NULL;
11191     mainw->stored_layout_undos = NULL;
11192     mainw->sl_undo_mem = NULL;
11193     stored_event_list_free_all(FALSE);
11194     if (!multi->event_list) {
11195       multi->clip_selected = mt_clip_from_file(multi, orig_file);
11196       multi->file_selected = orig_file;
11197       //mainw->sw_func = mainw_sw_func;
11198       if (prefs->show_msg_area) {
11199         mainw->message_box = mainw_message_box;
11200         mainw->msg_area = mainw_msg_area;
11201         mainw->msg_adj = mainw_msg_adj;
11202         mainw->msg_scrollbar = mainw_msg_scrollbar;
11203       }
11204       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
11205       return FALSE;
11206     }
11207     remove_markers(multi->event_list);
11208     set_audio_mixer_vols(multi, multi->event_list);
11209     lives_snprintf(multi->layout_name, 256, "%s", mainw->stored_layout_name);
11210     multi->changed = mainw->stored_event_list_changed;
11211     multi->auto_changed = mainw->stored_event_list_auto_changed;
11212     if (multi->auto_changed) lives_widget_set_sensitive(multi->backup, TRUE);
11213   }
11214 
11215   if (mainw->recoverable_layout && !multi->event_list && prefs->startup_interface == STARTUP_CE) {
11216     // failed to load recovery layout
11217     multi->clip_selected = mt_clip_from_file(multi, orig_file);
11218     multi->file_selected = orig_file;
11219     if (prefs->show_msg_area) {
11220       mainw->message_box = mainw_message_box;
11221       mainw->msg_area = mainw_msg_area;
11222       mainw->msg_adj = mainw_msg_adj;
11223       mainw->msg_scrollbar = mainw->msg_scrollbar;
11224     }
11225     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
11226     return FALSE;
11227   }
11228 
11229   if (prefs->show_gui) {
11230     lives_widget_object_ref(mainw->top_vbox);
11231     lives_widget_unparent(mainw->top_vbox);
11232   }
11233 
11234   lives_container_add(LIVES_CONTAINER(LIVES_MAIN_WINDOW_WIDGET), multi->top_vbox);
11235 
11236   if (prefs->show_gui) {
11237     lives_widget_show_all(multi->top_vbox);
11238     if (multi->clip_labels) {
11239       lives_widget_set_no_show_all(multi->poly_box, FALSE);
11240       lives_widget_show_all(multi->poly_box);
11241     }
11242     show_lives();
11243     scroll_track_on_screen(multi, 0);
11244     if (multi->nb_label) {
11245       lives_widget_hide(multi->poly_box);
11246       lives_widget_queue_resize(multi->nb_label);
11247     }
11248   }
11249 
11250   if (cfile->achans == 0) {
11251     multi->opts.pertrack_audio = FALSE;
11252   }
11253 
11254   if (!is_realtime_aplayer(prefs->audio_player)) {
11255     lives_widget_hide(mainw->vol_toolitem);
11256     if (mainw->vol_label) lives_widget_hide(mainw->vol_label);
11257   }
11258 
11259   if (!prefs->mt_show_ctx) {
11260     lives_widget_hide(multi->context_frame);
11261   }
11262 
11263   if (!(palette->style & STYLE_4)) {
11264     lives_widget_hide(multi->hseparator);
11265     if (multi->hseparator2) {
11266       lives_widget_hide(multi->hseparator2);
11267     }
11268   }
11269 
11270   if (!multi->opts.pertrack_audio) {
11271     lives_widget_hide(multi->insa_checkbutton);
11272   }
11273 
11274   track_select(multi);
11275 
11276   if (mainw->preview_box && lives_widget_get_parent(mainw->preview_box)) {
11277     lives_widget_object_unref(mainw->preview_box);
11278     lives_container_remove(LIVES_CONTAINER(mainw->play_window), mainw->preview_box);
11279     mainw->preview_box = NULL;
11280   }
11281 
11282   if (mainw->play_window) {
11283     lives_window_remove_accel_group(LIVES_WINDOW(mainw->play_window), mainw->accel_group);
11284     lives_window_add_accel_group(LIVES_WINDOW(mainw->play_window), multi->accel_group);
11285     resize_play_window();
11286   }
11287 
11288   if (cfile->achans > 0 && !is_realtime_aplayer(prefs->audio_player)) {
11289     do_mt_no_jack_error(WARN_MASK_MT_NO_JACK);
11290   }
11291 
11292   mt_zoom(multi, 1.);
11293 
11294   mainw->is_ready = TRUE;
11295 
11296   if (prefs->show_gui && prefs->open_maximised) {
11297     int bx, by;
11298     get_border_size(LIVES_MAIN_WINDOW_WIDGET, &bx, &by);
11299     if (by > MENU_HIDE_LIM)
11300       lives_window_set_hide_titlebar_when_maximized(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), TRUE);
11301     lives_window_maximize(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET));
11302   }
11303 
11304   reset_mt_play_sizes(multi);
11305   redraw_all_event_boxes(multi);
11306 
11307   if (multi->opts.pertrack_audio) {
11308     lives_toggle_button_toggle(LIVES_TOGGLE_BUTTON(multi->insa_checkbutton));
11309   }
11310   lives_toggle_button_toggle(LIVES_TOGGLE_BUTTON(multi->snapo_checkbutton));
11311 
11312   mt_clip_select(multi, TRUE); // call this again to scroll clip on screen
11313 
11314   lives_toggle_button_toggle(LIVES_TOGGLE_BUTTON(multi->insa_checkbutton));
11315   lives_toggle_button_toggle(LIVES_TOGGLE_BUTTON(multi->snapo_checkbutton));
11316 
11317   multi->no_expose = multi->no_expose_frame = FALSE;
11318 
11319   lives_container_child_set_shrinkable(LIVES_CONTAINER(multi->hpaned), multi->context_frame, TRUE);
11320 
11321   if (prefs->audio_src == AUDIO_SRC_EXT) {
11322     // switch to internal audio for multitrack
11323     pref_factory_bool(PREF_REC_EXT_AUDIO, FALSE, FALSE);
11324   }
11325 
11326   if (prefs->show_msg_area) {
11327     if (mainw->idlemax == 0)
11328       lives_idle_add_simple(resize_message_area, NULL);
11329     mainw->idlemax = DEF_IDLE_MAX;
11330   }
11331 
11332   lives_idle_add_simple(mt_idle_show_current_frame, (livespointer)multi);
11333   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
11334 
11335   if (prefs->show_gui && prefs->open_maximised) {
11336     int bx, by;
11337     get_border_size(LIVES_MAIN_WINDOW_WIDGET, &bx, &by);
11338     if (by > MENU_HIDE_LIM)
11339       lives_window_set_hide_titlebar_when_maximized(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), TRUE);
11340     lives_window_maximize(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET));
11341   }
11342 
11343   if (multi->opts.hpaned_pos != -1)
11344     lives_paned_set_position(LIVES_PANED(multi->hpaned), multi->opts.hpaned_pos);
11345   else
11346     lives_paned_set_position(LIVES_PANED(multi->hpaned), (float)GUI_SCREEN_WIDTH / 2.);
11347 
11348   lives_signal_connect(LIVES_GUI_OBJECT(multi->hpaned), LIVES_WIDGET_NOTIFY_SIGNAL "position",
11349                        LIVES_GUI_CALLBACK(hpaned_position), (livespointer)multi);
11350 
11351   lives_paned_set_position(LIVES_PANED(multi->top_vpaned), GUI_SCREEN_HEIGHT * 2 / 3);
11352 
11353   if (prefs->startup_interface == STARTUP_MT && mainw->go_away) {
11354     return FALSE;
11355   }
11356 
11357   set_interactive(prefs->interactive);
11358 
11359   if (mainw->reconfig) return FALSE;
11360 
11361   d_print(_("====== Switched to Multitrack mode ======\n"));
11362 
11363   lives_notify_int(LIVES_OSC_NOTIFY_MODE_CHANGED, STARTUP_MT);
11364 
11365   return TRUE;
11366 }
11367 
11368 
block_overlap(LiVESWidget * eventbox,double time_start,double time_end)11369 boolean block_overlap(LiVESWidget * eventbox, double time_start, double time_end) {
11370   weed_timecode_t tc_start = time_start * TICKS_PER_SECOND;
11371   weed_timecode_t tc_end = time_end * TICKS_PER_SECOND;
11372   track_rect *block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
11373 
11374   while (block) {
11375     if (get_event_timecode(block->start_event) > tc_end) return FALSE;
11376     if (get_event_timecode(block->end_event) >= tc_start) return TRUE;
11377     block = block->next;
11378   }
11379   return FALSE;
11380 }
11381 
11382 
get_block_before(LiVESWidget * eventbox,double time,boolean allow_cur)11383 static track_rect *get_block_before(LiVESWidget * eventbox, double time, boolean allow_cur) {
11384   // get the last block which ends before or at time
11385   // if allow_cur is TRUE, we may count blocks whose end is after "time" but whose start is
11386   // before or at time
11387 
11388   weed_timecode_t tc = time * TICKS_PER_SECOND;
11389   track_rect *block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks"), *last_block = NULL;
11390 
11391   while (block) {
11392     if ((allow_cur && get_event_timecode(block->start_event) >= tc) || (!allow_cur &&
11393         get_event_timecode(block->end_event) >= tc)) break;
11394     last_block = block;
11395     block = block->next;
11396   }
11397   return last_block;
11398 }
11399 
11400 
get_block_after(LiVESWidget * eventbox,double time,boolean allow_cur)11401 static track_rect *get_block_after(LiVESWidget * eventbox, double time, boolean allow_cur) {
11402   // return the first block which starts at or after time
11403   // if allow_cur is TRUE, we may count blocks whose end is after "time" but whose start is
11404   // before or at time
11405 
11406   weed_timecode_t tc = time * TICKS_PER_SECOND;
11407   track_rect *block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
11408 
11409   while (block) {
11410     if (get_event_timecode(block->start_event) >= tc || (allow_cur && get_event_timecode(block->end_event) >= tc)) break;
11411     block = block->next;
11412   }
11413   return block;
11414 }
11415 
11416 
move_block(lives_mt * mt,track_rect * block,double timesecs,int old_track,int new_track)11417 track_rect *move_block(lives_mt * mt, track_rect * block, double timesecs, int old_track, int new_track) {
11418   weed_timecode_t new_start_tc, end_tc;
11419   weed_timecode_t start_tc = get_event_timecode(block->start_event);
11420 
11421   ulong uid = block->uid;
11422 
11423   LiVESWidget *eventbox, *oeventbox;
11424 
11425   int clip, current_track = -1;
11426 
11427   boolean did_backup = mt->did_backup;
11428   boolean needs_idlefunc = FALSE;
11429 
11430   if (mt->idlefunc > 0) {
11431     needs_idlefunc = TRUE;
11432     lives_source_remove(mt->idlefunc);
11433     mt->idlefunc = 0;
11434   }
11435 
11436   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
11437   //lives_widget_context_update();
11438 
11439   if (is_audio_eventbox(block->eventbox) && (oeventbox =
11440         (LiVESWidget *)lives_widget_object_get_data
11441         (LIVES_WIDGET_OBJECT(block->eventbox), "owner")) != NULL) {
11442     // if moving an audio block we move the associated video block first
11443     block = get_block_from_time(oeventbox, start_tc / TICKS_PER_SECOND_DBL, mt);
11444   }
11445 
11446   mt->block_selected = block;
11447   end_tc = get_event_timecode(block->end_event);
11448 
11449   if (mt->opts.insert_mode == INSERT_MODE_NORMAL) {
11450     // first check if there is space to move the block to, otherwise we will abort the move
11451     weed_plant_t *event = NULL;
11452     weed_timecode_t tc = 0, tcnow;
11453     weed_timecode_t tclen = end_tc - start_tc;
11454     while (tc <= tclen) {
11455       tcnow = q_gint64(tc + timesecs * TICKS_PER_SECOND_DBL, mt->fps);
11456       tc += TICKS_PER_SECOND_DBL / mt->fps;
11457       if (old_track == new_track && tcnow >= start_tc && tcnow <= end_tc) continue; // ignore ourself !
11458       event = get_frame_event_at(mt->event_list, tcnow, event, TRUE);
11459       if (!event) break; // must be end of timeline
11460       if (new_track >= 0) {
11461         // is video track, if we have a non-blank frame, abort
11462         if (get_frame_event_clip(event, new_track) >= 0) {
11463           if (!did_backup) {
11464             if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
11465           }
11466           return NULL;
11467         }
11468       } else {
11469         // is audio track, see if we are in an audio block
11470         // or if one starts here
11471         if ((tc == start_tc && get_audio_block_start(mt->event_list, new_track, tcnow, TRUE) != NULL) ||
11472             (get_audio_block_start(mt->event_list, new_track, tcnow, FALSE) != NULL)) {
11473           if (!did_backup) {
11474             if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
11475           }
11476           return NULL;
11477 	  // *INDENT-OFF*
11478         }}}}
11479   // *INDENT-ON*
11480 
11481   if (old_track < 0) mt_backup(mt, MT_UNDO_MOVE_AUDIO_BLOCK, 0);
11482   else mt_backup(mt, MT_UNDO_MOVE_BLOCK, 0);
11483 
11484   mt->specific_event = get_prev_event(block->start_event);
11485   while (mt->specific_event && get_event_timecode(mt->specific_event) == start_tc) {
11486     mt->specific_event = get_prev_event(mt->specific_event);
11487   }
11488 
11489   if (old_track > -1) {
11490     clip = get_frame_event_clip(block->start_event, old_track);
11491     mt->insert_start = block->offset_start;
11492     mt->insert_end = block->offset_start + end_tc - start_tc + q_gint64(TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
11493   } else {
11494     clip = get_audio_frame_clip(block->start_event, old_track);
11495     mt->insert_avel = get_audio_frame_vel(block->start_event, old_track);
11496     mt->insert_start = q_gint64(get_audio_frame_seek(block->start_event, old_track) * TICKS_PER_SECOND_DBL, mt->fps);
11497     mt->insert_end = q_gint64(mt->insert_start + (end_tc - start_tc), mt->fps);
11498   }
11499 
11500   mt->moving_block = TRUE;
11501   mt->current_track = old_track;
11502   delete_block_cb(NULL, (livespointer)mt);
11503   mt->block_selected = NULL;
11504   mt->current_track = new_track;
11505   track_select(mt);
11506   mt->clip_selected = mt_clip_from_file(mt, clip);
11507   mt_clip_select(mt, TRUE);
11508   mt_tl_move(mt, timesecs);
11509 
11510   if (new_track != -1) insert_here_cb(NULL, (livespointer)mt);
11511 
11512   else {
11513     insert_audio_here_cb(NULL, (livespointer)mt);
11514     mt->insert_avel = 1.;
11515   }
11516 
11517   mt->insert_start = mt->insert_end = -1;
11518 
11519   new_start_tc = q_gint64(timesecs * TICKS_PER_SECOND_DBL, mt->fps);
11520 
11521   remove_end_blank_frames(mt->event_list, FALSE); // leave filter inits
11522 
11523   // if !move_effects we deleted fx in delete_block, here we move them
11524   if (mt->opts.move_effects) update_filter_events(mt, mt->specific_event, start_tc, end_tc, old_track, new_start_tc,
11525         mt->current_track);
11526 
11527   remove_end_blank_frames(mt->event_list, TRUE); // remove filter inits
11528   mt->moving_block = FALSE;
11529   mt->specific_event = NULL;
11530 
11531   if (new_track != -1) eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
11532   else eventbox = (LiVESWidget *)mt->audio_draws->data;
11533   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
11534 
11535   if (block && (mt->opts.grav_mode == GRAV_MODE_LEFT ||
11536                 (mt->opts.grav_mode == GRAV_MODE_RIGHT && block->next)) &&
11537       !did_backup) {
11538     double oldr_start = mt->region_start;
11539     double oldr_end = mt->region_end;
11540     LiVESList *tracks_sel = NULL;
11541     track_rect *lblock;
11542     double rtc = get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL - 1. / mt->fps, rstart = 0., rend;
11543 
11544     if (mt->opts.grav_mode == GRAV_MODE_LEFT) {
11545       // gravity left - move left until we hit another block or time 0
11546       if (rtc >= 0.) {
11547         lblock = block->prev;
11548         if (lblock) rstart = get_event_timecode(lblock->end_event) / TICKS_PER_SECOND_DBL;
11549       }
11550       rend = get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL;
11551     } else {
11552       // gravity right - move right until we hit the next block
11553       lblock = block->next;
11554       rstart = get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL;
11555       rend = get_event_timecode(lblock->start_event) / TICKS_PER_SECOND_DBL;
11556     }
11557 
11558     mt->region_start = rstart;
11559     mt->region_end = rend;
11560 
11561     if (new_track > -1) {
11562       tracks_sel = lives_list_copy(mt->selected_tracks);
11563       if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
11564       mt->selected_tracks = NULL;
11565       mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(new_track));
11566     } else {
11567       current_track = mt->current_track;
11568       mt->current_track = old_track;
11569     }
11570 
11571     remove_first_gaps(NULL, mt);
11572     if (old_track > -1) {
11573       lives_list_free(mt->selected_tracks);
11574       mt->selected_tracks = lives_list_copy(tracks_sel);
11575       if (tracks_sel) lives_list_free(tracks_sel);
11576     } else mt->current_track = current_track;
11577     mt->region_start = oldr_start;
11578     mt->region_end = oldr_end;
11579     mt_sensitise(mt);
11580   }
11581 
11582   // get this again because it could have moved
11583   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
11584 
11585   if (!did_backup) {
11586     if (mt->avol_fx != -1 && (!block || !block->next) && mt->audio_draws &&
11587         mt->audio_draws->data && get_first_event(mt->event_list)) {
11588       apply_avol_filter(mt);
11589     }
11590   }
11591 
11592   // apply autotransition
11593   if (prefs->atrans_fx != -1) {
11594     // add the insert and autotrans as 2 separate undo events
11595     mt->did_backup = did_backup;
11596     mt_do_autotransition(mt, block);
11597     mt->did_backup = TRUE;
11598   }
11599 
11600   if (!did_backup && mt->framedraw && mt->current_rfx && mt->init_event &&
11601       mt->poly_state == POLY_PARAMS && weed_plant_has_leaf(mt->init_event, WEED_LEAF_IN_TRACKS)) {
11602     weed_timecode_t tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) * TICKS_PER_SECOND_DBL +
11603                                   get_event_timecode(mt->init_event), mt->fps);
11604     get_track_index(mt, tc);
11605   }
11606 
11607   // give the new block the same uid as the old one
11608   if (block) block->uid = uid;
11609 
11610   if (!did_backup) {
11611     mt->did_backup = FALSE;
11612     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
11613     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
11614   }
11615 
11616   return block;
11617 }
11618 
11619 
unselect_all(lives_mt * mt)11620 void unselect_all(lives_mt * mt) {
11621   // unselect all blocks
11622   LiVESWidget *eventbox;
11623   LiVESList *list;
11624   track_rect *trec;
11625 
11626   if (mt->block_selected) lives_widget_queue_draw(mt->block_selected->eventbox);
11627 
11628   if (mainw->files[mt->render_file]->achans > 0) {
11629     for (list = mt->audio_draws; list; list = list->next) {
11630       eventbox = (LiVESWidget *)list->data;
11631       if (eventbox) {
11632         trec = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
11633         while (trec) {
11634           trec->state = BLOCK_UNSELECTED;
11635           trec = trec->next;
11636 	  // *INDENT-OFF*
11637         }}}}
11638   // *INDENT-ON*
11639 
11640   for (list = mt->video_draws; list; list = list->next) {
11641     eventbox = (LiVESWidget *)list->data;
11642     if (eventbox) {
11643       trec = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
11644       while (trec) {
11645         trec->state = BLOCK_UNSELECTED;
11646         trec = trec->next;
11647       }
11648     }
11649   }
11650   mt->block_selected = NULL;
11651   lives_widget_set_sensitive(mt->view_in_out, FALSE);
11652   lives_widget_set_sensitive(mt->delblock, FALSE);
11653 
11654   lives_widget_set_sensitive(mt->fx_block, FALSE);
11655   lives_widget_set_sensitive(mt->fx_blocka, FALSE);
11656   lives_widget_set_sensitive(mt->fx_blockv, FALSE);
11657   if (!nb_ignore && mt->poly_state != POLY_FX_STACK) polymorph(mt, POLY_CLIPS);
11658 }
11659 
11660 
_clear_context(lives_mt * mt)11661 static void _clear_context(lives_mt * mt) {
11662   if (!prefs->mt_show_ctx) return;
11663 
11664   if (mt->context_scroll) {
11665     lives_widget_destroy(mt->context_scroll);
11666   }
11667 
11668   mt->context_scroll = lives_scrolled_window_new(NULL, NULL);
11669   lives_widget_set_hexpand(mt->context_scroll, TRUE);
11670 
11671   lives_container_add(LIVES_CONTAINER(mt->context_frame), mt->context_scroll);
11672 
11673   lives_scrolled_window_set_policy(LIVES_SCROLLED_WINDOW(mt->context_scroll), LIVES_POLICY_NEVER,
11674                                    LIVES_POLICY_AUTOMATIC);
11675 
11676   mt->context_box = lives_vbox_new(FALSE, 4);
11677   lives_scrolled_window_add_with_viewport(LIVES_SCROLLED_WINDOW(mt->context_scroll), mt->context_box);
11678 
11679   // Apply theme background to scrolled window
11680   if (palette->style & STYLE_1) {
11681     lives_widget_set_fg_color(lives_bin_get_child(LIVES_BIN(mt->context_scroll)), LIVES_WIDGET_STATE_NORMAL,
11682                               &palette->normal_fore);
11683     lives_widget_set_bg_color(lives_bin_get_child(LIVES_BIN(mt->context_scroll)), LIVES_WIDGET_STATE_NORMAL,
11684                               &palette->normal_back);
11685   }
11686 
11687   add_context_label(mt, ("                                          ")); // info box stop from shrinking
11688   if (prefs->mt_show_ctx) {
11689     lives_widget_show_all(mt->context_frame);
11690   }
11691 }
11692 
clear_context(lives_mt * mt)11693 void clear_context(lives_mt * mt) {
11694   main_thread_execute((lives_funcptr_t)_clear_context, 0, NULL, "v", mt);
11695 }
11696 
11697 
add_context_label(lives_mt * mt,const char * text)11698 void add_context_label(lives_mt * mt, const char *text) {
11699   // WARNING - do not add > 8 lines of text (including newlines) - otherwise the window can get resized
11700   LiVESWidget *label;
11701 
11702   if (!prefs->mt_show_ctx) return;
11703 
11704   widget_opts.justify = LIVES_JUSTIFY_CENTER;
11705   widget_opts.line_wrap = TRUE;
11706   label = lives_standard_label_new(NULL);
11707   lives_label_set_markup(LIVES_LABEL(label), text);
11708   widget_opts.line_wrap = FALSE;
11709   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
11710 
11711   lives_widget_show(label);
11712   lives_box_pack_start(LIVES_BOX(mt->context_box), label, FALSE, FALSE, 0);
11713 
11714   if (palette->style & STYLE_1) {
11715     lives_widget_set_bg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
11716     lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
11717   }
11718 }
11719 
11720 
resize_timeline(lives_mt * mt)11721 boolean resize_timeline(lives_mt * mt) {
11722   double end_secs;
11723 
11724   if (!mt->event_list || !get_first_event(mt->event_list) || mt->tl_fixed_length > 0.) return FALSE;
11725 
11726   end_secs = event_list_get_end_secs(mt->event_list);
11727 
11728   if (end_secs > mt->end_secs) {
11729     set_timeline_end_secs(mt, end_secs);
11730     return TRUE;
11731   }
11732 
11733   redraw_all_event_boxes(mt);
11734 
11735   return FALSE;
11736 }
11737 
11738 
set_in_out_spin_ranges(lives_mt * mt,weed_timecode_t start_tc,weed_timecode_t end_tc)11739 static void set_in_out_spin_ranges(lives_mt * mt, weed_timecode_t start_tc, weed_timecode_t end_tc) {
11740   track_rect *block = mt->block_selected;
11741   weed_timecode_t min_tc = 0, max_tc = -1;
11742   weed_timecode_t offset_start = get_event_timecode(block->start_event);
11743   int filenum;
11744   double in_val = start_tc / TICKS_PER_SECOND_DBL, out_val = end_tc / TICKS_PER_SECOND_DBL, in_start_range = 0.,
11745          out_start_range = in_val + 1. / mt->fps;
11746   double out_end_range, real_out_end_range;
11747   double in_end_range = out_val - 1. / mt->fps, real_in_start_range = in_start_range;
11748   double avel = 1.;
11749 
11750   int track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
11751 
11752   lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
11753   lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
11754 
11755   if (block->prev) min_tc = get_event_timecode(block->prev->end_event) + (double)(track >= 0) * TICKS_PER_SECOND_DBL /
11756                               mt->fps;
11757   if (block->next) max_tc = get_event_timecode(block->next->start_event) - (double)(
11758                                 track >= 0) * TICKS_PER_SECOND_DBL / mt->fps;
11759 
11760   if (track >= 0) {
11761     filenum = get_frame_event_clip(block->start_event, track);
11762     if (!IS_VALID_CLIP(filenum)) return;
11763     // actually we should quantise this to the mt->fps, but we leave it in case clip has only
11764     // one frame -> otherwise we could quantise to zero frames
11765     out_end_range = count_resampled_frames(mainw->files[filenum]->frames, mainw->files[filenum]->fps, mt->fps) / mt->fps;
11766   } else {
11767     filenum = get_audio_frame_clip(block->start_event, track);
11768     if (!IS_VALID_CLIP(filenum)) return;
11769     out_end_range = q_gint64(mainw->files[filenum]->laudio_time * TICKS_PER_SECOND_DBL, mt->fps) / TICKS_PER_SECOND_DBL;
11770     avel = get_audio_frame_vel(block->start_event, track);
11771   }
11772   real_out_end_range = out_end_range;
11773 
11774   if (mt->opts.insert_mode != INSERT_MODE_OVERWRITE) {
11775     if (!block->end_anchored && max_tc > -1 &&
11776         (((max_tc - offset_start) / TICKS_PER_SECOND_DBL * ABS(avel) + in_val) < out_end_range))
11777       real_out_end_range = q_gint64((max_tc - offset_start) * ABS(avel) + in_val * TICKS_PER_SECOND_DBL,
11778                                     mt->fps) / TICKS_PER_SECOND_DBL;
11779     if (!block->start_anchored && min_tc > -1 &&
11780         (((min_tc - offset_start) / TICKS_PER_SECOND_DBL * ABS(avel) + in_val) > in_start_range))
11781       real_in_start_range = q_gint64((min_tc - offset_start) * ABS(avel) + in_val * TICKS_PER_SECOND_DBL,
11782                                      mt->fps) / TICKS_PER_SECOND_DBL;
11783     if (!block->start_anchored) out_end_range = real_out_end_range;
11784     if (!block->end_anchored) in_start_range = real_in_start_range;
11785   }
11786 
11787   if (block->end_anchored && (out_val - in_val > out_start_range)) out_start_range = in_start_range + out_val - in_val;
11788   if (block->start_anchored && (out_end_range - out_val + in_val) < in_end_range) in_end_range = out_end_range - out_val + in_val;
11789 
11790   in_end_range = lives_fix(in_end_range, 2);
11791   real_out_end_range = lives_fix(real_out_end_range, 2);
11792 
11793   out_start_range = lives_fix(out_start_range, 2);
11794   real_in_start_range = lives_fix(real_in_start_range, 2);
11795 
11796   if (avel > 0.) {
11797     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_out), out_start_range, real_out_end_range);
11798     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_in), real_in_start_range, in_end_range);
11799   } else {
11800     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_in), out_start_range, real_out_end_range);
11801     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_out), real_in_start_range, in_end_range);
11802   }
11803 
11804   lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
11805   lives_signal_handler_unblock(mt->spinbutton_in, mt->spin_in_func);
11806 }
11807 
11808 
update_in_image(lives_mt * mt)11809 static void update_in_image(lives_mt * mt) {
11810   LiVESPixbuf *thumb;
11811   track_rect *block = mt->block_selected;
11812   int track;
11813   int filenum;
11814   int frame_start;
11815   int width = mainw->files[mt->render_file]->hsize;
11816   int height = mainw->files[mt->render_file]->vsize;
11817 
11818   if (!mt->insurface) return;
11819 
11820   if (block) {
11821     track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
11822     filenum = get_frame_event_clip(block->start_event, track);
11823     if (!IS_VALID_CLIP(filenum)) return;
11824     frame_start = calc_frame_from_time(filenum, block->offset_start / TICKS_PER_SECOND_DBL);
11825   } else {
11826     filenum = mt->file_selected;
11827     frame_start = mainw->files[filenum]->start;
11828   }
11829 
11830   calc_maxspect((lives_widget_get_allocation_width(mt->in_frame) >> 1) << 1,
11831                 (lives_widget_get_allocation_height(mt->in_frame) >> 1) << 1, &width,
11832                 &height);
11833 
11834   thumb = make_thumb(mt, filenum,
11835                      width - 2 - ((widget_opts.border_width + 2) >> 1),
11836                      height - ((widget_opts.border_width + 2) >> 1),
11837                      frame_start, get_interp_value(prefs->pb_quality, FALSE), FALSE);
11838   set_drawing_area_from_pixbuf(mt->in_image, thumb, mt->insurface);
11839   if (thumb) lives_widget_object_unref(thumb);
11840 }
11841 
11842 
update_out_image(lives_mt * mt,weed_timecode_t end_tc)11843 static void update_out_image(lives_mt * mt, weed_timecode_t end_tc) {
11844   LiVESPixbuf *thumb;
11845   track_rect *block = mt->block_selected;
11846   int track;
11847   int filenum;
11848   int frame_end;
11849   int width = mainw->files[mt->render_file]->hsize;
11850   int height = mainw->files[mt->render_file]->vsize;
11851 
11852   if (!mt->outsurface) return;
11853 
11854   if (block) {
11855     track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
11856     filenum = get_frame_event_clip(block->start_event, track);
11857     if (!IS_VALID_CLIP(filenum)) return;
11858     frame_end = calc_frame_from_time(filenum, end_tc / TICKS_PER_SECOND_DBL - 1. / mt->fps);
11859     if (frame_end >= mainw->files[filenum]->frames) frame_end = mainw->files[filenum]->frames - 1;
11860   } else {
11861     filenum = mt->file_selected;
11862     frame_end = mainw->files[filenum]->end;
11863   }
11864 
11865   calc_maxspect((lives_widget_get_allocation_width(mt->out_frame) >> 1) << 1,
11866                 (lives_widget_get_allocation_height(mt->out_frame) >> 1) << 1, &width,
11867                 &height);
11868 
11869   thumb = make_thumb(mt, filenum,
11870                      width - ((widget_opts.border_width + 2) >> 1),
11871                      height - ((widget_opts.border_width + 2) >> 1),
11872                      frame_end, get_interp_value(prefs->pb_quality, FALSE), FALSE);
11873   set_drawing_area_from_pixbuf(mt->out_image, thumb, mt->outsurface);
11874   if (thumb) lives_widget_object_unref(thumb);
11875 }
11876 
11877 
show_in_out_images(livespointer user_data)11878 boolean show_in_out_images(livespointer user_data) {
11879   lives_mt *mt = (lives_mt *)user_data;
11880   track_rect *block = mt->block_selected;
11881   weed_timecode_t end_tc;
11882   int track;
11883   if (!block) return FALSE;
11884   track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
11885   if (track < 0) return FALSE;
11886 
11887   // wait for config events which will create insurface and outsurface
11888   if (!mt->insurface || !mt->outsurface) return TRUE;
11889   end_tc = block->offset_start + (weed_timecode_t)(TICKS_PER_SECOND_DBL / mt->fps) + (get_event_timecode(
11890              block->end_event) - get_event_timecode(block->start_event));
11891   update_in_image(mt);
11892   update_out_image(mt, end_tc);
11893   return FALSE;
11894 }
11895 
11896 
in_out_start_changed(LiVESWidget * widget,livespointer user_data)11897 void in_out_start_changed(LiVESWidget * widget, livespointer user_data) {
11898   lives_mt *mt = (lives_mt *)user_data;
11899 
11900   track_rect *block = mt->block_selected, *ablock = NULL;
11901 
11902   weed_plant_t *event;
11903   weed_plant_t *start_event = NULL, *event_next;
11904 
11905   weed_timecode_t new_start_tc, orig_start_tc, offset_end, tl_start;
11906   weed_timecode_t new_tl_tc;
11907 
11908   double new_start;
11909   double avel = 1., aseek = 0.;
11910 
11911   boolean was_moved;
11912   boolean start_anchored;
11913   boolean needs_idlefunc = FALSE;
11914   boolean did_backup = mt->did_backup;
11915 
11916   int track;
11917   int filenum;
11918   int aclip = 0;
11919 
11920   if (!LIVES_IS_INTERACTIVE) return;
11921 
11922   if (mt->idlefunc > 0) {
11923     needs_idlefunc = TRUE;
11924     lives_source_remove(mt->idlefunc);
11925     mt->idlefunc = 0;
11926   }
11927 
11928   lives_widget_set_sensitive(mt->spinbutton_in, FALSE);
11929 
11930   if (!block) {
11931     // if no block selected, set for current clip
11932     lives_clip_t *sfile = mainw->files[mt->file_selected];
11933     sfile->start = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(widget));
11934     set_clip_labels_variable(mt, mt->file_selected);
11935     update_in_image(mt);
11936 
11937     if (sfile->end < sfile->start) {
11938       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), (double)sfile->start);
11939     }
11940     if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
11941       mt->idlefunc = mt_idle_add(mt);
11942     }
11943     lives_widget_set_sensitive(mt->spinbutton_in, TRUE);
11944     return;
11945   }
11946 
11947   new_start = lives_spin_button_get_value(LIVES_SPIN_BUTTON(widget));
11948 
11949   event = block->start_event;
11950   orig_start_tc = block->offset_start;
11951   track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
11952   new_start_tc = q_dbl(new_start, mt->fps);
11953 
11954   if (new_start_tc == orig_start_tc || !block->ordered) {
11955     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), new_start_tc / TICKS_PER_SECOND_DBL);
11956     if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
11957       mt->idlefunc = mt_idle_add(mt);
11958     }
11959     lives_widget_set_sensitive(mt->spinbutton_in, TRUE);
11960     return;
11961   }
11962 
11963   tl_start = get_event_timecode(event);
11964 
11965   // get the audio block (if exists)
11966   if (track >= 0) {
11967     if (!mt->aud_track_selected) {
11968       if (mt->opts.pertrack_audio) {
11969         LiVESWidget *aeventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "atrack"));
11970         ablock = get_block_from_time(aeventbox, tl_start / TICKS_PER_SECOND_DBL, mt);
11971       }
11972       start_anchored = block->start_anchored;
11973     } else {
11974       LiVESWidget *eventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "owner"));
11975       ablock = block;
11976       block = get_block_from_time(eventbox, tl_start / TICKS_PER_SECOND_DBL, mt);
11977       start_anchored = ablock->start_anchored;
11978     }
11979     filenum = get_frame_event_clip(block->start_event, track);
11980   } else {
11981     ablock = block;
11982     start_anchored = block->start_anchored;
11983     avel = get_audio_frame_vel(ablock->start_event, track);
11984     filenum = get_audio_frame_clip(ablock->start_event, track);
11985   }
11986 
11987   if (!start_anchored) {
11988     if (new_start_tc > block->offset_start) {
11989       start_event = get_prev_frame_event(block->start_event);
11990 
11991       // start increased, not anchored
11992       while (event) {
11993         if ((get_event_timecode(event) - tl_start) >= (new_start_tc - block->offset_start) / avel) {
11994           //if tc of event - tc of block start event > new start tc (in source file) - offset tc (in source file)
11995 
11996           // done
11997           if (ablock) {
11998             aclip = get_audio_frame_clip(ablock->start_event, track);
11999             aseek = get_audio_frame_seek(ablock->start_event, track);
12000 
12001             if (ablock->prev && ablock->prev->end_event == ablock->start_event) {
12002               int last_aclip = get_audio_frame_clip(ablock->prev->start_event, track);
12003               insert_audio_event_at(ablock->start_event, track, last_aclip, 0., 0.);
12004             } else {
12005               remove_audio_for_track(ablock->start_event, track);
12006             }
12007             aseek += q_gint64(avel * (double)(get_event_timecode(event) - get_event_timecode(ablock->start_event)),
12008                               mt->fps) / TICKS_PER_SECOND_DBL;
12009             ablock->start_event = event;
12010             ablock->offset_start = new_start_tc;
12011           }
12012           if (block != ablock) {
12013             block->start_event = event;
12014             block->offset_start = new_start_tc;
12015           }
12016           break;
12017         }
12018 
12019         if (event == block->end_event) {
12020           if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
12021             mt->idlefunc = mt_idle_add(mt);
12022           }
12023           lives_widget_set_sensitive(mt->spinbutton_in, TRUE);
12024           return; // should never happen...
12025         }
12026         if (track >= 0) remove_frame_from_event(mt->event_list, event, track);
12027         event = get_next_frame_event(event);
12028       }
12029 
12030       if (ablock) {
12031         insert_audio_event_at(ablock->start_event, track, aclip, aseek, avel);
12032         ablock->offset_start = q_dbl(aseek * TICKS_PER_SECOND_DBL, mt->fps) / TICKS_PER_SECOND_DBL;
12033       }
12034 
12035       // move filter_inits right, and deinits left
12036       new_tl_tc = get_event_timecode(block->start_event);
12037       if (!start_event) event = get_first_event(mt->event_list);
12038       else event = get_next_event(start_event);
12039       while (event && get_event_timecode(event) < tl_start) event = get_next_event(event);
12040 
12041       while (event && get_event_timecode(event) < new_tl_tc) {
12042         event_next = get_next_event(event);
12043         was_moved = FALSE;
12044         if (WEED_EVENT_IS_FILTER_INIT(event) && event != mt->avol_init_event) {
12045           if (!move_event_right(mt->event_list, event, TRUE, mt->fps)) {
12046             was_moved = TRUE;
12047             if (event == start_event) start_event = NULL;
12048           }
12049         } else {
12050           if (WEED_EVENT_IS_FILTER_DEINIT(event)) {
12051             if (!move_event_left(mt->event_list, event, TRUE, mt->fps)) {
12052               was_moved = TRUE;
12053             }
12054           }
12055         }
12056         if (was_moved) {
12057           if (!start_event) event = get_first_event(mt->event_list);
12058           else event = get_next_event(start_event);
12059           while (event && get_event_timecode(event) < tl_start) event = get_next_event(event);
12060         } else {
12061           event = event_next;
12062           if (WEED_EVENT_IS_FRAME(event)) start_event = event;
12063         }
12064       }
12065 
12066     } else {
12067       // move start left, not anchored
12068       if (ablock) {
12069         aclip = get_audio_frame_clip(ablock->start_event, track);
12070         aseek = get_audio_frame_seek(ablock->start_event, track);
12071 
12072         remove_audio_for_track(ablock->start_event, track);
12073 
12074         aseek += q_gint64(new_start_tc - ablock->offset_start, mt->fps) / TICKS_PER_SECOND_DBL;
12075         ablock->start_event = get_frame_event_at_or_before(mt->event_list,
12076                               q_gint64(tl_start + (new_start_tc - ablock->offset_start) / avel, mt->fps),
12077                               get_prev_frame_event(ablock->start_event));
12078 
12079         insert_audio_event_at(ablock->start_event, track, aclip, aseek, avel);
12080         ablock->offset_start = q_dbl(aseek * TICKS_PER_SECOND_DBL, mt->fps) / TICKS_PER_SECOND_DBL;
12081       }
12082       if (block != ablock) {
12083         // do an insert from offset_start down
12084         insert_frames(filenum, block->offset_start, new_start_tc, tl_start, LIVES_DIRECTION_BACKWARD, block->eventbox, mt, block);
12085         block->offset_start = new_start_tc;
12086       }
12087 
12088       // any filter_inits with this track as owner get moved as far left as possible
12089       new_tl_tc = get_event_timecode(block->start_event);
12090       event = block->start_event;
12091 
12092       while (event && get_event_timecode(event) < tl_start) {
12093         start_event = event;
12094         event = get_next_event(event);
12095       }
12096       while (event && get_event_timecode(event) == tl_start) {
12097         if (WEED_EVENT_IS_FILTER_INIT(event) && event != mt->avol_init_event) {
12098           if (filter_init_has_owner(event, track)) {
12099             // candidate for moving
12100             move_filter_init_event(mt->event_list, new_tl_tc, event, mt->fps);
12101             // account for overlaps
12102             move_event_right(mt->event_list, event, TRUE, mt->fps);
12103             event = start_event;
12104             while (event && get_event_timecode(event) < tl_start) event = get_next_event(event);
12105             continue;
12106 	    // *INDENT-OFF*
12107           }}
12108         event = get_next_event(event);
12109       }}}
12110   // *INDENT-ON*
12111   else {
12112     // start is anchored, do a re-insert from start to end
12113     lives_mt_insert_mode_t insert_mode = mt->opts.insert_mode;
12114     offset_end = q_gint64((block->offset_start = new_start_tc) + (weed_timecode_t)(TICKS_PER_SECOND_DBL / mt->fps) +
12115                           (get_event_timecode(block->end_event) - get_event_timecode(block->start_event)), mt->fps);
12116     mt->opts.insert_mode = INSERT_MODE_OVERWRITE;
12117     if (track >= 0) insert_frames(filenum, new_start_tc, offset_end, tl_start, LIVES_DIRECTION_FORWARD,
12118                                     block->eventbox, mt, block);
12119     if (ablock) {
12120       aclip = get_audio_frame_clip(ablock->start_event, track);
12121       aseek = get_audio_frame_seek(ablock->start_event, track);
12122       aseek += q_gint64(new_start_tc - orig_start_tc, mt->fps) / TICKS_PER_SECOND_DBL;
12123       insert_audio_event_at(ablock->start_event, track, aclip, aseek, avel);
12124       ablock->offset_start = q_gint64(aseek * TICKS_PER_SECOND_DBL, mt->fps);
12125     }
12126     mt->opts.insert_mode = insert_mode;
12127   }
12128 
12129   offset_end = (new_start_tc = block->offset_start) + (weed_timecode_t)((double)(track >= 0) * TICKS_PER_SECOND_DBL / mt->fps) +
12130                avel * (get_event_timecode(block->end_event) - get_event_timecode(block->start_event));
12131 
12132   if (mt->poly_state == POLY_IN_OUT) {
12133     set_in_out_spin_ranges(mt, new_start_tc, offset_end);
12134     lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12135     lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
12136     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), offset_end / TICKS_PER_SECOND_DBL);
12137     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), new_start_tc / TICKS_PER_SECOND_DBL);
12138     lives_spin_button_update(LIVES_SPIN_BUTTON(mt->spinbutton_out));
12139     lives_spin_button_update(LIVES_SPIN_BUTTON(mt->spinbutton_in));
12140     lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
12141     lives_signal_handler_unblock(mt->spinbutton_in, mt->spin_in_func);
12142 
12143     if (track >= 0) {
12144       // update images
12145       update_in_image(mt);
12146       if (start_anchored) update_out_image(mt, offset_end);
12147       update_in_image(mt);
12148       if (start_anchored) update_out_image(mt, offset_end);
12149     }
12150   }
12151 
12152   if (!resize_timeline(mt)) {
12153     redraw_eventbox(mt, block->eventbox);
12154     paint_lines(mt, mt->ptr_time, TRUE, NULL);
12155   }
12156 
12157   if (prefs->mt_auto_back >= 0) {
12158     mt->auto_changed = TRUE;
12159     if (prefs->mt_auto_back < MT_INOUT_TIME) {
12160       // the user wants us to backup, but it would be tiresome to backup on every spin button change
12161       // so instead of adding the idle function we will add a timer
12162       //
12163       // if the spinbutton is changed again we we reset the timer
12164       // after any backup we put the normal idlefunc back again
12165       mt->idlefunc = lives_timer_add_simple(MT_INOUT_TIME, mt_auto_backup, mt);
12166     } else {
12167       mt->idlefunc = mt_idle_add(mt);
12168     }
12169   }
12170   lives_widget_set_sensitive(mt->spinbutton_in, TRUE);
12171 }
12172 
12173 
in_out_end_changed(LiVESWidget * widget,livespointer user_data)12174 void in_out_end_changed(LiVESWidget * widget, livespointer user_data) {
12175   lives_mt *mt = (lives_mt *)user_data;
12176 
12177   track_rect *block = mt->block_selected, *ablock = NULL;
12178 
12179   weed_timecode_t offset_end, orig_end_tc;
12180   weed_timecode_t new_end_tc, tl_end;
12181   weed_timecode_t new_tl_tc;
12182 
12183   weed_plant_t *event, *prevevent, *shortcut = NULL;
12184   weed_plant_t *start_event, *event_next, *init_event, *new_end_event;
12185 
12186   double new_end = lives_spin_button_get_value(LIVES_SPIN_BUTTON(widget));
12187   double start_val;
12188   double aseek, avel = 1.;
12189 
12190   boolean was_moved;
12191   boolean end_anchored;
12192   boolean needs_idlefunc = FALSE;
12193   boolean did_backup = mt->did_backup;
12194 
12195   int track;
12196   int filenum;
12197   int aclip = 0;
12198 
12199   if (!LIVES_IS_INTERACTIVE) return;
12200 
12201   lives_widget_set_sensitive(mt->spinbutton_out, FALSE);
12202 
12203   if (mt->idlefunc > 0) {
12204     needs_idlefunc = TRUE;
12205     lives_source_remove(mt->idlefunc);
12206     mt->idlefunc = 0;
12207   }
12208 
12209   if (!block) {
12210     lives_clip_t *sfile = mainw->files[mt->file_selected];
12211     sfile->end = (int)new_end;
12212     set_clip_labels_variable(mt, mt->file_selected);
12213     update_out_image(mt, 0);
12214 
12215     if (sfile->end < sfile->start) lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), (double)sfile->end);
12216     if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
12217       mt->idlefunc = mt_idle_add(mt);
12218     }
12219     lives_widget_set_sensitive(mt->spinbutton_out, TRUE);
12220     return;
12221   }
12222 
12223   start_val = block->offset_start / TICKS_PER_SECOND_DBL;
12224   event = block->end_event;
12225   track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
12226 
12227   tl_end = get_event_timecode(event);
12228 
12229   // get the audio block (if exists)
12230   if (track >= 0) {
12231     if (!mt->aud_track_selected) {
12232       if (mt->opts.pertrack_audio) {
12233         LiVESWidget *aeventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "atrack"));
12234         ablock = get_block_from_time(aeventbox, tl_end / TICKS_PER_SECOND_DBL - 1. / mt->fps, mt);
12235       }
12236       end_anchored = block->end_anchored;
12237     } else {
12238       LiVESWidget *eventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "owner"));
12239       ablock = block;
12240       block = get_block_from_time(eventbox, tl_end / TICKS_PER_SECOND_DBL - 1. / mt->fps, mt);
12241       end_anchored = ablock->end_anchored;
12242     }
12243     filenum = get_frame_event_clip(block->start_event, track);
12244   } else {
12245     ablock = block;
12246     end_anchored = block->end_anchored;
12247     avel = get_audio_frame_vel(ablock->start_event, track);
12248     filenum = get_audio_frame_clip(ablock->start_event, track);
12249   }
12250 
12251   // offset_end is timecode of end event within source (scaled for velocity)
12252   offset_end = q_gint64(block->offset_start + (weed_timecode_t)((double)(track >= 0) * TICKS_PER_SECOND_DBL / mt->fps) +
12253                         (weed_timecode_t)((double)(get_event_timecode(block->end_event) -
12254                                           get_event_timecode(block->start_event)) * avel), mt->fps);
12255 
12256   if (track >= 0 &&
12257       new_end > mainw->files[filenum]->frames / mainw->files[filenum]->fps) new_end = mainw->files[filenum]->frames /
12258             mainw->files[filenum]->fps;
12259 
12260   new_end_tc = q_gint64(block->offset_start + (new_end - start_val) * TICKS_PER_SECOND_DBL, mt->fps);
12261   orig_end_tc = offset_end;
12262 
12263 #ifdef DEBUG_BL_MOVE
12264   g_print("pt a %ld %ld %ld %.4f %ld %ld\n", block->offset_start, get_event_timecode(block->end_event),
12265           get_event_timecode(block->start_event), new_end, orig_end_tc, new_end_tc);
12266 #endif
12267 
12268   if (ABS(new_end_tc - orig_end_tc) < (.5 * TICKS_PER_SECOND_DBL) / mt->fps || !block->ordered) {
12269     lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12270     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), new_end_tc / TICKS_PER_SECOND_DBL);
12271     lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
12272     if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
12273       mt->idlefunc = mt_idle_add(mt);
12274     }
12275     lives_widget_set_sensitive(mt->spinbutton_out, TRUE);
12276     return;
12277   }
12278 
12279   start_event = get_prev_frame_event(event);
12280 
12281   if (!end_anchored) {
12282     new_tl_tc = q_gint64(get_event_timecode(block->start_event) + (new_end - start_val) * TICKS_PER_SECOND_DBL / avel -
12283                          (double)(track >= 0) * TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
12284     if (track < 0) new_tl_tc -= (TICKS_PER_SECOND_DBL / mt->fps);
12285 
12286 #ifdef DEBUG_BL_MOVE
12287     g_print("new tl tc is %ld %ld %.4f %.4f\n", new_tl_tc, tl_end, new_end, start_val);
12288 #endif
12289     if (tl_end > new_tl_tc) {
12290       // end decreased, not anchored
12291 
12292       while (event) {
12293         if (get_event_timecode(event) <= new_tl_tc) {
12294           // done
12295           if (ablock) {
12296             if (!ablock->next || ablock->next->start_event != ablock->end_event)
12297               remove_audio_for_track(ablock->end_event, track);
12298             aclip = get_audio_frame_clip(ablock->start_event, track);
12299           }
12300           block->end_event = event;
12301           break;
12302         }
12303         prevevent = get_prev_frame_event(event);
12304         if (track >= 0) remove_frame_from_event(mt->event_list, event, track);
12305         event = prevevent;
12306       }
12307 
12308       if (ablock) {
12309         new_end_event = get_next_frame_event(event);
12310         if (!new_end_event) {
12311           weed_plant_t *shortcut = ablock->end_event;
12312           mt->event_list = insert_blank_frame_event_at(mt->event_list,
12313                            q_gint64(new_tl_tc + (weed_timecode_t)((double)(track >= 0) *
12314                                     TICKS_PER_SECOND_DBL / mt->fps), mt->fps),
12315                            &shortcut);
12316           ablock->end_event = shortcut;
12317         } else ablock->end_event = new_end_event;
12318         insert_audio_event_at(ablock->end_event, track, aclip, 0., 0.);
12319       }
12320 
12321       // move filter_inits right, and deinits left
12322       new_tl_tc = get_event_timecode(block->end_event);
12323       start_event = block->end_event;
12324       while (event && get_event_timecode(event) <= new_tl_tc) event = get_next_event(event);
12325 
12326       while (event && get_event_timecode(event) <= tl_end) {
12327         event_next = get_next_event(event);
12328         was_moved = FALSE;
12329         if (WEED_EVENT_IS_FILTER_INIT(event)) {
12330           if (!move_event_right(mt->event_list, event, TRUE, mt->fps)) {
12331             was_moved = TRUE;
12332             if (event == start_event) start_event = NULL;
12333           }
12334         } else {
12335           if (WEED_EVENT_IS_FILTER_DEINIT(event)) {
12336             init_event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
12337             if (init_event != mt->avol_init_event) {
12338               if (!move_event_left(mt->event_list, event, TRUE, mt->fps)) {
12339                 was_moved = TRUE;
12340 		// *INDENT-OFF*
12341               }}}}
12342 	// *INDENT-ON*
12343         if (was_moved) {
12344           if (!start_event) event = get_first_event(mt->event_list);
12345           else event = get_next_event(start_event);
12346           while (event && get_event_timecode(event) <= new_tl_tc) event = get_next_event(event);
12347         } else {
12348           event = event_next;
12349           if (WEED_EVENT_IS_FRAME(event)) start_event = event;
12350         }
12351       }
12352       remove_end_blank_frames(mt->event_list, TRUE);
12353     } else {
12354       // end increased, not anchored
12355       if (track >= 0) {
12356         // do an insert from end_tc up, starting with end_frame and finishing at new_end
12357         insert_frames(filenum, offset_end, new_end_tc, tl_end + (weed_timecode_t)(TICKS_PER_SECOND_DBL / mt->fps),
12358                       LIVES_DIRECTION_FORWARD, block->eventbox, mt, block);
12359         block->end_event = get_frame_event_at(mt->event_list, q_gint64(new_end_tc + tl_end - offset_end, mt->fps),
12360                                               block->end_event, TRUE);
12361       } else {
12362         // for backing audio insert blank frames up to end
12363         weed_plant_t *last_frame_event = get_last_frame_event(mt->event_list);
12364         weed_timecode_t final_tc = get_event_timecode(last_frame_event);
12365         shortcut = last_frame_event;
12366         while (final_tc < new_tl_tc) {
12367           final_tc = q_gint64(final_tc + TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
12368           mt->event_list = insert_blank_frame_event_at(mt->event_list, final_tc, &shortcut);
12369         }
12370       }
12371       if (ablock) {
12372         new_end_event = get_frame_event_at(mt->event_list, q_gint64(new_tl_tc + TICKS_PER_SECOND_DBL / mt->fps, mt->fps),
12373                                            ablock->end_event, TRUE);
12374         if (new_end_event == ablock->end_event) {
12375           lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12376           lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), orig_end_tc / TICKS_PER_SECOND_DBL);
12377           lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
12378           return;
12379         }
12380         remove_audio_for_track(ablock->end_event, track);
12381         if (!new_end_event) {
12382           if (!shortcut) shortcut = ablock->end_event;
12383           mt->event_list = insert_blank_frame_event_at(mt->event_list,
12384                            q_gint64(new_tl_tc + TICKS_PER_SECOND_DBL / mt->fps, mt->fps), &shortcut);
12385           ablock->end_event = shortcut;
12386         } else ablock->end_event = new_end_event;
12387 
12388         if (!ablock->next || ablock->next->start_event != ablock->end_event) {
12389           aclip = get_audio_frame_clip(ablock->start_event, track);
12390           insert_audio_event_at(ablock->end_event, track, aclip, 0., 0.);
12391         }
12392       }
12393 
12394       new_tl_tc = get_event_timecode(block->end_event);
12395 
12396       start_event = event;
12397 
12398       while (event && get_event_timecode(event) == tl_end) {
12399         if (WEED_EVENT_IS_FILTER_DEINIT(event)) {
12400           init_event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
12401           if (init_event != mt->avol_init_event) {
12402             if (filter_init_has_owner(init_event, track)) {
12403               // candidate for moving
12404               move_filter_deinit_event(mt->event_list, new_tl_tc, event, mt->fps, TRUE);
12405               // account for overlaps
12406               //move_event_left(mt->event_list,event,TRUE,mt->fps);
12407               event = start_event;
12408               continue;
12409 	      // *INDENT-OFF*
12410             }}}
12411         event = get_next_event(event);
12412       }}}
12413   // *INDENT-ON*
12414   else {
12415     // end is anchored, do a re-insert from end to start
12416     weed_timecode_t offset_start;
12417     lives_mt_insert_mode_t insert_mode = mt->opts.insert_mode;
12418 
12419     offset_end = q_gint64((offset_start = block->offset_start + new_end_tc - orig_end_tc) +
12420                           (weed_timecode_t)((double)(track >= 0) * TICKS_PER_SECOND_DBL / mt->fps) +
12421                           (get_event_timecode(block->end_event) - get_event_timecode(block->start_event)), mt->fps);
12422 
12423     mt->opts.insert_mode = INSERT_MODE_OVERWRITE;
12424 
12425     // note: audio blocks end at the timecode, video blocks end at tc + TICKS_PER_SECOND_DBL/mt->fps
12426     if (track >= 0) insert_frames(filenum, offset_end, offset_start, tl_end +
12427                                     (weed_timecode_t)((double)(track >= 0
12428                                         && !mt->aud_track_selected)*TICKS_PER_SECOND_DBL / mt->fps),
12429                                     LIVES_DIRECTION_BACKWARD, block->eventbox, mt, block);
12430 
12431     block->offset_start = q_gint64(offset_start, mt->fps);
12432 
12433     if (ablock) {
12434       aclip = get_audio_frame_clip(ablock->start_event, track);
12435       aseek = get_audio_frame_seek(ablock->start_event, track);
12436       avel = get_audio_frame_vel(ablock->start_event, track);
12437       aseek += q_gint64(new_end_tc - orig_end_tc, mt->fps) / TICKS_PER_SECOND_DBL;
12438       insert_audio_event_at(ablock->start_event, track, aclip, aseek, avel);
12439       ablock->offset_start = q_gint64(aseek * TICKS_PER_SECOND_DBL, mt->fps);
12440     }
12441 
12442     mt->opts.insert_mode = insert_mode;
12443   }
12444 
12445   // get new offset_end
12446   new_end_tc = (block->offset_start + (weed_timecode_t)((double)(track >= 0) * TICKS_PER_SECOND_DBL / mt->fps) +
12447                 (get_event_timecode(block->end_event) - get_event_timecode(block->start_event)) * avel);
12448 
12449 #ifdef DEBUG_BL_MOVE
12450   g_print("new end tc is %ld %ld %ld %.4f\n", get_event_timecode(block->end_event),
12451           get_event_timecode(block->start_event), block->offset_start, avel);
12452 #endif
12453 
12454   if (mt->avol_fx != -1 && mt->avol_init_event && mt->audio_draws &&
12455       mt->audio_draws->data && !block->next) {
12456     apply_avol_filter(mt);
12457   }
12458 
12459   if (mt->poly_state == POLY_IN_OUT) {
12460     set_in_out_spin_ranges(mt, block->offset_start, new_end_tc);
12461     lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12462     lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
12463     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), block->offset_start / TICKS_PER_SECOND_DBL);
12464     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), new_end_tc / TICKS_PER_SECOND_DBL);
12465     lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
12466     lives_signal_handler_unblock(mt->spinbutton_in, mt->spin_in_func);
12467 
12468     if (track >= 0) {
12469       // update image
12470       update_out_image(mt, new_end_tc);
12471       if (end_anchored) update_in_image(mt);
12472     }
12473   }
12474 #ifdef DEBUG_BL_MOVE
12475   g_print("pt b %ld\n", q_gint64(new_end_tc / avel, mt->fps));
12476 #endif
12477   if (!resize_timeline(mt)) {
12478     redraw_eventbox(mt, block->eventbox);
12479     if (ablock && ablock != block) redraw_eventbox(mt, block->eventbox);
12480     paint_lines(mt, mt->ptr_time, TRUE, NULL);
12481     // TODO - redraw chans ??
12482   }
12483   if (prefs->mt_auto_back >= 0) {
12484     mt->auto_changed = TRUE;
12485     if (prefs->mt_auto_back < MT_INOUT_TIME) {
12486       // the user wants us to backup, but it would be tiresome to backup on every spin button change
12487       // so instead of adding the idle function we will add a timer
12488       //
12489       // if the spinbutton is changed again we we reset the timer
12490       // after any backup we put the normal idlefunc back again
12491       mt->idlefunc = lives_timer_add_simple(MT_INOUT_TIME, mt_auto_backup, mt);
12492     } else {
12493       mt->idlefunc = mt_idle_add(mt);
12494     }
12495   }
12496   lives_widget_set_sensitive(mt->spinbutton_out, TRUE);
12497 }
12498 
12499 
avel_reverse_toggled(LiVESToggleButton * togglebutton,livespointer user_data)12500 void avel_reverse_toggled(LiVESToggleButton * togglebutton, livespointer user_data) {
12501   lives_mt *mt = (lives_mt *)user_data;
12502   track_rect *block = mt->block_selected;
12503   int track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
12504   double avel = -get_audio_frame_vel(block->start_event, track);
12505   double aseek = get_audio_frame_seek(block->start_event, track), aseek_end;
12506   int aclip = get_audio_frame_clip(block->start_event, track);
12507 
12508   double old_in_val = lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->spinbutton_in));
12509   double old_out_val = lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->spinbutton_out));
12510 
12511   // update avel and aseek
12512   aseek_end = aseek + (get_event_timecode(block->end_event) - get_event_timecode(block->start_event)) / TICKS_PER_SECOND_DBL *
12513               (-avel);
12514   insert_audio_event_at(block->start_event, track, aclip, aseek_end, avel);
12515 
12516   if (avel < 0.) set_in_out_spin_ranges(mt, old_in_val * TICKS_PER_SECOND_DBL, old_out_val * TICKS_PER_SECOND_DBL);
12517   else set_in_out_spin_ranges(mt, old_out_val * TICKS_PER_SECOND_DBL, old_in_val * TICKS_PER_SECOND_DBL);
12518 
12519   lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12520   lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
12521 
12522   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), old_out_val);
12523   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), old_in_val);
12524 
12525   lives_signal_handler_unblock(mt->spinbutton_in, mt->spin_in_func);
12526   lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
12527 
12528   if (avel < 0.) {
12529     lives_widget_set_sensitive(mt->spinbutton_in, FALSE);
12530     lives_widget_set_sensitive(mt->spinbutton_out, FALSE);
12531     lives_widget_set_sensitive(mt->spinbutton_avel, FALSE);
12532     lives_widget_set_sensitive(mt->avel_scale, FALSE);
12533     lives_widget_set_sensitive(mt->checkbutton_start_anchored, FALSE);
12534     lives_widget_set_sensitive(mt->checkbutton_end_anchored, FALSE);
12535   } else {
12536     lives_widget_set_sensitive(mt->spinbutton_in, TRUE);
12537     lives_widget_set_sensitive(mt->spinbutton_out, TRUE);
12538     if (!block->start_anchored || !block->end_anchored) {
12539       lives_widget_set_sensitive(mt->spinbutton_avel, TRUE);
12540       lives_widget_set_sensitive(mt->avel_scale, TRUE);
12541     }
12542     lives_widget_set_sensitive(mt->checkbutton_start_anchored, TRUE);
12543     lives_widget_set_sensitive(mt->checkbutton_end_anchored, TRUE);
12544   }
12545 }
12546 
12547 
avel_spin_changed(LiVESSpinButton * spinbutton,livespointer user_data)12548 void avel_spin_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
12549   lives_mt *mt = (lives_mt *)user_data;
12550   track_rect *block = mt->block_selected;
12551   int track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
12552   double new_avel = lives_spin_button_get_value(spinbutton);
12553   double aseek = get_audio_frame_seek(block->start_event, track);
12554   int aclip = get_audio_frame_clip(block->start_event, track);
12555   weed_timecode_t new_end_tc, old_tl_tc, start_tc, new_tl_tc, min_tc;
12556   weed_plant_t *new_end_event, *new_start_event;
12557   double orig_end_val, orig_start_val;
12558   boolean was_adjusted = FALSE;
12559 
12560   if (!LIVES_IS_INTERACTIVE) return;
12561 
12562   if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(mt->checkbutton_avel_reverse))) new_avel = -new_avel;
12563 
12564   start_tc = block->offset_start;
12565 
12566   orig_end_val = lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->spinbutton_out));
12567   old_tl_tc = get_event_timecode(block->end_event);
12568 
12569   if (!block->end_anchored) {
12570     new_end_tc = q_gint64(start_tc + ((orig_end_val =
12571                                          lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->spinbutton_out)))
12572                                       * TICKS_PER_SECOND_DBL - start_tc) / new_avel, mt->fps);
12573 
12574     insert_audio_event_at(block->start_event, track, aclip, aseek, new_avel);
12575 
12576     new_tl_tc = q_gint64(get_event_timecode(block->start_event) + (orig_end_val * TICKS_PER_SECOND_DBL - start_tc) / new_avel,
12577                          mt->fps);
12578 
12579     // move end point (if we can)
12580     if (block->next && new_tl_tc >= get_event_timecode(block->next->start_event)) {
12581       new_end_tc = q_gint64((get_event_timecode(block->next->start_event) -
12582                              get_event_timecode(block->start_event)) * new_avel + block->offset_start, mt->fps);
12583       set_in_out_spin_ranges(mt, block->offset_start, new_end_tc);
12584       lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12585       lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
12586       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), new_end_tc / TICKS_PER_SECOND_DBL);
12587       lives_signal_handler_unblock(mt->spinbutton_in, mt->spin_in_func);
12588       lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
12589       return;
12590     }
12591 
12592     if (new_tl_tc != old_tl_tc) {
12593       weed_plant_t *shortcut;
12594       if (new_tl_tc > old_tl_tc) shortcut = block->end_event;
12595       else shortcut = block->start_event;
12596       if (!block->next || block->next->start_event != block->end_event) remove_audio_for_track(block->end_event, -1);
12597       new_end_event = get_frame_event_at(mt->event_list, new_tl_tc, shortcut, TRUE);
12598       if (new_end_event == block->start_event) return;
12599       block->end_event = new_end_event;
12600 
12601       if (!block->end_event) {
12602         weed_plant_t *last_frame_event = get_last_frame_event(mt->event_list);
12603         add_blank_frames_up_to(mt->event_list, last_frame_event, new_tl_tc, mt->fps);
12604         block->end_event = get_last_frame_event(mt->event_list);
12605       }
12606       if (!block->next || block->next->start_event != block->end_event)
12607         insert_audio_event_at(block->end_event, -1, aclip, 0., 0.);
12608 
12609       lives_widget_queue_draw((LiVESWidget *)mt->audio_draws->data);
12610       new_end_tc = start_tc + (get_event_timecode(block->end_event) - get_event_timecode(block->start_event)) * new_avel;
12611       lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12612       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), new_end_tc / TICKS_PER_SECOND_DBL);
12613       lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
12614       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), orig_end_val);
12615 
12616       remove_end_blank_frames(mt->event_list, TRUE);
12617 
12618       if (mt->avol_fx != -1 && !block->next) {
12619         apply_avol_filter(mt);
12620       }
12621     }
12622     if (!resize_timeline(mt)) {
12623       redraw_eventbox(mt, block->eventbox);
12624       paint_lines(mt, mt->ptr_time, TRUE, NULL);
12625     }
12626     return;
12627   }
12628 
12629   // move start point (if we can)
12630   min_tc = 0;
12631   if (block->prev) min_tc = get_event_timecode(block->prev->end_event);
12632 
12633   new_tl_tc = q_gint64(get_event_timecode(block->end_event) - (orig_end_val * TICKS_PER_SECOND_DBL - start_tc) / new_avel,
12634                        mt->fps);
12635   new_end_tc = orig_end_val * TICKS_PER_SECOND_DBL;
12636 
12637   if (new_tl_tc < min_tc) {
12638     aseek -= (new_tl_tc - min_tc) / TICKS_PER_SECOND_DBL;
12639     start_tc = block->offset_start = aseek * TICKS_PER_SECOND_DBL;
12640     new_tl_tc = min_tc;
12641     was_adjusted = TRUE;
12642   }
12643 
12644   orig_start_val = lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->spinbutton_in));
12645 
12646   if (was_adjusted || new_tl_tc != old_tl_tc) {
12647     weed_plant_t *shortcut;
12648     if (new_tl_tc > old_tl_tc) shortcut = block->start_event;
12649     else {
12650       if (block->prev) shortcut = block->prev->end_event;
12651       else shortcut = NULL;
12652     }
12653 
12654     new_start_event = get_frame_event_at(mt->event_list, new_tl_tc, shortcut, TRUE);
12655     if (new_start_event == block->end_event) return;
12656 
12657     if (!block->prev || block->start_event != block->prev->end_event) remove_audio_for_track(block->start_event, -1);
12658     else insert_audio_event_at(block->start_event, -1, aclip, 0., 0.);
12659     block->start_event = new_start_event;
12660 
12661     insert_audio_event_at(block->start_event, -1, aclip, aseek, new_avel);
12662 
12663     lives_widget_queue_draw((LiVESWidget *)mt->audio_draws->data);
12664 
12665     set_in_out_spin_ranges(mt, start_tc, new_end_tc);
12666     lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12667     lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
12668 
12669     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), start_tc / TICKS_PER_SECOND_DBL);
12670 
12671     if (!was_adjusted) lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), orig_start_val);
12672 
12673     lives_signal_handler_unblock(mt->spinbutton_in, mt->spin_in_func);
12674     lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
12675 
12676     if (mt->avol_fx != -1 && !block->next) {
12677       apply_avol_filter(mt);
12678     }
12679   }
12680 }
12681 
12682 
in_anchor_toggled(LiVESToggleButton * togglebutton,livespointer user_data)12683 void in_anchor_toggled(LiVESToggleButton * togglebutton, livespointer user_data) {
12684   lives_mt *mt = (lives_mt *)user_data;
12685   track_rect *block = mt->block_selected;
12686   weed_timecode_t offset_end;
12687   double avel = 1.;
12688 
12689   if (!LIVES_IS_INTERACTIVE) return;
12690 
12691   if (mt->current_track < 0) {
12692     avel = get_audio_frame_vel(block->start_event, mt->current_track);
12693   }
12694 
12695   offset_end = block->offset_start + (double)(mt->current_track >= 0 && !mt->aud_track_selected) *
12696                (weed_timecode_t)(TICKS_PER_SECOND_DBL / mt->fps) + ((get_event_timecode(block->end_event) -
12697                    get_event_timecode(block->start_event))) * avel;
12698 
12699   block->start_anchored = !block->start_anchored;
12700 
12701   set_in_out_spin_ranges(mt, block->offset_start, offset_end);
12702 
12703   if ((block->start_anchored && block->end_anchored) || LIVES_IS_PLAYING) {
12704     lives_widget_set_sensitive(mt->spinbutton_avel, FALSE);
12705     lives_widget_set_sensitive(mt->avel_scale, FALSE);
12706   } else {
12707     lives_widget_set_sensitive(mt->spinbutton_avel, TRUE);
12708     lives_widget_set_sensitive(mt->avel_scale, TRUE);
12709   }
12710 
12711   if (mt->current_track >= 0 && mt->opts.pertrack_audio) {
12712     LiVESWidget *xeventbox;
12713     track_rect *xblock;
12714 
12715     // if video, find the audio track, and vice-versa
12716     if (mt->aud_track_selected) {
12717       xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "owner");
12718     } else {
12719       xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "atrack");
12720     }
12721     if (xeventbox) {
12722       xblock = get_block_from_time(xeventbox, get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL, mt);
12723       if (xblock) xblock->start_anchored = block->start_anchored;
12724     }
12725   }
12726 }
12727 
12728 
out_anchor_toggled(LiVESToggleButton * togglebutton,livespointer user_data)12729 void out_anchor_toggled(LiVESToggleButton * togglebutton, livespointer user_data) {
12730   lives_mt *mt = (lives_mt *)user_data;
12731   track_rect *block = mt->block_selected;
12732   weed_timecode_t offset_end;
12733   double avel = 1.;
12734 
12735   if (!LIVES_IS_INTERACTIVE) return;
12736 
12737   if (mt->current_track < 0) {
12738     avel = get_audio_frame_vel(block->start_event, mt->current_track);
12739   }
12740 
12741   offset_end = block->offset_start + (double)(mt->current_track >= 0 && !mt->aud_track_selected) *
12742                (weed_timecode_t)(TICKS_PER_SECOND_DBL / mt->fps) + ((get_event_timecode(block->end_event) -
12743                    get_event_timecode(block->start_event))) * avel;
12744 
12745   block->end_anchored = !block->end_anchored;
12746 
12747   set_in_out_spin_ranges(mt, block->offset_start, offset_end);
12748 
12749   if ((block->start_anchored && block->end_anchored) || LIVES_IS_PLAYING) {
12750     lives_widget_set_sensitive(mt->spinbutton_avel, FALSE);
12751     lives_widget_set_sensitive(mt->avel_scale, FALSE);
12752   } else {
12753     lives_widget_set_sensitive(mt->spinbutton_avel, TRUE);
12754     lives_widget_set_sensitive(mt->avel_scale, TRUE);
12755   }
12756 
12757   if (mt->current_track >= 0 && mt->opts.pertrack_audio) {
12758     LiVESWidget *xeventbox;
12759     track_rect *xblock;
12760 
12761     // if video, find the audio track, and vice-versa
12762     if (mt->aud_track_selected) {
12763       xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "owner");
12764     } else {
12765       xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "atrack");
12766     }
12767     if (xeventbox) {
12768       xblock = get_block_from_time(xeventbox, get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL, mt);
12769       if (xblock) xblock->end_anchored = block->end_anchored;
12770     }
12771   }
12772 }
12773 
12774 
12775 #define POLY_WIDTH_MARGIN 4 // I think this is the frame border width (1 px) * 2 * 2
12776 
polymorph(lives_mt * mt,lives_mt_poly_state_t poly)12777 void polymorph(lives_mt * mt, lives_mt_poly_state_t poly) {
12778   LiVESPixbuf *thumb;
12779 
12780   weed_timecode_t offset_end = 0;
12781   weed_timecode_t tc;
12782 
12783   weed_plant_t *filter, *inst;
12784   weed_plant_t *frame_event, *filter_map = NULL;
12785   weed_plant_t *init_event;
12786   weed_plant_t *prev_fm_event, *next_fm_event, *shortcut;
12787 
12788   track_rect *block = mt->block_selected;
12789 
12790   void **init_events;
12791 
12792   int *in_tracks, *out_tracks;
12793 
12794   LiVESWidget *bbox;
12795   LiVESWidget *eventbox, *xeventbox, *yeventbox, *label, *vbox;
12796 
12797   double secs;
12798   double out_end_range;
12799   double avel = 1.;
12800 
12801   char *fhash;
12802   char *fname, *otrackname, *txt;
12803 
12804   boolean is_input, is_output;
12805   boolean has_effect = FALSE;
12806   boolean has_params;
12807   boolean tab_set = FALSE;
12808   boolean start_anchored, end_anchored;
12809 
12810   int num_in_tracks, num_out_tracks;
12811   int def_out_track = 0;
12812   int num_fx = 0;
12813   int fidx;
12814   int olayer;
12815   int fxcount = 0;
12816   int nins = 1;
12817   int width = mainw->files[mt->render_file]->hsize;
12818   int height = mainw->files[mt->render_file]->vsize;
12819   int track, fromtrack;
12820   int frame_start, frame_end = 0;
12821   int filenum;
12822 
12823   static int xxwidth = 0, xxheight = 0;
12824 
12825   register int i, j;
12826 
12827   if (mt->in_sensitise) return;
12828 
12829   if (poly == mt->poly_state && poly != POLY_PARAMS && poly != POLY_FX_STACK) {
12830     return;
12831   }
12832 
12833   switch (mt->poly_state) {
12834   case (POLY_CLIPS) :
12835     lives_container_remove(LIVES_CONTAINER(mt->poly_box), mt->clip_scroll);
12836     break;
12837   case (POLY_IN_OUT) :
12838     lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
12839     lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
12840     if (lives_widget_get_parent(mt->in_out_box)) lives_widget_unparent(mt->in_out_box);
12841     if (lives_widget_get_parent(mt->avel_box)) lives_widget_unparent(mt->avel_box);
12842 
12843     break;
12844   case (POLY_PARAMS) :
12845     if (mt->framedraw) {
12846       special_cleanup(FALSE);
12847       mt->framedraw = NULL;
12848     }
12849     if (mt->current_rfx) {
12850       rfx_free(mt->current_rfx);
12851       lives_free(mt->current_rfx);
12852     }
12853     mt->current_rfx = NULL;
12854 
12855     if (mt->fx_box) {
12856       lives_widget_destroy(mt->fx_box);
12857       mt->fx_box = NULL;
12858       lives_widget_destroy(mt->fx_params_label);
12859       mt->fx_params_label = NULL;
12860       lives_container_remove(LIVES_CONTAINER(mt->poly_box), mt->fx_base_box);
12861     }
12862 
12863     if (mt->mt_frame_preview) {
12864       // put blank back in preview window
12865       lives_widget_object_ref(mainw->playarea);
12866       if (palette->style & STYLE_1) {
12867         lives_widget_set_bg_color(mt->fd_frame, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
12868       }
12869     }
12870     if (pchain && poly != POLY_PARAMS) {
12871       // no freep !
12872       lives_free(pchain);
12873       pchain = NULL;
12874     }
12875     mouse_mode_context(mt); // reset context box text
12876     mt->last_fx_type = MT_LAST_FX_NONE;
12877 
12878     lives_widget_set_sensitive(mt->fx_edit, FALSE);
12879     lives_widget_set_sensitive(mt->fx_delete, FALSE);
12880     if (poly == POLY_PARAMS) {
12881       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
12882     } else {
12883       mt->init_event = NULL;
12884       mt_show_current_frame(mt, FALSE);
12885     }
12886 
12887     break;
12888   case POLY_FX_STACK:
12889     if (poly != POLY_FX_STACK) {
12890       mt->selected_init_event = NULL;
12891       mt->fm_edit_event = NULL;
12892       mt->context_time = -1.;
12893     }
12894     break;
12895   case POLY_EFFECTS:
12896   case POLY_TRANS:
12897   case POLY_COMP:
12898     free_pkg_list();
12899     if (mt->fx_list_scroll) lives_widget_destroy(mt->fx_list_scroll);
12900     mt->fx_list_scroll = NULL;
12901     break;
12902   default:
12903     break;
12904   }
12905 
12906   if (mt->fx_list_box) lives_widget_unparent(mt->fx_list_box);
12907   mt->fx_list_box = NULL;
12908   if (mt->nb_label) lives_widget_destroy(mt->nb_label);
12909   mt->nb_label = NULL;
12910 
12911   mt->poly_state = poly;
12912 
12913   if (mt->poly_state == POLY_NONE) return; // transitional state
12914 
12915   switch (poly) {
12916   case (POLY_IN_OUT) :
12917 
12918     set_poly_tab(mt, POLY_IN_OUT);
12919 
12920     while (xxwidth < 1 || xxheight < 1) {
12921       if (lives_widget_get_allocation_width(mt->poly_box) > 1 && lives_widget_get_allocation_height(mt->poly_box) > 1) {
12922         calc_maxspect(lives_widget_get_allocation_width(mt->poly_box) / 2 - POLY_WIDTH_MARGIN,
12923                       lives_widget_get_allocation_height(mt->poly_box) - POLY_WIDTH_MARGIN / 2 - 16 * widget_opts.packing_height -
12924                       ((!block || block->ordered) ? lives_widget_get_allocation_height(mainw->spinbutton_start) : 0),
12925                       &width, &height);
12926 
12927         xxwidth = width;
12928         xxheight = height;
12929       } else {
12930         // need to force show the widgets to get sizes
12931         lives_widget_show(mt->in_hbox);
12932         lives_widget_context_update();
12933         lives_usleep(prefs->sleep_time);
12934       }
12935     }
12936 
12937     width = xxwidth;
12938     height = xxheight;
12939 
12940     mt->init_event = NULL;
12941     if (!block || block->ordered) {
12942       lives_widget_show(mt->in_hbox);
12943       lives_widget_show(mt->out_hbox);
12944       lives_idle_add_simple(show_in_out_images, (livespointer)mt);
12945     } else {
12946       lives_widget_hide(mt->in_hbox);
12947       lives_widget_hide(mt->out_hbox);
12948     }
12949 
12950     if (block) {
12951       track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox), "layer_number"));
12952 
12953       offset_end = block->offset_start + (weed_timecode_t)((double)(track >= 0) * TICKS_PER_SECOND_DBL / mt->fps) +
12954                    ((get_event_timecode(block->end_event) - get_event_timecode(block->start_event)) * ABS(avel));
12955 
12956       start_anchored = block->start_anchored;
12957       end_anchored = block->end_anchored;
12958     } else {
12959       track = 0;
12960       start_anchored = end_anchored = FALSE;
12961       filenum = mt->file_selected;
12962       frame_start = mainw->files[filenum]->start;
12963       frame_end = mainw->files[filenum]->end;
12964     }
12965 
12966     if (track > -1) {
12967       LiVESWidget *oeventbox;
12968 
12969       if (block) {
12970         secs = lives_ruler_get_value(LIVES_RULER(mt->timeline));
12971         if (mt->context_time != -1. && mt->use_context) secs = mt->context_time;
12972         if (is_audio_eventbox(block->eventbox) &&
12973             (oeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(block->eventbox),
12974                          "owner"))) {
12975           // if moving an audio block we move the associated video block first
12976           block = get_block_from_time(oeventbox, secs, mt);
12977         }
12978         if (block) {
12979           filenum = get_frame_event_clip(block->start_event, track);
12980           frame_start = calc_frame_from_time(filenum, block->offset_start / TICKS_PER_SECOND_DBL);
12981           frame_end = calc_frame_from_time(filenum, offset_end / TICKS_PER_SECOND_DBL - 1. / mt->fps);
12982         } else {
12983           filenum = mt->file_selected;
12984           frame_start = mainw->files[filenum]->start;
12985           frame_end = mainw->files[filenum]->end;
12986         }
12987       }
12988 
12989       lives_container_set_border_width(LIVES_CONTAINER(mt->poly_box), 0);
12990       filenum = mt->file_selected;
12991 
12992       if (mainw->playing_file == filenum) {
12993         mainw->files[filenum]->event_list = mt->event_list;
12994       }
12995       // start image
12996       if (mt->insurface) {
12997         thumb = make_thumb(mt, filenum, width, height, frame_start, LIVES_INTERP_NORMAL, FALSE);
12998         set_drawing_area_from_pixbuf(mt->in_image, thumb, mt->insurface);
12999         if (thumb) lives_widget_object_unref(thumb);
13000       }
13001     } else {
13002       lives_container_set_border_width(LIVES_CONTAINER(mt->poly_box), widget_opts.border_width);
13003       filenum = get_audio_frame_clip(block->start_event, track);
13004       lives_box_pack_start(LIVES_BOX(mt->poly_box), mt->avel_box, TRUE, TRUE, 0);
13005       lives_widget_show_all_from_bg(mt->avel_box);
13006       avel = get_audio_frame_vel(block->start_event, track);
13007       offset_end = block->offset_start + q_gint64((weed_timecode_t)((double)(track >= 0) * TICKS_PER_SECOND_DBL / mt->fps) +
13008                    ((get_event_timecode(block->end_event) - get_event_timecode(block->start_event)) * ABS(avel)), mt->fps);
13009     }
13010 
13011     if (!block) {
13012       lives_widget_hide(mt->checkbutton_start_anchored);
13013       lives_widget_hide(mt->checkbutton_end_anchored);
13014       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(mt->spinbutton_in), 0);
13015       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(mt->spinbutton_out), 0);
13016       lives_spin_button_configure(LIVES_SPIN_BUTTON(mt->spinbutton_in), mainw->files[filenum]->start, 1.,
13017                                   mainw->files[filenum]->frames, 1., 100.);
13018       lives_spin_button_configure(LIVES_SPIN_BUTTON(mt->spinbutton_out), mainw->files[filenum]->end, 1.,
13019                                   mainw->files[filenum]->frames, 1., 100.);
13020     } else {
13021       lives_widget_show(mt->checkbutton_start_anchored);
13022       lives_widget_show(mt->checkbutton_end_anchored);
13023       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(mt->spinbutton_in), 2);
13024       lives_spin_button_set_digits(LIVES_SPIN_BUTTON(mt->spinbutton_out), 2);
13025       lives_spin_button_configure(LIVES_SPIN_BUTTON(mt->spinbutton_in), 0., 0., 0., 1. / mt->fps, 1.);
13026       lives_spin_button_configure(LIVES_SPIN_BUTTON(mt->spinbutton_out), 0., 0., 0., 1. / mt->fps, 1.);
13027     }
13028 
13029     if (avel > 0.) {
13030       if (block) {
13031         lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_in), 0., offset_end / TICKS_PER_SECOND_DBL - 1. / mt->fps);
13032         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), block->offset_start / TICKS_PER_SECOND_DBL);
13033 
13034       } else {
13035         filenum = mt->file_selected;
13036         lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_in), 1., mainw->files[filenum]->frames);
13037         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), mainw->files[filenum]->start);
13038       }
13039       lives_signal_handler_block(mt->checkbutton_start_anchored, mt->check_start_func);
13040       lives_signal_handler_block(mt->checkbutton_avel_reverse, mt->check_avel_rev_func);
13041       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(mt->checkbutton_start_anchored), start_anchored);
13042       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(mt->checkbutton_avel_reverse), FALSE);
13043       lives_signal_handler_unblock(mt->checkbutton_avel_reverse, mt->check_avel_rev_func);
13044       lives_signal_handler_unblock(mt->checkbutton_start_anchored, mt->check_start_func);
13045     } else {
13046       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_out), 0., offset_end / TICKS_PER_SECOND_DBL - 1. / mt->fps);
13047       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), block->offset_start / TICKS_PER_SECOND_DBL);
13048       lives_signal_handler_block(mt->checkbutton_start_anchored, mt->check_start_func);
13049       lives_signal_handler_block(mt->checkbutton_avel_reverse, mt->check_avel_rev_func);
13050       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(mt->checkbutton_start_anchored), start_anchored);
13051       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(mt->checkbutton_avel_reverse), TRUE);
13052       lives_signal_handler_unblock(mt->checkbutton_avel_reverse, mt->check_avel_rev_func);
13053       lives_signal_handler_unblock(mt->checkbutton_start_anchored, mt->check_start_func);
13054     }
13055 
13056     lives_signal_handler_block(mt->spinbutton_avel, mt->spin_avel_func);
13057     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_avel), ABS(avel));
13058     lives_signal_handler_unblock(mt->spinbutton_avel, mt->spin_avel_func);
13059 
13060     if (track > -1) {
13061       // end image
13062       if (mt->outsurface) {
13063         thumb = make_thumb(mt, filenum, width, height, frame_end, LIVES_INTERP_NORMAL, FALSE);
13064         set_drawing_area_from_pixbuf(mt->out_image, thumb, mt->outsurface);
13065         if (thumb) lives_widget_object_unref(thumb);
13066       }
13067       out_end_range = count_resampled_frames(mainw->files[filenum]->frames, mainw->files[filenum]->fps, mt->fps) / mt->fps;
13068     } else {
13069       out_end_range = q_gint64(mainw->files[filenum]->laudio_time * TICKS_PER_SECOND_DBL, mt->fps) / TICKS_PER_SECOND_DBL;
13070     }
13071     if (avel > 0.) {
13072       if (block) {
13073         lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_out), block->offset_start / TICKS_PER_SECOND_DBL + 1.
13074                                     / mt->fps, out_end_range);
13075         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_out), offset_end / TICKS_PER_SECOND_DBL);
13076         if (!block->start_anchored || !block->end_anchored) {
13077           lives_widget_set_sensitive(mt->spinbutton_avel, TRUE);
13078           lives_widget_set_sensitive(mt->avel_scale, TRUE);
13079         }
13080       }
13081       lives_widget_set_sensitive(mt->spinbutton_in, TRUE);
13082       lives_widget_set_sensitive(mt->spinbutton_out, TRUE);
13083 
13084       lives_widget_grab_focus(mt->spinbutton_in);
13085     } else {
13086       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_in), block->offset_start / TICKS_PER_SECOND_DBL + 1. / mt->fps,
13087                                   out_end_range);
13088       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_in), offset_end / TICKS_PER_SECOND_DBL);
13089       lives_widget_set_sensitive(mt->spinbutton_in, FALSE);
13090       lives_widget_set_sensitive(mt->spinbutton_out, FALSE);
13091       lives_widget_set_sensitive(mt->spinbutton_avel, FALSE);
13092       lives_widget_set_sensitive(mt->avel_scale, FALSE);
13093     }
13094 
13095     lives_signal_handler_block(mt->checkbutton_end_anchored, mt->check_end_func);
13096     lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(mt->checkbutton_end_anchored), end_anchored);
13097     lives_signal_handler_unblock(mt->checkbutton_end_anchored, mt->check_end_func);
13098     lives_box_pack_start(LIVES_BOX(mt->poly_box), mt->in_out_box, TRUE, TRUE, 0);
13099 
13100     lives_widget_show_all_from_bg(mt->in_out_box);
13101     if (track > -1) {
13102       lives_widget_hide(mt->avel_box);
13103     } else {
13104       lives_widget_hide(mt->in_image);
13105       lives_widget_hide(mt->out_image);
13106     }
13107 
13108     if (!block) {
13109       lives_widget_hide(mt->checkbutton_start_anchored);
13110       lives_widget_hide(mt->checkbutton_end_anchored);
13111     }
13112 
13113     lives_signal_handler_unblock(mt->spinbutton_in, mt->spin_in_func);
13114     lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
13115 
13116     if (LIVES_IS_PLAYING) mt_desensitise(mt);
13117     else {
13118       mt_sensitise(mt);
13119       lives_widget_grab_focus(mt->spinbutton_in);
13120     }
13121 
13122     //lives_widget_set_valign(mt->in_out_box, LIVES_ALIGN_CENTER);
13123     break;
13124   case (POLY_CLIPS) :
13125     set_poly_tab(mt, POLY_CLIPS);
13126     mt->init_event = NULL;
13127     lives_box_pack_start(LIVES_BOX(mt->poly_box), mt->clip_scroll, TRUE, TRUE, 0);
13128     if (mt->is_ready) mouse_mode_context(mt);
13129     break;
13130 
13131   case (POLY_PARAMS):
13132     lives_box_pack_start(LIVES_BOX(mt->poly_box), mt->fx_base_box, TRUE, TRUE, 0);
13133 
13134     filter = get_weed_filter(mt->current_fx);
13135 
13136     if (mt->current_rfx) {
13137       rfx_free(mt->current_rfx);
13138       lives_free(mt->current_rfx);
13139     }
13140 
13141     // init an inst, in case the plugin needs to set anything
13142     inst = weed_instance_from_filter(filter);
13143     weed_reinit_effect(inst, TRUE);
13144     check_string_choice_params(inst);
13145     weed_instance_unref(inst);
13146     weed_instance_unref(inst);
13147 
13148     mt->current_rfx = weed_to_rfx(filter, FALSE);
13149 
13150     tc = get_event_timecode(mt->init_event);
13151 
13152     if (fx_dialog[1]) {
13153       lives_rfx_t *rfx = fx_dialog[1]->rfx;
13154       on_paramwindow_button_clicked(NULL, rfx);
13155       lives_widget_destroy(fx_dialog[1]->dialog);
13156       lives_freep((void **)&fx_dialog[1]);
13157     }
13158 
13159     get_track_index(mt, tc);
13160 
13161     mt->prev_fx_time = 0; // force redraw in node_spin_val_changed
13162     has_params = add_mt_param_box(mt);
13163 
13164     if (has_params && mainw->playing_file < 0) {
13165       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
13166       mt->block_tl_move = TRUE;
13167       on_node_spin_value_changed(LIVES_SPIN_BUTTON(mt->node_spinbutton), mt); // force parameter interpolation
13168       mt->block_tl_move = FALSE;
13169     }
13170     clear_context(mt);
13171     if (has_params) {
13172       add_context_label(mt, _("Drag the time slider to where you"));
13173       add_context_label(mt, _("want to set effect parameters"));
13174       add_context_label(mt, _("Set parameters, then click \"Apply\"\n"));
13175       add_context_label(mt, _("NODES are points where parameters\nhave been set.\nNodes can be deleted."));
13176     } else {
13177       add_context_label(mt, _("Effect has no parameters.\n"));
13178     }
13179     lives_widget_show_all_from_bg(mt->fx_base_box);
13180     if (!has_params) {
13181       lives_widget_hide(mt->apply_fx_button);
13182       lives_widget_hide(mt->resetp_button);
13183       lives_widget_hide(mt->del_node_button);
13184       lives_widget_hide(mt->prev_node_button);
13185       lives_widget_hide(mt->next_node_button);
13186     }
13187     set_poly_tab(mt, POLY_PARAMS);
13188     if (mt->framedraw) mt_framedraw(mt, mainw->frame_layer);
13189     break;
13190   case POLY_FX_STACK:
13191     mt->init_event = NULL;
13192     if (mt->current_track >= 0) eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
13193     else eventbox = (LiVESWidget *)mt->audio_draws->data;
13194 
13195     if (!eventbox) break; /// < can happen during mt exit
13196 
13197     secs = mt->ptr_time;
13198     if (mt->context_time != -1. && mt->use_context) secs = mt->context_time;
13199 
13200     block = get_block_from_time(eventbox, secs, mt);
13201     if (!block) {
13202       block = get_block_before(eventbox, secs, FALSE);
13203       if (block) shortcut = block->end_event;
13204       else shortcut = NULL;
13205     } else shortcut = block->start_event;
13206 
13207     tc = q_gint64(secs * TICKS_PER_SECOND_DBL, mt->fps);
13208 
13209     frame_event = get_frame_event_at(mt->event_list, tc, shortcut, TRUE);
13210 
13211     if (frame_event)
13212       filter_map = mt->fm_edit_event = get_filter_map_before(frame_event, LIVES_TRACK_ANY, NULL);
13213 
13214     mt->fx_list_box = lives_vbox_new(FALSE, 0);
13215     lives_widget_apply_theme(mt->fx_list_box, LIVES_WIDGET_STATE_NORMAL);
13216     mt->fx_list_label = lives_label_new("");
13217     // TODO ***: make function
13218     set_css_value_direct(mt->fx_list_label, LIVES_WIDGET_STATE_NORMAL, "", "padding-top", "10px");
13219     set_css_value_direct(mt->fx_list_label, LIVES_WIDGET_STATE_NORMAL, "", "padding-bottom", "10px");
13220     lives_widget_apply_theme2(mt->fx_list_label, LIVES_WIDGET_STATE_NORMAL, TRUE);
13221     lives_box_pack_start(LIVES_BOX(mt->fx_list_box), mt->fx_list_label, FALSE, TRUE, widget_opts.packing_height);
13222 
13223     mt->fx_list_scroll = lives_scrolled_window_new(NULL, NULL);
13224     lives_scrolled_window_set_policy(LIVES_SCROLLED_WINDOW(mt->fx_list_scroll), LIVES_POLICY_AUTOMATIC, LIVES_POLICY_AUTOMATIC);
13225     lives_box_pack_start(LIVES_BOX(mt->fx_list_box), mt->fx_list_scroll, TRUE, TRUE, 0);
13226 
13227     lives_box_pack_start(LIVES_BOX(mt->poly_box), mt->fx_list_box, TRUE, TRUE, 0);
13228 
13229     mt->fx_list_vbox = lives_vbox_new(FALSE, widget_opts.packing_height);
13230     lives_container_set_border_width(LIVES_CONTAINER(mt->fx_list_vbox), widget_opts.border_width);
13231     lives_scrolled_window_add_with_viewport(LIVES_SCROLLED_WINDOW(mt->fx_list_scroll), mt->fx_list_vbox);
13232     lives_widget_set_bg_color(lives_bin_get_child(LIVES_BIN(mt->fx_list_scroll)), LIVES_WIDGET_STATE_NORMAL,
13233                               &palette->normal_back);
13234 
13235     if (filter_map) {
13236       init_events = weed_get_voidptr_array_counted(filter_map, WEED_LEAF_INIT_EVENTS, &num_fx);
13237       if (num_fx > 0) {
13238         for (i = 0; i < num_fx; i++) {
13239           init_event = (weed_plant_t *)init_events[i];
13240           if (init_event) {
13241             is_input = FALSE;
13242             fromtrack = -1;
13243             in_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &num_in_tracks);
13244             if (num_in_tracks > 0) {
13245               for (j = 0; j < num_in_tracks; j++) {
13246                 if (in_tracks[j] == mt->current_track) {
13247                   is_input = TRUE;
13248                 } else if (num_in_tracks == 2) fromtrack = in_tracks[j];
13249               }
13250               lives_free(in_tracks);
13251             }
13252             is_output = FALSE;
13253             out_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_OUT_TRACKS, &num_out_tracks);
13254             if (num_out_tracks > 0) {
13255               def_out_track = out_tracks[0];
13256               for (j = 0; j < num_out_tracks; j++) {
13257                 if (out_tracks[j] == mt->current_track) {
13258                   is_output = TRUE;
13259                   break;
13260                 }
13261               }
13262               lives_free(out_tracks);
13263             }
13264 
13265             if (!is_input && !is_output) continue;
13266 
13267             has_effect = TRUE;
13268 
13269             fxcount++;
13270 
13271             fhash = weed_get_string_value(init_event, WEED_LEAF_FILTER, NULL);
13272             fidx = weed_get_idx_for_hashname(fhash, TRUE);
13273             lives_free(fhash);
13274             fname = weed_filter_idx_get_name(fidx, FALSE, FALSE);
13275 
13276             if (!is_input) {
13277               txt = lives_strdup_printf(_("%s output"), fname);
13278             } else if (!is_output && num_out_tracks > 0) {
13279               if (def_out_track > -1) {
13280                 yeventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, def_out_track);
13281                 olayer = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(yeventbox), "layer_number"));
13282                 otrackname = lives_strdup_printf(_("layer %d"), olayer);
13283               } else otrackname = (_("audio track"));
13284               txt = lives_strdup_printf(_("%s to %s"), fname, otrackname);
13285               lives_free(otrackname);
13286             } else if (num_in_tracks == 2 && num_out_tracks > 0) {
13287               if (fromtrack > -1) {
13288                 yeventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, fromtrack);
13289                 olayer = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(yeventbox), "layer_number"));
13290                 otrackname = lives_strdup_printf(_("layer %d"), olayer);
13291               } else otrackname = (_("audio track"));
13292               txt = lives_strdup_printf(_("%s from %s"), fname, otrackname);
13293               lives_free(otrackname);
13294             } else {
13295               txt = lives_strdup(fname);
13296             }
13297             xeventbox = lives_event_box_new();
13298             lives_widget_object_set_data(LIVES_WIDGET_OBJECT(xeventbox), "init_event", (livespointer)init_event);
13299 
13300             lives_widget_add_events(xeventbox, LIVES_BUTTON_RELEASE_MASK | LIVES_BUTTON_PRESS_MASK);
13301 
13302             vbox = lives_vbox_new(FALSE, 0);
13303 
13304             lives_container_set_border_width(LIVES_CONTAINER(vbox), widget_opts.border_width >> 1);
13305             lives_container_add(LIVES_CONTAINER(xeventbox), vbox);
13306             label = lives_label_new(txt);
13307             lives_free(txt);
13308             lives_free(fname);
13309 
13310             lives_container_set_border_width(LIVES_CONTAINER(xeventbox), widget_opts.border_width >> 1);
13311             lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, 0);
13312             lives_box_pack_start(LIVES_BOX(mt->fx_list_vbox), xeventbox, FALSE, FALSE, 0);
13313 
13314             if (init_event == mt->selected_init_event) {
13315               lives_widget_apply_theme2(xeventbox, LIVES_WIDGET_STATE_NORMAL, TRUE);
13316               set_child_alt_colour(xeventbox, TRUE);
13317             } else {
13318               lives_widget_apply_theme(xeventbox, LIVES_WIDGET_STATE_NORMAL);
13319               set_child_colour(xeventbox, TRUE);
13320             }
13321 
13322             lives_signal_sync_connect(LIVES_GUI_OBJECT(xeventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
13323                                       LIVES_GUI_CALLBACK(fx_ebox_pressed), (livespointer)mt);
13324           }
13325         }
13326         lives_free(init_events);
13327       }
13328     }
13329 
13330     if (has_effect) add_hsep_to_box(LIVES_BOX(mt->fx_list_box));
13331 
13332     bbox = lives_hbutton_box_new();
13333 
13334     lives_button_box_set_layout(LIVES_BUTTON_BOX(bbox), LIVES_BUTTONBOX_SPREAD);
13335     lives_box_pack_end(LIVES_BOX(mt->fx_list_box), bbox, FALSE, FALSE, widget_opts.packing_height);
13336     lives_container_set_border_width(LIVES_CONTAINER(mt->fx_list_box), widget_opts.border_width);
13337 
13338     widget_opts.expand = LIVES_EXPAND_DEFAULT_WIDTH;
13339     mt->prev_fm_button
13340       = lives_standard_button_new_with_label(_("_Prev filter map"),
13341           DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);  // Note to translators: previous filter map
13342     widget_opts.expand = LIVES_EXPAND_DEFAULT;
13343     lives_box_pack_start(LIVES_BOX(bbox), mt->prev_fm_button, FALSE, FALSE, 0);
13344 
13345     lives_widget_set_sensitive(mt->prev_fm_button, (prev_fm_event = get_prev_fm(mt, mt->current_track, frame_event)) != NULL &&
13346                                (get_event_timecode(prev_fm_event) != (get_event_timecode(frame_event))));
13347 
13348     lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->prev_fm_button), LIVES_WIDGET_CLICKED_SIGNAL,
13349                               LIVES_GUI_CALLBACK(on_prev_fm_clicked),
13350                               (livespointer)mt);
13351 
13352     if (fxcount > 1) {
13353       mt->fx_ibefore_button = lives_standard_button_new_with_label(_("Insert _before"),
13354                               DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
13355       lives_box_pack_start(LIVES_BOX(bbox), mt->fx_ibefore_button, FALSE, FALSE, 0);
13356       lives_widget_set_sensitive(mt->fx_ibefore_button, mt->fx_order == FX_ORD_NONE &&
13357                                  get_event_timecode(mt->fm_edit_event) == get_event_timecode(frame_event) &&
13358                                  mt->selected_init_event != NULL);
13359 
13360       lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->fx_ibefore_button), LIVES_WIDGET_CLICKED_SIGNAL,
13361                                 LIVES_GUI_CALLBACK(on_fx_insb_clicked), (livespointer)mt);
13362 
13363       mt->fx_iafter_button = lives_standard_button_new_with_label(_("Insert _after"),
13364                              DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
13365 
13366       lives_box_pack_start(LIVES_BOX(bbox), mt->fx_iafter_button, FALSE, FALSE, 0);
13367       lives_widget_set_sensitive(mt->fx_iafter_button, mt->fx_order == FX_ORD_NONE &&
13368                                  get_event_timecode(mt->fm_edit_event) == get_event_timecode(frame_event) &&
13369                                  mt->selected_init_event != NULL);
13370 
13371       lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->fx_iafter_button), LIVES_WIDGET_CLICKED_SIGNAL,
13372                                 LIVES_GUI_CALLBACK(on_fx_insa_clicked), (livespointer)mt);
13373 
13374     } else {
13375       mt->fx_ibefore_button = mt->fx_iafter_button = NULL;
13376     }
13377 
13378     mt->next_fm_button = lives_standard_button_new_with_label(_("_Next filter map"),
13379                          DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
13380 
13381     lives_box_pack_end(LIVES_BOX(bbox), mt->next_fm_button, FALSE, FALSE, 0);
13382 
13383     lives_widget_set_sensitive(mt->next_fm_button, (next_fm_event = get_next_fm(mt, mt->current_track, frame_event)) != NULL &&
13384                                (get_event_timecode(next_fm_event) > get_event_timecode(frame_event)));
13385 
13386     lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->next_fm_button), LIVES_WIDGET_CLICKED_SIGNAL,
13387                               LIVES_GUI_CALLBACK(on_next_fm_clicked), (livespointer)mt);
13388 
13389     if (has_effect) {
13390       do_fx_list_context(mt, fxcount);
13391     } else {
13392       widget_opts.justify = LIVES_JUSTIFY_CENTER;
13393       label = lives_standard_label_new(_("\n\nNo effects at current track,\ncurrent time.\n"));
13394       widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
13395       lives_box_pack_start(LIVES_BOX(mt->fx_list_box), label, TRUE, TRUE, 0);
13396     }
13397 
13398     lives_widget_show_all_from_bg(mt->fx_list_box);
13399 
13400     if (!has_effect) {
13401       lives_widget_hide(mt->fx_list_scroll);
13402     }
13403 
13404     set_fxlist_label(mt);
13405 
13406     set_poly_tab(mt, POLY_FX_STACK);
13407 
13408     break;
13409 
13410   case POLY_COMP:
13411     set_poly_tab(mt, POLY_COMP);
13412     clear_context(mt);
13413     add_context_label(mt, (_("Drag a compositor anywhere\non the timeline\nto apply it to the selected region.")));
13414     tab_set = TRUE;
13415     ++nins;
13416   case POLY_TRANS:
13417     if (!tab_set) {
13418       set_poly_tab(mt, POLY_TRANS);
13419       clear_context(mt);
13420       add_context_label(mt, (_("Drag a transition anywhere\non the timeline\nto apply it to the selected region.")));
13421     }
13422     tab_set = TRUE;
13423     ++nins;
13424   case POLY_EFFECTS:
13425     pkg_list = NULL;
13426     if (!tab_set) {
13427       set_poly_tab(mt, POLY_EFFECTS);
13428       clear_context(mt);
13429       add_context_label(mt, (_("Effects can be dragged\nonto blocks on the timeline.")));
13430     }
13431     mt->fx_list_box = lives_vbox_new(FALSE, 0);
13432     lives_box_pack_start(LIVES_BOX(mt->poly_box), mt->fx_list_box, TRUE, TRUE, 0);
13433     mt->fx_list_scroll = NULL;
13434 
13435     if (mt->poly_state == POLY_COMP) nins = 1000000;
13436     populate_filter_box(nins, mt, 0);
13437     break;
13438 
13439   default:
13440     break;
13441   }
13442   lives_widget_queue_draw(mt->poly_box);
13443 }
13444 
13445 
mouse_select_start(LiVESWidget * widget,LiVESXEventButton * event,lives_mt * mt)13446 static void mouse_select_start(LiVESWidget * widget, LiVESXEventButton * event, lives_mt * mt) {
13447   double timesecs;
13448   int min_x;
13449 
13450   if (!LIVES_IS_INTERACTIVE || mt->sel_locked) return;
13451 
13452   lives_widget_set_sensitive(mt->mm_menuitem, FALSE);
13453   lives_widget_set_sensitive(mt->view_sel_events, FALSE);
13454 
13455   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13456                            mt->timeline, &mt->sel_x, &mt->sel_y);
13457   timesecs = get_time_from_x(mt, mt->sel_x);
13458   mt->region_start = mt->region_end = mt->region_init = timesecs;
13459 
13460   mt->region_updating = TRUE;
13461   on_timeline_update(mt->timeline_eb, NULL, mt);
13462   mt->region_updating = FALSE;
13463 
13464   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13465                            mt->tl_eventbox, &mt->sel_x, &mt->sel_y);
13466   lives_widget_get_position(mt->timeline_eb, &min_x, NULL);
13467 
13468   if (mt->sel_x < min_x) mt->sel_x = min_x;
13469   if (mt->sel_y < 0.) mt->sel_y = 0.;
13470 
13471   lives_widget_queue_draw(mt->tl_hbox);
13472   lives_widget_queue_draw(mt->timeline);
13473 
13474   mt->tl_selecting = TRUE;
13475 }
13476 
13477 
mouse_select_end(LiVESWidget * widget,LiVESXEventButton * event,lives_mt * mt)13478 static void mouse_select_end(LiVESWidget * widget, LiVESXEventButton * event, lives_mt * mt) {
13479   if (!LIVES_IS_INTERACTIVE) return;
13480 
13481   mt->tl_selecting = FALSE;
13482   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13483                            mt->timeline, &mt->sel_x, &mt->sel_y);
13484   lives_widget_set_sensitive(mt->mm_menuitem, TRUE);
13485   lives_widget_queue_draw(mt->tl_eventbox);
13486   on_timeline_release(mt->timeline_reg, NULL, mt);
13487 }
13488 
13489 
mouse_select_move(LiVESWidget * widget,LiVESXEventMotion * event,lives_mt * mt)13490 static void mouse_select_move(LiVESWidget * widget, LiVESXEventMotion * event, lives_mt * mt) {
13491   lives_painter_t *cr;
13492 
13493   LiVESWidget *xeventbox;
13494   LiVESWidget *checkbutton;
13495 
13496   int x, y;
13497   int start_x, start_y, width, height;
13498   int current_track = mt->current_track;
13499 
13500   int rel_x, rel_y, min_x;
13501   int offs_y_start, offs_y_end, xheight;
13502 
13503   register int i;
13504 
13505   if (!LIVES_IS_INTERACTIVE) return;
13506 
13507   if (mt->block_selected) unselect_all(mt);
13508 
13509   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13510                            mt->tl_eventbox, &x, &y);
13511   lives_widget_get_position(mt->timeline_eb, &min_x, NULL);
13512 
13513   if (x < min_x) x = min_x;
13514   if (y < 0.) y = 0.;
13515 
13516   lives_widget_queue_draw(mt->tl_hbox);
13517   lives_widget_process_updates(mt->tl_eventbox);
13518 
13519   if (x >= mt->sel_x) {
13520     start_x = mt->sel_x;
13521     width = x - mt->sel_x;
13522   } else {
13523     start_x = x;
13524     width = mt->sel_x - x;
13525   }
13526   if (y >= mt->sel_y) {
13527     start_y = mt->sel_y;
13528     height = y - mt->sel_y;
13529   } else {
13530     start_y = y;
13531     height = mt->sel_y - y;
13532   }
13533 
13534   if (start_x < 0) start_x = 0;
13535   if (start_y < 0) start_y = 0;
13536 
13537   cr = lives_painter_create_from_surface(mt->tl_ev_surf);
13538   lives_painter_set_source_rgb_from_lives_widget_color(cr, &palette->black);
13539 
13540   lives_painter_rectangle(cr, start_x, start_y, width, height);
13541   lives_painter_fill(cr);
13542 
13543   for (i = 0; i < mt->num_video_tracks; i++) {
13544     xeventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, i);
13545     if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), HIDDEN_KEY)) == 0) {
13546       xheight = lives_widget_get_allocation_height(xeventbox);
13547       checkbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "checkbutton");
13548       lives_widget_get_position(xeventbox, &rel_x, &rel_y);
13549       if (start_y > (rel_y + xheight / 2) || (start_y + height) < (rel_y + xheight / 2)) {
13550 #ifdef ENABLE_GIW
13551         if (!prefs->lamp_buttons) {
13552 #endif
13553           if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(checkbutton))) {
13554             lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(checkbutton), FALSE);
13555             mt->current_track = current_track;
13556             track_select(mt);
13557           }
13558 #ifdef ENABLE_GIW
13559         } else {
13560           if (giw_led_get_mode(GIW_LED(checkbutton))) {
13561             giw_led_set_mode(GIW_LED(checkbutton), FALSE);
13562             mt->current_track = current_track;
13563             track_select(mt);
13564           }
13565         }
13566 #endif
13567         continue;
13568       }
13569       offs_y_start = 0;
13570       offs_y_end = xheight;
13571 
13572       if (start_y < rel_y + xheight) {
13573         offs_y_start = start_y - rel_y;
13574         lives_painter_move_to(cr, start_x - rel_x, offs_y_start);
13575         lives_painter_line_to(cr, start_x + width - rel_x - 1, offs_y_start);
13576       }
13577       if (start_y + height < rel_y + xheight) {
13578         offs_y_end = start_y - rel_y + height;
13579         lives_painter_move_to(cr, start_x - rel_x, offs_y_end);
13580         lives_painter_line_to(cr, start_x + width - rel_x - 1, offs_y_end);
13581       }
13582 
13583       lives_painter_move_to(cr, start_x - rel_x, offs_y_start);
13584       lives_painter_line_to(cr, start_x - rel_x, offs_y_end);
13585       lives_painter_move_to(cr, start_x - rel_x - 1, offs_y_start);
13586       lives_painter_line_to(cr, start_x - rel_x - 1, offs_y_end);
13587       lives_painter_stroke(cr);
13588 
13589 #ifdef ENABLE_GIW
13590       if (!prefs->lamp_buttons) {
13591 #endif
13592         if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(checkbutton))) {
13593           lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(checkbutton), TRUE);
13594           mt->current_track = current_track;
13595           track_select(mt);
13596         }
13597 #ifdef ENABLE_GIW
13598       } else {
13599         if (!giw_led_get_mode(GIW_LED(checkbutton))) {
13600           giw_led_set_mode(GIW_LED(checkbutton), TRUE);
13601           mt->current_track = current_track;
13602           track_select(mt);
13603         }
13604       }
13605 #endif
13606     }
13607   }
13608 
13609   lives_painter_destroy(cr);
13610 
13611   if (widget != mt->timeline_eb) {
13612     mt->region_updating = TRUE;
13613     on_timeline_update(mt->timeline_eb, NULL, mt);
13614     mt->region_updating = FALSE;
13615   }
13616 }
13617 
13618 
do_block_context(lives_mt * mt,LiVESXEventButton * event,track_rect * block)13619 void do_block_context(lives_mt * mt, LiVESXEventButton * event, track_rect * block) {
13620   // pop up a context menu when a selected block is right clicked on
13621 
13622   LiVESWidget *delete_block;
13623   LiVESWidget *split_here;
13624   LiVESWidget *list_fx_here;
13625   LiVESWidget *selblock;
13626   LiVESWidget *avol;
13627   LiVESWidget *menu = lives_menu_new();
13628 
13629   double block_start_time, block_end_time;
13630 
13631   //mouse_select_end(NULL,mt);
13632   if (!LIVES_IS_INTERACTIVE) return;
13633 
13634   lives_menu_set_title(LIVES_MENU(menu), _("Selected Block/Frame"));
13635 
13636   selblock = lives_standard_menu_item_new_with_label(_("_Select this Block"));
13637   lives_container_add(LIVES_CONTAINER(menu), selblock);
13638 
13639   lives_signal_connect(LIVES_GUI_OBJECT(selblock), LIVES_WIDGET_ACTIVATE_SIGNAL,
13640                        LIVES_GUI_CALLBACK(selblock_cb), (livespointer)mt);
13641 
13642   if (block->ordered) { // TODO
13643     split_here = lives_standard_menu_item_new_with_label(_("_Split Block At Cursor"));
13644     lives_container_add(LIVES_CONTAINER(menu), split_here);
13645 
13646     // disable if cursor out block
13647     block_start_time = get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL + 1. / mainw->files[mt->render_file]->fps;
13648     block_end_time = get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL + (double)(!is_audio_eventbox(
13649                        block->eventbox)) / mainw->files[mt->render_file]->fps;
13650     if (mt->ptr_time < block_start_time || mt->ptr_time >= block_end_time)
13651       lives_widget_set_sensitive(split_here, FALSE);
13652 
13653     lives_signal_sync_connect(LIVES_GUI_OBJECT(split_here), LIVES_WIDGET_ACTIVATE_SIGNAL,
13654                               LIVES_GUI_CALLBACK(on_split_activate), (livespointer)mt);
13655   }
13656 
13657   list_fx_here = lives_standard_menu_item_new_with_label(_("List _Effects Here"));
13658   lives_container_add(LIVES_CONTAINER(menu), list_fx_here);
13659 
13660   lives_signal_connect(LIVES_GUI_OBJECT(list_fx_here), LIVES_WIDGET_ACTIVATE_SIGNAL,
13661                        LIVES_GUI_CALLBACK(list_fx_here_cb), (livespointer)mt);
13662 
13663   if (is_audio_eventbox(block->eventbox) && mt->avol_init_event) {
13664     char *avol_fxname = weed_get_string_value(get_weed_filter(mt->avol_fx), WEED_LEAF_NAME, NULL);
13665     char *text = lives_strdup_printf(_("_Adjust %s"), avol_fxname);
13666     avol = lives_standard_menu_item_new_with_label(text);
13667     lives_free(avol_fxname);
13668     lives_free(text);
13669     lives_container_add(LIVES_CONTAINER(menu), avol);
13670 
13671     lives_signal_connect(LIVES_GUI_OBJECT(avol), LIVES_WIDGET_ACTIVATE_SIGNAL,
13672                          LIVES_GUI_CALLBACK(mt_avol_quick), (livespointer)mt);
13673 
13674     if (!mt->event_list) lives_widget_set_sensitive(avol, FALSE);
13675   }
13676 
13677   delete_block = lives_standard_menu_item_new_with_label(_("_Delete this Block"));
13678   lives_container_add(LIVES_CONTAINER(menu), delete_block);
13679   if (mt->is_rendering) lives_widget_set_sensitive(delete_block, FALSE);
13680 
13681   lives_signal_connect(LIVES_GUI_OBJECT(delete_block), LIVES_WIDGET_ACTIVATE_SIGNAL,
13682                        LIVES_GUI_CALLBACK(delete_block_cb), (livespointer)mt);
13683 
13684   if (palette->style & STYLE_1) {
13685     set_child_alt_colour(menu, TRUE);
13686     lives_widget_set_bg_color(menu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
13687     lives_widget_set_fg_color(menu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
13688   }
13689 
13690   lives_widget_show_all(menu);
13691   lives_menu_popup(LIVES_MENU(menu), event);
13692 }
13693 
13694 
do_track_context(lives_mt * mt,LiVESXEventButton * event,double timesecs,int track)13695 void do_track_context(lives_mt * mt, LiVESXEventButton * event, double timesecs, int track) {
13696   // pop up a context menu when track is right clicked on
13697 
13698   LiVESWidget *insert_here, *avol;
13699   LiVESWidget *menu = lives_menu_new();
13700 
13701   boolean has_something = FALSE;
13702   boolean needs_idlefunc = FALSE;
13703   boolean did_backup = mt->did_backup;
13704 
13705   if (!LIVES_IS_INTERACTIVE) return;
13706 
13707   if (mt->idlefunc > 0) {
13708     lives_source_remove(mt->idlefunc);
13709     mt->idlefunc = 0;
13710     needs_idlefunc = TRUE;
13711   }
13712 
13713   mouse_select_end(NULL, event, mt);
13714 
13715   lives_menu_set_title(LIVES_MENU(menu), _("Selected Frame"));
13716 
13717   if (mt->file_selected > 0 && ((track < 0 && mainw->files[mt->file_selected]->achans > 0 &&
13718                                  mainw->files[mt->file_selected]->laudio_time > 0.) ||
13719                                 (track >= 0 && mainw->files[mt->file_selected]->frames > 0))) {
13720     if (track >= 0) {
13721       insert_here = lives_standard_menu_item_new_with_label(_("_Insert Here"));
13722       lives_signal_connect(LIVES_GUI_OBJECT(insert_here), LIVES_WIDGET_ACTIVATE_SIGNAL,
13723                            LIVES_GUI_CALLBACK(insert_at_ctx_cb),
13724                            (livespointer)mt);
13725     } else {
13726       insert_here = lives_standard_menu_item_new_with_label(_("_Insert Audio Here"));
13727       lives_signal_connect(LIVES_GUI_OBJECT(insert_here), LIVES_WIDGET_ACTIVATE_SIGNAL,
13728                            LIVES_GUI_CALLBACK(insert_audio_at_ctx_cb),
13729                            (livespointer)mt);
13730     }
13731     lives_container_add(LIVES_CONTAINER(menu), insert_here);
13732     has_something = TRUE;
13733   }
13734 
13735   if (mt->audio_draws && (track < 0 || mt->opts.pertrack_audio) && mt->event_list) {
13736     char *avol_fxname = weed_get_string_value(get_weed_filter(mt->avol_fx), WEED_LEAF_NAME, NULL);
13737     char *text = lives_strdup_printf(_("_Adjust %s"), avol_fxname);
13738     avol = lives_standard_menu_item_new_with_label(text);
13739     lives_free(avol_fxname);
13740     lives_free(text);
13741     lives_container_add(LIVES_CONTAINER(menu), avol);
13742 
13743     lives_signal_connect(LIVES_GUI_OBJECT(avol), LIVES_WIDGET_ACTIVATE_SIGNAL,
13744                          LIVES_GUI_CALLBACK(mt_avol_quick),
13745                          (livespointer)mt);
13746 
13747     if (!mt->event_list) lives_widget_set_sensitive(avol, FALSE);
13748 
13749     has_something = TRUE;
13750   }
13751 
13752   if (has_something) {
13753     if (palette->style & STYLE_1) {
13754       set_child_alt_colour(menu, TRUE);
13755       lives_widget_set_bg_color(menu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
13756       lives_widget_set_fg_color(menu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
13757     }
13758 
13759     lives_widget_show_all(menu);
13760     lives_menu_popup(LIVES_MENU(menu), event);
13761 
13762     lives_signal_connect(LIVES_GUI_OBJECT(menu), LIVES_WIDGET_UNMAP_SIGNAL,
13763                          LIVES_GUI_CALLBACK(rdrw_cb),
13764                          (livespointer)mt);
13765   } else lives_widget_destroy(menu);
13766 
13767   if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
13768     mt->idlefunc = mt_idle_add(mt);
13769   }
13770 }
13771 
13772 
on_track_release(LiVESWidget * eventbox,LiVESXEventButton * event,livespointer user_data)13773 boolean on_track_release(LiVESWidget * eventbox, LiVESXEventButton * event, livespointer user_data) {
13774   lives_mt *mt = (lives_mt *)user_data;
13775   weed_timecode_t tc, tcpp;
13776   LiVESList *list;
13777   LiVESWidget *xeventbox;
13778   LiVESWidget *oeventbox;
13779   LiVESWidget *xlabelbox;
13780   LiVESWidget *xahbox;
13781   LiVESXWindow *window;
13782 
13783   double timesecs;
13784 
13785   boolean got_track = FALSE;
13786   boolean needs_idlefunc = FALSE;
13787   boolean did_backup = mt->did_backup;
13788 
13789   int x, y;
13790   int track = 0;
13791 
13792   int win_x, win_y;
13793   int old_track = mt->current_track;
13794 
13795   register int i;
13796 
13797   if (!LIVES_IS_INTERACTIVE) return FALSE;
13798 
13799   if (mt->idlefunc > 0) {
13800     lives_source_remove(mt->idlefunc);
13801     mt->idlefunc = 0;
13802     needs_idlefunc = TRUE;
13803   }
13804 
13805   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
13806 
13807   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13808                            eventbox, &x, &y);
13809   timesecs = get_time_from_x(mt, x);
13810   tc = timesecs * TICKS_PER_SECOND;
13811 
13812   window = lives_display_get_window_at_pointer
13813            ((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13814             mt->display, &win_x, &win_y);
13815 
13816   if (mainw->files[mt->render_file]->achans > 0) {
13817     i = 0;
13818     for (list = mt->audio_draws; list; list = list->next, i++) {
13819       xeventbox = (LiVESWidget *)list->data;
13820       oeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "owner");
13821       if (i >= mt->opts.back_audio_tracks &&
13822           !LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(oeventbox), "expanded"))) continue;
13823       xlabelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "labelbox");
13824       xahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "ahbox");
13825       if (lives_widget_get_xwindow(xeventbox) == window || lives_widget_get_xwindow(xlabelbox) == window ||
13826           lives_widget_get_xwindow(xahbox) == window) {
13827         track = i - 1;
13828         got_track = TRUE;
13829         mt->aud_track_selected = TRUE;
13830         break;
13831       }
13832     }
13833   }
13834 
13835   if (track != -1) {
13836     for (list = mt->video_draws; list; list = list->next) {
13837       xeventbox = (LiVESWidget *)list->data;
13838       xlabelbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "labelbox");
13839       xahbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "ahbox");
13840       if (lives_widget_get_xwindow(xeventbox) == window || lives_widget_get_xwindow(xlabelbox) == window ||
13841           lives_widget_get_xwindow(xahbox) == window) {
13842         track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "layer_number"));
13843         mt->aud_track_selected = FALSE;
13844         got_track = TRUE;
13845         break;
13846       }
13847     }
13848   }
13849 
13850   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) {
13851     mouse_select_end(eventbox, event, mt);
13852     lives_signal_handler_block(mt->tl_eventbox, mt->mouse_mot2);
13853     mt->sel_y -= y + 2;
13854   } else {
13855     if (mt->hotspot_x != 0 || mt->hotspot_y != 0) {
13856       LiVESXScreen *screen;
13857       int abs_x, abs_y;
13858 
13859       int height = lives_widget_get_allocation_height(LIVES_WIDGET(lives_list_nth_data(mt->video_draws, 0)));
13860       lives_display_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13861                                 mt->display, &screen, &abs_x, &abs_y, NULL);
13862       lives_display_warp_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13863                                  mt->display, screen, abs_x + mt->hotspot_x, abs_y + mt->hotspot_y - height / 2);
13864       mt->hotspot_x = mt->hotspot_y = 0;
13865       // we need to call this to warp the pointer
13866       //lives_widget_context_update();
13867       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
13868     }
13869 
13870     if (doubleclick) {
13871       // this is a double-click
13872       mt->putative_block = get_block_from_time(eventbox, timesecs, mt);
13873       select_block(mt);
13874       mt->putative_block = NULL;
13875       doubleclick = FALSE;
13876       goto track_rel_done;
13877     }
13878 
13879     if (got_track && !mt->is_rendering && mt->putative_block && !LIVES_IS_PLAYING &&
13880         event->button == 1) {
13881       weed_timecode_t start_tc;
13882       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
13883 
13884       mt_desensitise(mt);
13885 
13886       start_tc = get_event_timecode(mt->putative_block->start_event);
13887 
13888       // timecodes per pixel
13889       tcpp = TICKS_PER_SECOND_DBL * ((mt->tl_max - mt->tl_min) /
13890                                      (double)lives_widget_get_allocation_width
13891                                      (LIVES_WIDGET(lives_list_nth_data(mt->video_draws, 0))));
13892 
13893       // need to move at least 1.5 pixels, or to another track
13894       if ((track != mt->current_track || (tc - start_tc > (tcpp * 3 / 2)) || (start_tc - tc > (tcpp * 3 / 2))) &&
13895           ((old_track < 0 && track < 0) || (old_track >= 0 && track >= 0))) {
13896         move_block(mt, mt->putative_block, timesecs, old_track, track);
13897         mt->putative_block = NULL;
13898 
13899         lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13900                                  eventbox, &x, &y);
13901         timesecs = get_time_from_x(mt, x);
13902 
13903         mt_tl_move(mt, timesecs);
13904       }
13905     }
13906   }
13907 
13908 track_rel_done:
13909 
13910   if (!LIVES_IS_PLAYING) mt_sensitise(mt);
13911   mt->hotspot_x = mt->hotspot_y = 0;
13912   mt->putative_block = NULL;
13913 
13914   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
13915   lives_widget_set_sensitive(mt->mm_menuitem, TRUE);
13916 
13917   if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
13918     mt->idlefunc = mt_idle_add(mt);
13919   }
13920 
13921   return TRUE;
13922 }
13923 
13924 
on_track_header_click(LiVESWidget * widget,LiVESXEventButton * event,livespointer user_data)13925 boolean on_track_header_click(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
13926   lives_mt *mt = (lives_mt *)user_data;
13927   if (!LIVES_IS_INTERACTIVE) return FALSE;
13928   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) {
13929     mouse_select_start(widget, event, mt);
13930     lives_signal_handler_unblock(widget, mt->mouse_mot1);
13931   }
13932   return TRUE;
13933 }
13934 
13935 
on_track_header_release(LiVESWidget * widget,LiVESXEventButton * event,livespointer user_data)13936 boolean on_track_header_release(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
13937   lives_mt *mt = (lives_mt *)user_data;
13938   if (!LIVES_IS_INTERACTIVE) return FALSE;
13939   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) {
13940     mouse_select_end(widget, event, mt);
13941     lives_signal_handler_block(widget, mt->mouse_mot1);
13942   }
13943   return TRUE;
13944 }
13945 
13946 
on_track_between_click(LiVESWidget * widget,LiVESXEventButton * event,livespointer user_data)13947 boolean on_track_between_click(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
13948   lives_mt *mt = (lives_mt *)user_data;
13949   if (!LIVES_IS_INTERACTIVE) return FALSE;
13950   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) {
13951     mouse_select_start(widget, event, mt);
13952     lives_signal_handler_unblock(mt->tl_eventbox, mt->mouse_mot2);
13953   }
13954   return TRUE;
13955 }
13956 
13957 
on_track_between_release(LiVESWidget * widget,LiVESXEventButton * event,livespointer user_data)13958 boolean on_track_between_release(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
13959   lives_mt *mt = (lives_mt *)user_data;
13960   if (!LIVES_IS_INTERACTIVE) return FALSE;
13961   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) {
13962     mouse_select_end(widget, event, mt);
13963     lives_signal_handler_block(mt->tl_eventbox, mt->mouse_mot2);
13964   }
13965   return TRUE;
13966 }
13967 
13968 
on_track_click(LiVESWidget * eventbox,LiVESXEventButton * event,livespointer user_data)13969 boolean on_track_click(LiVESWidget * eventbox, LiVESXEventButton * event, livespointer user_data) {
13970   lives_mt *mt = (lives_mt *)user_data;
13971 
13972   track_rect *block;
13973 
13974   double timesecs;
13975 
13976   int x, y;
13977   int track;
13978   int filenum = -1;
13979 
13980   //doubleclick=FALSE;
13981 
13982   if (!LIVES_IS_INTERACTIVE) return FALSE;
13983 
13984   mt->aud_track_selected = is_audio_eventbox(eventbox);
13985 
13986   lives_widget_set_sensitive(mt->mm_menuitem, FALSE);
13987 
13988   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
13989                            eventbox, &x, &y);
13990 
13991   timesecs = get_time_from_x(mt, x + mt->hotspot_x);
13992 
13993   if (mainw->files[mt->render_file]->achans == 0 || !mt->audio_draws || (mt->opts.back_audio_tracks == 0 ||
13994       eventbox != mt->audio_draws->data))
13995     track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number"));
13996   else track = -1;
13997   block = mt->putative_block = get_block_from_time(eventbox, timesecs, mt);
13998 
13999   unselect_all(mt); // set all blocks unselected
14000 
14001   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT && event->type == LIVES_BUTTON_PRESS) {
14002     mouse_select_start(eventbox, event, mt);
14003     lives_signal_handler_unblock(mt->tl_eventbox, mt->mouse_mot2);
14004   } else {
14005     if (lives_event_get_time((LiVESXEvent *)event) - last_press_time < capable->dclick_time
14006         && (x - last_x) * (x - last_x) + (y - last_y) * (y - last_y) < capable->dclick_dist * capable->dclick_dist) {
14007       doubleclick = TRUE;
14008       return TRUE;
14009     } else {
14010       // single click, TODO - locate the frame for the track in event_list
14011       if (event->button == 1) {
14012         if (!LIVES_IS_PLAYING) {
14013           mt->fm_edit_event = NULL;
14014           mt_tl_move(mt, timesecs);
14015         }
14016       }
14017 
14018       // for a double click, gdk normally sends 2 single click events,
14019       // followed by a double click
14020 
14021       // calling mt_tl_move() causes any double click to be triggered during
14022       // the second single click and then we return here
14023       // however, this is quite useful as we can skip the next bit
14024 
14025       if (event->time != dclick_time) {
14026         show_track_info(mt, eventbox, track, timesecs);
14027         if (block) {
14028           if (!is_audio_eventbox(eventbox))
14029             filenum = get_frame_event_clip(block->start_event, track);
14030           else filenum = get_audio_frame_clip(block->start_event, -1);
14031           if (filenum != mainw->scrap_file && filenum != mainw->ascrap_file) {
14032             mt->clip_selected = mt_clip_from_file(mt, filenum);
14033             mt_clip_select(mt, TRUE);
14034           }
14035 
14036           if (!mt->is_rendering) {
14037             double start_secs, end_secs;
14038 
14039             LiVESXScreen *screen;
14040             int abs_x, abs_y;
14041 
14042             int ebwidth = lives_widget_get_allocation_width(mt->timeline);
14043 
14044             double width = ((end_secs = (get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL)) -
14045                             (start_secs = (get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL)) + 1. / mt->fps);
14046             int height;
14047 
14048             // start point must be on timeline to move a block
14049             if (event->button == 1) {
14050               if (block && (mt->tl_min * TICKS_PER_SECOND_DBL > get_event_timecode(block->start_event))) {
14051                 mt->putative_block = NULL;
14052                 return TRUE;
14053               }
14054               if (!is_audio_eventbox(eventbox))
14055                 height = lives_widget_get_allocation_height(LIVES_WIDGET(lives_list_nth_data(mt->video_draws, 0)));
14056               else height = lives_widget_get_allocation_height(LIVES_WIDGET(mt->audio_draws->data));
14057 
14058               width = (width / (mt->tl_max - mt->tl_min) * (double)ebwidth);
14059               if (width > ebwidth) width = ebwidth;
14060               if (width < 2) width = 2;
14061 
14062               mt->hotspot_x = x - (int)((ebwidth * ((double)start_secs - mt->tl_min) / (mt->tl_max - mt->tl_min)) + .5);
14063               mt->hotspot_y = y;
14064               lives_display_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
14065                                         mt->display, &screen, &abs_x, &abs_y, NULL);
14066               lives_display_warp_pointer((LiVESXDevice *)mainw->mgeom[prefs->gui_monitor].mouse_device,
14067                                          mt->display, screen, abs_x - mt->hotspot_x, abs_y - y + height / 2);
14068               if (track >= 0 && !mt->aud_track_selected) {
14069                 if (mainw->files[filenum]->clip_type == CLIP_TYPE_FILE) {
14070                   lives_clip_data_t *cdata = ((lives_decoder_t *)mainw->files[filenum]->ext_src)->cdata;
14071                   if (cdata && !(cdata->seek_flag & LIVES_SEEK_FAST)) {
14072                     mt_set_cursor_style(mt, LIVES_CURSOR_VIDEO_BLOCK, width, height, filenum, 0, height / 2);
14073                   } else {
14074                     mt_set_cursor_style(mt, LIVES_CURSOR_BLOCK, width, height, filenum, 0, height / 2);
14075                   }
14076                 } else {
14077                   mt_set_cursor_style(mt, LIVES_CURSOR_BLOCK, width, height, filenum, 0, height / 2);
14078                 }
14079               } else mt_set_cursor_style(mt, LIVES_CURSOR_AUDIO_BLOCK, width, height, filenum, 0, height / 2);
14080 	      // *INDENT-OFF*
14081             }}}}
14082       else {
14083         mt->putative_block = NULL; // please don't move the block
14084       }}}
14085   // *INDENT-ON*
14086 
14087   mt->current_track = track;
14088   track_select(mt);
14089 
14090   if (event->button == 3 && !LIVES_IS_PLAYING) {
14091     lives_widget_set_sensitive(mt->mm_menuitem, TRUE);
14092     mt->context_time = timesecs;
14093     if (block) {
14094       // context menu for a selected block
14095       mt->putative_block = block;
14096       do_block_context(mt, event, block);
14097       return TRUE;
14098     } else {
14099       do_track_context(mt, event, timesecs, track);
14100       return TRUE;
14101     }
14102   }
14103 
14104   last_press_time = lives_event_get_time((LiVESXEvent *)event);
14105   last_x = x;
14106   last_y = y;
14107 
14108   return TRUE;
14109 }
14110 
14111 
on_track_move(LiVESWidget * widget,LiVESXEventMotion * event,livespointer user_data)14112 boolean on_track_move(LiVESWidget * widget, LiVESXEventMotion * event, livespointer user_data) {
14113   // used for mouse mode SELECT
14114   lives_mt *mt = (lives_mt *)user_data;
14115   if (!LIVES_IS_INTERACTIVE) return FALSE;
14116   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) mouse_select_move(widget, event, mt);
14117   return TRUE;
14118 }
14119 
14120 
on_track_header_move(LiVESWidget * widget,LiVESXEventMotion * event,livespointer user_data)14121 boolean on_track_header_move(LiVESWidget * widget, LiVESXEventMotion * event, livespointer user_data) {
14122   // used for mouse mode SELECT
14123   lives_mt *mt = (lives_mt *)user_data;
14124   if (!LIVES_IS_INTERACTIVE) return FALSE;
14125   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) mouse_select_move(widget, event, mt);
14126   return TRUE;
14127 }
14128 
14129 
unpaint_line(lives_mt * mt,LiVESWidget * eventbox)14130 void unpaint_line(lives_mt * mt, LiVESWidget * eventbox) {
14131   int xoffset;
14132   int ebwidth;
14133 
14134   if (mt->redraw_block) return; // don't update during expose event, otherwise we might leave lines
14135   if (!lives_widget_is_visible(eventbox)) return;
14136   if (!mt->is_ready) return;
14137   if ((xoffset = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "has_line"))) < 0) return;
14138 
14139   ebwidth = lives_widget_get_allocation_width(mt->timeline);
14140 
14141   if (xoffset < ebwidth) {
14142     lives_widget_queue_draw_area(eventbox, xoffset - 4, 0, 9, lives_widget_get_allocation_height(eventbox));
14143     // lives_widget_process_updates(eventbox);
14144   }
14145 
14146   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "has_line", LIVES_INT_TO_POINTER(-1));
14147 }
14148 
14149 
unpaint_lines(lives_mt * mt)14150 void unpaint_lines(lives_mt * mt) {
14151   if (!mt->is_ready) return;
14152   unpaint_line(mt, mt->timeline_table);
14153   return;
14154 }
14155 
14156 
paint_line(lives_mt * mt,LiVESWidget * eventbox,int offset,double currtime,lives_painter_t * cr)14157 static void paint_line(lives_mt * mt, LiVESWidget * eventbox, int offset, double currtime,
14158                        lives_painter_t *cr) {
14159   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "has_line", LIVES_INT_TO_POINTER(offset));
14160   lives_widget_queue_draw(eventbox);
14161 }
14162 
14163 
paint_lines(lives_mt * mt,double currtime,boolean unpaint,lives_painter_t * cr)14164 static void paint_lines(lives_mt * mt, double currtime, boolean unpaint, lives_painter_t *cr) {
14165   int ebwidth;
14166   int offset, off_x;
14167 
14168   if (!mt->is_ready) return;
14169 
14170   ebwidth = lives_widget_get_allocation_width(mt->timeline);
14171 
14172   if (unpaint) unpaint_line(mt, mt->timeline_table);
14173 
14174   if (currtime < mt->tl_min || currtime > mt->tl_max) return;
14175   offset = (currtime - mt->tl_min) / (mt->tl_max - mt->tl_min) * (double)ebwidth;
14176 
14177   lives_widget_get_position(mt->timeline_eb, &off_x, NULL);
14178   offset += off_x;
14179 
14180   if (offset > off_x && offset < ebwidth + off_x) {
14181     paint_line(mt, mt->timeline_table, offset, currtime, cr);
14182   }
14183 }
14184 
14185 
_animate_multitrack(lives_mt * mt)14186 static void _animate_multitrack(lives_mt * mt) {
14187   // update timeline pointer(s)
14188   double currtime = mainw->currticks / TICKS_PER_SECOND_DBL;
14189   double tl_page;
14190 
14191   int offset, offset_old;
14192 
14193   int ebwidth = lives_widget_get_allocation_width(mt->timeline);
14194   update_timecodes(mt, currtime);
14195 
14196   offset = (currtime - mt->tl_min) / (mt->tl_max - mt->tl_min) * (double)ebwidth;
14197   offset_old = (lives_ruler_get_value(LIVES_RULER(mt->timeline)) - mt->tl_min)
14198                / (mt->tl_max - mt->tl_min) * (double)ebwidth;
14199 
14200   mt->ptr_time = lives_ruler_set_value(LIVES_RULER(mt->timeline), currtime);
14201 
14202   if (offset == offset_old) return;
14203 
14204   if (mt->opts.follow_playback) {
14205     if (currtime > (mt->tl_min + ((tl_page = mt->tl_max - mt->tl_min)) * .85) &&
14206         event_list_get_end_secs(mt->event_list) > mt->tl_max) {
14207       // scroll right one page
14208       mt->tl_min += tl_page * .85;
14209       mt->tl_max += tl_page * .85;
14210       mt_zoom(mt, -1.);
14211     }
14212   }
14213 
14214   if ((offset < 0. && offset_old < 0.) || (offset > (double)ebwidth && offset_old > (double)ebwidth)) return;
14215 
14216   lives_widget_queue_draw(mt->timeline);
14217   if (mt->redraw_block) return; // don't update during expose event, otherwise we might leave lines
14218 
14219   paint_lines(mt, currtime, TRUE, NULL);
14220 }
14221 
14222 
animate_multitrack(lives_mt * mt)14223 void animate_multitrack(lives_mt * mt) {
14224   main_thread_execute((lives_funcptr_t)_animate_multitrack, 0, NULL, "v", mt);
14225 }
14226 
14227 ////////////////////////////////////////////////////
14228 // menuitem callbacks
14229 
multitrack_end(LiVESMenuItem * menuitem,livespointer user_data)14230 static boolean multitrack_end(LiVESMenuItem * menuitem, livespointer user_data) {
14231   lives_mt *mt = (lives_mt *)user_data;
14232   return multitrack_delete(mt, !(prefs->warning_mask & WARN_MASK_EXIT_MT) || !menuitem);
14233 }
14234 
14235 
14236 // callbacks for future adding to osc.c
multitrack_end_cb(LiVESMenuItem * menuitem,livespointer user_data)14237 void multitrack_end_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14238   lives_mt *mt = (lives_mt *)user_data;
14239   if (mt->is_rendering) return;
14240   multitrack_end(menuitem, user_data);
14241 }
14242 
14243 
insert_here_cb(LiVESMenuItem * menuitem,livespointer user_data)14244 void insert_here_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14245   lives_mt *mt = (lives_mt *)user_data;
14246   if (mt->is_rendering) return;
14247   multitrack_insert(NULL, user_data);
14248 }
14249 
14250 
insert_at_ctx_cb(LiVESMenuItem * menuitem,livespointer user_data)14251 void insert_at_ctx_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14252   lives_mt *mt = (lives_mt *)user_data;
14253   if (mt->is_rendering) return;
14254   mt->use_context = TRUE;
14255   multitrack_insert(NULL, user_data);
14256 }
14257 
14258 
edit_start_end_cb(LiVESMenuItem * menuitem,livespointer user_data)14259 void edit_start_end_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14260   lives_mt *mt = (lives_mt *)user_data;
14261   if (mt->is_rendering) return;
14262   multitrack_adj_start_end(NULL, user_data);
14263 }
14264 
14265 
close_clip_cb(LiVESMenuItem * menuitem,livespointer user_data)14266 void close_clip_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14267   lives_mt *mt = (lives_mt *)user_data;
14268   if (mt->is_rendering) return;
14269   on_close_activate(NULL, NULL);
14270 }
14271 
14272 
show_clipinfo_cb(LiVESMenuItem * menuitem,livespointer user_data)14273 void show_clipinfo_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14274   lives_mt *mt = (lives_mt *)user_data;
14275   int current_file = mainw->current_file;
14276   if (mt->file_selected != -1) {
14277     mainw->current_file = mt->file_selected;
14278     on_show_file_info_activate(NULL, NULL);
14279     mainw->current_file = current_file;
14280   }
14281 }
14282 
14283 
insert_audio_here_cb(LiVESMenuItem * menuitem,livespointer user_data)14284 void insert_audio_here_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14285   lives_mt *mt = (lives_mt *)user_data;
14286   if (mt->is_rendering) return;
14287   multitrack_audio_insert(NULL, user_data);
14288 }
14289 
14290 
insert_audio_at_ctx_cb(LiVESMenuItem * menuitem,livespointer user_data)14291 void insert_audio_at_ctx_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14292   lives_mt *mt = (lives_mt *)user_data;
14293   if (mt->is_rendering) return;
14294   mt->use_context = TRUE;
14295   multitrack_audio_insert(NULL, user_data);
14296 }
14297 
14298 
delete_block_cb(LiVESMenuItem * menuitem,livespointer user_data)14299 void delete_block_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14300   lives_mt *mt = (lives_mt *)user_data;
14301   if (mt->is_rendering) return;
14302   on_delblock_activate(NULL, user_data);
14303 }
14304 
14305 
selblock_cb(LiVESMenuItem * menuitem,livespointer user_data)14306 void selblock_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14307   lives_mt *mt = (lives_mt *)user_data;
14308   select_block(mt);
14309   mt->putative_block = NULL;
14310 }
14311 
14312 
list_fx_here_cb(LiVESMenuItem * menuitem,livespointer user_data)14313 void list_fx_here_cb(LiVESMenuItem * menuitem, livespointer user_data) {
14314   lives_mt *mt = (lives_mt *)user_data;
14315   mt_tl_move(mt, mt->context_time);
14316   mt->context_time = -1.;
14317   on_mt_list_fx_activate(NULL, user_data);
14318 }
14319 
14320 
14321 ///////////////////////////////////////////////////////////
14322 
on_move_fx_changed(LiVESMenuItem * menuitem,livespointer user_data)14323 static void on_move_fx_changed(LiVESMenuItem * menuitem, livespointer user_data) {
14324   lives_mt *mt = (lives_mt *)user_data;
14325   mt->opts.move_effects = !mt->opts.move_effects;
14326 }
14327 
14328 
select_all_time(LiVESMenuItem * menuitem,livespointer user_data)14329 static void select_all_time(LiVESMenuItem * menuitem, livespointer user_data) {
14330   lives_mt *mt = (lives_mt *)user_data;
14331   mt->region_start = 0.;
14332   mt->region_end = get_event_timecode(get_last_event(mt->event_list));
14333   on_timeline_release(mt->timeline_reg, NULL, mt);
14334 }
14335 
14336 
select_from_zero_time(LiVESMenuItem * menuitem,livespointer user_data)14337 static void select_from_zero_time(LiVESMenuItem * menuitem, livespointer user_data) {
14338   lives_mt *mt = (lives_mt *)user_data;
14339   if (mt->region_start == 0. && mt->region_end == 0.) mt->region_end = mt->ptr_time;
14340   mt->region_start = 0.;
14341   on_timeline_release(mt->timeline_reg, NULL, mt);
14342 }
14343 
14344 
select_to_end_time(LiVESMenuItem * menuitem,livespointer user_data)14345 static void select_to_end_time(LiVESMenuItem * menuitem, livespointer user_data) {
14346   lives_mt *mt = (lives_mt *)user_data;
14347   if (mt->region_start == 0. && mt->region_end == 0.) mt->region_start = mt->ptr_time;
14348   mt->region_end = get_event_timecode(get_last_event(mt->event_list));
14349   on_timeline_release(mt->timeline_reg, NULL, mt);
14350 }
14351 
14352 
select_all_vid(LiVESMenuItem * menuitem,livespointer user_data)14353 static void select_all_vid(LiVESMenuItem * menuitem, livespointer user_data) {
14354   lives_mt *mt = (lives_mt *)user_data;
14355   LiVESWidget *eventbox, *checkbutton;
14356   LiVESList *vdr = mt->video_draws;
14357 
14358   int current_track = mt->current_track;
14359   int i = 0;
14360 
14361   lives_signal_handler_block(mt->select_track, mt->seltrack_func);
14362   if (!lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(mt->select_track)))
14363     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->select_track), TRUE);
14364   lives_signal_handler_unblock(mt->select_track, mt->seltrack_func);
14365 
14366   while (vdr) {
14367     eventbox = (LiVESWidget *)vdr->data;
14368     checkbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "checkbutton");
14369 
14370 #ifdef ENABLE_GIW
14371     if (!prefs->lamp_buttons) {
14372 #endif
14373       if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(checkbutton)))
14374         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(checkbutton), TRUE);
14375 #ifdef ENABLE_GIW
14376     } else {
14377       if (!giw_led_get_mode(GIW_LED(checkbutton))) giw_led_set_mode(GIW_LED(checkbutton), TRUE);
14378     }
14379 #endif
14380     mt->current_track = i++;
14381     // we need to call this since it appears that checkbuttons on hidden tracks don't get updated until shown
14382     on_seltrack_activate(LIVES_MENU_ITEM(mt->select_track), mt);
14383     vdr = vdr->next;
14384   }
14385   mt->current_track = current_track;
14386 }
14387 
14388 
select_no_vid(LiVESMenuItem * menuitem,livespointer user_data)14389 static void select_no_vid(LiVESMenuItem * menuitem, livespointer user_data) {
14390   lives_mt *mt = (lives_mt *)user_data;
14391   LiVESWidget *eventbox, *checkbutton;
14392   LiVESList *vdr = mt->video_draws;
14393 
14394   int current_track = mt->current_track;
14395   int i = 0;
14396 
14397   lives_signal_handler_block(mt->select_track, mt->seltrack_func);
14398   if (lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(mt->select_track)))
14399     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mt->select_track), FALSE);
14400   lives_signal_handler_unblock(mt->select_track, mt->seltrack_func);
14401 
14402   while (vdr) {
14403     eventbox = (LiVESWidget *)vdr->data;
14404     checkbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "checkbutton");
14405 
14406 #ifdef ENABLE_GIW
14407     if (!prefs->lamp_buttons) {
14408 #endif
14409       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(checkbutton)))
14410         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(checkbutton), FALSE);
14411 #ifdef ENABLE_GIW
14412     } else {
14413       if (giw_led_get_mode(GIW_LED(checkbutton))) giw_led_set_mode(GIW_LED(checkbutton), FALSE);
14414     }
14415 #endif
14416     mt->current_track = i++;
14417     // we need to call this since it appears that checkbuttons on hidden tracks don't get updated until shown
14418     on_seltrack_activate(LIVES_MENU_ITEM(mt->select_track), mt);
14419     vdr = vdr->next;
14420   }
14421   mt->current_track = current_track;
14422 }
14423 
14424 
mt_fplay_toggled(LiVESMenuItem * menuitem,livespointer user_data)14425 static void mt_fplay_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
14426   lives_mt *mt = (lives_mt *)user_data;
14427   mt->opts.follow_playback = !mt->opts.follow_playback;
14428   //lives_widget_set_sensitive(mt->follow_play,mt->opts.follow_playback);
14429 }
14430 
14431 
mt_render_vid_toggled(LiVESMenuItem * menuitem,livespointer user_data)14432 static void mt_render_vid_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
14433   lives_mt *mt = (lives_mt *)user_data;
14434   mt->opts.render_vidp = !mt->opts.render_vidp;
14435   lives_widget_set_sensitive(mt->render_aud, mt->opts.render_vidp);
14436 }
14437 
14438 
mt_render_aud_toggled(LiVESMenuItem * menuitem,livespointer user_data)14439 static void mt_render_aud_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
14440   lives_mt *mt = (lives_mt *)user_data;
14441   mt->opts.render_audp = !mt->opts.render_audp;
14442   lives_widget_set_sensitive(mt->render_vid, mt->opts.render_audp);
14443   lives_widget_set_sensitive(mt->normalise_aud, mt->opts.render_audp);
14444 }
14445 
14446 
mt_norm_aud_toggled(LiVESMenuItem * menuitem,livespointer user_data)14447 static void mt_norm_aud_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
14448   lives_mt *mt = (lives_mt *)user_data;
14449   mt->opts.normalise_audp = !mt->opts.normalise_audp;
14450 }
14451 
14452 
mt_view_audio_toggled(LiVESMenuItem * menuitem,livespointer user_data)14453 static void mt_view_audio_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
14454   lives_mt *mt = (lives_mt *)user_data;
14455   mt->opts.show_audio = lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(menuitem));
14456 
14457   if (!mt->opts.show_audio) lives_widget_object_set_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), HIDDEN_KEY,
14458         LIVES_INT_TO_POINTER(TRACK_I_HIDDEN_USER));
14459   else lives_widget_object_set_data(LIVES_WIDGET_OBJECT(mt->audio_draws->data), HIDDEN_KEY, LIVES_INT_TO_POINTER(0));
14460 
14461   scroll_tracks(mt, mt->top_track, FALSE);
14462   track_select(mt);
14463 }
14464 
14465 
mt_ign_ins_sel_toggled(LiVESMenuItem * menuitem,livespointer user_data)14466 static void mt_ign_ins_sel_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
14467   lives_mt *mt = (lives_mt *)user_data;
14468   mt->opts.ign_ins_sel = lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(menuitem));
14469 }
14470 
14471 
remove_gaps_inner(LiVESMenuItem * menuitem,livespointer user_data,boolean only_first)14472 static void remove_gaps_inner(LiVESMenuItem * menuitem, livespointer user_data, boolean only_first) {
14473   lives_mt *mt = (lives_mt *)user_data;
14474 
14475   weed_timecode_t offset = 0;
14476   weed_timecode_t tc, new_tc, tc_last, new_tc_last, tc_first, block_tc;
14477 
14478   LiVESList *vsel = mt->selected_tracks;
14479   LiVESList *track_sel;
14480 
14481   LiVESWidget *eventbox;
14482 
14483   track_rect *block = NULL;
14484 
14485   boolean did_backup = mt->did_backup;
14486   boolean audio_done = FALSE;
14487   boolean needs_idlefunc = FALSE;
14488 
14489   int track;
14490   int filenum;
14491 
14492   if (mt->idlefunc > 0) {
14493     needs_idlefunc = TRUE;
14494     lives_source_remove(mt->idlefunc);
14495     mt->idlefunc = 0;
14496   }
14497 
14498   mt_backup(mt, MT_UNDO_REMOVE_GAPS, 0);
14499 
14500   //go through selected tracks, move each block as far left as possible
14501 
14502   tc_last = q_gint64(mt->region_end * TICKS_PER_SECOND_DBL, mt->fps);
14503 
14504   while (vsel || (mt->current_track == -1 && !audio_done)) {
14505     offset = 0;
14506     if (mt->current_track > -1) {
14507       track = LIVES_POINTER_TO_INT(vsel->data);
14508       eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, track);
14509     } else {
14510       track = -1;
14511       eventbox = (LiVESWidget *)mt->audio_draws->data;
14512     }
14513     tc = mt->region_start * TICKS_PER_SECOND_DBL;
14514     tc = q_gint64(tc, mt->fps);
14515 
14516     if (mt->opts.grav_mode != GRAV_MODE_RIGHT) {
14517       // adjust the region so it begins after any first partially contained block
14518       block = get_block_before(eventbox, tc / TICKS_PER_SECOND_DBL, TRUE);
14519       if (block) {
14520         new_tc = q_gint64(get_event_timecode(block->end_event) + (double)(track > -1) * TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
14521         if (new_tc > tc) tc = new_tc;
14522       }
14523     } else {
14524       // adjust the region so it ends before any last partially contained block
14525       block = get_block_after(eventbox, tc_last / TICKS_PER_SECOND_DBL, TRUE);
14526       if (block) {
14527         new_tc_last = q_gint64(get_event_timecode(block->start_event) - (double)(track > -1)
14528                                * TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
14529         if (new_tc_last < tc_last) tc_last = new_tc_last;
14530       }
14531     }
14532 
14533     if (mt->opts.grav_mode != GRAV_MODE_RIGHT) {
14534       // moving left
14535       // what we do here:
14536       // find first block in range. move it left to tc
14537       // then we adjust tc to the end of the block (+ 1 frame for video tracks)
14538       // and continue until we reach the end of the region
14539 
14540       // note video and audio are treated slightly differently
14541       // a video block must end before the next one starts
14542       // audio blocks can start and end at the same frame
14543 
14544       // if we remove only first gap, we move the first block, store how far it moved in offset
14545       // and then move all other blocks by offset
14546 
14547       while (tc <= tc_last) {
14548         block = get_block_after(eventbox, tc / TICKS_PER_SECOND_DBL, FALSE);
14549         if (!block) break;
14550 
14551         new_tc = get_event_timecode(block->start_event);
14552         if (new_tc > tc_last) break;
14553 
14554         if (tc < new_tc) {
14555           // move this block to tc
14556           if (offset > 0) tc = q_gint64(new_tc - offset, mt->fps);
14557           filenum = get_frame_event_clip(block->start_event, track);
14558           mt->clip_selected = mt_clip_from_file(mt, filenum);
14559           mt_clip_select(mt, FALSE);
14560           if (!mt->did_backup) mt_backup(mt, MT_UNDO_REMOVE_GAPS, 0);
14561 
14562           // save current selected_tracks, move_block may change this
14563           track_sel = mt->selected_tracks;
14564           mt->selected_tracks = NULL;
14565           block = move_block(mt, block, tc / TICKS_PER_SECOND_DBL, track, track);
14566           if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
14567           mt->selected_tracks = track_sel;
14568           if (only_first && offset == 0) offset = new_tc - tc;
14569         }
14570         tc = q_gint64(get_event_timecode(block->end_event) + (double)(track > -1) * TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
14571       }
14572       if (mt->current_track > -1) vsel = vsel->next;
14573       else audio_done = TRUE;
14574     } else {
14575       // moving right
14576       // here we do the reverse:
14577       // find last block in range. move it right so it ends at tc
14578       // then we adjust tc to the start of the block + 1 frame
14579       // and continue until we reach the end of the region
14580 
14581       tc_first = tc;
14582       tc = tc_last;
14583       while (tc >= tc_first) {
14584         block = get_block_before(eventbox, tc / TICKS_PER_SECOND_DBL, FALSE);
14585         if (!block) break;
14586 
14587         new_tc = get_event_timecode(block->end_event);
14588         if (new_tc < tc_first) break;
14589 
14590         // subtract the length of the block to get the start point
14591         block_tc = new_tc - get_event_timecode(block->start_event) + (double)(track > -1) * TICKS_PER_SECOND_DBL / mt->fps;
14592 
14593         if (tc > new_tc) {
14594           // move this block to tc
14595           if (offset > 0) tc = q_gint64(new_tc - block_tc + offset, mt->fps);
14596           else tc = q_gint64(tc - block_tc, mt->fps);
14597           filenum = get_frame_event_clip(block->start_event, track);
14598           mt->clip_selected = mt_clip_from_file(mt, filenum);
14599           mt_clip_select(mt, FALSE);
14600           if (!mt->did_backup) mt_backup(mt, MT_UNDO_REMOVE_GAPS, 0);
14601 
14602           // save current selected_tracks, move_block may change this
14603           track_sel = mt->selected_tracks;
14604           mt->selected_tracks = NULL;
14605           block = move_block(mt, block, tc / TICKS_PER_SECOND_DBL, track, track);
14606           if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
14607           mt->selected_tracks = track_sel;
14608           if (only_first && offset == 0) offset = tc - new_tc + block_tc;
14609         }
14610         tc = q_gint64(get_event_timecode(block->start_event) - (double)(track > -1) * TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
14611       }
14612       if (mt->current_track > -1) vsel = vsel->next;
14613       else audio_done = TRUE;
14614     }
14615   }
14616 
14617   if (!did_backup) {
14618     if (mt->avol_fx != -1 && (!block || !block->next) && mt->audio_draws
14619         && mt->audio_draws->data && get_first_event(mt->event_list)) {
14620       apply_avol_filter(mt);
14621     }
14622   }
14623 
14624   mt->did_backup = did_backup;
14625   if (!did_backup && mt->framedraw && mt->current_rfx && mt->init_event
14626       && mt->poly_state == POLY_PARAMS && weed_plant_has_leaf(mt->init_event, WEED_LEAF_IN_TRACKS)) {
14627     tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton))
14628                   * TICKS_PER_SECOND_DBL + get_event_timecode(mt->init_event), mt->fps);
14629     get_track_index(mt, tc);
14630   }
14631 
14632   if (!did_backup) {
14633     mt->did_backup = FALSE;
14634     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
14635     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
14636   }
14637 }
14638 
14639 
remove_first_gaps(LiVESMenuItem * menuitem,livespointer user_data)14640 void remove_first_gaps(LiVESMenuItem * menuitem, livespointer user_data) {
14641   // remove first gaps in selected time/tracks
14642   // if gravity is Right then we remove last gaps instead
14643 
14644   remove_gaps_inner(menuitem, user_data, TRUE);
14645 }
14646 
14647 
remove_gaps(LiVESMenuItem * menuitem,livespointer user_data)14648 void remove_gaps(LiVESMenuItem * menuitem, livespointer user_data) {
14649   remove_gaps_inner(menuitem, user_data, FALSE);
14650 }
14651 
14652 
split_block(lives_mt * mt,track_rect * block,weed_timecode_t tc,int track,boolean no_recurse)14653 static void split_block(lives_mt * mt, track_rect * block, weed_timecode_t tc, int track, boolean no_recurse) {
14654   weed_plant_t *event = block->start_event;
14655   weed_plant_t *start_event = event;
14656   weed_plant_t *old_end_event = block->end_event;
14657   int frame = 0, clip;
14658   LiVESWidget *eventbox;
14659   track_rect *new_block;
14660   weed_timecode_t offset_start;
14661   double seek, new_seek, vel;
14662 
14663   tc = q_gint64(tc, mt->fps);
14664 
14665   if (!block) return;
14666 
14667   mt->no_expose = TRUE;
14668 
14669   while (get_event_timecode(event) < tc) event = get_next_event(event);
14670   block->end_event = track >= 0 ? get_prev_event(event) : event;
14671   if (!WEED_EVENT_IS_FRAME(block->end_event)) block->end_event = get_prev_frame_event(event);
14672 
14673   if (!WEED_EVENT_IS_FRAME(event)) event = get_next_frame_event(event);
14674   eventbox = block->eventbox;
14675 
14676   if (!is_audio_eventbox(eventbox)) {
14677     if (!no_recurse) {
14678       // if we have an audio block, split it too
14679       LiVESWidget *aeventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "atrack"));
14680       if (aeventbox) {
14681         track_rect *ablock = get_block_from_time(aeventbox, tc / TICKS_PER_SECOND_DBL + 1. / mt->fps, mt);
14682         if (ablock) split_block(mt, ablock, tc + TICKS_PER_SECOND_DBL / mt->fps, track, TRUE);
14683       }
14684     }
14685     frame = get_frame_event_frame(event, track);
14686     clip = get_frame_event_clip(event, track);
14687   } else {
14688     if (!no_recurse) {
14689       // if we have a video block, split it too
14690       LiVESWidget *oeventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "owner"));
14691       if (oeventbox) split_block(mt, get_block_from_time(oeventbox, tc / TICKS_PER_SECOND_DBL - 1. / mt->fps, mt),
14692                                    tc - TICKS_PER_SECOND_DBL / mt->fps, track, TRUE);
14693     }
14694     clip = get_audio_frame_clip(start_event, track);
14695     seek = get_audio_frame_seek(start_event, track);
14696     vel = get_audio_frame_vel(start_event, track);
14697     event = block->end_event;
14698     new_seek = seek + (get_event_timecode(event) / TICKS_PER_SECOND_DBL
14699                        - get_event_timecode(start_event) / TICKS_PER_SECOND_DBL) * vel;
14700     insert_audio_event_at(event, track, clip, new_seek, vel);
14701   }
14702 
14703   if (block->ordered ||
14704       (is_audio_eventbox(eventbox))) offset_start = block->offset_start - get_event_timecode(start_event)
14705             + get_event_timecode(event);
14706   else offset_start = calc_time_from_frame(clip, frame) * TICKS_PER_SECOND_DBL;
14707 
14708   new_block = add_block_start_point(LIVES_WIDGET(eventbox), tc, clip, offset_start, event, block->ordered);
14709   new_block->end_event = old_end_event;
14710 
14711   mt->no_expose = FALSE;
14712 
14713   redraw_eventbox(mt, eventbox);
14714   paint_lines(mt, mt->ptr_time, TRUE, NULL);
14715 }
14716 
14717 
insgap_inner(lives_mt * mt,int tnum,boolean is_sel,int passnm)14718 static void insgap_inner(lives_mt * mt, int tnum, boolean is_sel, int passnm) {
14719   // insert a gap in track tnum
14720 
14721   // we will process in 2 passes
14722 
14723   // pass 1
14724 
14725   // if there is a block at start time, we split it
14726   // then we move the frame events for this track, inserting blanks if necessary, and we update all our blocks
14727 
14728   // pass 2
14729 
14730   // FILTER_INITs and FILTER_DEINITS - we move the filter init/deinit if "move effects with blocks" is selected and all in_tracks are in the tracks to be moved
14731   // (transitions may have one non-moving track)
14732 
14733   track_rect *sblock, *block, *ablock = NULL;
14734   LiVESWidget *eventbox;
14735   LiVESList *slist;
14736   weed_plant_t *event, *new_event = NULL, *last_frame_event;
14737   weed_plant_t *init_event;
14738   weed_timecode_t tc, new_tc;
14739   weed_timecode_t start_tc, new_init_tc, init_tc;
14740   double aseek = 0., avel = 0., *audio_seeks;
14741   double end_secs;
14742   boolean found;
14743   int clip, *clips, *new_clips;
14744   int64_t frame, *frames, *new_frames;
14745   int xnumclips, numclips, naclips;
14746   int aclip = 0, *audio_clips;
14747   int nintracks, *in_tracks;
14748   int notmatched;
14749   register int i;
14750 
14751   switch (passnm) {
14752   case 1:
14753     // frames and blocks
14754     if (tnum >= 0) eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, tnum);
14755     else eventbox = (LiVESWidget *)mt->audio_draws->data;
14756     tc = q_dbl(mt->region_start, mt->fps);
14757     sblock = get_block_from_time(eventbox, mt->region_start, mt);
14758 
14759     if (sblock) {
14760       split_block(mt, sblock, tc, tnum, FALSE);
14761       sblock = sblock->next;
14762     } else {
14763       sblock = get_block_after(eventbox, mt->region_start, FALSE);
14764     }
14765 
14766     if (!sblock) return;
14767 
14768     block = sblock;
14769     while (block->next) block = block->next;
14770     event = block->end_event;
14771 
14772     if (tnum >= 0 && mt->opts.pertrack_audio) {
14773       LiVESWidget *aeventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "atrack"));
14774       if (aeventbox) {
14775         ablock = get_block_after(aeventbox, mt->region_start, FALSE);
14776         if (ablock) {
14777           while (ablock->next) ablock = ablock->next;
14778           event = ablock->end_event;
14779         }
14780       }
14781     }
14782 
14783     while (event) {
14784       if (WEED_EVENT_IS_FRAME(event)) {
14785         tc = get_event_timecode(event);
14786         new_tc = q_gint64(tc + (mt->region_end - mt->region_start) * TICKS_PER_SECOND_DBL, mt->fps);
14787         new_event = event;
14788 
14789         if (tnum >= 0 && tc <= get_event_timecode(block->end_event)) {
14790           frame = get_frame_event_frame(event, tnum);
14791           clip = get_frame_event_clip(event, tnum);
14792 
14793           if (!(new_event = get_frame_event_at(mt->event_list, new_tc, event, TRUE))) {
14794             last_frame_event = get_last_frame_event(mt->event_list);
14795             mt->event_list = add_blank_frames_up_to(mt->event_list, last_frame_event, new_tc, mt->fps);
14796             new_event = get_last_frame_event(mt->event_list);
14797           }
14798 
14799           remove_frame_from_event(mt->event_list, event, tnum);
14800 
14801           clips = weed_get_int_array_counted(new_event, WEED_LEAF_CLIPS, &numclips);
14802           xnumclips = numclips;
14803           if (numclips < tnum + 1) xnumclips = tnum + 1;
14804 
14805           new_clips = (int *)lives_malloc(xnumclips * sizint);
14806           new_frames = (int64_t *)lives_malloc(xnumclips * 8);
14807 
14808           frames = weed_get_int64_array(new_event, WEED_LEAF_FRAMES, NULL);
14809 
14810           for (i = 0; i < xnumclips; i++) {
14811             if (i == tnum) {
14812               new_clips[i] = clip;
14813               new_frames[i] = frame;
14814             } else {
14815               if (i < numclips) {
14816                 new_clips[i] = clips[i];
14817                 new_frames[i] = frames[i];
14818               } else {
14819                 new_clips[i] = -1;
14820                 new_frames[i] = 0;
14821               }
14822             }
14823           }
14824 
14825           weed_set_int_array(new_event, WEED_LEAF_CLIPS, xnumclips, new_clips);
14826           weed_set_int64_array(new_event, WEED_LEAF_FRAMES, xnumclips, new_frames);
14827 
14828           lives_free(clips);
14829           lives_free(frames);
14830           lives_free(new_clips);
14831           lives_free(new_frames);
14832         }
14833 
14834         if (WEED_EVENT_IS_AUDIO_FRAME(event)) {
14835           if (!(new_event = get_frame_event_at(mt->event_list, new_tc, event, TRUE))) {
14836             last_frame_event = get_last_frame_event(mt->event_list);
14837             mt->event_list = add_blank_frames_up_to(mt->event_list, last_frame_event, q_gint64(new_tc, mt->fps), mt->fps);
14838             new_event = get_last_frame_event(mt->event_list);
14839           }
14840 
14841           audio_clips = weed_get_int_array_counted(event, WEED_LEAF_AUDIO_CLIPS, &naclips);
14842           audio_seeks = weed_get_double_array(event, WEED_LEAF_AUDIO_SEEKS, NULL);
14843 
14844           for (i = 0; i < naclips; i += 2) {
14845             if (audio_clips[i] == tnum) {
14846               aclip = audio_clips[i + 1];
14847               aseek = audio_seeks[i];
14848               avel = audio_seeks[i + 1];
14849             }
14850           }
14851 
14852           lives_free(audio_clips);
14853           lives_free(audio_seeks);
14854 
14855           remove_audio_for_track(event, tnum);
14856           insert_audio_event_at(new_event, tnum, aclip, aseek, avel);
14857 
14858           if (mt->avol_fx != -1) {
14859             apply_avol_filter(mt);
14860           }
14861 
14862         }
14863 
14864         if (new_event != event) {
14865 
14866           if (ablock) {
14867             if (event == ablock->end_event) ablock->end_event = new_event;
14868             else if (event == ablock->start_event) {
14869               ablock->start_event = new_event;
14870             }
14871           }
14872 
14873           if (event == block->end_event) block->end_event = new_event;
14874           else if (event == block->start_event) {
14875             block->start_event = new_event;
14876             if (block == sblock) {
14877               if (tnum < 0 || ablock) {
14878                 if (ablock) block = ablock;
14879                 if (block->prev && block->prev->end_event == event) {
14880                   // audio block was split, need to add a new "audio off" event
14881                   insert_audio_event_at(event, tnum, aclip, 0., 0.);
14882                 }
14883                 if (mt->avol_fx != -1) {
14884                   apply_avol_filter(mt);
14885                 }
14886               }
14887               mt_fixup_events(mt, event, new_event);
14888               redraw_eventbox(mt, eventbox);
14889               paint_lines(mt, mt->ptr_time, TRUE, NULL);
14890               return;
14891             }
14892             block = block->prev;
14893           }
14894           if (ablock && ablock->start_event == new_event) {
14895             ablock = ablock->prev;
14896             if (ablock && event == ablock->end_event) ablock->end_event = new_event;
14897           }
14898           mt_fixup_events(mt, event, new_event);
14899         }
14900       }
14901       if (tnum >= 0) event = get_prev_event(event);
14902       else {
14903         if (new_event == block->end_event) event = block->start_event;
14904         else event = block->end_event; // we will have moved to the previous block
14905       }
14906     }
14907 
14908     break;
14909 
14910   case 2:
14911     // FILTER_INITs
14912     start_tc = q_gint64(mt->region_start * TICKS_PER_SECOND_DBL, mt->fps);
14913     event = get_last_event(mt->event_list);
14914 
14915     while (event && (tc = get_event_timecode(event)) >= start_tc) {
14916       if (WEED_EVENT_IS_FILTER_DEINIT(event)) {
14917         init_event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
14918 
14919         if (init_event == mt->avol_init_event) {
14920           event = get_prev_event(event);
14921           continue;
14922         }
14923 
14924         // see if all of this filter`s in_tracks were moved
14925         in_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &nintracks);
14926 
14927         if (!is_sel) {
14928           if ((nintracks == 1 && in_tracks[0] != mt->current_track) || (nintracks == 2 && in_tracks[0] != mt->current_track &&
14929               in_tracks[1] != mt->current_track)) {
14930             event = get_prev_event(event);
14931             continue;
14932           }
14933         } else {
14934           for (i = 0; i < nintracks; i++) {
14935             slist = mt->selected_tracks;
14936             found = FALSE;
14937             notmatched = 0;
14938             while (slist && !found) {
14939               if (LIVES_POINTER_TO_INT(slist->data) == in_tracks[i]) found = TRUE;
14940               slist = slist->next;
14941             }
14942             if (!found) {
14943               if (nintracks != 2 || notmatched > 0) return;
14944               notmatched = 1;
14945             }
14946           }
14947         }
14948 
14949         lives_free(in_tracks);
14950 
14951         // move filter_deinit
14952         new_tc = q_gint64(tc + (mt->region_end - mt->region_start) * TICKS_PER_SECOND_DBL, mt->fps);
14953         move_filter_deinit_event(mt->event_list, new_tc, event, mt->fps, TRUE);
14954 
14955         init_tc = get_event_timecode(init_event);
14956 
14957         if (init_tc >= start_tc) {
14958           // move filter init
14959           new_init_tc = q_gint64(init_tc + (mt->region_end - mt->region_start) * TICKS_PER_SECOND_DBL, mt->fps);
14960           move_filter_init_event(mt->event_list, new_init_tc, init_event, mt->fps);
14961         }
14962 
14963         // for a transition where only one track moved, pack around the overlap
14964 
14965         if (nintracks == 2) {
14966           move_event_left(mt->event_list, event, TRUE, mt->fps);
14967           if (init_tc >= start_tc && init_event != mt->avol_init_event)
14968             move_event_right(mt->event_list, init_event, TRUE, mt->fps);
14969         }
14970       }
14971       event = get_prev_event(event);
14972     }
14973     break;
14974   }
14975   end_secs = event_list_get_end_secs(mt->event_list);
14976   if (end_secs > mt->end_secs) {
14977     set_timeline_end_secs(mt, end_secs);
14978   }
14979 }
14980 
14981 
on_insgap_sel_activate(LiVESMenuItem * menuitem,livespointer user_data)14982 void on_insgap_sel_activate(LiVESMenuItem * menuitem, livespointer user_data) {
14983 
14984   lives_mt *mt = (lives_mt *)user_data;
14985   LiVESList *slist = mt->selected_tracks;
14986   char *tstart, *tend;
14987   boolean did_backup = mt->did_backup;
14988   boolean needs_idlefunc = FALSE;
14989 
14990   int track;
14991 
14992   if (mt->idlefunc > 0) {
14993     needs_idlefunc = TRUE;
14994     lives_source_remove(mt->idlefunc);
14995     mt->idlefunc = 0;
14996   }
14997 
14998   mt_backup(mt, MT_UNDO_INSERT_GAP, 0);
14999 
15000   while (slist) {
15001     track = LIVES_POINTER_TO_INT(slist->data);
15002     insgap_inner(mt, track, TRUE, 1);
15003     slist = slist->next;
15004   }
15005 
15006   if (mt->opts.move_effects) {
15007     insgap_inner(mt, 0, TRUE, 2);
15008   }
15009 
15010   mt->did_backup = did_backup;
15011   mt_show_current_frame(mt, FALSE);
15012 
15013   tstart = time_to_string(QUANT_TIME(mt->region_start));
15014   tend = time_to_string(QUANT_TIME(mt->region_end));
15015 
15016   d_print(_("Inserted gap in selected tracks from time %s to time %s\n"), tstart, tend);
15017 
15018   lives_free(tstart);
15019   lives_free(tend);
15020 
15021   if (!did_backup) {
15022     mt->did_backup = FALSE;
15023     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
15024     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
15025   }
15026 }
15027 
15028 
on_insgap_cur_activate(LiVESMenuItem * menuitem,livespointer user_data)15029 void on_insgap_cur_activate(LiVESMenuItem * menuitem, livespointer user_data) {
15030   lives_mt *mt = (lives_mt *)user_data;
15031 
15032   boolean did_backup = mt->did_backup;
15033   boolean needs_idlefunc = FALSE;
15034 
15035   char *tstart, *tend;
15036   char *tname;
15037 
15038   if (mt->idlefunc > 0) {
15039     needs_idlefunc = TRUE;
15040     lives_source_remove(mt->idlefunc);
15041     mt->idlefunc = 0;
15042   }
15043 
15044   if (!did_backup) mt_backup(mt, MT_UNDO_INSERT_GAP, 0);
15045 
15046   insgap_inner(mt, mt->current_track, FALSE, 1);
15047 
15048   if (mt->opts.move_effects) {
15049     insgap_inner(mt, mt->current_track, FALSE, 2);
15050   }
15051 
15052   mt->did_backup = did_backup;
15053   mt_show_current_frame(mt, FALSE);
15054 
15055   tstart = time_to_string(QUANT_TIME(mt->region_start));
15056   tend = time_to_string(QUANT_TIME(mt->region_end));
15057 
15058   tname = get_track_name(mt, mt->current_track, FALSE);
15059   d_print(_("Inserted gap in track %s from time %s to time %s\n"), tname, tstart, tend);
15060 
15061   lives_free(tname);
15062   lives_free(tstart);
15063   lives_free(tend);
15064 
15065   if (!did_backup) {
15066     mt->did_backup = FALSE;
15067     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
15068     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
15069   }
15070 }
15071 
15072 
multitrack_undo(LiVESMenuItem * menuitem,livespointer user_data)15073 void multitrack_undo(LiVESMenuItem * menuitem, livespointer user_data) {
15074   lives_mt *mt = (lives_mt *)user_data;
15075 
15076   mt_undo *last_undo = (mt_undo *)lives_list_nth_data(mt->undos, lives_list_length(mt->undos) - 1 - mt->undo_offset);
15077   mt_undo *new_redo = NULL;
15078 
15079   LiVESList *slist;
15080   LiVESList *label_list = NULL;
15081   LiVESList *vlist, *llist;
15082   LiVESList *seltracks = NULL;
15083   LiVESList *aparam_view_list;
15084 
15085   LiVESWidget *checkbutton, *eventbox, *label;
15086 
15087   unsigned char *memblock, *omemblock, *mem_end;
15088 
15089   size_t space_avail = (size_t)(prefs->mt_undo_buf * 1024 * 1024) - mt->undo_buffer_used;
15090   size_t space_needed;
15091 
15092   double end_secs;
15093   double ptr_time;
15094 
15095   char *utxt, *tmp;
15096   char *txt;
15097 
15098   boolean block_is_selected = FALSE;
15099   boolean avoid_fx_list = FALSE;
15100 
15101   int current_track;
15102   int clip_sel;
15103   int avol_fx;
15104   int num_tracks;
15105 
15106   int i;
15107 
15108   if (!mt->undo_mem) return;
15109 
15110   if (mt->idlefunc > 0) {
15111     lives_source_remove(mt->idlefunc);
15112     mt->idlefunc = 0;
15113   }
15114 
15115   mt_desensitise(mt);
15116 
15117   mt->was_undo_redo = TRUE;
15118   ptr_time = mt->ptr_time;
15119 
15120   if (mt->block_selected) block_is_selected = TRUE;
15121 
15122   if (last_undo->action != MT_UNDO_NONE) {
15123     if (mt->undo_offset == 0) {
15124       add_markers(mt, mt->event_list, TRUE);
15125       if ((space_needed = estimate_space(mt, last_undo->action) + sizeof(mt_undo)) > space_avail) {
15126         if (!make_backup_space(mt, space_needed) || !mt->undos) {
15127           remove_markers(mt->event_list);
15128           mt->idlefunc = mt_idle_add(mt);
15129           do_mt_undo_buf_error();
15130           mt_sensitise(mt);
15131           return;
15132         }
15133       }
15134 
15135       new_redo = (mt_undo *)(mt->undo_mem + mt->undo_buffer_used);
15136       new_redo->action = last_undo->action;
15137 
15138       omemblock = memblock = (unsigned char *)new_redo + sizeof(mt_undo);
15139       save_event_list_inner(NULL, 0, mt->event_list, &memblock);
15140       new_redo->data_len = memblock - omemblock;
15141       space_needed = new_redo->data_len + sizeof(mt_undo);
15142       mt->undo_buffer_used += space_needed;
15143       mt->undos = lives_list_append(mt->undos, new_redo);
15144       mt->undo_offset++;
15145     }
15146 
15147     current_track = mt->current_track;
15148     end_secs = mt->end_secs;
15149     num_tracks = mt->num_video_tracks;
15150     clip_sel = mt->clip_selected;
15151 
15152     seltracks = lives_list_copy(mt->selected_tracks);
15153 
15154     vlist = mt->video_draws;
15155     while (vlist) {
15156       eventbox = LIVES_WIDGET(vlist->data);
15157       label = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label"));
15158       txt = lives_strdup(lives_label_get_text(LIVES_LABEL(label)));
15159       label_list = lives_list_append(label_list, txt);
15160       vlist = vlist->next;
15161     }
15162 
15163     aparam_view_list = lives_list_copy(mt->opts.aparam_view_list);
15164     avol_fx = mt->avol_fx;
15165     mt->avol_fx = -1;
15166 
15167     mt->no_expose = TRUE;
15168 
15169     event_list_free(mt->event_list);
15170     last_undo = (mt_undo *)lives_list_nth_data(mt->undos, lives_list_length(mt->undos) - 1 - mt->undo_offset);
15171     memblock = (unsigned char *)(last_undo) + sizeof(mt_undo);
15172     mem_end = memblock + last_undo->data_len - sizeof(mt_undo);
15173     mt->event_list = load_event_list_inner(mt, -1, FALSE, NULL, &memblock, mem_end);
15174 
15175     if (!event_list_rectify(mt, mt->event_list)) {
15176       event_list_free(mt->event_list);
15177       mt->event_list = NULL;
15178     }
15179 
15180     if (!get_first_event(mt->event_list)) {
15181       event_list_free(mt->event_list);
15182       mt->event_list = NULL;
15183     }
15184 
15185     for (i = 0; i < mt->num_video_tracks; i++) {
15186       delete_video_track(mt, i, FALSE);
15187     }
15188     lives_list_free(mt->video_draws);
15189     mt->video_draws = NULL;
15190     mt->num_video_tracks = 0;
15191 
15192     delete_audio_tracks(mt, mt->audio_draws, FALSE);
15193     mt->audio_draws = NULL;
15194 
15195     mt->fm_edit_event = NULL; // this might have been deleted; etc., c.f. fixup_events
15196     mt->init_event = NULL;
15197     mt->selected_init_event = NULL;
15198     mt->specific_event = NULL;
15199     mt->avol_init_event = NULL; // we will try to relocate this in mt_init_tracks()
15200 
15201     mt_init_tracks(mt, FALSE);
15202 
15203     if (mt->avol_fx == -1) mt->avol_fx = avol_fx;
15204     if (mt->avol_fx != -1) mt->opts.aparam_view_list = lives_list_copy(aparam_view_list);
15205     if (aparam_view_list) lives_list_free(aparam_view_list);
15206 
15207     add_aparam_menuitems(mt);
15208 
15209     unselect_all(mt);
15210     for (i = mt->num_video_tracks; i < num_tracks; i++) {
15211       add_video_track_behind(NULL, mt);
15212     }
15213 
15214     mt->clip_selected = clip_sel;
15215     mt_clip_select(mt, FALSE);
15216 
15217     vlist = mt->video_draws;
15218     llist = label_list;
15219     while (vlist) {
15220       eventbox = LIVES_WIDGET(vlist->data);
15221       label = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label"));
15222       widget_opts.mnemonic_label = FALSE;
15223       lives_label_set_text(LIVES_LABEL(label), (const char *)llist->data);
15224       widget_opts.mnemonic_label = TRUE;
15225       vlist = vlist->next;
15226       llist = llist->next;
15227     }
15228     lives_list_free(label_list);
15229 
15230     if (mt->event_list) remove_markers(mt->event_list);
15231 
15232     mt->selected_tracks = lives_list_copy(seltracks);
15233     slist = mt->selected_tracks;
15234     while (slist) {
15235       eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, LIVES_POINTER_TO_INT(slist->data));
15236       checkbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "checkbutton");
15237 #ifdef ENABLE_GIW
15238       if (!prefs->lamp_buttons) {
15239 #endif
15240         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(checkbutton), TRUE);
15241 #ifdef ENABLE_GIW
15242       } else {
15243         giw_led_set_mode(GIW_LED(checkbutton), TRUE);
15244       }
15245 #endif
15246       slist = slist->next;
15247     }
15248     if (seltracks) lives_list_free(seltracks);
15249 
15250     mt->current_track = current_track;
15251     track_select(mt);
15252     if (mt->end_secs != end_secs && event_list_get_end_secs(mt->event_list) <= end_secs) set_timeline_end_secs(mt, end_secs);
15253   }
15254 
15255   mt->no_expose = FALSE;
15256 
15257   mt->undo_offset++;
15258 
15259   if (mt->undo_offset == lives_list_length(mt->undos)) mt_set_undoable(mt, MT_UNDO_NONE, NULL, FALSE);
15260   else {
15261     mt_undo *undo = (mt_undo *)(lives_list_nth_data(mt->undos, lives_list_length(mt->undos) - mt->undo_offset - 1));
15262     mt_set_undoable(mt, undo->action, undo->extra, TRUE);
15263   }
15264   mt_set_redoable(mt, last_undo->action, last_undo->extra, TRUE);
15265   lives_ruler_set_value(LIVES_RULER(mt->timeline), ptr_time);
15266   lives_widget_queue_draw(mt->tlx_vbox);
15267 
15268   utxt = lives_utf8_strdown((tmp = get_undo_text(last_undo->action, last_undo->extra)), -1);
15269   lives_free(tmp);
15270 
15271   d_print(_("Undid %s\n"), utxt);
15272   lives_free(utxt);
15273 
15274   if (last_undo->action <= 1024 && block_is_selected) mt_selblock(LIVES_MENU_ITEM(mt->seldesel_menuitem), (livespointer)mt);
15275 
15276   // TODO - make sure this is the effect which is now deleted/added...
15277   if (mt->poly_state == POLY_PARAMS) {
15278     if (mt->last_fx_type == MT_LAST_FX_BLOCK && mt->block_selected) polymorph(mt, POLY_FX_STACK);
15279     else polymorph(mt, POLY_CLIPS);
15280     avoid_fx_list = TRUE;
15281   }
15282   if ((last_undo->action == MT_UNDO_FILTER_MAP_CHANGE || mt->poly_state == POLY_FX_STACK) && !avoid_fx_list) {
15283     if (last_undo->action == MT_UNDO_FILTER_MAP_CHANGE) mt_tl_move(mt, last_undo->tc);
15284     polymorph(mt, POLY_FX_STACK);
15285   }
15286   if (mt->poly_state != POLY_PARAMS) mt_show_current_frame(mt, FALSE);
15287 
15288   mt_desensitise(mt);
15289   mt_sensitise(mt);
15290 
15291   if (!mt->event_list) recover_layout_cancelled(FALSE);
15292 
15293   mt->idlefunc = mt_idle_add(mt);
15294   if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
15295 }
15296 
15297 
multitrack_redo(LiVESMenuItem * menuitem,livespointer user_data)15298 void multitrack_redo(LiVESMenuItem * menuitem, livespointer user_data) {
15299   lives_mt *mt = (lives_mt *)user_data;
15300 
15301   mt_undo *last_redo = (mt_undo *)lives_list_nth_data(mt->undos, lives_list_length(mt->undos) + 1 - mt->undo_offset);
15302 
15303   LiVESWidget *checkbutton, *eventbox, *label;
15304 
15305   LiVESList *slist;
15306   LiVESList *label_list = NULL;
15307   LiVESList *vlist, *llist;
15308   LiVESList *seltracks = NULL;
15309   LiVESList *aparam_view_list;
15310 
15311   unsigned char *memblock, *mem_end;
15312 
15313   char *txt;
15314   char *utxt, *tmp;
15315 
15316   double ptr_time;
15317   double end_secs;
15318 
15319   int current_track;
15320   int num_tracks;
15321   int clip_sel;
15322   int avol_fx;
15323 
15324   int i;
15325 
15326   if (!mt->undo_mem) return;
15327 
15328   if (mt->idlefunc > 0) {
15329     lives_source_remove(mt->idlefunc);
15330     mt->idlefunc = 0;
15331   }
15332 
15333   mt_desensitise(mt);
15334 
15335   //if (mt->block_selected!=NULL) block_is_selected=TRUE; // TODO *** - need to set track and time
15336 
15337   mt->was_undo_redo = TRUE;
15338   ptr_time = mt->ptr_time;
15339 
15340   if (last_redo->action != MT_UNDO_NONE) {
15341     current_track = mt->current_track;
15342     end_secs = mt->end_secs;
15343     num_tracks = mt->num_video_tracks;
15344     clip_sel = mt->clip_selected;
15345 
15346     seltracks = lives_list_copy(mt->selected_tracks);
15347 
15348     vlist = mt->video_draws;
15349     while (vlist) {
15350       eventbox = LIVES_WIDGET(vlist->data);
15351       label = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label"));
15352       txt = lives_strdup(lives_label_get_text(LIVES_LABEL(label)));
15353       label_list = lives_list_append(label_list, txt);
15354       vlist = vlist->next;
15355     }
15356 
15357     aparam_view_list = lives_list_copy(mt->opts.aparam_view_list);
15358     avol_fx = mt->avol_fx;
15359     mt->avol_fx = -1;
15360 
15361     mt->no_expose = TRUE;
15362 
15363     event_list_free(mt->event_list);
15364 
15365     memblock = (unsigned char *)(last_redo) + sizeof(mt_undo);
15366     mem_end = memblock + last_redo->data_len - sizeof(mt_undo);
15367     mt->event_list = load_event_list_inner(mt, -1, FALSE, NULL, &memblock, mem_end);
15368     if (!event_list_rectify(mt, mt->event_list)) {
15369       event_list_free(mt->event_list);
15370       mt->event_list = NULL;
15371     }
15372 
15373     if (!get_first_event(mt->event_list)) {
15374       event_list_free(mt->event_list);
15375       mt->event_list = NULL;
15376     }
15377 
15378     for (i = 0; i < mt->num_video_tracks; i++) {
15379       delete_video_track(mt, i, FALSE);
15380     }
15381     lives_list_free(mt->video_draws);
15382     mt->video_draws = NULL;
15383     mt->num_video_tracks = 0;
15384 
15385     delete_audio_tracks(mt, mt->audio_draws, FALSE);
15386     mt->audio_draws = NULL;
15387 
15388     mt->fm_edit_event = NULL; // this might have been deleted; etc., c.f. fixup_events
15389     mt->init_event = NULL;
15390     mt->selected_init_event = NULL;
15391     mt->specific_event = NULL;
15392     mt->avol_init_event = NULL; // we will try to relocate this in mt_init_tracks()
15393 
15394     mt_init_tracks(mt, FALSE);
15395 
15396     if (mt->avol_fx == avol_fx) {
15397       mt->opts.aparam_view_list = lives_list_copy(aparam_view_list);
15398     }
15399     if (aparam_view_list) lives_list_free(aparam_view_list);
15400 
15401     add_aparam_menuitems(mt);
15402 
15403     unselect_all(mt);
15404     for (i = mt->num_video_tracks; i < num_tracks; i++) {
15405       add_video_track_behind(NULL, mt);
15406     }
15407 
15408     mt->clip_selected = clip_sel;
15409     mt_clip_select(mt, FALSE);
15410 
15411     vlist = mt->video_draws;
15412     llist = label_list;
15413     while (vlist) {
15414       eventbox = LIVES_WIDGET(vlist->data);
15415       label = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "label"));
15416       widget_opts.mnemonic_label = FALSE;
15417       lives_label_set_text(LIVES_LABEL(label), (const char *)llist->data);
15418       widget_opts.mnemonic_label = TRUE;
15419       vlist = vlist->next;
15420       llist = llist->next;
15421     }
15422     lives_list_free(label_list);
15423 
15424     if (mt->event_list) remove_markers(mt->event_list);
15425 
15426     mt->selected_tracks = lives_list_copy(seltracks);
15427     slist = mt->selected_tracks;
15428     while (slist) {
15429       eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, LIVES_POINTER_TO_INT(slist->data));
15430       checkbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "checkbutton");
15431 #ifdef ENABLE_GIW
15432       if (!prefs->lamp_buttons) {
15433 #endif
15434         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(checkbutton), TRUE);
15435 #ifdef ENABLE_GIW
15436       } else {
15437         giw_led_set_mode(GIW_LED(checkbutton), TRUE);
15438       }
15439 #endif
15440       slist = slist->next;
15441     }
15442     if (seltracks) lives_list_free(seltracks);
15443 
15444     mt->current_track = current_track;
15445     track_select(mt);
15446     if (mt->end_secs != end_secs && event_list_get_end_secs(mt->event_list) <= end_secs) set_timeline_end_secs(mt, end_secs);
15447   }
15448 
15449   mt->no_expose = FALSE;
15450 
15451   mt->undo_offset--;
15452 
15453   if (mt->undo_offset <= 1) mt_set_redoable(mt, MT_UNDO_NONE, NULL, FALSE);
15454   else {
15455     mt_undo *redo = (mt_undo *)(lives_list_nth_data(mt->undos, lives_list_length(mt->undos) - mt->undo_offset));
15456     mt_set_redoable(mt, redo->action, redo->extra, TRUE);
15457   }
15458   last_redo = (mt_undo *)lives_list_nth_data(mt->undos, lives_list_length(mt->undos) - 1 - mt->undo_offset);
15459   mt_set_undoable(mt, last_redo->action, last_redo->extra, TRUE);
15460 
15461   lives_ruler_set_value(LIVES_RULER(mt->timeline), ptr_time);
15462   lives_widget_queue_draw(mt->tlx_vbox);
15463 
15464   // TODO *****
15465   //if (last_redo->action<1024&&block_is_selected) mt_selblock(NULL, NULL, 0, 0, (livespointer)mt);
15466 
15467   if (last_redo->action == MT_UNDO_FILTER_MAP_CHANGE || mt->poly_state == POLY_FX_STACK) {
15468     if (last_redo->action == MT_UNDO_FILTER_MAP_CHANGE) mt_tl_move(mt, last_redo->tc);
15469     polymorph(mt, POLY_FX_STACK);
15470   }
15471   if (mt->poly_state != POLY_PARAMS) mt_show_current_frame(mt, FALSE);
15472 
15473   utxt = lives_utf8_strdown((tmp = get_undo_text(last_redo->action, last_redo->extra)), -1);
15474   lives_free(tmp);
15475 
15476   d_print(_("Redid %s\n"), utxt);
15477   lives_free(utxt);
15478 
15479   mt_desensitise(mt);
15480   mt_sensitise(mt);
15481 
15482   if (!mt->event_list) recover_layout_cancelled(FALSE);
15483 
15484   mt->idlefunc = mt_idle_add(mt);
15485   if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
15486 }
15487 
15488 
multitrack_view_details(LiVESMenuItem * menuitem,livespointer user_data)15489 void multitrack_view_details(LiVESMenuItem * menuitem, livespointer user_data) {
15490   char buff[512];
15491   lives_clipinfo_t *filew;
15492   lives_mt *mt = (lives_mt *)user_data;
15493   lives_clip_t *rfile = mainw->files[mt->render_file];
15494   uint32_t bsize = 0;
15495   double time = 0.;
15496   int num_events = 0;
15497 
15498   filew = create_clip_info_window(mainw->files[mt->render_file]->achans, TRUE);
15499 
15500   // type
15501   lives_snprintf(buff, 512, "\n  Event List");
15502   lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_type), buff, -1);
15503 
15504   // fps
15505   if (mt->fps > 0) {
15506     lives_snprintf(buff, 512, "\n  %.3f%s", mt->fps, rfile->ratio_fps ? "..." : "");
15507   } else {
15508     lives_snprintf(buff, 512, "%s", _("\n (variable)"));
15509   }
15510 
15511   lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_fps), buff, -1);
15512 
15513   // image size
15514   lives_snprintf(buff, 512, "\n  %dx%d", rfile->hsize, rfile->vsize);
15515   lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_size), buff, -1);
15516 
15517   if (mt->event_list) {
15518     bsize = event_list_get_byte_size(mt, mt->event_list, TRUE, &num_events);
15519     time = event_list_get_end_secs(mt->event_list);
15520   }
15521 
15522   lives_snprintf(buff, 512, "\n  %d", (frames_t)(mt->fps * time));
15523   lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_frames), buff, -1);
15524 
15525   lives_snprintf(buff, 512, _("\n  %.3f sec"), time);
15526   lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_vtime), buff, -1);
15527 
15528   // byte size
15529   lives_snprintf(buff, 512, _("\n  %d bytes\n%d events"), bsize, num_events);
15530   lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_fsize), buff, -1);
15531 
15532   if (mainw->files[mt->render_file]->achans > 0) {
15533     lives_snprintf(buff, 512, "\n  %d Hz %d bit", mainw->files[mt->render_file]->arate, mainw->files[mt->render_file]->asampsize);
15534     lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_lrate), buff, -1);
15535   }
15536 
15537   if (mainw->files[mt->render_file]->achans > 1) {
15538     lives_snprintf(buff, 512, "\n  %d Hz %d bit", mainw->files[mt->render_file]->arate, mainw->files[mt->render_file]->asampsize);
15539     lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_rrate), buff, -1);
15540   }
15541 }
15542 
15543 
add_effect_inner(lives_mt * mt,int num_in_tracks,int * in_tracks,int num_out_tracks,int * out_tracks,weed_plant_t * start_event,weed_plant_t * end_event)15544 static void add_effect_inner(lives_mt * mt, int num_in_tracks, int *in_tracks, int num_out_tracks, int *out_tracks,
15545                              weed_plant_t *start_event, weed_plant_t *end_event) {
15546   void **init_events;
15547 
15548   weed_event_t *event;
15549   weed_plant_t *filter = get_weed_filter(mt->current_fx);
15550 
15551   double timesecs = mt->ptr_time;
15552 
15553   weed_timecode_t start_tc = get_event_timecode(start_event);
15554   weed_timecode_t end_tc = get_event_timecode(end_event);
15555   weed_timecode_t tc = q_gint64(timesecs * TICKS_PER_SECOND_DBL, mt->fps);
15556 
15557   lives_rfx_t *rfx;
15558 
15559   boolean has_params;
15560 
15561   // set track_index (for special widgets)
15562   mt->track_index = -1;
15563   for (int i = 0; i < num_in_tracks; i++) {
15564     if (mt->current_track == in_tracks[i]) {
15565       mt->track_index = i;
15566       break;
15567     }
15568   }
15569 
15570   mt_backup(mt, MT_UNDO_APPLY_FILTER, 0);
15571 
15572   // add effect_init event
15573   mt->event_list = append_filter_init_event(mt->event_list, start_tc, mt->current_fx, num_in_tracks, -1, NULL);
15574   mt->init_event = get_last_event(mt->event_list);
15575   unlink_event(mt->event_list, mt->init_event);
15576   weed_set_int_array(mt->init_event, WEED_LEAF_IN_TRACKS, num_in_tracks, in_tracks);
15577   weed_set_int_array(mt->init_event, WEED_LEAF_OUT_TRACKS, num_out_tracks, out_tracks);
15578   insert_filter_init_event_at(mt->event_list, start_event, mt->init_event);
15579 
15580   if (pchain) {
15581     // no freep !
15582     lives_free(pchain);
15583     pchain = NULL;
15584   }
15585 
15586   if (num_in_params(filter, FALSE, FALSE) > 0)
15587     pchain = filter_init_add_pchanges(mt->event_list, filter, mt->init_event, num_in_tracks, 0);
15588 
15589   // add effect map event
15590   init_events = get_init_events_before(start_event, mt->init_event, TRUE);
15591   mt->event_list = append_filter_map_event(mt->event_list, start_tc, init_events);
15592   lives_free(init_events);
15593   event = get_last_event(mt->event_list);
15594   unlink_event(mt->event_list, event);
15595   insert_filter_map_event_at(mt->event_list, start_event, event, TRUE);
15596 
15597   // update all effect maps in block, appending init_event
15598   update_filter_maps(start_event, end_event, mt->init_event);
15599 
15600   // add effect deinit event
15601   mt->event_list = append_filter_deinit_event(mt->event_list, end_tc, (void *)mt->init_event, pchain);
15602   event = get_last_event(mt->event_list);
15603   unlink_event(mt->event_list, event);
15604   insert_filter_deinit_event_at(mt->event_list, end_event, event);
15605 
15606   // zip forward a bit, in case there is a FILTER_MAP at end_tc after our FILTER_DEINIT (e.g. if adding multiple filters)
15607   while (event && get_event_timecode(event) == end_tc) event = get_next_event(event);
15608   if (!event) event = get_last_event(mt->event_list);
15609   else event = get_prev_event(event);
15610 
15611   // add effect map event
15612   init_events = get_init_events_before(event, mt->init_event, FALSE); // also deletes the effect
15613   mt->event_list = append_filter_map_event(mt->event_list, end_tc, init_events);
15614   lives_free(init_events);
15615 
15616   event = get_last_event(mt->event_list);
15617   unlink_event(mt->event_list, event);
15618   insert_filter_map_event_at(mt->event_list, end_event, event, FALSE);
15619 
15620   if (mt->event_list) lives_widget_set_sensitive(mt->clear_event_list, TRUE);
15621 
15622   if (mt->current_fx == mt->avol_fx) return;
15623 
15624   if (mt->avol_fx != -1) {
15625     apply_avol_filter(mt);
15626   }
15627 
15628   if (mt->is_atrans) return;
15629 
15630   get_track_index(mt, tc);
15631 
15632   if (mt->track_index > -1) {
15633     rfx = weed_to_rfx(filter, FALSE);
15634 
15635     // here we just check if we have any params to display
15636     has_params = make_param_box(NULL, rfx);
15637     rfx_free(rfx);
15638     lives_free(rfx);
15639 
15640     if (has_params) {
15641       polymorph(mt, POLY_PARAMS);
15642       lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
15643       lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
15644     } else polymorph(mt, POLY_FX_STACK);
15645 
15646     mt_show_current_frame(mt, FALSE);
15647   }
15648 }
15649 
add_blank_frames_up_to(weed_plant_t * event_list,weed_plant_t * start_event,weed_timecode_t end_tc,double fps)15650 weed_plant_t *add_blank_frames_up_to(weed_plant_t *event_list, weed_plant_t *start_event, weed_timecode_t end_tc, double fps) {
15651   // add blank frames from FRAME event (or NULL) start_event up to and including (quantised) end_tc
15652   // returns updated event_list
15653   weed_timecode_t tc;
15654   weed_plant_t *shortcut = NULL;
15655   weed_timecode_t tl = q_dbl(1. / fps, fps);
15656   int blank_clip = -1;
15657   int64_t blank_frame = 0;
15658 
15659   if (start_event) tc = get_event_timecode(start_event) + tl;
15660   else tc = 0;
15661 
15662   for (; tc <= end_tc; tc = q_gint64(tc + tl, fps)) {
15663     event_list = insert_frame_event_at(event_list, tc, 1, &blank_clip, &blank_frame, &shortcut);
15664   }
15665   weed_set_double_value(event_list, WEED_LEAF_FPS, fps);
15666   return event_list;
15667 }
15668 
15669 
mt_add_region_effect(LiVESMenuItem * menuitem,livespointer user_data)15670 void mt_add_region_effect(LiVESMenuItem * menuitem, livespointer user_data) {
15671   lives_mt *mt = (lives_mt *)user_data;
15672 
15673   LiVESList *llist;
15674 
15675   weed_plant_t *start_event;
15676   weed_plant_t *end_event;
15677   weed_plant_t *last_frame_event = NULL;
15678 
15679   weed_timecode_t start_tc = q_gint64(mt->region_start * TICKS_PER_SECOND_DBL, mt->fps);
15680   weed_timecode_t end_tc = q_gint64(mt->region_end * TICKS_PER_SECOND_DBL - TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
15681   weed_timecode_t last_frame_tc = 0;
15682 
15683   char *filter_name;
15684   char *tname, *track_desc;
15685   char *tmp, *tmp1;
15686   char *tstart, *tend;
15687 
15688   boolean did_backup = mt->did_backup;
15689   boolean needs_idlefunc = FALSE;
15690 
15691   int numtracks = lives_list_length(mt->selected_tracks);
15692   int tcount = 0, tlast = -1000000, tsmall = -1, ctrack;
15693 
15694   int *tracks = (int *)lives_malloc(numtracks * sizint);
15695 
15696   if (mt->idlefunc > 0) {
15697     needs_idlefunc = TRUE;
15698     lives_source_remove(mt->idlefunc);
15699     mt->idlefunc = 0;
15700   }
15701 
15702   // sort selected tracks into ascending order
15703   while (tcount < numtracks) {
15704     tsmall = -1000000;
15705     llist = mt->selected_tracks;
15706     while (llist) {
15707       ctrack = LIVES_POINTER_TO_INT(llist->data);
15708       if ((tsmall == -1000000 || ctrack < tsmall) && ctrack > tlast) tsmall = ctrack;
15709       llist = llist->next;
15710     }
15711     tracks[tcount++] = tlast = tsmall;
15712   }
15713 
15714   // add blank frames up to region end (if necessary)
15715   if (mt->event_list &&
15716       ((last_frame_event = get_last_frame_event(mt->event_list)))) last_frame_tc = get_event_timecode(last_frame_event);
15717   if (end_tc > last_frame_tc) mt->event_list = add_blank_frames_up_to(mt->event_list, last_frame_event,
15718         end_tc - (double)(tracks[0] < 0) * TICKS_PER_SECOND_DBL / mt->fps,
15719         mt->fps);
15720 
15721   if (menuitem) mt->current_fx = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(menuitem), "idx"));
15722 
15723   start_event = get_frame_event_at(mt->event_list, start_tc, NULL, TRUE);
15724   end_event = get_frame_event_at(mt->event_list, end_tc, start_event, TRUE);
15725 
15726   add_effect_inner(mt, numtracks, tracks, 1, &tracks[0], start_event, end_event);
15727 
15728   if (!menuitem && !mt->is_atrans) {
15729     if (!did_backup) {
15730       if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
15731     }
15732     lives_free(tracks);
15733     return;
15734   }
15735 
15736   mt->last_fx_type = MT_LAST_FX_REGION;
15737 
15738   // create user message
15739   filter_name = weed_filter_idx_get_name(mt->current_fx, FALSE, FALSE);
15740   numtracks = enabled_in_channels(get_weed_filter(mt->current_fx), TRUE); // count repeated channels
15741   switch (numtracks) {
15742   case 1:
15743     tname = lives_fx_cat_to_text(LIVES_FX_CAT_EFFECT, FALSE); // effect
15744     track_desc = lives_strdup_printf(_("track %s"), (tmp = get_track_name(mt, tracks[0], FALSE)));
15745     lives_free(tmp);
15746     break;
15747   case 2:
15748     tname = lives_fx_cat_to_text(LIVES_FX_CAT_TRANSITION, FALSE); // transition
15749     track_desc = lives_strdup_printf(_("tracks %s and %s"), (tmp1 = get_track_name(mt, tracks[0], FALSE)),
15750                                      (tmp = get_track_name(mt, tracks[1], FALSE)));
15751     lives_free(tmp);
15752     lives_free(tmp1);
15753     break;
15754   default:
15755     tname = lives_fx_cat_to_text(LIVES_FX_CAT_COMPOSITOR, FALSE); // compositor
15756     track_desc = (_("selected tracks"));
15757     break;
15758   }
15759   lives_free(tracks);
15760 
15761   tstart = time_to_string(QUANT_TICKS(start_tc));
15762   tend = time_to_string(QUANT_TICKS(end_tc + TICKS_PER_SECOND_DBL / mt->fps));
15763 
15764   d_print(_("Added %s %s to %s from time %s to time %s\n"), tname, filter_name, track_desc, tstart, tend);
15765 
15766   lives_free(tstart);
15767   lives_free(tend);
15768   lives_free(filter_name);
15769   lives_free(tname);
15770   lives_free(track_desc);
15771 
15772   if (!did_backup) {
15773     mt->did_backup = FALSE;
15774     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
15775     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
15776   }
15777 }
15778 
15779 
mt_add_region_effect_idle(livespointer user_data)15780 static boolean mt_add_region_effect_idle(livespointer user_data) {
15781   mt_add_region_effect(LIVES_MENU_ITEM(dummy_menuitem), user_data);
15782   lives_widget_object_unref(dummy_menuitem);
15783   return FALSE;
15784 }
15785 
15786 
mt_add_block_effect(LiVESMenuItem * menuitem,livespointer user_data)15787 void mt_add_block_effect(LiVESMenuItem * menuitem, livespointer user_data) {
15788   lives_mt *mt = (lives_mt *)user_data;
15789   weed_plant_t *start_event = mt->block_selected->start_event;
15790   weed_plant_t *end_event = mt->block_selected->end_event;
15791   weed_timecode_t start_tc = get_event_timecode(start_event);
15792   weed_timecode_t end_tc = get_event_timecode(end_event);
15793   char *filter_name;
15794   char *tstart, *tend;
15795   char *tmp;
15796   int selected_track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mt->block_selected->eventbox),
15797                        "layer_number"));
15798   boolean did_backup = mt->did_backup;
15799   boolean needs_idlefunc = FALSE;
15800 
15801   if (mt->idlefunc > 0) {
15802     needs_idlefunc = TRUE;
15803     lives_source_remove(mt->idlefunc);
15804     mt->idlefunc = 0;
15805   }
15806 
15807   if (menuitem) mt->current_fx
15808       = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(menuitem), "idx"));
15809 
15810   mt->last_fx_type = MT_LAST_FX_BLOCK;
15811   add_effect_inner(mt, 1, &selected_track, 1, &selected_track, start_event, end_event);
15812 
15813   filter_name = weed_filter_idx_get_name(mt->current_fx, FALSE, FALSE);
15814 
15815   tstart = time_to_string(QUANT_TICKS(start_tc));
15816   tend = time_to_string(QUANT_TICKS(end_tc + TICKS_PER_SECOND_DBL / mt->fps));
15817 
15818   d_print(_("Added effect %s to track %s from time %s to time %s\n"), filter_name,
15819           (tmp = get_track_name(mt, selected_track, mt->aud_track_selected)),
15820           tstart, tend);
15821 
15822   lives_free(tstart);
15823   lives_free(tend);
15824   lives_free(tmp);
15825   lives_free(filter_name);
15826 
15827   if (!did_backup) {
15828     mt->did_backup = FALSE;
15829     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
15830     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
15831   }
15832 }
15833 
15834 
mt_add_block_effect_idle(livespointer user_data)15835 static boolean mt_add_block_effect_idle(livespointer user_data) {
15836   mt_add_block_effect(LIVES_MENU_ITEM(dummy_menuitem), user_data);
15837   lives_widget_object_unref(dummy_menuitem);
15838   return FALSE;
15839 }
15840 
15841 
on_mt_list_fx_activate(LiVESMenuItem * menuitem,livespointer user_data)15842 void on_mt_list_fx_activate(LiVESMenuItem * menuitem, livespointer user_data) {
15843   // list effects at current frame/track
15844   lives_mt *mt = (lives_mt *)user_data;
15845   polymorph(mt, POLY_FX_STACK);
15846 }
15847 
15848 
on_mt_delfx_activate(LiVESMenuItem * menuitem,livespointer user_data)15849 void on_mt_delfx_activate(LiVESMenuItem * menuitem, livespointer user_data) {
15850   lives_mt *mt = (lives_mt *)user_data;
15851 
15852   weed_timecode_t start_tc, end_tc;
15853 
15854   weed_plant_t *deinit_event, *init_event = mt->selected_init_event;
15855 
15856   int *tracks;
15857 
15858   char *fhash, *filter_name;
15859   char *tname, *track_desc;
15860   char *tmp, *tmp1;
15861   char *tstart, *tend;
15862 
15863   boolean did_backup = mt->did_backup;
15864   boolean needs_idlefunc = FALSE;
15865 
15866   int numtracks;
15867 
15868   if (!mt->selected_init_event) return;
15869 
15870   if (mt->is_rendering) return;
15871 
15872   if (mt->idlefunc > 0) {
15873     needs_idlefunc = TRUE;
15874     lives_source_remove(mt->idlefunc);
15875     mt->idlefunc = 0;
15876   }
15877 
15878   fhash = weed_get_string_value(init_event, WEED_LEAF_FILTER, NULL);
15879   mt->current_fx = weed_get_idx_for_hashname(fhash, TRUE);
15880   lives_free(fhash);
15881 
15882   deinit_event = weed_get_plantptr_value(init_event, WEED_LEAF_DEINIT_EVENT, NULL);
15883   filter_name = weed_filter_idx_get_name(mt->current_fx, FALSE, FALSE);
15884   start_tc = get_event_timecode(init_event);
15885   end_tc = get_event_timecode(deinit_event) + TICKS_PER_SECOND_DBL / mt->fps;
15886 
15887   tracks = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &numtracks);
15888 
15889   numtracks = enabled_in_channels(get_weed_filter(mt->current_fx), TRUE); // count repeated channels
15890   switch (numtracks) {
15891   case 1:
15892     tname = lives_fx_cat_to_text(LIVES_FX_CAT_EFFECT, FALSE); // effect
15893     track_desc = lives_strdup_printf(_("track %s"), (tmp = get_track_name(mt, tracks[0], FALSE)));
15894     lives_free(tmp);
15895     break;
15896   case 2:
15897     tname = lives_fx_cat_to_text(LIVES_FX_CAT_TRANSITION, FALSE); // transition
15898     track_desc = lives_strdup_printf(_("tracks %s and %s"), (tmp1 = get_track_name(mt, tracks[0], FALSE)),
15899                                      (tmp = get_track_name(mt, tracks[1], FALSE)));
15900     lives_free(tmp);
15901     lives_free(tmp1);
15902     break;
15903   default:
15904     tname = lives_fx_cat_to_text(LIVES_FX_CAT_COMPOSITOR, FALSE); // compositor
15905     track_desc = (_("selected tracks"));
15906     break;
15907   }
15908 
15909   lives_free(tracks);
15910 
15911   mt_backup(mt, MT_UNDO_DELETE_FILTER, 0);
15912 
15913   remove_filter_from_event_list(mt->event_list, mt->selected_init_event);
15914   remove_end_blank_frames(mt->event_list, TRUE);
15915 
15916   tstart = time_to_string(QUANT_TICKS(start_tc));
15917   tend = time_to_string(QUANT_TICKS(end_tc + TICKS_PER_SECOND_DBL / mt->fps));
15918 
15919   d_print(_("Deleted %s %s from %s from time %s to time %s\n"), tname, filter_name, track_desc, tstart, tend);
15920 
15921   lives_free(tstart);
15922   lives_free(tend);
15923   lives_free(filter_name);
15924   lives_free(track_desc);
15925 
15926   mt->selected_init_event = NULL;
15927   mt->current_fx = -1;
15928   if (mt->poly_state == POLY_PARAMS) polymorph(mt, POLY_CLIPS);
15929   else if (mt->poly_state == POLY_FX_STACK) polymorph(mt, POLY_FX_STACK);
15930   mt_show_current_frame(mt, FALSE);
15931 
15932   if (!did_backup) {
15933     mt->did_backup = FALSE;
15934     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
15935     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
15936   }
15937 }
15938 
15939 
mt_jumpto(lives_mt * mt,lives_direction_t dir)15940 static void mt_jumpto(lives_mt * mt, lives_direction_t dir) {
15941   track_rect *block;
15942 
15943   weed_timecode_t tc = q_gint64(mt->ptr_time * TICKS_PER_SECOND_DBL, mt->fps);
15944   weed_timecode_t start_tc, end_tc;
15945 
15946   LiVESWidget *eventbox;
15947 
15948   double secs = tc / TICKS_PER_SECOND_DBL;
15949   double offs = 1.;
15950 
15951   if (mt->current_track > -1 &&
15952       !mt->aud_track_selected) eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
15953   else {
15954     eventbox = (LiVESWidget *)lives_list_nth_data(mt->audio_draws, mt->current_track + mt->opts.back_audio_tracks);
15955     offs = 0.;
15956   }
15957   block = get_block_from_time(eventbox, secs, mt);
15958 
15959   if (block) {
15960     if (dir == LIVES_DIRECTION_BACKWARD) {
15961       if (tc == (start_tc = get_event_timecode(block->start_event))) {
15962         secs -= 1. / mt->fps;
15963         block = NULL;
15964       } else secs = start_tc / TICKS_PER_SECOND_DBL;
15965     } else {
15966       if (tc == q_gint64((end_tc = get_event_timecode(block->end_event)) + (offs * TICKS_PER_SECOND_DBL) / mt->fps, mt->fps)) {
15967         secs += 1. / mt->fps;
15968         block = NULL;
15969       } else secs = end_tc / TICKS_PER_SECOND_DBL + offs / mt->fps;
15970     }
15971   }
15972   if (!block) {
15973     if (dir == LIVES_DIRECTION_BACKWARD) {
15974       block = get_block_before(eventbox, secs, TRUE);
15975       if (!block) secs = 0.;
15976       else {
15977         if (tc == q_gint64((end_tc = get_event_timecode(block->end_event)) + (offs * TICKS_PER_SECOND_DBL) / mt->fps, mt->fps)) {
15978           secs = get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL;
15979         } else secs = end_tc / TICKS_PER_SECOND_DBL + offs / mt->fps;
15980       }
15981     } else {
15982       block = get_block_after(eventbox, secs, FALSE);
15983       if (!block) return;
15984       secs = get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL;
15985     }
15986   }
15987 
15988   if (secs < 0.) secs = 0.;
15989   if (secs > mt->end_secs) set_timeline_end_secs(mt, secs);
15990   mt->fm_edit_event = NULL;
15991   mt_tl_move(mt, secs);
15992 }
15993 
15994 
on_jumpback_activate(LiVESMenuItem * menuitem,livespointer user_data)15995 void on_jumpback_activate(LiVESMenuItem * menuitem, livespointer user_data) {
15996   lives_mt *mt = (lives_mt *)user_data;
15997   mt_jumpto(mt, LIVES_DIRECTION_BACKWARD);
15998 }
15999 
16000 
on_jumpnext_activate(LiVESMenuItem * menuitem,livespointer user_data)16001 void on_jumpnext_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16002   lives_mt *mt = (lives_mt *)user_data;
16003   mt_jumpto(mt, LIVES_DIRECTION_FORWARD);
16004 }
16005 
16006 
mt_jumpto_mark(lives_mt * mt,lives_direction_t dir)16007 static void mt_jumpto_mark(lives_mt * mt, lives_direction_t dir) {
16008   LiVESList *tl_marks = mt->tl_marks;
16009   double time = -1., marktime = -1.;
16010   double ptr_time = q_dbl(mt->ptr_time, mt->fps) / TICKS_PER_SECOND_DBL;
16011 
16012   while (tl_marks) {
16013     time = q_dbl(strtod((char *)tl_marks->data, NULL), mt->fps) / TICKS_PER_SECOND_DBL;
16014     if (time > ptr_time) break;
16015     if (marktime == time) continue;
16016     marktime = time;
16017     tl_marks = tl_marks->next;
16018   }
16019   if (dir == LIVES_DIRECTION_FORWARD) marktime = time;
16020   if (marktime > 0.)
16021     mt_tl_move(mt, marktime);
16022 }
16023 
16024 
on_jumpback_mark_activate(LiVESMenuItem * menuitem,livespointer user_data)16025 void on_jumpback_mark_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16026   lives_mt *mt = (lives_mt *)user_data;
16027   mt_jumpto_mark(mt, LIVES_DIRECTION_BACKWARD);
16028 }
16029 
16030 
on_jumpnext_mark_activate(LiVESMenuItem * menuitem,livespointer user_data)16031 void on_jumpnext_mark_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16032   lives_mt *mt = (lives_mt *)user_data;
16033   mt_jumpto_mark(mt, LIVES_DIRECTION_FORWARD);
16034 }
16035 
16036 
on_rename_track_activate(LiVESMenuItem * menuitem,livespointer user_data)16037 void on_rename_track_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16038   lives_mt *mt = (lives_mt *)user_data;
16039   _entryw *rnentry;
16040   LiVESWidget *xeventbox;
16041 
16042   char *cname;
16043 
16044   int response;
16045 
16046   if (mt->current_track < 0) return;
16047 
16048   xeventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
16049 
16050   cname = (char *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(xeventbox), "track_name");
16051 
16052   rnentry = create_rename_dialog(7);
16053 
16054   response = lives_dialog_run(LIVES_DIALOG(rnentry->dialog));
16055 
16056   if (response == LIVES_RESPONSE_CANCEL) return; // destroyed and freed in a callback
16057 
16058   lives_free(cname);
16059 
16060   cname = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rnentry->entry)));
16061 
16062   lives_widget_destroy(rnentry->dialog);
16063   lives_free(rnentry);
16064 
16065   lives_widget_object_set_data_auto(LIVES_WIDGET_OBJECT(xeventbox), "track_name", cname);
16066 
16067   set_track_label(LIVES_EVENT_BOX(xeventbox), mt->current_track);
16068 }
16069 
16070 
on_cback_audio_activate(LiVESMenuItem * menuitem,livespointer user_data)16071 void on_cback_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16072   lives_mt *mt = (lives_mt *)user_data;
16073   mt->current_track = -1;
16074   track_select(mt);
16075 }
16076 
16077 
on_render_activate(LiVESMenuItem * menuitem,livespointer user_data)16078 boolean on_render_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16079   lives_mt *mt = (lives_mt *)user_data;
16080   LiVESList *list;
16081   char *com;
16082 
16083   boolean had_audio = FALSE;
16084   boolean post_reset_ba = FALSE;
16085   boolean post_reset_ca = FALSE;
16086   boolean retval = FALSE;
16087 
16088   // save these values, because reget_afilesize() can reset them
16089   int arate = mainw->files[mt->render_file]->arate;
16090   int arps = mainw->files[mt->render_file]->arps;
16091   int asampsize = mainw->files[mt->render_file]->asampsize;
16092   int achans = mainw->files[mt->render_file]->achans;
16093   int signed_endian = mainw->files[mt->render_file]->signed_endian;
16094 
16095   int orig_file;
16096   int i;
16097 
16098   if (mt->idlefunc > 0) {
16099     lives_source_remove(mt->idlefunc);
16100     mt->idlefunc = 0;
16101   }
16102 
16103   if (!menuitem) {
16104     // pre-render audio (not used currently)
16105     mt->pr_audio = TRUE;
16106     had_audio = mt->has_audio_file;
16107     if (had_audio) {
16108       lives_rm(mainw->files[mt->render_file]->info_file);
16109       mainw->error = FALSE;
16110       mainw->cancelled = CANCEL_NONE;
16111       com = lives_strdup_printf("%s backup_audio \"%s\"", prefs->backend_sync, mainw->files[mt->render_file]->handle);
16112       lives_popen(com, TRUE, mainw->msg, MAINW_MSG_SIZE);
16113       lives_free(com);
16114       handle_backend_errors(FALSE);
16115       if (mainw->error) return FALSE;
16116     }
16117     mt->has_audio_file = TRUE;
16118   } else {
16119     mt->pr_audio = FALSE;
16120   }
16121 
16122   mt_desensitise(mt);
16123 
16124   /// disable playback optimisations
16125   mainw->effort = -EFFORT_RANGE_MAX;
16126   mainw->event_list = mt->event_list;
16127 
16128   mt->is_rendering = TRUE; // use this to test for rendering from mt (not mainw->is_rendering)
16129   lives_widget_set_sensitive(mt->render_vid, FALSE);
16130   lives_widget_set_sensitive(mt->render_aud, FALSE);
16131   lives_widget_set_sensitive(mt->normalise_aud, FALSE);
16132 
16133   if (mt->poly_state == POLY_PARAMS) polymorph(mt, POLY_FX_STACK);
16134 
16135   mt->pb_start_event = get_first_event(mainw->event_list);
16136 
16137   if (mt->opts.normalise_audp) {
16138     // Normalise audio (preference)
16139 
16140     // TODO - in future we could also check the pb volume levels and adjust to prevent clipping
16141     // - although this would be time consuming when clicking or activating "render" in mt
16142 
16143     // auto-adjust mixer levels:
16144     boolean has_backing_audio = FALSE;
16145     boolean has_channel_audio = FALSE;
16146 
16147     // -> if we have either but not both: backing audio or channel audio
16148     list = mt->audio_draws;
16149     if (mt->opts.back_audio_tracks >= 1) {
16150       // check backing track(s) for audio blocks
16151       for (i = 0; i < mt->opts.back_audio_tracks; list = list->next, i++) {
16152         if (!is_empty_track(LIVES_WIDGET_OBJECT(list->data))) {
16153           if (get_mixer_track_vol(mt, i) == 0.5) {
16154             has_backing_audio = TRUE;
16155 	    // *INDENT-OFF*
16156           }}}}
16157     // *INDENT-ON*
16158 
16159     list = mt->audio_draws;
16160     for (i = mt->opts.back_audio_tracks + 1; --i > 0; list = list->next);
16161     for (; list; list = list->next, i++) {
16162       // check channel track(s) for audio blocks
16163       if (!is_empty_track(LIVES_WIDGET_OBJECT(list->data))) {
16164         if (get_mixer_track_vol(mt, i) == 0.5) {
16165           has_channel_audio = TRUE;
16166         }
16167       }
16168     }
16169 
16170     // first checks done ^
16171 
16172     if (has_backing_audio && !has_channel_audio) {
16173       // backing but no channel audio
16174 
16175       // ->
16176       // if ALL backing levels are at 0.5, set them to 1.0
16177       list = mt->audio_draws;
16178       for (i = 0; i < mt->opts.back_audio_tracks; i++, list = list->next) {
16179         if (!is_empty_track(LIVES_WIDGET_OBJECT(list->data))) {
16180           if (get_mixer_track_vol(mt, i) != 0.5) {
16181             has_backing_audio = FALSE;
16182             break;
16183 	    // *INDENT-OFF*
16184           }}}}
16185     // *INDENT-ON*
16186 
16187     if (has_backing_audio) {
16188       post_reset_ba = TRUE; // reset levels after rendering
16189       list = mt->audio_draws;
16190       for (i = 0; i < mt->opts.back_audio_tracks; i++, list = list->next) {
16191         if (!is_empty_track(LIVES_WIDGET_OBJECT(list->data))) {
16192           set_mixer_track_vol(mt, i, 1.0);
16193         }
16194       }
16195     }
16196 
16197     if (!has_backing_audio && has_channel_audio) {
16198       // channel but no backing audio
16199 
16200       // ->
16201       // if ALL channel levels are at 0.5, set them all to 1.0
16202       list = mt->audio_draws;
16203       for (i = mt->opts.back_audio_tracks + 1; --i > 0; list = list->next);
16204       // check channel track(s) for audio blocks
16205       if (!is_empty_track(LIVES_WIDGET_OBJECT(list->data))) {
16206         if (get_mixer_track_vol(mt, i) != 0.5) {
16207           has_channel_audio = FALSE;
16208         }
16209       }
16210     }
16211 
16212     if (has_channel_audio) {
16213       post_reset_ca = TRUE; // reset levels after rendering
16214       list = mt->audio_draws;
16215       for (i = 0; i < mt->opts.back_audio_tracks; i++, list = list->next);
16216       // check channel track(s) for audio blocks
16217       for (; list; list = list->next) {
16218         if (!is_empty_track(LIVES_WIDGET_OBJECT(list->data))) {
16219           // set to 1.0
16220           set_mixer_track_vol(mt, i++, 1.0);
16221 	  // *INDENT-OFF*
16222 	}}}}
16223   // *INDENT-ON*
16224 
16225   if (render_to_clip(FALSE, FALSE)) {
16226     // rendering was successful
16227 
16228 #if 0
16229     if (mt->pr_audio) {
16230       mt->pr_audio = FALSE;
16231       lives_widget_set_sensitive(mt->render_vid, TRUE);
16232       lives_widget_set_sensitive(mt->render_aud, TRUE);
16233       lives_widget_set_sensitive(mt->normalise_aud, TRUE);
16234       mt->idlefunc = mt_idle_add(mt);
16235       return FALSE;
16236     }
16237 #endif
16238 
16239     mainw->files[mt->render_file]->start = mainw->files[mt->render_file]->frames > 0 ? 1 : 0;
16240     mainw->files[mt->render_file]->end = mainw->files[mt->render_file]->frames;
16241     if (mainw->files[mt->render_file]->frames == 0) {
16242       mainw->files[mt->render_file]->hsize = mainw->files[mt->render_file]->vsize = 0;
16243     }
16244     set_undoable(NULL, FALSE);
16245     mainw->files[mt->render_file]->changed = TRUE;
16246     add_to_clipmenu();
16247     mt->file_selected = orig_file = mainw->current_file;
16248     d_print(_("rendered %d frames to new clip.\n"), mainw->files[mt->render_file]->frames);
16249     if (mainw->scrap_file != -1 || mainw->ascrap_file != -1) mt->changed = FALSE;
16250     mt->is_rendering = FALSE;
16251 
16252     save_clip_values(orig_file);
16253 
16254     if (prefs->crash_recovery) add_to_recovery_file(mainw->files[mt->render_file]->handle);
16255     reset_clipmenu();
16256 
16257     if (post_reset_ba) {
16258       // reset after normalising backing audio
16259       for (i = 0; i < mt->opts.back_audio_tracks; i++) {
16260         if (!is_empty_track(LIVES_WIDGET_OBJECT(lives_list_nth_data(mt->audio_draws, i)))) {
16261           if (get_mixer_track_vol(mt, i) == 1.0) {
16262             set_mixer_track_vol(mt, i, 0.5);
16263 	    // *INDENT-OFF*
16264           }}}}
16265     // *INDENT-ON*
16266 
16267     if (post_reset_ca) {
16268       // reset after normalising channel audio
16269       list = mt->audio_draws;
16270       for (i = 0; i < mt->opts.back_audio_tracks; i++, list = list->next);
16271       // check channel track(s) for audio blocks
16272       for (; list; list = list->next) {
16273         if (!is_empty_track(LIVES_WIDGET_OBJECT(list->data))) {
16274           if (get_mixer_track_vol(mt, i) == 1.0) {
16275             set_mixer_track_vol(mt, i, 0.5);
16276 	    // *INDENT-OFF*
16277           }}}}
16278     // *INDENT-ON*
16279 
16280     mainw->current_file = mainw->first_free_file;
16281 
16282     if (!get_new_handle(mainw->current_file, NULL)) {
16283       mainw->current_file = orig_file;
16284       if (!multitrack_end(NULL, user_data)) switch_to_file((mainw->current_file = 0), orig_file);
16285       mt->idlefunc = mt_idle_add(mt);
16286       return FALSE;
16287     }
16288 
16289     cfile->hsize = mainw->files[orig_file]->hsize;
16290     cfile->vsize = mainw->files[orig_file]->vsize;
16291 
16292     cfile->img_type = mainw->files[orig_file]->img_type;
16293 
16294     cfile->pb_fps = cfile->fps = mainw->files[orig_file]->fps;
16295     cfile->ratio_fps = mainw->files[orig_file]->ratio_fps;
16296 
16297     cfile->arate = arate;
16298     cfile->arps = arps;
16299     cfile->asampsize = asampsize;
16300 
16301     cfile->achans = achans;
16302     cfile->signed_endian = signed_endian;
16303 
16304     cfile->bpp = cfile->img_type == IMG_TYPE_JPEG ? 24 : 32;
16305     cfile->changed = TRUE;
16306     cfile->is_loaded = TRUE;
16307 
16308     cfile->old_frames = cfile->frames;
16309 
16310     mt->render_file = mainw->current_file;
16311 
16312     if (prefs->mt_exit_render) {
16313       if (multitrack_end(menuitem, user_data)) return TRUE;
16314     }
16315 
16316     mt_init_clips(mt, orig_file, TRUE);
16317     if (mt->idlefunc > 0) lives_source_remove(mt->idlefunc);
16318     mt->idlefunc = 0;
16319     lives_widget_context_update();
16320     mt_clip_select(mt, TRUE);
16321 
16322     retval = TRUE;
16323   } else {
16324     char *curworkdir;
16325     // rendering failed - clean up
16326 
16327     cfile->frames = cfile->start = cfile->end = 0;
16328     mt->is_rendering = FALSE;
16329 
16330     mainw->event_list = NULL;
16331     if (mt->pr_audio) {
16332       com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, cfile->handle);
16333       lives_system(com, FALSE);
16334       lives_free(com);
16335       mt->has_audio_file = had_audio;
16336     } else {
16337       // remove subdir
16338       do_threaded_dialog(_("Cleaning up..."), FALSE);
16339       curworkdir = lives_build_filename(prefs->workdir, cfile->handle, NULL);
16340       lives_rmdir(curworkdir, TRUE);
16341       end_threaded_dialog();
16342     }
16343   }
16344 
16345   // enable GUI for next rendering
16346   lives_widget_set_sensitive(mt->render_vid, TRUE);
16347   lives_widget_set_sensitive(mt->render_aud, TRUE);
16348   lives_widget_set_sensitive(mt->normalise_aud, TRUE);
16349   mt_sensitise(mt);
16350 
16351   mt->idlefunc = mt_idle_add(mt);
16352   check_storage_space(-1, FALSE);
16353 
16354   return retval;
16355 }
16356 
16357 
on_prerender_aud_activate(LiVESMenuItem * menuitem,livespointer user_data)16358 void on_prerender_aud_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16359   lives_mt *mt = (lives_mt *)user_data;
16360   on_render_activate(menuitem, user_data);
16361   mainw->is_rendering = mainw->internal_messaging = mt->is_rendering = FALSE;
16362   mt_sensitise(mt);
16363   lives_widget_set_sensitive(mt->prerender_aud, FALSE);
16364 }
16365 
16366 
update_filter_events(lives_mt * mt,weed_plant_t * first_event,weed_timecode_t start_tc,weed_timecode_t end_tc,int track,weed_timecode_t new_start_tc,int new_track)16367 void update_filter_events(lives_mt * mt, weed_plant_t *first_event, weed_timecode_t start_tc, weed_timecode_t end_tc,
16368                           int track, weed_timecode_t new_start_tc, int new_track) {
16369   // move/remove filter_inits param_change and filter_deinits after deleting/moving a block
16370 
16371   // first_event: event just before block which is removed
16372 
16373   // start_tc, end_tc start and end timecodes of block on track
16374 
16375   // new_start_tc, new_track: new positions
16376 
16377   //if block is being deleted, or moved and move_effects is FALSE, this is called during block_delete to remove effects
16378 
16379   //if block is being moved and move_effects is TRUE, this is called after block was deleted and reinserted
16380 
16381   // filters which do not have our deleted block as an input, and filters with >2 in channels are not affected
16382 
16383   // filters with 1 in track (ours) are moved to the new block, if the option is set
16384 
16385   // other filters which are affected are deleted if their init/deinit reside entirely in the old block:
16386   // otherwise,
16387   // if their init_event is within the old block it moves right until we hit a frame
16388   // on the same track (+ the second track if applicable). If we pass the deinit_event, the filter is removed.
16389 
16390   // then, if the deinit event is within the old block, it is moved left until we find the frames for it similarly
16391 
16392   LiVESList *moved_events = NULL;
16393 
16394   weed_plant_t *event, *event_next;
16395   weed_plant_t *init_event, *deinit_event;
16396 
16397   weed_timecode_t last_frame_tc = 0, event_tc;
16398 
16399   boolean was_moved;
16400   boolean leave_event;
16401 
16402   int nins;
16403 
16404   register int i;
16405 
16406   event = get_last_frame_event(mt->event_list);
16407   if (event) last_frame_tc = get_event_timecode(event);
16408 
16409   // find first event inside old block
16410   if (!first_event) event = get_first_event(mt->event_list);
16411   else event = get_next_event(first_event);
16412   while (event && get_event_timecode(event) < start_tc) event = get_next_event(event);
16413 
16414   while (event && get_event_timecode(event) <= end_tc) {
16415     // step through all events in old block
16416     event_next = get_next_event(event);
16417     was_moved = FALSE;
16418 
16419     if (WEED_EVENT_IS_FILTER_INIT(event)) {
16420       // filter init event
16421       if (event == mt->avol_init_event) {
16422         event = event_next;
16423         continue;  // we move our audio volume effect using a separate mechanism
16424       }
16425       if (mt->opts.move_effects && mt->moving_block) {
16426         // move effects
16427 
16428         if (weed_plant_has_leaf(event, WEED_LEAF_DEINIT_EVENT) && weed_leaf_num_elements(event, WEED_LEAF_IN_TRACKS) == 1
16429             && weed_get_int_value(event, WEED_LEAF_IN_TRACKS, NULL) == track) {
16430           // this effect has a deinit_event, it has one in_track, which is this one
16431 
16432           deinit_event = weed_get_plantptr_value(event, WEED_LEAF_DEINIT_EVENT, NULL);
16433           if (get_event_timecode(deinit_event) <= end_tc) {
16434             //if the effect also ends within the block, we will move it to the new block
16435 
16436             if (lives_list_index(moved_events, event) == -1) {
16437               // update owners,in_tracks and out_tracks
16438               weed_set_int_value(event, WEED_LEAF_IN_TRACKS, new_track); // update the in_track to the new one
16439 
16440               if (weed_plant_has_leaf(event, WEED_LEAF_OUT_TRACKS)) {
16441                 int num_tracks;
16442                 int *out_tracks = weed_get_int_array_counted(event, WEED_LEAF_OUT_TRACKS, &num_tracks);
16443                 for (i = 0; i < num_tracks; i++) {
16444                   // update the out_track to the new one
16445                   if (out_tracks[i] == track) out_tracks[i] = new_track;
16446                 }
16447                 weed_set_int_array(event, WEED_LEAF_OUT_TRACKS, num_tracks, out_tracks);
16448                 lives_free(out_tracks);
16449               }
16450 
16451               // move to new position
16452               if (new_start_tc < start_tc) {
16453                 // if moving earlier, we need to move the init_event first, then the deinit_event
16454                 // this will also update the filter_maps, and param_changes
16455                 move_filter_init_event(mt->event_list, get_event_timecode(event) + new_start_tc - start_tc, event, mt->fps);
16456                 move_filter_deinit_event(mt->event_list, get_event_timecode(deinit_event) + new_start_tc - start_tc,
16457                                          deinit_event, mt->fps, TRUE);
16458                 if (event == first_event) first_event = NULL;
16459                 was_moved = TRUE;
16460               } else if (new_start_tc > start_tc) {
16461                 // if moving later, we need to move the deinit_event first, then the init_event
16462                 // this will also update the filter_maps, and param_changes
16463                 move_filter_deinit_event(mt->event_list, get_event_timecode(deinit_event) + new_start_tc - start_tc,
16464                                          deinit_event, mt->fps, TRUE);
16465                 move_filter_init_event(mt->event_list, get_event_timecode(event) + new_start_tc - start_tc, event, mt->fps);
16466                 if (event == first_event) first_event = NULL;
16467                 was_moved = TRUE;
16468               }
16469               // add this effect to our list of moved_events, so we don't end up moving it multiple times
16470               moved_events = lives_list_prepend(moved_events, event);
16471 	      // *INDENT-OFF*
16472             }}}}
16473       // *INDENT-ON*
16474 
16475       if (lives_list_index(moved_events, event) == -1 && event != mt->avol_init_event) {
16476         if (weed_plant_has_leaf(event, WEED_LEAF_DEINIT_EVENT) && weed_plant_has_leaf(event, WEED_LEAF_IN_TRACKS) &&
16477             (nins = weed_leaf_num_elements(event, WEED_LEAF_IN_TRACKS)) <= 2) {
16478           int *in_tracks = weed_get_int_array(event, WEED_LEAF_IN_TRACKS, NULL);
16479           if (in_tracks[0] == track || (nins == 2 && in_tracks[1] == track)) {
16480             // if the event wasnt moved (either because user chose not to, or block was deleted, or it had 2 tracks)
16481             // move the init_event to the right until we find frames from all tracks. If we pass the deinit_event then
16482             // the effect is removed.
16483             // Effects with one in_track which is other, or effects with >2 in tracks, do not suffer this fate.
16484 
16485             deinit_event = weed_get_plantptr_value(event, WEED_LEAF_DEINIT_EVENT, NULL);
16486 
16487             if (get_event_timecode(deinit_event) <= end_tc) {
16488               remove_filter_from_event_list(mt->event_list, event);
16489               was_moved = TRUE;
16490               if (event == first_event) first_event = NULL;
16491             } else {
16492               if (!move_event_right(mt->event_list, event, TRUE, mt->fps)) {
16493                 // moved event right until it hits a frame from all tracks, if it passed the deinit_event it is removed
16494                 // param_change events are also scaled in time
16495                 was_moved = TRUE;
16496                 if (event == first_event) first_event = NULL;
16497 		// *INDENT-OFF*
16498               }}}
16499 	  // *INDENT-ON*
16500           lives_free(in_tracks);
16501         }
16502       }
16503     } else {
16504       leave_event = TRUE;
16505       if (WEED_EVENT_IS_FILTER_DEINIT(event)) {
16506         // check filter deinit
16507         if (mt->opts.move_effects && mt->moving_block) {
16508           if (weed_plant_has_leaf(event, WEED_LEAF_INIT_EVENT)) {
16509             init_event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
16510             event_tc = get_event_timecode(event);
16511             if (init_event != mt->avol_init_event &&
16512                 (event_tc > last_frame_tc ||
16513                  (lives_list_index(moved_events, init_event) == -1 &&
16514                   weed_plant_has_leaf(event, WEED_LEAF_IN_TRACKS)
16515                   && (nins = weed_leaf_num_elements(event, WEED_LEAF_IN_TRACKS)) <= 2))) {
16516               // move it if: it is not avol event, and either it is after all frames or init_event was not moved
16517               // and it has one or two tracks, one of which is our track
16518               if (event_tc <= last_frame_tc) {
16519                 int *in_tracks = weed_get_int_array(event, WEED_LEAF_IN_TRACKS, NULL);
16520                 if (in_tracks[0] == track || (nins == 2 && in_tracks[1] == track)) {
16521                   leave_event = FALSE;
16522                 }
16523                 lives_free(in_tracks);
16524               } else leave_event = FALSE;
16525 	      // *INDENT-OFF*
16526             }}}
16527 	// *INDENT-ON*
16528 
16529         if (!leave_event && !move_event_left(mt->event_list, event, TRUE, mt->fps)) {
16530           // move the event left until it hits a frame from all tracks
16531           // if it passes the init_event, it is removed
16532           // param change events are also scaled in time
16533           was_moved = TRUE;
16534 	  // *INDENT-OFF*
16535         }}}
16536     // *INDENT-ON*
16537 
16538     if (was_moved) {
16539       // if we moved an event, re-scan from the start of the old block
16540       if (!first_event) event = get_first_event(mt->event_list);
16541       else event = get_next_event(first_event);
16542       while (event && get_event_timecode(event) < start_tc) event = get_next_event(event);
16543     } else {
16544       event = event_next;
16545       if (WEED_EVENT_IS_FRAME(event)) first_event = event;
16546     }
16547   }
16548   if (moved_events) lives_list_free(moved_events);
16549 }
16550 
16551 
on_split_activate(LiVESMenuItem * menuitem,livespointer user_data)16552 void on_split_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16553   // split current block at current time
16554   lives_mt *mt = (lives_mt *)user_data;
16555 
16556   weed_timecode_t tc;
16557 
16558   double timesecs = mt->ptr_time;
16559 
16560   boolean did_backup = mt->did_backup;
16561   boolean needs_idlefunc = FALSE;
16562 
16563   if (!mt->putative_block) return;
16564 
16565   if (mt->idlefunc > 0) {
16566     needs_idlefunc = TRUE;
16567     lives_source_remove(mt->idlefunc);
16568     mt->idlefunc = 0;
16569   }
16570 
16571   if (mt->context_time != -1. && mt->use_context) {
16572     timesecs = mt->context_time;
16573     mt->context_time = -1.;
16574     mt->use_context = FALSE;
16575   }
16576 
16577   mt_backup(mt, MT_UNDO_SPLIT, 0);
16578 
16579   tc = q_gint64(timesecs * TICKS_PER_SECOND_DBL, mt->fps);
16580 
16581   split_block(mt, mt->putative_block, tc, mt->current_track, FALSE);
16582 
16583   if (!did_backup) {
16584     mt->did_backup = FALSE;
16585     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
16586     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
16587   }
16588 }
16589 
16590 
on_split_curr_activate(LiVESMenuItem * menuitem,livespointer user_data)16591 void on_split_curr_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16592   // split current track at current time
16593   lives_mt *mt = (lives_mt *)user_data;
16594   double timesecs = mt->ptr_time;
16595   boolean did_backup = mt->did_backup;
16596   boolean needs_idlefunc = FALSE;
16597   weed_timecode_t tc;
16598   LiVESWidget *eventbox;
16599   track_rect *block;
16600 
16601   if (mt->idlefunc > 0) {
16602     needs_idlefunc = TRUE;
16603     lives_source_remove(mt->idlefunc);
16604     mt->idlefunc = 0;
16605   }
16606 
16607   tc = q_gint64(timesecs * TICKS_PER_SECOND_DBL, mt->fps);
16608 
16609   if (mt->current_track == -1) eventbox = (LiVESWidget *)mt->audio_draws->data;
16610   else eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
16611 
16612   block = get_block_from_time(eventbox, timesecs, mt);
16613 
16614   if (!block) {
16615     if (!did_backup) {
16616       if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
16617     }
16618     return;
16619   }
16620 
16621   mt_backup(mt, MT_UNDO_SPLIT, 0);
16622   split_block(mt, block, tc, mt->current_track, FALSE);
16623 
16624   if (!did_backup) {
16625     mt->did_backup = FALSE;
16626     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
16627     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
16628   }
16629 }
16630 
16631 
on_split_sel_activate(LiVESMenuItem * menuitem,livespointer user_data)16632 void on_split_sel_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16633   // split selected tracks at current time
16634   lives_mt *mt = (lives_mt *)user_data;
16635   LiVESList *selt = mt->selected_tracks;
16636   LiVESWidget *eventbox;
16637   int track;
16638   track_rect *block;
16639   double timesecs = mt->ptr_time;
16640   boolean did_backup = mt->did_backup;
16641   boolean needs_idlefunc = FALSE;
16642 
16643   if (!mt->selected_tracks) return;
16644 
16645   if (mt->idlefunc > 0) {
16646     needs_idlefunc = TRUE;
16647     lives_source_remove(mt->idlefunc);
16648     mt->idlefunc = 0;
16649   }
16650 
16651   mt_backup(mt, MT_UNDO_SPLIT_MULTI, 0);
16652 
16653   while (selt) {
16654     track = LIVES_POINTER_TO_INT(selt->data);
16655     eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, track);
16656     block = get_block_from_time(eventbox, timesecs, mt);
16657     if (block) split_block(mt, block, timesecs * TICKS_PER_SECOND_DBL, track, FALSE);
16658     selt = selt->next;
16659   }
16660 
16661   if (!did_backup) {
16662     mt->did_backup = FALSE;
16663     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
16664     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
16665   }
16666 }
16667 
16668 
on_delblock_activate(LiVESMenuItem * menuitem,livespointer user_data)16669 static void on_delblock_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16670   lives_mt *mt = (lives_mt *)user_data;
16671   weed_timecode_t start_tc, end_tc;
16672   weed_plant_t *first_event;
16673   weed_plant_t *event, *prevevent;
16674 
16675   track_rect *block, *blockprev, *blocknext;
16676 
16677   LiVESWidget *eventbox, *aeventbox;
16678 
16679   char *tmp;
16680   char *tstart, *tend;
16681 
16682   boolean done = FALSE;
16683   boolean did_backup = mt->did_backup;
16684   boolean needs_idlefunc = FALSE;
16685 
16686   int track;
16687 
16688   if (mt->is_rendering) return;
16689 
16690   if (mt->idlefunc > 0) {
16691     needs_idlefunc = TRUE;
16692     lives_source_remove(mt->idlefunc);
16693     mt->idlefunc = 0;
16694   }
16695 
16696   mt->context_time = -1.;
16697 
16698   if (mt->current_track != -1) mt_backup(mt, MT_UNDO_DELETE_BLOCK, 0);
16699   else mt_backup(mt, MT_UNDO_DELETE_AUDIO_BLOCK, 0);
16700 
16701   if (!mt->block_selected) mt->block_selected = mt->putative_block;
16702   block = mt->block_selected;
16703   eventbox = block->eventbox;
16704 
16705   if (mt->current_track != -1) track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox),
16706                                          "layer_number"));
16707   else track = -1;
16708 
16709   if ((aeventbox = LIVES_WIDGET(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "atrack"))) != NULL) {
16710     int current_track = mt->current_track;
16711     mt->current_track = track;
16712     mt->block_selected = get_block_from_time(aeventbox, get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL, mt);
16713     if (mt->block_selected) on_delblock_activate(NULL, user_data);
16714     mt->block_selected = block;
16715     mt->current_track = current_track;
16716   }
16717 
16718   mt_desensitise(mt);
16719 
16720   start_tc = get_event_timecode(block->start_event);
16721   end_tc = get_event_timecode(block->end_event);
16722 
16723   first_event = get_prev_event(block->start_event);
16724   while (first_event && get_event_timecode(first_event) == start_tc) {
16725     first_event = get_prev_event(first_event);
16726   }
16727 
16728   event = block->end_event;
16729 
16730   if (mt->current_track != -1 && !is_audio_eventbox(eventbox)) {
16731     // delete frames
16732     while (event && !done) {
16733       prevevent = get_prev_frame_event(event);
16734       if (event == block->start_event) done = TRUE;
16735       remove_frame_from_event(mt->event_list, event, track);
16736       if (!done) event = prevevent;
16737     }
16738   } else {
16739     // update audio events
16740     // if last event in block turns audio off, delete it
16741     if (get_audio_frame_vel(block->end_event, track) == 0.) {
16742       remove_audio_for_track(block->end_event, track);
16743     }
16744 
16745     // if first event in block is the end of another block, turn audio off (velocity==0)
16746     if (block->prev && block->start_event == block->prev->end_event) {
16747       insert_audio_event_at(block->start_event, track, 1, 0., 0.);
16748     }
16749     // else we'll delete it
16750     else {
16751       remove_audio_for_track(block->start_event, track);
16752     }
16753   }
16754 
16755   if ((blockprev = block->prev) != NULL) blockprev->next = block->next;
16756   else lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "blocks", (livespointer)block->next);
16757   if ((blocknext = block->next) != NULL) blocknext->prev = blockprev;
16758 
16759   if (block == mt->block_selected) mt->block_selected = NULL;
16760   lives_free(block);
16761 
16762   lives_widget_queue_draw(eventbox);
16763   if (cfile->achans > 0 && mt->audio_draws && mt->opts.back_audio_tracks > 0 && eventbox == mt->audio_draws->data &&
16764       LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"))) {
16765     LiVESWidget *xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "achan0");
16766     if (xeventbox) lives_widget_queue_draw(xeventbox);
16767     xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "achan1");
16768     if (xeventbox) lives_widget_queue_draw(xeventbox);
16769   }
16770 
16771   tmp = get_track_name(mt, mt->current_track, FALSE);
16772 
16773   tstart = time_to_string(QUANT_TICKS(start_tc));
16774   tend = time_to_string(QUANT_TICKS(end_tc + TICKS_PER_SECOND_DBL / mt->fps));
16775 
16776   if (mt->current_track != -1 && !is_audio_eventbox(eventbox)) {
16777     d_print(_("Deleted frames from time %s to time %s on track %s\n"), tstart, tend, tmp);
16778   } else {
16779     d_print(_("Deleted audio from time %s to time %s on track %s\n"), tstart, tend, tmp);
16780   }
16781   lives_free(tmp);
16782   lives_free(tstart);
16783   lives_free(tend);
16784 
16785   if ((mt->opts.grav_mode == GRAV_MODE_LEFT || mt->opts.grav_mode == GRAV_MODE_RIGHT) && !mt->moving_block && !did_backup) {
16786     // gravity left - remove first gap from old block start to end time
16787     // gravity right - remove last gap from 0 to old block end time
16788 
16789     double oldr_start = mt->region_start;
16790     double oldr_end = mt->region_end;
16791     LiVESList *tracks_sel = NULL;
16792     if (mt->current_track != -1) {
16793       tracks_sel = lives_list_copy(mt->selected_tracks);
16794       if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
16795       mt->selected_tracks = NULL;
16796       mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(mt->current_track));
16797     }
16798 
16799     if (mt->opts.grav_mode == GRAV_MODE_LEFT) {
16800       mt->region_start = start_tc / TICKS_PER_SECOND_DBL;
16801       mt->region_end = mt->end_secs;
16802     } else {
16803       mt->region_start = 0.;
16804       mt->region_end = end_tc / TICKS_PER_SECOND_DBL;
16805     }
16806 
16807     remove_first_gaps(NULL, mt);
16808     if (mt->current_track > -1) {
16809       lives_list_free(mt->selected_tracks);
16810       mt->selected_tracks = lives_list_copy(tracks_sel);
16811       if (tracks_sel) lives_list_free(tracks_sel);
16812     }
16813     mt->region_start = oldr_start;
16814     mt->region_end = oldr_end;
16815     mt_sensitise(mt);
16816   }
16817 
16818   remove_end_blank_frames(mt->event_list, FALSE); // leave filter_inits
16819 
16820   if (!mt->opts.move_effects || !mt->moving_block) {
16821     update_filter_events(mt, first_event, start_tc, end_tc, track, start_tc, track);
16822     if (mt->block_selected == block) {
16823       mt->block_selected = NULL;
16824       unselect_all(mt);
16825     }
16826     mt_sensitise(mt);
16827   }
16828 
16829   remove_end_blank_frames(mt->event_list, TRUE); // remove filter inits
16830 
16831   if ((!mt->moving_block || !get_first_frame_event(mt->event_list)) && mt->avol_fx != -1 && !blocknext &&
16832       mt->audio_draws && get_first_event(mt->event_list)) {
16833     apply_avol_filter(mt);
16834   }
16835 
16836   if (!did_backup && mt->framedraw && mt->current_rfx && mt->init_event &&
16837       mt->poly_state == POLY_PARAMS && weed_plant_has_leaf(mt->init_event, WEED_LEAF_IN_TRACKS)) {
16838     weed_timecode_t tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) * TICKS_PER_SECOND_DBL +
16839                                   get_event_timecode(mt->init_event), mt->fps);
16840     get_track_index(mt, tc);
16841   }
16842 
16843   if (!mt->moving_block) {
16844     redraw_eventbox(mt, eventbox);
16845     paint_lines(mt, mt->ptr_time, TRUE, NULL);
16846     mt_show_current_frame(mt, FALSE);
16847   }
16848 
16849   if (!did_backup) {
16850     mt->did_backup = FALSE;
16851     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
16852     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
16853   }
16854 
16855   if (!mt->moving_block) {
16856     mt_desensitise(mt);
16857     mt_sensitise(mt);
16858   }
16859   if (!mt->event_list) recover_layout_cancelled(FALSE);
16860 }
16861 
16862 
mt_selection_lock(LiVESMenuItem * menuitem,livespointer user_data)16863 void mt_selection_lock(LiVESMenuItem * menuitem, livespointer user_data) {
16864   lives_mt *mt = (lives_mt *)user_data;
16865   mt->sel_locked = lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(menuitem));
16866   lives_widget_set_sensitive(mt->spinbutton_start, !mt->sel_locked);
16867   lives_widget_set_sensitive(mt->spinbutton_end, !mt->sel_locked);
16868 }
16869 
16870 
on_seltrack_activate(LiVESMenuItem * menuitem,livespointer user_data)16871 void on_seltrack_activate(LiVESMenuItem * menuitem, livespointer user_data) {
16872   lives_mt *mt = (lives_mt *)user_data;
16873   LiVESWidget *eventbox;
16874   LiVESWidget *checkbutton;
16875 
16876   boolean mi_state;
16877 
16878   lives_mt_poly_state_t statep;
16879 
16880   if (mt->current_track == -1) return;
16881 
16882   eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
16883   checkbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "checkbutton");
16884 
16885   mi_state = lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(menuitem));
16886 
16887   if (mi_state) {
16888     // selected
16889     if (lives_list_index(mt->selected_tracks, LIVES_INT_TO_POINTER(mt->current_track)) == -1)
16890       mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(mt->current_track));
16891   } else {
16892     // unselected
16893     if (lives_list_index(mt->selected_tracks, LIVES_INT_TO_POINTER(mt->current_track)) != -1)
16894       mt->selected_tracks = lives_list_remove(mt->selected_tracks, LIVES_INT_TO_POINTER(mt->current_track));
16895   }
16896 
16897 #ifdef ENABLE_GIW
16898   if (!prefs->lamp_buttons) {
16899 #endif
16900     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(checkbutton)) != mi_state) {
16901       lives_signal_handlers_block_by_func(checkbutton, (livespointer)on_seltrack_toggled, (livespointer)mt);
16902       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(checkbutton), mi_state);
16903       lives_signal_handlers_unblock_by_func(checkbutton, (livespointer)on_seltrack_toggled, (livespointer)mt);
16904     }
16905 #ifdef ENABLE_GIW
16906   } else {
16907     if (giw_led_get_mode(GIW_LED(checkbutton)) != mi_state) {
16908       lives_signal_handlers_block_by_func(checkbutton, (livespointer)on_seltrack_toggled, (livespointer)mt);
16909       giw_led_set_mode(GIW_LED(checkbutton), mi_state);
16910       lives_signal_handlers_unblock_by_func(checkbutton, (livespointer)on_seltrack_toggled, (livespointer)mt);
16911     }
16912   }
16913 #endif
16914   do_sel_context(mt);
16915 
16916   lives_widget_set_sensitive(mt->ins_gap_sel, FALSE);
16917   lives_widget_set_sensitive(mt->remove_gaps, FALSE);
16918   lives_widget_set_sensitive(mt->remove_first_gaps, FALSE);
16919   lives_widget_set_sensitive(mt->split_sel, FALSE);
16920   lives_widget_set_sensitive(mt->fx_region, FALSE);
16921 
16922   if (mt->selected_tracks) {
16923     if (mt->event_list && get_first_event(mt->event_list)) {
16924       lives_widget_set_sensitive(mt->split_sel, mt_selblock(NULL, (livespointer)mt) != NULL);
16925     }
16926 
16927     if (mt->region_start != mt->region_end) {
16928       if (mt->event_list && get_first_event(mt->event_list)) {
16929         lives_widget_set_sensitive(mt->ins_gap_sel, TRUE);
16930         lives_widget_set_sensitive(mt->remove_gaps, TRUE);
16931         lives_widget_set_sensitive(mt->remove_first_gaps, TRUE);
16932       }
16933       lives_widget_set_sensitive(mt->fx_region, TRUE);
16934       switch (lives_list_length(mt->selected_tracks)) {
16935       case 1:
16936         if (cfile->achans == 0) lives_widget_set_sensitive(mt->fx_region_a, FALSE);
16937         lives_widget_set_sensitive(mt->fx_region_2a, FALSE);
16938         lives_widget_set_sensitive(mt->fx_region_2v, FALSE);
16939         lives_widget_set_sensitive(mt->fx_region_2av, FALSE);
16940         break;
16941       case 2:
16942         lives_widget_set_sensitive(mt->fx_region_a, FALSE);
16943         lives_widget_set_sensitive(mt->fx_region_v, FALSE);
16944         if (!mt->opts.pertrack_audio)
16945           lives_widget_set_sensitive(mt->fx_region_2a, FALSE);
16946         break;
16947       default:
16948         break;
16949       }
16950     }
16951   }
16952 
16953   // update labels
16954   statep = get_poly_state_from_page(mt);
16955   if (statep == POLY_TRANS || statep == POLY_COMP) {
16956     polymorph(mt, POLY_NONE);
16957     polymorph(mt, statep);
16958   }
16959 }
16960 
16961 
on_seltrack_toggled(LiVESWidget * checkbutton,livespointer user_data)16962 void on_seltrack_toggled(LiVESWidget * checkbutton, livespointer user_data) {
16963   int track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(checkbutton), "layer_number"));
16964   lives_mt *mt = (lives_mt *)user_data;
16965 
16966   if (!LIVES_IS_INTERACTIVE) return;
16967 
16968   mt->current_track = track;
16969 
16970   if (track > -1) mt->aud_track_selected = FALSE;
16971   else mt->aud_track_selected = TRUE;
16972 
16973   // track_select will call on_seltrack_activate, which will set our new state
16974   track_select(mt);
16975 
16976 }
16977 
16978 
mt_desensitise(lives_mt * mt)16979 void mt_desensitise(lives_mt * mt) {
16980   double val;
16981 
16982   mainw->sense_state &= LIVES_SENSE_STATE_INTERACTIVE;
16983   mainw->sense_state |= LIVES_SENSE_STATE_INSENSITIZED;
16984 
16985   lives_widget_set_sensitive(mt->clipedit, FALSE);
16986   lives_widget_set_sensitive(mt->insert, FALSE);
16987   lives_widget_set_sensitive(mt->audio_insert, FALSE);
16988   lives_widget_set_sensitive(mt->playall, FALSE);
16989   lives_widget_set_sensitive(mt->playsel, FALSE);
16990   lives_widget_set_sensitive(mt->view_events, FALSE);
16991   lives_widget_set_sensitive(mt->view_sel_events, FALSE);
16992   lives_widget_set_sensitive(mt->render, FALSE);
16993   lives_widget_set_sensitive(mt->prerender_aud, FALSE);
16994   lives_widget_set_sensitive(mt->delblock, FALSE);
16995   lives_widget_set_sensitive(mt->save_event_list, FALSE);
16996   lives_widget_set_sensitive(mt->load_event_list, FALSE);
16997   lives_widget_set_sensitive(mt->clear_event_list, FALSE);
16998   lives_widget_set_sensitive(mt->remove_gaps, FALSE);
16999   lives_widget_set_sensitive(mt->remove_first_gaps, FALSE);
17000   lives_widget_set_sensitive(mt->undo, FALSE);
17001   lives_widget_set_sensitive(mt->redo, FALSE);
17002   lives_widget_set_sensitive(mt->show_quota, FALSE);
17003   lives_widget_set_sensitive(mt->jumpback, FALSE);
17004   lives_widget_set_sensitive(mt->jumpnext, FALSE);
17005   lives_widget_set_sensitive(mt->mark_jumpback, FALSE);
17006   lives_widget_set_sensitive(mt->mark_jumpnext, FALSE);
17007   lives_widget_set_sensitive(mt->fx_edit, FALSE);
17008   lives_widget_set_sensitive(mt->fx_delete, FALSE);
17009   lives_widget_set_sensitive(mt->checkbutton_avel_reverse, FALSE);
17010   lives_widget_set_sensitive(mt->spinbutton_avel, FALSE);
17011   lives_widget_set_sensitive(mt->avel_scale, FALSE);
17012   lives_widget_set_sensitive(mt->change_vals, FALSE);
17013   lives_widget_set_sensitive(mt->add_vid_behind, FALSE);
17014   lives_widget_set_sensitive(mt->add_vid_front, FALSE);
17015   lives_widget_set_sensitive(mt->quit, FALSE);
17016   lives_widget_set_sensitive(mt->clear_ds, FALSE);
17017   lives_widget_set_sensitive(mt->open_menu, FALSE);
17018 #ifdef HAVE_WEBM
17019   lives_widget_set_sensitive(mt->open_loc_menu, FALSE);
17020 #endif
17021 #ifdef ENABLE_DVD_GRAB
17022   lives_widget_set_sensitive(mt->vcd_dvd_menu, FALSE);
17023 #endif
17024 #ifdef HAVE_LDVGRAB
17025   lives_widget_set_sensitive(mt->device_menu, FALSE);
17026 #endif
17027   lives_widget_set_sensitive(mt->recent_menu, FALSE);
17028   lives_widget_set_sensitive(mt->load_set, FALSE);
17029   lives_widget_set_sensitive(mt->save_set, FALSE);
17030   lives_widget_set_sensitive(mt->close, FALSE);
17031   lives_widget_set_sensitive(mt->capture, FALSE);
17032   lives_widget_set_sensitive(mt->gens_submenu, FALSE);
17033   lives_widget_set_sensitive(mt->troubleshoot, FALSE);
17034   lives_widget_set_sensitive(mt->expl_missing, FALSE);
17035 
17036   lives_widget_set_sensitive(mt->fx_region, FALSE);
17037   lives_widget_set_sensitive(mt->ins_gap_sel, FALSE);
17038   lives_widget_set_sensitive(mt->ins_gap_cur, FALSE);
17039 
17040   if (mt->poly_state == POLY_IN_OUT) {
17041     if (mt->block_selected) {
17042       val = lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->spinbutton_in));
17043       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_in), val, val);
17044 
17045       val = lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->spinbutton_out));
17046       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_out), val, val);
17047     }
17048   }
17049 }
17050 
17051 
mt_sensitise(lives_mt * mt)17052 void mt_sensitise(lives_mt * mt) {
17053   LiVESWidget *eventbox = NULL;
17054 
17055   if (mt->in_sensitise) return; // prevent infinite loops
17056   mt->in_sensitise = TRUE;
17057 
17058   mainw->sense_state &= LIVES_SENSE_STATE_INTERACTIVE;
17059   mainw->sense_state |= LIVES_SENSE_STATE_SENSITIZED;
17060 
17061   if (mt->event_list && get_first_event(mt->event_list)) {
17062     lives_widget_set_sensitive(mt->playall, TRUE);
17063     lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
17064     lives_widget_set_sensitive(mt->view_events, TRUE);
17065     lives_widget_set_sensitive(mt->view_sel_events, mt->region_start != mt->region_end);
17066     lives_widget_set_sensitive(mt->render, TRUE);
17067     if (mt->avol_init_event && mt->opts.pertrack_audio && mainw->files[mt->render_file]->achans > 0)
17068       lives_widget_set_sensitive(mt->prerender_aud, TRUE);
17069     lives_widget_set_sensitive(mt->save_event_list, !mainw->recording_recovered);
17070   } else {
17071     lives_widget_set_sensitive(mt->playall, FALSE);
17072     lives_widget_set_sensitive(mt->playsel, FALSE);
17073     lives_widget_set_sensitive(mainw->m_playbutton, FALSE);
17074     lives_widget_set_sensitive(mt->view_events, FALSE);
17075     lives_widget_set_sensitive(mt->view_sel_events, FALSE);
17076     lives_widget_set_sensitive(mt->render, FALSE);
17077     lives_widget_set_sensitive(mt->save_event_list, FALSE);
17078   }
17079 
17080   if (mt->event_list) lives_widget_set_sensitive(mt->clear_event_list, TRUE);
17081 
17082   lives_widget_set_sensitive(mt->add_vid_behind, TRUE);
17083   lives_widget_set_sensitive(mt->add_vid_front, TRUE);
17084   lives_widget_set_sensitive(mt->quit, TRUE);
17085   lives_widget_set_sensitive(mt->clear_ds, TRUE);
17086   lives_widget_set_sensitive(mt->open_menu, TRUE);
17087   lives_widget_set_sensitive(mt->show_quota, TRUE);
17088 #ifdef HAVE_WEBM
17089   lives_widget_set_sensitive(mt->open_loc_menu, TRUE);
17090 #endif
17091 #ifdef ENABLE_DVD_GRAB
17092   lives_widget_set_sensitive(mt->vcd_dvd_menu, TRUE);
17093 #endif
17094 #ifdef HAVE_LDVGRAB
17095   lives_widget_set_sensitive(mt->device_menu, TRUE);
17096 #endif
17097   lives_widget_set_sensitive(mt->recent_menu, TRUE);
17098   lives_widget_set_sensitive(mt->capture, TRUE);
17099   lives_widget_set_sensitive(mt->gens_submenu, TRUE);
17100   lives_widget_set_sensitive(mt->troubleshoot, TRUE);
17101   lives_widget_set_sensitive(mt->expl_missing, TRUE);
17102 
17103   lives_widget_set_sensitive(mainw->m_mutebutton, TRUE);
17104 
17105   lives_widget_set_sensitive(mt->load_set, !mainw->was_set);
17106 
17107   if (mt->undoable) lives_widget_set_sensitive(mt->undo, TRUE);
17108   if (mt->redoable) lives_widget_set_sensitive(mt->redo, TRUE);
17109   if (mt->selected_init_event) {
17110     lives_widget_set_sensitive(mt->fx_edit, TRUE);
17111     lives_widget_set_sensitive(mt->fx_delete, TRUE);
17112   }
17113 
17114   if (mt->checkbutton_avel_reverse) {
17115     lives_widget_set_sensitive(mt->checkbutton_avel_reverse, TRUE);
17116 
17117     if (mt->block_selected && (!mt->block_selected->start_anchored ||
17118                                !mt->block_selected->end_anchored) && !lives_toggle_button_get_active
17119         (LIVES_TOGGLE_BUTTON(mt->checkbutton_avel_reverse))) {
17120       lives_widget_set_sensitive(mt->spinbutton_avel, TRUE);
17121       lives_widget_set_sensitive(mt->avel_scale, TRUE);
17122     }
17123   }
17124 
17125   lives_widget_set_sensitive(mt->load_event_list, *mainw->set_name != 0);
17126   lives_widget_set_sensitive(mt->clipedit, TRUE);
17127   if (mt->file_selected > -1) {
17128     if (mainw->files[mt->file_selected]->frames > 0) lives_widget_set_sensitive(mt->insert, TRUE);
17129     if (mainw->files[mt->file_selected]->achans > 0 && mainw->files[mt->file_selected]->laudio_time > 0.)
17130       lives_widget_set_sensitive(mt->audio_insert, TRUE);
17131     lives_widget_set_sensitive(mt->save_set, !mainw->recording_recovered);
17132     lives_widget_set_sensitive(mt->close, TRUE);
17133     lives_widget_set_sensitive(mt->adjust_start_end, TRUE);
17134   }
17135 
17136   if (mt->video_draws &&
17137       mt->current_track > -1) eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
17138   else if (mt->audio_draws) eventbox = (LiVESWidget *)mt->audio_draws->data;
17139 
17140   if (eventbox) {
17141     lives_widget_set_sensitive(mt->jumpback, lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks") != NULL);
17142     lives_widget_set_sensitive(mt->jumpnext, lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks") != NULL);
17143   }
17144 
17145   if (mt->tl_marks) {
17146     lives_widget_set_sensitive(mt->mark_jumpback, TRUE);
17147     lives_widget_set_sensitive(mt->mark_jumpnext, TRUE);
17148   }
17149 
17150   lives_widget_set_sensitive(mt->change_vals, TRUE);
17151 
17152   if (mt->block_selected) {
17153     lives_widget_set_sensitive(mt->delblock, TRUE);
17154     if (mt->poly_state == POLY_IN_OUT && mt->block_selected->ordered) {
17155       weed_timecode_t offset_end = mt->block_selected->offset_start + (weed_timecode_t)(TICKS_PER_SECOND_DBL / mt->fps) +
17156                                    (get_event_timecode(mt->block_selected->end_event)
17157                                     - get_event_timecode(mt->block_selected->start_event));
17158       set_in_out_spin_ranges(mt, mt->block_selected->offset_start, offset_end);
17159     }
17160   } else if (mt->poly_state == POLY_IN_OUT) {
17161     int filenum = mt_file_from_clip(mt, mt->clip_selected);
17162     lives_signal_handler_block(mt->spinbutton_in, mt->spin_in_func);
17163     lives_signal_handler_block(mt->spinbutton_out, mt->spin_out_func);
17164     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_in), 1., mainw->files[filenum]->frames);
17165     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_out), 1., mainw->files[filenum]->frames);
17166 
17167     lives_signal_handler_unblock(mt->spinbutton_in, mt->spin_in_func);
17168     lives_signal_handler_unblock(mt->spinbutton_out, mt->spin_out_func);
17169   }
17170 
17171   if (mt->region_end > mt->region_start && mt->event_list && get_first_event(mt->event_list)) {
17172     if (mt->selected_tracks) {
17173       lives_widget_set_sensitive(mt->fx_region, TRUE);
17174       lives_widget_set_sensitive(mt->ins_gap_sel, TRUE);
17175       lives_widget_set_sensitive(mt->remove_gaps, TRUE);
17176       lives_widget_set_sensitive(mt->remove_first_gaps, TRUE);
17177       lives_widget_set_sensitive(mt->fx_region, TRUE);
17178       if (mt->selected_tracks && mt->region_end != mt->region_start) {
17179         switch (lives_list_length(mt->selected_tracks)) {
17180         case 1:
17181           lives_widget_set_sensitive(mt->fx_region_v, TRUE);
17182           if (cfile->achans == 0) lives_widget_set_sensitive(mt->fx_region_a, FALSE);
17183           break;
17184         case 2:
17185           lives_widget_set_sensitive(mt->fx_region_v, FALSE);
17186           lives_widget_set_sensitive(mt->fx_region_a, FALSE);
17187           if (!mt->opts.pertrack_audio)
17188             lives_widget_set_sensitive(mt->fx_region_2a, FALSE);
17189           break;
17190         default:
17191           break;
17192         }
17193       }
17194     }
17195     lives_widget_set_sensitive(mt->playsel, TRUE);
17196     lives_widget_set_sensitive(mt->ins_gap_cur, TRUE);
17197     lives_widget_set_sensitive(mt->view_sel_events, TRUE);
17198   }
17199 
17200   track_select(mt);
17201 
17202   mt->in_sensitise = FALSE;
17203 }
17204 
17205 
mt_swap_play_pause(lives_mt * mt,boolean put_pause)17206 void mt_swap_play_pause(lives_mt * mt, boolean put_pause) {
17207   LiVESWidget *tmp_img = NULL;
17208   static LiVESWidgetClosure *freeze_closure = NULL;
17209 
17210   if (!freeze_closure) freeze_closure = lives_cclosure_new(LIVES_GUI_CALLBACK(freeze_callback), NULL, NULL);
17211 
17212   if (put_pause) {
17213 #if GTK_CHECK_VERSION(2, 6, 0)
17214     tmp_img =
17215       lives_image_new_from_stock
17216       (LIVES_STOCK_MEDIA_PAUSE, lives_toolbar_get_icon_size(LIVES_TOOLBAR(mt->btoolbar2)));
17217 #endif
17218     lives_menu_item_set_text(mt->playall, _("_Pause"), TRUE);
17219     lives_widget_set_tooltip_text(mainw->m_playbutton, _("Pause (p)"));
17220     lives_widget_set_sensitive(mt->playall, TRUE);
17221     lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
17222     lives_signal_handlers_disconnect_by_func(mt->playall, LIVES_GUI_CALLBACK(on_playall_activate), NULL);
17223     lives_signal_sync_connect(LIVES_GUI_OBJECT(mt->playall), LIVES_WIDGET_ACTIVATE_SIGNAL,
17224                               LIVES_GUI_CALLBACK(on_playall_activate), NULL);
17225     lives_signal_handlers_disconnect_by_func(mainw->m_playbutton, LIVES_GUI_CALLBACK(on_playall_activate), NULL);
17226     lives_signal_sync_connect(LIVES_GUI_OBJECT(mainw->m_playbutton), LIVES_WIDGET_CLICKED_SIGNAL,
17227                               LIVES_GUI_CALLBACK(on_playall_activate), NULL);
17228     lives_accel_group_connect(LIVES_ACCEL_GROUP(mt->accel_group), LIVES_KEY_BackSpace,
17229                               (LiVESXModifierType)LIVES_CONTROL_MASK,
17230                               (LiVESAccelFlags)0, freeze_closure);
17231   } else {
17232     tmp_img = lives_image_new_from_stock(LIVES_STOCK_MEDIA_PLAY, lives_toolbar_get_icon_size(LIVES_TOOLBAR(mt->btoolbar2)));
17233     lives_menu_item_set_text(mt->playall, _("_Play from Timeline Position"), TRUE);
17234     lives_widget_set_tooltip_text(mainw->m_playbutton, _("Play all (p)"));
17235     lives_signal_handlers_disconnect_by_func(mt->playall, LIVES_GUI_CALLBACK(on_playall_activate), NULL);
17236     lives_signal_connect(LIVES_GUI_OBJECT(mt->playall), LIVES_WIDGET_ACTIVATE_SIGNAL,
17237                          LIVES_GUI_CALLBACK(on_playall_activate), NULL);
17238     lives_signal_handlers_disconnect_by_func(mainw->m_playbutton, LIVES_GUI_CALLBACK(on_playall_activate), NULL);
17239     lives_signal_connect(LIVES_GUI_OBJECT(mainw->m_playbutton), LIVES_WIDGET_CLICKED_SIGNAL,
17240                          LIVES_GUI_CALLBACK(on_playall_activate), NULL);
17241     lives_accel_group_disconnect(LIVES_ACCEL_GROUP(mt->accel_group), freeze_closure);
17242     freeze_closure = NULL;
17243   }
17244 
17245   if (tmp_img) lives_widget_show(tmp_img);
17246   lives_tool_button_set_icon_widget(LIVES_TOOL_BUTTON(mainw->m_playbutton), tmp_img);
17247 }
17248 
17249 
multitrack_preview_clicked(LiVESWidget * button,livespointer user_data)17250 void multitrack_preview_clicked(LiVESWidget * button, livespointer user_data) {
17251   //preview during rendering
17252   lives_mt *mt = (lives_mt *)user_data;
17253 
17254   if (!LIVES_IS_PLAYING) multitrack_playall(mt);
17255   else mainw->cancelled = CANCEL_NO_PROPOGATE;
17256 }
17257 
17258 
mt_prepare_for_playback(lives_mt * mt)17259 void mt_prepare_for_playback(lives_mt * mt) {
17260   // called from on_preview_clicked
17261 
17262   pb_loop_event = mt->pb_loop_event;
17263   pb_filter_map = mainw->filter_map; // keep a copy of this, in case we are rendering
17264   pb_afilter_map = mainw->afilter_map; // keep a copy of this, in case we are rendering
17265   pb_audio_needs_prerender = lives_widget_is_sensitive(mt->prerender_aud);
17266 
17267   mt_desensitise(mt);
17268 
17269   if (mt->mt_frame_preview) {
17270     // put blank back in preview window
17271     if (palette->style & STYLE_1) {
17272       if (mt->framedraw) lives_widget_set_bg_color(mt->fd_frame, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
17273     }
17274   }
17275 
17276   lives_widget_set_sensitive(mt->stop, TRUE);
17277   lives_widget_set_sensitive(mt->rewind, FALSE);
17278   lives_widget_set_sensitive(mainw->m_rewindbutton, FALSE);
17279   lives_widget_set_sensitive(mt->playall, TRUE);
17280   lives_widget_context_update();
17281 }
17282 
17283 
mt_post_playback(lives_mt * mt)17284 void mt_post_playback(lives_mt * mt) {
17285   // called from on_preview_clicked
17286   unhide_cursor(lives_widget_get_xwindow(mainw->playarea));
17287 
17288   lives_widget_show(mainw->playarea);
17289 
17290   if (mainw->cancelled != CANCEL_USER_PAUSED && !((mainw->cancelled == CANCEL_NONE ||
17291       mainw->cancelled == CANCEL_NO_MORE_PREVIEW) && mt->is_paused)) {
17292     lives_widget_set_sensitive(mt->stop, FALSE);
17293     mt->no_frame_update = FALSE;
17294 
17295     mt_tl_move(mt, mt->pb_unpaused_start_time);
17296 
17297     if (mt->opts.follow_playback) {
17298       double currtime = mt->ptr_time;
17299       if (currtime > mt->tl_max || currtime < mt->tl_min) {
17300         mt_zoom(mt, 1.);
17301       }
17302     }
17303   } else {
17304     double curtime;
17305     mt->is_paused = TRUE;
17306     if ((curtime = mt->ptr_time) > 0.) {
17307       lives_widget_set_sensitive(mt->rewind, TRUE);
17308       lives_widget_set_sensitive(mainw->m_rewindbutton, TRUE);
17309     }
17310     paint_lines(mt, curtime, TRUE, NULL);
17311     mt->no_frame_update = FALSE;
17312     mt_show_current_frame(mt, FALSE);
17313   }
17314 
17315   mainw->cancelled = CANCEL_NONE;
17316 
17317   if (!mt->is_rendering) {
17318     if (mt->poly_state == POLY_PARAMS) {
17319       if (mt->init_event) {
17320         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->node_spinbutton),
17321                                     mt->ptr_time - get_event_timecode(mt->init_event) / TICKS_PER_SECOND_DBL);
17322         lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
17323         lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
17324       }
17325     }
17326     if (mt->poly_state == POLY_FX_STACK) {
17327       polymorph(mt, POLY_FX_STACK);
17328     }
17329   }
17330 
17331   if (mt->ptr_time > 0.) {
17332     lives_widget_set_sensitive(mt->rewind, TRUE);
17333     lives_widget_set_sensitive(mainw->m_rewindbutton, TRUE);
17334   }
17335 
17336   mainw->filter_map = pb_filter_map;
17337   mainw->afilter_map = pb_afilter_map;
17338 
17339   if (mt->is_paused) mt->pb_loop_event = pb_loop_event;
17340   lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
17341   paint_lines(mt, mt->ptr_time, FALSE, NULL);
17342 }
17343 
17344 
multitrack_playall(lives_mt * mt)17345 void multitrack_playall(lives_mt * mt) {
17346   LiVESWidget *old_context_scroll = mt->context_scroll;
17347   boolean needs_idlefunc = FALSE;
17348 
17349   if (!CURRENT_CLIP_IS_VALID) return;
17350   mt->no_expose_frame = TRUE;
17351   mt->no_frame_update = TRUE;
17352 
17353   if (mt->idlefunc > 0) {
17354     lives_source_remove(mt->idlefunc);
17355     mt->idlefunc = 0;
17356     needs_idlefunc = TRUE;
17357   }
17358 
17359   if (mt->fx_base_box) lives_widget_set_sensitive(mt->fx_base_box, FALSE);
17360   lives_widget_set_sensitive(mt->quit, TRUE);
17361 
17362   if (mt->context_scroll) {
17363     lives_widget_object_ref(mt->context_scroll); // this allows us to get our old messages back
17364     lives_container_remove(LIVES_CONTAINER(mt->context_frame), mt->context_scroll);
17365     mt->context_scroll = NULL;
17366   }
17367 
17368   clear_context(mt);
17369 
17370   add_context_label(mt, _("Press 'm' during playback"));
17371   add_context_label(mt, _("to make a mark on the timeline"));
17372 
17373   add_context_label(mt, "\n\n");
17374 
17375   add_context_label(mt, _("Press 't' during playback"));
17376   add_context_label(mt, _("to toggle timecode overlay"));
17377 
17378   if (mt->opts.follow_playback) {
17379     double currtime = mt->ptr_time;
17380     if (currtime > mt->tl_max || currtime < mt->tl_min) {
17381       double page = mt->tl_max - mt->tl_min;
17382       mt->tl_min = currtime - page * .25;
17383       mt->tl_max = currtime + page * .75;
17384       mt_zoom(mt, -1.);
17385     }
17386   }
17387 
17388   if (needs_clear) {
17389     set_drawing_area_from_pixbuf(mainw->play_image, NULL, mainw->play_surface);
17390     needs_clear = FALSE;
17391   }
17392 
17393   if (mt->is_rendering) {
17394     // preview during rendering
17395     boolean had_audio = mt->has_audio_file;
17396     mt->pb_start_event = NULL;
17397     mt->has_audio_file = TRUE;
17398     main_thread_execute((lives_funcptr_t)on_preview_clicked, 0, NULL, "vv", mainw->proc_ptr->preview_button, NULL);
17399     //on_preview_clicked(LIVES_BUTTON(
17400     mt->has_audio_file = had_audio;
17401   } else {
17402     if (mt->event_list) {
17403       mainw->is_rendering = TRUE; // NOTE : mainw->is_rendering is not the same as mt->is_rendering !
17404       set_play_position(mt);
17405       if (mainw->cancelled != CANCEL_VID_END) {
17406         // otherwise jack transport set us out of range
17407 
17408         if (mt->playing_sel)
17409           mt->pb_loop_event = get_frame_event_at(mt->event_list,
17410                                                  q_gint64(mt->region_start * TICKS_PER_SECOND_DBL, mt->fps), NULL, TRUE);
17411         else if (mt->is_paused) mt->pb_loop_event = pb_loop_event;
17412 
17413         on_preview_clicked(NULL, LIVES_INT_TO_POINTER(1));
17414       }
17415 
17416       mainw->is_rendering = mainw->is_processing = FALSE;
17417     }
17418   }
17419 
17420   mt_swap_play_pause(mt, FALSE);
17421 
17422   if (mt->context_scroll)
17423     lives_container_remove(LIVES_CONTAINER(mt->context_frame), mt->context_scroll);
17424 
17425   mt->context_scroll = old_context_scroll;
17426   if (mt->context_scroll)
17427     lives_container_add(LIVES_CONTAINER(mt->context_frame), mt->context_scroll);
17428 
17429   if (prefs->mt_show_ctx) {
17430     lives_widget_show_all(mt->context_frame);
17431     lives_widget_set_sensitive(mt->context_frame, TRUE);
17432   }
17433 
17434   if (mt->context_scroll)
17435     lives_widget_object_unref(mt->context_scroll);
17436 
17437   if (!pb_audio_needs_prerender) lives_widget_set_sensitive(mt->prerender_aud, FALSE);
17438   if (mt->fx_base_box) lives_widget_set_sensitive(mt->fx_base_box, TRUE);
17439 
17440   if (!mt->is_rendering) {
17441     mt_sensitise(mt);
17442     if (needs_idlefunc) mt->idlefunc = mt_idle_add(mt);
17443   }
17444 
17445   mt->no_expose_frame = FALSE;
17446 }
17447 
17448 
multitrack_play_sel(LiVESMenuItem * menuitem,livespointer user_data)17449 void multitrack_play_sel(LiVESMenuItem * menuitem, livespointer user_data) {
17450   // get current pointer time; if it is outside the time region jump to start
17451   double ptr_time;
17452   lives_mt *mt = (lives_mt *)user_data;
17453 
17454   ptr_time = mt->ptr_time;
17455 
17456   if (ptr_time < mt->region_start || ptr_time >= mt->region_end) {
17457     mt->ptr_time = lives_ruler_set_value(LIVES_RULER(mt->timeline), mt->region_start);
17458   }
17459 
17460   // set loop start point to region start, and pb_start to current position
17461   // set loop end point to region end or tl end, whichever is soonest
17462   mt->playing_sel = TRUE;
17463 
17464   multitrack_playall(mt);
17465 
17466   // on return here, return the pointer to its original position, unless paused
17467   if (!mt->is_paused) {
17468     mt->playing_sel = FALSE;
17469   }
17470 }
17471 
17472 
multitrack_adj_start_end(LiVESMenuItem * menuitem,livespointer user_data)17473 void multitrack_adj_start_end(LiVESMenuItem * menuitem, livespointer user_data) {
17474   lives_mt *mt = (lives_mt *)user_data;
17475   unselect_all(mt);
17476   polymorph(mt, POLY_IN_OUT);
17477 }
17478 
17479 
multitrack_insert(LiVESMenuItem * menuitem,livespointer user_data)17480 boolean multitrack_insert(LiVESMenuItem * menuitem, livespointer user_data) {
17481   lives_mt *mt = (lives_mt *)user_data;
17482   lives_clip_t *sfile = mainw->files[mt->file_selected];
17483 
17484   double secs = mt->ptr_time;
17485 
17486   LiVESWidget *eventbox;
17487 
17488   weed_timecode_t ins_start = (sfile->start - 1.) / sfile->fps * TICKS_PER_SECOND_DBL;
17489   weed_timecode_t ins_end = (double)(sfile->end) / sfile->fps * TICKS_PER_SECOND_DBL;
17490 
17491   boolean did_backup = mt->did_backup;
17492   boolean needs_idlefunc = FALSE;
17493 
17494   track_rect *block;
17495 
17496   if (mt->current_track < 0) return multitrack_audio_insert(menuitem, user_data);
17497 
17498   if (sfile->frames == 0) return FALSE;
17499 
17500   if (mt->idlefunc > 0) {
17501     needs_idlefunc = TRUE;
17502     lives_source_remove(mt->idlefunc);
17503     mt->idlefunc = 0;
17504   }
17505 
17506   if (mt->context_time != -1. && mt->use_context) {
17507     secs = mt->context_time;
17508     mt->context_time = -1.;
17509     mt->use_context = FALSE;
17510   }
17511 
17512   eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, mt->current_track);
17513 
17514   if (mt->opts.ign_ins_sel) {
17515     // ignore selection limits
17516     ins_start = 0;
17517     ins_end = (double)(sfile->frames) / sfile->fps * TICKS_PER_SECOND_DBL;
17518   }
17519 
17520   if (mt->insert_start != -1) {
17521     // used if we move a block
17522     ins_start = mt->insert_start;
17523     ins_end = mt->insert_end;
17524   }
17525 
17526   if (mt->opts.insert_mode == INSERT_MODE_NORMAL) {
17527     // first check if there is space to insert the block to, otherwise we will abort the insert
17528     weed_plant_t *event = NULL;
17529     weed_timecode_t tc = 0, tcnow;
17530     weed_timecode_t tclen = ins_end - ins_start;
17531 
17532     while (tc <= tclen) {
17533       tcnow = q_gint64(tc + secs * TICKS_PER_SECOND_DBL, mt->fps);
17534       tc += TICKS_PER_SECOND_DBL / mt->fps;
17535       event = get_frame_event_at(mt->event_list, tcnow, event, TRUE);
17536       if (!event) break; // must be end of timeline
17537       // is video track, if we have a non-blank frame, abort
17538       if (get_frame_event_clip(event, mt->current_track) >= 0) {
17539         if (!did_backup) {
17540           if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
17541         }
17542         return FALSE;
17543       }
17544     }
17545   }
17546 
17547   mt_backup(mt, MT_UNDO_INSERT_BLOCK, 0);
17548 
17549   insert_frames(mt->file_selected, ins_start, ins_end, secs * TICKS_PER_SECOND, LIVES_DIRECTION_FORWARD, eventbox, mt, NULL);
17550 
17551   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
17552 
17553   if (block && (mt->opts.grav_mode == GRAV_MODE_LEFT || (block->next
17554                 && mt->opts.grav_mode == GRAV_MODE_RIGHT)) &&
17555       !(did_backup || mt->moving_block)) {
17556     double oldr_start = mt->region_start;
17557     double oldr_end = mt->region_end;
17558     LiVESList *tracks_sel;
17559     track_rect *selblock = NULL;
17560     if (mt->block_selected != block) selblock = mt->block_selected;
17561     tracks_sel = lives_list_copy(mt->selected_tracks);
17562     if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
17563     mt->selected_tracks = NULL;
17564     mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(mt->current_track));
17565 
17566     if (mt->opts.grav_mode == GRAV_MODE_LEFT) {
17567       if (block->prev) mt->region_start = get_event_timecode(block->prev->end_event) / TICKS_PER_SECOND_DBL;
17568       else mt->region_start = 0.;
17569       mt->region_end = get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL;
17570     } else {
17571       mt->region_start = get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL;
17572       mt->region_end = get_event_timecode(block->next->start_event) / TICKS_PER_SECOND_DBL;
17573     }
17574 
17575     remove_first_gaps(NULL, mt);
17576     lives_list_free(mt->selected_tracks);
17577     mt->selected_tracks = lives_list_copy(tracks_sel);
17578     if (tracks_sel) lives_list_free(tracks_sel);
17579     mt->region_start = oldr_start;
17580     mt->region_end = oldr_end;
17581     mt_sensitise(mt);
17582     if (selblock) mt->block_selected = selblock;
17583   }
17584 
17585   // get this again because it could have moved
17586   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
17587 
17588   if (!did_backup) {
17589     if (mt->avol_fx != -1 && block && !block->next && get_first_event(mt->event_list)) {
17590       apply_avol_filter(mt);
17591     }
17592   }
17593 
17594   if (!mt->moving_block && prefs->atrans_fx != -1) {
17595     // add the insert and autotrans as 2 separate undo events
17596     mt->did_backup = did_backup;
17597     mt_do_autotransition(mt, block);
17598     mt->did_backup = TRUE;
17599   }
17600 
17601   if (block && !resize_timeline(mt) && !did_backup) {
17602     lives_painter_surface_t *bgimage = (lives_painter_surface_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox),
17603                                        "bgimg");
17604     if (bgimage) {
17605       draw_block(mt, NULL, bgimage, block, 0, lives_widget_get_allocation_width(eventbox));
17606       lives_widget_queue_draw(eventbox);
17607     }
17608   }
17609 
17610   if (!did_backup && mt->framedraw && mt->current_rfx && mt->init_event &&
17611       mt->poly_state == POLY_PARAMS &&
17612       weed_plant_has_leaf(mt->init_event, WEED_LEAF_IN_TRACKS)) {
17613     weed_timecode_t tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) *
17614                                   TICKS_PER_SECOND_DBL + get_event_timecode(mt->init_event), mt->fps);
17615     get_track_index(mt, tc);
17616   }
17617 
17618   // expand the play preview as necessary
17619   reset_mt_play_sizes(mt);
17620 
17621   mt_tl_move_relative(mt, 0.);
17622 
17623   if (!did_backup) {
17624     mt->did_backup = FALSE;
17625     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
17626     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
17627   }
17628   return TRUE;
17629 }
17630 
17631 
multitrack_audio_insert(LiVESMenuItem * menuitem,livespointer user_data)17632 boolean multitrack_audio_insert(LiVESMenuItem * menuitem, livespointer user_data) {
17633   lives_mt *mt = (lives_mt *)user_data;
17634   lives_clip_t *sfile = mainw->files[mt->file_selected];
17635 
17636   double secs = mt->ptr_time;
17637   double tstart, tend;
17638 
17639   LiVESWidget *eventbox = (LiVESWidget *)mt->audio_draws->data;
17640 
17641   weed_timecode_t ins_start = q_gint64((sfile->start - 1.) / sfile->fps * TICKS_PER_SECOND_DBL, mt->fps);
17642   weed_timecode_t ins_end = q_gint64((double)sfile->end / sfile->fps * TICKS_PER_SECOND_DBL, mt->fps);
17643 
17644   boolean did_backup = mt->did_backup;
17645   boolean needs_idlefunc = FALSE;
17646 
17647   track_rect *block;
17648 
17649   char *tmp;
17650   char *istart, *iend;
17651 
17652   lives_direction_t dir;
17653 
17654   if (mt->current_track != -1 || sfile->achans == 0) return FALSE;
17655 
17656   if (mt->idlefunc > 0) {
17657     needs_idlefunc = TRUE;
17658     lives_source_remove(mt->idlefunc);
17659     mt->idlefunc = 0;
17660   }
17661 
17662   if (mt->context_time != -1. && mt->use_context) {
17663     secs = mt->context_time;
17664     mt->context_time = -1.;
17665     mt->use_context = FALSE;
17666   }
17667 
17668   if (sfile->frames == 0 || mt->opts.ign_ins_sel) {
17669     ins_start = 0;
17670     ins_end = q_gint64(sfile->laudio_time * TICKS_PER_SECOND_DBL, mt->fps);
17671   }
17672 
17673   if (ins_start > q_gint64(sfile->laudio_time * TICKS_PER_SECOND_DBL, mt->fps)) {
17674     if (!did_backup) {
17675       if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
17676     }
17677     return FALSE;
17678   }
17679 
17680   if (ins_end > q_gint64(sfile->laudio_time * TICKS_PER_SECOND_DBL, mt->fps)) {
17681     ins_end = q_gint64(sfile->laudio_time * TICKS_PER_SECOND_DBL, mt->fps);
17682   }
17683 
17684   if (mt->insert_start != -1) {
17685     ins_start = mt->insert_start;
17686     ins_end = mt->insert_end;
17687   }
17688 
17689   if (mt->insert_avel > 0.) dir = LIVES_DIRECTION_FORWARD;
17690   else dir = LIVES_DIRECTION_BACKWARD;
17691 
17692   if (mt->opts.insert_mode == INSERT_MODE_NORMAL) {
17693     // first check if there is space to insert the block to, otherwise we will abort the insert
17694     weed_plant_t *event = NULL;
17695     weed_timecode_t tc = 0, tcnow;
17696     weed_timecode_t tclen = ins_end - ins_start;
17697 
17698     //if (dir==LIVES_DIRECTION_BACKWARD) tc+=TICKS_PER_SECOND_DBL/mt->fps; // TODO - check if we need this
17699 
17700     while (tc <= tclen) {
17701       tcnow = q_gint64(tc + secs * TICKS_PER_SECOND_DBL, mt->fps);
17702       tc += TICKS_PER_SECOND_DBL / mt->fps;
17703       event = get_frame_event_at(mt->event_list, tcnow, event, TRUE);
17704       if (!event) break; // must be end of timeline
17705       // is audio track, see if we are in an audio block
17706       if ((tc == 0 && get_audio_block_start(mt->event_list, mt->current_track, tcnow, TRUE)) ||
17707           (get_audio_block_start(mt->event_list, mt->current_track, tcnow, FALSE))) {
17708         if (!did_backup) {
17709           if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
17710         }
17711         return FALSE;
17712       }
17713     }
17714   }
17715 
17716   mt_backup(mt, MT_UNDO_INSERT_AUDIO_BLOCK, 0);
17717 
17718   insert_audio(mt->file_selected, ins_start, ins_end, secs * TICKS_PER_SECOND, mt->insert_avel, dir, eventbox, mt, NULL);
17719 
17720   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
17721 
17722   if (block && (mt->opts.grav_mode == GRAV_MODE_LEFT ||
17723                 (mt->opts.grav_mode == GRAV_MODE_RIGHT && block->next))
17724       && !(did_backup || mt->moving_block)) {
17725     double oldr_start = mt->region_start;
17726     double oldr_end = mt->region_end;
17727     LiVESList *tracks_sel;
17728     track_rect *selblock = NULL;
17729     if (mt->block_selected != block) selblock = mt->block_selected;
17730     tracks_sel = lives_list_copy(mt->selected_tracks);
17731     if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
17732     mt->selected_tracks = NULL;
17733     mt->current_track = -1;
17734 
17735     if (mt->opts.grav_mode == GRAV_MODE_LEFT) {
17736       if (block->prev) mt->region_start = get_event_timecode(block->prev->end_event) / TICKS_PER_SECOND_DBL;
17737       else mt->region_start = 0.;
17738       mt->region_end = get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL;
17739     } else {
17740       mt->region_start = get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL;
17741       mt->region_end = get_event_timecode(block->next->start_event) / TICKS_PER_SECOND_DBL;
17742     }
17743 
17744     remove_first_gaps(NULL, mt);
17745     lives_list_free(mt->selected_tracks);
17746     mt->selected_tracks = lives_list_copy(tracks_sel);
17747     if (tracks_sel) lives_list_free(tracks_sel);
17748     mt->region_start = oldr_start;
17749     mt->region_end = oldr_end;
17750     if (selblock) mt->block_selected = selblock;
17751   }
17752 
17753   mt->did_backup = did_backup;
17754 
17755   tstart = QUANT_TICKS(ins_start);
17756   tend = QUANT_TICKS(ins_end);
17757   istart = time_to_string(QUANT_TIME(secs));
17758   iend = time_to_string(QUANT_TIME(secs + (ins_end - ins_start) / TICKS_PER_SECOND_DBL));
17759 
17760   d_print(_("Inserted audio %.4f to %.4f from clip %s into backing audio from time %s to time %s\n"),
17761           tstart, tend, (tmp = get_menu_name(sfile, FALSE)), istart, iend);
17762   lives_free(tmp);
17763 
17764   lives_free(istart);
17765   lives_free(iend);
17766 
17767   if (!resize_timeline(mt) && !did_backup) {
17768     lives_painter_surface_t *bgimage = (lives_painter_surface_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox),
17769                                        "bgimg");
17770     if (bgimage) {
17771       draw_block(mt, NULL, bgimage, block, 0, lives_widget_get_allocation_width(eventbox));
17772       lives_widget_queue_draw(eventbox);
17773     }
17774 
17775     if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "expanded"))) {
17776       LiVESWidget *xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "achan0");
17777       if (xeventbox) lives_widget_queue_draw(xeventbox);
17778       xeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "achan1");
17779       if (xeventbox) lives_widget_queue_draw(xeventbox);
17780     }
17781   }
17782 
17783   // get this again because it could have moved
17784   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
17785 
17786   if (!did_backup) {
17787     if (mt->avol_fx != -1 && block && !block->next && get_first_event(mt->event_list)) {
17788       apply_avol_filter(mt);
17789     }
17790   }
17791 
17792   mt_tl_move_relative(mt, 0.);
17793 
17794   if (!did_backup && mt->framedraw && mt->current_rfx && mt->init_event &&
17795       mt->poly_state == POLY_PARAMS && weed_plant_has_leaf(mt->init_event, WEED_LEAF_IN_TRACKS)) {
17796     weed_timecode_t tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) *
17797                                   TICKS_PER_SECOND_DBL + get_event_timecode(mt->init_event), mt->fps);
17798     get_track_index(mt, tc);
17799   }
17800 
17801   if (!did_backup) {
17802     mt->did_backup = FALSE;
17803     if (needs_idlefunc || (!did_backup && mt->auto_changed)) mt->idlefunc = mt_idle_add(mt);
17804     if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
17805   }
17806   return TRUE;
17807 }
17808 
17809 
insert_frames(int filenum,weed_timecode_t offset_start,weed_timecode_t offset_end,weed_timecode_t tc,lives_direction_t direction,LiVESWidget * eventbox,lives_mt * mt,track_rect * in_block)17810 void insert_frames(int filenum, weed_timecode_t offset_start, weed_timecode_t offset_end, weed_timecode_t tc,
17811                    lives_direction_t direction, LiVESWidget * eventbox, lives_mt * mt, track_rect * in_block) {
17812   // insert the selected frames from mainw->files[filenum] from source file filenum into mt->event_list starting at timeline timecode tc
17813   // if in_block is non-NULL, then we extend (existing) in_block with the new frames; otherwise we create a new block and insert it into eventbox
17814 
17815   // this is quite complex as the framerates of the sourcefile and the timeline might not match. Therefore we resample (in memory) our source file
17816   // After resampling, we insert resampled frames from offset_start (inclusive) to offset_end (non-inclusive) [forwards]
17817   // or from offset_start (non-inclusive) to offset_end (inclusive) if going backwards
17818 
17819   // if we are inserting in an existing block, we can only use this to extend the end (not shrink it)
17820 
17821   // we also optionally insert with audio
17822 
17823   // TODO - handle extend with audio
17824 
17825   // TODO - handle insert before, insert after
17826 
17827   // TODO - handle case where frames are overwritten
17828 
17829   lives_clip_t *sfile = mainw->files[filenum];
17830 
17831   weed_timecode_t last_tc = 0, offset_start_tc, start_tc, last_offset;
17832   weed_timecode_t orig_st = offset_start, orig_end = offset_end;
17833 
17834   int *clips = NULL, *rep_clips;
17835   int64_t *frames = NULL, *rep_frames;
17836   weed_plant_t *last_frame_event = NULL;
17837   weed_plant_t *event, *shortcut1 = NULL, *shortcut2 = NULL;
17838   track_rect *new_block = NULL;
17839 
17840   LiVESWidget *aeventbox = NULL;
17841 
17842   double aseek;
17843   double end_secs;
17844 
17845   boolean isfirst = TRUE;
17846 
17847   int64_t frame = (int64_t)((double)(offset_start / TICKS_PER_SECOND_DBL) * mt->fps + 1.4999);
17848   int track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number"));
17849   int numframes, i;
17850   int render_file = mainw->current_file;
17851 
17852   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
17853   lives_widget_context_update();
17854 
17855   mt_desensitise(mt);
17856 
17857   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "block_last", (livespointer)NULL);
17858   if ((aeventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "atrack")) != NULL)
17859     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(aeventbox), "block_last", (livespointer)NULL);
17860 
17861   last_offset = offset_start_tc = q_gint64(offset_start, mt->fps);
17862   offset_end = q_gint64(offset_end, mt->fps);
17863   start_tc = q_gint64(tc, mt->fps);
17864   if (direction == LIVES_DIRECTION_BACKWARD) tc -= TICKS_PER_SECOND_DBL / mt->fps;
17865   last_tc = q_gint64(tc, mt->fps);
17866 
17867   if (direction == LIVES_DIRECTION_FORWARD) {
17868     // fill in blank frames in a gap
17869     if (mt->event_list) last_frame_event = get_last_frame_event(mt->event_list);
17870     mt->event_list = add_blank_frames_up_to(mt->event_list, last_frame_event,
17871                                             q_gint64(start_tc - 1. / mt->fps, mt->fps), mt->fps);
17872   }
17873 
17874   mainw->current_file = filenum;
17875 
17876   if (cfile->fps != mt->fps && !cfile->event_list) {
17877     // resample clip to render fps
17878     cfile->undo1_dbl = mt->fps;
17879     on_resample_vid_ok(NULL, NULL);
17880   }
17881 
17882   mainw->current_file = render_file;
17883 
17884   while ((direction == LIVES_DIRECTION_FORWARD &&
17885           (offset_start = q_gint64(last_tc - start_tc + offset_start_tc, mt->fps)) < offset_end)
17886          || (direction == LIVES_DIRECTION_BACKWARD &&
17887              (offset_start = q_gint64(last_tc + offset_start_tc - start_tc, mt->fps)) >= offset_end)) {
17888     numframes = 0;
17889     clips = rep_clips = NULL;
17890     frames = rep_frames = NULL;
17891 
17892     if ((event = get_frame_event_at(mt->event_list, last_tc, shortcut1, TRUE))) {
17893       // TODO - memcheck
17894       clips = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &numframes);
17895       frames = weed_get_int64_array(event, WEED_LEAF_FRAMES, NULL);
17896       shortcut1 = event;
17897     } else if (direction == LIVES_DIRECTION_FORWARD && mt->event_list) {
17898       shortcut1 = get_last_event(mt->event_list);
17899     }
17900 
17901     if (numframes <= track) {
17902       // TODO - memcheck
17903       rep_clips = (int *)lives_malloc(track * sizint + sizint);
17904       rep_frames = (int64_t *)lives_malloc(track * 8 + 8);
17905 
17906       for (i = 0; i < track; i++) {
17907         if (i < numframes) {
17908           rep_clips[i] = clips[i];
17909           rep_frames[i] = frames[i];
17910         } else {
17911           rep_clips[i] = -1;
17912           rep_frames[i] = 0;
17913         }
17914       }
17915       numframes = track + 1;
17916     } else {
17917       if (mt->opts.insert_mode == INSERT_MODE_NORMAL && frames[track] > 0) {
17918         if (!in_block && new_block) {
17919           if (direction == LIVES_DIRECTION_FORWARD) {
17920             shortcut1 = get_prev_frame_event(shortcut1);
17921           }
17922         }
17923         lives_freep((void **)&clips);
17924         lives_freep((void **)&frames);
17925         break; // do not allow overwriting in this mode
17926       }
17927       rep_clips = clips;
17928       rep_frames = frames;
17929     }
17930 
17931     if (sfile->event_list) event = get_frame_event_at(sfile->event_list, offset_start, shortcut2, TRUE);
17932     if (sfile->event_list && !event) {
17933       if (rep_clips != clips && rep_clips) lives_free(rep_clips);
17934       if (rep_frames != frames && rep_frames) lives_free(rep_frames);
17935       lives_freep((void **)&clips);
17936       lives_freep((void **)&frames);
17937       break; // insert finished: ran out of frames in resampled clip
17938     }
17939     last_offset = offset_start;
17940     if (sfile->event_list) {
17941       // frames were resampled, get new frame at the source file timecode
17942       frame = weed_get_int64_value(event, WEED_LEAF_FRAMES, NULL);
17943       if (direction == LIVES_DIRECTION_FORWARD) shortcut2 = event;
17944       else shortcut2 = get_prev_frame_event(event); // TODO : this is not optimal for the first frame
17945     }
17946     rep_clips[track] = filenum;
17947     rep_frames[track] = frame;
17948 
17949     // TODO - memcheck
17950     if (!mt->event_list) {
17951       mainw->event_list = lives_event_list_new(NULL, NULL);
17952     }
17953 
17954     mt->event_list = insert_frame_event_at(mt->event_list, last_tc, numframes, rep_clips, rep_frames, &shortcut1);
17955 
17956     if (rep_clips != clips && rep_clips) lives_free(rep_clips);
17957     if (rep_frames != frames && rep_frames) lives_free(rep_frames);
17958 
17959     if (isfirst) {
17960       // TODO - memcheck
17961       if (!in_block) {
17962         new_block = add_block_start_point(eventbox, last_tc, filenum, offset_start, shortcut1, TRUE);
17963         if (aeventbox) {
17964           if (cfile->achans > 0 && sfile->achans > 0 && mt->opts.insert_audio) {
17965             // insert audio start or end
17966             if (direction == LIVES_DIRECTION_FORWARD) {
17967               aseek = ((double)frame - 1.) / sfile->fps;
17968 
17969               insert_audio_event_at(shortcut1, track, filenum, aseek, 1.);
17970               add_block_start_point(aeventbox, last_tc, filenum, offset_start, shortcut1, TRUE);
17971             } else {
17972               weed_plant_t *nframe;
17973               if (!(nframe = get_next_frame_event(shortcut1))) {
17974                 mt->event_list = insert_blank_frame_event_at(mt->event_list,
17975                                  q_gint64(last_tc + TICKS_PER_SECOND_DBL / mt->fps, mt->fps), &shortcut1);
17976                 nframe = shortcut1;
17977               }
17978               insert_audio_event_at(nframe, track, filenum, 0., 0.);
17979 	      // *INDENT-OFF*
17980             }}}
17981         isfirst = FALSE;
17982       }}
17983     // *INDENT-ON*
17984 
17985     lives_freep((void **)&clips);
17986     lives_freep((void **)&frames);
17987 
17988     if (direction == LIVES_DIRECTION_FORWARD) {
17989       last_tc += TICKS_PER_SECOND_DBL / mt->fps + 1;
17990       last_tc = q_gint64(last_tc, mt->fps);
17991     } else {
17992       if (last_tc < TICKS_PER_SECOND_DBL / mt->fps) {
17993         break;
17994       }
17995       last_tc -= TICKS_PER_SECOND_DBL / mt->fps;
17996       last_tc = q_gint64(last_tc, mt->fps);
17997     }
17998     if (!sfile->event_list) if ((direction == LIVES_DIRECTION_FORWARD && (++frame > (int64_t)sfile->frames)) ||
17999                                   (direction == LIVES_DIRECTION_BACKWARD && (--frame < 1))) {
18000         break;
18001       }
18002   }
18003 
18004   if (!isfirst || direction == LIVES_DIRECTION_BACKWARD) {
18005     if (direction == LIVES_DIRECTION_FORWARD) {
18006       if (in_block) lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "block_last", (livespointer)in_block);
18007       add_block_end_point(eventbox, shortcut1);
18008 
18009       if (cfile->achans > 0 && sfile->achans > 0 && mt->opts.insert_audio && mt->opts.pertrack_audio) {
18010         weed_plant_t *shortcut2 = get_next_frame_event(shortcut1);
18011         if (!shortcut2) {
18012           mt->event_list = insert_blank_frame_event_at(mt->event_list, last_tc, &shortcut1);
18013         } else shortcut1 = shortcut2;
18014         insert_audio_event_at(shortcut1, track, filenum, 0., 0.);
18015         add_block_end_point(aeventbox, shortcut1);
18016       }
18017     } else if (in_block) {
18018       in_block->offset_start = last_offset;
18019       in_block->start_event = shortcut1;
18020       if (cfile->achans > 0 && sfile->achans > 0 && mt->opts.insert_audio && mt->opts.pertrack_audio) {
18021         weed_plant_t *shortcut2 = get_next_frame_event(shortcut1);
18022         if (!shortcut2) {
18023           mt->event_list = insert_blank_frame_event_at(mt->event_list, last_tc, &shortcut1);
18024         } else shortcut1 = shortcut2;
18025       }
18026     }
18027   }
18028 
18029   mt->last_direction = direction;
18030 
18031   if (mt->event_list) {
18032     weed_set_double_value(mt->event_list, WEED_LEAF_FPS, mainw->files[render_file]->fps);
18033   }
18034 
18035   if (!in_block) {
18036     char *tmp, *tmp1, *istart, *iend;
18037 
18038     istart = time_to_string(QUANT_TICKS((orig_st + start_tc)));
18039     iend = time_to_string(QUANT_TICKS((orig_end + start_tc)));
18040 
18041     d_print(_("Inserted frames %d to %d from clip %s into track %s from time %s to time %s\n"),
18042             sfile->start, sfile->end, (tmp1 = get_menu_name(sfile, FALSE)),
18043             (tmp = get_track_name(mt, mt->current_track, FALSE)), istart, iend);
18044     lives_free(tmp);
18045     lives_free(tmp1);
18046     lives_free(istart);
18047     lives_free(iend);
18048   }
18049 
18050   end_secs = event_list_get_end_secs(mt->event_list);
18051   if (end_secs > mt->end_secs) {
18052     set_timeline_end_secs(mt, end_secs);
18053   }
18054   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
18055   mt_sensitise(mt);
18056 }
18057 
18058 
insert_audio(int filenum,weed_timecode_t offset_start,weed_timecode_t offset_end,weed_timecode_t tc,double avel,lives_direction_t direction,LiVESWidget * eventbox,lives_mt * mt,track_rect * in_block)18059 void insert_audio(int filenum, weed_timecode_t offset_start, weed_timecode_t offset_end, weed_timecode_t tc,
18060                   double avel, lives_direction_t direction, LiVESWidget * eventbox,
18061                   lives_mt * mt, track_rect * in_block) {
18062   // insert the selected audio from mainw->files[filenum] from source file filenum into mt->event_list starting at timeline timecode tc
18063   // if in_block is non-NULL, then we extend (existing) in_block with the new frames; otherwise we create a new block and insert it into eventbox
18064   weed_timecode_t start_tc = q_gint64(tc, mt->fps);
18065   weed_timecode_t end_tc = q_gint64(start_tc + offset_end - offset_start, mt->fps);
18066   weed_plant_t *last_frame_event;
18067   track_rect *block;
18068   weed_plant_t *shortcut = NULL;
18069   weed_plant_t *frame_event;
18070 
18071   double end_secs;
18072 
18073   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(eventbox), "block_last", (livespointer)NULL);
18074 
18075   if (direction == LIVES_DIRECTION_BACKWARD) {
18076     weed_timecode_t tmp_tc = offset_end;
18077     offset_end = offset_start;
18078     offset_start = tmp_tc;
18079   }
18080 
18081   // if already block at tc, return
18082   if ((block = get_block_from_time((LiVESWidget *)mt->audio_draws->data, start_tc / TICKS_PER_SECOND_DBL, mt)) != NULL &&
18083       get_event_timecode(block->end_event) > start_tc) return;
18084 
18085   // insert blank frames up to end_tc
18086   last_frame_event = get_last_frame_event(mt->event_list);
18087   mt->event_list = add_blank_frames_up_to(mt->event_list, last_frame_event, end_tc, mt->fps);
18088 
18089   block = get_block_before((LiVESWidget *)mt->audio_draws->data, start_tc / TICKS_PER_SECOND_DBL, TRUE);
18090   if (block) shortcut = block->end_event;
18091 
18092   block = get_block_after((LiVESWidget *)mt->audio_draws->data, start_tc / TICKS_PER_SECOND_DBL, FALSE);
18093 
18094   // insert audio seek at tc
18095   frame_event = get_frame_event_at(mt->event_list, start_tc, shortcut, TRUE);
18096 
18097   if (direction == LIVES_DIRECTION_FORWARD) {
18098     insert_audio_event_at(frame_event, -1, filenum, offset_start / TICKS_PER_SECOND_DBL, avel);
18099   } else {
18100     insert_audio_event_at(frame_event, -1, filenum, offset_end / TICKS_PER_SECOND_DBL, avel);
18101     offset_start = offset_start - offset_end + offset_end * mt->insert_avel;
18102   }
18103 
18104   add_block_start_point((LiVESWidget *)mt->audio_draws->data, start_tc, filenum, offset_start, frame_event, TRUE);
18105 
18106   if (!block || get_event_timecode(block->start_event) > end_tc) {
18107     // if no blocks after end point, insert audio off at end point
18108     frame_event = get_frame_event_at(mt->event_list, end_tc, frame_event, TRUE);
18109     insert_audio_event_at(frame_event, -1, filenum, 0., 0.);
18110     add_block_end_point((LiVESWidget *)mt->audio_draws->data, frame_event);
18111   } else add_block_end_point((LiVESWidget *)mt->audio_draws->data, block->start_event);
18112 
18113   end_secs = event_list_get_end_secs(mt->event_list);
18114   if (end_secs > mt->end_secs) {
18115     set_timeline_end_secs(mt, end_secs);
18116   }
18117 
18118   mt_sensitise(mt);
18119 }
18120 
18121 
multitrack_view_events(LiVESMenuItem * menuitem,livespointer user_data)18122 void multitrack_view_events(LiVESMenuItem * menuitem, livespointer user_data) {
18123   lives_mt *mt = (lives_mt *)user_data;
18124   LiVESWidget *elist_dialog;
18125   if ((prefs->event_window_show_frame_events && count_events(mt->event_list, TRUE, 0, 0) > 1000) ||
18126       (!prefs->event_window_show_frame_events && ((count_events(mt->event_list, TRUE, 0, 0)
18127           - count_events(mt->event_list, FALSE, 0, 0)) > 1000)))
18128     if (!do_event_list_warning()) return;
18129   mt_desensitise(mt);
18130   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
18131   elist_dialog = create_event_list_dialog(mt->event_list, 0, 0);
18132   lives_dialog_run(LIVES_DIALOG(elist_dialog));
18133   mt_sensitise(mt);
18134 }
18135 
18136 
multitrack_view_sel_events(LiVESMenuItem * menuitem,livespointer user_data)18137 void multitrack_view_sel_events(LiVESMenuItem * menuitem, livespointer user_data) {
18138   lives_mt *mt = (lives_mt *)user_data;
18139   LiVESWidget *elist_dialog;
18140 
18141   weed_timecode_t tc_start = q_gint64(mt->region_start * TICKS_PER_SECOND, mt->fps);
18142   weed_timecode_t tc_end = q_gint64(mt->region_end * TICKS_PER_SECOND, mt->fps);
18143 
18144   if ((prefs->event_window_show_frame_events && count_events(mt->event_list, TRUE, tc_start, tc_end) > 1000) ||
18145       (!prefs->event_window_show_frame_events && ((count_events(mt->event_list, TRUE, tc_start, tc_end)
18146           - count_events(mt->event_list, FALSE, tc_start, tc_end)) > 1000)))
18147     if (!do_event_list_warning()) return;
18148   mt_desensitise(mt);
18149   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
18150   elist_dialog = create_event_list_dialog(mt->event_list, tc_start, tc_end);
18151   mt_sensitise(mt);
18152   lives_dialog_run(LIVES_DIALOG(elist_dialog));
18153 }
18154 
18155 
18156 ////////////////////////////////////////////////////////
18157 // region functions
18158 
draw_region(lives_mt * mt)18159 void draw_region(lives_mt * mt) {
18160   lives_painter_t *cr;
18161 
18162   double start, end;
18163 
18164   if (mt->tl_reg_surf) lives_painter_surface_destroy(mt->tl_reg_surf);
18165   mt->tl_reg_surf = lives_widget_create_painter_surface(mt->timeline_reg);
18166 
18167   if (mt->region_start == mt->region_end) {
18168     cr = lives_painter_create_from_surface(mt->tl_reg_surf);
18169 
18170     if (palette->style & STYLE_3) {
18171       lives_painter_set_source_rgb_from_lives_widget_color(cr, &palette->menu_and_bars);
18172     } else {
18173       lives_painter_set_source_rgb_from_lives_widget_color(cr, &palette->white);
18174     }
18175 
18176     lives_painter_rectangle(cr, 0, 0,
18177                             lives_widget_get_allocation_width(mt->timeline_reg),
18178                             lives_widget_get_allocation_height(mt->timeline_reg));
18179     lives_painter_fill(cr);
18180     lives_painter_destroy(cr);
18181   }
18182 
18183   if (mt->region_start < mt->region_end) {
18184     start = mt->region_start;
18185     end = mt->region_end;
18186   } else {
18187     start = mt->region_end;
18188     end = mt->region_start;
18189   }
18190 
18191   if (mt->region_start == mt->region_end) {
18192     lives_widget_set_sensitive(mt->rs_to_tc, FALSE);
18193     lives_widget_set_sensitive(mt->re_to_tc, FALSE);
18194   } else {
18195     lives_widget_set_sensitive(mt->rs_to_tc, TRUE);
18196     lives_widget_set_sensitive(mt->re_to_tc, TRUE);
18197   }
18198 
18199   cr = lives_painter_create_from_surface(mt->tl_reg_surf);
18200 
18201   if (palette->style & STYLE_3) {
18202     lives_painter_set_source_rgb_from_lives_widget_color(cr, &palette->menu_and_bars);
18203   } else {
18204     lives_painter_set_source_rgb_from_lives_widget_color(cr, &palette->white);
18205   }
18206 
18207   lives_painter_rectangle(cr, 0, 0,
18208                           lives_widget_get_allocation_width(mt->timeline_reg),
18209                           lives_widget_get_allocation_height(mt->timeline_reg));
18210   lives_painter_fill(cr);
18211 
18212   lives_painter_set_source_rgb_from_lives_rgba(cr, &palette->mt_timeline_reg);
18213   lives_painter_rectangle(cr, (start - mt->tl_min)*lives_widget_get_allocation_width(mt->timeline) / (mt->tl_max - mt->tl_min),
18214                           0,
18215                           (end - start)*lives_widget_get_allocation_width(mt->timeline) / (mt->tl_max - mt->tl_min),
18216                           lives_widget_get_allocation_height(mt->timeline_reg) - 2);
18217   lives_painter_fill(cr);
18218   lives_painter_destroy(cr);
18219   lives_widget_queue_draw(mt->timeline_reg);
18220 }
18221 
18222 
EXPOSE_FN_DECL(expose_timeline_reg_event,timeline,user_data)18223 static EXPOSE_FN_DECL(expose_timeline_reg_event, timeline, user_data) {
18224   lives_mt *mt = (lives_mt *)user_data;
18225   LiVESList *tl_marks = mt->tl_marks;
18226 
18227   double time;
18228 
18229   int ebwidth;
18230   int offset;
18231 
18232   if (mt->no_expose) return TRUE;
18233   if (event && event->count > 0) return FALSE;
18234   if (LIVES_IS_PLAYING || mt->is_rendering) return FALSE;
18235   draw_region(mt);
18236 
18237   if (event) cairo = lives_painter_create_from_surface(mt->tl_surf);
18238 
18239   lives_painter_set_source_rgb_from_lives_rgba(cairo, &palette->mt_mark);
18240 
18241   while (tl_marks) {
18242     time = strtod((char *)tl_marks->data, NULL);
18243     ebwidth = lives_widget_get_allocation_width(mt->timeline);
18244     offset = (time - mt->tl_min) / (mt->tl_max - mt->tl_min) * (double)ebwidth;
18245 
18246     lives_painter_move_to(cairo, offset, 1);
18247     lives_painter_line_to(cairo, offset, lives_widget_get_allocation_height(mt->timeline_reg) - 2);
18248     lives_painter_stroke(cairo);
18249 
18250     tl_marks = tl_marks->next;
18251   }
18252 
18253   if (event) lives_painter_destroy(cairo);
18254 
18255   paint_lines(mt, mt->ptr_time, FALSE, NULL);
18256 
18257   return TRUE;
18258 }
18259 EXPOSE_FN_END
18260 
18261 
draw_soundwave(LiVESWidget * ebox,lives_painter_surface_t * surf,int chnum,lives_mt * mt)18262 static void draw_soundwave(LiVESWidget * ebox, lives_painter_surface_t *surf, int chnum, lives_mt * mt) {
18263   weed_plant_t *event;
18264   weed_timecode_t tc;
18265 
18266   lives_painter_t *cr = lives_painter_create_from_surface(surf);
18267 
18268   LiVESWidget *eventbox = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(ebox), "owner");
18269 
18270   track_rect *block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
18271 
18272   char *filename;
18273 
18274   double offset_startd, offset_endd; // time values
18275   double tl_span = mt->tl_max - mt->tl_min;
18276   double secs;
18277   double ypos;
18278   double seek, vel;
18279 
18280   int offset_start, offset_end; // pixel values
18281   int fnum;
18282   int width = lives_widget_get_allocation_width(ebox);
18283   int track = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "layer_number"));
18284 
18285   aofile = -1;
18286   afd = -1;
18287 
18288   THREADVAR(read_failed) = FALSE;
18289 
18290   while (block) {
18291     event = block->start_event;
18292     tc = get_event_timecode(event);
18293 
18294     offset_startd = tc / TICKS_PER_SECOND_DBL;
18295     if (offset_startd > mt->tl_max) {
18296       if (afd != -1) lives_close_buffered(afd);
18297       return;
18298     }
18299 
18300     offset_start = (int)((offset_startd - mt->tl_min) / tl_span * width + .5);
18301     if (offset_start < 0) offset_start = 0;
18302 
18303     offset_endd = get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL; //+1./cfile->fps;
18304     if (offset_endd < mt->tl_min) {
18305       block = block->next;
18306       continue;
18307     }
18308     if (offset_endd > mt->tl_max) offset_endd = mt->tl_max;
18309     offset_end = (offset_endd - mt->tl_min) / tl_span * width;
18310 
18311     fnum = get_audio_frame_clip(block->start_event, track);
18312     seek = get_audio_frame_seek(block->start_event, track);
18313     vel = get_audio_frame_vel(block->start_event, track);
18314 
18315     lives_painter_set_source_rgb(cr, 1., 1., 1.);
18316     lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(ebox) - 1);
18317     lives_painter_fill(cr);
18318 
18319     lives_painter_set_source_rgb(cr, 0., 0., 0.);
18320     lives_painter_set_line_width(cr, 1.);
18321     lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(ebox) - 1);
18322     lives_painter_stroke(cr);
18323 
18324     lives_painter_set_source_rgb(cr, 0.5, 0.5, 0.5);
18325 
18326     // open audio file here
18327 
18328     if (fnum != aofile) {
18329       // does not make sense to use buffer reads, as we may read very sparsely from the file
18330       if (afd != -1) close(afd);
18331       filename = lives_get_audio_file_name(fnum);
18332       afd = lives_open_buffered_rdonly(filename);
18333       lives_free(filename);
18334       aofile = fnum;
18335     }
18336 
18337     for (int i = offset_start; i <= offset_end; i++) {
18338       secs = (double)i / (double)width * tl_span + mt->tl_min;
18339       secs -= offset_startd;
18340       secs = secs * vel + seek;
18341       if (secs >= (chnum == 0 ? mainw->files[fnum]->laudio_time : mainw->files[fnum]->raudio_time)) break;
18342 
18343       // seek and read
18344       if (afd == -1) {
18345         THREADVAR(read_failed) = -2;
18346         return;
18347       }
18348       ypos = get_float_audio_val_at_time(fnum, afd, secs, chnum, cfile->achans) * .5;
18349 
18350       lives_painter_move_to(cr, i, (float)lives_widget_get_allocation_height(ebox) / 2.);
18351       lives_painter_line_to(cr, i, (.5 - ypos) * (float)lives_widget_get_allocation_height(ebox));
18352       lives_painter_stroke(cr);
18353     }
18354     block = block->next;
18355 
18356     if (THREADVAR(read_failed)) {
18357       filename = lives_get_audio_file_name(fnum);
18358       do_read_failed_error_s(filename, NULL);
18359       lives_free(filename);
18360     }
18361   }
18362 
18363   lives_painter_destroy(cr);
18364 
18365   if (afd != -1) lives_close_buffered(afd);
18366 }
18367 
18368 
EXPOSE_FN_DECL(mt_expose_audtrack_event,ebox,user_data)18369 static EXPOSE_FN_DECL(mt_expose_audtrack_event, ebox, user_data) {
18370   lives_mt *mt = (lives_mt *)user_data;
18371 
18372   lives_painter_surface_t *bgimage;
18373 
18374   int startx, starty, width, height;
18375   int hidden;
18376   int channum;
18377 
18378   if (mt->no_expose) return TRUE;
18379 
18380   if (event) {
18381     if (event->count > 0) {
18382       return TRUE;
18383     }
18384     startx = event->area.x;
18385     starty = event->area.y;
18386     width = event->area.width;
18387     height = event->area.height;
18388   } else {
18389     startx = starty = 0;
18390     width = lives_widget_get_allocation_width(ebox);
18391     height = lives_widget_get_allocation_height(ebox);
18392   }
18393 
18394   if (width == 0) return FALSE;
18395 
18396   hidden = (int)LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(ebox), HIDDEN_KEY));
18397   if (hidden != 0) {
18398     return FALSE;
18399   }
18400 
18401   if (width > lives_widget_get_allocation_width(ebox) - startx) width = lives_widget_get_allocation_width(ebox) - startx;
18402 
18403 #if !GTK_CHECK_VERSION(3, 22, 0)
18404   if (event) cairo = lives_painter_create_from_widget(ebox);
18405 #endif
18406 
18407   bgimage = (lives_painter_surface_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(ebox), "bgimg");
18408 
18409   if (LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(ebox), "drawn"))) {
18410     if (bgimage) {
18411       lives_painter_set_source_surface(cairo, bgimage, startx, starty);
18412       lives_painter_rectangle(cairo, startx, starty, width, height);
18413       lives_painter_fill(cairo);
18414       if (event) lives_painter_destroy(cairo);
18415       return TRUE;
18416     }
18417   }
18418 
18419   if (event) {
18420     lives_painter_destroy(cairo);
18421     width = lives_widget_get_allocation_width(ebox);
18422     height = lives_widget_get_allocation_height(ebox);
18423   }
18424 
18425   if (bgimage) lives_painter_surface_destroy(bgimage);
18426 
18427   bgimage = lives_widget_create_painter_surface(ebox);
18428 
18429   if (palette->style & STYLE_1) {
18430     lives_painter_t *crx = lives_painter_create_from_surface(bgimage);
18431     lives_painter_set_source_rgb_from_lives_rgba(crx, &palette->mt_evbox);
18432     lives_painter_rectangle(crx, 0., 0., width, height);
18433     lives_painter_fill(crx);
18434     lives_painter_paint(crx);
18435     lives_painter_destroy(crx);
18436   } else clear_widget_bg(ebox, bgimage);
18437 
18438   channum = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(ebox), "channel"));
18439 
18440   if (bgimage) {
18441     draw_soundwave(ebox, bgimage, channum, mt);
18442     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ebox), "drawn", LIVES_INT_TO_POINTER(TRUE));
18443     lives_widget_queue_draw(ebox);
18444   } else if (bgimage) {
18445     lives_painter_surface_destroy(bgimage);
18446     bgimage = NULL;
18447   }
18448 
18449   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(ebox), "bgimg", bgimage);
18450 
18451   return TRUE;
18452 }
18453 EXPOSE_FN_END
18454 
18455 
18456 ////////////////////////////////////////////////////
18457 
18458 // functions for moving and clicking on the timeline
18459 
on_timeline_update(LiVESWidget * widget,LiVESXEventMotion * event,livespointer user_data)18460 boolean on_timeline_update(LiVESWidget * widget, LiVESXEventMotion * event, livespointer user_data) {
18461   lives_mt *mt = (lives_mt *)user_data;
18462   int x;
18463   double pos;
18464 
18465   if (LIVES_IS_PLAYING) return TRUE;
18466 
18467   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
18468                            widget, &x, NULL);
18469   pos = get_time_from_x(mt, x);
18470 
18471   if (!mt->region_updating) {
18472     if (mt->tl_mouse) {
18473       mt->fm_edit_event = NULL;
18474       mt_tl_move(mt, pos);
18475     }
18476     return TRUE;
18477   }
18478 
18479   if (!mt->sel_locked) {
18480     if (pos > mt->region_init) {
18481       mt->region_start = mt->region_init;
18482       mt->region_end = pos;
18483     } else {
18484       mt->region_start = pos;
18485       mt->region_end = mt->region_init;
18486     }
18487   }
18488 
18489   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_start), mt->region_start);
18490   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_end), mt->region_end);
18491 
18492   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT && mt->tl_selecting && event) mouse_select_move(widget, event, mt);
18493 
18494   return TRUE;
18495 }
18496 
18497 
all_present(weed_plant_t * event,LiVESList * sel)18498 boolean all_present(weed_plant_t *event, LiVESList * sel) {
18499   int *clips;
18500   int64_t *frames;
18501   int numclips, layer;
18502 
18503   // see if we have an actual clip/frame for each layer in sel
18504   if (!event || !sel) return FALSE;
18505 
18506   clips = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &numclips);
18507 
18508   if (!numclips) return FALSE;
18509 
18510   frames = weed_get_int64_array(event, WEED_LEAF_FRAMES, NULL);
18511 
18512   while (sel) {
18513     layer = LIVES_POINTER_TO_INT(sel->data);
18514     if (layer >= numclips || clips[layer] < 1 || frames[layer] < 1) {
18515       lives_free(clips);
18516       lives_free(frames);
18517       return FALSE;
18518     }
18519     sel = sel->next;
18520   }
18521   lives_free(clips);
18522   lives_free(frames);
18523   return TRUE;
18524 }
18525 
18526 
get_region_overlap(lives_mt * mt)18527 void get_region_overlap(lives_mt * mt) {
18528   // get region which overlaps all selected tracks
18529   weed_plant_t *event;
18530   weed_timecode_t tc;
18531 
18532   if (!mt->selected_tracks || !mt->event_list) {
18533     mt->region_start = mt->region_end = 0.;
18534     return;
18535   }
18536 
18537   tc = q_gint64(mt->region_start * TICKS_PER_SECOND, mt->fps);
18538   event = get_frame_event_at(mt->event_list, tc, NULL, TRUE);
18539 
18540   while (all_present(event, mt->selected_tracks)) {
18541     // move start to left
18542     event = get_prev_frame_event(event);
18543   }
18544 
18545   if (!event) {
18546     event = get_first_event(mt->event_list);
18547     if (!WEED_EVENT_IS_FRAME(event)) event = get_next_frame_event(event);
18548   }
18549 
18550   while (event && !all_present(event, mt->selected_tracks)) {
18551     event = get_next_frame_event(event);
18552   }
18553 
18554   if (!event) mt->region_start = 0.;
18555   else mt->region_start = get_event_timecode(event) / TICKS_PER_SECOND_DBL;
18556 
18557   tc = q_gint64(mt->region_end * TICKS_PER_SECOND, mt->fps);
18558   event = get_frame_event_at(mt->event_list, tc, NULL, TRUE);
18559 
18560   while (all_present(event, mt->selected_tracks)) {
18561     // move end to right
18562     event = get_next_frame_event(event);
18563   }
18564 
18565   if (!event) {
18566     event = get_last_event(mt->event_list);
18567     if (!WEED_EVENT_IS_FRAME(event)) event = get_prev_frame_event(event);
18568   }
18569 
18570   while (event && !all_present(event, mt->selected_tracks)) {
18571     event = get_prev_frame_event(event);
18572   }
18573 
18574   if (!event) mt->region_end = 0.;
18575   mt->region_end = get_event_timecode(event) / TICKS_PER_SECOND_DBL + 1. / mt->fps;
18576 
18577   if (mt->event_list && get_first_event(mt->event_list)) {
18578     lives_widget_set_sensitive(mt->view_sel_events, mt->region_start != mt->region_end);
18579   }
18580 }
18581 
18582 
do_sel_context(lives_mt * mt)18583 void do_sel_context(lives_mt * mt) {
18584   char *msg;
18585   if (mt->region_start == mt->region_end || mt->did_backup) return;
18586   clear_context(mt);
18587   msg = lives_strdup_printf(_("Time region %.3f to %.3f\nselected.\n"), mt->region_start, mt->region_end);
18588   add_context_label(mt, msg);
18589   lives_free(msg);
18590   if (!mt->selected_tracks) {
18591     msg = lives_strdup_printf(_("select one or more tracks\nto create a region.\n"), lives_list_length(mt->selected_tracks));
18592   } else {
18593     int llen = lives_list_length(mt->selected_tracks);
18594     msg = lives_strdup_printf(P_("%d video track selected.\n", "%d video tracks selected.\n", llen),
18595                               llen);
18596   }
18597   add_context_label(mt, msg);
18598   add_context_label(mt, _("Double click on timeline\nto deselect time region."));
18599   lives_free(msg);
18600 }
18601 
18602 
do_fx_list_context(lives_mt * mt,int fxcount)18603 void do_fx_list_context(lives_mt * mt, int fxcount) {
18604   clear_context(mt);
18605   add_context_label(mt, (_("Single click on an effect\nto select it.")));
18606   add_context_label(mt, (_("Double click on an effect\nto edit it.")));
18607   add_context_label(mt, (_("Right click on an effect\nfor context menu.\n")));
18608   add_context_label(mt, (_("\n\nEffects are applied in order from top to bottom.\n")));
18609   if (fxcount > 1) {
18610     add_context_label(mt, (_("Effect order can be changed at\nFILTER MAPS")));
18611   }
18612 }
18613 
18614 
do_fx_move_context(lives_mt * mt)18615 void do_fx_move_context(lives_mt * mt) {
18616   clear_context(mt);
18617   add_context_label(mt, (_("You can select an effect,\nthen use the INSERT BEFORE")));
18618   add_context_label(mt, (_("or INSERT AFTER buttons to move it.")));
18619 }
18620 
18621 
on_timeline_release(LiVESWidget * eventbox,LiVESXEventButton * event,livespointer user_data)18622 boolean on_timeline_release(LiVESWidget * eventbox, LiVESXEventButton * event, livespointer user_data) {
18623   //button release
18624   lives_mt *mt = (lives_mt *)user_data;
18625 
18626   double pos = mt->region_end;
18627 
18628   lives_mt_poly_state_t statep;
18629 
18630   if (!LIVES_IS_INTERACTIVE || mt->sel_locked) return FALSE;
18631 
18632   if (LIVES_IS_PLAYING) return FALSE;
18633 
18634   mt->tl_mouse = FALSE;
18635 
18636   if (eventbox != mt->timeline_reg || mt->sel_locked) {
18637     return FALSE;
18638   }
18639 
18640   if (event) mt->region_updating = FALSE;
18641 
18642   if (mt->region_start == mt->region_end && eventbox == mt->timeline_reg) {
18643     mt->region_start = mt->region_end = 0;
18644     lives_widget_set_sensitive(mt->view_sel_events, FALSE);
18645     lives_signal_handler_block(mt->spinbutton_start, mt->spin_start_func);
18646     lives_signal_handler_block(mt->spinbutton_end, mt->spin_end_func);
18647     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_start), 0., mt->end_secs);
18648     lives_spin_button_set_range(LIVES_SPIN_BUTTON(mt->spinbutton_end), 0., mt->end_secs + 1. / mt->fps);
18649     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_start), 0.);
18650     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_end), 0.);
18651     lives_signal_handler_unblock(mt->spinbutton_start, mt->spin_start_func);
18652     lives_signal_handler_unblock(mt->spinbutton_end, mt->spin_end_func);
18653     lives_widget_queue_draw(mt->timeline_reg);
18654     draw_region(mt);
18655     no_time_selected(mt);
18656   }
18657 
18658   if ((mt->region_end != mt->region_start) && eventbox == mt->timeline_reg) {
18659     if (mt->opts.snap_over) get_region_overlap(mt);
18660     if (mt->region_end < mt->region_start) {
18661       mt->region_start -= mt->region_end;
18662       mt->region_end += mt->region_start;
18663       mt->region_start = mt->region_end - mt->region_start;
18664     }
18665     if (mt->region_end > mt->region_start && mt->event_list && get_first_event(mt->event_list)) {
18666       if (mt->selected_tracks) {
18667         lives_widget_set_sensitive(mt->fx_region, TRUE);
18668         lives_widget_set_sensitive(mt->ins_gap_sel, TRUE);
18669         lives_widget_set_sensitive(mt->remove_gaps, TRUE);
18670         lives_widget_set_sensitive(mt->remove_first_gaps, TRUE);
18671       } else {
18672         lives_widget_set_sensitive(mt->fx_region, FALSE);
18673       }
18674       lives_widget_set_sensitive(mt->playsel, TRUE);
18675       lives_widget_set_sensitive(mt->ins_gap_cur, TRUE);
18676       lives_widget_set_sensitive(mt->view_sel_events, TRUE);
18677     } else {
18678       lives_widget_set_sensitive(mt->playsel, FALSE);
18679       lives_widget_set_sensitive(mt->fx_region, FALSE);
18680       lives_widget_set_sensitive(mt->ins_gap_cur, FALSE);
18681       lives_widget_set_sensitive(mt->ins_gap_sel, FALSE);
18682       lives_widget_set_sensitive(mt->remove_gaps, FALSE);
18683       lives_widget_set_sensitive(mt->remove_first_gaps, FALSE);
18684     }
18685     if (mt->region_start == mt->region_end) lives_widget_queue_draw(mt->timeline);
18686   } else {
18687     if (eventbox != mt->timeline_reg) mt_tl_move(mt, pos);
18688     lives_widget_set_sensitive(mt->fx_region, FALSE);
18689     lives_widget_set_sensitive(mt->ins_gap_cur, FALSE);
18690     lives_widget_set_sensitive(mt->ins_gap_sel, FALSE);
18691     lives_widget_set_sensitive(mt->playsel, FALSE);
18692     lives_widget_set_sensitive(mt->remove_gaps, FALSE);
18693     lives_widget_set_sensitive(mt->remove_first_gaps, FALSE);
18694     if (mt->init_event && mt->poly_state == POLY_PARAMS)
18695       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->node_spinbutton),
18696                                   pos - get_event_timecode(mt->init_event) / TICKS_PER_SECOND_DBL);
18697   }
18698 
18699   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_start), mt->region_start);
18700   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->spinbutton_end), mt->region_end);
18701 
18702   pos = mt->ptr_time;
18703   if (pos > mt->region_end - 1. / mt->fps) lives_widget_set_sensitive(mt->tc_to_rs, FALSE);
18704   else lives_widget_set_sensitive(mt->tc_to_rs, TRUE);
18705   if (pos < mt->region_start + 1. / mt->fps) lives_widget_set_sensitive(mt->tc_to_re, FALSE);
18706   else lives_widget_set_sensitive(mt->tc_to_re, TRUE);
18707 
18708   if (mt->opts.mouse_mode == MOUSE_MODE_SELECT && event) mouse_select_end(eventbox, event, mt);
18709 
18710   if (mt->selected_tracks && mt->region_end != mt->region_start) {
18711     lives_widget_set_sensitive(mt->fx_region, TRUE);
18712     switch (lives_list_length(mt->selected_tracks)) {
18713     case 1:
18714       lives_widget_set_sensitive(mt->fx_region_v, TRUE);
18715       if (cfile->achans == 0) lives_widget_set_sensitive(mt->fx_region_a, FALSE);
18716       break;
18717     case 2:
18718       if (!mt->opts.pertrack_audio)
18719         lives_widget_set_sensitive(mt->fx_region_2a, FALSE);
18720       lives_widget_set_sensitive(mt->fx_region_v, FALSE);
18721       lives_widget_set_sensitive(mt->fx_region_a, FALSE);
18722       break;
18723     default:
18724       break;
18725     }
18726   } else lives_widget_set_sensitive(mt->fx_region, FALSE);
18727 
18728   // update labels
18729   statep = get_poly_state_from_page(mt);
18730   if (statep == POLY_TRANS || statep == POLY_COMP) {
18731     polymorph(mt, POLY_NONE);
18732     polymorph(mt, statep);
18733   }
18734 
18735   return TRUE;
18736 }
18737 
18738 
on_timeline_press(LiVESWidget * widget,LiVESXEventButton * event,livespointer user_data)18739 boolean on_timeline_press(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
18740   lives_mt *mt = (lives_mt *)user_data;
18741 
18742   LiVESXModifierType modmask;
18743   LiVESXDevice *device;
18744 
18745   double pos;
18746 
18747   int x;
18748 
18749   if (!LIVES_IS_INTERACTIVE) return FALSE;
18750 
18751   if (LIVES_IS_PLAYING) return FALSE;
18752 
18753   lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
18754                            widget, &x, NULL);
18755   pos = get_time_from_x(mt, x);
18756   if (widget == mt->timeline_reg && !mt->sel_locked) {
18757     mt->region_start = mt->region_end = mt->region_init = pos;
18758     lives_widget_set_sensitive(mt->view_sel_events, FALSE);
18759     mt->region_updating = TRUE;
18760   }
18761 
18762   device = (LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device;
18763   lives_widget_get_modmask(device, widget, &modmask);
18764 
18765   if (event->button == 1) {
18766     if (widget == mt->timeline_eb) {
18767       mt->fm_edit_event = NULL;
18768       mt_tl_move(mt, pos);
18769       mt->tl_mouse = TRUE;
18770     }
18771 
18772     if (widget == mt->timeline) {
18773       mt->fm_edit_event = NULL;
18774       mt_tl_move(mt, pos);
18775     }
18776 
18777     if (mt->opts.mouse_mode == MOUSE_MODE_SELECT) mouse_select_start(widget, event, mt);
18778   }
18779 
18780   return TRUE;
18781 }
18782 
18783 
get_prev_fm(lives_mt * mt,int current_track,weed_plant_t * event)18784 weed_plant_t *get_prev_fm(lives_mt * mt, int current_track, weed_plant_t *event) {
18785   weed_plant_t *event2, *event3, *eventx;
18786 
18787   if (!event) return NULL;
18788 
18789   eventx = get_filter_map_before(event, current_track, NULL);
18790 
18791   if (!eventx) return NULL;
18792 
18793   if (get_event_timecode(eventx) == get_event_timecode(event)) {
18794     // start with a map different from current
18795 
18796     while (1) {
18797       event2 = get_prev_event(eventx);
18798 
18799       if (!event2) return NULL;
18800 
18801       event3 = get_filter_map_before(event2, current_track, NULL);
18802 
18803       if (!compare_filter_maps(event3, eventx, current_track)) {
18804         event = event2 = event3;
18805         break; // continue with event 3
18806       }
18807       eventx = event3;
18808     }
18809   } else {
18810     if ((event2 = get_prev_frame_event(event)) == NULL) return NULL;
18811 
18812     event2 = get_filter_map_before(event2, current_track, NULL);
18813 
18814     if (!event2) return NULL;
18815   }
18816 
18817   // now find the earliest which is the same
18818   while (1) {
18819     event = event2;
18820 
18821     event3 = get_prev_event(event2);
18822 
18823     if (!event3) break;
18824 
18825     event2 = get_filter_map_before(event3, current_track, NULL);
18826 
18827     if (!event2) break;
18828 
18829     if (!compare_filter_maps(event2, event, current_track)) break;
18830   }
18831 
18832   if (filter_map_after_frame(event)) return get_next_frame_event(event);
18833 
18834   return event;
18835 }
18836 
18837 
get_next_fm(lives_mt * mt,int current_track,weed_plant_t * event)18838 weed_plant_t *get_next_fm(lives_mt * mt, int current_track, weed_plant_t *event) {
18839   weed_plant_t *event2, *event3;
18840 
18841   if (!event) return NULL;
18842   if ((event2 = get_filter_map_after(event, current_track)) == NULL) return event;
18843   event3 = get_filter_map_before(event, LIVES_TRACK_ANY, NULL);
18844   if (!event3) return NULL;
18845 
18846   // find the first filter_map which differs from the current
18847   while (1) {
18848     if (!compare_filter_maps(event2, event3, current_track)) break;
18849     event = get_next_event(event2);
18850     if (!event) return NULL;
18851     event3 = event2;
18852     if ((event2 = get_filter_map_after(event, current_track)) == NULL) return NULL;
18853   }
18854 
18855   if (filter_map_after_frame(event2)) return get_next_frame_event(event2);
18856 
18857   return event2;
18858 }
18859 
18860 
add_mark_at(lives_mt * mt,double time)18861 static void add_mark_at(lives_mt * mt, double time) {
18862   lives_painter_t *cr;
18863 
18864   char *tstring = lives_strdup_printf("%.6f", time);
18865   int offset;
18866 
18867   lives_widget_set_sensitive(mt->clear_marks, TRUE);
18868   mt->tl_marks = lives_list_append(mt->tl_marks, tstring);
18869   offset = (time - mt->tl_min) / (mt->tl_max - mt->tl_min) * (double)lives_widget_get_allocation_width(mt->timeline);
18870 
18871   cr = lives_painter_create_from_surface(mt->tl_reg_surf);
18872 
18873   lives_painter_set_source_rgb_from_lives_rgba(cr, &palette->mt_mark);
18874 
18875   lives_painter_move_to(cr, offset, 1);
18876   lives_painter_line_to(cr, offset, lives_widget_get_allocation_height(mt->timeline_reg) - 2);
18877   lives_painter_stroke(cr);
18878 
18879   lives_painter_destroy(cr);
18880 }
18881 
18882 
mt_mark_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)18883 boolean mt_mark_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
18884                          livespointer user_data) {
18885   lives_mt *mt = (lives_mt *)user_data;
18886   double cur_time;
18887 
18888   if (!LIVES_IS_PLAYING) return TRUE;
18889 
18890   cur_time = mt->ptr_time;
18891 
18892   add_mark_at(mt, cur_time);
18893   return TRUE;
18894 }
18895 
18896 
mt_tcoverlay_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)18897 boolean mt_tcoverlay_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
18898                               livespointer user_data) {
18899   lives_mt *mt = (lives_mt *)user_data;
18900   if (LIVES_IS_PLAYING) {
18901     mt->opts.overlay_timecode = !mt->opts.overlay_timecode;
18902   }
18903   return FALSE;
18904 }
18905 
18906 
on_fx_insa_clicked(LiVESWidget * button,livespointer user_data)18907 void on_fx_insa_clicked(LiVESWidget * button, livespointer user_data) {
18908   lives_mt *mt = (lives_mt *)user_data;
18909   mt->fx_order = FX_ORD_AFTER;
18910   lives_widget_set_sensitive(mt->fx_ibefore_button, FALSE);
18911   lives_widget_set_sensitive(mt->fx_iafter_button, FALSE);
18912 
18913   clear_context(mt);
18914   add_context_label(mt, (_("Click on another effect,")));
18915   add_context_label(mt, (_("and the selected one\nwill be inserted")));
18916   add_context_label(mt, (_("after it.\n")));
18917 }
18918 
18919 
on_fx_insb_clicked(LiVESWidget * button,livespointer user_data)18920 void on_fx_insb_clicked(LiVESWidget * button, livespointer user_data) {
18921   lives_mt *mt = (lives_mt *)user_data;
18922   mt->fx_order = FX_ORD_BEFORE;
18923   lives_widget_set_sensitive(mt->fx_ibefore_button, FALSE);
18924   lives_widget_set_sensitive(mt->fx_iafter_button, FALSE);
18925 
18926   clear_context(mt);
18927   add_context_label(mt, (_("Click on another effect,")));
18928   add_context_label(mt, (_("and the selected one\nwill be inserted")));
18929   add_context_label(mt, (_("before it.\n")));
18930 }
18931 
18932 
on_prev_fm_clicked(LiVESWidget * button,livespointer user_data)18933 void on_prev_fm_clicked(LiVESWidget * button, livespointer user_data) {
18934   lives_mt *mt = (lives_mt *)user_data;
18935   weed_timecode_t tc;
18936   double secs = mt->ptr_time;
18937   tc = q_gint64(secs * TICKS_PER_SECOND_DBL, mt->fps);
18938   weed_plant_t *event;
18939 
18940   event = get_frame_event_at(mt->event_list, tc, mt->fm_edit_event, TRUE);
18941 
18942   event = get_prev_fm(mt, mt->current_track, event);
18943 
18944   if (event) tc = get_event_timecode(event);
18945 
18946   mt_tl_move(mt, tc / TICKS_PER_SECOND_DBL);
18947 }
18948 
18949 
on_next_fm_clicked(LiVESWidget * button,livespointer user_data)18950 void on_next_fm_clicked(LiVESWidget * button, livespointer user_data) {
18951   lives_mt *mt = (lives_mt *)user_data;
18952   weed_timecode_t tc;
18953   weed_plant_t *event;
18954   double secs = mt->ptr_time;
18955   tc = q_gint64(secs * TICKS_PER_SECOND_DBL, mt->fps);
18956 
18957   event = get_frame_event_at(mt->event_list, tc, mt->fm_edit_event, TRUE);
18958 
18959   event = get_next_fm(mt, mt->current_track, event);
18960 
18961   if (event) tc = get_event_timecode(event);
18962 
18963   mt_tl_move(mt, tc / TICKS_PER_SECOND_DBL);
18964 }
18965 
18966 
get_prev_node_tc(lives_mt * mt,weed_timecode_t tc)18967 static weed_timecode_t get_prev_node_tc(lives_mt * mt, weed_timecode_t tc) {
18968   int num_params = num_in_params(get_weed_filter(mt->current_fx), FALSE, FALSE);
18969   weed_timecode_t prev_tc = -1;
18970   weed_plant_t *event;
18971   weed_timecode_t ev_tc;
18972 
18973   if (!pchain) return tc;
18974 
18975   for (int i = 0; i < num_params; i++) {
18976     event = (weed_plant_t *)pchain[i];
18977     while (event && (ev_tc = get_event_timecode(event)) < tc) {
18978       if (ev_tc > prev_tc) prev_tc = ev_tc;
18979       event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, NULL);
18980     }
18981   }
18982   return prev_tc;
18983 }
18984 
18985 
get_next_node_tc(lives_mt * mt,weed_timecode_t tc)18986 static weed_timecode_t get_next_node_tc(lives_mt * mt, weed_timecode_t tc) {
18987   int num_params = num_in_params(get_weed_filter(mt->current_fx), FALSE, FALSE);
18988   weed_timecode_t next_tc = -1;
18989   weed_plant_t *event;
18990   weed_timecode_t ev_tc;
18991 
18992   if (!pchain) return tc;
18993 
18994   for (int i = 0; i < num_params; i++) {
18995     event = (weed_plant_t *)pchain[i];
18996     while (event && (ev_tc = get_event_timecode(event)) <= tc)
18997       event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, NULL);
18998     if (event) {
18999       if (next_tc == -1 || ev_tc < next_tc) next_tc = ev_tc;
19000     }
19001   }
19002   return next_tc;
19003 }
19004 
19005 
is_node_tc(lives_mt * mt,weed_timecode_t tc)19006 static boolean is_node_tc(lives_mt * mt, weed_timecode_t tc) {
19007   int num_params = num_in_params(get_weed_filter(mt->current_fx), FALSE, FALSE);
19008   weed_plant_t *event;
19009   weed_timecode_t ev_tc;
19010 
19011   for (int i = 0; i < num_params; i++) {
19012     event = (weed_plant_t *)pchain[i];
19013     ev_tc = -1;
19014     while (event &&
19015            (ev_tc = get_event_timecode(event)) < tc)
19016       event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, NULL);
19017     if (ev_tc == tc && !weed_plant_has_leaf(event, WEED_LEAF_IS_DEF_VALUE)) return TRUE;
19018   }
19019   return FALSE;
19020 }
19021 
19022 
19023 // apply the param changes and update widgets
on_node_spin_value_changed(LiVESSpinButton * spinbutton,livespointer user_data)19024 void on_node_spin_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
19025   lives_mt *mt = (lives_mt *)user_data;
19026   weed_timecode_t init_tc = get_event_timecode(mt->init_event);
19027   weed_timecode_t otc = lives_spin_button_get_value(spinbutton) * TICKS_PER_SECOND_DBL + init_tc;
19028   weed_timecode_t tc = q_gint64(otc, mt->fps);
19029   weed_timecode_t pn_tc, nn_tc;
19030   double timesecs;
19031   boolean auto_prev = mt->opts.fx_auto_preview;
19032 
19033   lives_signal_handlers_block_by_func(spinbutton, (livespointer)on_node_spin_value_changed, (livespointer)mt);
19034 
19035   if (!mt->block_tl_move) {
19036     timesecs = otc / TICKS_PER_SECOND_DBL;
19037     mt->block_node_spin = TRUE;
19038     if (mt->current_track >= 0 && (mt->opts.fx_auto_preview || mainw->play_window))
19039       mt->no_frame_update = TRUE; // we will preview anyway later, so don't do it twice
19040     mt_tl_move(mt, timesecs);
19041     mt->no_frame_update = FALSE;
19042     mt->block_node_spin = FALSE;
19043   }
19044 
19045   if (mt->prev_fx_time == 0. || tc == init_tc) {
19046     //add_mt_param_box(mt); // sensitise/desensitise reinit params
19047   } else mt->prev_fx_time = mt_get_effect_time(mt);
19048 
19049   interpolate_params((weed_plant_t *)mt->current_rfx->source, pchain, tc);
19050 
19051   set_params_unchanged(mt, mt->current_rfx);
19052 
19053   get_track_index(mt, tc);
19054 
19055   mt->opts.fx_auto_preview = FALSE; // we will preview anyway later, so don't do it twice
19056   mt->current_rfx->needs_reinit = FALSE;
19057   mainw->block_param_updates = TRUE;
19058   update_visual_params(mt->current_rfx, FALSE);
19059   mainw->block_param_updates = FALSE;
19060   if (mt->current_rfx->needs_reinit) {
19061     mt->current_rfx->needs_reinit = FALSE;
19062     weed_reinit_effect(mt->current_rfx->source, TRUE);
19063   }
19064 
19065   mt->opts.fx_auto_preview = auto_prev;
19066 
19067   // get timecodes of previous and next fx nodes
19068   pn_tc = get_prev_node_tc(mt, tc);
19069   nn_tc = get_next_node_tc(mt, tc);
19070 
19071   if (pn_tc > -1) lives_widget_set_sensitive(mt->prev_node_button, TRUE);
19072   else lives_widget_set_sensitive(mt->prev_node_button, FALSE);
19073 
19074   if (nn_tc > -1) lives_widget_set_sensitive(mt->next_node_button, TRUE);
19075   else lives_widget_set_sensitive(mt->next_node_button, FALSE);
19076 
19077   if (is_node_tc(mt, tc)) {
19078     lives_widget_set_sensitive(mt->del_node_button, TRUE);
19079     lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
19080   } else lives_widget_set_sensitive(mt->del_node_button, FALSE);
19081 
19082   lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
19083 
19084   if (mt->current_track >= 0) {
19085     if (mt->opts.fx_auto_preview || mainw->play_window) mt_show_current_frame(mt, FALSE);
19086   }
19087 
19088   lives_signal_handlers_unblock_by_func(spinbutton, (livespointer)on_node_spin_value_changed, (livespointer)mt);
19089 }
19090 
19091 
check_can_resetp(lives_mt * mt)19092 static boolean check_can_resetp(lives_mt * mt) {
19093   int nparams;
19094   boolean can_reset = FALSE;
19095   weed_plant_t *filter = get_weed_filter(mt->current_fx);
19096 
19097   /// create new in_params with default vals.
19098   weed_plant_t **def_params = weed_params_create(filter, TRUE);
19099   weed_plant_t **in_params = weed_instance_get_in_params(mt->current_rfx->source, &nparams);
19100   int ninpar, i;
19101 
19102   if (mt->init_event) {
19103     int num_in_tracks;
19104     int *in_tracks = weed_get_int_array_counted(mt->init_event, WEED_LEAF_IN_TRACKS, &num_in_tracks);
19105     if (in_tracks) {
19106       for (i = 0; i < num_in_tracks; i++) {
19107         if (in_tracks[i] == mt->current_track) {
19108           mt->track_index = i;
19109           break;
19110 	  // *INDENT-OFF*
19111 	}}}}
19112   // *INDENT-ON*
19113 
19114   /// check for variance
19115   for (i = 0; i < nparams; i++) {
19116     if (weed_param_is_hidden(in_params[i], WEED_TRUE)) continue;
19117     if (is_perchannel_multiw(in_params[i])) {
19118       /// if param is "value_per_channel", leave the other channels unaltered
19119       fill_param_vals_to(def_params[i], weed_param_get_template(def_params[i]), mt->track_index);
19120       if (!weed_leaf_elements_equate(in_params[i], WEED_LEAF_VALUE, def_params[i], WEED_LEAF_VALUE, mt->track_index)) {
19121         can_reset = TRUE;
19122         break;
19123       }
19124     } else {
19125       if (!weed_leaf_elements_equate(in_params[i], WEED_LEAF_VALUE, def_params[i], WEED_LEAF_VALUE, -1)) {
19126         can_reset = TRUE;
19127         break;
19128       }
19129     }
19130   }
19131   ninpar = num_in_params(filter, FALSE, FALSE);
19132   for (i = 0; i < ninpar; i++) {
19133     weed_plant_free(def_params[i]);
19134   }
19135   lives_free(def_params);
19136   lives_free(in_params);
19137   return can_reset;
19138 }
19139 
on_resetp_clicked(LiVESWidget * button,livespointer user_data)19140 void on_resetp_clicked(LiVESWidget * button, livespointer user_data) {
19141   lives_mt *mt = (lives_mt *)user_data;
19142   int nparams;
19143   boolean can_apply = FALSE;
19144   boolean aprev = mt->opts.fx_auto_preview;
19145   weed_plant_t *filter = get_weed_filter(mt->current_fx);
19146 
19147   /// create new in_params with default vals.
19148   weed_plant_t **def_params = weed_params_create(filter, TRUE);
19149   weed_plant_t **in_params = weed_instance_get_in_params(mt->current_rfx->source, &nparams);
19150   int i;
19151 
19152   if (mt->init_event) {
19153     int num_in_tracks;
19154     int *in_tracks = weed_get_int_array_counted(mt->init_event, WEED_LEAF_IN_TRACKS, &num_in_tracks);
19155     if (num_in_tracks > 0) {
19156       for (i = 0; i < num_in_tracks; i++) {
19157         if (in_tracks[i] == mt->current_track) {
19158           mt->track_index = i;
19159           break;
19160 	  // *INDENT-OFF*
19161 	}}}}
19162   // *INDENT-ON*
19163 
19164   /// set params to the default value,
19165   /// and if any change we'll sensitize the "Apply Button"
19166   for (i = 0; i < nparams; i++) {
19167     if (weed_param_is_hidden(in_params[i], WEED_TRUE)) continue;
19168     if (is_perchannel_multiw(in_params[i])) {
19169       /// if param is "value_per_channel", leave the other channels unaltered
19170       fill_param_vals_to(def_params[i], weed_param_get_template(def_params[i]), mt->track_index);
19171       if (!weed_leaf_elements_equate(in_params[i], WEED_LEAF_VALUE, def_params[i], WEED_LEAF_VALUE, mt->track_index)) {
19172         weed_leaf_dup_nth(in_params[i], def_params[i], WEED_LEAF_VALUE, mt->track_index);
19173         can_apply = TRUE;
19174         mt->current_rfx->params[i].changed = TRUE;
19175       }
19176     } else {
19177       if (!weed_leaf_elements_equate(in_params[i], WEED_LEAF_VALUE, def_params[i], WEED_LEAF_VALUE, -1)) {
19178         weed_leaf_dup(in_params[i], def_params[i], WEED_LEAF_VALUE);
19179         mt->current_rfx->params[i].changed = TRUE;
19180         can_apply = TRUE;
19181       }
19182     }
19183     weed_plant_free(def_params[i]);
19184   }
19185   lives_free(def_params);
19186   lives_free(in_params);
19187 
19188   mt->opts.fx_auto_preview = FALSE;
19189   mainw->block_param_updates = TRUE;
19190   mt->current_rfx->needs_reinit = FALSE;
19191   mt->current_rfx->flags |= RFX_FLAGS_NO_RESET;
19192   update_visual_params(mt->current_rfx, TRUE);
19193   mainw->block_param_updates = FALSE;
19194   if (mt->current_rfx->needs_reinit) {
19195     weed_reinit_effect(mt->current_rfx->source, TRUE);
19196     mt->current_rfx->needs_reinit = FALSE;
19197   }
19198   mt->current_rfx->flags ^= RFX_FLAGS_NO_RESET;
19199   mt->opts.fx_auto_preview = aprev;
19200   lives_widget_set_sensitive(mt->apply_fx_button, can_apply);
19201   lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
19202   activate_mt_preview(mt);
19203 }
19204 
19205 
19206 // node buttons
on_next_node_clicked(LiVESWidget * button,livespointer user_data)19207 void on_next_node_clicked(LiVESWidget * button, livespointer user_data) {
19208   lives_mt *mt = (lives_mt *)user_data;
19209   weed_timecode_t init_tc = get_event_timecode(mt->init_event);
19210   weed_timecode_t tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) * TICKS_PER_SECOND_DBL +
19211                                 init_tc,
19212                                 mt->fps);
19213   weed_timecode_t next_tc = get_next_node_tc(mt, tc);
19214   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->node_spinbutton), (next_tc - init_tc) / TICKS_PER_SECOND_DBL);
19215   if (mt->current_track >= 0) mt_show_current_frame(mt, FALSE);
19216   lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
19217   lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
19218 }
19219 
19220 
on_prev_node_clicked(LiVESWidget * button,livespointer user_data)19221 void on_prev_node_clicked(LiVESWidget * button, livespointer user_data) {
19222   lives_mt *mt = (lives_mt *)user_data;
19223   weed_timecode_t init_tc = get_event_timecode(mt->init_event);
19224   weed_timecode_t tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) * TICKS_PER_SECOND_DBL +
19225                                 init_tc,
19226                                 mt->fps);
19227   weed_timecode_t prev_tc = get_prev_node_tc(mt, tc);
19228   lives_spin_button_set_value(LIVES_SPIN_BUTTON(mt->node_spinbutton), (prev_tc - init_tc) / TICKS_PER_SECOND_DBL);
19229   if (mt->current_track >= 0) mt_show_current_frame(mt, FALSE);
19230   lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
19231   lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
19232 }
19233 
19234 
on_del_node_clicked(LiVESWidget * button,livespointer user_data)19235 void on_del_node_clicked(LiVESWidget * button, livespointer user_data) {
19236   lives_mt *mt = (lives_mt *)user_data;
19237 
19238   int error;
19239 
19240   weed_plant_t **in_params = weed_get_plantptr_array((weed_plant_t *)mt->current_rfx->source, WEED_LEAF_IN_PARAMETERS, &error);
19241 
19242   weed_plant_t *event;
19243   weed_plant_t *prev_pchange, *next_pchange;
19244 
19245   weed_timecode_t ev_tc;
19246   weed_timecode_t tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) * TICKS_PER_SECOND_DBL +
19247                                 get_event_timecode(mt->init_event), mt->fps);
19248 
19249   char *filter_name;
19250 
19251   int num_params = num_in_params((weed_plant_t *)mt->current_rfx->source, FALSE, FALSE);
19252 
19253   register int i;
19254 
19255   if (mt->idlefunc > 0) {
19256     lives_source_remove(mt->idlefunc);
19257     mt->idlefunc = 0;
19258   }
19259 
19260   // TODO - undo: but we need to reinsert the values in pchains...
19261 
19262   for (i = 0; i < num_params; i++) {
19263     event = (weed_plant_t *)pchain[i];
19264     ev_tc = -1;
19265     while (event && (ev_tc = get_event_timecode(event)) < tc)
19266       event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, &error);
19267     if (ev_tc == tc) {
19268       prev_pchange = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_PREV_CHANGE, &error);
19269       next_pchange = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, &error);
19270       if (event != pchain[i]) {
19271         delete_event(mt->event_list, event);
19272         if (prev_pchange) weed_set_voidptr_value(prev_pchange, WEED_LEAF_NEXT_CHANGE, next_pchange);
19273         if (next_pchange) weed_set_voidptr_value(next_pchange, WEED_LEAF_PREV_CHANGE, prev_pchange);
19274       } else {
19275         // is initial pchange, reset to defaults, c.f. paramspecial.c
19276         weed_plant_t *param = in_params[i];
19277         weed_plant_t *paramtmpl = weed_param_get_template(param);
19278         if (weed_plant_has_leaf(paramtmpl, WEED_LEAF_HOST_DEFAULT)) {
19279           weed_leaf_copy(event, WEED_LEAF_VALUE, paramtmpl, WEED_LEAF_HOST_DEFAULT);
19280         } else weed_leaf_copy(event, WEED_LEAF_VALUE, paramtmpl, WEED_LEAF_DEFAULT);
19281         if (is_perchannel_multiw(param)) {
19282           int num_in_tracks = weed_leaf_num_elements(mt->init_event, WEED_LEAF_IN_TRACKS);
19283           fill_param_vals_to(event, paramtmpl, num_in_tracks - 1);
19284         }
19285         weed_set_boolean_value(event, WEED_LEAF_IS_DEF_VALUE, WEED_TRUE);
19286       }
19287     }
19288   }
19289   lives_free(in_params);
19290 
19291   if (mt->current_fx == mt->avol_fx && mt->avol_init_event && mt->opts.aparam_view_list) {
19292     LiVESList *slist = mt->audio_draws;
19293     while (slist) {
19294       lives_widget_queue_draw((LiVESWidget *)slist->data);
19295       slist = slist->next;
19296     }
19297   }
19298 
19299   filter_name = weed_filter_idx_get_name(mt->current_fx, FALSE, FALSE);
19300 
19301   d_print(_("Removed parameter values for effect %s at time %s\n"), filter_name, mt->timestring);
19302   lives_free(filter_name);
19303   mt->block_tl_move = TRUE;
19304   on_node_spin_value_changed(LIVES_SPIN_BUTTON(mt->node_spinbutton), (livespointer)mt);
19305   mt->block_tl_move = FALSE;
19306   lives_widget_set_sensitive(mt->del_node_button, FALSE);
19307   if (mt->current_track >= 0) {
19308     mt_show_current_frame(mt, FALSE);
19309   }
19310   lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
19311   lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
19312 
19313   if (!mt->auto_changed) mt->auto_changed = TRUE;
19314   if (prefs->mt_auto_back > 0) mt->idlefunc = mt_idle_add(mt);
19315   else if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
19316   else lives_widget_set_sensitive(mt->backup, TRUE);
19317   mt->changed = TRUE;
19318 }
19319 
19320 
mt_fixup_events(lives_mt * mt,weed_plant_t * old_event,weed_plant_t * new_event)19321 void mt_fixup_events(lives_mt * mt, weed_plant_t *old_event, weed_plant_t *new_event) {
19322   // if any "notable" events have changed, we should repoint them here
19323 
19324   if (!mt) return;
19325 
19326   if (mt->fm_edit_event == old_event) {
19327     //g_print("fme event\n");
19328     mt->fm_edit_event = new_event;
19329   }
19330   if (mt->init_event == old_event) {
19331     //g_print("ie event\n");
19332     mt->init_event = new_event;
19333   }
19334   if (mt->selected_init_event == old_event) {
19335     //g_print("se event\n");
19336     mt->selected_init_event = new_event;
19337   }
19338   if (mt->avol_init_event == old_event) {
19339     //g_print("aie event\n");
19340     mt->avol_init_event = new_event;
19341   }
19342   if (mt->specific_event == old_event) {
19343     //g_print("spec event\n");
19344     mt->specific_event = new_event;
19345   }
19346 }
19347 
19348 
combine_ign(weed_plant_t * xnew,weed_plant_t * xold)19349 static void combine_ign(weed_plant_t *xnew, weed_plant_t *xold) {
19350   int num, numo, *nign, *oign, i;
19351 
19352   // combine WEED_LEAF_IGNORE values using NAND
19353   oign = weed_get_boolean_array_counted(xold, WEED_LEAF_IGNORE, &numo);
19354   if (!numo) return;
19355   nign = weed_get_boolean_array_counted(xnew, WEED_LEAF_IGNORE, &num);
19356   for (i = 0; i < num; i++) if (i >= numo || oign[i] == WEED_FALSE) nign[i] = WEED_FALSE;
19357   weed_set_boolean_array(xnew, WEED_LEAF_IGNORE, num, nign);
19358   lives_free(oign);
19359   lives_free(nign);
19360 }
19361 
19362 
add_to_pchain(weed_plant_t * event_list,weed_plant_t * init_event,weed_plant_t * pchange,int index,weed_timecode_t tc)19363 static void add_to_pchain(weed_plant_t *event_list, weed_plant_t *init_event, weed_plant_t *pchange, int index,
19364                           weed_timecode_t tc) {
19365   weed_plant_t *event = (weed_plant_t *)pchain[index];
19366   weed_plant_t *last_event = NULL;
19367   int error;
19368 
19369   while (event && get_event_timecode(event) < tc) {
19370     last_event = event;
19371     event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, &error);
19372   }
19373 
19374   if (event && get_event_timecode(event) == tc) {
19375     // replace an existing change
19376     weed_plant_t *next_event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, &error);
19377     if (next_event) weed_set_voidptr_value(next_event, WEED_LEAF_PREV_CHANGE, pchange);
19378     weed_set_voidptr_value(pchange, WEED_LEAF_NEXT_CHANGE, next_event);
19379     if (event == pchain[index]) weed_leaf_delete(pchange, WEED_LEAF_IGNORE); // never ignore our init pchanges
19380     if (weed_plant_has_leaf(pchange, WEED_LEAF_IGNORE)) combine_ign(pchange, event);
19381     delete_event(event_list, event);
19382   } else {
19383     weed_set_voidptr_value(pchange, WEED_LEAF_NEXT_CHANGE, event);
19384     if (event) weed_set_voidptr_value(event, WEED_LEAF_PREV_CHANGE, pchange);
19385   }
19386 
19387   if (last_event) weed_set_voidptr_value(last_event, WEED_LEAF_NEXT_CHANGE, pchange);
19388   else {
19389     // update "in_params" for init_event
19390     int numin;
19391     void **in_params = weed_get_voidptr_array_counted(init_event, WEED_LEAF_IN_PARAMETERS, &numin);
19392     in_params[index] = pchain[index] = (void *)pchange;
19393     weed_set_voidptr_array(init_event, WEED_LEAF_IN_PARAMETERS, numin, in_params);
19394     lives_free(in_params);
19395   }
19396   weed_set_voidptr_value(pchange, WEED_LEAF_PREV_CHANGE, last_event);
19397 }
19398 
19399 
activate_mt_preview(lives_mt * mt)19400 void activate_mt_preview(lives_mt * mt) {
19401   // called from paramwindow.c when a parameter changes - show effect with currently unapplied values
19402   static boolean norecurse = FALSE;
19403   if (norecurse) return;
19404   norecurse = TRUE;
19405 
19406   if (mt->poly_state == POLY_PARAMS) {
19407     if (mt->opts.fx_auto_preview) {
19408       mainw->no_interp = TRUE; // no interpolation - parameter is in an uncommited state
19409       mt_show_current_frame(mt, FALSE);
19410       mainw->no_interp = FALSE;
19411     }
19412     if (mt->apply_fx_button) {
19413       int nparams = mt->current_rfx->num_params;
19414       lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
19415       set_child_alt_colour(mt->apply_fx_button, TRUE);
19416       for (int i = 0; i < nparams; i++) {
19417         if (mt->current_rfx->params[i].changed) {
19418           lives_widget_set_sensitive(mt->apply_fx_button, TRUE);
19419           set_child_colour(mt->apply_fx_button, TRUE);
19420           break;
19421         }
19422       }
19423       lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
19424     }
19425   } else mt_show_current_frame(mt, FALSE);
19426   norecurse = FALSE;
19427 }
19428 
19429 
on_set_pvals_clicked(LiVESWidget * button,livespointer user_data)19430 void on_set_pvals_clicked(LiVESWidget * button, livespointer user_data) {
19431   lives_mt *mt = (lives_mt *)user_data;
19432 
19433   weed_plant_t *inst = (weed_plant_t *)mt->current_rfx->source;
19434   weed_plant_t *param, *pchange, *at_event;
19435 
19436   weed_timecode_t tc = q_gint64(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)) * TICKS_PER_SECOND_DBL +
19437                                 get_event_timecode(mt->init_event), mt->fps);
19438 
19439   int *tracks;
19440 
19441   char *tmp, *tmp2;
19442   char *filter_name;
19443   char *tname, *track_desc;
19444 
19445   boolean has_multi = FALSE;
19446   boolean was_changed = FALSE;
19447   boolean needs_idlefunc = FALSE;
19448   boolean did_backup = mt->did_backup;
19449   int nparams;
19450   int numtracks;
19451   int i;
19452 
19453   if (mt->current_rfx && mainw->textwidget_focus) {
19454     // make sure text widgets are updated if they activate the default
19455     LiVESWidget *textwidget = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mainw->textwidget_focus),
19456                               TEXTWIDGET_KEY);
19457     after_param_text_changed(textwidget, mt->current_rfx);
19458   }
19459   if (mt->framedraw) {
19460     if (!check_filewrite_overwrites()) {
19461       return;
19462     }
19463   }
19464 
19465   if (mt->idlefunc > 0) {
19466     needs_idlefunc = TRUE;
19467     lives_source_remove(mt->idlefunc);
19468     mt->idlefunc = 0;
19469   }
19470 
19471   lives_widget_set_sensitive(mt->apply_fx_button, FALSE);
19472   lives_widget_set_sensitive(mt->resetp_button, check_can_resetp(mt));
19473 
19474   for (i = 0; ((param = weed_inst_in_param(inst, i, FALSE, FALSE)) != NULL); i++) {
19475     if (!mt->current_rfx->params[i].changed) continue; // set only user changed parameters
19476     pchange = weed_plant_new(WEED_PLANT_EVENT);
19477     weed_set_int_value(pchange, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_PARAM_CHANGE);
19478     weed_set_int64_value(pchange, WEED_LEAF_TIMECODE, tc);
19479     weed_set_voidptr_value(pchange, WEED_LEAF_INIT_EVENT, mt->init_event);
19480     weed_set_int_value(pchange, WEED_LEAF_INDEX, i);
19481     if (is_perchannel_multiw(param)) {
19482       has_multi = TRUE;
19483       if (weed_plant_has_leaf(param, WEED_LEAF_IGNORE)) {
19484         int j;
19485         int num_vals;
19486         int *ign = weed_get_boolean_array_counted(param, WEED_LEAF_IGNORE, &num_vals);
19487         weed_set_boolean_array(pchange, WEED_LEAF_IGNORE, num_vals, ign);
19488         for (j = 0; j < num_vals; j++) {
19489           if (ign[j] == WEED_FALSE) {
19490             was_changed = TRUE;
19491             ign[j] = WEED_TRUE;
19492           }
19493         }
19494         if (was_changed)
19495           weed_set_boolean_array(param, WEED_LEAF_IGNORE, num_vals, ign);
19496         lives_free(ign);
19497       }
19498     } else was_changed = TRUE;
19499 
19500     weed_leaf_copy(pchange, WEED_LEAF_VALUE, param, WEED_LEAF_VALUE);
19501     //weed_add_plant_flags(pchange, WEED_LEAF_READONLY_PLUGIN);
19502 
19503     // mark the default value as changed, so the user can delete this node (which will reset to defaults)
19504     if (weed_plant_has_leaf(pchange, WEED_LEAF_IS_DEF_VALUE))
19505       weed_leaf_delete(pchange, WEED_LEAF_IS_DEF_VALUE);
19506 
19507     // set next_change, prev_change
19508     add_to_pchain(mt->event_list, mt->init_event, pchange, i, tc);
19509 
19510     at_event = get_frame_event_at(mt->event_list, tc, mt->init_event, TRUE);
19511     insert_param_change_event_at(mt->event_list, at_event, pchange);
19512   }
19513 
19514   if (!was_changed) {
19515     if (needs_idlefunc || (!did_backup && mt->auto_changed))
19516       mt->idlefunc = mt_idle_add(mt);
19517     return;
19518   }
19519 
19520   filter_name = weed_filter_idx_get_name(mt->current_fx, FALSE, FALSE);
19521   tracks = weed_get_int_array(mt->init_event, WEED_LEAF_IN_TRACKS, NULL);
19522   numtracks = enabled_in_channels(get_weed_filter(mt->current_fx), TRUE); // count repeated channels
19523 
19524   nparams = mt->current_rfx->num_params;
19525   for (i = 0; i < nparams; i++) mt->current_rfx->params[i].changed = FALSE;
19526 
19527   switch (numtracks) {
19528   case 1:
19529     tname = lives_fx_cat_to_text(LIVES_FX_CAT_EFFECT, FALSE); // effect
19530     track_desc = lives_strdup_printf(_("track %s"), (tmp = get_track_name(mt, tracks[0], FALSE)));
19531     lives_free(tmp);
19532     break;
19533   case 2:
19534     tname = lives_fx_cat_to_text(LIVES_FX_CAT_TRANSITION, FALSE); // transition
19535     track_desc = lives_strdup_printf(_("tracks %s and %s"), (tmp = get_track_name(mt, tracks[0], FALSE)),
19536                                      (tmp2 = get_track_name(mt, tracks[1], FALSE)));
19537     lives_free(tmp);
19538     lives_free(tmp2);
19539     break;
19540   default:
19541     tname = lives_fx_cat_to_text(LIVES_FX_CAT_COMPOSITOR, FALSE); // compositor
19542     if (has_multi) {
19543       track_desc = lives_strdup_printf(_("track %s"), (tmp = get_track_name(mt, mt->current_track, mt->aud_track_selected)));
19544       lives_free(tmp);
19545     } else track_desc = (_("selected tracks"));
19546     break;
19547   }
19548   lives_free(tracks);
19549   if (mt->current_fx == mt->avol_fx) {
19550     lives_free(tname);
19551     tname = (_("audio"));
19552   }
19553 
19554   d_print(_("Set parameter values for %s %s on %s at time %s\n"), tname, filter_name, track_desc, mt->timestring);
19555   lives_free(filter_name);
19556   lives_free(tname);
19557   lives_free(track_desc);
19558 
19559   lives_widget_set_sensitive(mt->del_node_button, TRUE);
19560 
19561   if (mt->current_fx == mt->avol_fx && mt->avol_init_event && mt->opts.aparam_view_list) {
19562     LiVESList *slist = mt->audio_draws;
19563     while (slist) {
19564       lives_widget_queue_draw((LiVESWidget *)slist->data);
19565       slist = slist->next;
19566     }
19567   }
19568 
19569   if (mt->current_track >= 0) {
19570     mt_show_current_frame(mt, FALSE); // show full preview in play window
19571   }
19572 
19573   if (!mt->auto_changed) mt->auto_changed = TRUE;
19574   else lives_widget_set_sensitive(mt->backup, TRUE);
19575   if (prefs->mt_auto_back > 0) mt->idlefunc = mt_idle_add(mt);
19576   else if (prefs->mt_auto_back == 0) mt_auto_backup(mt);
19577   mt->changed = TRUE;
19578 }
19579 
19580 //////////////////////////////////////////////////////////////////////////
19581 static int free_tt_key;
19582 static int elist_errors;
19583 
load_layout_map(void)19584 LiVESList *load_layout_map(void) {
19585   // load in a layout "map" for the set, [create mainw->current_layouts_map]
19586 
19587   // the layout.map file maps clip "unique_id" and "handle" stored in the header.lives file and matches it with
19588   // the clip numbers in each layout file (.lay) file for that set
19589 
19590   // [thus a layout could be transferred to another set and the unique_id's/handles altered,
19591   // one could use a layout.map and a layout file as a template for
19592   // rendering many different sets]
19593 
19594   // this is called from recover_layout_map() in saveplay.c, where the map entries entry->list are assigned
19595   // to files (clips)
19596 
19597   char **array;
19598   LiVESList *lmap = NULL;
19599   layout_map *lmap_entry;
19600   uint64_t unique_id;
19601   ssize_t bytes;
19602 
19603   char *lmap_name = lives_build_filename(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME, LAYOUT_MAP_FILENAME, NULL);
19604   char *handle;
19605   char *entry;
19606   char *string;
19607   char *name;
19608 
19609   int len, nm, i;
19610   int fd;
19611   int retval;
19612 
19613   boolean err = FALSE;
19614 
19615   if (!lives_file_test(lmap_name, LIVES_FILE_TEST_EXISTS)) {
19616     lives_free(lmap_name);
19617     return NULL;
19618   }
19619 
19620   do {
19621     retval = 0;
19622     fd = lives_open2(lmap_name, O_RDONLY);
19623     if (fd < 0) {
19624       retval = do_read_failed_error_s_with_retry(lmap_name, NULL);
19625     } else {
19626       while (1) {
19627         bytes = lives_read_le_buffered(fd, &len, 4, TRUE);
19628         if (bytes < 4) {
19629           break;
19630         }
19631         handle = (char *)lives_malloc(len + 1);
19632         bytes = lives_read_buffered(fd, handle, len, TRUE);
19633         if (bytes < len) {
19634           break;
19635         }
19636         lives_memset(handle + len, 0, 1);
19637         bytes = lives_read_le_buffered(fd, &unique_id, 8, TRUE);
19638         if (bytes < 8) {
19639           break;
19640         }
19641         bytes = lives_read_le_buffered(fd, &len, 4, TRUE);
19642         if (bytes < 4) {
19643           break;
19644         }
19645         name = (char *)lives_malloc(len + 1);
19646         bytes = lives_read_buffered(fd, name, len, TRUE);
19647         if (bytes < len) {
19648           break;
19649         }
19650         lives_memset(name + len, 0, 1);
19651         bytes = lives_read_le_buffered(fd, &nm, 4, TRUE);
19652         if (bytes < 4) {
19653           break;
19654         }
19655 
19656         ////////////////////////////////////////////////////////////
19657         // this is one mapping entry (actually only the unique id matters)
19658         lmap_entry = (layout_map *)lives_malloc(sizeof(layout_map));
19659         lmap_entry->handle = handle;
19660         lmap_entry->unique_id = unique_id;
19661         lmap_entry->name = name;
19662         lmap_entry->list = NULL;
19663         ///////////////////////////////////////////
19664 
19665         //////////////////////////////////////////////
19666         // now we read in a list of layouts this clip is used in, and create mainw->current_layouts_map
19667 
19668         // format is:
19669         // layout_file_filename|clip_number|max_frame_used|clip fps|max audio time|audio rate
19670 
19671         // here we only add layout_file_filename
19672 
19673         for (i = 0; i < nm; i++) {
19674           bytes = lives_read_le_buffered(fd, &len, 4, TRUE);
19675           if (bytes < sizint) {
19676             err = TRUE;
19677             break;
19678           }
19679           entry = (char *)lives_malloc(len + 1);
19680           bytes = lives_read_buffered(fd, entry, len, TRUE);
19681           if (bytes < len) {
19682             err = TRUE;
19683             break;
19684           }
19685           lives_memset(entry + len, 0, 1);
19686           string = repl_workdir(entry, FALSE); // allow relocation of workdir
19687           lmap_entry->list = lives_list_append(lmap_entry->list, lives_strdup(string));
19688           array = lives_strsplit(string, "|", -1);
19689           lives_free(string);
19690           mainw->current_layouts_map = lives_list_append_unique(mainw->current_layouts_map, array[0]);
19691           lives_strfreev(array);
19692           lives_free(entry);
19693         }
19694         if (err) break;
19695         lmap = lives_list_append(lmap, lmap_entry);
19696         //g_print("add layout %p %p %p\n", lmap, lmap_entry, lmap_entry->list);
19697       }
19698     }
19699 
19700     ////////////////////////////////////////////////////////////////////////////
19701 
19702     if (fd >= 0) lives_close_buffered(fd);
19703 
19704     if (err) {
19705       retval = do_read_failed_error_s_with_retry(lmap_name, NULL);
19706     }
19707   } while (retval == LIVES_RESPONSE_RETRY);
19708 
19709   lives_free(lmap_name);
19710   return lmap;
19711 }
19712 
19713 
save_layout_map(int * lmap,double * lmap_audio,const char * file,const char * dir)19714 void save_layout_map(int *lmap, double * lmap_audio, const char *file, const char *dir) {
19715   // in the file "layout.map", we map each clip used in the set to which layouts (if any) it is used in
19716   // we also record the highest frame number used and the max audio time; and the current fps of the clip
19717   // and audio rate;
19718 
19719   // one entry per layout file, per clip
19720 
19721   // map format in memory is:
19722 
19723   // this was knocked together very hastily, so it could probably be improved upon
19724 
19725   // layout_file_filename|clip_number|max_frame_used|clip fps|max audio time|audio rate
19726 
19727   // when we save this to a file, we use the (int32)data_length data
19728   // convention
19729   // and the format is:
19730   // 4 bytes handle len
19731   // n bytes handle
19732   // 8 bytes unique_id
19733   // 4 bytes file name len
19734   // n bytes file name
19735   // 4 bytes data len
19736   // n bytes data
19737 
19738   // where data is simply a text dump of the above memory format
19739 
19740   // lmap[] and lmap_audio[] hold the highest frame numbers and highest audio time respectively
19741   // when we save a layout we update these from the current layout
19742 
19743   LiVESList *map, *map_next;
19744 
19745   char *new_entry;
19746   char *map_name = NULL, *ldir = NULL;
19747   char *string;
19748 
19749   off_t size = 0;
19750 
19751   double max_atime;
19752 
19753   boolean written = FALSE;
19754 
19755   boolean write_to_file = TRUE;
19756 
19757   int fd = 0;
19758   int len;
19759   int retval;
19760   int max_frame;
19761 
19762   register int i;
19763 
19764   if (!dir && !(*mainw->set_name)) return;
19765 
19766   if (!file) write_to_file = FALSE;
19767   else {
19768     if (file && (!mainw->current_layouts_map ||
19769                  !lives_list_find(mainw->current_layouts_map, file)))
19770       mainw->current_layouts_map = lives_list_append(mainw->current_layouts_map, lives_strdup(file));
19771     if (!dir) ldir = lives_build_filename(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME, NULL);
19772     else ldir = lives_strdup(dir);
19773 
19774     map_name = lives_build_filename(ldir, LAYOUT_MAP_FILENAME, NULL);
19775 
19776     lives_mkdir_with_parents(ldir, capable->umask);
19777   }
19778 
19779   do {
19780     retval = 0;
19781     if (write_to_file) fd = lives_create_buffered(map_name, DEF_FILE_PERMS);
19782 
19783     if (fd == -1) {
19784       retval = do_write_failed_error_s_with_retry(map_name, lives_strerror(errno));
19785     } else {
19786       THREADVAR(write_failed) = FALSE;
19787 
19788       for (i = 1; i <= MAX_FILES; i++) {
19789         // add or update
19790         if (mainw->files[i]) {
19791 
19792           if (mainw->files[i]->layout_map) {
19793             map = mainw->files[i]->layout_map;
19794             while (map) {
19795               map_next = map->next;
19796               if (map->data) {
19797                 char **array = lives_strsplit((char *)map->data, "|", -1);
19798                 if ((file && !strcmp(array[0], file)) || (!file && !dir &&
19799                     !lives_file_test(array[0], LIVES_FILE_TEST_EXISTS))) {
19800                   // remove prior entry
19801                   lives_free((livespointer)map->data);
19802                   mainw->files[i]->layout_map = lives_list_delete_link(mainw->files[i]->layout_map, map);
19803                   break;
19804                 }
19805                 lives_strfreev(array);
19806               }
19807               map = map_next;
19808             }
19809           }
19810 
19811           if (file && ((lmap && lmap[i] != 0) || (lmap_audio && lmap_audio[i] != 0.))) {
19812             if (lmap) max_frame = lmap[i];
19813             else max_frame = 0;
19814             if (lmap_audio) max_atime = lmap_audio[i];
19815             else max_atime = 0.;
19816 
19817             new_entry = lives_strdup_printf("%s|%d|%d|%.8f|%.8f|%.8f", file, i, max_frame, mainw->files[i]->fps,
19818                                             max_atime, (double)((int)((double)(mainw->files[i]->arps) /
19819                                                 (double)mainw->files[i]->arate * 10000. + .5)) / 10000.);
19820             mainw->files[i]->layout_map = lives_list_prepend(mainw->files[i]->layout_map, new_entry);
19821           }
19822 
19823           if (write_to_file && ((map = mainw->files[i]->layout_map) != NULL)) {
19824             written = TRUE;
19825             len = strlen(mainw->files[i]->handle);
19826             lives_write_le_buffered(fd, &len, 4, TRUE);
19827             lives_write_buffered(fd, mainw->files[i]->handle, len, TRUE);
19828             lives_write_le_buffered(fd, &mainw->files[i]->unique_id, 8, TRUE);
19829             len = strlen(mainw->files[i]->name);
19830             lives_write_le_buffered(fd, &len, 4, TRUE);
19831             lives_write_buffered(fd, mainw->files[i]->name, len, TRUE);
19832             len = lives_list_length(map);
19833             lives_write_le_buffered(fd, &len, 4, TRUE);
19834             while (map) {
19835               string = repl_workdir((char *)map->data, TRUE); // allow relocation of workdir
19836               len = strlen(string);
19837               lives_write_le_buffered(fd, &len, 4, TRUE);
19838               lives_write_buffered(fd, string, len, TRUE);
19839               lives_free(string);
19840               map = map->next;
19841             }
19842           }
19843         }
19844         if (THREADVAR(write_failed)) break;
19845       }
19846       if (THREADVAR(write_failed)) {
19847         retval = do_write_failed_error_s_with_retry(map_name, NULL);
19848         THREADVAR(write_failed) = FALSE;
19849       }
19850 
19851     }
19852     if (retval == LIVES_RESPONSE_RETRY && fd >= 0) lives_close_buffered(fd);
19853   } while (retval == LIVES_RESPONSE_RETRY);
19854 
19855   if (write_to_file && retval != LIVES_RESPONSE_CANCEL) {
19856     lives_close_buffered(fd);
19857     size = sget_file_size(map_name);
19858 
19859     if (size <= 0 || !written) {
19860       LIVES_DEBUG("Removing layout map file: ");
19861       LIVES_DEBUG(map_name);
19862       lives_rm(map_name);
19863     }
19864 
19865     LIVES_DEBUG("Removing layout dir: ");
19866     LIVES_DEBUG(ldir);
19867     lives_rmdir(ldir, FALSE);
19868   }
19869 
19870   if (write_to_file) {
19871     lives_free(ldir);
19872     lives_free(map_name);
19873   }
19874 }
19875 
19876 
add_markers(lives_mt * mt,weed_plant_t * event_list,boolean add_block_ids)19877 void add_markers(lives_mt * mt, weed_plant_t *event_list, boolean add_block_ids) {
19878   // add "block_start" and "block_unordered" markers to a timeline
19879   // this is done when we save an event_list (layout file).
19880   // these markers are removed when the event_list is loaded and displayed
19881 
19882   // if add_block_ids id FALSE, we add block start markers only where blocks are split
19883   // if it is TRUE, we add block start markers for all blocks along with the block uid.
19884   // This helps us keep the same block selected for undo/redo. (work in progress)
19885 
19886   // other hosts are not bound to take notice of "marker" events, so these could be absent or misplaced
19887   // when the layout is reloaded
19888 
19889   LiVESList *track_blocks = NULL;
19890   LiVESList *tlist = mt->video_draws;
19891   LiVESList *blist;
19892   track_rect *block;
19893   weed_timecode_t tc;
19894   LiVESWidget *eventbox;
19895   weed_plant_t *event;
19896   int track;
19897 
19898   while (tlist) {
19899     eventbox = (LiVESWidget *)tlist->data;
19900     block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
19901     track_blocks = lives_list_append(track_blocks, (livespointer)block);
19902     tlist = tlist->next;
19903   }
19904 
19905   event = get_first_event(event_list);
19906 
19907   while (event) {
19908     if (WEED_EVENT_IS_FRAME(event)) {
19909       tc = get_event_timecode(event);
19910       blist = track_blocks;
19911       track = 0;
19912       while (blist) {
19913         block = (track_rect *)blist->data;
19914         if (block) {
19915           if (block->prev && (get_event_timecode(block->prev->end_event) ==
19916                               q_gint64(tc - TICKS_PER_SECOND_DBL / mt->fps, mt->fps))
19917               && (tc == get_event_timecode(block->start_event)) &&
19918               (get_frame_event_clip(block->prev->end_event, track) == get_frame_event_clip(block->start_event, track))) {
19919 
19920             insert_marker_event_at(mt->event_list, event, EVENT_MARKER_BLOCK_START, LIVES_INT_TO_POINTER(track));
19921 
19922             if (mt->audio_draws && lives_list_length(mt->audio_draws) >= track + mt->opts.back_audio_tracks) {
19923               // insert in audio too
19924               insert_marker_event_at(mt->event_list, event, EVENT_MARKER_BLOCK_START,
19925                                      LIVES_INT_TO_POINTER(-track - mt->opts.back_audio_tracks - 1));
19926             }
19927           }
19928           if (!block->ordered)  {
19929             insert_marker_event_at(mt->event_list, event, EVENT_MARKER_BLOCK_UNORDERED, LIVES_INT_TO_POINTER(track));
19930           }
19931           if (event == block->end_event) blist->data = block->next;
19932         }
19933         track++;
19934         blist = blist->next;
19935       }
19936     }
19937     event = get_next_event(event);
19938   }
19939 
19940   if (track_blocks) lives_list_free(track_blocks);
19941 }
19942 
19943 
set_new_set_name(lives_mt * mt)19944 boolean set_new_set_name(lives_mt * mt) {
19945   char new_set_name[MAX_SET_NAME_LEN];
19946 
19947   char *tmp;
19948 
19949   boolean response;
19950   boolean needs_idlefunc = FALSE;
19951 
19952   if (mt && mt->idlefunc > 0) {
19953     lives_source_remove(mt->idlefunc);
19954     mt->idlefunc = 0;
19955     needs_idlefunc = TRUE;
19956   }
19957 
19958   do {
19959     // prompt for a set name, advise user to save set
19960     renamew = create_rename_dialog(4);
19961     lives_widget_show(renamew->dialog);
19962     lives_widget_context_update();
19963     response = lives_dialog_run(LIVES_DIALOG(renamew->dialog));
19964     if (response == LIVES_RESPONSE_CANCEL) {
19965       mainw->cancelled = CANCEL_USER;
19966       if (mt) {
19967         if (needs_idlefunc) {
19968           mt->idlefunc = mt_idle_add(mt);
19969         }
19970         mt_sensitise(mt);
19971       }
19972       return FALSE;
19973     }
19974     lives_snprintf(new_set_name, MAX_SET_NAME_LEN, "%s", (tmp = U82F(lives_entry_get_text(LIVES_ENTRY(renamew->entry)))));
19975     lives_widget_destroy(renamew->dialog);
19976     lives_freep((void **)&renamew);
19977     lives_free(tmp);
19978     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
19979   } while (!is_legal_set_name(new_set_name, FALSE, FALSE));
19980 
19981   lives_snprintf(mainw->set_name, MAX_SET_NAME_LEN, "%s", new_set_name);
19982 
19983   if (!mt->auto_changed) mt->auto_changed = TRUE;
19984   if (prefs->mt_auto_back >= 0) mt_auto_backup(mt);
19985   else lives_widget_set_sensitive(mt->backup, TRUE);
19986   return TRUE;
19987 }
19988 
19989 
on_save_event_list_activate(LiVESMenuItem * menuitem,livespointer user_data)19990 boolean on_save_event_list_activate(LiVESMenuItem * menuitem, livespointer user_data) {
19991   //  here we save a layout list (*.lay) file
19992 
19993   // we dump (serialise) the event_list plant, followed by all of its events
19994   // serialisation method is described in the weed-docs/weedevents spec.
19995   // (serialising of event_lists)
19996 
19997   // loading an event list is simply the reverse of this process
19998 
19999   lives_mt *mt = (lives_mt *)user_data;
20000 
20001   char *filt[] = {"*." LIVES_FILE_EXT_LAYOUT, NULL};
20002 
20003   int *layout_map;
20004 
20005   double *layout_map_audio;
20006 
20007   LiVESWidget *ar_checkbutton;
20008   LiVESWidget *hbox;
20009 
20010   weed_plant_t *event_list;
20011 
20012   char *layout_name;
20013   char *esave_dir;
20014   char *esave_file;
20015 
20016   char xlayout_name[PATH_MAX];
20017 
20018   boolean orig_ar_layout = prefs->ar_layout, ar_layout;
20019   boolean was_set = mainw->was_set;
20020   boolean retval = TRUE;
20021 
20022   boolean needs_idlefunc = FALSE;
20023   boolean did_backup;
20024 
20025   int retval2;
20026   int fd;
20027 
20028   if (!mt) {
20029     event_list = mainw->stored_event_list;
20030     layout_name = mainw->stored_layout_name;
20031   } else {
20032     did_backup = mt->did_backup;
20033     mt_desensitise(mt);
20034     event_list = mt->event_list;
20035     layout_name = mt->layout_name;
20036   }
20037 
20038   // update layout map
20039   layout_map = update_layout_map(event_list);
20040   layout_map_audio = update_layout_map_audio(event_list);
20041 
20042   if (mainw->scrap_file != -1 && layout_map[mainw->scrap_file] != 0) {
20043     // can't save if we have generated frames
20044     do_layout_scrap_file_error();
20045     lives_freep((void **)&layout_map);
20046     lives_freep((void **)&layout_map_audio);
20047     mainw->cancelled = CANCEL_USER;
20048     if (mt) mt_sensitise(mt);
20049     return FALSE;
20050   }
20051 
20052   if (mainw->ascrap_file != -1 && layout_map_audio[mainw->ascrap_file] != 0) {
20053     // can't save if we have recorded audio
20054     do_layout_ascrap_file_error();
20055     lives_freep((void **)&layout_map);
20056     lives_freep((void **)&layout_map_audio);
20057     mainw->cancelled = CANCEL_USER;
20058     if (mt) mt_sensitise(mt);
20059     return FALSE;
20060   }
20061 
20062   if (mt && mt->idlefunc > 0) {
20063     lives_source_remove(mt->idlefunc);
20064     mt->idlefunc = 0;
20065     needs_idlefunc = TRUE;
20066   }
20067 
20068   if (*mainw->set_name) {
20069     char *tmp;
20070     weed_set_string_value(event_list, WEED_LEAF_NEEDS_SET, (tmp = F2U8(mainw->set_name)));
20071     lives_free(tmp);
20072   } else if (mt) {
20073     if (LIVES_IS_INTERACTIVE) {
20074       if (!set_new_set_name(mt)) {
20075         if (needs_idlefunc) {
20076           mt->idlefunc = mt_idle_add(mt);
20077         }
20078         mt_sensitise(mt);
20079         lives_freep((void **)&layout_map);
20080         lives_freep((void **)&layout_map_audio);
20081         return FALSE;
20082       }
20083     } else {
20084       if ((needs_idlefunc || (!did_backup && mt->auto_changed))) {
20085         mt->idlefunc = mt_idle_add(mt);
20086       }
20087       mt_sensitise(mt);
20088       lives_freep((void **)&layout_map);
20089       lives_freep((void **)&layout_map_audio);
20090       return FALSE;
20091     }
20092   }
20093 
20094   esave_dir = lives_build_filename(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME, NULL);
20095   lives_mkdir_with_parents(esave_dir, capable->umask);
20096 
20097   hbox = lives_hbox_new(FALSE, 0);
20098 
20099   ar_checkbutton = make_autoreload_check(LIVES_HBOX(hbox), prefs->ar_layout);
20100   lives_signal_sync_connect(LIVES_GUI_OBJECT(ar_checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
20101                             LIVES_GUI_CALLBACK(toggle_sets_pref),
20102                             (livespointer)PREF_AR_LAYOUT);
20103 
20104   lives_widget_show_all(hbox);
20105 
20106   if (!(*layout_name)) esave_file = choose_file(esave_dir, NULL, filt, LIVES_FILE_CHOOSER_ACTION_SAVE, NULL, hbox);
20107   else esave_file = choose_file(esave_dir, layout_name, filt, LIVES_FILE_CHOOSER_ACTION_SAVE, NULL, hbox);
20108 
20109   ar_layout = prefs->ar_layout;
20110   prefs->ar_layout = orig_ar_layout;
20111 
20112   if (esave_file) {
20113     lives_free(esave_dir);
20114     esave_dir = get_dir(esave_file);
20115   }
20116 
20117   if (!esave_file || !check_storage_space(-1, FALSE)) {
20118     char *cdir;
20119     lives_rmdir(esave_dir, FALSE);
20120 
20121     cdir = lives_build_filename(prefs->workdir, mainw->set_name, NULL);
20122     lives_rmdir(cdir, FALSE);
20123 
20124     lives_freep((void **)&esave_file);
20125     lives_freep((void **)&esave_dir);
20126     lives_freep((void **)&layout_map);
20127     lives_freep((void **)&layout_map_audio);
20128     mainw->was_set = was_set;
20129     if (!was_set) lives_memset(mainw->set_name, 0, 1);
20130     mainw->cancelled = CANCEL_USER;
20131 
20132     if (mt) {
20133       mt_sensitise(mt);
20134       if (needs_idlefunc || (!did_backup && mt->auto_changed)) {
20135         mt->idlefunc = mt_idle_add(mt);
20136       }
20137     }
20138     return FALSE;
20139   }
20140 
20141   esave_file = ensure_extension(esave_file, LIVES_FILE_EXT_LAYOUT);
20142 
20143   lives_snprintf(xlayout_name, PATH_MAX, "%s", esave_file);
20144   get_basename(xlayout_name);
20145 
20146   if (mt) add_markers(mt, mt->event_list, FALSE);
20147 
20148   do {
20149     retval2 = 0;
20150     retval = TRUE;
20151 
20152     fd = lives_create_buffered(esave_file, DEF_FILE_PERMS);
20153     if (fd >= 0) {
20154       do_threaded_dialog(_("Saving layout"), FALSE);
20155 
20156       retval = save_event_list_inner(mt, fd, event_list, NULL);
20157       lives_close_buffered(fd);
20158 
20159       end_threaded_dialog();
20160     }
20161 
20162     if (!retval || fd < 0) {
20163       retval2 = do_write_failed_error_s_with_retry(esave_file, (fd < 0) ? lives_strerror(errno) : NULL);
20164       if (retval2 == LIVES_RESPONSE_CANCEL) {
20165         if (mt) {
20166           if (needs_idlefunc) {
20167             mt->idlefunc = mt_idle_add(mt);
20168           }
20169           mt_sensitise(mt);
20170         }
20171         lives_freep((void **)&esave_file);
20172         lives_freep((void **)&esave_dir);
20173         lives_freep((void **)&layout_map);
20174         lives_freep((void **)&layout_map_audio);
20175         return FALSE;
20176       }
20177     }
20178   } while (retval2 == LIVES_RESPONSE_RETRY);
20179 
20180   if (retval2 != LIVES_RESPONSE_CANCEL) {
20181     lives_snprintf(mainw->recent_file, PATH_MAX, "%s", xlayout_name);
20182     d_print(_("Saved layout to %s\n"), esave_file);
20183   }
20184 
20185   // save layout map
20186   save_layout_map(layout_map, layout_map_audio, esave_file, esave_dir);
20187 
20188   if (mt) mt->changed = FALSE;
20189 
20190   if (!ar_layout) {
20191     prefs->ar_layout = FALSE;
20192     set_string_pref(PREF_AR_LAYOUT, "");
20193     lives_memset(prefs->ar_layout_name, 0, 1);
20194   } else {
20195     prefs->ar_layout = TRUE;
20196     set_string_pref(PREF_AR_LAYOUT, layout_name);
20197     lives_snprintf(prefs->ar_layout_name, PATH_MAX, "%s", xlayout_name);
20198   }
20199 
20200   lives_freep((void **)&esave_file);
20201   lives_freep((void **)&esave_dir);
20202   lives_freep((void **)&layout_map);
20203   lives_freep((void **)&layout_map_audio);
20204 
20205   if (mainw->was_set) recover_layout_cancelled(FALSE);
20206 
20207   if (mt) {
20208     mt->auto_changed = FALSE;
20209     lives_widget_set_sensitive(mt->backup, FALSE);
20210     mt_sensitise(mt);
20211   }
20212 
20213   return TRUE;
20214 }
20215 
20216 // next functions are mainly to do with event_list manipulation
20217 
rec_error_add(char * ebuf,char * msg,int num,weed_timecode_t tc)20218 static char *rec_error_add(char *ebuf, char *msg, int num, weed_timecode_t tc) {
20219   // log an error generated during event_list rectification
20220 
20221   char *tmp;
20222   char *xnew;
20223 
20224   elist_errors++;
20225 
20226   threaded_dialog_spin(0.);
20227   if (tc == -1) xnew = lives_strdup(msg); // missing timecode
20228   else {
20229     if (num == -1) xnew = lives_strdup_printf("%s at timecode %"PRId64"\n", msg, tc);
20230     else xnew = lives_strdup_printf("%s %d at timecode %"PRId64"\n", msg, num, tc);
20231   }
20232   tmp = lives_strconcat(ebuf, xnew, NULL);
20233   //#define SILENT_EVENT_LIST_LOAD
20234 #ifndef SILENT_EVENT_LIST_LOAD
20235   lives_printerr("Rec error: %s", xnew);
20236 #endif
20237   lives_free(ebuf);
20238   lives_free(xnew);
20239   threaded_dialog_spin(0.);
20240   return tmp;
20241 }
20242 
20243 
get_next_tt_key(ttable * trans_table)20244 static int get_next_tt_key(ttable * trans_table) {
20245   int i;
20246   for (i = free_tt_key; i < FX_KEYS_MAX - FX_KEYS_MAX_VIRTUAL; i++) {
20247     if (trans_table[i].in == 0) return i;
20248   }
20249   return -1;
20250 }
20251 
20252 
find_init_event_in_ttable(ttable * trans_table,uint64_t in,boolean normal)20253 void *find_init_event_in_ttable(ttable * trans_table, uint64_t in, boolean normal) {
20254   for (int i = 0; i < FX_KEYS_MAX - FX_KEYS_MAX_VIRTUAL; i++) {
20255     if (normal && trans_table[i].in == in) return trans_table[i].out;
20256 
20257     /// reverse lookup for past filter_map check
20258     if (!normal && (uint64_t)trans_table[i].out == in) return (void *)trans_table[i].in;
20259     if (!trans_table[i].out) return NULL;
20260   }
20261   return NULL;
20262 }
20263 
20264 
remove_nulls_from_filter_map(void ** init_events,int * num_events)20265 static void **remove_nulls_from_filter_map(void **init_events, int *num_events) {
20266   // remove NULLs from filter_map init_events
20267 
20268   // old array may be free()d, return value should be freed() unless NULL
20269   int num_nulls = 0, i, j = 0;
20270   void **new_init_events;
20271 
20272   if (*num_events == 1) return init_events;
20273 
20274   for (i = 0; i < *num_events; i++) if (!init_events[i]) num_nulls++;
20275   if (num_nulls == 0) return init_events;
20276 
20277   *num_events -= num_nulls;
20278 
20279   if (*num_events == 0) new_init_events = NULL;
20280 
20281   else new_init_events = (void **)lives_malloc((*num_events) * sizeof(void *));
20282 
20283   for (i = 0; i < *num_events + num_nulls; i++) if (init_events[i]) new_init_events[j++] = init_events[i];
20284 
20285   lives_free(init_events);
20286 
20287   if (*num_events == 0) *num_events = 1;
20288 
20289   return new_init_events;
20290 }
20291 
20292 
move_init_in_filter_map(lives_mt * mt,weed_plant_t * event_list,weed_plant_t * event,weed_plant_t * ifrom,weed_plant_t * ito,int track,boolean after)20293 void move_init_in_filter_map(lives_mt * mt, weed_plant_t *event_list, weed_plant_t *event, weed_plant_t *ifrom,
20294                              weed_plant_t *ito, int track, boolean after) {
20295   int i, j;
20296   weed_plant_t *deinit_event = weed_get_plantptr_value(ifrom, WEED_LEAF_DEINIT_EVENT, NULL);
20297   void **events_before = NULL;
20298   void **events_after = NULL;
20299   int num_before = 0, j1;
20300   int num_after = 0, j2;
20301   boolean got_after;
20302   void **init_events, **new_init_events;
20303   int num_inits;
20304 
20305   while (event != deinit_event) {
20306     if (!WEED_EVENT_IS_FILTER_MAP(event)) {
20307       event = get_next_event(event);
20308       continue;
20309     }
20310     init_events = weed_get_voidptr_array_counted(event, WEED_LEAF_INIT_EVENTS, &num_inits);
20311     if (!events_before && !events_after) {
20312       j = 0;
20313       for (i = 0; i < num_inits; i++) {
20314         if (init_events[i] == ifrom) continue;
20315         if (init_events[i] != ito && !init_event_is_relevant((weed_plant_t *)init_events[i], track)) continue;
20316         j++;
20317         if (init_events[i] == ito) {
20318           num_before = j - 1 + after;
20319           j = 1;
20320         }
20321       }
20322       num_after = j - after;
20323       if (num_before > 0) events_before = (void **)lives_malloc(num_before * sizeof(void *));
20324       if (num_after > 0) events_after = (void **)lives_malloc(num_after * sizeof(void *));
20325       j1 = j2 = 0;
20326       for (i = 0; i < num_inits; i++) {
20327         if (!init_event_is_relevant((weed_plant_t *)init_events[i], track)) continue;
20328         if (init_events[i] == ifrom) continue;
20329         if (j1 < num_before) {
20330           events_before[j1] = init_events[i];
20331           j1++;
20332         } else {
20333           events_after[j2] = init_events[i];
20334           j2++;
20335         }
20336       }
20337     }
20338     // check to see if we can move event without problem
20339     got_after = FALSE;
20340     for (i = 0; i < num_inits; i++) {
20341       if (init_events[i] == ifrom) continue;
20342       if (!init_event_is_relevant((weed_plant_t *)init_events[i], track)) continue;
20343       if (!got_after && init_event_in_list(events_after, num_after, (weed_plant_t *)init_events[i])) got_after = TRUE;
20344       if (got_after && init_event_in_list(events_before, num_before, (weed_plant_t *)init_events[i])) {
20345         lives_free(init_events);
20346         if (events_before) lives_free(events_before);
20347         if (events_after) lives_free(events_after);
20348         return; // order has changed, give up
20349       }
20350     }
20351     new_init_events = (void **)lives_malloc(num_inits * sizeof(void *));
20352     got_after = FALSE;
20353     j = 0;
20354     for (i = 0; i < num_inits; i++) {
20355       if (init_events[i] == ifrom) continue;
20356       if ((init_event_in_list(events_before, num_before, (weed_plant_t *)init_events[i]) ||
20357            !init_event_is_relevant((weed_plant_t *)init_events[i], track)) &&
20358           !init_event_is_process_last((weed_plant_t *)init_events[i]) && !init_event_is_process_last(ifrom))
20359         new_init_events[j] = init_events[i];
20360       else {
20361         if (!got_after) {
20362           got_after = TRUE;
20363           new_init_events[j] = ifrom;
20364           i--;
20365           j++;
20366           continue;
20367         }
20368         new_init_events[j] = init_events[i];
20369       }
20370       j++;
20371     }
20372     if (j < num_inits) new_init_events[j] = ifrom;
20373     weed_set_voidptr_array(event, WEED_LEAF_INIT_EVENTS, num_inits, new_init_events);
20374     lives_free(new_init_events);
20375     lives_free(init_events);
20376     event = get_next_event(event);
20377   }
20378 
20379   if (events_before) lives_free(events_before);
20380   if (events_after) lives_free(events_after);
20381 }
20382 
20383 
compare_filter_maps(weed_plant_t * fm1,weed_plant_t * fm2,int ctrack)20384 boolean compare_filter_maps(weed_plant_t *fm1, weed_plant_t *fm2, int ctrack) {
20385   // return TRUE if the maps match exactly; if ctrack is !=LIVES_TRACK_ANY,
20386   // then we only compare filter maps where ctrack is an in_track or out_track
20387   int i1, i2, num_events1, num_events2;
20388   void **inits1, **inits2;
20389 
20390   if (!weed_plant_has_leaf(fm1, WEED_LEAF_INIT_EVENTS) && !weed_plant_has_leaf(fm2, WEED_LEAF_INIT_EVENTS)) return TRUE;
20391   if (ctrack == LIVES_TRACK_ANY && ((!weed_plant_has_leaf(fm1, WEED_LEAF_INIT_EVENTS) &&
20392                                      weed_get_voidptr_value(fm2, WEED_LEAF_INIT_EVENTS, NULL)) ||
20393                                     (!weed_plant_has_leaf(fm2, WEED_LEAF_INIT_EVENTS) &&
20394                                      weed_get_voidptr_value(fm1, WEED_LEAF_INIT_EVENTS, NULL)))) return FALSE;
20395 
20396   if (ctrack == LIVES_TRACK_ANY && (weed_plant_has_leaf(fm1, WEED_LEAF_INIT_EVENTS)) &&
20397       weed_plant_has_leaf(fm2, WEED_LEAF_INIT_EVENTS) &&
20398       ((!weed_get_voidptr_value(fm1, WEED_LEAF_INIT_EVENTS, NULL) &&
20399         weed_get_voidptr_value(fm2, WEED_LEAF_INIT_EVENTS, NULL)) ||
20400        (weed_get_voidptr_value(fm1, WEED_LEAF_INIT_EVENTS, NULL) &&
20401         !weed_get_voidptr_value(fm2, WEED_LEAF_INIT_EVENTS, NULL)))) return FALSE;
20402 
20403   num_events1 = weed_leaf_num_elements(fm1, WEED_LEAF_INIT_EVENTS);
20404   num_events2 = weed_leaf_num_elements(fm2, WEED_LEAF_INIT_EVENTS);
20405   if (ctrack == LIVES_TRACK_ANY && num_events1 != num_events2) return FALSE;
20406 
20407   inits1 = weed_get_voidptr_array(fm1, WEED_LEAF_INIT_EVENTS, NULL);
20408   inits2 = weed_get_voidptr_array(fm2, WEED_LEAF_INIT_EVENTS, NULL);
20409 
20410   if (!inits1 && !inits2) return TRUE;
20411 
20412   i2 = 0;
20413 
20414   for (i1 = 0; i1 < num_events1; i1++) {
20415 
20416     if (i2 < num_events2 && init_event_is_process_last((weed_plant_t *)inits2[i2])) {
20417       // for process_last we don't care about the exact order
20418       if (init_event_in_list(inits1, num_events1, (weed_plant_t *)inits2[i2])) {
20419         i2++;
20420         i1--;
20421         continue;
20422       }
20423     }
20424 
20425     if (init_event_is_process_last((weed_plant_t *)inits1[i1])) {
20426       // for process_last we don't care about the exact order
20427       if (init_event_in_list(inits2, num_events2, (weed_plant_t *)inits1[i1])) {
20428         continue;
20429       }
20430     }
20431 
20432     if (ctrack != LIVES_TRACK_ANY) {
20433       if (inits1[i1]) {
20434         if (init_event_is_relevant((weed_plant_t *)inits1[i1], ctrack)) {
20435           if (i2 >= num_events2) {
20436             lives_free(inits1);
20437             lives_free(inits2);
20438             return FALSE;
20439           }
20440         } else continue; // skip this one, it doesn't involve ctrack
20441       } else continue; // skip NULLS
20442     }
20443 
20444     if (i2 < num_events2) {
20445       if (ctrack != LIVES_TRACK_ANY) {
20446         if (!inits2[i2] || !init_event_is_relevant((weed_plant_t *)inits2[i2], ctrack)) {
20447           i2++;
20448           i1--;
20449           continue; // skip this one, it doesn't involve ctrack
20450         }
20451       }
20452 
20453       if (inits1[i1] != inits2[i2]) {
20454         lives_free(inits1);
20455         lives_free(inits2);
20456         return FALSE;
20457       }
20458       i2++;
20459     }
20460   }
20461 
20462   if (i2 < num_events2) {
20463     if (ctrack == LIVES_TRACK_ANY) {
20464       lives_free(inits1);
20465       return FALSE;
20466     }
20467     for (; i2 < num_events2; i2++) {
20468       if (inits2[i2]) {
20469         if (init_event_is_process_last((weed_plant_t *)inits2[i2])) {
20470           // for process_last we don't care about the exact order
20471           if (init_event_in_list(inits1, num_events1, (weed_plant_t *)inits2[i2])) continue;
20472         }
20473 
20474         if (init_event_is_relevant((weed_plant_t *)inits2[i2], ctrack)) {
20475           lives_free(inits1);
20476           lives_free(inits2);
20477           return FALSE;
20478         }
20479       }
20480     }
20481   }
20482   if (inits1) lives_free(inits1);
20483   if (inits2) lives_free(inits2);
20484   return TRUE;
20485 }
20486 
20487 
filter_map_check(ttable * trans_table,weed_plant_t * filter_map,weed_timecode_t deinit_tc,weed_timecode_t fm_tc,char * ebuf)20488 static char *filter_map_check(ttable * trans_table, weed_plant_t *filter_map, weed_timecode_t deinit_tc,
20489                               weed_timecode_t fm_tc, char *ebuf) {
20490   int num_init_events;
20491   void **copy_events, **pinit_events;
20492   int i;
20493   uint64_t *init_events;
20494 
20495   if (!weed_plant_has_leaf(filter_map, WEED_LEAF_INIT_EVENTS)) return ebuf;
20496   // check no deinited events are active
20497   num_init_events = weed_leaf_num_elements(filter_map, WEED_LEAF_INIT_EVENTS);
20498 
20499   if (weed_leaf_seed_type(filter_map, WEED_LEAF_INIT_EVENTS) == WEED_SEED_INT64) {
20500     if (num_init_events == 1 && weed_get_int64_value(filter_map, WEED_LEAF_INIT_EVENTS, NULL) == 0) return ebuf;
20501     init_events = (uint64_t *)(weed_get_int64_array(filter_map, WEED_LEAF_INIT_EVENTS, NULL));
20502   } else {
20503     if (num_init_events == 1 && !weed_get_voidptr_value(filter_map, WEED_LEAF_INIT_EVENTS, NULL)) return ebuf;
20504     pinit_events = weed_get_voidptr_array(filter_map, WEED_LEAF_INIT_EVENTS, NULL);
20505     init_events = (uint64_t *)lives_malloc(num_init_events * sizeof(uint64_t));
20506     for (i = 0; i < num_init_events; i++) init_events[i] = (uint64_t)pinit_events[i];
20507     lives_free(pinit_events);
20508   }
20509 
20510   copy_events = (void **)lives_malloc(num_init_events * sizeof(weed_plant_t *));
20511   for (i = 0; i < num_init_events; i++) {
20512     if (find_init_event_in_ttable(trans_table, init_events[i], FALSE)) copy_events[i] = (void *)init_events[i]; // !!
20513     else {
20514       copy_events[i] = NULL;
20515       ebuf = rec_error_add(ebuf, "Filter_map points to invalid filter_init", -1, fm_tc);
20516     }
20517   }
20518   if (num_init_events > 1) copy_events = remove_nulls_from_filter_map(copy_events, &num_init_events);
20519 
20520   if (copy_events) lives_free(copy_events);
20521   lives_free(init_events);
20522   return ebuf;
20523 }
20524 
20525 
add_filter_deinits(weed_plant_t * event_list,ttable * trans_table,void *** pchains,weed_timecode_t tc,char * ebuf)20526 static char *add_filter_deinits(weed_plant_t *event_list, ttable * trans_table, void ***pchains,
20527                                 weed_timecode_t tc, char *ebuf) {
20528   // add filter deinit events for any remaining active filters
20529   int i, j, num_params;
20530   char *filter_hash;
20531   int idx;
20532   weed_plant_t *init_event, *event;
20533   void **in_pchanges;
20534 
20535   for (i = 0; i < FX_KEYS_MAX - FX_KEYS_MAX_VIRTUAL; i++) {
20536     if (!trans_table[i].out) continue;
20537     if (trans_table[i].in != 0) {
20538       event_list = append_filter_deinit_event(event_list, tc, (init_event = (weed_plant_t *)trans_table[i].out), pchains[i]);
20539       event = get_last_event(event_list);
20540 
20541       filter_hash = weed_get_string_value(init_event, WEED_LEAF_FILTER, NULL);
20542       if ((idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
20543         if ((num_params = weed_leaf_num_elements(init_event, WEED_LEAF_IN_PARAMETERS)) > 0) {
20544           in_pchanges = (void **)lives_malloc(num_params * sizeof(void *));
20545           for (j = 0; j < num_params; j++) {
20546             if (!WEED_EVENT_IS_FILTER_INIT((weed_plant_t *)pchains[i][j]))
20547               in_pchanges[j] = (weed_plant_t *)pchains[i][j];
20548             else in_pchanges[j] = NULL;
20549           }
20550           weed_set_voidptr_array(event, WEED_LEAF_IN_PARAMETERS, num_params, in_pchanges); // set array to last param_changes
20551           lives_free(in_pchanges);
20552           lives_free(pchains[i]);
20553         }
20554       }
20555       lives_free(filter_hash);
20556       ebuf = rec_error_add(ebuf, "Added missing filter_deinit", -1, tc);
20557     }
20558   }
20559   return ebuf;
20560 }
20561 
20562 
add_null_filter_map(weed_plant_t * event_list,weed_plant_t * last_fm,weed_timecode_t tc,char * ebuf)20563 static char *add_null_filter_map(weed_plant_t *event_list, weed_plant_t *last_fm, weed_timecode_t tc, char *ebuf) {
20564   int num_events;
20565 
20566   if (!weed_plant_has_leaf(last_fm, WEED_LEAF_INIT_EVENTS)) return ebuf;
20567 
20568   num_events = weed_leaf_num_elements(last_fm, WEED_LEAF_INIT_EVENTS);
20569   if (num_events == 1 && !weed_get_voidptr_value(last_fm, WEED_LEAF_INIT_EVENTS, NULL)) return ebuf;
20570 
20571   event_list = append_filter_map_event(event_list, tc, NULL);
20572 
20573   ebuf = rec_error_add(ebuf, "Added missing empty filter_map", -1, tc);
20574   return ebuf;
20575 }
20576 
20577 
duplicate_frame_at(weed_plant_t * event_list,weed_plant_t * src_frame,weed_timecode_t tc)20578 static weed_plant_t *duplicate_frame_at(weed_plant_t *event_list, weed_plant_t *src_frame, weed_timecode_t tc) {
20579   // tc should be > src_frame tc : i.e. copy is forward in time because insert_frame_event_at searches forward
20580   int *clips;
20581   int64_t *frames;
20582   int numframes;
20583 
20584   clips = weed_get_int_array_counted(src_frame, WEED_LEAF_CLIPS, &numframes);
20585   if (!numframes) return src_frame;
20586 
20587   frames = weed_get_int64_array(src_frame, WEED_LEAF_FRAMES, NULL);
20588 
20589   event_list = insert_frame_event_at(event_list, tc, numframes, clips, frames, &src_frame);
20590 
20591   lives_free(clips);
20592   lives_free(frames);
20593   return get_frame_event_at(event_list, tc, src_frame, TRUE);
20594 }
20595 
20596 
20597 static LiVESList *atrack_list;
20598 
add_atrack_to_list(int track,int clip)20599 static void add_atrack_to_list(int track, int clip) {
20600   // keep record of audio tracks so we can add closures if missing
20601   LiVESList *alist = atrack_list;
20602   char *entry;
20603   char **array;
20604 
20605   while (alist) {
20606     entry = (char *)alist->data;
20607     array = lives_strsplit(entry, "|", -1);
20608     if (atoi(array[0]) == track) {
20609       lives_free((livespointer)alist->data);
20610       alist->data = lives_strdup_printf("%d|%d", track, clip);
20611       lives_strfreev(array);
20612       return;
20613     }
20614     lives_strfreev(array);
20615     alist = alist->next;
20616   }
20617   atrack_list = lives_list_append(atrack_list, lives_strdup_printf("%d|%d", track, clip));
20618 }
20619 
20620 
remove_atrack_from_list(int track)20621 static void remove_atrack_from_list(int track) {
20622   // keep record of audio tracks so we can add closures if missing
20623   LiVESList *alist = atrack_list, *alist_next;
20624   char *entry;
20625   char **array;
20626 
20627   while (alist) {
20628     alist_next = alist->next;
20629     entry = (char *)alist->data;
20630     array = lives_strsplit(entry, "|", -1);
20631     if (atoi(array[0]) == track) {
20632       atrack_list = lives_list_remove(atrack_list, entry);
20633       lives_strfreev(array);
20634       lives_free(entry);
20635       return;
20636     }
20637     lives_strfreev(array);
20638     alist = alist_next;
20639   }
20640 }
20641 
20642 
add_missing_atrack_closers(weed_plant_t * event_list,double fps,char * ebuf)20643 static char *add_missing_atrack_closers(weed_plant_t *event_list, double fps, char *ebuf) {
20644   LiVESList *alist = atrack_list;
20645   char *entry;
20646   char **array;
20647   int i = 0;
20648 
20649   int *aclips;
20650   double *aseeks;
20651 
20652   weed_plant_t *last_frame;
20653   int num_atracks;
20654   weed_timecode_t tc;
20655 
20656   if (!atrack_list) return ebuf;
20657 
20658   num_atracks = lives_list_length(atrack_list) * 2;
20659 
20660   aclips = (int *)lives_malloc(num_atracks * sizint);
20661   aseeks = (double *)lives_malloc(num_atracks * sizdbl);
20662 
20663   last_frame = get_last_frame_event(event_list);
20664   tc = get_event_timecode(last_frame);
20665 
20666   if (!is_blank_frame(last_frame, TRUE)) {
20667     weed_plant_t *shortcut = last_frame;
20668     event_list = insert_blank_frame_event_at(event_list, q_gint64(tc + 1. / TICKS_PER_SECOND_DBL, fps), &shortcut);
20669   }
20670 
20671   while (alist) {
20672     entry = (char *)alist->data;
20673     array = lives_strsplit(entry, "|", -1);
20674     aclips[i] = atoi(array[0]);
20675     aclips[i + 1] = atoi(array[1]);
20676     aseeks[i] = 0.;
20677     aseeks[i + 1] = 0.;
20678     lives_strfreev(array);
20679     if (aclips[i] >= 0) ebuf = rec_error_add(ebuf, "Added missing audio closure", aclips[i], tc);
20680     else ebuf = rec_error_add(ebuf, "Added missing audio closure to backing track", -aclips[i], tc);
20681     i += 2;
20682     alist = alist->next;
20683   }
20684 
20685   weed_set_int_array(last_frame, WEED_LEAF_AUDIO_CLIPS, num_atracks, aclips);
20686   weed_set_double_array(last_frame, WEED_LEAF_AUDIO_SEEKS, num_atracks, aseeks);
20687 
20688   lives_free(aclips);
20689   lives_free(aseeks);
20690 
20691   lives_list_free_all(&atrack_list);
20692 
20693   return ebuf;
20694 }
20695 
20696 
get_int64_array_convert(weed_plant_t * plant,const char * key)20697 static int64_t *get_int64_array_convert(weed_plant_t *plant, const char *key) {
20698   if (weed_leaf_seed_type(plant, key) == WEED_SEED_INT) {
20699     int nvals;
20700     int *ivals = weed_get_int_array_counted(plant, key, &nvals);
20701     if (!nvals) return NULL;
20702     int64_t *i64vals = lives_calloc(nvals, 8);
20703     for (int i = 0; i < nvals; i++) i64vals[i] = (int64_t)ivals[i];
20704     lives_free(ivals);
20705     weed_leaf_delete(plant, key);
20706     weed_set_int64_array(plant, key, nvals, i64vals);
20707     return i64vals;
20708   }
20709   return weed_get_int64_array(plant, key, NULL);
20710 }
20711 
20712 
event_list_rectify(lives_mt * mt,weed_plant_t * event_list)20713 boolean event_list_rectify(lives_mt * mt, weed_plant_t *event_list) {
20714   // check and reassemble a newly loaded event_list
20715   // reassemply consists of matching init_event(s) to event_id's
20716   // we also rebuild our param_change chains (WEED_LEAF_IN_PARAMETERS in filter_init and filter_deinit,
20717   // and WEED_LEAF_NEXT_CHANGE and WEED_LEAF_PREV_CHANGE
20718   // in other param_change events)
20719 
20720   // The checking done is quite sophisticated, and can correct many errors in badly-formed event_lists
20721 
20722   weed_plant_t **ptmpls;
20723   weed_plant_t **ctmpls;
20724 
20725   weed_plant_t *event = get_first_event(event_list), *event_next;
20726   weed_plant_t *shortcut = NULL;
20727   weed_plant_t *last_frame_event;
20728   weed_plant_t *last_filter_map = NULL;
20729   weed_plant_t *filter = NULL;
20730   weed_plant_t *last_event;
20731 
20732   weed_timecode_t tc = 0, last_tc = 0;
20733   weed_timecode_t last_frame_tc = -1;
20734   weed_timecode_t last_deinit_tc = -1;
20735   weed_timecode_t last_filter_map_tc = -1;
20736   weed_timecode_t cur_tc = 0;
20737 
20738   char *ebuf = lives_strdup("");
20739   char *host_tag_s;
20740   char *filter_hash;
20741   char *bit1 = lives_strdup(""), *bit2 = NULL, *bit3 = lives_strdup("."), *msg;
20742   int64_t *frame_index, *new_frame_index;
20743   int *inct, *outct;
20744   int *clip_index, *aclip_index, *new_aclip_index;
20745   int *new_clip_index;
20746 
20747   int i, idx, filter_idx, j;
20748   int host_tag;
20749   int num_ctmpls, num_inct, num_outct;
20750   int pnum, thisct;
20751   int num_init_events;
20752   int num_params;
20753   int num_tracks, num_atracks;
20754   int last_valid_frame;
20755   int marker_type;
20756   int ev_count = 0;
20757   int api_version = 100;
20758   int event_type;
20759 
20760   boolean check_filter_map = FALSE;
20761   boolean was_deleted = FALSE;
20762   boolean was_moved;
20763   boolean missing_clips = FALSE, missing_frames = FALSE;
20764   boolean has_event_type;
20765 
20766   void *init_event;
20767   void **new_init_events;
20768   void **in_pchanges;
20769   void **pchains[FX_KEYS_MAX - FX_KEYS_MAX_VIRTUAL]; // parameter chains
20770 
20771   double fps = 0.;
20772   double *aseek_index, *new_aseek_index;
20773 
20774   uint64_t event_id;
20775 
20776   uint64_t *init_events;
20777 
20778   ttable trans_table[FX_KEYS_MAX - FX_KEYS_MAX_VIRTUAL]; // translation table for init_events
20779 
20780   if (weed_plant_has_leaf(event_list, WEED_LEAF_FPS)) fps = weed_get_double_value(event_list, WEED_LEAF_FPS, NULL);
20781 
20782   api_version = weed_get_int_value(event_list, WEED_LEAF_WEED_EVENT_API_VERSION, NULL);
20783 
20784   if (mt) mt->layout_prompt = FALSE;
20785 
20786   for (i = 0; i < FX_KEYS_MAX - FX_KEYS_MAX_VIRTUAL; i++) {
20787     trans_table[i].in = 0;
20788     trans_table[i].out = NULL;
20789   }
20790 
20791   free_tt_key = 0;
20792 
20793   atrack_list = NULL;
20794 
20795   while (event) {
20796     was_deleted = FALSE;
20797     event_next = get_next_event(event);
20798     if (!weed_plant_has_leaf(event, WEED_LEAF_TIMECODE)) {
20799       ebuf = rec_error_add(ebuf, "Event has no timecode", weed_get_plant_type(event), -1);
20800       delete_event(event_list, event);
20801       event = event_next;
20802       continue;
20803     }
20804     tc = get_event_timecode(event);
20805     if (fps != 0.) {
20806       tc = q_gint64(tc + TICKS_PER_SECOND_DBL / (2. * fps) - 1, fps);
20807       weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
20808     }
20809     ev_count++;
20810     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%d|", ev_count);
20811     if ((ev_count % 100) == 0) threaded_dialog_spin(0.);
20812 
20813     if (weed_get_plant_type(event) != WEED_PLANT_EVENT) {
20814       ebuf = rec_error_add(ebuf, "Invalid plant type", weed_get_plant_type(event), tc);
20815       delete_event(event_list, event);
20816       event = event_next;
20817       continue;
20818     }
20819     if (tc < last_tc) {
20820       break_me("oo event in event_list_rectify");
20821       ebuf = rec_error_add(ebuf, "Out of order event", -1, tc);
20822       delete_event(event_list, event);
20823       event = event_next;
20824       continue;
20825     }
20826     has_event_type = TRUE;
20827     if (!weed_plant_has_leaf(event, WEED_LEAF_EVENT_TYPE)) {
20828       has_event_type = FALSE;
20829       if (api_version < 122) {
20830         if (weed_plant_has_leaf(event, WEED_LEAF_HINT)) {
20831           weed_leaf_copy(event, WEED_LEAF_EVENT_TYPE, event, WEED_LEAF_HINT);
20832           weed_leaf_delete(event, WEED_LEAF_HINT);
20833           has_event_type = TRUE;
20834         }
20835       }
20836     }
20837     if (!has_event_type) {
20838       ebuf = rec_error_add(ebuf, "Event has no event_type", weed_get_plant_type(event), tc);
20839       delete_event(event_list, event);
20840       event = event_next;
20841       continue;
20842     }
20843 
20844     event_type = get_event_type(event);
20845 
20846     switch (event_type) {
20847     case WEED_EVENT_TYPE_FILTER_INIT:
20848 #ifdef DEBUG_TTABLE
20849       g_print("\n\ngot filter init %p\n", event);
20850 #endif
20851       // set in table
20852       if (!weed_plant_has_leaf(event, WEED_LEAF_EVENT_ID)) {
20853         ebuf = rec_error_add(ebuf, "Filter_init missing event_id", -1, tc);
20854         /* delete_event(event_list, event); */
20855         /* was_deleted = TRUE; */
20856         weed_set_int64_value(event, WEED_LEAF_EVENT_ID, (uint64_t)((void *)event));
20857       }
20858 
20859       if (1) {
20860         if (!weed_plant_has_leaf(event, WEED_LEAF_FILTER)) {
20861           ebuf = rec_error_add(ebuf, "Filter_init missing filter", -1, tc);
20862           delete_event(event_list, event);
20863           was_deleted = TRUE;
20864         } else {
20865           filter_hash = weed_get_string_value(event, WEED_LEAF_FILTER, NULL);
20866           if ((filter_idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
20867             filter = get_weed_filter(filter_idx);
20868             if (weed_plant_has_leaf(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES)) {
20869               if (!weed_plant_has_leaf(event, WEED_LEAF_IN_COUNT)) {
20870                 ebuf = rec_error_add(ebuf, "Filter_init missing filter", -1, tc);
20871                 delete_event(event_list, event);
20872                 was_deleted = TRUE;
20873               } else {
20874                 num_ctmpls = weed_leaf_num_elements(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES);
20875                 num_inct = weed_leaf_num_elements(event, WEED_LEAF_IN_COUNT);
20876                 if (num_ctmpls != num_inct) {
20877                   ebuf = rec_error_add(ebuf, "Filter_init has invalid in_count", -1, tc);
20878                   delete_event(event_list, event);
20879                   was_deleted = TRUE;
20880                 } else {
20881                   inct = weed_get_int_array(event, WEED_LEAF_IN_COUNT, NULL);
20882                   ctmpls = weed_get_plantptr_array(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, NULL);
20883                   for (i = 0; i < num_ctmpls; i++) {
20884                     thisct = inct[i];
20885                     if (thisct == 0 && !weed_chantmpl_is_optional(ctmpls[i])) {
20886                       ebuf = rec_error_add(ebuf, "Filter_init disables a non-optional in channel", i, tc);
20887                       delete_event(event_list, event);
20888                       was_deleted = TRUE;
20889                     } else {
20890                       if (thisct > 1 && (!weed_plant_has_leaf(ctmpls[i], WEED_LEAF_MAX_REPEATS) ||
20891                                          (weed_get_int_value(ctmpls[i], WEED_LEAF_MAX_REPEATS, NULL) > 0 &&
20892                                           weed_get_int_value(ctmpls[i], WEED_LEAF_MAX_REPEATS, NULL) < thisct))) {
20893                         ebuf = rec_error_add(ebuf, "Filter_init has too many repeats of in channel", i, tc);
20894                         delete_event(event_list, event);
20895                         was_deleted = TRUE;
20896                       }
20897                     }
20898                   }
20899 
20900                   lives_free(inct);
20901                   lives_free(ctmpls);
20902 
20903                   if (!was_deleted) {
20904                     num_ctmpls = weed_leaf_num_elements(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES);
20905                     num_outct = weed_leaf_num_elements(event, WEED_LEAF_OUT_COUNT);
20906                     if (num_ctmpls != num_outct) {
20907                       ebuf = rec_error_add(ebuf, "Filter_init has invalid out_count", -1, tc);
20908                       delete_event(event_list, event);
20909                       was_deleted = TRUE;
20910                     } else {
20911                       outct = weed_get_int_array(event, WEED_LEAF_OUT_COUNT, NULL);
20912                       ctmpls = weed_get_plantptr_array(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, NULL);
20913                       for (i = 0; i < num_ctmpls; i++) {
20914                         thisct = outct[i];
20915                         if (thisct == 0 && !weed_chantmpl_is_optional(ctmpls[i])) {
20916                           ebuf = rec_error_add(ebuf, "Filter_init disables a non-optional out channel", i, tc);
20917                           delete_event(event_list, event);
20918                           was_deleted = TRUE;
20919                         } else {
20920                           if (thisct > 1 && (!weed_plant_has_leaf(ctmpls[i], WEED_LEAF_MAX_REPEATS) ||
20921                                              (weed_get_int_value(ctmpls[i], WEED_LEAF_MAX_REPEATS, NULL) > 0 &&
20922                                               weed_get_int_value(ctmpls[i], WEED_LEAF_MAX_REPEATS, NULL) < thisct))) {
20923                             ebuf = rec_error_add(ebuf, "Filter_init has too many repeats of out channel", i, tc);
20924                             delete_event(event_list, event);
20925                             was_deleted = TRUE;
20926                           } else {
20927                             if (weed_plant_has_leaf(event, WEED_LEAF_IN_TRACKS)) {
20928                               int ntracks;
20929                               int *trax = weed_get_int_array_counted(event, WEED_LEAF_IN_TRACKS, &ntracks);
20930                               for (i = 0; i < ntracks; i++) {
20931                                 if (trax[i] >= 0 && !has_video_chans_in(filter, FALSE)) {
20932                                   // TODO ** inform user
20933                                   if (mt && !mt->opts.pertrack_audio) {
20934                                     lives_widget_set_sensitive(mt->fx_region_2a, TRUE);
20935                                     mt->opts.pertrack_audio = TRUE;
20936                                   } else force_pertrack_audio = TRUE;
20937                                 }
20938 
20939                                 if (trax[i] == -1) {
20940                                   // TODO ** inform user
20941                                   if (mt && mt->opts.back_audio_tracks == 0) {
20942                                     mt->opts.back_audio_tracks = 1;
20943                                     ebuf = rec_error_add(ebuf, "Adding backing audio", -1, tc);
20944                                   } else force_backing_tracks = 1;
20945                                 }
20946                               }
20947 
20948                               lives_free(trax);
20949                               trax = weed_get_int_array_counted(event, WEED_LEAF_OUT_TRACKS, &ntracks);
20950                               for (i = 0; i < ntracks; i++) {
20951                                 if (trax[i] >= 0 && !has_video_chans_out(filter, FALSE)) {
20952                                   // TODO ** inform user
20953                                   if (mt && !mt->opts.pertrack_audio) {
20954                                     lives_widget_set_sensitive(mt->fx_region_2a, TRUE);
20955                                     mt->opts.pertrack_audio = TRUE;
20956                                   } else force_pertrack_audio = TRUE;
20957                                 }
20958                                 if (trax[i] == -1) {
20959                                   // TODO ** inform user
20960                                   if (mt && mt->opts.back_audio_tracks == 0) {
20961                                     mt->opts.back_audio_tracks = 1;
20962                                   } else force_backing_tracks = 1;
20963                                 }
20964                               }
20965                               lives_free(trax);
20966                             }
20967                           }
20968                           // all tests passed
20969                           if (tc == 0) {
20970                             if (mt && mt->avol_fx == -1) {
20971                               // check if it can be a filter delegate
20972                               LiVESList *clist = mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].list;
20973                               while (clist) {
20974                                 if (LIVES_POINTER_TO_INT(clist->data) == filter_idx) {
20975                                   mt->avol_fx = filter_idx;
20976                                   mt->avol_init_event = event;
20977                                   break;
20978                                 }
20979                                 clist = clist->next;
20980 				// *INDENT-OFF*
20981                               }}}}}
20982                       lives_free(outct);
20983                       lives_free(ctmpls);
20984                     }}}}}
20985 	    // *INDENT-ON*
20986           } else {
20987             lives_printerr("Layout contains unknown filter %s\n", filter_hash);
20988             ebuf = rec_error_add(ebuf, "Layout contains unknown filter", -1, tc);
20989             delete_event(event_list, event);
20990             was_deleted = TRUE;
20991             if (mt) mt->layout_prompt = TRUE;
20992           }
20993           lives_free(filter_hash);
20994           if (!was_deleted) {
20995             host_tag = get_next_tt_key(trans_table) + FX_KEYS_MAX_VIRTUAL + 1;
20996             if (host_tag == -1) {
20997               ebuf = rec_error_add(ebuf, "Fatal: too many active effects", FX_KEYS_MAX - FX_KEYS_MAX_VIRTUAL, tc);
20998               end_threaded_dialog();
20999               return FALSE;
21000             }
21001             host_tag_s = lives_strdup_printf("%d", host_tag);
21002             weed_set_string_value(event, WEED_LEAF_HOST_TAG, host_tag_s);
21003             lives_free(host_tag_s);
21004 
21005             if (weed_leaf_seed_type(event, WEED_LEAF_EVENT_ID) == WEED_SEED_INT64)
21006               event_id = (uint64_t)(weed_get_int64_value(event, WEED_LEAF_EVENT_ID, NULL));
21007             else
21008               event_id = (uint64_t)((weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_EVENT_ID, NULL));
21009 
21010             trans_table[(idx = host_tag - FX_KEYS_MAX_VIRTUAL - 1)].in = event_id;
21011             trans_table[idx].out = event;
21012 #ifdef DEBUG_TTABLE
21013             g_print("adding lookup %"PRIu64" -> %p\n", event_id, event);
21014 #endif
21015 
21016             // use pchain array
21017             if (weed_plant_has_leaf(event, WEED_LEAF_IN_PARAMETERS)) {
21018               num_params = weed_leaf_num_elements(event, WEED_LEAF_IN_PARAMETERS);
21019               pchains[idx] = (void **)lives_malloc((num_params + 1) * sizeof(void *));
21020               in_pchanges = (void **)lives_malloc(num_params * sizeof(void *));
21021               for (i = 0; i < num_params; i++) {
21022                 pchains[idx][i] = event;
21023                 in_pchanges[i] = NULL;
21024               }
21025               pchains[idx][i] = NULL;
21026               // set all to NULL, we will re-fill as we go along
21027               weed_leaf_delete(event, WEED_LEAF_IN_PARAMETERS);
21028               weed_set_voidptr_array(event, WEED_LEAF_IN_PARAMETERS, num_params, in_pchanges);
21029               lives_free(in_pchanges);
21030 	      // *INDENT-OFF*
21031             }}}}
21032       // *INDENT-ON*
21033 
21034       break;
21035     case WEED_EVENT_TYPE_FILTER_DEINIT:
21036 
21037       // update "init_event" from table, remove entry; we will check filter_map at end of tc
21038       if (!weed_plant_has_leaf(event, WEED_LEAF_INIT_EVENT)) {
21039         ebuf = rec_error_add(ebuf, "Filter_deinit missing init_event", -1, tc);
21040         delete_event(event_list, event);
21041         was_deleted = TRUE;
21042       } else {
21043         if (weed_leaf_seed_type(event, WEED_LEAF_INIT_EVENT) == WEED_SEED_INT64)
21044           event_id = (uint64_t)(weed_get_int64_value(event, WEED_LEAF_INIT_EVENT, NULL));
21045         else
21046           event_id = (uint64_t)((weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL));
21047 
21048 #ifdef DEBUG_TTABLE
21049         g_print("looking for %"PRIu64" in ttable\n", event_id);
21050 #endif
21051         init_event = find_init_event_in_ttable(trans_table, event_id, TRUE);
21052 
21053         if (!init_event) {
21054           ebuf = rec_error_add(ebuf, "Filter_deinit has invalid init_event", -1, tc);
21055           delete_event(event_list, event);
21056           was_deleted = TRUE;
21057         } else {
21058           weed_leaf_delete((weed_plant_t *)init_event, WEED_LEAF_DEINIT_EVENT);
21059           weed_set_plantptr_value((weed_plant_t *)init_event, WEED_LEAF_DEINIT_EVENT, event);
21060 
21061           host_tag_s = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG, NULL);
21062           host_tag = atoi(host_tag_s);
21063           lives_free(host_tag_s);
21064           trans_table[(idx = host_tag - FX_KEYS_MAX_VIRTUAL - 1)].in = 0;
21065           if (idx < free_tt_key) free_tt_key = idx;
21066           weed_leaf_delete(event, WEED_LEAF_INIT_EVENT);
21067           weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENT, init_event);
21068           check_filter_map = TRUE;
21069           last_deinit_tc = tc;
21070 
21071           filter_hash = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_FILTER, NULL);
21072           if ((filter_idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
21073             if ((num_params = weed_leaf_num_elements((weed_plant_t *)init_event, WEED_LEAF_IN_PARAMETERS)) > 0) {
21074               in_pchanges = (void **)lives_malloc(num_params * sizeof(void *));
21075               for (i = 0; i < num_params; i++) {
21076                 if (!WEED_EVENT_IS_FILTER_INIT((weed_plant_t *)pchains[idx][i]))
21077                   in_pchanges[i] = (weed_plant_t *)pchains[idx][i];
21078                 else in_pchanges[i] = NULL;
21079               }
21080               weed_leaf_delete(event, WEED_LEAF_IN_PARAMETERS);
21081               weed_set_voidptr_array(event, WEED_LEAF_IN_PARAMETERS, num_params, in_pchanges); // set array to last param_changes
21082               lives_free(in_pchanges);
21083               lives_free(pchains[idx]);
21084             }
21085           }
21086           lives_free(filter_hash);
21087         }
21088       }
21089       break;
21090     case WEED_EVENT_TYPE_FILTER_MAP:
21091       // update "init_events" from table
21092       if (weed_plant_has_leaf(event, WEED_LEAF_INIT_EVENTS)) {
21093         num_init_events = weed_leaf_num_elements(event, WEED_LEAF_INIT_EVENTS);
21094         if (weed_leaf_seed_type(event, WEED_LEAF_INIT_EVENTS) == WEED_SEED_INT64)
21095           init_events = (uint64_t *)weed_get_int64_array(event, WEED_LEAF_INIT_EVENTS, NULL);
21096         else  {
21097           void **pinit_events = weed_get_voidptr_array(event, WEED_LEAF_INIT_EVENTS, NULL);
21098           init_events = (uint64_t *)lives_malloc(num_init_events * sizeof(uint64_t));
21099           for (i = 0; i < num_init_events; i++) init_events[i] = (uint64_t)pinit_events[i];
21100           lives_free(pinit_events);
21101         }
21102 
21103         new_init_events = (void **)lives_malloc(num_init_events * sizeof(void *));
21104         for (i = 0; i < num_init_events; i++) {
21105           event_id = (uint64_t)init_events[i];
21106           if (event_id != 0) {
21107             init_event = find_init_event_in_ttable(trans_table, event_id, TRUE);
21108 #ifdef DEBUG_TTABLE
21109             g_print("looking for %"PRIu64" in ttable, got %p\n", event_id, init_event);
21110 #endif
21111             if (!init_event) {
21112               ebuf = rec_error_add(ebuf, "Filter_map has invalid init_event", -1, tc);
21113               new_init_events[i] = NULL;
21114             } else new_init_events[i] = init_event;
21115           } else new_init_events[i] = NULL;
21116         }
21117         new_init_events = remove_nulls_from_filter_map(new_init_events, &num_init_events);
21118 
21119         weed_leaf_delete(event, WEED_LEAF_INIT_EVENTS);
21120 
21121         if (!new_init_events) weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENTS, NULL);
21122         else {
21123           weed_set_voidptr_array(event, WEED_LEAF_INIT_EVENTS, num_init_events, new_init_events);
21124 
21125           for (i = 0; i < num_init_events; i++) {
21126             if (init_event_is_process_last((weed_plant_t *)new_init_events[i])) {
21127               // reposition process_last events to the end
21128               add_init_event_to_filter_map(event, (weed_plant_t *)new_init_events[i], NULL);
21129             }
21130           }
21131           lives_free(new_init_events);
21132         }
21133         lives_free(init_events);
21134       } else {
21135         weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENTS, NULL);
21136       }
21137       if (last_filter_map) {
21138         if (compare_filter_maps(last_filter_map, event, LIVES_TRACK_ANY)) {
21139           // filter map is identical to prior one, we can remove this one
21140           delete_event(event_list, event);
21141           was_deleted = TRUE;
21142         }
21143       } else if (weed_leaf_num_elements(event, WEED_LEAF_INIT_EVENTS) == 1 &&
21144                  !weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENTS, NULL)) {
21145         delete_event(event_list, event);
21146         was_deleted = TRUE;
21147       }
21148       if (!was_deleted) last_filter_map = event;
21149 
21150       break;
21151     case WEED_EVENT_TYPE_PARAM_CHANGE:
21152       if (!weed_plant_has_leaf(event, WEED_LEAF_INDEX)) {
21153         ebuf = rec_error_add(ebuf, "Param_change has no index", -1, tc);
21154         delete_event(event_list, event);
21155         was_deleted = TRUE;
21156       } else {
21157         if (!weed_plant_has_leaf(event, WEED_LEAF_VALUE)) {
21158           ebuf = rec_error_add(ebuf, "Param_change has no value", -1, tc);
21159           delete_event(event_list, event);
21160           was_deleted = TRUE;
21161         } else {
21162           if (!weed_plant_has_leaf(event, WEED_LEAF_INIT_EVENT)) {
21163             ebuf = rec_error_add(ebuf, "Param_change has no init_event", -1, tc);
21164             delete_event(event_list, event);
21165             was_deleted = TRUE;
21166           } else {
21167             if (weed_leaf_seed_type(event, WEED_LEAF_INIT_EVENT) == WEED_SEED_INT64)
21168               event_id = (uint64_t)(weed_get_int64_value(event, WEED_LEAF_INIT_EVENT, NULL));
21169             else
21170               event_id = (uint64_t)((weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL));
21171 
21172 #ifdef DEBUG_TTABLE
21173             g_print("pc looking for %"PRIu64" in ttable %d\n", event_id, error);
21174 #endif
21175 
21176             if ((init_event = find_init_event_in_ttable(trans_table, event_id, TRUE)) == NULL) {
21177               ebuf = rec_error_add(ebuf, "Param_change has invalid init_event", -1, tc);
21178               delete_event(event_list, event);
21179               was_deleted = TRUE;
21180             } else {
21181               filter_hash = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_FILTER, NULL);
21182               if ((filter_idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
21183                 filter = get_weed_filter(filter_idx);
21184                 pnum = weed_get_int_value(event, WEED_LEAF_INDEX, NULL);
21185                 if (pnum < 0 || pnum >= num_in_params(filter, FALSE, FALSE) ||
21186                     pnum >= (num_params = weed_leaf_num_elements((weed_plant_t *)init_event, WEED_LEAF_IN_PARAMETERS))) {
21187                   ebuf = rec_error_add(ebuf, "Param_change has invalid index", pnum, tc);
21188                   delete_event(event_list, event);
21189                   was_deleted = TRUE;
21190                 } else {
21191                   ptmpls = weed_get_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, NULL);
21192                   if (!weed_plant_has_leaf(event, WEED_LEAF_VALUE)) {
21193                     ebuf = rec_error_add(ebuf, "Param_change has no value with index", pnum, tc);
21194                     delete_event(event_list, event);
21195                     was_deleted = TRUE;
21196                   } else {
21197                     if (weed_leaf_seed_type(event, WEED_LEAF_VALUE) != weed_leaf_seed_type(ptmpls[pnum], WEED_LEAF_DEFAULT)) {
21198                       ebuf = rec_error_add(ebuf, "Param_change has invalid seed type with index", pnum, tc);
21199                       delete_event(event_list, event);
21200                       was_deleted = TRUE;
21201                     } else {
21202                       // all checks passed
21203                       host_tag_s = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG, NULL);
21204                       host_tag = atoi(host_tag_s);
21205                       lives_free(host_tag_s);
21206                       idx = host_tag - FX_KEYS_MAX_VIRTUAL - 1;
21207 
21208                       if (pchains[idx][pnum] == init_event) {
21209                         if (weed_leaf_seed_type((weed_plant_t *)init_event, WEED_LEAF_IN_PARAMETERS) == WEED_SEED_INT64) {
21210                           // leave as int64_t and we will change afterwards
21211                           uint64_t *orig_pchanges = (uint64_t *)weed_get_int64_array((weed_plant_t *)init_event,
21212                                                     WEED_LEAF_IN_PARAMETERS, NULL);
21213 
21214                           uint64_t *pin_pchanges = (uint64_t *)lives_malloc(num_params * sizeof(uint64_t));
21215 
21216                           for (i = 0; i < num_params; i++) {
21217                             if (orig_pchanges[i] == 0 && i == pnum) pin_pchanges[i] = (uint64_t)event;
21218                             else pin_pchanges[i] = (uint64_t)orig_pchanges[i];
21219                           }
21220 
21221                           weed_leaf_delete((weed_plant_t *)init_event, WEED_LEAF_IN_PARAMETERS);
21222                           weed_set_int64_array((weed_plant_t *)init_event, WEED_LEAF_IN_PARAMETERS, num_params,
21223                                                (int64_t *)pin_pchanges);
21224 
21225                           lives_free(pin_pchanges);
21226                           lives_free(orig_pchanges);
21227                         } else {
21228                           void **orig_pchanges = weed_get_voidptr_array((weed_plant_t *)init_event,
21229                                                  WEED_LEAF_IN_PARAMETERS, NULL);
21230                           void **pin_pchanges = (void **)lives_malloc(num_params * sizeof(void *));
21231 
21232                           for (i = 0; i < num_params; i++) {
21233                             if (!orig_pchanges[i] && i == pnum) pin_pchanges[i] = (void *)event;
21234                             else pin_pchanges[i] = (void *)orig_pchanges[i];
21235                           }
21236                           weed_leaf_delete((weed_plant_t *)init_event, WEED_LEAF_IN_PARAMETERS);
21237                           weed_set_voidptr_array((weed_plant_t *)init_event, WEED_LEAF_IN_PARAMETERS, num_params, pin_pchanges);
21238 
21239                           lives_free(pin_pchanges);
21240                           lives_free(orig_pchanges);
21241                         }
21242                         weed_leaf_delete(event, WEED_LEAF_PREV_CHANGE);
21243                         weed_set_voidptr_value(event, WEED_LEAF_PREV_CHANGE, NULL);
21244                       } else {
21245                         weed_leaf_delete(event, WEED_LEAF_NEXT_CHANGE);
21246                         weed_set_voidptr_value((weed_plant_t *)pchains[idx][pnum], WEED_LEAF_NEXT_CHANGE, event);
21247                         weed_leaf_delete(event, WEED_LEAF_PREV_CHANGE);
21248                         weed_set_voidptr_value(event, WEED_LEAF_PREV_CHANGE, pchains[idx][pnum]);
21249                       }
21250                       weed_leaf_delete(event, WEED_LEAF_NEXT_CHANGE);
21251                       weed_set_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, NULL);
21252                       weed_leaf_delete(event, WEED_LEAF_INIT_EVENT);
21253                       weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENT, init_event);
21254                       pchains[idx][pnum] = event;
21255                     }
21256                   }
21257                   lives_free(ptmpls);
21258                 }
21259                 lives_free(filter_hash);
21260 		// *INDENT-OFF*
21261               }}}}}
21262       // *INDENT-ON*
21263       break;
21264     case WEED_EVENT_TYPE_FRAME:
21265       if (tc == last_frame_tc) {
21266         ebuf = rec_error_add(ebuf, "Duplicate frame event", -1, tc);
21267         delete_event(event_list, event);
21268         was_deleted = TRUE;
21269       } else {
21270         if (!weed_plant_has_leaf(event, WEED_LEAF_CLIPS)) {
21271           weed_set_int_value(event, WEED_LEAF_CLIPS, -1);
21272           weed_set_int64_value(event, WEED_LEAF_FRAMES, 0);
21273           ebuf = rec_error_add(ebuf, "Frame event missing clips at", -1, tc);
21274         }
21275 
21276         last_frame_tc = tc;
21277 
21278         clip_index = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &num_tracks);
21279         frame_index = get_int64_array_convert(event, WEED_LEAF_FRAMES);
21280 
21281         new_clip_index = (int *)lives_malloc(num_tracks * sizint);
21282         new_frame_index = (int64_t *)lives_malloc(num_tracks * 8);
21283         last_valid_frame = 0;
21284         //#define DEBUG_MISSING_CLIPS
21285 #ifdef DEBUG_MISSING_CLIPS
21286         g_print("pt zzz %d\n", num_tracks);
21287 #endif
21288         for (i = 0; i < num_tracks; i++) {
21289           if (clip_index[i] > 0 && (clip_index[i] > MAX_FILES || renumbered_clips[clip_index[i]] < 1 ||
21290                                     !mainw->files[renumbered_clips[clip_index[i]]])) {
21291             // clip has probably been closed, so we remove its frames
21292 
21293             new_clip_index[i] = -1;
21294             new_frame_index[i] = 0;
21295             ebuf = rec_error_add(ebuf, "Invalid clip number", clip_index[i], tc);
21296 
21297 #ifdef DEBUG_MISSING_CLIPS
21298             g_print("found invalid clip number %d on track %d, renumbered_clips=%d\n", clip_index[i], i,
21299                     renumbered_clips[clip_index[i]]);
21300 #endif
21301             missing_clips = TRUE;
21302           } else {
21303             // take into account the fact that clip could have been resampled since layout was saved
21304             if (clip_index[i] > 0 && frame_index[i] > 0) {
21305               int rclip = renumbered_clips[clip_index[i]];
21306               if (lfps[rclip] != 0.) {
21307                 new_frame_index[i] = count_resampled_frames(frame_index[i], lfps[rclip],
21308                                      mainw->files[rclip]->fps);
21309               } else new_frame_index[i] = frame_index[i];
21310               // the scrap_file has no real frames so we allow it to pass
21311               if (rclip != mainw->scrap_file && new_frame_index[i] > mainw->files[rclip]->frames) {
21312                 ebuf = rec_error_add(ebuf, "Invalid frame number", new_frame_index[i], tc);
21313                 new_clip_index[i] = -1;
21314                 new_frame_index[i] = 0;
21315                 missing_frames = TRUE;
21316               } else {
21317                 // if recovering a recording we will be rendering from the clip editor and not using renumbered clips
21318                 // so we must adjust the clip number in the layout
21319                 // for multitrack, we leave the original clip number and use our renumbered clips mapping
21320                 if (mainw->recording_recovered) new_clip_index[i] = rclip;
21321                 else new_clip_index[i] = clip_index[i];
21322                 new_frame_index[i] = frame_index[i];
21323                 last_valid_frame = i + 1;
21324               }
21325             } else {
21326               new_clip_index[i] = clip_index[i];
21327               new_frame_index[i] = frame_index[i];
21328               last_valid_frame = i + 1;
21329             }
21330           }
21331         }
21332 
21333         if (last_valid_frame == 0) {
21334           lives_free(new_clip_index);
21335           lives_free(new_frame_index);
21336           new_clip_index = (int *)lives_malloc(sizint);
21337           new_frame_index = (int64_t *)lives_malloc(8);
21338           *new_clip_index = -1;
21339           *new_frame_index = 0;
21340           num_tracks = 1;
21341           weed_set_int_array(event, WEED_LEAF_CLIPS, num_tracks, new_clip_index);
21342           weed_set_int64_array(event, WEED_LEAF_FRAMES, num_tracks, new_frame_index);
21343         } else {
21344           if (last_valid_frame < num_tracks) {
21345             lives_free(clip_index);
21346             lives_free(frame_index);
21347             clip_index = (int *)lives_malloc(last_valid_frame * sizint);
21348             frame_index = (int64_t *)lives_malloc(last_valid_frame * 8);
21349             for (i = 0; i < last_valid_frame; i++) {
21350               clip_index[i] = new_clip_index[i];
21351               frame_index[i] = new_frame_index[i];
21352             }
21353             num_tracks = last_valid_frame;
21354             weed_set_int_array(event, WEED_LEAF_CLIPS, num_tracks, clip_index);
21355             weed_set_int64_array(event, WEED_LEAF_FRAMES, num_tracks, frame_index);
21356           } else {
21357             weed_set_int_array(event, WEED_LEAF_CLIPS, num_tracks, new_clip_index);
21358             weed_set_int64_array(event, WEED_LEAF_FRAMES, num_tracks, new_frame_index);
21359           }
21360         }
21361 
21362         lives_free(new_clip_index);
21363         lives_free(clip_index);
21364         lives_free(new_frame_index);
21365         lives_free(frame_index);
21366 
21367         if (WEED_EVENT_IS_AUDIO_FRAME(event)) {
21368           // check audio clips
21369           num_atracks = weed_leaf_num_elements(event, WEED_LEAF_AUDIO_CLIPS);
21370           if ((num_atracks & 1) != 0) {
21371             ebuf = rec_error_add(ebuf, "Invalid number of audio_clips", -1, tc);
21372             weed_leaf_delete(event, WEED_LEAF_AUDIO_CLIPS);
21373             weed_leaf_delete(event, WEED_LEAF_AUDIO_SEEKS);
21374           } else {
21375             if (!weed_plant_has_leaf(event, WEED_LEAF_AUDIO_SEEKS) || weed_leaf_num_elements(event,
21376                 WEED_LEAF_AUDIO_SEEKS) != num_atracks) {
21377               ebuf = rec_error_add(ebuf, "Invalid number of audio_seeks", -1, tc);
21378               weed_leaf_delete(event, WEED_LEAF_AUDIO_CLIPS);
21379               weed_leaf_delete(event, WEED_LEAF_AUDIO_SEEKS);
21380             } else {
21381               aclip_index = weed_get_int_array(event, WEED_LEAF_AUDIO_CLIPS, NULL);
21382               aseek_index = weed_get_double_array(event, WEED_LEAF_AUDIO_SEEKS, NULL);
21383               new_aclip_index = (int *)lives_malloc(num_atracks * sizint);
21384               new_aseek_index = (double *)lives_malloc(num_atracks * sizdbl);
21385               j = 0;
21386               for (i = 0; i < num_atracks; i += 2) {
21387                 if (aclip_index[i + 1] > 0) {
21388                   if ((aclip_index[i + 1] > MAX_FILES || renumbered_clips[aclip_index[i + 1]] < 1 ||
21389                        !mainw->files[renumbered_clips[aclip_index[i + 1]]]) && aseek_index[i + 1] != 0.) {
21390                     // clip has probably been closed, so we remove its frames
21391                     ebuf = rec_error_add(ebuf, "Invalid audio clip number", aclip_index[i + 1], tc);
21392                     missing_clips = TRUE;
21393                   } else {
21394                     new_aclip_index[j] = aclip_index[i];
21395                     new_aclip_index[j + 1] = aclip_index[i + 1];
21396                     new_aseek_index[j] = aseek_index[i];
21397                     new_aseek_index[j + 1] = aseek_index[i + 1];
21398                     if (aseek_index[j + 1] != 0.) add_atrack_to_list(aclip_index[i], aclip_index[i + 1]);
21399                     else remove_atrack_from_list(aclip_index[i]);
21400                     j += 2;
21401                   }
21402                 }
21403                 if (aclip_index[i] > -1) {
21404                   if (mt && !mt->opts.pertrack_audio) {
21405                     mt->opts.pertrack_audio = TRUE;
21406                     // enable audio transitions
21407                     lives_widget_set_sensitive(mt->fx_region_2a, TRUE);
21408                     ebuf = rec_error_add(ebuf, "Adding pertrack audio", -1, tc);
21409                   } else force_pertrack_audio = TRUE;
21410                   // TODO ** inform user
21411                 }
21412                 if (aclip_index[i] == -1) {
21413                   if (mt && mt->opts.back_audio_tracks == 0) {
21414                     mt->opts.back_audio_tracks = 1;
21415                     ebuf = rec_error_add(ebuf, "Adding backing audio", -1, tc);
21416                   } else force_backing_tracks = 1;
21417                   // TODO ** inform user
21418                 }
21419               }
21420               if (j == 0) {
21421                 weed_leaf_delete(event, WEED_LEAF_AUDIO_CLIPS);
21422                 weed_leaf_delete(event, WEED_LEAF_AUDIO_SEEKS);
21423               } else {
21424                 weed_set_int_array(event, WEED_LEAF_AUDIO_CLIPS, j, new_aclip_index);
21425                 weed_set_double_array(event, WEED_LEAF_AUDIO_SEEKS, j, new_aseek_index);
21426               }
21427               lives_free(aclip_index);
21428               lives_free(aseek_index);
21429               lives_free(new_aclip_index);
21430               lives_free(new_aseek_index);
21431 	      // *INDENT-OFF*
21432             }}}}
21433       // *INDENT-ON*
21434       break;
21435 
21436     case WEED_EVENT_TYPE_MARKER:
21437       // check marker values
21438       if (!weed_plant_has_leaf(event, WEED_LEAF_LIVES_TYPE)) {
21439         ebuf = rec_error_add(ebuf, "Unknown marker type", -1, tc);
21440         delete_event(event_list, event);
21441         was_deleted = TRUE;
21442       } else {
21443         marker_type = weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, NULL);
21444         if (marker_type != EVENT_MARKER_BLOCK_START && marker_type != EVENT_MARKER_BLOCK_UNORDERED &&
21445             marker_type != EVENT_MARKER_RECORD_END && marker_type != EVENT_MARKER_RECORD_START) {
21446           ebuf = rec_error_add(ebuf, "Unknown marker type", marker_type, tc);
21447           delete_event(event_list, event);
21448           was_deleted = TRUE;
21449         }
21450         if (marker_type == EVENT_MARKER_BLOCK_START && !weed_plant_has_leaf(event, WEED_LEAF_TRACKS)) {
21451           ebuf = rec_error_add(ebuf, "Block start marker has no tracks", -1, tc);
21452           delete_event(event_list, event);
21453           was_deleted = TRUE;
21454         }
21455       }
21456       break;
21457     default:
21458       ebuf = rec_error_add(ebuf, "Invalid event_type", event_type, tc);
21459       delete_event(event_list, event);
21460       was_deleted = TRUE;
21461     }
21462     if (!was_deleted && check_filter_map && last_filter_map
21463         && (!event_next || get_event_timecode(event_next) > last_deinit_tc)) {
21464       // if our last filter_map refers to filter instances which were deinited, we must add another filter_map here
21465       ebuf = filter_map_check(trans_table, last_filter_map, last_deinit_tc, tc, ebuf);
21466       check_filter_map = FALSE;
21467     }
21468     event = event_next;
21469 
21470     if (!was_deleted && fps != 0.) {
21471       while (cur_tc < last_frame_tc) {
21472         // add blank frames
21473         if (!has_frame_event_at(event_list, cur_tc, &shortcut)) {
21474           if (shortcut) {
21475             shortcut = duplicate_frame_at(event_list, shortcut, cur_tc);
21476             ebuf = rec_error_add(ebuf, "Duplicated frame at", -1, cur_tc);
21477           } else {
21478             event_list = insert_blank_frame_event_at(event_list, cur_tc, &shortcut);
21479             ebuf = rec_error_add(ebuf, "Inserted missing blank frame", -1, cur_tc);
21480           }
21481         }
21482         cur_tc += TICKS_PER_SECOND_DBL / fps;
21483         cur_tc = q_gint64(cur_tc, fps);
21484       }
21485     }
21486     last_tc = tc;
21487   }
21488 
21489   if (fps == 0.) {
21490     lives_free(ebuf);
21491     return TRUE;
21492   }
21493 
21494   // add any missing filter_deinit events
21495   ebuf = add_filter_deinits(event_list, trans_table, pchains, last_tc, ebuf);
21496 
21497   // check the last filter map
21498   if (last_filter_map) ebuf = add_null_filter_map(event_list, last_filter_map, last_tc, ebuf);
21499 
21500   last_event = get_last_event(event_list);
21501   remove_end_blank_frames(event_list, TRUE);
21502 
21503   if (get_last_event(event_list) != last_event) {
21504     last_event = get_last_event(event_list);
21505     last_tc = get_event_timecode(last_event);
21506     ebuf = rec_error_add(ebuf, "Removed final blank frames", -1, last_tc);
21507   }
21508 
21509   // pass 2 - move left any FILTER_DEINITS before the FRAME, move right any FILTER_INITS or PARAM_CHANGES after the FRAME
21510   // ensure we have at most 1 FILTER_MAP before each FRAME, and 1 FILTER_MAP after a FRAME
21511 
21512   // we do this as a second pass since we may have inserted blank frames
21513   last_frame_tc = last_filter_map_tc = -1;
21514   last_frame_event = NULL;
21515 
21516   event = get_first_event(event_list);
21517   while (event) {
21518     was_moved = FALSE;
21519     event_next = get_next_event(event);
21520     tc = get_event_timecode(event);
21521     event_type = get_event_type(event);
21522     switch (event_type) {
21523     case WEED_EVENT_TYPE_FILTER_INIT:
21524       // if our in_parameters are int64, convert to void *
21525       if (weed_plant_has_leaf(event, WEED_LEAF_IN_PARAMETERS)) {
21526         uint64_t *pin_params;
21527         void **nin_params;
21528         num_params = weed_leaf_num_elements(event, WEED_LEAF_IN_PARAMETERS);
21529 
21530         if (weed_leaf_seed_type(event, WEED_LEAF_IN_PARAMETERS) == WEED_SEED_INT64) {
21531           pin_params = (uint64_t *)weed_get_int64_array(event, WEED_LEAF_IN_PARAMETERS, NULL);
21532           nin_params = (void **)lives_malloc(num_params * sizeof(void *));
21533           for (i = 0; i < num_params; i++) {
21534             nin_params[i] = (void *)pin_params[i];
21535           }
21536           lives_free(pin_params);
21537           weed_leaf_delete(event, WEED_LEAF_IN_PARAMETERS);
21538           weed_set_voidptr_array(event, WEED_LEAF_IN_PARAMETERS, num_params, nin_params);
21539           lives_free(nin_params);
21540         }
21541 
21542         filter_hash = weed_get_string_value(event, WEED_LEAF_FILTER, NULL);
21543         if ((filter_idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
21544           void **pchain;
21545           filter = get_weed_filter(filter_idx);
21546           // fill in any newly added params
21547           num_tracks = weed_leaf_num_elements(event, WEED_LEAF_IN_TRACKS);
21548           pchain = filter_init_add_pchanges(event_list, filter, event, num_tracks, num_params);
21549           lives_free(pchain);
21550         }
21551         lives_free(filter_hash);
21552       }
21553       if (mt && event != mt->avol_init_event) {
21554         if (!move_event_right(event_list, event, last_frame_tc != tc, fps)) was_moved = TRUE;
21555       }
21556       break;
21557     case WEED_EVENT_TYPE_PARAM_CHANGE:
21558       if (last_frame_tc == tc) if (!move_event_right(event_list, event, FALSE, fps)) was_moved = TRUE;
21559       break;
21560     case WEED_EVENT_TYPE_FILTER_DEINIT:
21561       if (mt && weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL) != mt->avol_init_event) {
21562         if (!move_event_left(event_list, event, last_frame_tc == tc, fps)) was_moved = TRUE;
21563       }
21564       break;
21565     case WEED_EVENT_TYPE_FILTER_MAP:
21566       if (last_filter_map_tc == tc) {
21567         // remove last filter_map
21568         ebuf = rec_error_add(ebuf, "Duplicate filter maps", -1, tc);
21569         delete_event(event_list, last_filter_map);
21570       }
21571       last_filter_map_tc = tc;
21572       last_filter_map = event;
21573       break;
21574     case WEED_EVENT_TYPE_FRAME:
21575       last_frame_tc = tc;
21576       last_filter_map_tc = -1;
21577       last_frame_event = event;
21578       break;
21579     }
21580     if (was_moved) {
21581       if (last_frame_event) event = last_frame_event;
21582       else event = get_first_event(event_list);
21583     } else event = event_next;
21584   }
21585 
21586   ebuf = add_missing_atrack_closers(event_list, fps, ebuf);
21587 
21588   if (missing_clips && missing_frames) {
21589     bit2 = (_("clips and frames"));
21590   } else {
21591     if (missing_clips) {
21592       bit2 = (_("clips"));
21593     } else if (missing_frames) {
21594       bit2 = (_("frames"));
21595     }
21596   }
21597 
21598   end_threaded_dialog();
21599 
21600   if (bit2) {
21601     if (mt && mt->auto_reloading) {
21602       lives_free(bit1);
21603       lives_free(bit3);
21604       bit1 = (_("\nAuto reload layout.\n"));
21605       bit3 = lives_strdup_printf("\n%s", prefs->ar_layout_name);
21606     }
21607     msg = lives_strdup_printf(_("%s\nSome %s are missing from the layout%s\nTherefore it could not be loaded properly.\n"),
21608                               bit1, bit2, bit3);
21609     do_error_dialog(msg);
21610     lives_free(msg);
21611     lives_free(bit2);
21612     if (mt) mt->layout_prompt = TRUE;
21613   }
21614   lives_free(bit1);
21615   lives_free(bit3);
21616 
21617   lives_free(ebuf); // TODO - allow option of viewing/saving this
21618 
21619   return TRUE;
21620 }
21621 
21622 
get_eload_filename(lives_mt * mt,boolean allow_auto_reload)21623 char *get_eload_filename(lives_mt * mt, boolean allow_auto_reload) {
21624   LiVESWidget *hbox;
21625   LiVESWidget *ar_checkbutton;
21626 
21627   boolean needs_idlefunc = FALSE;
21628   boolean did_backup = mt->did_backup;
21629 
21630   char *filt[] = {"*." LIVES_FILE_EXT_LAYOUT, NULL};
21631 
21632   char *eload_dir;
21633   char *eload_file;
21634   char *startdir = NULL;
21635 
21636   if (!(*mainw->set_name)) {
21637     LIVES_ERROR("Loading event list for unknown set");
21638     return NULL;
21639   }
21640 
21641   eload_dir = lives_build_path(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME, NULL);
21642 
21643   lives_mkdir_with_parents(eload_dir, capable->umask);
21644 
21645   if (!mainw->recoverable_layout && !lives_file_test(eload_dir, LIVES_FILE_TEST_IS_DIR)) {
21646     lives_free(eload_dir);
21647     return NULL;
21648   }
21649 
21650   startdir = lives_strdup(eload_dir);
21651 
21652   hbox = lives_hbox_new(FALSE, 0);
21653 
21654   if (allow_auto_reload) {
21655     ar_checkbutton = make_autoreload_check(LIVES_HBOX(hbox), prefs->ar_layout);
21656     lives_signal_sync_connect(LIVES_GUI_OBJECT(ar_checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
21657                               LIVES_GUI_CALLBACK(toggle_sets_pref),
21658                               (livespointer)PREF_AR_LAYOUT);
21659   }
21660 
21661   if (mt->idlefunc > 0) {
21662     lives_source_remove(mt->idlefunc);
21663     mt->idlefunc = 0;
21664     needs_idlefunc = TRUE;
21665   }
21666 
21667   lives_widget_show_all(hbox);
21668 
21669   eload_file = choose_file(startdir, NULL, filt, LIVES_FILE_CHOOSER_ACTION_OPEN, NULL, hbox);
21670 
21671   lives_free(startdir);
21672 
21673   if (!eload_file) {
21674     // if the user cancelled see if we can clear the directories
21675     // this will fail if there are any files in the directories
21676 
21677     char *cdir;
21678     lives_rmdir(eload_dir, FALSE);
21679 
21680     cdir = lives_build_filename(prefs->workdir, mainw->set_name, NULL);
21681     lives_rmdir(cdir, FALSE);
21682 
21683     if (needs_idlefunc || (!did_backup && mt->auto_changed))
21684       mt->idlefunc = mt_idle_add(mt);
21685   }
21686 
21687   lives_free(eload_dir);
21688 
21689   return eload_file;
21690 }
21691 
21692 
load_event_list(lives_mt * mt,char * eload_file)21693 weed_plant_t *load_event_list(lives_mt * mt, char *eload_file) {
21694   // load (deserialise) a serialised event_list
21695   // after loading we perform sophisticated checks on it to detect
21696   // and try to repair any errors in it
21697   weed_plant_t *event_list = NULL;
21698 
21699   char *msg;
21700   char *eload_name;
21701 
21702   boolean free_eload_file = TRUE;
21703   boolean orig_ar_layout = prefs->ar_layout, ar_layout;
21704   boolean retval = TRUE;
21705   boolean needs_idlefunc = FALSE;
21706 
21707   int num_events = 0;
21708   int retval2;
21709   int old_avol_fx;
21710   int fd;
21711 
21712   if (mt) {
21713     old_avol_fx  = mt->avol_fx;
21714     if (mt->idlefunc > 0) {
21715       lives_source_remove(mt->idlefunc);
21716       mt->idlefunc = 0;
21717       needs_idlefunc = TRUE;
21718     }
21719   }
21720 
21721   if (!eload_file) {
21722     eload_file = get_eload_filename(mt, TRUE);
21723     if (!eload_file) {
21724       if (needs_idlefunc) mt->idlefunc = mt_idle_add(mt);
21725       return NULL;
21726     }
21727   } else free_eload_file = FALSE;
21728 
21729   ar_layout = prefs->ar_layout;
21730   prefs->ar_layout = orig_ar_layout;
21731 
21732   if (!mainw->recoverable_layout) eload_name = lives_strdup(eload_file);
21733   else eload_name = (_("auto backup"));
21734 
21735   if ((fd = lives_open_buffered_rdonly(eload_file)) < 0) {
21736     if (mt) {
21737       msg = lives_strdup_printf(_("\nUnable to load layout file %s\n"), eload_name);
21738       do_error_dialog(msg);
21739       lives_free(msg);
21740       if (needs_idlefunc) mt->idlefunc = mt_idle_add(mt);
21741     }
21742     lives_free(eload_name);
21743     return NULL;
21744   }
21745 
21746   lives_buffered_rdonly_slurp(fd, 0);
21747 
21748   if (mt) {
21749     event_list_free_undos(mt);
21750 
21751     if (mainw->event_list) {
21752       event_list_free(mt->event_list);
21753       mt->event_list = NULL;
21754       mt_clear_timeline(mt);
21755     }
21756 
21757     mainw->no_switch_dprint = TRUE;
21758     d_print(_("Loading layout from %s..."), eload_name);
21759     mainw->no_switch_dprint = FALSE;
21760 
21761     mt_desensitise(mt);
21762   }
21763 
21764   do {
21765     retval = 0;
21766     if ((event_list = load_event_list_inner(mt, fd, mt != NULL, &num_events, NULL, NULL)) == NULL) {
21767       lives_close_buffered(fd);
21768 
21769       if (THREADVAR(read_failed) == fd + 1) {
21770         THREADVAR(read_failed) = 0;
21771         if (mt) retval = do_read_failed_error_s_with_retry(eload_name, NULL);
21772         THREADVAR(read_failed) = FALSE;
21773       }
21774 
21775       if (mt && retval != LIVES_RESPONSE_RETRY) {
21776         if (mt->is_ready) mt_sensitise(mt);
21777         lives_free(eload_name);
21778         if (needs_idlefunc) mt->idlefunc = mt_idle_add(mt);
21779         return NULL;
21780       }
21781     } else lives_close_buffered(fd);
21782 
21783     if (!mt) {
21784       lives_free(eload_name);
21785       renumber_from_backup_layout_numbering(NULL);
21786       if (!event_list_rectify(NULL, event_list)) {
21787         event_list_free(event_list);
21788         event_list = NULL;
21789       }
21790       if (!get_first_event(event_list)) {
21791         event_list_free(event_list);
21792         event_list = NULL;
21793       }
21794       return event_list;
21795     }
21796   } while (retval == LIVES_RESPONSE_RETRY);
21797 
21798   lives_free(eload_name);
21799 
21800   d_print_done();
21801 
21802   d_print(_("Got %d events...processing..."), num_events);
21803 
21804   mt->changed = mainw->recoverable_layout;
21805   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
21806 
21807   cfile->progress_start = 1;
21808   cfile->progress_end = num_events;
21809 
21810   // event list loaded, now we set the pointers for filter_map (init_events), param_change (init_events and param chains),
21811   // filter_deinit (init_events)
21812   do_threaded_dialog(_("Checking and rebuilding event list"), FALSE);
21813 
21814   elist_errors = 0;
21815 
21816   if (!mainw->recoverable_layout) {
21817     // re-map clips so our loaded event_list refers to the correct clips and frames
21818     rerenumber_clips(eload_file, NULL);
21819   } else {
21820     renumber_from_backup_layout_numbering(mt);
21821   }
21822 
21823   mt->avol_init_event = NULL;
21824   mt->avol_fx = -1;
21825 
21826   if (!event_list_rectify(mt, event_list)) {
21827     event_list_free(event_list);
21828     event_list = NULL;
21829   }
21830 
21831   if (!get_first_event(event_list)) {
21832     event_list_free(event_list);
21833     event_list = NULL;
21834   }
21835 
21836   if (event_list) {
21837     d_print(_("%d errors detected.\n"), elist_errors);
21838     if (!mt->auto_reloading) {
21839       if (!mt->layout_prompt || do_mt_rect_prompt()) {
21840         do {
21841           retval2 = 0;
21842           retval = TRUE;
21843 
21844           // resave with corrections/updates
21845           fd = lives_create_buffered(eload_file, DEF_FILE_PERMS);
21846           if (fd >= 0) {
21847             retval = save_event_list_inner(NULL, fd, event_list, NULL);
21848             lives_close_buffered(fd);
21849           }
21850 
21851           if (fd < 0 || !retval) {
21852             retval2 = do_write_failed_error_s_with_retry(eload_file, (fd < 0) ? lives_strerror(errno) : NULL);
21853             if (retval2 == LIVES_RESPONSE_CANCEL) d_print_file_error_failed();
21854           }
21855         } while (retval2 == LIVES_RESPONSE_RETRY);
21856       }
21857     }
21858   } else d_print_failed();
21859 
21860   mt->layout_prompt = FALSE;
21861 
21862   if (mt->avol_fx == -1 && mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].delegate != -1) {
21863     // user (or system) has delegated an audio volume filter from the candidates
21864     mt->avol_fx = LIVES_POINTER_TO_INT(lives_list_nth_data(mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].list,
21865                                        mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL].delegate));
21866   }
21867 
21868   if (mt->avol_fx != old_avol_fx && mt->opts.aparam_view_list) {
21869     // audio volume effect changed, so we reset which parameters are viewed
21870     lives_list_free(mt->opts.aparam_view_list);
21871     mt->opts.aparam_view_list = NULL;
21872   }
21873 
21874   if (event_list) {
21875     if (!mainw->recoverable_layout) {
21876       lives_snprintf(mt->layout_name, PATH_MAX, "%s", eload_file);
21877       get_basename(mt->layout_name);
21878     }
21879 
21880     if (mt->layout_set_properties) msg = mt_set_vals_string();
21881     else msg = lives_strdup_printf(_("Multitrack fps set to %.3f\n"), mainw->files[mt->render_file]->fps);
21882     d_print(msg);
21883     lives_free(msg);
21884 
21885     set_mt_title(mt);
21886 
21887     if (!ar_layout) {
21888       prefs->ar_layout = FALSE;
21889       set_string_pref(PREF_AR_LAYOUT, "");
21890       lives_memset(prefs->ar_layout_name, 0, 1);
21891     } else {
21892       if (!mainw->recoverable_layout) {
21893         prefs->ar_layout = TRUE;
21894         set_string_pref(PREF_AR_LAYOUT, mt->layout_name);
21895         lives_snprintf(prefs->ar_layout_name, 128, "%s", mt->layout_name);
21896       }
21897     }
21898   }
21899 
21900   if (mainw->files[mt->render_file]->achans > 0) {
21901     set_audio_filter_channel_values(mt);
21902   }
21903 
21904   if (mt->opts.back_audio_tracks > 0) {
21905     lives_widget_show(mt->view_audio);
21906   }
21907 
21908   if (free_eload_file) lives_free(eload_file);
21909 
21910   if (!mainw->recoverable_layout) {
21911     polymorph(mt, POLY_CLIPS);
21912   }
21913 
21914   return (event_list);
21915 }
21916 
21917 
remove_markers(weed_plant_t * event_list)21918 void remove_markers(weed_plant_t *event_list) {
21919   weed_plant_t *event = get_first_event(event_list);
21920   weed_plant_t *event_next;
21921   int marker_type;
21922 
21923   while (event) {
21924     event_next = get_next_event(event);
21925     if (WEED_EVENT_IS_MARKER(event)) {
21926       marker_type = weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, NULL);
21927       if (marker_type == EVENT_MARKER_BLOCK_START || marker_type == EVENT_MARKER_BLOCK_UNORDERED) {
21928         delete_event(event_list, event);
21929       }
21930     }
21931     event = event_next;
21932   }
21933 }
21934 
21935 
wipe_layout(lives_mt * mt)21936 void wipe_layout(lives_mt * mt) {
21937   mt_desensitise(mt);
21938 
21939   if (mt->idlefunc > 0) {
21940     lives_source_remove(mt->idlefunc);
21941     mt->idlefunc = 0;
21942   }
21943 
21944   print_layout_wiped();
21945 
21946   close_scrap_file(TRUE);
21947   close_ascrap_file(TRUE);
21948 
21949   recover_layout_cancelled(FALSE);
21950   mainw->recording_recovered = FALSE;
21951 
21952   if (*mt->layout_name && !strcmp(mt->layout_name, prefs->ar_layout_name)) {
21953     set_string_pref(PREF_AR_LAYOUT, "");
21954     lives_memset(prefs->ar_layout_name, 0, 1);
21955     prefs->ar_layout = FALSE;
21956   }
21957 
21958   event_list_free(mt->event_list);
21959   mt->event_list = NULL;
21960 
21961   event_list_free_undos(mt);
21962 
21963   mt_clear_timeline(mt);
21964 
21965   mt_sensitise(mt);
21966 
21967   mt->idlefunc = mt_idle_add(mt);
21968 }
21969 
21970 
on_clear_event_list_activate(LiVESMenuItem * menuitem,livespointer user_data)21971 void on_clear_event_list_activate(LiVESMenuItem * menuitem, livespointer user_data) {
21972   lives_mt *mt = (lives_mt *)user_data;
21973   _entryw *cdsw;
21974 
21975   int resp = 2;
21976 
21977   boolean rev_resp = FALSE; // if TRUE, a return value of 2 means save, otherwise it means delete
21978 
21979   if (mt->idlefunc > 0) {
21980     lives_source_remove(mt->idlefunc);
21981     mt->idlefunc = 0;
21982   }
21983 
21984   if (*mt->layout_name) {
21985     // delete : 2
21986     // wipe : 1
21987     cdsw = create_cds_dialog(2);
21988     rev_resp = FALSE;
21989   } else {
21990     // save: 2
21991     // wipe: 1
21992     cdsw = create_cds_dialog(3);
21993     rev_resp = TRUE;
21994   }
21995 
21996   do {
21997     mainw->cancelled = CANCEL_NONE;
21998     resp = lives_dialog_run(LIVES_DIALOG(cdsw->dialog));
21999 
22000     if (resp == 2 && rev_resp) {
22001       // save
22002       on_save_event_list_activate(NULL, mt);
22003       if (mainw->cancelled == CANCEL_NONE) break;
22004     }
22005   } while (resp == 2 && rev_resp);
22006 
22007   lives_widget_destroy(cdsw->dialog);
22008   lives_free(cdsw);
22009 
22010   if (resp == LIVES_RESPONSE_CANCEL) {
22011     mt->idlefunc = mt_idle_add(mt);
22012     return; // cancel
22013   }
22014 
22015   if (resp == 2 && !rev_resp) {
22016     // delete from disk
22017     LiVESList *layout_map = NULL;
22018     char *lmap_file;
22019     if (!do_yesno_dialog("\nLayout will be deleted from the disk.\nAre you sure ?\n")) {
22020       mt->idlefunc = mt_idle_add(mt);
22021       return;
22022     }
22023 
22024     lmap_file = lives_build_filename(WORKDIR_LITERAL, mainw->set_name, LAYOUTS_DIRNAME, mt->layout_name, NULL);
22025     layout_map = lives_list_append(layout_map, lmap_file);
22026     remove_layout_files(layout_map);
22027     lives_free(lmap_file);
22028   } else {
22029     // wipe
22030     if (mt->changed) {
22031       if (!do_yesno_dialog_with_check(
22032             _("The current layout has changes which have not been saved.\nAre you sure you wish to wipe it ?\n"),
22033             WARN_MASK_LAYOUT_WIPE)) {
22034         mt->idlefunc = mt_idle_add(mt);
22035         return;
22036       }
22037     }
22038   }
22039 
22040   // wipe
22041   wipe_layout(mt);
22042 }
22043 
22044 
on_load_event_list_activate(LiVESMenuItem * menuitem,livespointer user_data)22045 boolean on_load_event_list_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22046   int i;
22047   lives_mt *mt = (lives_mt *)user_data;
22048   weed_plant_t *new_event_list;
22049 
22050   if (LIVES_IS_INTERACTIVE)
22051     if (!check_for_layout_del(mt, FALSE)) return FALSE;
22052 
22053   if (mt->idlefunc > 0) {
22054     lives_source_remove(mt->idlefunc);
22055     mt->idlefunc = 0;
22056   }
22057 
22058   new_event_list = load_event_list(mt, mt->force_load_name);
22059 
22060   if (mainw->was_set) recover_layout_cancelled(FALSE);
22061 
22062   if (!new_event_list) {
22063     mt_sensitise(mt);
22064     mt->idlefunc = mt_idle_add(mt);
22065     return FALSE;
22066   }
22067 
22068   if (mt->event_list) event_list_free(mt->event_list);
22069   mt->event_list = NULL;
22070 
22071   mt->undo_buffer_used = 0;
22072   mt->undo_offset = 0;
22073   lives_list_free(mt->undos);
22074   mt->undos = NULL;
22075   mt_set_undoable(mt, MT_UNDO_NONE, NULL, FALSE);
22076   mt_set_redoable(mt, MT_UNDO_NONE, NULL, FALSE);
22077 
22078   for (i = 0; i < mt->num_video_tracks; i++) {
22079     delete_video_track(mt, i, FALSE);
22080   }
22081   lives_list_free(mt->video_draws);
22082   mt->video_draws = NULL;
22083   mt->num_video_tracks = 0;
22084 
22085   if (mt->amixer) on_amixer_close_clicked(NULL, mt);
22086 
22087   delete_audio_tracks(mt, mt->audio_draws, FALSE);
22088   mt->audio_draws = NULL;
22089 
22090   if (mt->audio_vols) lives_list_free(mt->audio_vols);
22091   mt->audio_vols = NULL;
22092 
22093   mt->event_list = new_event_list;
22094 
22095   if (mt->selected_tracks) lives_list_free(mt->selected_tracks);
22096   mt->selected_tracks = NULL;
22097 
22098   mt_init_tracks(mt, TRUE);
22099 
22100   if (!mt->ignore_load_vals) set_audio_mixer_vols(mt, mt->event_list);
22101 
22102   add_aparam_menuitems(mt);
22103 
22104   unselect_all(mt);
22105   remove_markers(mt->event_list);
22106   mt_sensitise(mt);
22107   mt_show_current_frame(mt, FALSE);
22108 
22109   mt->idlefunc = mt_idle_add(mt);
22110 
22111   return TRUE;
22112 }
22113 
22114 
migrate_layouts(const char * old_set_name,const char * new_set_name)22115 void migrate_layouts(const char *old_set_name, const char *new_set_name) {
22116   // if we change the name of a set, we must also update the layouts - at the very least 2 things need to happen
22117   // 1) the WEED_LEAF_NEEDS_SET leaf in each layout must be updated
22118   // 2) the layouts will be physically moved, so if appending we check for name collisions
22119   // 3) the names of layouts in mainw->affected_layouts_map must be altered
22120 
22121   // here we also update mainw->current_layouts_map and the layout_maps for each clip
22122 
22123   // this last may not be necessary as we are probably closing the set
22124 
22125   // on return from here we physically move the layouts, and we append the layout_map to the new one
22126 
22127   // load each event_list in mainw->current_layouts_map
22128   LiVESList *map = mainw->current_layouts_map;
22129   int fd;
22130   int i;
22131   int retval2 = 0;
22132   weed_plant_t *event_list;
22133   char *tmp;
22134   boolean retval = TRUE;
22135 
22136   char *changefrom = NULL;
22137   size_t chlen;
22138 
22139   if (old_set_name) {
22140     changefrom = lives_build_path(prefs->workdir, old_set_name, LAYOUTS_DIRNAME, NULL);
22141     chlen = strlen(changefrom);
22142   } else chlen = 0;
22143 
22144   while (map) {
22145     if (old_set_name) {
22146       // load and save each layout, updating the WEED_LEAF_NEEDS_SET leaf
22147       do {
22148         retval2 = 0;
22149         if ((fd = lives_open_buffered_rdonly((char *)map->data)) > -1) {
22150           lives_buffered_rdonly_slurp(fd, 0);
22151           if ((event_list = load_event_list_inner(NULL, fd, FALSE, NULL, NULL, NULL)) != NULL) {
22152             lives_close_buffered(fd);
22153             // adjust the value of WEED_LEAF_NEEDS_SET to new_set_name
22154             weed_set_string_value(event_list, WEED_LEAF_NEEDS_SET, (tmp = F2U8(new_set_name)));
22155             lives_free(tmp);
22156             // save the event_list with the same name
22157             lives_rm((char *)map->data);
22158 
22159             do {
22160               retval2 = 0;
22161               fd = lives_create_buffered((char *)map->data, DEF_FILE_PERMS);
22162               if (fd >= 0) {
22163                 retval = save_event_list_inner(NULL, fd, event_list, NULL);
22164               }
22165               if (fd < 0 || !retval) {
22166                 if (fd > 0) lives_close_buffered(fd);
22167                 retval2 = do_write_failed_error_s_with_retry((char *)map->data, (fd < 0) ? lives_strerror(errno) : NULL);
22168               }
22169             } while (retval2 == LIVES_RESPONSE_RETRY);
22170 
22171             event_list_free(event_list);
22172           }
22173           if (retval2 == 0) lives_close_buffered(fd);
22174         } else {
22175           retval2 = do_read_failed_error_s_with_retry((char *)map->data, NULL);
22176         }
22177       } while (retval2 == LIVES_RESPONSE_RETRY);
22178     }
22179 
22180     if (old_set_name && !lives_strncmp((char *)map->data, changefrom, chlen)) {
22181       // update entries in mainw->current_layouts_map
22182       tmp = lives_build_filename(prefs->workdir, new_set_name, LAYOUTS_DIRNAME, (char *)map->data + chlen, NULL);
22183       if (lives_file_test(tmp, LIVES_FILE_TEST_EXISTS)) {
22184         // prevent duplication of layouts
22185         lives_free(tmp);
22186         tmp = lives_strdup_printf("%s" LIVES_DIR_SEP "%s" LIVES_DIR_SEP LAYOUTS_DIRNAME LIVES_DIR_SEP "%s-%s",
22187                                   prefs->workdir, new_set_name, old_set_name, (char *)map->data + chlen);
22188         lives_mv((const char *)map->data, tmp);
22189       }
22190       lives_free((livespointer)map->data);
22191       map->data = tmp;
22192     }
22193     map = map->next;
22194   }
22195 
22196   // update layout_map's in mainw->files
22197   for (i = 1; i <= MAX_FILES; i++) {
22198     if (mainw->files[i]) {
22199       if (mainw->files[i]->layout_map) {
22200         map = mainw->files[i]->layout_map;
22201         while (map) {
22202           if (map->data) {
22203             if ((old_set_name && !lives_strncmp((char *)map->data, changefrom, chlen)) ||
22204                 (!old_set_name && (strstr((char *)map->data, new_set_name) == NULL))) {
22205 
22206               char **array = lives_strsplit((char *)map->data, "|", -1);
22207               size_t origlen = strlen(array[0]);
22208               char *tmp2 = lives_build_filename(prefs->workdir, new_set_name, LAYOUTS_DIRNAME, array[0] + chlen, NULL);
22209               if (lives_file_test(tmp2, LIVES_FILE_TEST_EXISTS)) {
22210                 tmp2 = lives_strdup_printf("%s" LIVES_DIR_SEP "%s" LIVES_DIR_SEP LAYOUTS_DIRNAME LIVES_DIR_SEP "%s-%s",
22211                                            prefs->workdir, new_set_name, old_set_name, array[0] + chlen);
22212               }
22213               tmp = lives_strdup_printf("%s%s", tmp2, (char *)map->data + origlen);
22214               lives_free(tmp2);
22215               lives_strfreev(array);
22216 
22217               lives_free((livespointer)map->data);
22218               map->data = tmp;
22219             }
22220             map = map->next;
22221 	    // *INDENT-OFF*
22222           }}}}}
22223   // *INDENT-ON*
22224 
22225   // update mainw->affected_layouts_map
22226   map = mainw->affected_layouts_map;
22227   while (map) {
22228     if ((old_set_name && !lives_strncmp((char *)map->data, changefrom, chlen)) ||
22229         (!old_set_name && (strstr((char *)map->data, new_set_name) == NULL))) {
22230       if (strcmp(mainw->string_constants[LIVES_STRING_CONSTANT_CL], (char *)map->data + chlen)) {
22231         tmp = lives_build_filename(prefs->workdir, new_set_name, LAYOUTS_DIRNAME, (char *)map->data + chlen, NULL);
22232         if (lives_file_test(tmp, LIVES_FILE_TEST_EXISTS)) {
22233           lives_free(tmp);
22234           tmp = lives_strdup_printf("%s" LIVES_DIR_SEP "%s" LIVES_DIR_SEP LAYOUTS_DIRNAME LIVES_DIR_SEP "%s-%s",
22235                                     prefs->workdir, new_set_name, old_set_name, (char *)map->data + chlen);
22236         }
22237         lives_free((livespointer)map->data);
22238         map->data = tmp;
22239       }
22240     }
22241     map = map->next;
22242   }
22243   lives_freep((void **)&changefrom);
22244 }
22245 
22246 
layout_frame_is_affected(int clipno,int start,int end,LiVESList * xlays)22247 LiVESList *layout_frame_is_affected(int clipno, int start, int end, LiVESList * xlays) {
22248   // return list of names of layouts which are affected, or NULL
22249   // list and list->data should be freed after use
22250 
22251   char **array;
22252   LiVESList *lmap = mainw->files[clipno]->layout_map;
22253   double orig_fps;
22254   int resampled_frame;
22255 
22256   if (mainw->stored_event_list && mainw->files[clipno]->stored_layout_frame != 0) {
22257     // see if it affects the current layout
22258     resampled_frame = count_resampled_frames(mainw->files[clipno]->stored_layout_frame, mainw->files[clipno]->stored_layout_fps,
22259                       mainw->files[clipno]->fps);
22260     if (start <= resampled_frame && (end == 0 || end >= resampled_frame))
22261       xlays = lives_list_append_unique(xlays, mainw->string_constants[LIVES_STRING_CONSTANT_CL]);
22262   }
22263 
22264   while (lmap) {
22265     array = lives_strsplit((char *)lmap->data, "|", -1);
22266     if (atoi(array[2]) != 0) {
22267       orig_fps = strtod(array[3], NULL);
22268       resampled_frame = count_resampled_frames(atoi(array[2]), orig_fps, mainw->files[clipno]->fps);
22269       if (array[2] == 0) resampled_frame = 0;
22270       if (start <= resampled_frame && (end == 0 || end >= resampled_frame))
22271         xlays = lives_list_append_unique(xlays, array[0]);
22272     }
22273     lives_strfreev(array);
22274     lmap = lmap->next;
22275   }
22276 
22277   return xlays;
22278 }
22279 
22280 
layout_audio_is_affected(int clipno,double stime,double etime,LiVESList * xlays)22281 LiVESList *layout_audio_is_affected(int clipno, double stime, double etime, LiVESList * xlays) {
22282   char **array;
22283   LiVESList *lmap = mainw->files[clipno]->layout_map;
22284   double max_time;
22285 
22286   if (mainw->files[clipno]->arate == 0) return mainw->xlays;
22287 
22288   // adjust time depending on if we have stretched audio
22289   stime *= mainw->files[clipno]->arps / mainw->files[clipno]->arate;
22290   etime *= mainw->files[clipno]->arps / mainw->files[clipno]->arate;
22291 
22292   if (mainw->stored_event_list) {
22293     // see if it affects the current layout
22294     if (mainw->files[clipno]->stored_layout_audio > 0. && stime <= mainw->files[clipno]->stored_layout_audio &&
22295         (etime == 0. || etime <= mainw->files[clipno]->stored_layout_audio))
22296       xlays = lives_list_append_unique(xlays, mainw->string_constants[LIVES_STRING_CONSTANT_CL]);
22297   }
22298 
22299   while (lmap) {
22300     if (get_token_count((char *)lmap->data, '|') < 5) continue;
22301     array = lives_strsplit((char *)lmap->data, "|", -1);
22302     max_time = strtod(array[4], NULL);
22303     if (max_time > 0. && stime <= max_time && (etime == 0. || etime <= mainw->files[clipno]->stored_layout_audio)) {
22304       xlays = lives_list_append_unique(xlays, array[0]);
22305     }
22306     lives_strfreev(array);
22307     lmap = lmap->next;
22308   }
22309 
22310   return xlays;
22311 }
22312 
22313 
mt_change_disp_tracks_ok(LiVESButton * button,livespointer user_data)22314 void mt_change_disp_tracks_ok(LiVESButton * button, livespointer user_data) {
22315   lives_mt *mt = (lives_mt *)user_data;
22316   lives_general_button_clicked(button, NULL);
22317   prefs->max_disp_vtracks = mainw->fx1_val;
22318   set_int_pref(PREF_MAX_DISP_VTRACKS, prefs->max_disp_vtracks);
22319   scroll_tracks(mt, mt->top_track, FALSE);
22320 }
22321 
22322 
22323 ///////////////////////////////////////////////
22324 
show_frame_events_activate(LiVESMenuItem * menuitem,livespointer user_data)22325 void show_frame_events_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22326   prefs->event_window_show_frame_events = !prefs->event_window_show_frame_events;
22327 }
22328 
22329 
mt_change_max_disp_tracks(LiVESMenuItem * menuitem,livespointer user_data)22330 void mt_change_max_disp_tracks(LiVESMenuItem * menuitem, livespointer user_data) {
22331   LiVESWidget *dialog;
22332   lives_mt *mt = (lives_mt *)user_data;
22333 
22334   mainw->fx1_val = prefs->max_disp_vtracks;
22335   dialog = create_cdtrack_dialog(LIVES_DEVICE_INTERNAL, mt);
22336   lives_widget_show(dialog);
22337 }
22338 
22339 
mt_load_vals_toggled(LiVESMenuItem * menuitem,livespointer user_data)22340 void mt_load_vals_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
22341   lives_mt *mt = (lives_mt *)user_data;
22342   mt->ignore_load_vals = !mt->ignore_load_vals;
22343 }
22344 
22345 
mt_ac_audio_toggled(LiVESMenuItem * menuitem,livespointer user_data)22346 static void mt_ac_audio_toggled(LiVESMenuItem * menuitem, livespointer user_data) {
22347   lives_mt *mt = (lives_mt *)user_data;
22348   mt->opts.autocross_audio = !mt->opts.autocross_audio;
22349 }
22350 
22351 
mt_change_vals_activate(LiVESMenuItem * menuitem,livespointer user_data)22352 void mt_change_vals_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22353   lives_mt *mt = (lives_mt *)user_data;
22354   boolean response;
22355   char *msg;
22356 
22357   rdet = create_render_details(4);
22358   rdet->enc_changed = FALSE;
22359   do {
22360     rdet->suggestion_followed = FALSE;
22361     if ((response = lives_dialog_run(LIVES_DIALOG(rdet->dialog))) == LIVES_RESPONSE_OK) {
22362       if (rdet->enc_changed) {
22363         check_encoder_restrictions(FALSE, FALSE, TRUE);
22364       }
22365     }
22366   } while (rdet->suggestion_followed);
22367 
22368   if (resaudw) {
22369     xarate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
22370     xachans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
22371     xasamps = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
22372 
22373     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned))) {
22374       xse = AFORM_UNSIGNED;
22375     } else xse = 0;
22376     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))) {
22377       xse |= AFORM_BIG_ENDIAN;
22378     }
22379   } else {
22380     xachans = xarate = xasamps = 0;
22381     xse = mainw->files[mt->render_file]->signed_endian;
22382   }
22383 
22384   if (response == LIVES_RESPONSE_CANCEL) {
22385     lives_widget_destroy(rdet->dialog);
22386     lives_free(rdet->encoder_name);
22387     lives_freep((void **)&rdet);
22388     lives_freep((void **)&resaudw);
22389     return;
22390   }
22391 
22392   if (xachans == 0 && mt->audio_draws) {
22393     LiVESList *slist = mt->audio_draws;
22394     while (slist) {
22395       if (lives_widget_object_get_data(LIVES_WIDGET_OBJECT(slist->data), "blocks")) {
22396         do_mt_no_audchan_error();
22397         lives_widget_destroy(rdet->dialog);
22398         lives_free(rdet->encoder_name);
22399         lives_freep((void **)&rdet);
22400         lives_freep((void **)&resaudw);
22401         return;
22402       }
22403       slist = slist->next;
22404     }
22405   }
22406 
22407   if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rdet->always_checkbutton))) {
22408     prefs->mt_enter_prompt = FALSE;
22409     set_boolean_pref(PREF_MT_ENTER_PROMPT, prefs->mt_enter_prompt);
22410     prefs->mt_def_width = rdet->width;
22411     set_int_pref(PREF_MT_DEF_WIDTH, prefs->mt_def_width);
22412     prefs->mt_def_height = rdet->height;
22413     set_int_pref(PREF_MT_DEF_HEIGHT, prefs->mt_def_height);
22414     prefs->mt_def_fps = rdet->fps;
22415     set_double_pref(PREF_MT_DEF_FPS, prefs->mt_def_fps);
22416     prefs->mt_def_arate = xarate;
22417     set_int_pref(PREF_MT_DEF_ARATE, prefs->mt_def_arate);
22418     prefs->mt_def_achans = xachans;
22419     set_int_pref(PREF_MT_DEF_ACHANS, prefs->mt_def_achans);
22420     prefs->mt_def_asamps = xasamps;
22421     set_int_pref(PREF_MT_DEF_ASAMPS, prefs->mt_def_asamps);
22422     prefs->mt_def_signed_endian = xse;
22423     set_int_pref(PREF_MT_DEF_SIGNED_ENDIAN, prefs->mt_def_signed_endian);
22424     prefs->mt_pertrack_audio = ptaud;
22425     set_boolean_pref(PREF_MT_PERTRACK_AUDIO, prefs->mt_pertrack_audio);
22426     prefs->mt_backaudio = btaud;
22427     set_int_pref(PREF_MT_BACKAUDIO, prefs->mt_backaudio);
22428   } else {
22429     if (!prefs->mt_enter_prompt) {
22430       prefs->mt_enter_prompt = TRUE;
22431       set_boolean_pref(PREF_MT_ENTER_PROMPT, prefs->mt_enter_prompt);
22432     }
22433   }
22434 
22435   lives_widget_destroy(rdet->dialog);
22436 
22437   mt->user_width = rdet->width;
22438   mt->user_height = rdet->height;
22439   mt->user_fps = rdet->fps;
22440   mt->user_arate = xarate;
22441   mt->user_achans = xachans;
22442   mt->user_asamps = xasamps;
22443   mt->user_signed_endian = xse;
22444 
22445   lives_free(rdet->encoder_name);
22446   lives_freep((void **)&rdet);
22447   lives_freep((void **)&resaudw);
22448 
22449   msg = set_values_from_defs(mt, FALSE);
22450   if (msg) {
22451     d_print(msg);
22452     lives_free(msg);
22453 
22454     set_mt_title(mt);
22455   }
22456 
22457   if (mainw->files[mt->render_file]->achans == 0) {
22458     delete_audio_tracks(mt, mt->audio_draws, FALSE);
22459     mt->audio_draws = NULL;
22460 
22461     if (mt->amixer) on_amixer_close_clicked(NULL, mt);
22462 
22463     if (mt->audio_vols) lives_list_free(mt->audio_vols);
22464     mt->audio_vols = NULL;
22465   }
22466 
22467   set_interactive(prefs->interactive);
22468 
22469   scroll_tracks(mt, mt->top_track, FALSE);
22470 
22471   if (mt->current_track >= 0) {
22472     mt_show_current_frame(mt, FALSE); // show full preview in play window
22473   }
22474 
22475   mt->auto_changed = TRUE;
22476   if (prefs->mt_auto_back >= 0) save_mt_autoback(mt);
22477   mt->changed = TRUE;
22478 }
22479 
22480 
event_list_get_byte_size(lives_mt * mt,weed_plant_t * event_list,boolean nxprev,int * num_events)22481 static uint32_t event_list_get_byte_size(lives_mt * mt, weed_plant_t *event_list, boolean nxprev, int *num_events) {
22482   // return serialisation size
22483   int i, j;
22484   uint32_t tot = 0;
22485   weed_plant_t *event = get_first_event(event_list);
22486   char **leaves;
22487   weed_size_t ne;
22488   uint32_t st;
22489   int tot_events = 0;
22490 
22491   // write extra bits in event_list
22492   save_event_list_inner(mt, -1, event_list, NULL);
22493 
22494   while (event) {
22495     if (WEED_EVENT_IS_FILTER_INIT(event)) {
22496       weed_leaf_delete(event, WEED_LEAF_EVENT_ID);
22497       weed_set_int64_value(event, WEED_LEAF_EVENT_ID, (uint64_t)((void *)event));
22498     }
22499     tot_events++;
22500     leaves = weed_plant_list_leaves(event, NULL);
22501     tot += 4; //number of leaves
22502     for (i = 0; leaves[i]; i++) {
22503       if (!nxprev && (!strcmp(leaves[i], WEED_LEAF_NEXT) || !strcmp(leaves[i], WEED_LEAF_PREVIOUS))) {
22504         lives_free(leaves[i]);
22505         continue;
22506       }
22507       tot += 4 * 3 + strlen(leaves[i]); // key_length, seed_type, num_elements
22508       ne = weed_leaf_num_elements(event, leaves[i]);
22509       st = weed_leaf_seed_type(event, leaves[i]);
22510       // sum data_len + data
22511       for (j = 0; j < ne; j++) tot += 4 + (st > 64 ? 8 : weed_leaf_element_size(event, leaves[i], j));
22512       lives_free(leaves[i]);
22513     }
22514     lives_free(leaves);
22515     event = get_next_event(event);
22516   }
22517 
22518   event = event_list;
22519   leaves = weed_plant_list_leaves(event, NULL);
22520   tot += 4;
22521   for (i = 0; leaves[i]; i++) {
22522     tot += 4 * 3 + strlen(leaves[i]);
22523     ne = weed_leaf_num_elements(event, leaves[i]);
22524     st = weed_leaf_seed_type(event, leaves[i]);
22525     // sum data_len + data
22526     for (j = 0; j < ne; j++) tot += 4 + (st > 64 ? 8 : weed_leaf_element_size(event, leaves[i], j));
22527     lives_free(leaves[i]);
22528   }
22529   lives_free(leaves);
22530 
22531   if (num_events) *num_events = tot_events;
22532   return tot;
22533 }
22534 
22535 
on_amixer_close_clicked(LiVESButton * button,lives_mt * mt)22536 void on_amixer_close_clicked(LiVESButton * button, lives_mt * mt) {
22537   lives_amixer_t *amixer = mt->amixer;
22538   double val;
22539 
22540   if (!LIVES_IS_INTERACTIVE) return;
22541 
22542   mt->opts.gang_audio = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(amixer->gang_checkbutton));
22543 
22544   // set vols from slider vals
22545 
22546   for (int i = 0; i < amixer->nchans; i++) {
22547 #if ENABLE_GIW
22548     if (prefs->lamp_buttons) {
22549       val = giw_vslider_get_value(GIW_VSLIDER(amixer->ch_sliders[i]));
22550     } else {
22551 #endif
22552       val = lives_range_get_value(LIVES_RANGE(amixer->ch_sliders[i]));
22553 #if ENABLE_GIW
22554     }
22555 #endif
22556     if (0)
22557       val = lives_vol_from_linear(val);
22558     set_mixer_track_vol(mt, i, val);
22559   }
22560 
22561   lives_widget_destroy(amixer->window);
22562   lives_free(amixer->ch_sliders);
22563   lives_free(amixer->ch_slider_fns);
22564   lives_free(amixer);
22565   mt->amixer = NULL;
22566   if (mt->audio_vols_back) lives_list_free(mt->audio_vols_back);
22567   //lives_widget_set_sensitive(mt->prerender_aud,TRUE);
22568 }
22569 
22570 
on_amixer_reset_clicked(LiVESButton * button,lives_mt * mt)22571 static void on_amixer_reset_clicked(LiVESButton * button, lives_mt * mt) {
22572   lives_amixer_t *amixer = mt->amixer;
22573   int i;
22574 
22575   if (!LIVES_IS_INTERACTIVE) return;
22576 
22577   lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(amixer->inv_checkbutton), FALSE);
22578   lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(amixer->gang_checkbutton), mt->opts.gang_audio);
22579 
22580   // copy vols to slider vals
22581 
22582   for (i = 0; i < amixer->nchans; i++) {
22583     float val = (float)LIVES_POINTER_TO_INT(lives_list_nth_data(mt->audio_vols_back, i)) / LIVES_AVOL_SCALE;
22584     if (0)
22585       val = lives_vol_to_linear(val);
22586 #if ENABLE_GIW
22587     if (prefs->lamp_buttons) {
22588       lives_signal_handler_block(giw_vslider_get_adjustment(GIW_VSLIDER(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22589       giw_vslider_set_value(GIW_VSLIDER(amixer->ch_sliders[i]), val);
22590       lives_signal_handler_unblock(giw_vslider_get_adjustment(GIW_VSLIDER(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22591     } else {
22592 #endif
22593       lives_signal_handler_block(lives_range_get_adjustment(LIVES_RANGE(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22594       lives_range_set_value(LIVES_RANGE(amixer->ch_sliders[i]), val);
22595       //lives_scale_add_mark(LIVES_SCALE(amixer->ch_sliders[i]), val, LIVES_POS_LEFT, NULL);
22596       //lives_scale_add_mark(LIVES_SCALE(amixer->ch_sliders[i]), val, LIVES_POS_RIGHT, NULL);
22597       lives_signal_handler_unblock(lives_range_get_adjustment(LIVES_RANGE(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22598 #if ENABLE_GIW
22599     }
22600 #endif
22601   }
22602 }
22603 
22604 
after_amixer_gang_toggled(LiVESToggleButton * toggle,lives_amixer_t * amixer)22605 static void after_amixer_gang_toggled(LiVESToggleButton * toggle, lives_amixer_t *amixer) {
22606   lives_widget_set_sensitive(amixer->inv_checkbutton, (lives_toggle_button_get_active(toggle)));
22607 }
22608 
22609 
on_amixer_slider_changed(LiVESAdjustment * adj,lives_mt * mt)22610 void on_amixer_slider_changed(LiVESAdjustment * adj, lives_mt * mt) {
22611   lives_amixer_t *amixer = mt->amixer;
22612   int layer = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(adj), "layer"));
22613   boolean gang = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(amixer->gang_checkbutton));
22614   boolean inv = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(amixer->inv_checkbutton));
22615   double val;
22616   int i;
22617 
22618 #if ENABLE_GIW
22619   if (prefs->lamp_buttons) {
22620     GiwVSlider *slider = GIW_VSLIDER(amixer->ch_sliders[layer]);
22621     val = giw_vslider_get_value(slider);
22622   } else {
22623 #endif
22624     if (TRUE) {
22625       LiVESRange *range = LIVES_RANGE(amixer->ch_sliders[layer]);
22626       val = lives_range_get_value(range);
22627     }
22628 #if ENABLE_GIW
22629   }
22630 #endif
22631 
22632   if (gang) {
22633     if (layer > 0) {
22634       for (i = mt->opts.back_audio_tracks; i < amixer->nchans; i++) {
22635 #if ENABLE_GIW
22636         if (prefs->lamp_buttons) {
22637           lives_signal_handler_block(giw_vslider_get_adjustment(GIW_VSLIDER(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22638           giw_vslider_set_value(GIW_VSLIDER(amixer->ch_sliders[i]), val);
22639           lives_signal_handler_unblock(giw_vslider_get_adjustment(GIW_VSLIDER(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22640         } else {
22641 #endif
22642           lives_signal_handler_block(lives_range_get_adjustment(LIVES_RANGE(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22643           lives_range_set_value(LIVES_RANGE(amixer->ch_sliders[i]), val);
22644           lives_signal_handler_unblock(lives_range_get_adjustment(LIVES_RANGE(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22645 #if ENABLE_GIW
22646         }
22647 #endif
22648       }
22649       if (inv && mt->opts.back_audio_tracks > 0) {
22650 #if ENABLE_GIW
22651         if (prefs->lamp_buttons) {
22652           lives_signal_handler_block(giw_vslider_get_adjustment(GIW_VSLIDER(amixer->ch_sliders[0])), amixer->ch_slider_fns[0]);
22653           giw_vslider_set_value(GIW_VSLIDER(amixer->ch_sliders[0]), 1. - val < 0. ? 0. : 1. - val);
22654           lives_signal_handler_unblock(giw_vslider_get_adjustment(GIW_VSLIDER(amixer->ch_sliders[0])), amixer->ch_slider_fns[0]);
22655         } else {
22656 #endif
22657           lives_signal_handler_block(lives_range_get_adjustment(LIVES_RANGE(amixer->ch_sliders[0])), amixer->ch_slider_fns[0]);
22658           lives_range_set_value(LIVES_RANGE(amixer->ch_sliders[0]), 1. - val);
22659           lives_signal_handler_unblock(lives_range_get_adjustment(LIVES_RANGE(amixer->ch_sliders[0])), amixer->ch_slider_fns[0]);
22660 #if ENABLE_GIW
22661         }
22662 #endif
22663       }
22664     } else {
22665       if (inv) {
22666         for (i = 1; i < amixer->nchans; i++) {
22667 #if ENABLE_GIW
22668           if (prefs->lamp_buttons) {
22669             lives_signal_handler_block(giw_vslider_get_adjustment(GIW_VSLIDER(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22670             giw_vslider_set_value(GIW_VSLIDER(amixer->ch_sliders[i]), 1. - val < 0. ? 0. : 1. - val);
22671             lives_signal_handler_unblock(giw_vslider_get_adjustment(GIW_VSLIDER(amixer->ch_sliders[i])),
22672                                          amixer->ch_slider_fns[i]);
22673           } else {
22674 #endif
22675             lives_signal_handler_block(lives_range_get_adjustment(LIVES_RANGE(amixer->ch_sliders[i])), amixer->ch_slider_fns[i]);
22676             lives_range_set_value(LIVES_RANGE(amixer->ch_sliders[i]), 1. - val);
22677             lives_signal_handler_unblock(lives_range_get_adjustment(LIVES_RANGE(amixer->ch_sliders[i])),
22678                                          amixer->ch_slider_fns[i]);
22679 #if ENABLE_GIW
22680           }
22681 #endif
22682 	  // *INDENT-OFF*
22683         }}}}
22684   // *INDENT-ON*
22685 
22686   if (!mt->is_rendering) {
22687     if (0)
22688       val = lives_vol_from_linear(val);
22689     set_mixer_track_vol(mt, layer, val);
22690   }
22691 }
22692 
22693 
amixer_add_channel_slider(lives_mt * mt,int i)22694 LiVESWidget *amixer_add_channel_slider(lives_mt * mt, int i) {
22695   // add a slider to audio mixer for layer i; i<0 are backing audio tracks
22696   // automatically sets the track name and layer number
22697 
22698   LiVESWidgetObject *adj;
22699   LiVESWidget *spinbutton;
22700   LiVESWidget *label;
22701   LiVESWidget *vbox;
22702   lives_amixer_t *amixer = mt->amixer;
22703   char *tname;
22704 
22705   i += mt->opts.back_audio_tracks;
22706 
22707   adj = (LiVESWidgetObject *)lives_adjustment_new(0.5, 0., 4., 0.01, 0.01, 0.);
22708 
22709 #if ENABLE_GIW
22710   if (prefs->lamp_buttons) {
22711     amixer->ch_sliders[i] = giw_vslider_new(LIVES_ADJUSTMENT(adj));
22712     giw_vslider_set_legends_digits(GIW_VSLIDER(amixer->ch_sliders[i]), 1);
22713     giw_vslider_set_major_ticks_number(GIW_VSLIDER(amixer->ch_sliders[i]), 5);
22714     giw_vslider_set_minor_ticks_number(GIW_VSLIDER(amixer->ch_sliders[i]), 4);
22715     if (palette->style & STYLE_1) {
22716       lives_widget_set_bg_color(amixer->ch_sliders[i], LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
22717     }
22718   } else {
22719 #endif
22720     amixer->ch_sliders[i] = lives_vscale_new(LIVES_ADJUSTMENT(adj));
22721     lives_range_set_inverted(LIVES_RANGE(amixer->ch_sliders[i]), TRUE);
22722     lives_scale_set_digits(LIVES_SCALE(amixer->ch_sliders[i]), 2);
22723     lives_scale_set_value_pos(LIVES_SCALE(amixer->ch_sliders[i]), LIVES_POS_BOTTOM);
22724 #if ENABLE_GIW
22725   }
22726 #endif
22727 
22728   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(amixer->ch_sliders[i]), "adj", adj);
22729   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(adj), "layer", LIVES_INT_TO_POINTER(i));
22730 
22731   amixer->ch_slider_fns[i] = lives_signal_sync_connect_after(LIVES_GUI_OBJECT(adj), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
22732                              LIVES_GUI_CALLBACK(on_amixer_slider_changed), (livespointer)mt);
22733 
22734   if (palette->style & STYLE_1) {
22735     lives_widget_set_fg_color(amixer->ch_sliders[i], LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
22736   }
22737 
22738   tname = get_track_name(mt, i - mt->opts.back_audio_tracks, TRUE);
22739   label = lives_standard_label_new(tname);
22740   lives_free(tname);
22741 
22742   lives_widget_object_set_data(LIVES_WIDGET_OBJECT(amixer->ch_sliders[i]), "label", label);
22743 
22744   vbox = lives_vbox_new(FALSE, widget_opts.packing_height * 1.5);
22745   lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
22746   lives_box_pack_start(LIVES_BOX(vbox), amixer->ch_sliders[i], TRUE, TRUE, widget_opts.packing_height * 5);
22747 
22748   spinbutton = lives_standard_spin_button_new(NULL, 0.5, 0., 4., 0.01, 0.01, 3, LIVES_BOX(vbox), NULL);
22749   gtk_spin_button_set_adjustment(LIVES_SPIN_BUTTON(spinbutton), LIVES_ADJUSTMENT(adj));
22750 
22751   amixer->nchans++;
22752 
22753   return vbox;
22754 }
22755 
22756 
amixer_show(LiVESButton * button,livespointer user_data)22757 void amixer_show(LiVESButton * button, livespointer user_data) {
22758   lives_mt *mt = (lives_mt *)user_data;
22759   LiVESWidget *amixerw;
22760   LiVESWidget *top_vbox;
22761   LiVESWidget *vbox;
22762   LiVESWidget *vbox2;
22763   LiVESWidget *hbox;
22764   LiVESWidget *hbuttonbox;
22765   LiVESWidget *scrolledwindow;
22766   LiVESWidget *label;
22767   LiVESWidget *filler;
22768   LiVESWidget *eventbox;
22769   LiVESWidget *close_button;
22770   LiVESWidget *reset_button;
22771   LiVESAccelGroup *accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
22772 
22773   lives_amixer_t *amixer;
22774 
22775   int nachans = lives_list_length(mt->audio_draws);
22776 
22777   int winsize_h = GUI_SCREEN_WIDTH * AMIXER_WRATIO;
22778   int winsize_v = GUI_SCREEN_HEIGHT * AMIXER_HRATIO;
22779 
22780   if (!LIVES_IS_INTERACTIVE) return;
22781 
22782   if (nachans == 0) return;
22783 
22784   if (mt->amixer) {
22785     on_amixer_close_clicked(NULL, mt);
22786     return;
22787   }
22788 
22789   mt->audio_vols_back = lives_list_copy(mt->audio_vols);
22790 
22791   amixer = mt->amixer = (lives_amixer_t *)lives_malloc(sizeof(lives_amixer_t));
22792   amixer->nchans = 0;
22793 
22794   amixer->ch_sliders = (LiVESWidget **)lives_malloc(nachans * sizeof(LiVESWidget *));
22795   amixer->ch_slider_fns = (ulong *)lives_malloc(nachans * sizeof(ulong));
22796 
22797   amixer->window = amixerw = lives_window_new(LIVES_WINDOW_TOPLEVEL);
22798   if (palette->style & STYLE_1) {
22799     lives_widget_set_bg_color(amixerw, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
22800     lives_widget_set_fg_color(amixerw, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
22801   }
22802 
22803   lives_window_set_title(LIVES_WINDOW(amixerw), _("Multitrack Audio Mixer"));
22804 
22805   top_vbox = lives_vbox_new(FALSE, 0);
22806 
22807   amixer->main_hbox = lives_hbox_new(FALSE, widget_opts.packing_width * 2);
22808 
22809   scrolledwindow = lives_standard_scrolled_window_new(winsize_h, winsize_v, amixer->main_hbox);
22810 
22811   if (prefs->gui_monitor != 0) {
22812     lives_window_center(LIVES_WINDOW(amixerw));
22813   }
22814 
22815   lives_window_set_transient_for(LIVES_WINDOW(amixerw), LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET));
22816 
22817   lives_box_pack_start(LIVES_BOX(top_vbox), scrolledwindow, TRUE, TRUE, widget_opts.packing_height);
22818   lives_container_add(LIVES_CONTAINER(amixerw), top_vbox);
22819 
22820   hbuttonbox = lives_hbutton_box_new();
22821   lives_box_pack_start(LIVES_BOX(top_vbox), hbuttonbox, FALSE, TRUE, widget_opts.packing_height * 2);
22822 
22823   lives_button_box_set_layout(LIVES_BUTTON_BOX(hbuttonbox), LIVES_BUTTONBOX_SPREAD);
22824 
22825   filler = add_fill_to_box(LIVES_BOX(hbuttonbox));
22826   lives_widget_apply_theme2(filler, LIVES_WIDGET_STATE_NORMAL, TRUE);
22827 
22828   reset_button = lives_dialog_add_button_from_stock(NULL, NULL, _("_Reset values"),
22829                  LIVES_RESPONSE_RESET);
22830 
22831   lives_container_add(LIVES_CONTAINER(hbuttonbox), reset_button);
22832 
22833   close_button = lives_dialog_add_button_from_stock(NULL, NULL, _("_Close mixer"),
22834                  LIVES_RESPONSE_OK);
22835 
22836   lives_container_add(LIVES_CONTAINER(hbuttonbox), close_button);
22837 
22838   filler = add_fill_to_box(LIVES_BOX(hbuttonbox));
22839   lives_widget_apply_theme2(filler, LIVES_WIDGET_STATE_NORMAL, TRUE);
22840 
22841   lives_widget_add_accelerator(close_button, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
22842                                LIVES_KEY_m, LIVES_CONTROL_MASK,
22843                                (LiVESAccelFlags)0);
22844 
22845   lives_window_add_accel_group(LIVES_WINDOW(amixerw), accel_group);
22846 
22847   if (mt->opts.back_audio_tracks > 0) {
22848     vbox = amixer_add_channel_slider(mt, -1);
22849     lives_box_pack_start(LIVES_BOX(amixer->main_hbox), vbox, FALSE, FALSE, widget_opts.packing_width);
22850   }
22851 
22852   vbox2 = lives_vbox_new(FALSE, 0);
22853   lives_box_pack_start(LIVES_BOX(amixer->main_hbox), vbox2, FALSE, FALSE, widget_opts.packing_width);
22854 
22855   add_fill_to_box(LIVES_BOX(vbox2));
22856 
22857   vbox = lives_vbox_new(FALSE, 0);
22858   lives_box_pack_start(LIVES_BOX(vbox2), vbox, TRUE, TRUE, widget_opts.packing_height);
22859 
22860   if (prefs->lamp_buttons) {
22861     amixer->inv_checkbutton = lives_check_button_new_with_label(" ");
22862     lives_toggle_button_set_mode(LIVES_TOGGLE_BUTTON(amixer->inv_checkbutton), FALSE);
22863 #if GTK_CHECK_VERSION(3, 0, 0)
22864     lives_signal_sync_connect(LIVES_GUI_OBJECT(amixer->inv_checkbutton), LIVES_WIDGET_EXPOSE_EVENT,
22865                               LIVES_GUI_CALLBACK(draw_cool_toggle),
22866                               NULL);
22867 #endif
22868     lives_widget_set_bg_color(amixer->inv_checkbutton, LIVES_WIDGET_STATE_ACTIVE, &palette->light_green);
22869     lives_widget_set_bg_color(amixer->inv_checkbutton, LIVES_WIDGET_STATE_NORMAL, &palette->dark_red);
22870 
22871     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(amixer->inv_checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
22872                                     LIVES_GUI_CALLBACK(lives_cool_toggled),
22873                                     NULL);
22874 
22875     lives_cool_toggled(amixer->inv_checkbutton, NULL);
22876 
22877   } else amixer->inv_checkbutton = lives_check_button_new();
22878 
22879   if (mt->opts.back_audio_tracks > 0 && mt->opts.pertrack_audio) {
22880     label = lives_standard_label_new_with_mnemonic_widget(_("_Invert backing audio\nand layer volumes"), amixer->inv_checkbutton);
22881 
22882     lives_widget_set_tooltip_text(amixer->inv_checkbutton, _("Adjust backing and layer audio values so that they sum to 1.0"));
22883     eventbox = lives_event_box_new();
22884     lives_tooltips_copy(eventbox, amixer->inv_checkbutton);
22885     lives_label_set_mnemonic_widget(LIVES_LABEL(label), amixer->inv_checkbutton);
22886 
22887     lives_container_add(LIVES_CONTAINER(eventbox), label);
22888     lives_signal_sync_connect(LIVES_GUI_OBJECT(eventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
22889                               LIVES_GUI_CALLBACK(label_act_toggle),
22890                               amixer->inv_checkbutton);
22891 
22892     if (palette->style & STYLE_1) {
22893       lives_widget_apply_theme(eventbox, LIVES_WIDGET_STATE_NORMAL);
22894     }
22895 
22896     hbox = lives_hbox_new(FALSE, 0);
22897     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
22898     lives_box_pack_start(LIVES_BOX(hbox), eventbox, FALSE, FALSE, widget_opts.packing_width);
22899     lives_box_pack_start(LIVES_BOX(hbox), amixer->inv_checkbutton, FALSE, FALSE, 0);
22900     lives_widget_set_can_focus_and_default(amixer->inv_checkbutton);
22901   }
22902 
22903   if (prefs->lamp_buttons) {
22904     amixer->gang_checkbutton = lives_check_button_new_with_label(" ");
22905     lives_toggle_button_set_mode(LIVES_TOGGLE_BUTTON(amixer->gang_checkbutton), FALSE);
22906 #if GTK_CHECK_VERSION(3, 0, 0)
22907     lives_signal_sync_connect(LIVES_GUI_OBJECT(amixer->gang_checkbutton), LIVES_WIDGET_EXPOSE_EVENT,
22908                               LIVES_GUI_CALLBACK(draw_cool_toggle),
22909                               NULL);
22910 #endif
22911     lives_widget_set_bg_color(amixer->gang_checkbutton, LIVES_WIDGET_STATE_ACTIVE, &palette->light_green);
22912     lives_widget_set_bg_color(amixer->gang_checkbutton, LIVES_WIDGET_STATE_NORMAL, &palette->dark_red);
22913   } else amixer->gang_checkbutton = lives_check_button_new();
22914 
22915   if (mt->opts.pertrack_audio) {
22916     label = lives_standard_label_new_with_mnemonic_widget(_("_Gang layer audio"), amixer->gang_checkbutton);
22917 
22918     lives_widget_set_tooltip_text(amixer->gang_checkbutton, _("Adjust all layer audio values to the same value"));
22919     eventbox = lives_event_box_new();
22920     lives_tooltips_copy(eventbox, amixer->gang_checkbutton);
22921 
22922     lives_container_add(LIVES_CONTAINER(eventbox), label);
22923     lives_signal_sync_connect(LIVES_GUI_OBJECT(eventbox), LIVES_WIDGET_BUTTON_PRESS_EVENT,
22924                               LIVES_GUI_CALLBACK(label_act_toggle),
22925                               amixer->gang_checkbutton);
22926 
22927     lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(amixer->gang_checkbutton), mt->opts.gang_audio);
22928 
22929     if (palette->style & STYLE_1) {
22930       lives_widget_apply_theme(eventbox, LIVES_WIDGET_STATE_NORMAL);
22931     }
22932 
22933     hbox = lives_hbox_new(FALSE, 0);
22934     lives_box_pack_end(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
22935     lives_box_pack_start(LIVES_BOX(hbox), eventbox, FALSE, FALSE, widget_opts.packing_width);
22936     lives_box_pack_start(LIVES_BOX(hbox), amixer->gang_checkbutton, FALSE, FALSE, widget_opts.packing_width);
22937     lives_widget_set_can_focus_and_default(amixer->gang_checkbutton);
22938   }
22939 
22940   add_fill_to_box(LIVES_BOX(vbox2));
22941 
22942   for (register int i = 0; i < nachans - mt->opts.back_audio_tracks; i++) {
22943     vbox = amixer_add_channel_slider(mt, i);
22944     lives_box_pack_start(LIVES_BOX(amixer->main_hbox), vbox, FALSE, FALSE, widget_opts.packing_width);
22945   }
22946 
22947   lives_signal_sync_connect(LIVES_GUI_OBJECT(close_button), LIVES_WIDGET_CLICKED_SIGNAL,
22948                             LIVES_GUI_CALLBACK(on_amixer_close_clicked),
22949                             (livespointer)mt);
22950 
22951   lives_signal_sync_connect(LIVES_GUI_OBJECT(reset_button), LIVES_WIDGET_CLICKED_SIGNAL,
22952                             LIVES_GUI_CALLBACK(on_amixer_reset_clicked),
22953                             (livespointer)mt);
22954 
22955   lives_widget_add_accelerator(close_button, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
22956                                LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
22957 
22958   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(amixer->gang_checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
22959                                   LIVES_GUI_CALLBACK(after_amixer_gang_toggled),
22960                                   (livespointer)amixer);
22961   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(amixer->gang_checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
22962                                   LIVES_GUI_CALLBACK(lives_cool_toggled),
22963                                   NULL);
22964 
22965   lives_cool_toggled(amixer->gang_checkbutton, NULL);
22966   after_amixer_gang_toggled(LIVES_TOGGLE_BUTTON(amixer->gang_checkbutton), amixer);
22967 
22968   lives_widget_grab_focus(close_button);
22969 
22970   on_amixer_reset_clicked(NULL, mt);
22971 
22972   lives_widget_show_all(amixerw);
22973 }
22974 
22975 
on_mt_showkeys_activate(LiVESMenuItem * menuitem,livespointer user_data)22976 void on_mt_showkeys_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22977   do_mt_keys_window();
22978 }
22979 
22980 
get_eventbox_for_track(lives_mt * mt,int ntrack)22981 LiVESWidget *get_eventbox_for_track(lives_mt * mt, int ntrack) {
22982   LiVESWidget *eventbox = NULL;
22983   if (mt) {
22984     if (mt_track_is_video(mt, ntrack)) {
22985       eventbox = (LiVESWidget *)lives_list_nth_data(mt->video_draws, ntrack);
22986     } else if (mt_track_is_audio(mt, ntrack)) {
22987       eventbox = (LiVESWidget *)lives_list_nth_data(mt->audio_draws, 1 - ntrack);
22988     }
22989   }
22990   return eventbox;
22991 }
22992 
22993 
get_nth_block_for_track(lives_mt * mt,int itrack,int iblock)22994 static track_rect *get_nth_block_for_track(lives_mt * mt, int itrack, int iblock) {
22995   int count = 0;
22996   track_rect *block;
22997   LiVESWidget *eventbox = get_eventbox_for_track(mt, itrack);
22998   if (!eventbox) return NULL; //<invalid track
22999   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
23000   while (block) {
23001     if (count == iblock) return block;
23002     block = block->next;
23003     count++;
23004   }
23005 
23006   return NULL; ///<invalid block
23007 }
23008 
23009 
23010 // remote API helpers
23011 
find_block_by_uid(lives_mt * mt,ulong uid)23012 track_rect *find_block_by_uid(lives_mt * mt, ulong uid) {
23013   LiVESList *list;
23014   track_rect *block;
23015 
23016   if (!mt || uid == 0l) return NULL;
23017 
23018   list = mt->video_draws;
23019 
23020   while (list) {
23021     block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(list->data), "blocks");
23022     while (block) {
23023       if (block->uid == uid) return block;
23024       block = block->next;
23025     }
23026   }
23027 
23028   list = mt->audio_draws;
23029 
23030   while (list) {
23031     block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(list->data), "blocks");
23032     while (block) {
23033       if (block->uid == uid) return block;
23034       block = block->next;
23035     }
23036   }
23037 
23038   return NULL;
23039 }
23040 
23041 
mt_track_is_video(lives_mt * mt,int ntrack)23042 boolean mt_track_is_video(lives_mt * mt, int ntrack) {
23043   if (ntrack >= 0 && mt->video_draws && ntrack < lives_list_length(mt->video_draws)) return TRUE;
23044   return FALSE;
23045 }
23046 
23047 
mt_track_is_audio(lives_mt * mt,int ntrack)23048 boolean mt_track_is_audio(lives_mt * mt, int ntrack) {
23049   if (ntrack <= 0 && mt->audio_draws && ntrack >= -(lives_list_length(mt->audio_draws))) return TRUE;
23050   return FALSE;
23051 }
23052 
23053 
mt_get_last_block_uid(lives_mt * mt)23054 ulong mt_get_last_block_uid(lives_mt * mt) {
23055   int track = mt->current_track;
23056   track_rect *lastblock;
23057   LiVESWidget *eventbox = get_eventbox_for_track(mt, track);
23058   if (!eventbox) return 0l; //<invalid track
23059   lastblock = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
23060   if (!lastblock) return 0l; ///< no blocks in track
23061   return lastblock->uid;
23062 }
23063 
23064 
mt_get_block_count(lives_mt * mt,int ntrack)23065 int mt_get_block_count(lives_mt * mt, int ntrack) {
23066   int count = 0;
23067   track_rect *block, *lastblock;
23068   LiVESWidget *eventbox = get_eventbox_for_track(mt, ntrack);
23069   if (!eventbox) return -1; //<invalid track
23070   lastblock = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "block_last");
23071   if (!lastblock) return -1; ///< no blocks in track
23072   block = (track_rect *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(eventbox), "blocks");
23073   while (block) {
23074     if (block == lastblock) break;
23075     block = block->next;
23076     count++;
23077   }
23078 
23079   return count;
23080 }
23081 
23082 
23083 /// return time in seconds of first frame event in block
mt_get_block_sttime(lives_mt * mt,int ntrack,int iblock)23084 double mt_get_block_sttime(lives_mt * mt, int ntrack, int iblock) {
23085   track_rect *block = get_nth_block_for_track(mt, ntrack, iblock);
23086   if (!block) return -1; ///< invalid track or block number
23087   return (double)get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL;
23088 }
23089 
23090 
23091 /// return time in seconds of last frame event in block, + event duration
mt_get_block_entime(lives_mt * mt,int ntrack,int iblock)23092 double mt_get_block_entime(lives_mt * mt, int ntrack, int iblock) {
23093   track_rect *block = get_nth_block_for_track(mt, ntrack, iblock);
23094   if (!block) return -1; ///< invalid track or block number
23095   return (double)get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL + 1. / mt->fps;
23096 }
23097 
23098 
get_block_from_track_and_time(lives_mt * mt,int track,double time)23099 track_rect *get_block_from_track_and_time(lives_mt * mt, int track, double time) {
23100   LiVESWidget *ebox;
23101   if (!mt) return NULL;
23102   ebox = get_eventbox_for_track(mt, track);
23103   return get_block_from_time(ebox, time, mt);
23104 }
23105 
23106 
get_clip_for_block(track_rect * block)23107 int get_clip_for_block(track_rect * block) {
23108   int track;
23109   if (!block) return -1;
23110   track = get_track_for_block(block);
23111   return get_frame_event_clip(block->start_event, track);
23112 }
23113 
23114 ////////////////////////////////////
23115 // autotransitions
23116 //
23117 
23118 
mt_do_autotransition(lives_mt * mt,track_rect * block)23119 void mt_do_autotransition(lives_mt * mt, track_rect * block) {
23120   // prefs->atrans_track0 should be the output track (usually the lower of the two)
23121 
23122   track_rect *oblock = NULL;
23123   weed_timecode_t sttc, endtc = 0;
23124 
23125   weed_plant_t **ptmpls;
23126   weed_plant_t **oparams;
23127 
23128   weed_plant_t *stevent, *enevent;
23129   weed_plant_t *filter;
23130   weed_plant_t *ptm;
23131   weed_plant_t *old_mt_init = mt->init_event;
23132 
23133   LiVESList *slist;
23134 
23135   double region_start = mt->region_start;
23136   double region_end = mt->region_end;
23137 
23138   boolean did_backup = FALSE;
23139 
23140   int nvids = lives_list_length(mt->video_draws);
23141   int current_fx = mt->current_fx;
23142 
23143   int tparam;
23144   int nparams = 0;
23145   int param_type;
23146   int track;
23147 
23148   int i;
23149 
23150   if (!block) return; ///<invalid block
23151 
23152   filter = get_weed_filter(prefs->atrans_fx);
23153   if (num_in_params(filter, TRUE, TRUE) == 0) return; ///<filter has no (visible) in parameters
23154 
23155   tparam = get_transition_param(filter, FALSE);
23156   if (tparam == -1) return; ///< filter has no transition parameter
23157 
23158   ptmpls = weed_filter_get_in_paramtmpls(filter, NULL);
23159   ptm = ptmpls[tparam];
23160   param_type = weed_paramtmpl_get_type(ptm);
23161 
23162   mt->current_fx = prefs->atrans_fx;
23163 
23164   sttc = get_event_timecode(block->start_event);
23165 
23166   track = get_track_for_block(block);
23167 
23168   // part 1 - transition in
23169 
23170   slist = lives_list_copy(mt->selected_tracks);
23171 
23172   if (mt->selected_tracks) {
23173     lives_list_free(mt->selected_tracks);
23174     mt->selected_tracks = NULL;
23175   }
23176 
23177   for (i = 0; i < nvids; i++) {
23178     if (i == track) continue; ///< cannot transition with self !
23179     oblock = get_block_from_time((LiVESWidget *)lives_list_nth_data(mt->video_draws, i),
23180                                  (double)sttc / TICKS_PER_SECOND_DBL + 0.5 / mt->fps, mt);
23181 
23182     if (oblock) {
23183       if (get_event_timecode(oblock->end_event) <= get_event_timecode(block->end_event)) {
23184         endtc = q_gint64(get_event_timecode(oblock->end_event) + TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
23185         break;
23186       } else oblock = NULL;
23187     }
23188   }
23189 
23190   mt->is_atrans = TRUE; ///< force some visual changes
23191 
23192   if (oblock) {
23193     mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(track));
23194     mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(i));
23195 
23196     //mt_backup(mt, MT_UNDO_APPLY_FILTER, 0);
23197 
23198     mt->region_start = sttc / TICKS_PER_SECOND_DBL;
23199     mt->region_end = endtc / TICKS_PER_SECOND_DBL;
23200     mt_add_region_effect(NULL, mt);
23201 
23202     oparams = (weed_plant_t **)weed_get_voidptr_array_counted(mt->init_event, WEED_LEAF_IN_PARAMETERS, &nparams);
23203 
23204     for (i = 0; i < nparams; i++) {
23205       if (weed_get_int_value(oparams[i], WEED_LEAF_INDEX, NULL) == tparam) break;
23206     }
23207 
23208     stevent = oparams[i];
23209 
23210     enevent = weed_plant_new(WEED_PLANT_EVENT);
23211     weed_set_int_value(enevent, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_PARAM_CHANGE);
23212     weed_set_int64_value(enevent, WEED_LEAF_TIMECODE, endtc);
23213     weed_set_int_value(enevent, WEED_LEAF_INDEX, tparam);
23214 
23215     weed_set_voidptr_value(enevent, WEED_LEAF_INIT_EVENT, mt->init_event);
23216     weed_set_voidptr_value(enevent, WEED_LEAF_NEXT_CHANGE, NULL);
23217     weed_set_voidptr_value(enevent, WEED_LEAF_PREV_CHANGE, stevent);
23218     //weed_add_plant_flags(enevent, WEED_LEAF_READONLY_PLUGIN);
23219 
23220     weed_set_voidptr_value(stevent, WEED_LEAF_NEXT_CHANGE, enevent);
23221 
23222     if (param_type == WEED_PARAM_INTEGER) {
23223       int min = weed_get_int_value(ptm, WEED_LEAF_MIN, NULL);
23224       int max = weed_get_int_value(ptm, WEED_LEAF_MAX, NULL);
23225       weed_set_int_value(stevent, WEED_LEAF_VALUE, i < track ? min : max);
23226       weed_set_int_value(enevent, WEED_LEAF_VALUE, i < track ? max : min);
23227     } else {
23228       double min = weed_get_double_value(ptm, WEED_LEAF_MIN, NULL);
23229       double max = weed_get_double_value(ptm, WEED_LEAF_MAX, NULL);
23230       weed_set_double_value(stevent, WEED_LEAF_VALUE, i < track ? min : max);
23231       weed_set_double_value(enevent, WEED_LEAF_VALUE, i < track ? max : min);
23232     }
23233 
23234     insert_param_change_event_at(mt->event_list, oblock->end_event, enevent);
23235     lives_free(oparams);
23236   }
23237 
23238   // part 2, check if there is a transition out
23239 
23240   oblock = NULL;
23241   endtc = q_gint64(get_event_timecode(block->end_event) + TICKS_PER_SECOND_DBL / mt->fps, mt->fps);
23242 
23243   if (mt->selected_tracks) {
23244     lives_list_free(mt->selected_tracks);
23245     mt->selected_tracks = NULL;
23246   }
23247 
23248   for (i = 0; i < nvids; i++) {
23249     if (i == track) continue; ///< cannot transition with self !
23250     oblock = get_block_from_time((LiVESWidget *)lives_list_nth_data(mt->video_draws, i),
23251                                  (double)endtc / TICKS_PER_SECOND_DBL + 0.5 / mt->fps, mt);
23252 
23253     if (oblock) {
23254       sttc = get_event_timecode(oblock->start_event);
23255       if (sttc < get_event_timecode(block->start_event)) oblock = NULL;
23256       else break;
23257     }
23258   }
23259 
23260   if (oblock) {
23261     mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(track));
23262     mt->selected_tracks = lives_list_append(mt->selected_tracks, LIVES_INT_TO_POINTER(i));
23263 
23264     //mt_backup(mt, MT_UNDO_APPLY_FILTER, 0);
23265 
23266     mt->region_start = sttc / TICKS_PER_SECOND_DBL;
23267     mt->region_end = endtc / TICKS_PER_SECOND_DBL;
23268     mt_add_region_effect(NULL, mt);
23269 
23270     oparams = (weed_plant_t **)weed_get_voidptr_array_counted(mt->init_event, WEED_LEAF_IN_PARAMETERS, &nparams);
23271 
23272     for (i = 0; i < nparams; i++) {
23273       if (weed_get_int_value(oparams[i], WEED_LEAF_INDEX, NULL) == tparam) break;
23274     }
23275 
23276     stevent = oparams[i];
23277 
23278     enevent = weed_plant_new(WEED_PLANT_EVENT);
23279     weed_set_int_value(enevent, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_PARAM_CHANGE);
23280     weed_event_set_timecode(enevent, weed_event_get_timecode(block->end_event));
23281     weed_set_int_value(enevent, WEED_LEAF_INDEX, tparam);
23282 
23283     weed_set_voidptr_value(enevent, WEED_LEAF_INIT_EVENT, mt->init_event);
23284     weed_set_voidptr_value(enevent, WEED_LEAF_NEXT_CHANGE, NULL);
23285     weed_set_voidptr_value(enevent, WEED_LEAF_PREV_CHANGE, stevent);
23286     //weed_add_plant_flags(enevent, WEED_LEAF_READONLY_PLUGIN);
23287 
23288     weed_set_voidptr_value(stevent, WEED_LEAF_NEXT_CHANGE, enevent);
23289 
23290     if (param_type == WEED_PARAM_INTEGER) {
23291       int min = weed_get_int_value(ptm, WEED_LEAF_MIN, NULL);
23292       int max = weed_get_int_value(ptm, WEED_LEAF_MAX, NULL);
23293       weed_set_int_value(stevent, WEED_LEAF_VALUE, i < track ? max : min);
23294       weed_set_int_value(enevent, WEED_LEAF_VALUE, i < track ? min : max);
23295     } else {
23296       double min = weed_get_double_value(ptm, WEED_LEAF_MIN, NULL);
23297       double max = weed_get_double_value(ptm, WEED_LEAF_MAX, NULL);
23298       weed_set_double_value(stevent, WEED_LEAF_VALUE, i < track ? max : min);
23299       weed_set_double_value(enevent, WEED_LEAF_VALUE, i < track ? min : max);
23300     }
23301 
23302     insert_param_change_event_at(mt->event_list, block->end_event, enevent);
23303     lives_free(oparams);
23304   }
23305 
23306   // crossfade audio
23307   if (mt->init_event && mt->opts.autocross_audio)
23308     weed_set_boolean_value(mt->init_event, WEED_LEAF_HOST_AUDIO_TRANSITION, WEED_TRUE);
23309 
23310   mt->is_atrans = FALSE;
23311   mt->region_start = region_start;
23312   mt->region_end = region_end;
23313   lives_list_free(mt->selected_tracks);
23314   mt->selected_tracks = lives_list_copy(slist);
23315   if (slist) lives_list_free(slist);
23316   mt->current_fx = current_fx;
23317   mt->init_event = old_mt_init;
23318 
23319   lives_free(ptmpls);
23320 
23321   mt->changed = mt->auto_changed = TRUE;
23322   mt->did_backup = did_backup;
23323 }
23324 
23325