LiVES  3.2.0
multitrack.c
Go to the documentation of this file.
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
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 
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 
115 static int renumbered_clips[MAX_FILES + 1];
116 
117 static double lfps[MAX_FILES + 1];
118 static void **pchain;
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 
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 
205 
206 #define LIVES_AVOL_SCALE ((double)1000000.)
207 
208 void maybe_add_mt_idlefunc(void) {
212  }
213 }
214 
215 
216 LIVES_INLINE int mt_file_from_clip(lives_mt *mt, int clip) {
217  return clips_to_files[clip];
218 }
219 
220 
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 
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 
236 LIVES_INLINE boolean is_empty_track(LiVESWidgetObject *track) {
237  return (lives_widget_object_get_data(track, "blocks") == NULL);
238 }
239 
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 
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 
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 
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 
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  }
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;
398  }
399  }
400  if (THREADVAR(write_failed)) return FALSE;
401  return TRUE;
402 }
403 
404 
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);
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 
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 
523 }
524 
525 
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 
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
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;
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;
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);
662 
663  if (pixbuf) lives_widget_object_unref(pixbuf);
664  if (cursor) lives_cursor_unref(cursor);
665 }
666 
667 
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  }
698  }
699 
700  if (THREADVAR(write_failed)) return FALSE;
701  return TRUE;
702 }
703 
704 
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 
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,
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,
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  }
777  }
778  lives_free(aload_file);
779 }
780 
781 
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(),
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
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 
818 
819  retval = save_event_list_inner(mt, fd, mt->event_list, NULL);
820  if (retval) retval = write_backup_layout_numbering(mt);
821 
823 
825 
827  paint_lines(mt, mt->ptr_time, FALSE, NULL);
828 
829  remove_markers(mt->event_list);
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 
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 
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 
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 
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 
916 
918 
919  return retval;
920 }
921 
922 
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(),
926 
927  if (is_startup) mainw->recoverable_layout = FALSE;
928 
929  lives_rm(eload_file);
930  lives_free(eload_file);
931 
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 
941 boolean mt_load_recovery_layout(lives_mt *mt) {
942  boolean recovered = TRUE;
943  char *aload_file = NULL, *eload_file;
944 
945  if (mt) {
947  capable->mainpid);
948  eload_file = lives_strdup_printf("%s/%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, lives_getuid(), lives_getgid(),
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(),
968 
969  if ((fd = lives_open_buffered_rdonly(eload_file)) < 0) {
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 
1010 boolean recover_layout(void) {
1011  boolean loaded = TRUE;
1012 
1014  if (!on_multitrack_activate(NULL, NULL)) {
1015  loaded = FALSE;
1018  }
1019  } else {
1020  mainw->multitrack->auto_reloading = TRUE;
1021  set_string_pref(PREF_AR_LAYOUT, ""); // in case we crash...
1023  mainw->multitrack->auto_reloading = FALSE;
1026  }
1029  return loaded;
1030 }
1031 
1032 
1033 void **mt_get_pchain(void) {
1034  return pchain;
1035 }
1036 
1037 
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 
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 
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 
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 
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 
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  }
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 
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 
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;
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 
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 
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:
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 
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
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
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
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;
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 {
1345  lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(eventbox));
1346  lives_painter_fill(cr);
1347  }
1350  lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(eventbox));
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) {
1374  if (luma > 0.2) colr = &col_black;
1375  }
1376 
1377  layout = render_text_to_cr(NULL, cr, fname, sfont, 10.,
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:
1400 
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 
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 
1416 
1417  break;
1418  }
1419 
1420  if (surf) lives_painter_destroy(cr);
1421 
1422 }
1423 
1424 
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 
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 
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 
1543 
1544  lives_free(pchainx);
1545  lives_free(in_params);
1546 }
1547 
1548 
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 
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);
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) {
1663 
1664  if (palette->style & STYLE_1) {
1665  lives_painter_t *crx = lives_painter_create_from_surface(bgimage);
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 
1691  } else if (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 
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 
1736  return QUANT_TIME(lives_spin_button_get_value(LIVES_SPIN_BUTTON(mt->node_spinbutton)));
1737 }
1738 
1739 
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 
1791  res = make_param_box(LIVES_VBOX(mt->fx_box), mt->current_rfx);
1793  widget_opts.border_width = dbw;
1794 
1795  ltext = mt_params_label(mt);
1796 
1798  widget_opts.justify = LIVES_JUSTIFY_CENTER;
1800  lives_label_set_text(LIVES_LABEL(mt->fx_params_label), ltext);
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 
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 
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 
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 
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;
2149  mt->current_rfx->needs_reinit = FALSE;
2150  mt->current_rfx->flags |= RFX_FLAGS_NO_RESET;
2151  update_visual_params(mt->current_rfx, 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 
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 
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 
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 
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 
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 
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 
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 
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 
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)
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 
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();
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();
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);
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();
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);
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();
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);
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),
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();
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 
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))
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();
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);
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))
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();
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);
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;
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 
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 
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 
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 
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 
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);
2880  lives_free(timestring);
2881 }
2882 
2883 
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 
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();
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 
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))) {
2996  array = lives_strsplit((char *)lmap->data, "|", -1);
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);
3007  lives_strfreev(array);
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 
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;
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 
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 
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 
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 
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 
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 
3169 
3170 #ifdef ENABLE_JACK_TRANSPORT
3171  // if we have jack transport enabled, we get our playback start time from there
3172 
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) {
3180  return 0;
3181  }
3182  }
3183  if (end_tc > 0) tc %= end_tc;
3184  mt->is_paused = FALSE;
3185  } else {
3186 #endif
3187 
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 
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 
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 
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 
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);
3256  mainw->plug = NULL;
3257  }
3258 
3259  if (LIVES_IS_WIDGET(mainw->playarea)) lives_widget_destroy(mainw->playarea);
3260 
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();
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;
3280  }
3281 #else
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;
3290  }
3291 #else
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
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) {
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 
3367  mt->preview_layer = weed_get_int_value(mt->init_event, WEED_LEAF_OUT_TRACKS, NULL);
3368 
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 
3378 
3379  // start decoder plugins, one per track
3381 
3382  // render one frame
3383  process_events(mt->pb_start_event, FALSE, 0);
3385  mt->preview_layer = -100000;
3386  mt->solo_inst = NULL;
3387  mainw->internal_messaging = internal_messaging;
3388  mainw->current_file = current_file;
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) {
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
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 
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) {
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)
3458 
3459  if (mt->framedraw) mt_framedraw(mt, mainw->frame_layer); // framedraw will free the frame_layer itself
3460  else {
3461  if (lastblank) {
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;
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) {
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;
3488 #else
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 
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 
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 
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 
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 
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 
3537 
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);
3551  } else {
3552  lives_widget_set_sensitive(mt->rewind, FALSE);
3554  }
3555 
3556  if (mt->is_paused) {
3557  mt->is_paused = 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) {
3590  paint_lines(mt, pos, TRUE, NULL);
3591  }
3592 }
3593 
3594 
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 
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 
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 
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 
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 
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 
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 
3650 void scroll_track_by_scrollbar(LiVESScrollbar * sbar, livespointer user_data) {
3651  lives_mt *mt = (lives_mt *)user_data;
3653  track_select(mt);
3654 }
3655 
3656 
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 
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)))
3705  mt_zoom(mt, -1.);
3706  paint_lines(mt, mt->ptr_time, TRUE, NULL);
3707 }
3708 
3709 
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 
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 
3770 LIVES_INLINE int poly_page_to_tab(uint32_t page) {
3771  return ++page;
3772 }
3773 
3774 
3775 LIVES_INLINE int poly_tab_to_page(uint32_t tab) {
3776  return --tab;
3777 }
3778 
3779 
3782 }
3783 
3784 
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 
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)),
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 
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 
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 
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 
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 
4028 LIVES_LOCAL_INLINE int pkg_in_list(char *pkgstring) {
4029  return lives_list_strcmp_index(pkg_list, pkgstring, FALSE) + 1;
4030 }
4031 
4032 
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 
4039  return strdup(lives_list_nth_data(pkg_list, pkgnum - 1));
4040 }
4041 
4042 
4044  lives_list_free_all((LiVESList **)&pkg_list);
4045 }
4046 
4047 static void populate_filter_box(int ninchans, lives_mt * mt, int pkgnum);
4048 
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 
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 
4100 
4101  if (mt->is_rendering || LIVES_IS_PLAYING || mt->selected_filter == -1) {
4102  mt->selected_filter = -1;
4103  return FALSE;
4104  }
4105 
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) {
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) {
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 
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);
4230  label = lives_standard_label_new(fname);
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 
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) :
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 
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 
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 
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 
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 
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 
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 
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)) {
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 
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)) {
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 
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 
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 
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 
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) {
4670  // double click, open up in clip editor
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;
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);
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 
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 
4743  //lives_widget_context_update();
4744 
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 {
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  }
4774  if (timesecs > 0.) {
4775  lives_widget_set_sensitive(mt->rewind, 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);
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 {
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) {
4816  if (timesecs > 0.) {
4817  lives_widget_set_sensitive(mt->rewind, 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);
4832  return FALSE;
4833 }
4834 
4835 
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;
4840  return FALSE;
4841 }
4842 
4843 
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 
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 
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 
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  }
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 
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 {
4956  lives_label_set_text(LIVES_LABEL(mt->ins_label), mtext);
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 
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 
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 {
4996  lives_label_set_text(LIVES_LABEL(mt->mm_label), text);
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 
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 
5030  lives_label_set_text(LIVES_LABEL(mt->grav_label), mtext);
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 
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 
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 
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"));
5086  return (_("Move audio block"));
5087  case MT_UNDO_DELETE_BLOCK:
5088  return (_("Delete 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"));
5110  return (_("Insert audio block"));
5112  return (_("Effect order change"));
5113  }
5114  return lives_strdup("");
5115 }
5116 
5117 
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 
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 
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 
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;
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 
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 
5303 static void destroy_widget(LiVESWidget * widget, livespointer user_data) {
5304  lives_widget_destroy(widget);
5305 }
5306 
5307 
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);
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) {
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  }
5364  rfx_free(rfx);
5365  lives_free(rfx);
5366 }
5367 
5368 
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 
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 
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 
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 
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 
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 
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 
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 
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);
5847  mainw->sl_undo_offset = 0;
5848 }
5849 
5850 
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) {
5864  if (mt) lives_widget_set_sensitive(mt->show_layout_errors, FALSE);
5865  }
5866 
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 
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 
5910 
5912  mainw->stored_event_list = NULL;
5913 
5914  if (wiped) {
5917  }
5918 }
5919 
5920 
5921 LIVES_INLINE void print_layout_wiped(void) {d_print(_("Layout was wiped.\n"));}
5922 
5923 
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)) &&
5931 
5932  if (((mt && (mt->changed || mainw->scrap_file != -1 || mainw->ascrap_file != -1)) ||
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
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 
5951  lives_free(cdsw);
5952 
5953  if (resp == LIVES_RESPONSE_CANCEL) {
5954  // cancel
5955  return FALSE;
5956  }
5957 
5959 
5960  if (resp == 1 && !exiting) {
5961  // wipe
5962  prefs->ar_layout = FALSE;
5965  }
5966  }
5967 
5971  } else if (mt && mt->event_list && (exiting || resp == 1)) {
5972  event_list_free(mt->event_list);
5974  mt->event_list = NULL;
5975  mt_clear_timeline(mt);
5980  }
5982  return TRUE;
5983 }
5984 
5985 
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 
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 
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 
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 
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 
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 
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  }
6089 }
6090 
6091 
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 
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 
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 
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 
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;
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 
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 
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 
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 
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 
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);
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) {
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());
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 
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,
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) {
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 
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 
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 
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 
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();
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);
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 
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 
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 
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 
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
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)) {
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);
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);
7445 
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) {
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 
7732 
7733  lives_container_add(LIVES_CONTAINER(mt->render_menu), sep);
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"),
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 
8081  lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_sepwinbutton), -1);
8083 
8086  lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_rewindbutton), -1);
8088  lives_widget_set_sensitive(mainw->m_rewindbutton, (mt->event_list && get_first_event(mt->event_list)));
8089 
8092  lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_playbutton), -1);
8094  lives_widget_set_sensitive(mainw->m_playbutton, (mt->event_list && get_first_event(mt->event_list)));
8095 
8098  lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_stopbutton), -1);
8101 
8104  lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbar2), LIVES_TOOL_ITEM(mainw->m_loopbutton), -1);
8106 
8108  widget_opts.justify = LIVES_JUSTIFY_CENTER;
8111  mt->timecode = lives_standard_entry_new(NULL, NULL, TIMECODE_LENGTH, TIMECODE_LENGTH, LIVES_BOX(hbox), NULL);
8113  lives_widget_set_valign(mt->timecode, LIVES_ALIGN_CENTER);
8114  widget_opts.apply_theme = woat;
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 
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 
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 
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 
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,
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 
8308  lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbary), LIVES_TOOL_ITEM(mainw->m_mutebutton), -1);
8310 
8311  if (!lives_scale_button_set_orientation(LIVES_SCALE_BUTTON(mainw->volume_scale),
8312  LIVES_ORIENTATION_HORIZONTAL)) {
8313  if (mainw->vol_label) {
8316  lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbary), LIVES_TOOL_ITEM(mainw->vol_label), -1);
8318  }
8319  }
8320 
8323  lives_toolbar_insert(LIVES_TOOLBAR(mt->btoolbary), LIVES_TOOL_ITEM(mainw->vol_toolitem), -1);
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
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 
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 
8482  add_hsep_to_box(LIVES_BOX(mt->fx_contents_box));
8484 
8485  lives_box_pack_end(LIVES_BOX(mt->fx_base_box), mt->fx_contents_box, FALSE, FALSE, 0);
8486 
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 
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 
8536  mt->del_node_button = lives_standard_button_new_full(_("_Del. node"), DEF_BUTTON_WIDTH >> 1,
8537  -1, LIVES_BOX(hbox), TRUE, NULL);
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 
8546  mt->next_node_button = lives_standard_button_new_full(_("_Next node"), DEF_BUTTON_WIDTH >> 1,
8547  -1, LIVES_BOX(hbox), TRUE, NULL);
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 
8556  mt->prev_node_button = lives_standard_button_new_full(_("_Prev node"), DEF_BUTTON_WIDTH >> 1,
8557  -1, LIVES_BOX(hbox), TRUE, NULL);
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);
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 
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 
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);
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 
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);
8888  } else {
8889  prefs->ar_layout = FALSE;
8891  mt_init_tracks(mt, TRUE);
8893  }
8894  } else if (mainw->recoverable_layout) {
8896  } else {
8897  mt_init_tracks(mt, TRUE);
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 =
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 
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 
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) {
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) {
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) {
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 
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 
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 
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 
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 
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.))) {
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.;
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 
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);
9321 
9322  if (mainw->play_window) {
9323  lives_window_remove_accel_group(LIVES_WINDOW(mainw->play_window), mt->accel_group);
9325  }
9326 
9327  // put buttons back in mainw->menubar
9329 
9333 
9335  lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->mute_audio), mainw->mute);
9337 
9340  lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_sepwinbutton), 0);
9342 
9345  lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_rewindbutton), 1);
9347 
9350  lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_playbutton), 2);
9352 
9355  lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_stopbutton), 3);
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 
9365  lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_loopbutton), 5);
9367 
9370  lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->m_mutebutton), 6);
9372 
9373  if (!lives_scale_button_set_orientation(LIVES_SCALE_BUTTON(mainw->volume_scale),
9374  LIVES_ORIENTATION_HORIZONTAL)) {
9375  if (mainw->vol_label) {
9378  lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->vol_label), 7);
9380  }
9381  }
9382 
9385  lives_toolbar_insert(LIVES_TOOLBAR(mainw->btoolbar), LIVES_TOOL_ITEM(mainw->vol_toolitem), -1);
9387 
9388  if (mainw->gens_menu) {
9390  lives_menu_detach(LIVES_MENU(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);
9398  mainw->plug = NULL;
9399  }
9400 
9402 
9403  lives_container_add(LIVES_CONTAINER(mainw->pl_eventbox), mainw->playarea);
9406 
9407  if (palette->style & STYLE_1) {
9408  lives_widget_set_bg_color(mainw->playframe, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
9409  }
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 
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);
9442 
9443  for (i = 1; i < MAX_FILES; i++) {
9444  if (mainw->files[i]) {
9445  if (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++) {
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);
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 
9476  // switch back to external audio
9478  }
9479 
9481 
9483 
9484  reset_clipmenu();
9485  mainw->last_dprint_file = -1;
9486 
9487  if (prefs->show_gui && prefs->open_maximised) {
9488  int bx, by;
9490  if (by > MENU_HIDE_LIM)
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 !!!
9501 
9502  if (mt->file_selected != -1) {
9503  switch_to_file((mainw->current_file = 0), mt->file_selected);
9504  } else {
9508  mainw->drawsrc = -1;
9509  resize(1);
9511  load_start_image(0);
9512  load_end_image(0);
9513  }
9514 
9516 
9517  lives_free(mt);
9518 
9520  make_play_window();
9521  }
9522 
9524 
9526 
9527  lives_idle_add_simple(redraw_tl_idle, NULL);
9528 
9529  if (prefs->show_msg_area) {
9531  if (mainw->idlemax == 0)
9532  lives_idle_add_simple(resize_message_area, NULL);
9534  }
9535 
9536  d_print(_("====== Switched to Clip Edit mode ======\n"));
9537 
9539 
9540  return TRUE;
9541 }
9542 
9543 
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 
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 
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 
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;
9634  return FALSE;
9635 }
9636 
9637 
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;
9642  return FALSE;
9643 }
9644 
9645 
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 
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 
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;
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 
10118 }
10119 
10120 
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) {
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 
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 
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 
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);
10341  lives_label_set_text(LIVES_LABEL(label), tname);
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 
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);
10360  lives_label_set_text(LIVES_LABEL(label), newtext);
10362  lives_free(newtext);
10363 }
10364 
10365 
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 
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"));
10463  lives_label_set_text(LIVES_LABEL(label), newtext);
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")))));
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 
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 
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 
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);
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 
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 
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 
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 
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);
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 
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:
10643  mt_backup(mt, MT_UNDO_FILTER_MAP_CHANGE, get_event_timecode(mt->fm_edit_event));
10644 
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 
10660  mt_backup(mt, MT_UNDO_FILTER_MAP_CHANGE, get_event_timecode(mt->fm_edit_event));
10661 
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);
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 
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 
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,
10786  } else mt->avol_fx = -1;
10787  mt->avol_init_event = NULL;
10788 
10790 
10791  lives_memset(mt->layout_name, 0, 1);
10792 
10794 
10796  mainw->no_configs = FALSE;
10797 }
10798 
10799 
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 
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;
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 
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)
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 
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);
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 
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 
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);
11020  return FALSE;
11021 }
11022 
11023 
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 
11035  mainw->frame_layer = NULL;
11036 
11039  // WARNING:
11040  rdet = create_render_details(3); // WARNING !! - rdet is global in events.h
11041  rdet->enc_changed = FALSE;
11043  if (prefs->show_gui) {
11045  lives_window_present(LIVES_WINDOW(rdet->dialog));
11047  }
11048  }
11049  do {
11051  if ((response = lives_dialog_run(LIVES_DIALOG(rdet->dialog))) == LIVES_RESPONSE_OK) {
11052  if (rdet->enc_changed) {
11054  }
11055  }
11056  } while (rdet->suggestion_followed || response == LIVES_RESPONSE_RESET);
11057 
11058  if (response == LIVES_RESPONSE_CANCEL) {
11061  lives_freep((void **)&rdet);
11062  lives_freep((void **)&resaudw);
11063  return FALSE;
11064  }
11065 
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))) {
11099  prefs->mt_def_fps = rdet->fps;
11101  prefs->mt_def_arate = xarate;
11103  prefs->mt_def_achans = xachans;
11105  prefs->mt_def_asamps = xasamps;
11107  prefs->mt_def_signed_endian = xse;
11109  prefs->mt_pertrack_audio = ptaud;
11111  prefs->mt_backaudio = btaud;
11113  } else {
11114  if (!prefs->mt_enter_prompt) {
11117  }
11118  }
11119 
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;
11136 
11137  if (!get_new_handle(mainw->current_file, NULL)) {
11138  mainw->current_file = orig_file;
11139  if (rdet) {
11141  lives_freep((void **)&rdet);
11142  lives_freep((void **)&resaudw);
11143  }
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) {
11170  lives_freep((void **)&rdet);
11171  lives_freep((void **)&resaudw);
11172  }
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);
11180 
11182 
11184  multi = multitrack(event_list, orig_file, cfile->fps); // also frees rdet
11186 
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;
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  }
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;
11224  }
11226  return FALSE;
11227  }
11228 
11229  if (prefs->show_gui) {
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 
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 
11279  mainw->preview_box = NULL;
11280  }
11281 
11282  if (mainw->play_window) {
11284  lives_window_add_accel_group(LIVES_WINDOW(mainw->play_window), multi->accel_group);
11286  }
11287 
11288  if (cfile->achans > 0 && !is_realtime_aplayer(prefs->audio_player)) {
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;
11299  if (by > MENU_HIDE_LIM)
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
11324  }
11325 
11326  if (prefs->show_msg_area) {
11327  if (mainw->idlemax == 0)
11328  lives_idle_add_simple(resize_message_area, NULL);
11330  }
11331 
11332  lives_idle_add_simple(mt_idle_show_current_frame, (livespointer)multi);
11334 
11335  if (prefs->show_gui && prefs->open_maximised) {
11336  int bx, by;
11338  if (by > MENU_HIDE_LIM)
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 
11354  return FALSE;
11355  }
11356 
11358 
11359  if (mainw->reconfig) return FALSE;
11360 
11361  d_print(_("====== Switched to Multitrack mode ======\n"));
11362 
11364 
11365  return TRUE;
11366 }
11367 
11368 
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 
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 
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 
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 
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 
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 
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 
11693 void clear_context(lives_mt * mt) {
11694  main_thread_execute((lives_funcptr_t)_clear_context, 0, NULL, "v", mt);
11695 }
11696 
11697 
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;
11706  label = lives_standard_label_new(NULL);
11707  lives_label_set_markup(LIVES_LABEL(label), text);
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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) {
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
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) {
12882  } else {
12883  mt->init_event = NULL;
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 
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) {
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);
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 
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);
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) {
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  }
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;
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 
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
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"),
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"),
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"),
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"));
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 
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) {
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 
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 
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 
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 
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;
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 
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 
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);
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);
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 
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 
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);
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 
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);
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 
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 
13806 
13808  eventbox, &x, &y);
13809  timesecs = get_time_from_x(mt, x);
13810  tc = timesecs * TICKS_PER_SECOND;
13811 
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)));
13861  mt->display, &screen, &abs_x, &abs_y, NULL);
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();
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;
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) /
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 
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 
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 
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 
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 
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 
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 
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 
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;
14065  mt->display, &screen, &abs_x, &abs_y, NULL);
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 
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 
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 
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 
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 
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 
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 
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 
14223 void animate_multitrack(lives_mt * mt) {
14224  main_thread_execute((lives_funcptr_t)_animate_multitrack, 0, NULL, "v", mt);
14225 }
14226 
14228 // menuitem callbacks
14229 
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
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
14322 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
14648 void remove_gaps(LiVESMenuItem * menuitem, livespointer user_data) {
14649  remove_gaps_inner(menuitem, user_data, FALSE);
14650 }
14651 
14652 
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 
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 
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;
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 
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;
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 
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);
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 
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"));
15223  lives_label_set_text(LIVES_LABEL(label), (const char *)llist->data);
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 
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 
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"));
15417  lives_label_set_text(LIVES_LABEL(label), (const char *)llist->data);
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 
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 
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 
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 
15647  }
15648 }
15649 
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 
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 
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 
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 
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 
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 
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 
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);
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 
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 
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 
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 
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 
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 
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 
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 
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 
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;
16111  com = lives_strdup_printf("%s backup_audio \"%s\"", prefs->backend_sync, mainw->files[mt->render_file]->handle);
16113  lives_free(com);
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 
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 
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;
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);
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);
16353 
16354  return retval;
16355 }
16356 
16357 
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 
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 
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 
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 
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 
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 
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);
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);
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 
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 
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 
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 
16979 void mt_desensitise(lives_mt * mt) {
16980  double val;
16981 
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 
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 
17060 
17061  if (mt->event_list && get_first_event(mt->event_list)) {
17062  lives_widget_set_sensitive(mt->playall, 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);
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 
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);
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 
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 =
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);
17220  lives_widget_set_sensitive(mt->playall, 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);
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);
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);
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 
17250 void multitrack_preview_clicked(LiVESWidget * button, livespointer user_data) {
17251  //preview during rendering
17252  lives_mt *mt = (lives_mt *)user_data;
17253 
17256 }
17257 
17258 
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);
17279  lives_widget_set_sensitive(mt->playall, TRUE);
17281 }
17282 
17283 
17284 void mt_post_playback(lives_mt * mt) {
17285  // called from on_preview_clicked
17287 
17289 
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);
17309  }
17310  paint_lines(mt, curtime, TRUE, NULL);
17311  mt->no_frame_update = FALSE;
17313  }
17314 
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);
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;
17341  paint_lines(mt, mt->ptr_time, FALSE, NULL);
17342 }
17343 
17344 
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) {
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;
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 
17417  }
17418  }
17419 
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 
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 
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 
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 
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 
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 
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 
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 
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  }
18055  mt_sensitise(mt);
18056 }
18057 
18058 
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 
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);
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 
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);
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 
18157 // region functions
18158 
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) {
18172  } else {
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);
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) {
18203  } else {
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 
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);
18219  lives_widget_queue_draw(mt->timeline_reg);
18220 }
18221 
18222 
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 
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 
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.);
18321  lives_painter_rectangle(cr, offset_start, 0, offset_end - offset_start, lives_widget_get_allocation_height(ebox) - 1);
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 
18351  lives_painter_line_to(cr, i, (.5 - ypos) * (float)lives_widget_get_allocation_height(ebox));
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 
18364 
18365  if (afd != -1) lives_close_buffered(afd);
18366 }
18367 
18368 
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);
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));
18444  } else if (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 
18457 
18458 // functions for moving and clicking on the timeline
18459 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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);
18878 
18880 }
18881 
18882 
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 
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 
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 
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 
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 
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 
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 
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 
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
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;
19058  update_visual_params(mt->current_rfx, 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 
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 
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 
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])) {
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 
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 
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 
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])) {
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;
19190  mt->current_rfx->needs_reinit = FALSE;
19191  mt->current_rfx->flags |= RFX_FLAGS_NO_RESET;
19192  update_visual_params(mt->current_rfx, TRUE);
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
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 
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 
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) {
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 
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 
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 
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 
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
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 
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 
19581 static int free_tt_key;
19582 static int elist_errors;
19583 
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 
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;
19664 
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);
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 
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 
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);
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) {
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 
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 
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
19963  response = lives_dialog_run(LIVES_DIALOG(renamew->dialog));
19964  if (response == LIVES_RESPONSE_CANCEL) {
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)))));
19976  lives_freep((void **)&renamew);
19977  lives_free(tmp);
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 
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
20045  lives_freep((void **)&layout_map);
20046  lives_freep((void **)&layout_map_audio);
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
20055  lives_freep((void **)&layout_map);
20056  lives_freep((void **)&layout_map_audio);
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);
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);
20158 
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;
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 
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 
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 
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);
20240  return tmp;
20241 }
20242 
20243 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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);
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 
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 
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 
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 
21747 
21748  if (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 
21758  d_print(_("Loading layout from %s..."), eload_name);
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) {
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;
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);
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,
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;
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 
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 
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 
21945 
21948 
21951 
21952  if (*mt->layout_name && !strcmp(mt->layout_name, prefs->ar_layout_name)) {
21955  prefs->ar_layout = FALSE;
21956  }
21957 
21958  event_list_free(mt->event_list);
21959  mt->event_list = NULL;
21960 
21962 
21963  mt_clear_timeline(mt);
21964 
21965  mt_sensitise(mt);
21966 
21967  mt->idlefunc = mt_idle_add(mt);
21968 }
21969 
21970 
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 {
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 
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);
22027  lives_free(lmap_file);
22028  } else {
22029  // wipe
22030  if (mt->changed) {
22032  _("The current layout has changes which have not been saved.\nAre you sure you wish to wipe it ?\n"),
22034  mt->idlefunc = mt_idle_add(mt);
22035  return;
22036  }
22037  }
22038  }
22039 
22040  // wipe
22041  wipe_layout(mt);
22042 }
22043 
22044 
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 
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 
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 
22103 
22104  unselect_all(mt);
22105  remove_markers(mt->event_list);
22106  mt_sensitise(mt);
22108 
22109  mt->idlefunc = mt_idle_add(mt);
22110 
22111  return TRUE;
22112 }
22113 
22114 
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) {
22151  if ((event_list = load_event_list_inner(NULL, fd, FALSE, NULL, NULL, NULL)) != NULL) {
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);
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)) {
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);
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 
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))
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 
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))
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 
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);
22319  scroll_tracks(mt, mt->top_track, FALSE);
22320 }
22321 
22322 
22324 
22325 void show_frame_events_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22327 }
22328 
22329 
22330 void mt_change_max_disp_tracks(LiVESMenuItem * menuitem, livespointer user_data) {
22331  LiVESWidget *dialog;
22332  lives_mt *mt = (lives_mt *)user_data;
22333 
22336  lives_widget_show(dialog);
22337 }
22338 
22339 
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 
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 
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 
22358  rdet->enc_changed = FALSE;
22359  do {
22361  if ((response = lives_dialog_run(LIVES_DIALOG(rdet->dialog))) == LIVES_RESPONSE_OK) {
22362  if (rdet->enc_changed) {
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) {
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")) {
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))) {
22414  prefs->mt_def_fps = rdet->fps;
22416  prefs->mt_def_arate = xarate;
22418  prefs->mt_def_achans = xachans;
22420  prefs->mt_def_asamps = xasamps;
22422  prefs->mt_def_signed_endian = xse;
22424  prefs->mt_pertrack_audio = ptaud;
22426  prefs->mt_backaudio = btaud;
22428  } else {
22429  if (!prefs->mt_enter_prompt) {
22432  }
22433  }
22434 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
22976 void on_mt_showkeys_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22978 }
22979 
22980 
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 
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;
23007 }
23008 
23009 
23010 // remote API helpers
23011 
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 
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 
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 
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;
23061  return lastblock->uid;
23062 }
23063 
23064 
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;
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 
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;
23087  return (double)get_event_timecode(block->start_event) / TICKS_PER_SECOND_DBL;
23088 }
23089 
23090 
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;
23095  return (double)get_event_timecode(block->end_event) / TICKS_PER_SECOND_DBL + 1. / mt->fps;
23096 }
23097 
23098 
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 
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 
23115 // autotransitions
23116 //
23117 
23118 
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;
23151 
23152  filter = get_weed_filter(prefs->atrans_fx);
23153  if (num_in_params(filter, TRUE, TRUE) == 0) return;
23154 
23155  tparam = get_transition_param(filter, FALSE);
23156  if (tparam == -1) return;
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;
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;
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;
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 
lives_toolbar_set_show_arrow
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toolbar_set_show_arrow(LiVESToolbar *toolbar, boolean show)
Definition: widget-helper.c:5390
set_double_pref
int set_double_pref(const char *key, double value)
Definition: preferences.c:346
render_details::fps
double fps
Definition: events.h:218
lives_painter_line_to
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_line_to(lives_painter_t *cr, double x, double y)
Definition: widget-helper.c:533
mainwindow::help_menu
LiVESWidget * help_menu
Definition: mainwindow.h:1408
LIVES_DEBUG
#define LIVES_DEBUG(x)
Definition: main.h:1848
lives_freep
boolean lives_freep(void **ptr)
Definition: utils.c:1411
lives_painter_get_target
WIDGET_HELPER_GLOBAL_INLINE lives_painter_surface_t * lives_painter_get_target(lives_painter_t *cr)
Definition: widget-helper.c:711
LIVES_GLOBAL_INLINE
#define LIVES_GLOBAL_INLINE
Definition: main.h:239
mainwindow::layout_textbuffer
LiVESTextBuffer * layout_textbuffer
stores layout errors
Definition: mainwindow.h:1468
widget_opts_t::packing_width
int packing_width
horizontal pixels between widgets
Definition: widget-helper.h:1410
render_details::okbutton
LiVESWidget * okbutton
Definition: events.h:221
LIVES_CURSOR_FX_BLOCK
@ LIVES_CURSOR_FX_BLOCK
Definition: widget-helper.h:1305
layout_frame_is_affected
LiVESList * layout_frame_is_affected(int clipno, int start, int end, LiVESList *xlays)
Definition: multitrack.c:22247
lives_source_remove
WIDGET_HELPER_GLOBAL_INLINE boolean lives_source_remove(uint32_t handle)
Definition: widget-helper.c:7361
AFORM_UNSIGNED
#define AFORM_UNSIGNED
Definition: main.h:786
lives_standard_label_new_with_mnemonic_widget
LiVESWidget * lives_standard_label_new_with_mnemonic_widget(const char *text, LiVESWidget *mnemonic_widget)
Definition: widget-helper.c:8720
mt_trdown
boolean mt_trdown(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3710
render_details::encoder_name
char * encoder_name
Definition: events.h:245
MT_TRACK_HEIGHT
#define MT_TRACK_HEIGHT
Definition: multitrack.h:33
mainwindow::internal_messaging
boolean internal_messaging
internal fx
Definition: mainwindow.h:1043
mainwindow::msg_surface
lives_painter_surface_t * msg_surface
Definition: mainwindow.h:1328
on_open_activate
void on_open_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7418
mainwindow::fullscreen_cb_func
ulong fullscreen_cb_func
Definition: mainwindow.h:1073
_prefs::mt_def_achans
int mt_def_achans
Definition: preferences.h:273
lives_adjustment_set_value
WIDGET_HELPER_GLOBAL_INLINE boolean lives_adjustment_set_value(LiVESAdjustment *adj, double value)
Definition: widget-helper.c:5636
append_filter_deinit_event
weed_plant_t * append_filter_deinit_event(weed_plant_t *event_list, weed_timecode_t tc, void *init_event, void **pchain)
Definition: events.c:2882
lives_spin_button_set_range
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_set_range(LiVESSpinButton *button, double min, double max)
Definition: widget-helper.c:5129
on_split_curr_activate
void on_split_curr_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16591
desensitize
void desensitize(void)
Definition: main.c:5302
lives_window_center
boolean lives_window_center(LiVESWindow *window)
Definition: widget-helper.c:11251
WEED_LEAF_WEED_EVENT_API_VERSION
#define WEED_LEAF_WEED_EVENT_API_VERSION
parts of this may eventually become libweed-events
Definition: events.h:18
lives_buffered_rdonly_slurp
void lives_buffered_rdonly_slurp(int fd, off_t skip)
Definition: utils.c:671
WEED_LEAF_AUDIO_SEEKS
#define WEED_LEAF_AUDIO_SEEKS
Definition: events.h:41
lives_box_pack_top
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_pack_top(LiVESBox *box, LiVESWidget *child, boolean expand, boolean fill, uint32_t padding)
Definition: widget-helper.c:11742
on_render_activate
boolean on_render_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16078
lives_paned_set_position
WIDGET_HELPER_GLOBAL_INLINE boolean lives_paned_set_position(LiVESPaned *paned, int pos)
Definition: widget-helper.c:4422
lives_check_menu_item_set_active
WIDGET_HELPER_GLOBAL_INLINE boolean lives_check_menu_item_set_active(LiVESCheckMenuItem *item, boolean state)
Definition: widget-helper.c:6537
LIVES_IS_PLAYING
#define LIVES_IS_PLAYING
Definition: main.h:840
LIVES_LOCAL_INLINE
#define LIVES_LOCAL_INLINE
Definition: main.h:246
write_backup_layout_numbering
boolean write_backup_layout_numbering(lives_mt *mt)
Definition: multitrack.c:668
EFFORT_RANGE_MAX
#define EFFORT_RANGE_MAX
if set to TRUE during playback then a new frame (or possibly the current one) will be displayed ASAP
Definition: mainwindow.h:1770
do_event_list_warning
LIVES_GLOBAL_INLINE boolean do_event_list_warning(void)
Definition: dialogs.c:3707
lives_signal_connect
ulong lives_signal_connect(LiVESWidget *, const char *signal_name, ulong funcptr, livespointer data)
migrate_layouts
void migrate_layouts(const char *old_set_name, const char *new_set_name)
Definition: multitrack.c:22115
mainwindow::mt_needs_idlefunc
boolean mt_needs_idlefunc
set if we need to re-add the idlefunc for autobackup
Definition: mainwindow.h:1088
WEED_LEAF_INIT_EVENTS
#define WEED_LEAF_INIT_EVENTS
Definition: events.h:55
pref_factory_int
boolean pref_factory_int(const char *prefidx, int newval, boolean permanent)
Definition: preferences.c:1053
mainwindow::last_display_ticks
ticks_t last_display_ticks
Definition: mainwindow.h:1012
lives_mt_undo_t
lives_mt_undo_t
Definition: multitrack.h:90
lives_notebook_set_tab_label
WIDGET_HELPER_GLOBAL_INLINE boolean lives_notebook_set_tab_label(LiVESNotebook *nbook, LiVESWidget *child, LiVESWidget *tablabel)
Definition: widget-helper.c:6921
mainwindow::cliplist
LiVESList * cliplist
hash table of clips in menu order
Definition: mainwindow.h:743
show_in_out_images
boolean show_in_out_images(livespointer user_data)
Definition: multitrack.c:11878
mainwindow::m_mutebutton
LiVESWidget * m_mutebutton
Definition: mainwindow.h:1370
lives_mt_insert_mode_t
lives_mt_insert_mode_t
Definition: multitrack.h:75
rte_window.h
mainwindow::recent
LiVESWidget * recent[N_RECENT_FILES]
Definition: mainwindow.h:1129
lives_dialog_add_button_from_stock
LiVESWidget * lives_dialog_add_button_from_stock(LiVESDialog *dialog, const char *stock_id, const char *label, int response_id)
Definition: widget-helper.c:9892
insert_marker_event_at
weed_plant_t * insert_marker_event_at(weed_plant_t *event_list, weed_plant_t *at_event, int marker_type, livespointer data)
Definition: events.c:1418
set_undoable
void set_undoable(const char *what, boolean sensitive)
Definition: utils.c:4784
mainwindow::recent_file
char recent_file[PATH_MAX]
Definition: mainwindow.h:737
lives_spin_button_set_snap_to_ticks
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_set_snap_to_ticks(LiVESSpinButton *button, boolean snap)
Definition: widget-helper.c:5147
_palette::normal_back
LiVESWidgetColor normal_back
Definition: mainwindow.h:324
lives_mt_poly_state_t
lives_mt_poly_state_t
Definition: multitrack.h:121
WEED_LEAF_INIT_EVENT
#define WEED_LEAF_INIT_EVENT
Definition: events.h:52
weed_frame_event_get_audio_tracks
LIVES_GLOBAL_INLINE int weed_frame_event_get_audio_tracks(weed_event_t *event, int **clips, double **seeks)
Definition: events.c:59
on_full_screen_activate
void on_full_screen_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7990
PRId64
#define PRId64
Definition: machinestate.h:169
LIVES_SENSE_STATE_INSENSITIZED
#define LIVES_SENSE_STATE_INSENSITIZED
Definition: mainwindow.h:1705
lives_ruler_get_value
WIDGET_HELPER_GLOBAL_INLINE double lives_ruler_get_value(LiVESRuler *ruler)
Definition: widget-helper.c:5272
lives_list_strcmp_index
int lives_list_strcmp_index(LiVESList *list, livesconstpointer data, boolean case_sensitive)
Definition: utils.c:4678
lives_vol_to_linear
#define lives_vol_to_linear(vol)
Definition: audio.h:270
MT_UNDO_NONE
@ MT_UNDO_NONE
no event_list
Definition: multitrack.h:93
mainwindow::plug
LiVESWidget * plug
Definition: mainwindow.h:1099
get_audio_block_start
weed_plant_t * get_audio_block_start(weed_plant_t *event_list, int track, weed_timecode_t tc, boolean seek_back)
Definition: events.c:434
_prefs::mt_def_signed_endian
int mt_def_signed_endian
Definition: preferences.h:273
mainwindow::m_stopbutton
LiVESWidget * m_stopbutton
Definition: mainwindow.h:1369
weed_plant_deserialise
weed_plant_t * weed_plant_deserialise(int fd, unsigned char **mem, weed_plant_t *plant)
Definition: effects-weed.c:11770
menu_sets_visible
WIDGET_HELPER_GLOBAL_INLINE boolean menu_sets_visible(LiVESCheckMenuItem *mi, LiVESWidget *widget, boolean invert)
Definition: widget-helper.c:11448
lives_free
#define lives_free
Definition: machinestate.h:52
do_track_context
void do_track_context(lives_mt *mt, LiVESXEventButton *event, double timesecs, int track)
Definition: multitrack.c:13695
LIVES_TRACK_ANY
#define LIVES_TRACK_ANY
Definition: events.h:92
POLY_IN_OUT
@ POLY_IN_OUT
Definition: multitrack.h:124
mt_get_pchain
void ** mt_get_pchain(void)
Definition: multitrack.c:1033
lives_cursor_t
lives_cursor_t
Definition: widget-helper.h:1291
on_troubleshoot_activate
void on_troubleshoot_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: startup.c:1362
check_string_choice_params
void check_string_choice_params(weed_plant_t *inst)
Definition: rte_window.c:1774
WARN_MASK_LAYOUT_LB
#define WARN_MASK_LAYOUT_LB
Definition: preferences.h:128
lives_box_pack_end
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_pack_end(LiVESBox *box, LiVESWidget *child, boolean expand, boolean fill, uint32_t padding)
Definition: widget-helper.c:3291
LIVES_WARN
#define LIVES_WARN(x)
Definition: main.h:1862
list_fx_here_cb
void list_fx_here_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14313
MAIN_SPIN_SPACER
#define MAIN_SPIN_SPACER
pixel spacing for start/end spins for clip and multitrack editors
Definition: mainwindow.h:164
weed_filter_idx_get_name
char * weed_filter_idx_get_name(int idx, boolean add_subcats, boolean add_notes)
Definition: effects-weed.c:9475
HIDDEN_KEY
#define HIDDEN_KEY
Definition: widget-helper.h:1485
mainwindow::unordered_blocks
boolean unordered_blocks
are we recording unordered blocks ?
Definition: mainwindow.h:1488
update_visual_params
void update_visual_params(lives_rfx_t *rfx, boolean update_hidden)
apply internal value changes to interface widgets
Definition: paramwindow.c:3361
multitrack_undo
void multitrack_undo(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15073
add_init_event_to_filter_map
void add_init_event_to_filter_map(weed_plant_t *fmap, weed_plant_t *event, void **hints)
Definition: events.c:1731
lives_scrolled_window_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_scrolled_window_new(LiVESAdjustment *hadj, LiVESAdjustment *vadj)
Definition: widget-helper.c:6236
mainwindow::playarea
LiVESWidget * playarea
Definition: mainwindow.h:1321
lives_widget_add_accelerator
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_add_accelerator(LiVESWidget *widget, const char *accel_signal, LiVESAccelGroup *accel_group, uint32_t accel_key, LiVESXModifierType accel_mods, LiVESAccelFlags accel_flags)
Definition: widget-helper.c:2953
WEED_LEAF_IN_COUNT
#define WEED_LEAF_IN_COUNT
Definition: events.h:45
lives_painter_create_from_widget
lives_painter_t * lives_painter_create_from_widget(LiVESWidget *)
set_interactive
void set_interactive(boolean interactive)
Definition: gui.c:3072
MT_UNDO_INSERT_GAP
@ MT_UNDO_INSERT_GAP
Definition: multitrack.h:110
lives_standard_check_menu_item_new_with_label
LiVESWidget * lives_standard_check_menu_item_new_with_label(const char *label, boolean active)
Definition: widget-helper.c:8547
PREF_MT_DEF_ARATE
#define PREF_MT_DEF_ARATE
Definition: preferences.h:1009
update_filter_maps
void update_filter_maps(weed_plant_t *event, weed_plant_t *end_event, weed_plant_t *init_event)
Definition: events.c:1009
lives_malloc
#define lives_malloc
Definition: machinestate.h:46
choose_file
char * choose_file(const char *dir, const char *fname, char **const filt, LiVESFileChooserAction act, const char *title, LiVESWidget *extra_widget)
Definition: interface.c:4080
GRAV_MODE_LEFT
@ GRAV_MODE_LEFT
Definition: multitrack.h:86
mainwindow::is_ready
boolean is_ready
Definition: mainwindow.h:787
PREF_REC_EXT_AUDIO
#define PREF_REC_EXT_AUDIO
Definition: preferences.h:892
mainwindow::idlemax
int idlemax
Definition: mainwindow.h:1741
TIMECODE_LENGTH
#define TIMECODE_LENGTH
length of timecode text entry, must be > 12
Definition: multitrack.h:35
LIVES_CURSOR_NORMAL
@ LIVES_CURSOR_NORMAL
must be zero
Definition: widget-helper.h:1292
lives_window_set_title
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_set_title(LiVESWindow *window, const char *title)
Definition: widget-helper.c:2620
TIMELINE_TABLE_COLUMNS
#define TIMELINE_TABLE_COLUMNS
Definition: multitrack.h:37
move_filter_deinit_event
void move_filter_deinit_event(weed_plant_t *event_list, weed_timecode_t new_tc, weed_plant_t *deinit_event, double fps, boolean rescale_pchanges)
Definition: events.c:1892
lives_display_warp_pointer
WIDGET_HELPER_GLOBAL_INLINE boolean lives_display_warp_pointer(LiVESXDevice *device, LiVESXDisplay *display, LiVESXScreen *screen, int x, int y)
Definition: widget-helper.c:7269
get_frame_event_at_or_before
weed_plant_t * get_frame_event_at_or_before(weed_plant_t *event_list, weed_timecode_t tc, weed_plant_t *shortcut)
Definition: events.c:812
lives_pixbuf_new_from_stock_at_size
LiVESPixbuf * lives_pixbuf_new_from_stock_at_size(const char *stock_id, LiVESIconSize size, int x, int y)
Definition: widget-helper.c:2339
on_framedraw_scroll
boolean on_framedraw_scroll(LiVESWidget *widget, LiVESXEventScroll *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:1263
_entryw::dialog
LiVESWidget * dialog
Definition: interface.h:94
resaudw
_resaudw * resaudw
Definition: resample.h:38
widget_opts_t::justify
LiVESJustification justify
justify for labels
Definition: widget-helper.h:1412
get_first_frame_event
weed_plant_t * get_first_frame_event(weed_plant_t *event_list)
Definition: events.c:404
on_mt_delfx_activate
void on_mt_delfx_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15849
lives_window_set_transient_for
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_set_transient_for(LiVESWindow *window, LiVESWindow *parent)
Definition: widget-helper.c:2634
mainwindow::mgeom
lives_mgeometry_t * mgeom
multi-head support
Definition: mainwindow.h:1576
lives_scrolled_window_add_with_viewport
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scrolled_window_add_with_viewport(LiVESScrolledWindow *scrolledwindow, LiVESWidget *child)
Definition: widget-helper.c:6274
lives_get_audio_file_name
LIVES_GLOBAL_INLINE char * lives_get_audio_file_name(int fnum)
Definition: audio.c:55
mt_desensitise
void mt_desensitise(lives_mt *mt)
Definition: multitrack.c:16979
_palette::vidcol
lives_colRGBA64_t vidcol
Definition: mainwindow.h:340
lives_widget_destroy
LIVES_GLOBAL_INLINE boolean lives_widget_destroy(LiVESWidget *widget)
Definition: widget-helper.c:1553
lives_widget_grab_focus
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_grab_focus(LiVESWidget *widget)
Definition: widget-helper.c:4712
lives_standard_button_new_with_label
#define lives_standard_button_new_with_label(l, w, h)
Definition: widget-helper.h:1043
break_me
void break_me(const char *brkstr)
Definition: main.c:159
lives_standard_spin_button_new
LiVESWidget * lives_standard_spin_button_new(const char *labeltext, double val, double min, double max, double step, double page, int dp, LiVESBox *box, const char *tooltip)
Definition: widget-helper.c:9397
weed_instance_get_in_params
WEED_GLOBAL_INLINE weed_plant_t ** weed_instance_get_in_params(weed_plant_t *instance, int *nparams)
Definition: weed-effects-utils.c:602
on_amixer_slider_changed
void on_amixer_slider_changed(LiVESAdjustment *adj, lives_mt *mt)
Definition: multitrack.c:22610
LIVES_LIVES_STOCK_AUDIO
#define LIVES_LIVES_STOCK_AUDIO
Definition: widget-helper.h:1342
lives_pixbuf_scale_simple
WIDGET_HELPER_GLOBAL_INLINE LiVESPixbuf * lives_pixbuf_scale_simple(const LiVESPixbuf *src, int dest_width, int dest_height, LiVESInterpType interp_type)
Definition: widget-helper.c:3173
mt_clear_timeline
void mt_clear_timeline(lives_mt *mt)
Definition: multitrack.c:10742
on_capture_activate
void on_capture_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:11400
CANCEL_USER_PAUSED
@ CANCEL_USER_PAUSED
cancelled and paused
Definition: main.h:746
mt_fixup_events
void mt_fixup_events(lives_mt *mt, weed_plant_t *old_event, weed_plant_t *new_event)
Definition: multitrack.c:19321
MENUBAR_MIN
#define MENUBAR_MIN
Definition: multitrack.h:39
mainwindow::xlays
LiVESList * xlays
immediately (to be) affected layout maps
Definition: mainwindow.h:1477
mainwindow::msg_scrollbar
LiVESWidget * msg_scrollbar
Definition: mainwindow.h:1325
lives_set_cursor_style
void lives_set_cursor_style(lives_cursor_t cstyle, LiVESWidget *widget)
Definition: widget-helper.c:11950
lives_colRGBA64_t::alpha
uint16_t alpha
Definition: main.h:326
NB_ERROR_NOTRANS
@ NB_ERROR_NOTRANS
Definition: multitrack.h:116
lives_clip_t::laudio_drawable
lives_painter_surface_t * laudio_drawable
Definition: main.h:1084
_prefs::workdir
char workdir[PATH_MAX]
kept in locale encoding
Definition: preferences.h:61
mt_init_start_end_spins
void mt_init_start_end_spins(lives_mt *mt)
Definition: multitrack.c:4844
mainwindow::sl_undo_buffer_used
size_t sl_undo_buffer_used
Definition: mainwindow.h:811
lives_spin_button_set_value
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_set_value(LiVESSpinButton *button, double value)
Definition: widget-helper.c:5119
MT_UNDO_REMOVE_GAPS
@ MT_UNDO_REMOVE_GAPS
Definition: multitrack.h:107
MT_UNDO_DELETE_FILTER
@ MT_UNDO_DELETE_FILTER
Definition: multitrack.h:99
lives_window_maximize
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_maximize(LiVESWindow *window)
Definition: widget-helper.c:2889
lives_table_set_row_homogeneous
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_set_row_homogeneous(LiVESTable *table, boolean homogeneous)
Definition: widget-helper.c:6985
mainwindow::pl_eventbox
LiVESWidget * pl_eventbox
Definition: mainwindow.h:1100
lives_spin_button_get_value_as_int
WIDGET_HELPER_GLOBAL_INLINE int lives_spin_button_get_value_as_int(LiVESSpinButton *button)
Definition: widget-helper.c:5091
LIVES_FX_CAT_VIDEO_EFFECT
@ LIVES_FX_CAT_VIDEO_EFFECT
Definition: effects.h:25
_prefs::mt_def_fps
double mt_def_fps
Definition: preferences.h:271
LIVES_SEEK_FAST
#define LIVES_SEEK_FAST
good
Definition: plugins.h:312
get_audio_frame_clip
int get_audio_frame_clip(weed_plant_t *event, int track)
returns clip number for track (track==-1 is backing audio)
Definition: events.c:147
lives_table_set_col_spacings
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_set_col_spacings(LiVESTable *table, uint32_t spacing)
Definition: widget-helper.c:6972
lives_clip_t::event_list
weed_plant_t * event_list
Definition: main.h:1033
mainwindow::sense_state
uint32_t sense_state
Definition: mainwindow.h:1713
_prefs::show_gui
boolean show_gui
Definition: preferences.h:290
do_mt_audchan_error
LIVES_GLOBAL_INLINE void do_mt_audchan_error(int warn_mask)
Definition: dialogs.c:3606
insert_audio
void 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)
Definition: multitrack.c:18059
update_filter_events
void 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)
Definition: multitrack.c:16367
lives_datetime
char * lives_datetime(uint64_t secs, boolean use_local)
Definition: machinestate.c:860
DEF_IDLE_MAX
#define DEF_IDLE_MAX
Definition: mainwindow.h:1740
clear_widget_bg
WIDGET_HELPER_GLOBAL_INLINE boolean clear_widget_bg(LiVESWidget *widget, lives_painter_surface_t *s)
Definition: widget-helper.c:11108
LIVES_SENSE_STATE_SENSITIZED
#define LIVES_SENSE_STATE_SENSITIZED
Definition: mainwindow.h:1707
_prefs::jack_opts
uint32_t jack_opts
Definition: preferences.h:232
mainwindow::is_exiting
volatile boolean is_exiting
set during shutdown (inverse of only_close then)
Definition: mainwindow.h:1440
on_preferences_activate
void on_preferences_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: preferences.c:5772
mainwindow::current_file
int current_file
Definition: mainwindow.h:727
reshow_msg_area
boolean reshow_msg_area(LiVESWidget *widget, lives_painter_t *cr, livespointer psurf)
Definition: interface.c:7242
lives_standard_vpaned_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_standard_vpaned_new(void)
Definition: widget-helper.c:8410
IS_VALID_CLIP
#define IS_VALID_CLIP(clip)
Definition: main.h:808
lives_decoder_t
Definition: plugins.h:449
mainwindow::no_interp
boolean no_interp
block interpolation (for single frame previews)
Definition: mainwindow.h:1551
_prefs::crash_recovery
boolean crash_recovery
TRUE==maintain mainw->recovery file.
Definition: preferences.h:259
lives_pixbuf_get_width
WIDGET_HELPER_GLOBAL_INLINE int lives_pixbuf_get_width(const LiVESPixbuf *pixbuf)
Definition: widget-helper.c:3107
CLIP_THUMB_HEIGHT
#define CLIP_THUMB_HEIGHT
Definition: multitrack.h:15
multitrack_preview_clicked
void multitrack_preview_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:17250
NB_ERROR_NOCOMP
@ NB_ERROR_NOCOMP
Definition: multitrack.h:117
weed_plant_serialise
size_t weed_plant_serialise(int fd, weed_plant_t *plant, unsigned char **mem)
Definition: effects-weed.c:11198
on_close_activate
void on_close_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:1432
widget_opts_t::line_wrap
boolean line_wrap
line wrapping for labels
Definition: widget-helper.h:1419
on_resample_vid_ok
void on_resample_vid_ok(LiVESButton *, LiVESEntry *entry)
Definition: resample.c:1379
mainwindow::m_rewindbutton
LiVESWidget * m_rewindbutton
Definition: mainwindow.h:1369
weed_param_get_template
WEED_GLOBAL_INLINE weed_plant_t * weed_param_get_template(weed_plant_t *param)
Definition: weed-effects-utils.c:518
_palette::dark_red
LiVESWidgetColor dark_red
Definition: mainwindow.h:311
load_event_list
weed_plant_t * load_event_list(lives_mt *mt, char *eload_file)
Definition: multitrack.c:21693
lives_toolbar_set_style
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toolbar_set_style(LiVESToolbar *toolbar, LiVESToolbarStyle style)
Definition: widget-helper.c:5416
LIVES_TEXT_SIZE_LARGE
#define LIVES_TEXT_SIZE_LARGE
Definition: widget-helper.h:1370
get_weed_filter
weed_plant_t * get_weed_filter(int idx)
Definition: effects-weed.c:11014
_prefs::use_screen_gamma
boolean use_screen_gamma
Definition: preferences.h:452
delete_event
void delete_event(weed_plant_t *event_list, weed_plant_t *event)
Definition: events.c:311
_prefs::mt_def_width
int mt_def_width
Definition: preferences.h:270
_palette::style
int style
Definition: mainwindow.h:297
NB_ERROR_NOEFFECT
@ NB_ERROR_NOEFFECT
Definition: multitrack.h:115
render_details::width
int width
Definition: events.h:216
_resaudw::rb_unsigned
LiVESWidget * rb_unsigned
Definition: resample.h:24
MAX_TRACKS
#define MAX_TRACKS
Definition: multitrack.h:1044
lives_clip_t::ext_src
void * ext_src
points to opaque source for non-disk types
Definition: main.h:1040
cfile
#define cfile
Definition: main.h:1833
print_layout_wiped
LIVES_INLINE void print_layout_wiped(void)
Definition: multitrack.c:5921
on_rename_track_activate
void on_rename_track_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16037
lives_label_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_label_new(const char *text)
Definition: widget-helper.c:3457
lives_painter_set_source_rgb
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_rgb(lives_painter_t *cr, double red, double green, double blue)
Definition: widget-helper.c:604
lives_widget_hide
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_hide(LiVESWidget *widget)
Definition: widget-helper.c:1514
on_multitrack_activate
boolean on_multitrack_activate(LiVESMenuItem *menuitem, weed_plant_t *event_list)
menuitem callback
Definition: multitrack.c:11024
lives_clip_t::start
frames_t start
Definition: main.h:891
mt_add_region_effect
void mt_add_region_effect(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15670
count_resampled_frames
int count_resampled_frames(int in_frames, double orig_fps, double resampled_fps)
Definition: resample.c:72
get_audio_frame_seek
double get_audio_frame_seek(weed_plant_t *event, int track)
returns velocity for track (track==-1 is backing audio)
Definition: events.c:187
all_expose
boolean all_expose(LiVESWidget *widget, lives_painter_t *cr, livespointer psurf)
Definition: callbacks.c:9856
mainwindow::vol_label
LiVESWidget * vol_label
Definition: mainwindow.h:1365
check_for_layout_del
boolean check_for_layout_del(lives_mt *mt, boolean exiting)
Definition: multitrack.c:5924
multitrack_adj_start_end
void multitrack_adj_start_end(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:17473
effects.h
WEED_LEAF_TRACK_LABEL_VALUES
#define WEED_LEAF_TRACK_LABEL_VALUES
Definition: events.h:25
lives_label_set_mnemonic_widget
WIDGET_HELPER_GLOBAL_INLINE boolean lives_label_set_mnemonic_widget(LiVESLabel *label, LiVESWidget *widget)
Definition: widget-helper.c:6086
on_timeline_update
EXPOSE_FN_END boolean on_timeline_update(LiVESWidget *widget, LiVESXEventMotion *event, livespointer user_data)
Definition: multitrack.c:18460
lives_fix
double lives_fix(double val, int decimals) GNU_CONST
Definition: utils.c:1446
lives_table_attach
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_attach(LiVESTable *table, LiVESWidget *child, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom, LiVESAttachOptions xoptions, LiVESAttachOptions yoptions, uint32_t xpad, uint32_t ypad)
Definition: widget-helper.c:7035
_palette::mt_evbox
lives_colRGBA64_t mt_evbox
Definition: mainwindow.h:346
WORKDIR_LITERAL
#define WORKDIR_LITERAL
Definition: mainwindow.h:540
move_filter_init_event
void move_filter_init_event(weed_plant_t *event_list, weed_timecode_t new_tc, weed_plant_t *init_event, double fps)
Definition: events.c:1784
calc_frame_from_time
int calc_frame_from_time(int filenum, double time)
nearest frame [1, frames]
Definition: utils.c:1759
lives_widget_reparent
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_reparent(LiVESWidget *widget, LiVESWidget *new_parent)
Definition: widget-helper.c:1721
lives_clip_t::bpp
int bpp
bits per pixel of the image frames, 24 or 32
Definition: main.h:901
lives_hscrollbar_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hscrollbar_new(LiVESAdjustment *adj)
Definition: widget-helper.c:3425
_palette::normal_fore
LiVESWidgetColor normal_fore
Definition: mainwindow.h:325
lives_rfx_t::params
lives_param_t * params
Definition: plugins.h:649
mt_do_autotransition
void mt_do_autotransition(lives_mt *mt, track_rect *block)
call this on a block to apply autotransition on it
Definition: multitrack.c:23119
LIVES_ERROR
#define LIVES_ERROR(x)
Definition: main.h:1870
MT_UNDO_FILTER_MAP_CHANGE
@ MT_UNDO_FILTER_MAP_CHANGE
Definition: multitrack.h:102
lives_clip_t::frames
frames_t frames
number of video frames
Definition: main.h:890
LIVES_JUSTIFY_DEFAULT
#define LIVES_JUSTIFY_DEFAULT
Definition: widget-helper.h:1289
lives_tcache_entry_t::pixbuf
LiVESPixbuf * pixbuf
Definition: main.h:1107
lives_clip_t::clip_type
lives_clip_type_t clip_type
Definition: main.h:886
lives_signal_handlers_block_by_func
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handlers_block_by_func(livespointer instance, LiVESGuiCallback func, livespointer data)
Definition: widget-helper.c:1417
selblock_cb
void selblock_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14306
cvirtual.h
lives_label_set_text
WIDGET_HELPER_GLOBAL_INLINE boolean lives_label_set_text(LiVESLabel *label, const char *text)
Definition: widget-helper.c:6064
on_sepwin_activate
void on_sepwin_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:8055
_palette::fxcol
lives_colRGBA64_t fxcol
Definition: mainwindow.h:341
lives_check_menu_item_get_active
WIDGET_HELPER_GLOBAL_INLINE boolean lives_check_menu_item_get_active(LiVESCheckMenuItem *item)
Definition: widget-helper.c:6546
on_cback_audio_activate
void on_cback_audio_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16071
COMBOWIDTHCHARS
#define COMBOWIDTHCHARS
char width of combo entries (default)
Definition: mainwindow.h:74
mainwindow::play_window
LiVESWidget * play_window
Definition: mainwindow.h:947
MT_UNDO_APPLY_FILTER
@ MT_UNDO_APPLY_FILTER
Definition: multitrack.h:98
prefs
_prefs * prefs
Definition: preferences.h:847
MENU_HIDE_LIM
#define MENU_HIDE_LIM
Definition: mainwindow.h:86
insert_frames
void 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)
Definition: multitrack.c:17810
lives_painter_set_operator
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_operator(lives_painter_t *cr, lives_painter_operator_t op)
Definition: widget-helper.c:588
virtual_to_images
frames_t virtual_to_images(int sfileno, frames_t sframe, frames_t eframe, boolean update_progress, LiVESPixbuf **pbr)
Definition: cvirtual.c:719
WEED_LEAF_CLIPS
#define WEED_LEAF_CLIPS
Definition: events.h:39
on_mt_list_fx_activate
void on_mt_list_fx_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15842
_prefs::mt_backaudio
int mt_backaudio
Definition: preferences.h:279
on_about_activate
void on_about_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:6977
polymorph
void polymorph(lives_mt *mt, lives_mt_poly_state_t poly)
Definition: multitrack.c:12777
_prefs::audio_player
short audio_player
Definition: preferences.h:40
LIVES_STOCK_LABEL_QUIT
char LIVES_STOCK_LABEL_QUIT[32]
Definition: widget-helper.h:1384
lives_adjustment_set_upper
WIDGET_HELPER_GLOBAL_INLINE boolean lives_adjustment_set_upper(LiVESAdjustment *adj, double upper)
Definition: widget-helper.c:5584
get_eload_filename
char * get_eload_filename(lives_mt *mt, boolean allow_auto_reload)
Definition: multitrack.c:21623
lives_scale_set_digits
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scale_set_digits(LiVESScale *scale, int digits)
Definition: widget-helper.c:6669
stored_event_list_free_undos
void stored_event_list_free_undos(void)
Definition: multitrack.c:5842
LAYOUT_MAP_FILENAME
#define LAYOUT_MAP_FILENAME
Definition: mainwindow.h:571
on_open_sel_activate
void on_open_sel_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:619
get_filter_map_before
weed_plant_t * get_filter_map_before(weed_plant_t *event, int ctrack, weed_plant_t *stop_event)
Definition: events.c:895
lives_dialog_run
WIDGET_HELPER_GLOBAL_INLINE LiVESResponseType lives_dialog_run(LiVESDialog *dialog)
Definition: widget-helper.c:1783
lives_open2
int lives_open2(const char *pathname, int flags)
Definition: utils.c:99
JACK_OPTS_TRANSPORT_CLIENT
#define JACK_OPTS_TRANSPORT_CLIENT
jack can start/stop
Definition: preferences.h:233
filter_map_after_frame
boolean filter_map_after_frame(weed_plant_t *fmap)
Definition: events.c:803
mainwindow::frame_layer
weed_plant_t * frame_layer
Definition: mainwindow.h:948
lives_toggle_button_set_mode
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_set_mode(LiVESToggleButton *button, boolean drawind)
Definition: widget-helper.c:4496
FX_BLOCK_WIDTH
#define FX_BLOCK_WIDTH
Definition: multitrack.h:30
lives_menu_shell_append
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_shell_append(LiVESMenuShell *menushell, LiVESWidget *child)
Definition: widget-helper.c:6611
lives_container_get_children
WIDGET_HELPER_GLOBAL_INLINE LiVESList * lives_container_get_children(LiVESContainer *cont)
Definition: widget-helper.c:4979
do_fx_list_context
void do_fx_list_context(lives_mt *mt, int fxcount)
Definition: multitrack.c:18603
lives_menu_set_title
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_set_title(LiVESMenu *menu, const char *title)
Definition: widget-helper.c:6566
backup_host_tags
void backup_host_tags(weed_plant_t *event_list, weed_timecode_t curr_tc)
Definition: events.c:1590
wipe_layout
void wipe_layout(lives_mt *mt)
Definition: multitrack.c:21936
do_read_failed_error_s_with_retry
LiVESResponseType do_read_failed_error_s_with_retry(const char *fname, const char *errtext)
Definition: dialogs.c:4122
lives_fx_candidate_t::list
LiVESList * list
list of filter_idx from which user can delegate
Definition: plugins.h:687
lives_accel_group_disconnect
WIDGET_HELPER_GLOBAL_INLINE boolean lives_accel_group_disconnect(LiVESAccelGroup *group, LiVESWidgetClosure *closure)
Definition: widget-helper.c:2940
on_msg_area_scroll
boolean on_msg_area_scroll(LiVESWidget *widget, LiVESXEventScroll *event, livespointer user_data)
Definition: interface.c:7298
out_anchor_toggled
void out_anchor_toggled(LiVESToggleButton *togglebutton, livespointer user_data)
Definition: multitrack.c:12729
LIVES_STRING_CONSTANT_CL
@ LIVES_STRING_CONSTANT_CL
"the current layout"
Definition: mainwindow.h:374
config_event
boolean config_event(LiVESWidget *widget, LiVESXEventConfigure *event, livespointer user_data)
Definition: callbacks.c:10090
FX_ORD_BEFORE
@ FX_ORD_BEFORE
Definition: multitrack.h:140
LIVES_TEXT_MODE_FOREGROUND_ONLY
@ LIVES_TEXT_MODE_FOREGROUND_ONLY
Definition: pangotext.h:50
lives_menu_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_menu_new(void)
Definition: widget-helper.c:6371
mainwindow::multi_opts
mt_opts multi_opts
some multitrack options that survive between mt calls
Definition: mainwindow.h:1492
lives_pixbuf_get_pixels
WIDGET_HELPER_GLOBAL_INLINE unsigned char * lives_pixbuf_get_pixels(const LiVESPixbuf *pixbuf)
Definition: widget-helper.c:3140
LIVES_DIRECTION_FORWARD
@ LIVES_DIRECTION_FORWARD
Definition: main.h:854
WARN_MASK_MT_NO_JACK
#define WARN_MASK_MT_NO_JACK
Definition: preferences.h:118
do_after_crash_warning
LIVES_GLOBAL_INLINE void do_after_crash_warning(void)
Definition: dialogs.c:3742
lives_frame_get_label_widget
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_frame_get_label_widget(LiVESFrame *frame)
Definition: widget-helper.c:6867
is_blank_frame
boolean is_blank_frame(weed_plant_t *event, boolean count_audio)
Definition: events.c:523
lives_label_set_markup
WIDGET_HELPER_GLOBAL_INLINE boolean lives_label_set_markup(LiVESLabel *label, const char *markup)
Definition: widget-helper.c:6076
add_hsep_to_box
LiVESWidget * add_hsep_to_box(LiVESBox *box)
Definition: widget-helper.c:12355
PREF_SEPWIN_TYPE
#define PREF_SEPWIN_TYPE
Definition: preferences.h:894
_prefs::show_msg_area
boolean show_msg_area
Definition: preferences.h:225
POLY_PARAMS
@ POLY_PARAMS
Definition: multitrack.h:126
get_event_type
LIVES_GLOBAL_INLINE int get_event_type(weed_plant_t *plant)
Definition: events.c:103
SEPWIN_TYPE_STICKY
#define SEPWIN_TYPE_STICKY
Definition: preferences.h:188
activate_mt_preview
void activate_mt_preview(lives_mt *mt)
sensitize Show Preview and Apply buttons
Definition: multitrack.c:19400
mt_center_on_cursor
void mt_center_on_cursor(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:4412
lives_spin_button_get_adjustment
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_spin_button_get_adjustment(LiVESSpinButton *button)
Definition: widget-helper.c:5099
check_for_ratio_fps
boolean check_for_ratio_fps(double fps)
Definition: utils.c:5361
WEED_LEAF_LIVES_TYPE
#define WEED_LEAF_LIVES_TYPE
Definition: events.h:79
_resaudw::entry_arate
LiVESWidget * entry_arate
Definition: resample.h:20
multitrack_view_sel_events
void multitrack_view_sel_events(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:18137
_palette::menu_and_bars_fore
LiVESWidgetColor menu_and_bars_fore
Definition: mainwindow.h:328
lives_fx_cat_to_text
char * lives_fx_cat_to_text(lives_fx_cat_t cat, boolean plural)
Definition: effects.c:40
lives_pixbuf_is_all_black
LIVES_GLOBAL_INLINE boolean lives_pixbuf_is_all_black(LiVESPixbuf *pixbuf)
Definition: colourspace.c:2193
lives_adjustment_get_upper
WIDGET_HELPER_GLOBAL_INLINE double lives_adjustment_get_upper(LiVESAdjustment *adj)
Definition: widget-helper.c:5523
_prefs::backend_sync
char backend_sync[PATH_MAX *4]
Definition: preferences.h:410
mt_get_block_count
int mt_get_block_count(lives_mt *mt, int ntrack)
count blocks in track
Definition: multitrack.c:23065
mainwindow::mute_audio
LiVESWidget * mute_audio
Definition: mainwindow.h:1177
PREF_MT_DEF_FPS
#define PREF_MT_DEF_FPS
Definition: preferences.h:1091
TICKS_PER_SECOND_DBL
#define TICKS_PER_SECOND_DBL
actually microseconds / 100.
Definition: mainwindow.h:37
render_details::backaudio_checkbutton
LiVESWidget * backaudio_checkbutton
Definition: events.h:232
mt_file_from_clip
LIVES_INLINE int mt_file_from_clip(lives_mt *mt, int clip)
Definition: multitrack.c:216
_palette::mt_timeline_reg
lives_colRGBA64_t mt_timeline_reg
Definition: mainwindow.h:342
lives_widget_set_no_show_all
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_no_show_all(LiVESWidget *widget, boolean set)
Definition: widget-helper.c:4868
mt_init_clips
void mt_init_clips(lives_mt *mt, int orig_file, boolean add)
Definition: multitrack.c:10859
do_mt_no_jack_error
LIVES_GLOBAL_INLINE void do_mt_no_jack_error(int warn_mask)
Definition: dialogs.c:3619
render_details::pertrack_checkbutton
LiVESWidget * pertrack_checkbutton
Definition: events.h:231
_resaudw::entry_asamps
LiVESWidget * entry_asamps
Definition: resample.h:22
weed_layer_get_height
LIVES_GLOBAL_INLINE int weed_layer_get_height(weed_layer_t *layer)
Definition: colourspace.c:13953
add_context_label
void add_context_label(lives_mt *mt, const char *text)
Definition: multitrack.c:11698
set_mt_play_sizes_cfg
void set_mt_play_sizes_cfg(lives_mt *mt)
Definition: multitrack.c:5516
sensitize
void sensitize(void)
Definition: main.c:5078
add_spring_to_box
LiVESWidget * add_spring_to_box(LiVESBox *box, int min)
Definition: widget-helper.c:12421
lives_random
LIVES_GLOBAL_INLINE uint64_t lives_random(void)
Definition: machinestate.c:58
used_in_current_layout
boolean used_in_current_layout(lives_mt *mt, int file)
Definition: multitrack.c:9180
BLOCK_UNSELECTED
@ BLOCK_UNSELECTED
Definition: multitrack.h:146
event_list_free_undos
void event_list_free_undos(lives_mt *mt)
Definition: multitrack.c:5827
POLY_TRANS
@ POLY_TRANS
Definition: multitrack.h:128
_palette::menu_and_bars
LiVESWidgetColor menu_and_bars
Definition: mainwindow.h:327
get_token_count
size_t get_token_count(const char *string, int delim)
Definition: utils.c:5430
mt_nextclip
boolean mt_nextclip(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3107
CLIP_TYPE_GENERATOR
@ CLIP_TYPE_GENERATOR
frames from generator plugin
Definition: main.h:766
lives_standard_image_menu_item_new_from_stock
LiVESWidget * lives_standard_image_menu_item_new_from_stock(const char *stock_id, LiVESAccelGroup *accel_group)
Definition: widget-helper.c:8511
add_to_clipmenu
void add_to_clipmenu(void)
Definition: gui.c:4512
sizdbl
ssize_t sizdbl
Definition: main.c:102
mt_undo::extra
void * extra
Definition: multitrack.h:694
lives_adjustment_clamp_page
WIDGET_HELPER_GLOBAL_INLINE boolean lives_adjustment_clamp_page(LiVESAdjustment *adj, double lower, double upper)
Definition: widget-helper.c:5645
lives_clipinfo_t
Definition: interface.h:77
CANCEL_USER
@ CANCEL_USER
user pressed stop
Definition: main.h:704
widget_opts_t::apply_theme
int apply_theme
theming variation for widget (0 -> no theme, 1 -> normal colours, 2+ -> theme variants)
Definition: widget-helper.h:1409
pkg_in_list
LIVES_LOCAL_INLINE int pkg_in_list(char *pkgstring)
Definition: multitrack.c:4028
lives_widget_get_xwindow
WIDGET_HELPER_GLOBAL_INLINE LiVESXWindow * lives_widget_get_xwindow(LiVESWidget *widget)
Definition: widget-helper.c:4759
add_fill_to_box
LiVESWidget * add_fill_to_box(LiVESBox *box)
Definition: widget-helper.c:12377
capability::mainpid
pid_t mainpid
Definition: main.h:591
check_storage_space
boolean check_storage_space(int clipno, boolean is_processing)
Definition: dialogs.c:1086
on_timeline_press
boolean on_timeline_press(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:18739
GRAV_MODE_RIGHT
@ GRAV_MODE_RIGHT
Definition: multitrack.h:87
make_thumb
LiVESPixbuf * make_thumb(lives_mt *mt, int file, int width, int height, frames_t frame, LiVESInterpType interp, boolean noblanks)
Definition: multitrack.c:405
calc_time_from_frame
double calc_time_from_frame(int clip, int frame)
Definition: utils.c:1756
fx_dialog
_fx_dialog * fx_dialog[2]
Definition: mainwindow.h:1851
xprocess::preview_button
LiVESWidget * preview_button
Definition: mainwindow.h:713
TICKS_PER_SECOND
#define TICKS_PER_SECOND
ticks per second - GLOBAL TIMEBASE
Definition: mainwindow.h:36
mainwindow::imframe
LiVESPixbuf * imframe
Definition: mainwindow.h:1102
lives_widget_queue_draw
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_queue_draw(LiVESWidget *widget)
Definition: widget-helper.c:1580
enabled_in_channels
int enabled_in_channels(weed_plant_t *plant, boolean count_repeats)
Definition: effects-weed.c:3985
ABS
#define ABS(a)
Definition: videoplugin.h:63
num_in_params
int num_in_params(weed_plant_t *plant, boolean skip_hidden, boolean skip_internal)
Definition: effects-weed.c:3920
run_diskspace_dialog_cb
void run_diskspace_dialog_cb(LiVESWidget *w, livespointer data)
Definition: interface.c:5748
render_text_to_cr
LingoLayout * render_text_to_cr(LiVESWidget *widget, lives_painter_t *cr, const char *text, const char *fontname, double size, lives_text_mode_t mode, lives_colRGBA64_t *fg, lives_colRGBA64_t *bg, boolean center, boolean rising, double *top, int *offs_x, int dwidth, int *dheight)
Definition: pangotext.c:468
on_rtew_delete_event
boolean on_rtew_delete_event(LiVESWidget *widget, LiVESXEventDelete *event, livespointer user_data)
Definition: rte_window.c:1846
set_poly_tab
void set_poly_tab(lives_mt *mt, uint32_t tab)
Definition: multitrack.c:3962
lives_widget_is_sensitive
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_is_sensitive(LiVESWidget *widget)
Definition: widget-helper.c:4885
on_insgap_cur_activate
void on_insgap_cur_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15029
_fx_dialog::rfx
lives_rfx_t * rfx
Definition: mainwindow.h:1843
LIVES_EXPAND_EXTRA_HEIGHT
#define LIVES_EXPAND_EXTRA_HEIGHT
Definition: widget-helper.h:1315
_prefs::apply_gamma
boolean apply_gamma
Definition: preferences.h:451
lives_image_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_image_new(void)
Definition: widget-helper.c:2308
mainwindow::show_layout_errors
LiVESWidget * show_layout_errors
Definition: mainwindow.h:1225
move_init_in_filter_map
void 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)
Definition: multitrack.c:20293
do_effect_context
void do_effect_context(lives_mt *mt, LiVESXEventButton *event)
Definition: multitrack.c:10573
lives_ruler_set_range
WIDGET_HELPER_GLOBAL_INLINE boolean lives_ruler_set_range(LiVESRuler *ruler, double lower, double upper, double position, double max_size)
Definition: widget-helper.c:5244
remove_current_from_affected_layouts
void remove_current_from_affected_layouts(lives_mt *mt)
Definition: multitrack.c:5851
weed_filter_hints_unstable
WEED_GLOBAL_INLINE int weed_filter_hints_unstable(weed_plant_t *filter)
Definition: weed-effects-utils.c:181
_prefs::lamp_buttons
boolean lamp_buttons
Definition: preferences.h:343
reset_renumbering
void reset_renumbering(void)
Definition: multitrack.c:9646
lives_ruler_set_value
WIDGET_HELPER_GLOBAL_INLINE double lives_ruler_set_value(LiVESRuler *ruler, double value)
Definition: widget-helper.c:5284
STYLE_3
#define STYLE_3
style is lightish - allow themeing of widgets with dark text, otherwise use menu bg
Definition: mainwindow.h:301
set_track_label_string
void set_track_label_string(lives_mt *mt, int track, const char *label)
Definition: multitrack.c:10366
on_split_activate
void on_split_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16552
_palette::light_green
LiVESWidgetColor light_green
Definition: mainwindow.h:310
remove_end_blank_frames
void remove_end_blank_frames(weed_plant_t *event_list, boolean remove_filter_inits)
Definition: events.c:546
mt_get_block_entime
double mt_get_block_entime(lives_mt *mt, int ntrack, int iblock)
return time in seconds of last frame event in block, + event duration
Definition: multitrack.c:23092
lives_hbutton_box_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hbutton_box_new(void)
Definition: widget-helper.c:3329
on_timeline_release
boolean on_timeline_release(LiVESWidget *eventbox, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:18622
mainwindow::event_list
weed_event_t * event_list
current event_list, for recording
Definition: mainwindow.h:803
letterbox_layer
boolean letterbox_layer(weed_layer_t *layer, int nwidth, int nheight, int width, int height, LiVESInterpType interp, int tpal, int tclamp)
Definition: colourspace.c:13015
lives_open_buffered_rdonly
int lives_open_buffered_rdonly(const char *pathname)
Definition: utils.c:636
mt_change_disp_tracks_ok
void mt_change_disp_tracks_ok(LiVESButton *button, livespointer user_data)
Definition: multitrack.c:22314
lives_container_set_border_width
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_set_border_width(LiVESContainer *container, uint32_t width)
Definition: widget-helper.c:4947
_prefs::gui_monitor
int gui_monitor
Definition: preferences.h:305
mainwindow::playframe
LiVESWidget * playframe
Definition: mainwindow.h:1098
lives_painter_surface_destroy
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_surface_destroy(lives_painter_surface_t *surf)
Definition: widget-helper.c:457
WEED_LEAF_NEEDS_SET
#define WEED_LEAF_NEEDS_SET
Definition: events.h:66
lives_ruler_set_lower
WIDGET_HELPER_GLOBAL_INLINE double lives_ruler_set_lower(LiVESRuler *ruler, double value)
Definition: widget-helper.c:5315
lives_touch
int lives_touch(const char *tfile)
Definition: utils.c:4455
mainwindow::sl_undo_mem
unsigned char * sl_undo_mem
Definition: mainwindow.h:812
WEED_LEAF_AUDIO_VOLUME_TRACKS
#define WEED_LEAF_AUDIO_VOLUME_TRACKS
Definition: events.h:22
ticks_t
int64_t ticks_t
Definition: main.h:97
insert_filter_map_event_at
boolean insert_filter_map_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event, boolean before_frames)
Definition: events.c:1066
mainwindow::sl_undo_offset
int sl_undo_offset
Definition: mainwindow.h:813
multitrack_play_sel
void multitrack_play_sel(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:17449
on_rewind_activate
void on_rewind_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:4730
weed_inst_in_param
weed_plant_t * weed_inst_in_param(weed_plant_t *inst, int param_num, boolean skip_hidden, boolean skip_internal)
Definition: effects-weed.c:8724
get_time_from_x
LIVES_INLINE double get_time_from_x(lives_mt *mt, int x)
Definition: multitrack.c:1048
set_mixer_track_vol
void set_mixer_track_vol(lives_mt *mt, int trackno, double vol)
Definition: multitrack.c:246
lives_painter_set_source_surface
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_surface(lives_painter_t *cr, lives_painter_surface_t *surface, double x, double y)
Definition: widget-helper.c:339
lives_label_get_text
WIDGET_HELPER_GLOBAL_INLINE const char * lives_label_get_text(LiVESLabel *label)
Definition: widget-helper.c:6056
LIVES_OSC_NOTIFY_MODE_CHANGED
#define LIVES_OSC_NOTIFY_MODE_CHANGED
mode changed to clip editor or to multitrack
Definition: osc_notify.h:56
EVENT_MARKER_BLOCK_START
#define EVENT_MARKER_BLOCK_START
Definition: events.h:353
lives_widget_set_vexpand
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_vexpand(LiVESWidget *widget, boolean state)
Definition: widget-helper.c:6359
LIVES_FX_CAT_TRANSITION
@ LIVES_FX_CAT_TRANSITION
Definition: effects.h:20
LIVES_PARAM_NUM
@ LIVES_PARAM_NUM
Definition: plugins.h:502
get_first_event
LIVES_GLOBAL_INLINE weed_plant_t * get_first_event(weed_plant_t *event_list)
Definition: events.c:119
rfx_free
void rfx_free(lives_rfx_t *rfx)
Definition: plugins.c:2987
LIVES_PREVIEW_TYPE_VIDEO_ONLY
#define LIVES_PREVIEW_TYPE_VIDEO_ONLY
Definition: interface.h:171
IMG_TYPE_BEST
#define IMG_TYPE_BEST
Definition: main.h:781
on_paramwindow_button_clicked
void on_paramwindow_button_clicked(LiVESButton *button, lives_rfx_t *rfx)
Definition: paramwindow.c:90
CLIP_THUMB_WIDTH
#define CLIP_THUMB_WIDTH
Definition: multitrack.h:14
do_error_dialog
LIVES_GLOBAL_INLINE LiVESResponseType do_error_dialog(const char *text)
Definition: dialogs.c:749
mainwindow::set_name
char set_name[256]
Definition: mainwindow.h:749
quantise_events
weed_plant_t * quantise_events(weed_plant_t *in_list, double qfps, boolean allow_gap)
quantise from event_list_t *in_list to *out_list at the new rate of qfps
Definition: resample.c:456
get_last_frame_event
weed_plant_t * get_last_frame_event(weed_plant_t *event_list)
Definition: events.c:419
PREF_MT_BACKAUDIO
#define PREF_MT_BACKAUDIO
Definition: preferences.h:1017
LIVES_AVOL_SCALE
#define LIVES_AVOL_SCALE
lives_standard_entry_new
LiVESWidget * lives_standard_entry_new(const char *labeltext, const char *txt, int dispwidth, int maxchars, LiVESBox *box, const char *tooltip)
Definition: widget-helper.c:9688
get_interp_value
LiVESInterpType get_interp_value(short quality, boolean low_for_mt)
Definition: utils.c:5744
create_cdtrack_dialog
LiVESWidget * create_cdtrack_dialog(int type, livespointer user_data)
Definition: interface.c:3138
lives_clipinfo_t::textview_lrate
LiVESWidget * textview_lrate
Definition: interface.h:87
lives_ruler_set_upper
WIDGET_HELPER_GLOBAL_INLINE double lives_ruler_set_upper(LiVESRuler *ruler, double value)
Definition: widget-helper.c:5296
do_header_write_error
boolean do_header_write_error(int clip)
Definition: dialogs.c:4169
WEED_EVENT_IS_FRAME
#define WEED_EVENT_IS_FRAME(event)
Definition: events.h:361
lives_window_add_accel_group
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_add_accel_group(LiVESWindow *window, LiVESAccelGroup *group)
Definition: widget-helper.c:2968
end_threaded_dialog
void end_threaded_dialog(void)
Definition: dialogs.c:3883
make_play_window
void make_play_window(void)
actually in gui.c
Definition: gui.c:3932
mainwindow::loop_cont_func
ulong loop_cont_func
Definition: mainwindow.h:1071
layout_map
Definition: multitrack.h:745
threaded_dialog_spin
void threaded_dialog_spin(double fraction)
Definition: dialogs.c:3823
_palette::mt_mark
lives_colRGBA64_t mt_mark
Definition: mainwindow.h:345
get_dir
char * get_dir(const char *filename)
Definition: utils.c:3185
move_block
track_rect * move_block(lives_mt *mt, track_rect *block, double timesecs, int old_track, int new_track)
Definition: multitrack.c:11417
lives_range_set_range
WIDGET_HELPER_GLOBAL_INLINE boolean lives_range_set_range(LiVESRange *range, double min, double max)
Definition: widget-helper.c:5672
append_filter_init_event
weed_plant_t * append_filter_init_event(weed_plant_t *event_list, weed_timecode_t tc, int filter_idx, int num_in_tracks, int key, weed_plant_t *inst)
Definition: events.c:2731
get_track_for_block
int get_track_for_block(track_rect *block)
return track number for a given block
Definition: multitrack.c:231
on_jumpnext_mark_activate
void on_jumpnext_mark_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16031
_prefs::warning_mask
uint64_t warning_mask
Definition: preferences.h:80
TRUE
#define TRUE
Definition: videoplugin.h:59
lives_clip_t::img_type
lives_img_type_t img_type
Definition: main.h:887
lives_big_and_bold
char * lives_big_and_bold(const char *fmt,...)
Definition: widget-helper.c:8648
lives_vscrollbar_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_vscrollbar_new(LiVESAdjustment *adj)
Definition: widget-helper.c:3441
PREF_MT_DEF_SIGNED_ENDIAN
#define PREF_MT_DEF_SIGNED_ENDIAN
Definition: preferences.h:1012
FX_CANDIDATE_AUDIO_VOL
#define FX_CANDIDATE_AUDIO_VOL
Definition: plugins.h:694
sizint
ssize_t sizint
type sizes
Definition: main.c:102
get_frame_event_clip
int get_frame_event_clip(weed_plant_t *event, int layer)
Definition: events.c:209
reset_mt_play_sizes
LIVES_LOCAL_INLINE void reset_mt_play_sizes(lives_mt *mt)
Definition: multitrack.c:518
do_yesno_dialog_with_check
boolean do_yesno_dialog_with_check(const char *text, uint64_t warn_mask_number)
Definition: dialogs.c:595
capability::umask
mode_t umask
Definition: main.h:597
on_framedraw_mouse_reset
boolean on_framedraw_mouse_reset(LiVESWidget *widget, LiVESXEventButton *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:1156
mainwindow::string_constants
char * string_constants[NUM_LIVES_STRING_CONSTANTS]
Definition: mainwindow.h:1539
_prefs::mt_def_arate
int mt_def_arate
Definition: preferences.h:273
lives_colRGBA64_t::green
uint16_t green
Definition: main.h:324
WEED_LEAF_PREV_CHANGE
#define WEED_LEAF_PREV_CHANGE
Definition: events.h:72
add_markers
void add_markers(lives_mt *mt, weed_plant_t *event_list, boolean add_block_ids)
Definition: multitrack.c:19877
MAX_VIDEO_TRACKS
#define MAX_VIDEO_TRACKS
Definition: multitrack.h:1045
_palette::audcol
lives_colRGBA64_t audcol
Definition: mainwindow.h:339
lives_widget_get_display
WIDGET_HELPER_GLOBAL_INLINE LiVESXDisplay * lives_widget_get_display(LiVESWidget *widget)
Definition: widget-helper.c:7229
lives_clip_t::next_event
weed_plant_t * next_event
Definition: main.h:1035
lives_read_buffered
ssize_t lives_read_buffered(int fd, void *buf, ssize_t count, boolean allow_less)
Definition: utils.c:924
lives_memset
#define lives_memset
Definition: machinestate.h:61
get_block_from_track_and_time
track_rect * get_block_from_track_and_time(lives_mt *mt, int track, double time)
get timeline end time of block
Definition: multitrack.c:23099
WEED_LEAF_TRACKS
#define WEED_LEAF_TRACKS
Definition: events.h:80
lives_widget_set_hexpand
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_hexpand(LiVESWidget *widget, boolean state)
Definition: widget-helper.c:6347
widget_opts_t::last_container
LiVESWidget * last_container
container which wraps last widget created + subwidgets (READONLY)
Definition: widget-helper.h:1407
layout_map::handle
char * handle
Definition: multitrack.h:746
MT_UNDO_MOVE_BLOCK
@ MT_UNDO_MOVE_BLOCK
Definition: multitrack.h:106
lives_menu_bar_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_menu_bar_new(void)
Definition: widget-helper.c:6380
render_details::dialog
LiVESWidget * dialog
Definition: events.h:220
LIVES_CURSOR_BLOCK
@ LIVES_CURSOR_BLOCK
non-standard cursors
Definition: widget-helper.h:1302
lives_direction_t
lives_direction_t
use REVERSE / FORWARD when a sign is used, BACKWARD / FORWARD when a parity is used
Definition: main.h:851
lives_widget_get_modmask
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_get_modmask(LiVESXDevice *device, LiVESWidget *widget, LiVESXModifierType *modmask)
Definition: widget-helper.c:7195
THREADVAR
#define THREADVAR(var)
Definition: machinestate.h:531
lives_table_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_table_new(uint32_t rows, uint32_t cols, boolean homogeneous)
Definition: widget-helper.c:6931
init_track_decoders
void init_track_decoders(void)
Definition: main.c:7816
redraw_tl_idle
boolean redraw_tl_idle(void *data)
Definition: interface.c:3457
QUANT_TIME
#define QUANT_TIME(dbltime)
Definition: multitrack.h:1049
do_mt_undo_mem_error
LIVES_GLOBAL_INLINE void do_mt_undo_mem_error(void)
Definition: dialogs.c:3579
mainwindow::recent_submenu
LiVESWidget * recent_submenu
Definition: mainwindow.h:1128
mainwindow::pretty_colours
boolean pretty_colours
Definition: mainwindow.h:1796
mainwindow::loop_cont
volatile boolean loop_cont
Definition: mainwindow.h:764
mt_spin_end_value_changed
void mt_spin_end_value_changed(LiVESSpinButton *spinbutton, livespointer user_data)
Definition: multitrack.c:4506
remove_markers
void remove_markers(weed_plant_t *event_list)
Definition: multitrack.c:21918
CURRENT_CLIP_HAS_AUDIO
#define CURRENT_CLIP_HAS_AUDIO
Definition: main.h:818
on_jumpnext_activate
void on_jumpnext_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16001
multitrack_redo
void multitrack_redo(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15298
FX_ORD_NONE
@ FX_ORD_NONE
Definition: multitrack.h:139
lives_vscale_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_vscale_new(LiVESAdjustment *adj)
Definition: widget-helper.c:3383
filter_init_has_owner
boolean filter_init_has_owner(weed_plant_t *init_event, int track)
Definition: events.c:1570
WEED_LEAF_FRAMES
#define WEED_LEAF_FRAMES
Definition: events.h:38
lives_rfx_t
Definition: plugins.h:625
lives_painter_set_source_pixbuf
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_pixbuf(lives_painter_t *cr, const LiVESPixbuf *pixbuf, double pixbuf_x, double pixbuf_y)
Definition: widget-helper.c:321
lives_toggle_button_toggle
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_toggle(LiVESToggleButton *tbutton)
Definition: widget-helper.c:11494
is_pure_audio
boolean is_pure_audio(weed_plant_t *plant, boolean count_opt)
TRUE if audio in or out and no vid in/out.
Definition: effects-weed.c:714
mainwindow::m_sepwinbutton
LiVESWidget * m_sepwinbutton
Definition: mainwindow.h:1369
mainwindow::cancelled
volatile lives_cancel_t cancelled
Definition: mainwindow.h:798
mt_post_playback
void mt_post_playback(lives_mt *mt)
Definition: multitrack.c:17284
events.h
show_lives
void show_lives(void)
Definition: gui.c:3006
WEED_LEAF_EVENT_ID
#define WEED_LEAF_EVENT_ID
Definition: events.h:49
lives_painter_set_source_rgb_from_lives_widget_color
WIDGET_HELPER_GLOBAL_INLINE LiVESWidgetColor * lives_painter_set_source_rgb_from_lives_widget_color(lives_painter_t *cr, LiVESWidgetColor *wcol)
Definition: widget-helper.c:11099
WEED_EVENT_IS_FILTER_DEINIT
#define WEED_EVENT_IS_FILTER_DEINIT(event)
Definition: events.h:365
is_realtime_aplayer
#define is_realtime_aplayer(ptype)
Definition: audio.h:236
_future_prefs::msg_textsize
int msg_textsize
Definition: preferences.h:842
email_author_activate
void email_author_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7048
add_video_track_behind
int add_video_track_behind(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:10523
lives_clip_t::fps
double fps
Definition: main.h:893
mainwindow::msg_adj
LiVESAdjustment * msg_adj
Definition: mainwindow.h:1326
on_track_header_move
boolean on_track_header_move(LiVESWidget *widget, LiVESXEventMotion *event, livespointer user_data)
Definition: multitrack.c:14121
recover_layout_cancelled
void recover_layout_cancelled(boolean is_startup)
Definition: multitrack.c:923
lives_widget_set_bg_color
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_bg_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
Definition: widget-helper.c:2056
insert_here_cb
void insert_here_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14244
mainwindow::afilter_map
weed_plant_t * afilter_map
Definition: mainwindow.h:1299
count_events
int count_events(weed_plant_t *event_list, boolean all_events, weed_timecode_t start_tc, weed_timecode_t end_tc)
Definition: events.c:4542
on_framedraw_leave
boolean on_framedraw_leave(LiVESWidget *widget, LiVESXEventCrossing *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:855
widget_opts_t::last_label
LiVESWidget * last_label
commonly adjusted values //////
Definition: widget-helper.h:1406
lives_scale_set_value_pos
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scale_set_value_pos(LiVESScale *scale, LiVESPositionType ptype)
Definition: widget-helper.c:6660
recover_layout
boolean recover_layout(void)
Definition: multitrack.c:1010
lives_menu_popup
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_popup(LiVESMenu *menu, LiVESXEventButton *event)
Definition: widget-helper.c:6579
close_ascrap_file
void close_ascrap_file(boolean remove)
Definition: saveplay.c:5612
lives_write_le_buffered
ssize_t lives_write_le_buffered(int fd, livesconstpointer buf, ssize_t count, boolean allow_fail)
lives_widget_unparent
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_unparent(LiVESWidget *widget)
Definition: widget-helper.c:11295
show_frame_events_activate
void show_frame_events_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22325
is_audio_channel_in
boolean is_audio_channel_in(weed_plant_t *inst, int chnum)
Definition: effects-weed.c:577
poly_page_to_tab
LIVES_INLINE int poly_page_to_tab(uint32_t page)
Definition: multitrack.c:3770
IMG_TYPE_JPEG
@ IMG_TYPE_JPEG
Definition: main.h:776
on_cleardisk_activate
void on_cleardisk_activate(LiVESWidget *widget, livespointer user_data)
Definition: callbacks.c:6139
mt_tl_move_relative
LIVES_INLINE void mt_tl_move_relative(lives_mt *mt, double pos_rel)
Definition: multitrack.c:3600
LIVES_FX_CAT_AUDIO_TRANSITION
@ LIVES_FX_CAT_AUDIO_TRANSITION
Definition: effects.h:23
on_mt_fx_edit_activate
void on_mt_fx_edit_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:10543
PREF_MT_ENTER_PROMPT
#define PREF_MT_ENTER_PROMPT
Definition: preferences.h:1033
delete_video_track
void delete_video_track(lives_mt *mt, int layer, boolean full)
Definition: multitrack.c:10121
callbacks.h
LIVES_FX_CAT_AV_TRANSITION
@ LIVES_FX_CAT_AV_TRANSITION
Definition: effects.h:21
LIVES_CURSOR_HAND2
@ LIVES_CURSOR_HAND2
Definition: widget-helper.h:1295
lives_painter_paint
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_paint(lives_painter_t *cr)
Definition: widget-helper.c:355
lives_clip_t::stored_layout_idx
int stored_layout_idx
M highest value used.
Definition: main.h:1072
set_submenu_colours
boolean set_submenu_colours(LiVESMenu *menu, LiVESWidgetColor *colf, LiVESWidgetColor *colb)
Definition: widget-helper.c:11762
make_param_box
boolean make_param_box(LiVESVBox *top_vbox, lives_rfx_t *rfx)
make a dynamic parameter window
Definition: paramwindow.c:1015
weed_get_idx_for_hashname
int weed_get_idx_for_hashname(const char *hashname, boolean fullname)
fullname includes author and version
Definition: effects-weed.c:10840
set_sel_label
void set_sel_label(LiVESWidget *label)
Definition: utils.c:4838
rte_get_numfilters
int rte_get_numfilters(void)
Definition: effects-weed.c:9899
CLIP_TOTAL_TIME
#define CLIP_TOTAL_TIME(clip)
Definition: main.h:830
_prefs::audio_src
int audio_src
Definition: preferences.h:204
mt_get_block_sttime
double mt_get_block_sttime(lives_mt *mt, int ntrack, int iblock)
return time in seconds of first frame event in block
Definition: multitrack.c:23084
add_aparam_menuitems
void add_aparam_menuitems(lives_mt *mt)
Definition: multitrack.c:5308
MT_UNDO_MOVE_AUDIO_BLOCK
@ MT_UNDO_MOVE_AUDIO_BLOCK
Definition: multitrack.h:109
lives_colRGBA64_t::blue
uint16_t blue
Definition: main.h:325
capable
capability * capable
Definition: main.h:627
draw_cool_toggle
boolean draw_cool_toggle(LiVESWidget *widget, lives_painter_t *cr, livespointer data)
Definition: widget-helper.c:12116
mainwindow::imsep
LiVESPixbuf * imsep
Definition: mainwindow.h:1104
GRAV_MODE_NORMAL
@ GRAV_MODE_NORMAL
Definition: multitrack.h:85
on_jumpback_activate
void on_jumpback_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15995
lives_toggle_button_get_active
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_get_active(LiVESToggleButton *button)
Definition: widget-helper.c:4472
set_params_unchanged
LIVES_INLINE void set_params_unchanged(lives_mt *mt, lives_rfx_t *rfx)
Definition: multitrack.c:1057
lives_arrow_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_arrow_new(LiVESArrowType arrow_type, LiVESShadowType shadow_type)
Definition: widget-helper.c:3469
AMIXER_HRATIO
#define AMIXER_HRATIO
audio mixer height ratio (fraction of screen height)
Definition: multitrack.h:45
LIVES_IS_INTERACTIVE
#define LIVES_IS_INTERACTIVE
Definition: mainwindow.h:1710
mt_track_is_video
boolean mt_track_is_video(lives_mt *mt, int ntrack)
return TRUE if ntrack is a valid video track
Definition: multitrack.c:23042
pull_frame
LIVES_GLOBAL_INLINE boolean pull_frame(weed_layer_t *layer, const char *image_ext, weed_timecode_t tc)
pull a frame from an external source into a layer the WEED_LEAF_CLIP and WEED_LEAF_FRAME leaves must ...
Definition: main.c:7500
mainwindow::recoverable_layout
boolean recoverable_layout
Definition: mainwindow.h:1483
widget_opts_t::mnemonic_label
boolean mnemonic_label
if underscore in label text should be mnemonic accelerator
Definition: widget-helper.h:1420
delete_audio_track
void delete_audio_track(lives_mt *mt, LiVESWidget *eventbox, boolean full)
Definition: multitrack.c:8993
lives_range_set_increments
WIDGET_HELPER_GLOBAL_INLINE boolean lives_range_set_increments(LiVESRange *range, double step, double page)
Definition: widget-helper.c:5681
WEED_LEAF_HINT
#define WEED_LEAF_HINT
for backwards compat.
Definition: events.h:13
lives_clipinfo_t::textview_frames
LiVESWidget * textview_frames
Definition: interface.h:82
mt_init_tracks
void mt_init_tracks(lives_mt *mt, boolean set_min_max)
add basic tracks, or set tracks from mt->event_list
Definition: multitrack.c:9679
lives_standard_menu_tool_button_new
LiVESToolItem * lives_standard_menu_tool_button_new(LiVESWidget *icon, const char *label)
Definition: widget-helper.c:9228
d_print
void d_print(const char *fmt,...)
Definition: utils.c:2542
lives_clip_t::name
char name[CLIP_NAME_MAXLEN]
the display name
Definition: main.h:922
on_boolean_toggled
void on_boolean_toggled(LiVESWidgetObject *obj, livespointer user_data)
Definition: callbacks.c:8278
lives_spin_button_configure
boolean lives_spin_button_configure(LiVESSpinButton *spinbutton, double value, double lower, double upper, double step_increment, double page_increment)
Definition: widget-helper.c:11783
close_current_file
void close_current_file(int file_to_switch_to)
close current file, and try to switch to file_to_switch_to
Definition: main.c:9373
lives_adjustment_get_page_size
WIDGET_HELPER_GLOBAL_INLINE double lives_adjustment_get_page_size(LiVESAdjustment *adj)
Definition: widget-helper.c:5549
create_cds_dialog
_entryw * create_cds_dialog(int type)
Definition: interface.c:4348
_prefs::back_compat
boolean back_compat
Definition: preferences.h:471
unhide_cursor
WIDGET_HELPER_GLOBAL_INLINE boolean unhide_cursor(LiVESXWindow *window)
Definition: widget-helper.c:12056
mainwindow::raudio_drawable
lives_painter_surface_t * raudio_drawable
Definition: mainwindow.h:1386
switch_to_file
void switch_to_file(int old_file, int new_file)
Definition: main.c:9646
lives_widget_object_set_data_auto
WIDGET_HELPER_GLOBAL_INLINE void lives_widget_object_set_data_auto(LiVESWidgetObject *obj, const char *key, livespointer data)
Definition: widget-helper.c:285
int_array_contains_value
boolean int_array_contains_value(int *array, int num_elems, int value)
Definition: utils.c:4284
deinit_render_effects
void deinit_render_effects(void)
switch off effects after render preview during rendering/render preview, we use the "keys" FX_KEYS_MA...
Definition: effects-weed.c:7358
on_quit_activate
void on_quit_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:2133
WEED_LEAF_IN_TRACKS
#define WEED_LEAF_IN_TRACKS
Definition: events.h:47
mt_get_last_block_uid
ulong mt_get_last_block_uid(lives_mt *mt)
get index of last inserted (wallclock time) block for track
Definition: multitrack.c:23054
lives_standard_hruler_new
LiVESWidget * lives_standard_hruler_new(void)
Definition: widget-helper.c:10231
QUANT_TICKS
#define QUANT_TICKS(ticks)
Definition: multitrack.h:1052
mt_set_autotrans
void mt_set_autotrans(int idx)
Definition: multitrack.c:6080
has_non_alpha_palette
boolean has_non_alpha_palette(weed_plant_t *ctmpl, weed_plant_t *filter)
Definition: effects-weed.c:143
STYLE_2
#define STYLE_2
colour the spinbuttons on the front page if set
Definition: mainwindow.h:300
PREF_LETTERBOXMT
#define PREF_LETTERBOXMT
Definition: preferences.h:1070
_prefs::open_maximised
boolean open_maximised
Definition: preferences.h:28
mainwindow::preview_box
LiVESWidget * preview_box
Definition: mainwindow.h:1304
WEED_AUDIO_BIG_ENDIAN
#define WEED_AUDIO_BIG_ENDIAN
Definition: multitrack.c:94
mt_tcoverlay_callback
boolean mt_tcoverlay_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:18897
lives_painter_create_from_surface
WIDGET_HELPER_GLOBAL_INLINE lives_painter_t * lives_painter_create_from_surface(lives_painter_surface_t *target)
Definition: widget-helper.c:309
PREF_MAX_DISP_VTRACKS
#define PREF_MAX_DISP_VTRACKS
Definition: preferences.h:983
_palette::mt_timecode_bg
LiVESWidgetColor mt_timecode_bg
Definition: mainwindow.h:332
lives_notify_int
LIVES_GLOBAL_INLINE void lives_notify_int(int msgnumber, int msgint)
Definition: callbacks.c:77
lives_painter_move_to
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_move_to(lives_painter_t *cr, double x, double y)
Definition: widget-helper.c:520
lives_general_button_clicked
void lives_general_button_clicked(LiVESButton *button, livespointer data_to_free)
Definition: widget-helper.c:12306
set_boolean_pref
int set_boolean_pref(const char *key, boolean value)
Definition: preferences.c:354
get_basename
void get_basename(char *filename)
Definition: utils.c:3194
lives_adjustment_new
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_adjustment_new(double value, double lower, double upper, double step_increment, double page_increment, double page_size)
Definition: widget-helper.c:3196
do_mt_no_audchan_error
LIVES_GLOBAL_INLINE void do_mt_no_audchan_error(void)
Definition: dialogs.c:3614
on_preview_clicked
void on_preview_clicked(LiVESButton *button, livespointer user_data)
Definition: callbacks.c:10245
weed_layer_get_palette
LIVES_GLOBAL_INLINE int weed_layer_get_palette(weed_layer_t *layer)
Definition: colourspace.c:13977
lives_rfx_t::num_params
int num_params
Definition: plugins.h:644
WARN_MASK_EXIT_MT
#define WARN_MASK_EXIT_MT
off by default on a fresh install
Definition: preferences.h:106
lives_widget_object_ref
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_object_ref(livespointer object)
increase refcount by one
Definition: widget-helper.c:797
JACK_OPTS_TIMEBASE_START
#define JACK_OPTS_TIMEBASE_START
jack sets play start position
Definition: preferences.h:238
CANCEL_VID_END
@ CANCEL_VID_END
video playback completed
Definition: main.h:725
lives_clip_t::signed_endian
uint32_t signed_endian
bitfield
Definition: main.h:909
mainwindow::sep_win
boolean sep_win
Definition: mainwindow.h:761
get_frame_event_frame
frames_t get_frame_event_frame(weed_plant_t *event, int layer)
Definition: events.c:224
mainwindow::play_image
LiVESWidget * play_image
Definition: mainwindow.h:946
mainwindow::msg
char msg[MAINW_MSG_SIZE]
Definition: mainwindow.h:724
WEED_LEAF_HOST_TAG
#define WEED_LEAF_HOST_TAG
Definition: effects-weed.h:66
CLIP_TYPE_FILE
@ CLIP_TYPE_FILE
unimported video, not or partially broken in frames
Definition: main.h:765
mainwindow::pre_src_file
int pre_src_file
video file we were playing before any ext input started
Definition: mainwindow.h:971
POLY_EFFECTS
@ POLY_EFFECTS
Definition: multitrack.h:127
WEED_AUDIO_LITTLE_ENDIAN
#define WEED_AUDIO_LITTLE_ENDIAN
Definition: multitrack.c:93
lives_mv
int lives_mv(const char *from, const char *to)
Definition: utils.c:4446
SignalHandlerPointer
void(* SignalHandlerPointer)(int)
Definition: main.h:1464
on_node_spin_value_changed
void on_node_spin_value_changed(LiVESSpinButton *spinbutton, livespointer user_data)
Definition: multitrack.c:19024
block_overlap
boolean block_overlap(LiVESWidget *eventbox, double time_start, double time_end)
Definition: multitrack.c:11369
has_frame_event_at
boolean has_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc, weed_plant_t **shortcut)
Definition: events.c:129
_resaudw::aud_checkbutton
LiVESWidget * aud_checkbutton
Definition: resample.h:32
maybe_add_mt_idlefunc
void maybe_add_mt_idlefunc(void)
lives_painter_surface_flush
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_surface_flush(lives_painter_surface_t *surf)
Definition: widget-helper.c:648
mainwindow::message_box
LiVESWidget * message_box
Definition: mainwindow.h:1323
mainwindow::sepwin
LiVESWidget * sepwin
Definition: mainwindow.h:1176
layout_map::name
char * name
Definition: multitrack.h:748
lives_spin_button_get_value
WIDGET_HELPER_GLOBAL_INLINE double lives_spin_button_get_value(LiVESSpinButton *button)
Definition: widget-helper.c:5083
mainwindow::spinbutton_start
LiVESWidget * spinbutton_start
Definition: mainwindow.h:1288
track_arrow_pressed
boolean track_arrow_pressed(LiVESWidget *ebox, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:2807
LAYOUT_NUMBERING_FILENAME
#define LAYOUT_NUMBERING_FILENAME
Definition: mainwindow.h:572
freeze_callback
boolean freeze_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: callbacks.c:11135
CANCEL_NO_MORE_PREVIEW
@ CANCEL_NO_MORE_PREVIEW
ran out of preview frames
Definition: main.h:716
d_print_file_error_failed
void d_print_file_error_failed(void)
Definition: utils.c:2625
lives_signal_handlers_unblock_by_func
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handlers_unblock_by_func(livespointer instance, LiVESGuiCallback func, livespointer data)
Definition: widget-helper.c:1435
HIDDEN_MULTI
#define HIDDEN_MULTI
Definition: plugins.h:552
_palette::white
LiVESWidgetColor white
Definition: mainwindow.h:306
pref_factory_bool
boolean pref_factory_bool(const char *prefidx, boolean newval, boolean permanent)
Definition: preferences.c:717
get_filter_map_after
weed_plant_t * get_filter_map_after(weed_plant_t *event, int ctrack)
Definition: events.c:821
lives_clip_t::ratio_fps
boolean ratio_fps
framerate of the clip
Definition: main.h:894
layout_map::list
LiVESList * list
Definition: multitrack.h:749
CAM_FORMAT_HDV
#define CAM_FORMAT_HDV
Definition: ldvgrab.h:15
lives_standard_label_new
LiVESWidget * lives_standard_label_new(const char *text)
Definition: widget-helper.c:8601
mainwindow::textwidget_focus
LiVESWidget * textwidget_focus
Definition: mainwindow.h:1569
all_present
boolean all_present(weed_plant_t *event, LiVESList *sel)
Definition: multitrack.c:18498
on_split_sel_activate
void on_split_sel_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16632
mainwindow::scrap_file
int scrap_file
we throw odd sized frames here when recording in real time; used if a source is a generator or stream
Definition: mainwindow.h:874
mt_get_effect_time
LIVES_GLOBAL_INLINE double mt_get_effect_time(lives_mt *mt)
Definition: multitrack.c:1735
weed_filter_get_in_paramtmpls
WEED_GLOBAL_INLINE weed_plant_t ** weed_filter_get_in_paramtmpls(weed_plant_t *filter, int *ntmpls)
Definition: weed-effects-utils.c:257
PREF_ACTIVE_AUTOTRANS
#define PREF_ACTIVE_AUTOTRANS
Definition: preferences.h:936
FN_KEYS
#define FN_KEYS
number of function keys
Definition: mainwindow.h:195
weed_paramtmpl_get_type
WEED_GLOBAL_INLINE int weed_paramtmpl_get_type(weed_plant_t *paramtmpl)
Definition: weed-effects-utils.c:312
update_grav_mode
void update_grav_mode(lives_mt *mt)
Definition: multitrack.c:5012
lives_container_child_set_shrinkable
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_child_set_shrinkable(LiVESContainer *c, LiVESWidget *child, boolean val)
Definition: widget-helper.c:11750
insert_audio_event_at
void insert_audio_event_at(weed_plant_t *event, int track, int clipnum, double seek, double vel)
Definition: events.c:1243
ttable::in
uint64_t in
Definition: multitrack.h:740
lives_menu_detach
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_detach(LiVESMenu *menu)
Definition: widget-helper.c:6601
on_open_fw_activate
void on_open_fw_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: ldvgrab.c:300
mt_tl_move
void mt_tl_move(lives_mt *mt, double pos)
Definition: multitrack.c:3595
reset_clipmenu
void reset_clipmenu(void)
Definition: utils.c:4290
is_empty_track
LIVES_INLINE boolean is_empty_track(LiVESWidgetObject *track)
Definition: multitrack.c:236
lives_check_button_new_with_label
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_check_button_new_with_label(const char *label)
Definition: widget-helper.c:4606
lives_clipinfo_t::textview_type
LiVESWidget * textview_type
Definition: interface.h:79
LIVES_DIRECTION_BACKWARD
@ LIVES_DIRECTION_BACKWARD
Definition: main.h:853
CAM_FORMAT_DV
#define CAM_FORMAT_DV
Definition: ldvgrab.h:14
mainwindow::stored_layout_undos
LiVESList * stored_layout_undos
Definition: mainwindow.h:810
get_float_audio_val_at_time
float get_float_audio_val_at_time(int fnum, int afd, double secs, int chnum, int chans)
Definition: audio.c:374
lives_clip_t::info_file
char info_file[PATH_MAX]
used for asynch communication with externals
Definition: main.h:1009
lives_display_get_pointer
WIDGET_HELPER_GLOBAL_INLINE boolean lives_display_get_pointer(LiVESXDevice *device, LiVESXDisplay *display, LiVESXScreen **screen, int *x, int *y, LiVESXModifierType *mask)
Definition: widget-helper.c:7254
EVENT_MARKER_BLOCK_UNORDERED
#define EVENT_MARKER_BLOCK_UNORDERED
Definition: events.h:354
_entryw::entry
LiVESWidget * entry
Definition: interface.h:95
interface.h
lives_cursor_unref
WIDGET_HELPER_GLOBAL_INLINE boolean lives_cursor_unref(LiVESXCursor *cursor)
Definition: widget-helper.c:11139
weed_event_set_timecode
LIVES_GLOBAL_INLINE weed_timecode_t weed_event_set_timecode(weed_event_t *event, weed_timecode_t tc)
Definition: events.c:83
lives_vbox_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_vbox_new(boolean homogeneous, int spacing)
Definition: widget-helper.c:3267
WEED_LEAF_FILTER
#define WEED_LEAF_FILTER
Definition: events.h:44
FX_BLOCK_HEIGHT
#define FX_BLOCK_HEIGHT
Definition: multitrack.h:31
WARN_MASK_LAYOUT_WIPE
#define WARN_MASK_LAYOUT_WIPE
Definition: preferences.h:124
RFX_FLAGS_NO_RESET
#define RFX_FLAGS_NO_RESET
Definition: plugins.h:647
lives_text_buffer_get_iter_at_mark
WIDGET_HELPER_GLOBAL_INLINE boolean lives_text_buffer_get_iter_at_mark(LiVESTextBuffer *tbuff, LiVESTextIter *iter, LiVESTextMark *mark)
Definition: widget-helper.c:4142
d_print_failed
void d_print_failed(void)
Definition: utils.c:2615
get_prev_event
LIVES_GLOBAL_INLINE weed_plant_t * get_prev_event(weed_plant_t *event)
Definition: events.c:109
lives_notebook_set_current_page
WIDGET_HELPER_GLOBAL_INLINE boolean lives_notebook_set_current_page(LiVESNotebook *nbook, int pagenum)
Definition: widget-helper.c:6912
lives_text_buffer_delete
WIDGET_HELPER_GLOBAL_INLINE boolean lives_text_buffer_delete(LiVESTextBuffer *tbuff, LiVESTextIter *start, LiVESTextIter *end)
Definition: widget-helper.c:4126
mt_prevclip
boolean mt_prevclip(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3096
unpaint_line
void unpaint_line(lives_mt *mt, LiVESWidget *eventbox)
Definition: multitrack.c:14130
widget_opts_t::use_markup
boolean use_markup
whether markup should be used in labels
Definition: widget-helper.h:1421
WEED_EVENT_IS_PARAM_CHANGE
#define WEED_EVENT_IS_PARAM_CHANGE(event)
Definition: events.h:367
BLOCK_DRAW_SIMPLE
#define BLOCK_DRAW_SIMPLE
Definition: multitrack.h:51
capability::has_mplayer
lives_checkstatus_t has_mplayer
Definition: main.h:511
mt_tlfor_frame
boolean mt_tlfor_frame(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3615
restore_weed_instances
void restore_weed_instances(void)
Definition: effects-weed.c:423
lives_toolbar_set_icon_size
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toolbar_set_icon_size(LiVESToolbar *toolbar, LiVESIconSize icon_size)
Definition: widget-helper.c:5407
avel_reverse_toggled
void avel_reverse_toggled(LiVESToggleButton *togglebutton, livespointer user_data)
Definition: multitrack.c:12500
process_events
weed_plant_t * process_events(weed_plant_t *next_event, boolean process_audio, weed_timecode_t curr_tc)
Definition: events.c:3082
mainwindow::playing_file
int playing_file
which number file we are playing (or -1) [generally mainw->current_file]
Definition: mainwindow.h:943
lives_widget_get_parent
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_widget_get_parent(LiVESWidget *widget)
Definition: widget-helper.c:4739
lives_paned_pack
WIDGET_HELPER_GLOBAL_INLINE boolean lives_paned_pack(int where, LiVESPaned *paned, LiVESWidget *child, boolean resize, boolean shrink)
Definition: widget-helper.c:4432
P_
#define P_(String, StringPlural, n)
Definition: support.h:46
widget_opts_t::border_width
int border_width
border width in pixels
Definition: widget-helper.h:1416
on_amixer_close_clicked
void on_amixer_close_clicked(LiVESButton *button, lives_mt *mt)
Definition: multitrack.c:22536
remove_audio_for_track
void remove_audio_for_track(weed_plant_t *event, int track)
Definition: events.c:1349
do_layout_ascrap_file_error
LIVES_GLOBAL_INLINE void do_layout_ascrap_file_error(void)
Definition: dialogs.c:3059
LIVES_EXPAND_DEFAULT_HEIGHT
#define LIVES_EXPAND_DEFAULT_HEIGHT
Definition: widget-helper.h:1312
mainwindow::m_loopbutton
LiVESWidget * m_loopbutton
Definition: mainwindow.h:1370
mainwindow::files
lives_clip_t * files[MAX_FILES+1]
+1 for the clipboard
Definition: mainwindow.h:729
explain_missing_activate
void explain_missing_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: startup.c:1397
lives_clip_t::stored_layout_fps
double stored_layout_fps
Definition: main.h:1074
mainwindow::mute_audio_func
ulong mute_audio_func
Definition: mainwindow.h:1072
mouse_mode_context
void mouse_mode_context(lives_mt *mt)
Definition: multitrack.c:4916
POLY_CLIPS
@ POLY_CLIPS
Definition: multitrack.h:123
lives_widget_get_allocation_height
WIDGET_HELPER_GLOBAL_INLINE int lives_widget_get_allocation_height(LiVESWidget *widget)
Definition: widget-helper.c:5470
LIVES_FX_CAT_AUDIO_EFFECT
@ LIVES_FX_CAT_AUDIO_EFFECT
Definition: effects.h:26
save_clip_value
boolean save_clip_value(int which, lives_clip_details_t, void *val)
Definition: utils.c:5175
weed_instance_from_filter
weed_plant_t * weed_instance_from_filter(weed_plant_t *filter)
Definition: effects-weed.c:6469
widget_opts_t::monitor
int monitor
monitor we are displaying on
Definition: widget-helper.h:1437
ttable::out
void * out
Definition: multitrack.h:741
main_thread_execute
void * main_thread_execute(lives_funcptr_t func, int return_type, void *retval, const char *args_fmt,...)
Definition: machinestate.c:1741
mt_show_current_frame
void mt_show_current_frame(lives_mt *mt, boolean return_layer)
preview the current frame
Definition: multitrack.c:3214
lives_toolbar_get_icon_size
WIDGET_HELPER_GLOBAL_INLINE LiVESIconSize lives_toolbar_get_icon_size(LiVESToolbar *toolbar)
Definition: widget-helper.c:5399
set_mt_colours
void set_mt_colours(lives_mt *mt)
Definition: multitrack.c:6148
CANCEL_NONE
@ CANCEL_NONE
no cancel
Definition: main.h:701
weed_palette_has_alpha
LIVES_GLOBAL_INLINE boolean weed_palette_has_alpha(int pal)
Definition: colourspace.c:1466
WEED_LEAF_OUT_COUNT
#define WEED_LEAF_OUT_COUNT
Definition: events.h:46
MOUSE_MODE_MOVE
@ MOUSE_MODE_MOVE
Definition: multitrack.h:70
label_act_toggle
boolean label_act_toggle(LiVESWidget *widget, LiVESXEventButton *event, LiVESWidget *togglebutton)
Definition: widget-helper.c:11476
mainwindow::first_free_file
int first_free_file
Definition: mainwindow.h:728
do_layout_scrap_file_error
LIVES_GLOBAL_INLINE void do_layout_scrap_file_error(void)
Definition: dialogs.c:3053
mt_selection_lock
void mt_selection_lock(LiVESMenuItem *menuitem, livespointer user_data)
lock the time selection
Definition: multitrack.c:16863
lives_widget_set_can_focus_and_default
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_can_focus_and_default(LiVESWidget *widget)
Definition: widget-helper.c:12300
WEED_LEAF_DEINIT_EVENT
#define WEED_LEAF_DEINIT_EVENT
Definition: events.h:76
mainwindow::signal_caught
uint32_t signal_caught
Definition: mainwindow.h:1673
mt_load_recovery_layout
boolean mt_load_recovery_layout(lives_mt *mt)
Definition: multitrack.c:941
mt_add_block_effect
void mt_add_block_effect(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15787
lives_close_buffered
int lives_close_buffered(int fd)
Definition: utils.c:716
mt_framedraw
LiVESPixbuf * mt_framedraw(lives_mt *mt, weed_layer_t *layer)
Definition: paramspecial.c:741
SEPWIN_TYPE_NON_STICKY
#define SEPWIN_TYPE_NON_STICKY
Definition: preferences.h:187
lives_window_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_window_new(LiVESWindowType wintype)
Definition: widget-helper.c:2611
WEED_LEAF_PTRSIZE
#define WEED_LEAF_PTRSIZE
deprecated
Definition: events.h:84
widget_opts_t::packing_height
int packing_height
vertical pixels between widgets
Definition: widget-helper.h:1411
multitrack_view_clips
void multitrack_view_clips(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:2844
lives_menu_item_set_submenu
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_item_set_submenu(LiVESMenuItem *menuitem, LiVESWidget *menu)
Definition: widget-helper.c:6519
insert_at_ctx_cb
void insert_at_ctx_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14251
has_video_chans_out
boolean has_video_chans_out(weed_plant_t *filter, boolean count_opt)
Definition: effects-weed.c:676
mainwindow::sepwin_cb_func
ulong sepwin_cb_func
Definition: mainwindow.h:1074
lives_range_get_value
WIDGET_HELPER_GLOBAL_INLINE double lives_range_get_value(LiVESRange *range)
Definition: widget-helper.c:5699
free_pkg_list
LIVES_LOCAL_INLINE void free_pkg_list(void)
Definition: multitrack.c:4043
lives_range_set_inverted
WIDGET_HELPER_GLOBAL_INLINE boolean lives_range_set_inverted(LiVESRange *range, boolean invert)
Definition: widget-helper.c:5690
animate_multitrack
void animate_multitrack(lives_mt *mt)
Definition: multitrack.c:14223
lives_widget_object_set_data_widget_object
WIDGET_HELPER_GLOBAL_INLINE void lives_widget_object_set_data_widget_object(LiVESWidgetObject *obj, const char *key, livespointer other)
Definition: widget-helper.c:298
mainwindow::accel_group
LiVESAccelGroup * accel_group
Definition: mainwindow.h:1228
add_to_recovery_file
void add_to_recovery_file(const char *handle)
Definition: saveplay.c:6460
palette
_palette * palette
interface colour settings
Definition: main.c:101
weed_params_create
weed_plant_t ** weed_params_create(weed_plant_t *filter, boolean in)
Definition: effects-weed.c:6318
insert_audio_at_ctx_cb
void insert_audio_at_ctx_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14291
on_track_header_click
boolean on_track_header_click(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13925
lives_image_new_from_pixbuf
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_image_new_from_pixbuf(LiVESPixbuf *pixbuf)
Definition: widget-helper.c:2428
lives_widget_object_ref_sink
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_object_ref_sink(livespointer object)
Definition: widget-helper.c:845
get_achannel_name
LIVES_GLOBAL_INLINE char * get_achannel_name(int totchans, int idx)
Definition: audio.c:28
_prefs::force64bit
boolean force64bit
< force system clock (rather than soundcard) for timing ( better for high framerates )
Definition: preferences.h:368
q_gint64
LIVES_GLOBAL_INLINE ticks_t q_gint64(ticks_t in, double fps)
Definition: resample.c:25
sget_file_size
off_t sget_file_size(const char *name)
Definition: machinestate.c:962
load_start_image
void load_start_image(int frame)
Definition: main.c:5650
event_list_free
void event_list_free(weed_plant_t *event_list)
Definition: events.c:2313
PREF_MT_DEF_HEIGHT
#define PREF_MT_DEF_HEIGHT
Definition: preferences.h:1008
mt_track_is_audio
boolean mt_track_is_audio(lives_mt *mt, int ntrack)
return TRUE if ntrack is a valid backing audio track
Definition: multitrack.c:23048
EVENT_MARKER_RECORD_END
#define EVENT_MARKER_RECORD_END
Definition: events.h:356
mainwindow::recording_recovered
boolean recording_recovered
Definition: mainwindow.h:1486
mt_swap_play_pause
void mt_swap_play_pause(lives_mt *mt, boolean put_pause)
Definition: multitrack.c:17206
lives_tool_button_set_border_color
boolean lives_tool_button_set_border_color(LiVESWidget *button, LiVESWidgetState state, LiVESWidgetColor *colour)
Definition: widget-helper.c:12520
lives_event_list_new
weed_event_t * lives_event_list_new(weed_event_t *elist, const char *cdate)
lib-ish stuff
Definition: events.c:240
render_details::enc_changed
boolean enc_changed
Definition: events.h:244
draw_region
void draw_region(lives_mt *mt)
Definition: multitrack.c:18159
error
error("LSD_RANDFUNC(ptr, size) must be defined")
weed_generator_end
void weed_generator_end(weed_plant_t *inst)
Definition: effects-weed.c:8176
LIVES_TEXT_SIZE_MEDIUM
#define LIVES_TEXT_SIZE_MEDIUM
Definition: widget-helper.h:1369
lives_hbox_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hbox_new(boolean homogeneous, int spacing)
Definition: widget-helper.c:3253
lives_adjustment_get_value
WIDGET_HELPER_GLOBAL_INLINE double lives_adjustment_get_value(LiVESAdjustment *adj)
Definition: widget-helper.c:5575
mainwindow::stored_event_list
weed_event_t * stored_event_list
stored mt -> clip editor
Definition: mainwindow.h:804
_prefs::mt_def_asamps
int mt_def_asamps
Definition: preferences.h:273
lives_signal_handlers_disconnect_by_func
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handlers_disconnect_by_func(livespointer instance, LiVESGuiCallback func, livespointer data)
Definition: widget-helper.c:1399
check_layer_ready
boolean check_layer_ready(weed_layer_t *layer)
block until layer pixel_data is ready.
Definition: main.c:7528
close_scrap_file
void close_scrap_file(boolean remove)
Definition: saveplay.c:5583
mainwindow::m_playbutton
LiVESWidget * m_playbutton
Definition: mainwindow.h:1369
get_next_fm
weed_plant_t * get_next_fm(lives_mt *mt, int current_track, weed_plant_t *event)
Definition: multitrack.c:18838
LIVES_CURSOR_SB_H_DOUBLE_ARROW
@ LIVES_CURSOR_SB_H_DOUBLE_ARROW
Definition: widget-helper.h:1296
on_insgap_sel_activate
void on_insgap_sel_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14982
widget_opts_t::pack_end
boolean pack_end
pack widget at end or start
Definition: widget-helper.h:1418
DEF_BUTTON_WIDTH
#define DEF_BUTTON_WIDTH
Definition: mainwindow.h:182
suggest_feature_activate
void suggest_feature_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7058
ttable
Definition: multitrack.h:739
lives_widget_set_app_paintable
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_app_paintable(LiVESWidget *widget, boolean paintable)
Definition: widget-helper.c:1748
lives_fx_candidate_t::delegate
int delegate
offset in list of current delegate
Definition: plugins.h:688
on_show_file_info_activate
void on_show_file_info_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:6776
WEED_LEAF_LAST
#define WEED_LEAF_LAST
Definition: events.h:65
NB_ERROR_SEL
@ NB_ERROR_SEL
Definition: multitrack.h:114
fill_param_vals_to
void fill_param_vals_to(weed_plant_t *param, weed_plant_t *paramtmpl, int index)
for a multi valued parameter or pchange, we will fill WEED_LEAF_VALUE up to element index with WEED_L...
Definition: effects-weed.c:9913
lives_get_current_ticks
LIVES_GLOBAL_INLINE ticks_t lives_get_current_ticks(void)
Definition: machinestate.c:835
mainwindow::laudio_drawable
lives_painter_surface_t * laudio_drawable
Definition: mainwindow.h:1386
set_int_pref
int set_int_pref(const char *key, int value)
Definition: preferences.c:329
_future_prefs::sepwin_type
short sepwin_type
Definition: preferences.h:832
WEED_LEAF_NEXT_CHANGE
#define WEED_LEAF_NEXT_CHANGE
Definition: events.h:71
lives_painter_new_path
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_new_path(lives_painter_t *cr)
Definition: widget-helper.c:478
STYLE_LIGHT
#define STYLE_LIGHT
Definition: mainwindow.h:304
in_out_end_changed
void in_out_end_changed(LiVESWidget *widget, livespointer user_data)
Definition: multitrack.c:12174
lives_widget_queue_draw_area
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_queue_draw_area(LiVESWidget *widget, int x, int y, int width, int height)
Definition: widget-helper.c:1593
resize_play_window
void resize_play_window(void)
Definition: gui.c:4349
_prefs::mt_pertrack_audio
boolean mt_pertrack_audio
Definition: preferences.h:278
unselect_all
void unselect_all(lives_mt *mt)
unselect all blocks
Definition: multitrack.c:11620
lives_widget_get_position
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_get_position(LiVESWidget *widget, int *x, int *y)
Definition: widget-helper.c:2802
MIN_MSGBAR_HEIGHT
#define MIN_MSGBAR_HEIGHT
Definition: mainwindow.h:135
lives_widget_add_events
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_add_events(LiVESWidget *widget, int events)
Definition: widget-helper.c:4821
on_mute_activate
void on_mute_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:8399
multitrack_view_events
void multitrack_view_events(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:18122
on_open_vcd_activate
void on_open_vcd_activate(LiVESMenuItem *menuitem, livespointer device_type)
Definition: callbacks.c:720
widget_opts_t::scale
double scale
scale factor for all sizes
Definition: widget-helper.h:1433
BLOCK_THUMB_WIDTH
#define BLOCK_THUMB_WIDTH
Definition: multitrack.h:19
do_threaded_dialog
void do_threaded_dialog(const char *trans_text, boolean has_cancel)
Definition: dialogs.c:3849
lives_clip_t::arps
int arps
audio physical sample rate (i.e the "normal" sample rate of the clip when played at 1,...
Definition: main.h:905
lives_spin_button_set_adjustment
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_spin_button_set_adjustment(LiVESSpinButton *button, LiVESAdjustment *adj)
Definition: widget-helper.c:5111
lives_list_append_unique
LiVESList * lives_list_append_unique(LiVESList *xlist, const char *add)
Definition: utils.c:5776
on_fx_insa_clicked
void on_fx_insa_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:18907
catch_sigint
void catch_sigint(int signum)
Definition: main.c:296
rte_keymode_get_instance
weed_plant_t * rte_keymode_get_instance(int key, int mode)
returns refcounted filter_instance bound to key/mode (or NULL)
Definition: effects-weed.c:9450
lives_rmdir
int lives_rmdir(const char *dir, boolean force)
Definition: utils.c:4366
lives_strdup_printf
#define lives_strdup_printf(fmt,...)
Definition: support.c:27
lives_text_buffer_delete_mark
WIDGET_HELPER_GLOBAL_INLINE boolean lives_text_buffer_delete_mark(LiVESTextBuffer *tbuff, LiVESTextMark *mark)
Definition: widget-helper.c:4117
lives_pixbuf_new
WIDGET_HELPER_GLOBAL_INLINE LiVESPixbuf * lives_pixbuf_new(boolean has_alpha, int width, int height)
Definition: widget-helper.c:3047
after_param_text_changed
void after_param_text_changed(LiVESWidget *textwidget, lives_rfx_t *rfx)
Definition: paramwindow.c:2635
WEED_LEAF_INDEX
#define WEED_LEAF_INDEX
Definition: events.h:58
lives_paned_get_position
WIDGET_HELPER_GLOBAL_INLINE int lives_paned_get_position(LiVESPaned *paned)
Definition: widget-helper.c:4414
GUI_SCREEN_HEIGHT
#define GUI_SCREEN_HEIGHT
Definition: mainwindow.h:100
get_event_timecode
LIVES_GLOBAL_INLINE weed_timecode_t get_event_timecode(weed_plant_t *plant)
Definition: events.c:98
on_prev_fm_clicked
void on_prev_fm_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:18933
insert_param_change_event_at
void insert_param_change_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event)
Definition: events.c:1116
WEED_LEAF_AUDIO_SIGNED
#define WEED_LEAF_AUDIO_SIGNED
Definition: events.h:19
lives_calloc
#define lives_calloc
Definition: machinestate.h:67
add_to_pkg_list
LIVES_LOCAL_INLINE int add_to_pkg_list(char *pkgstring)
Definition: multitrack.c:4033
remove_first_gaps
void remove_first_gaps(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14640
lives_tool_button_set_label_widget
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tool_button_set_label_widget(LiVESToolButton *button, LiVESWidget *label)
Definition: widget-helper.c:5218
set_child_colour
WIDGET_HELPER_GLOBAL_INLINE void set_child_colour(LiVESWidget *widget, boolean set_all)
Definition: widget-helper.c:11592
ldvgrab.h
backup_weed_instances
void backup_weed_instances(void)
for multitrack
Definition: effects-weed.c:410
mt_tlback_frame
boolean mt_tlback_frame(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3634
set_drawing_area_from_pixbuf
void set_drawing_area_from_pixbuf(LiVESWidget *widget, LiVESPixbuf *pixbuf, lives_painter_surface_t *surface)
Definition: main.c:5525
lives_image_set_from_pixbuf
WIDGET_HELPER_GLOBAL_INLINE boolean lives_image_set_from_pixbuf(LiVESImage *image, LiVESPixbuf *pixbuf)
Definition: widget-helper.c:2440
paramwindow.h
lives_clip_t::tcache_dubious_from
frames_t tcache_dubious_from
height for thumbnail cache (width is fixed, but if this changes, invalidate)
Definition: main.h:1099
lives_clip_t::changed
boolean changed
Definition: main.h:915
mt_auto_backup
boolean mt_auto_backup(livespointer user_data)
Definition: multitrack.c:863
lives_create_buffered
int lives_create_buffered(const char *pathname, int mode)
Definition: utils.c:698
lives_scale_button_set_orientation
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scale_button_set_orientation(LiVESScaleButton *scale, LiVESOrientation orientation)
Definition: widget-helper.c:6678
get_last_event
LIVES_GLOBAL_INLINE weed_plant_t * get_last_event(weed_plant_t *event_list)
Definition: events.c:124
mainwindow::stored_layout_name
char stored_layout_name[PATH_MAX]
Definition: mainwindow.h:808
lives_clip_t::unique_id
uint64_t unique_id
this and the handle can be used to uniquely id a file
Definition: main.h:880
lives_list_free_all
void lives_list_free_all(LiVESList **)
Definition: utils.c:4873
WEED_LEAF_IS_DEF_VALUE
#define WEED_LEAF_IS_DEF_VALUE
Definition: events.h:73
weed_filter_get_out_chantmpls
WEED_GLOBAL_INLINE weed_plant_t ** weed_filter_get_out_chantmpls(weed_plant_t *filter, int *ntmpls)
Definition: weed-effects-utils.c:251
ensure_extension
char * ensure_extension(const char *fname, const char *ext) WARN_UNUSED
Definition: utils.c:3232
donate_activate
void donate_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7068
UNREC_LAYOUTS_DIR
#define UNREC_LAYOUTS_DIR
Definition: mainwindow.h:587
mt_spin_start_value_changed
void mt_spin_start_value_changed(LiVESSpinButton *spinbutton, livespointer user_data)
Definition: multitrack.c:4452
PRIu64
#define PRIu64
Definition: machinestate.h:170
lives_clip_t::hsize
int hsize
frame width (horizontal) in pixels (NOT macropixels !)
Definition: main.h:896
mainwindow::is_rendering
boolean is_rendering
Definition: mainwindow.h:821
init_event_is_relevant
boolean init_event_is_relevant(weed_plant_t *init_event, int ctrack)
Definition: events.c:859
popup_lmap_errors
void popup_lmap_errors(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:9335
LIVES_MAIN_WINDOW_WIDGET
#define LIVES_MAIN_WINDOW_WIDGET
Definition: mainwindow.h:188
lives_menu_tool_button_set_menu
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_tool_button_set_menu(LiVESMenuToolButton *toolbutton, LiVESWidget *menu)
Definition: widget-helper.c:6510
event_list_rectify
boolean event_list_rectify(lives_mt *mt, weed_plant_t *event_list)
Definition: multitrack.c:20713
on_open_loc_activate
void on_open_loc_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:745
lives_text_view_set_text
boolean lives_text_view_set_text(LiVESTextView *textview, const char *text, int len)
Definition: widget-helper.c:11715
MOUSE_MODE_SELECT
@ MOUSE_MODE_SELECT
Definition: multitrack.h:71
FX_KEYS_MAX_VIRTUAL
#define FX_KEYS_MAX_VIRTUAL
must be >= FX_KEYS_PHYSICAL, and <=64 (number of bits in a 64bit int mask) (max number of keys accesi...
Definition: mainwindow.h:203
lives_window_set_hide_titlebar_when_maximized
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_set_hide_titlebar_when_maximized(LiVESWindow *window, boolean setting)
Definition: widget-helper.c:2833
lives_tooltips_copy
void lives_tooltips_copy(LiVESWidget *dest, LiVESWidget *source)
Definition: widget-helper.c:7488
enabled_out_channels
int enabled_out_channels(weed_plant_t *plant, boolean count_repeats)
Definition: effects-weed.c:4043
lives_clipinfo_t::textview_size
LiVESWidget * textview_size
Definition: interface.h:81
_prefs::max_disp_vtracks
int max_disp_vtracks
Definition: preferences.h:430
lives_widget_set_valign
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_valign(LiVESWidget *widget, LiVESAlign align)
Definition: widget-helper.c:3537
lives_clip_data_t::seek_flag
int seek_flag
plugin can change per frame
Definition: plugins.h:397
CLIP_LABEL_LENGTH
#define CLIP_LABEL_LENGTH
Definition: multitrack.h:17
LIVES_FX_CAT_VIDEO_TRANSITION
@ LIVES_FX_CAT_VIDEO_TRANSITION
Definition: effects.h:22
mainwindow::ascrap_file
int ascrap_file
scrap file for recording audio scraps
Definition: mainwindow.h:875
mainwindow::stored_event_list_changed
boolean stored_event_list_changed
Definition: mainwindow.h:805
lives_table_set_row_spacings
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_set_row_spacings(LiVESTable *table, uint32_t spacing)
Definition: widget-helper.c:6959
get_mixer_track_vol
double get_mixer_track_vol(lives_mt *mt, int trackno)
Definition: multitrack.c:240
get_transition_param
int get_transition_param(weed_plant_t *filter, boolean skip_internal)
Definition: effects-weed.c:8656
mt_zoom_in
void mt_zoom_in(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:4419
_entryw
Definition: interface.h:93
INSERT_MODE_OVERWRITE
@ INSERT_MODE_OVERWRITE
overwite existing blocks
Definition: multitrack.h:79
lives_clip_data_t
Definition: plugins.h:319
WEED_PLANT_IS_EVENT_LIST
#define WEED_PLANT_IS_EVENT_LIST(plant)
Definition: events.h:359
lives_memmove
#define lives_memmove
Definition: machinestate.h:64
lives_tool_button_set_icon_widget
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tool_button_set_icon_widget(LiVESToolButton *button, LiVESWidget *icon)
Definition: widget-helper.c:5201
track_select
void track_select(lives_mt *mt)
must call after setting mt->current_track
Definition: multitrack.c:1941
WEED_LEAF_AUDIO_ENDIAN
#define WEED_LEAF_AUDIO_ENDIAN
Definition: events.h:20
lives_bin_get_child
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_bin_get_child(LiVESBin *bin)
Definition: widget-helper.c:5514
_palette::black
LiVESWidgetColor black
Definition: mainwindow.h:307
lives_mgeometry_t::width
int width
Definition: mainwindow.h:355
lives_rgba_to_widget_color
WIDGET_HELPER_GLOBAL_INLINE LiVESWidgetColor * lives_rgba_to_widget_color(LiVESWidgetColor *color, lives_colRGBA64_t *lcolor)
Definition: widget-helper.c:12593
LIVES_CURSOR_AUDIO_BLOCK
@ LIVES_CURSOR_AUDIO_BLOCK
Definition: widget-helper.h:1303
mt_load_vals_toggled
void mt_load_vals_toggled(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22340
mt_quit_activate
void mt_quit_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:5996
lives_memcpy
#define lives_memcpy
Definition: machinestate.h:55
mainwindow::block_param_updates
boolean block_param_updates
block visual param changes from updating real values
Definition: mainwindow.h:1550
lives_widget_get_pointer
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_get_pointer(LiVESXDevice *device, LiVESWidget *widget, int *x, int *y)
Definition: widget-helper.c:7190
POLY_COMP
@ POLY_COMP
Definition: multitrack.h:129
set_child_alt_colour
WIDGET_HELPER_GLOBAL_INLINE void set_child_alt_colour(LiVESWidget *widget, boolean set_all)
Definition: widget-helper.c:11656
_resaudw::rb_bigend
LiVESWidget * rb_bigend
Definition: resample.h:25
lives_accel_group_connect
WIDGET_HELPER_GLOBAL_INLINE boolean lives_accel_group_connect(LiVESAccelGroup *group, uint32_t key, LiVESXModifierType mod, LiVESAccelFlags flags, LiVESWidgetClosure *closure)
Definition: widget-helper.c:2927
future_prefs
_future_prefs * future_prefs
Definition: preferences.h:848
lives_param_t::hidden
uint32_t hidden
Definition: plugins.h:546
event_list_add_track
void event_list_add_track(weed_plant_t *event_list, int layer)
Definition: events.c:2497
lives_widget_set_sensitive
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_sensitive(LiVESWidget *widget, boolean state)
Definition: widget-helper.c:1477
lives_popen
ssize_t lives_popen(const char *com, boolean allow_error, char *buff, ssize_t buflen)
Definition: utils.c:194
weed_filter_idx_get_package_name
char * weed_filter_idx_get_package_name(int idx)
Definition: effects-weed.c:9510
lives_strappend
LIVES_GLOBAL_INLINE int lives_strappend(const char *string, int len, const char *xnew)
Definition: machinestate.c:1436
PREF_MT_SHOW_CTX
#define PREF_MT_SHOW_CTX
Definition: preferences.h:1018
lives_accel_group_new
WIDGET_HELPER_GLOBAL_INLINE LiVESAccelGroup * lives_accel_group_new(void)
Definition: widget-helper.c:2915
free_thumb_cache
void free_thumb_cache(int fnum, frames_t fromframe)
Definition: multitrack.c:1148
lives_widget_show_all_from_bg
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show_all_from_bg(LiVESWidget *widget)
Definition: widget-helper.c:1535
clear_context
void clear_context(lives_mt *mt)
Definition: multitrack.c:11693
PREF_MT_PERTRACK_AUDIO
#define PREF_MT_PERTRACK_AUDIO
Definition: preferences.h:1036
weed_filter_get_package_name
WEED_GLOBAL_INLINE char * weed_filter_get_package_name(weed_plant_t *filter)
Definition: weed-effects-utils.c:154
on_track_click
boolean on_track_click(LiVESWidget *eventbox, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13969
LIVES_FILENAME_NOREMOVE
#define LIVES_FILENAME_NOREMOVE
Definition: mainwindow.h:577
insert_filter_deinit_event_at
void insert_filter_deinit_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event)
Definition: events.c:1043
make_weed_hashname
char * make_weed_hashname(int filter_idx, boolean fullname, boolean use_extra_authors, char sep, boolean subs)
return value should be freed after use
Definition: effects-weed.c:10730
load_layout_map
LiVESList * load_layout_map(void)
Definition: multitrack.c:19584
lives_clip_t::raudio_time
double raudio_time
Definition: main.h:929
amixer_show
void amixer_show(LiVESButton *button, livespointer user_data)
Definition: multitrack.c:22757
NB_ERROR_NOCLIP
@ NB_ERROR_NOCLIP
Definition: multitrack.h:118
in_out_start_changed
void in_out_start_changed(LiVESWidget *widget, livespointer user_data)
Definition: multitrack.c:11897
create_render_details
render_details * create_render_details(int type)
Definition: events.c:6252
get_track_name
char * get_track_name(lives_mt *mt, int track_num, boolean is_audio)
Definition: multitrack.c:1038
multitrack_view_in_out
void multitrack_view_in_out(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:2850
lives_getuid
LIVES_GLOBAL_INLINE int lives_getuid(void)
Definition: machinestate.c:2416
init_event_in_list
LIVES_GLOBAL_INLINE boolean init_event_in_list(void **init_events, int num_inits, weed_plant_t *event)
Definition: events.c:1550
lives_param_t::name
char * name
Definition: plugins.h:540
lives_spin_button_set_digits
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_set_digits(LiVESSpinButton *button, uint32_t digits)
Definition: widget-helper.c:5156
on_track_release
boolean on_track_release(LiVESWidget *eventbox, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13773
multitrack_playall
void multitrack_playall(lives_mt *mt)
Definition: multitrack.c:17345
mainwindow::multitrack
lives_mt * multitrack
holds a pointer to the entire multitrack environment; NULL in Clip Edit mode
Definition: mainwindow.h:1087
mainwindow::def_trans_idx
int def_trans_idx
Definition: mainwindow.h:1747
lives_box_reorder_child
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_reorder_child(LiVESBox *box, LiVESWidget *child, int pos)
Definition: widget-helper.c:3225
lives_widget_apply_theme
void lives_widget_apply_theme(LiVESWidget *widget, LiVESWidgetState state)
Definition: widget-helper.c:11156
CURRENT_CLIP_IS_VALID
#define CURRENT_CLIP_IS_VALID
Definition: main.h:809
lives_spin_button_update
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_update(LiVESSpinButton *button)
Definition: widget-helper.c:5165
get_next_frame_event
weed_plant_t * get_next_frame_event(weed_plant_t *event)
Definition: events.c:356
FX_ORD_AFTER
@ FX_ORD_AFTER
Definition: multitrack.h:141
is_virtual_frame
boolean is_virtual_frame(int sfileno, frames_t frame)
Definition: cvirtual.c:1063
DEF_BUTTON_HEIGHT
#define DEF_BUTTON_HEIGHT
Definition: mainwindow.h:183
lives_vol_from_linear
#define lives_vol_from_linear(vol)
Definition: audio.h:269
multitrack_end_cb
void multitrack_end_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14237
mainwindow::was_set
boolean was_set
Definition: mainwindow.h:750
mt_change_max_disp_tracks
void mt_change_max_disp_tracks(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22330
_prefs::ar_layout_name
char ar_layout_name[PATH_MAX]
locale
Definition: preferences.h:286
mainwindow::go_away
boolean go_away
Definition: mainwindow.h:1614
mt_zoom_out
void mt_zoom_out(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:4429
on_next_fm_clicked
void on_next_fm_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:18950
lives_entry_set_width_chars
WIDGET_HELPER_GLOBAL_INLINE boolean lives_entry_set_width_chars(LiVESEntry *entry, int nchars)
Definition: widget-helper.c:6223
lives_clip_t::laudio_time
double laudio_time
Definition: main.h:929
avel_spin_changed
void avel_spin_changed(LiVESSpinButton *spinbutton, livespointer user_data)
Definition: multitrack.c:12548
multitrack_audio_insert
boolean multitrack_audio_insert(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:17632
lives_widget_queue_resize
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_queue_resize(LiVESWidget *widget)
Definition: widget-helper.c:1605
weed_event_t
weed_plant_t weed_event_t
Definition: events.h:97
lives_window_remove_accel_group
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_remove_accel_group(LiVESWindow *window, LiVESAccelGroup *group)
Definition: widget-helper.c:3007
lives_standard_notebook_new
LiVESWidget * lives_standard_notebook_new(const LiVESWidgetColor *bg_color, const LiVESWidgetColor *act_color)
Definition: widget-helper.c:8579
lives_image_new_from_stock
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_image_new_from_stock(const char *stock_id, LiVESIconSize size)
Definition: widget-helper.c:2412
lives_widget_set_can_focus
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_can_focus(LiVESWidget *widget, boolean state)
Definition: widget-helper.c:4789
LIVES_DEVICE_INTERNAL
@ LIVES_DEVICE_INTERNAL
Definition: mainwindow.h:263
capability::has_mplayer2
lives_checkstatus_t has_mplayer2
Definition: main.h:512
get_audio_and_effects_state_at
lives_audio_track_state_t * get_audio_and_effects_state_at(weed_plant_t *event_list, weed_plant_t *st_event, weed_timecode_t fill_tc, int what_to_get, boolean exact)
get audio (and optionally video) state at timecode tc OR before event st_event
Definition: audio.c:2419
do_write_failed_error_s_with_retry
LiVESResponseType do_write_failed_error_s_with_retry(const char *fname, const char *errtext)
Definition: dialogs.c:4058
on_clear_event_list_activate
void on_clear_event_list_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:21971
mainwindow::last_dprint_file
int last_dprint_file
message output settings
Definition: mainwindow.h:1535
mt_clip_select
void mt_clip_select(lives_mt *mt, boolean scroll)
Definition: multitrack.c:3024
render_to_clip
boolean render_to_clip(boolean new_clip, boolean transcode)
rendering
Definition: events.c:4635
lives_read_le_buffered
ssize_t lives_read_le_buffered(int fd, void *buf, ssize_t count, boolean allow_less)
Definition: utils.c:1158
lives_standard_menu_item_new_with_label
LiVESWidget * lives_standard_menu_item_new_with_label(const char *label)
Definition: widget-helper.c:8474
mainwindow::msg_area
LiVESWidget * msg_area
Definition: mainwindow.h:1324
do_mt_backup_space_error
LIVES_GLOBAL_INLINE void do_mt_backup_space_error(lives_mt *mt, int memreq_mb)
Definition: dialogs.c:3560
show_manual_activate
void show_manual_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7043
on_playall_activate
void on_playall_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:4530
rte_window
LiVESWidget * rte_window
Definition: rte_window.h:58
mainwindow::current_layouts_map
LiVESList * current_layouts_map
map of all layouts for set
Definition: mainwindow.h:1470
multitrack_insert
boolean multitrack_insert(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:17480
MT_LAST_FX_BLOCK
@ MT_LAST_FX_BLOCK
Definition: multitrack.h:134
on_mouse_scroll
boolean on_mouse_scroll(LiVESWidget *widget, LiVESXEventScroll *event, livespointer user_data)
Definition: callbacks.c:10602
main.h
lives_frame_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_frame_new(const char *label)
Definition: widget-helper.c:6831
LIVES_CURSOR_BUSY
@ LIVES_CURSOR_BUSY
Definition: widget-helper.h:1293
PEB_HRATIO
#define PEB_HRATIO
preview eventbox height ratio (fraction of screen height)
Definition: multitrack.h:42
lives_toolbar_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_toolbar_new(void)
Definition: widget-helper.c:5372
lives_tcache_entry_t::frame
frames_t frame
list of entries in clip thumbnail cache (for multitrack timeline)
Definition: main.h:1106
WEED_LEAF_HOST_MENU_HIDE
#define WEED_LEAF_HOST_MENU_HIDE
Definition: effects-weed.h:61
lives_clip_t::tcache
LiVESList * tcache
set by clip alterations, frames from here onwards should be freed
Definition: main.h:1100
multitrack
lives_mt * multitrack(weed_plant_t *event_list, int orig_file, double fps)
create and return lives_mt struct
Definition: multitrack.c:6448
lives_colRGBA64_t
Definition: main.h:322
_prefs::mt_exit_render
boolean mt_exit_render
Definition: preferences.h:275
msg_area_scroll
void msg_area_scroll(LiVESAdjustment *adj, livespointer userdata)
Definition: interface.c:7284
render_details::height
int height
Definition: events.h:217
lives_get_scroll_direction
WIDGET_HELPER_GLOBAL_INLINE LiVESScrollDirection lives_get_scroll_direction(LiVESXEventScroll *event)
Definition: widget-helper.c:8785
LIVES_EXPAND_DEFAULT
#define LIVES_EXPAND_DEFAULT
Definition: widget-helper.h:1314
free_track_decoders
LIVES_GLOBAL_INLINE void free_track_decoders(void)
Definition: main.c:7826
edit_start_end_cb
void edit_start_end_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14259
lives_layer_new_for_frame
LIVES_GLOBAL_INLINE weed_layer_t * lives_layer_new_for_frame(int clip, frames_t frame)
Definition: colourspace.c:9833
get_audio_frame_vel
double get_audio_frame_vel(weed_plant_t *event, int track)
returns velocity for track (track==-1 is backing audio)
Definition: events.c:165
CLIP_DETAILS_UNIQUE_ID
@ CLIP_DETAILS_UNIQUE_ID
Definition: main.h:1147
MAX_FILES
#define MAX_FILES
max files is actually 1 more than this, since file 0 is the clipboard
Definition: main.h:184
add_track_to_avol_init
void add_track_to_avol_init(weed_plant_t *filter, weed_plant_t *event, int nbtracks, boolean behind)
Definition: events.c:2435
on_seltrack_toggled
void on_seltrack_toggled(LiVESWidget *checkbutton, livespointer user_data)
Definition: multitrack.c:16962
lives_painter_set_line_width
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_line_width(lives_painter_t *cr, double width)
Definition: widget-helper.c:507
find_block_by_uid
track_rect * find_block_by_uid(lives_mt *mt, ulong uid)
Definition: multitrack.c:23012
lives_container_remove
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_remove(LiVESContainer *container, LiVESWidget *widget)
Definition: widget-helper.c:4938
lives_widget_show_now
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show_now(LiVESWidget *widget)
Definition: widget-helper.c:1544
lives_param_t::changed
boolean changed
Definition: plugins.h:597
mainwindow::fs
boolean fs
Definition: mainwindow.h:762
on_prev_node_clicked
void on_prev_node_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19221
MAX_SET_NAME_LEN
#define MAX_SET_NAME_LEN
sets
Definition: mainwindow.h:748
lives_toolbar_insert
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toolbar_insert(LiVESToolbar *toolbar, LiVESToolItem *item, int pos)
Definition: widget-helper.c:5381
insert_filter_init_event_at
void insert_filter_init_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event)
Definition: events.c:1021
stored_event_list_free_all
void stored_event_list_free_all(boolean wiped)
Definition: multitrack.c:5897
resample.h
_prefs::sleep_time
int sleep_time
Definition: preferences.h:176
AUDIO_SRC_EXT
#define AUDIO_SRC_EXT
Definition: preferences.h:206
DEF_TIME
#define DEF_TIME
default seconds when there is no event_list
Definition: multitrack.h:57
SELBLOCK_ALPHA
#define SELBLOCK_ALPHA
Definition: multitrack.h:55
on_prerender_aud_activate
void on_prerender_aud_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16358
MT_UNDO_DELETE_AUDIO_BLOCK
@ MT_UNDO_DELETE_AUDIO_BLOCK
Definition: multitrack.h:108
get_poly_state_from_page
LIVES_INLINE lives_mt_poly_state_t get_poly_state_from_page(lives_mt *mt)
Definition: multitrack.c:3780
delete_audio_tracks
void delete_audio_tracks(lives_mt *mt, LiVESList *list, boolean full)
Definition: multitrack.c:5986
lives_painter_clip
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_clip(lives_painter_t *cr)
Definition: widget-helper.c:397
_prefs::vj_mode
boolean vj_mode
Definition: preferences.h:459
get_frame_event_at
weed_plant_t * get_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc, weed_plant_t *shortcut, boolean exact)
Definition: events.c:780
mainwindow::stored_event_list_auto_changed
boolean stored_event_list_auto_changed
Definition: mainwindow.h:806
do_mt_lb_warn
boolean do_mt_lb_warn(boolean lb)
Definition: dialogs.c:3451
mainwindow::effort
int effort
Definition: mainwindow.h:1773
set_values_from_defs
char * set_values_from_defs(lives_mt *mt, boolean from_prefs)
Definition: multitrack.c:5768
set_signal_handlers
void set_signal_handlers(SignalHandlerPointer sigfunc)
Definition: main.c:4077
append_filter_map_event
weed_plant_t * append_filter_map_event(weed_plant_t *event_list, weed_timecode_t tc, void **init_events)
Definition: events.c:2968
lives_adjustment_set_page_size
WIDGET_HELPER_GLOBAL_INLINE boolean lives_adjustment_set_page_size(LiVESAdjustment *adj, double page_size)
Definition: widget-helper.c:5610
lives_widget_create_painter_surface
WIDGET_HELPER_GLOBAL_INLINE lives_painter_surface_t * lives_widget_create_painter_surface(LiVESWidget *widget)
Definition: widget-helper.c:700
do_mt_undo_buf_error
LIVES_GLOBAL_INLINE void do_mt_undo_buf_error(void)
Definition: dialogs.c:3587
mainw
mainwindow * mainw
Definition: main.c:103
on_jumpback_mark_activate
void on_jumpback_mark_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16025
get_image_ext_for_type
const char * get_image_ext_for_type(lives_img_type_t imgtype)
Definition: utils.c:3025
WEED_LEAF_OUT_TRACKS
#define WEED_LEAF_OUT_TRACKS
Definition: events.h:48
lives_widget_set_halign
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_halign(LiVESWidget *widget, LiVESAlign align)
Definition: widget-helper.c:3504
MT_UNDO_INSERT_AUDIO_BLOCK
@ MT_UNDO_INSERT_AUDIO_BLOCK
Definition: multitrack.h:95
lives_widget_set_margin_left
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_margin_left(LiVESWidget *widget, int margin)
Definition: widget-helper.c:2516
do_bad_layout_error
LIVES_GLOBAL_INLINE void do_bad_layout_error(void)
Definition: dialogs.c:3635
framedraw.h
LIVES_RFX_SOURCE_WEED
@ LIVES_RFX_SOURCE_WEED
Definition: plugins.h:514
lives_standard_hscale_new
LiVESWidget * lives_standard_hscale_new(LiVESAdjustment *adj)
Definition: widget-helper.c:10192
LIVES_STRING_CONSTANT_NONE
@ LIVES_STRING_CONSTANT_NONE
Definition: mainwindow.h:371
do_sel_context
void do_sel_context(lives_mt *mt)
Definition: multitrack.c:18583
lives_standard_drawing_area_new
LiVESWidget * lives_standard_drawing_area_new(LiVESGuiCallback callback, lives_painter_surface_t **ppsurf)
Definition: widget-helper.c:8693
lives_toolbar_insert_space
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_toolbar_insert_space(LiVESToolbar *bar)
Definition: widget-helper.c:12434
remove_filter_from_event_list
void remove_filter_from_event_list(weed_plant_t *event_list, weed_plant_t *init_event)
Definition: events.c:1480
lives_cool_toggled
void lives_cool_toggled(LiVESWidget *tbutton, livespointer user_data)
Definition: widget-helper.c:12094
weed_instance_unref
LIVES_GLOBAL_INLINE int weed_instance_unref(weed_plant_t *inst)
Definition: effects-weed.c:6234
mainwindow::affected_layouts_map
LiVESList * affected_layouts_map
map of layouts with errors
Definition: mainwindow.h:1469
N_RECENT_FILES
#define N_RECENT_FILES
Definition: main.h:657
mainwindow::jack_can_stop
boolean jack_can_stop
Definition: mainwindow.h:934
lives_widget_set_events
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_events(LiVESWidget *widget, int events)
Definition: widget-helper.c:4830
update_insert_mode
void update_insert_mode(lives_mt *mt)
Definition: multitrack.c:4946
lives_widget_is_visible
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_is_visible(LiVESWidget *widget)
Definition: widget-helper.c:4901
make_thumb_fast_between
LiVESPixbuf * make_thumb_fast_between(lives_mt *mt, int fileno, int width, int height, int tframe, int range)
Definition: multitrack.c:489
on_fx_insb_clicked
void on_fx_insb_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:18920
in_anchor_toggled
void in_anchor_toggled(LiVESToggleButton *togglebutton, livespointer user_data)
Definition: multitrack.c:12683
mainwindow::proc_ptr
xprocess * proc_ptr
Definition: mainwindow.h:1090
convert_layer_palette_full
boolean convert_layer_palette_full(weed_layer_t *layer, int outpl, int oclamping, int osampling, int osubspace, int tgamma)
convert the palette of a layer
Definition: colourspace.c:10160
multitrack_delete
boolean multitrack_delete(lives_mt *mt, boolean save_layout)
Definition: multitrack.c:9204
MAX_AUDIO_TRACKS
#define MAX_AUDIO_TRACKS
Definition: multitrack.h:1046
mainwindow::btoolbar
LiVESWidget * btoolbar
button toolbar - clip editor
Definition: mainwindow.h:1368
lives_standard_scrolled_window_new
LiVESWidget * lives_standard_scrolled_window_new(int width, int height, LiVESWidget *child)
Definition: widget-helper.c:10272
mainwindow::play_surface
lives_painter_surface_t * play_surface
Definition: mainwindow.h:950
WEED_LEAF_TRACK_LABEL_TRACKS
#define WEED_LEAF_TRACK_LABEL_TRACKS
Definition: events.h:24
PREF_MT_DEF_WIDTH
#define PREF_MT_DEF_WIDTH
Definition: preferences.h:1007
insert_frame_event_at
weed_plant_t * insert_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc, int numframes, int *clips, int64_t *frames, weed_plant_t **shortcut)
Definition: events.c:1144
lives_tcache_entry_t
Definition: main.h:1104
mainwindow::top_vbox
LiVESWidget * top_vbox
Definition: mainwindow.h:1352
lives_button_box_set_layout
WIDGET_HELPER_GLOBAL_INLINE boolean lives_button_box_set_layout(LiVESButtonBox *bbox, LiVESButtonBoxStyle bstyle)
Definition: widget-helper.c:3355
filter_init_add_pchanges
void ** filter_init_add_pchanges(weed_plant_t *event_list, weed_plant_t *plant, weed_plant_t *init_event, int ntracks, int leave)
Definition: events.c:2648
frames_t
int frames_t
Definition: main.h:99
show_clipinfo_cb
void show_clipinfo_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14273
resize_layer
boolean resize_layer(weed_layer_t *layer, int width, int height, LiVESInterpType interp, int opal_hint, int oclamp_hint)
resize a layer
Definition: colourspace.c:12537
_prefs::ar_layout
boolean ar_layout
Definition: preferences.h:284
lives_entry_set_text
WIDGET_HELPER_GLOBAL_INLINE boolean lives_entry_set_text(LiVESEntry *entry, const char *text)
Definition: widget-helper.c:6211
mainwindow::loop_continue
LiVESWidget * loop_continue
Definition: mainwindow.h:1174
get_init_events_before
void ** get_init_events_before(weed_plant_t *event, weed_plant_t *init_event, boolean add)
Definition: events.c:936
MT_INOUT_TIME
#define MT_INOUT_TIME
min milliseconds to save autobackup when changing in / out spins
Definition: multitrack.h:49
unlink_event
void unlink_event(weed_plant_t *event_list, weed_plant_t *event)
Definition: events.c:297
lives_clip_t::pb_fps
double pb_fps
current playback rate, may vary from fps, can be 0. or negative
Definition: main.h:1007
_prefs::mt_show_ctx
boolean mt_show_ctx
Definition: preferences.h:277
WEED_LEAF_HOST_AUDIO_TRANSITION
#define WEED_LEAF_HOST_AUDIO_TRANSITION
Definition: events.h:86
get_pkg_name
LIVES_LOCAL_INLINE char * get_pkg_name(int pkgnum)
Definition: multitrack.c:4038
lives_container_add
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_add(LiVESContainer *container, LiVESWidget *widget)
Definition: widget-helper.c:4929
POLY_WIDTH_MARGIN
#define POLY_WIDTH_MARGIN
Definition: multitrack.c:12775
on_framedraw_mouse_start
boolean on_framedraw_mouse_start(LiVESWidget *widget, LiVESXEventButton *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:871
PREF_MT_DEF_ASAMPS
#define PREF_MT_DEF_ASAMPS
Definition: preferences.h:1011
LIVES_FILE_EXT_LAYOUT
#define LIVES_FILE_EXT_LAYOUT
Definition: mainwindow.h:513
lives_widget_set_opacity
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_opacity(LiVESWidget *widget, double opacity)
Definition: widget-helper.c:1757
on_next_node_clicked
void on_next_node_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19207
lives_write_buffered
ssize_t lives_write_buffered(int fd, const char *buf, ssize_t count, boolean allow_fail)
Definition: utils.c:1226
LIVES_FX_CAT_COMPOSITOR
@ LIVES_FX_CAT_COMPOSITOR
Definition: effects.h:28
lives_notebook_get_nth_page
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_notebook_get_nth_page(LiVESNotebook *nbook, int pagenum)
Definition: widget-helper.c:6894
POLY_FX_STACK
@ POLY_FX_STACK
Definition: multitrack.h:125
_prefs::startup_interface
int startup_interface
Definition: preferences.h:336
widget_opts_t::icon_size
int icon_size
icon size for tooltips image, warn image, toolbar img, etc.
Definition: widget-helper.h:1429
_resaudw::entry_achans
LiVESWidget * entry_achans
Definition: resample.h:21
lives_system
int lives_system(const char *com, boolean allow_error)
Definition: utils.c:145
delete_block_cb
void delete_block_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14299
lives_scrolled_window_get_hadjustment
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_scrolled_window_get_hadjustment(LiVESScrolledWindow *swindow)
Definition: widget-helper.c:6245
_prefs::mt_auto_back
int mt_auto_back
time diff to backup (-1 == never, 0 == after every change, > 0 == seconds)
Definition: preferences.h:281
lives_mgeometry_t::mouse_device
LiVESXDevice * mouse_device
unused for gtk+ < 3.0.0
Definition: mainwindow.h:357
lives_widget_show_all
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show_all(LiVESWidget *widget)
Definition: widget-helper.c:1523
on_del_node_clicked
void on_del_node_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19235
MT_UNDO_SPLIT_MULTI
@ MT_UNDO_SPLIT_MULTI
Definition: multitrack.h:101
add_audio_track
LiVESWidget * add_audio_track(lives_mt *mt, int track, boolean behind)
Definition: multitrack.c:10166
do_fx_move_context
void do_fx_move_context(lives_mt *mt)
Definition: multitrack.c:18615
weed_layer_free
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_free(weed_layer_t *layer)
frees pixel_data for a layer, then the layer itself
Definition: colourspace.c:13883
_prefs::interactive
boolean interactive
Definition: preferences.h:478
on_track_between_release
boolean on_track_between_release(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13958
on_seltrack_activate
void on_seltrack_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16871
lives_standard_image_menu_item_new_with_label
LiVESWidget * lives_standard_image_menu_item_new_with_label(const char *label)
Definition: widget-helper.c:8492
_prefs::mt_enter_prompt
boolean mt_enter_prompt
Definition: preferences.h:268
MT_UNDO_DELETE_BLOCK
@ MT_UNDO_DELETE_BLOCK
Definition: multitrack.h:105
lives_glowing_check_button_new
LiVESWidget * lives_glowing_check_button_new(const char *labeltext, LiVESBox *box, const char *tooltip, boolean *togglevalue)
Definition: widget-helper.c:9163
has_video_chans_in
boolean has_video_chans_in(weed_plant_t *filter, boolean count_opt)
Definition: effects-weed.c:620
get_border_size
boolean get_border_size(LiVESWidget *win, int *bx, int *by)
Definition: widget-helper.c:12255
weed_param_is_hidden
WEED_GLOBAL_INLINE int weed_param_is_hidden(weed_plant_t *param, int temporary)
Definition: weed-effects-utils.c:170
lives_container_foreach
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_foreach(LiVESContainer *cont, LiVESWidgetCallback callback, livespointer cb_data)
Definition: widget-helper.c:4957
lives_painter_stroke
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_stroke(lives_painter_t *cr)
Definition: widget-helper.c:382
mt_aparam_view_toggled
void mt_aparam_view_toggled(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:5289
gen_unique_id
uint64_t gen_unique_id(void)
Definition: machinestate.c:68
weed_layer_get_width_pixels
LIVES_GLOBAL_INLINE int weed_layer_get_width_pixels(weed_layer_t *layer)
Definition: colourspace.c:13947
rdet
render_details * rdet
Definition: events.h:256
mainwindow::fx_candidates
lives_fx_candidate_t fx_candidates[MAX_FX_CANDIDATE_TYPES]
< effects which can have candidates from which a delegate is selected (current examples are: audio_vo...
Definition: mainwindow.h:1514
lives_mt_nb_error_t
lives_mt_nb_error_t
Definition: multitrack.h:113
MT_UNDO_SPLIT
@ MT_UNDO_SPLIT
Definition: multitrack.h:100
on_show_messages_activate
void on_show_messages_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:6771
lives_funcptr_t
void *(* lives_funcptr_t)(void *)
Definition: machinestate.h:378
_prefs::pb_quality
short pb_quality
Definition: preferences.h:31
do_yesno_dialog
boolean do_yesno_dialog(const char *text)
Definition: dialogs.c:655
lives_toggle_button_set_active
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_set_active(LiVESToggleButton *button, boolean active)
Definition: widget-helper.c:4483
lives_clipinfo_t::textview_fps
LiVESWidget * textview_fps
Definition: interface.h:80
widget_opts
widget_opts_t widget_opts
Definition: widget-helper.h:1442
interpolate_params
boolean interpolate_params(weed_plant_t *inst, void **pchains, weed_timecode_t tc)
interpolate all in_parameters for filter_instance inst, using void **pchain, which is an array of par...
Definition: effects-weed.c:10684
WEED_EVENT_IS_FILTER_MAP
#define WEED_EVENT_IS_FILTER_MAP(event)
Definition: events.h:366
lives_tool_button_set_use_underline
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tool_button_set_use_underline(LiVESToolButton *button, boolean use_underline)
Definition: widget-helper.c:5235
lives_entry_get_text
WIDGET_HELPER_GLOBAL_INLINE const char * lives_entry_get_text(LiVESEntry *entry)
Definition: widget-helper.c:6203
BLOCK_DRAW_TYPE
#define BLOCK_DRAW_TYPE
Definition: multitrack.h:53
STARTUP_CE
#define STARTUP_CE
Definition: preferences.h:338
mainwindow::gens_menu
LiVESWidget * gens_menu
Definition: mainwindow.h:1413
render_details::always_checkbutton
LiVESWidget * always_checkbutton
Definition: events.h:233
get_prev_frame_event
weed_plant_t * get_prev_frame_event(weed_plant_t *event)
Definition: events.c:368
lives_clip_t::vsize
int vsize
frame height (vertical) in pixels
Definition: main.h:897
init_event_is_process_last
boolean init_event_is_process_last(weed_plant_t *event)
Definition: events.c:1718
resize
void resize(double scale)
Definition: main.c:10230
LAYOUTS_DIRNAME
#define LAYOUTS_DIRNAME
Definition: mainwindow.h:619
PATH_MAX
#define PATH_MAX
Definition: main.h:255
widget_opts_t::expand
lives_expand_t expand
how much space to apply between widgets
Definition: widget-helper.h:1408
restore_host_tags
void restore_host_tags(weed_plant_t *event_list, weed_timecode_t curr_tc)
Definition: events.c:1605
WEED_LEAF_AUDIO_CLIPS
#define WEED_LEAF_AUDIO_CLIPS
Definition: events.h:40
get_eventbox_for_track
LiVESWidget * get_eventbox_for_track(lives_mt *mt, int ntrack)
Definition: multitrack.c:22981
LIVES_DIR_SEP
#define LIVES_DIR_SEP
Definition: main.h:197
mt_undo::tc
ticks_t tc
Definition: multitrack.h:693
get_next_event
LIVES_GLOBAL_INLINE weed_plant_t * get_next_event(weed_plant_t *event)
Definition: events.c:114
mainwindow::clipstore
int clipstore[FN_KEYS - 1][2]
stored clips (bookmarks) [0] = clip, [1] = frame
Definition: mainwindow.h:986
lives_clip_t::raudio_drawable
lives_painter_surface_t * raudio_drawable
Definition: main.h:1084
weed_event_get_timecode
LIVES_GLOBAL_INLINE weed_timecode_t weed_event_get_timecode(weed_event_t *event)
Definition: events.c:89
mt_sensitise
void mt_sensitise(lives_mt *mt)
Definition: multitrack.c:17052
special_cleanup
boolean special_cleanup(boolean is_ok)
Definition: paramspecial.c:641
lives_hseparator_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hseparator_new(void)
Definition: widget-helper.c:3301
WEED_EVENT_IS_MARKER
#define WEED_EVENT_IS_MARKER(event)
Definition: events.h:368
lives_pixbuf_get_height
WIDGET_HELPER_GLOBAL_INLINE int lives_pixbuf_get_height(const LiVESPixbuf *pixbuf)
Definition: widget-helper.c:3118
get_menu_name
char * get_menu_name(lives_clip_t *sfile, boolean add_setname)
Definition: gui.c:4487
lives_standard_menu_item_new
LiVESWidget * lives_standard_menu_item_new(void)
Definition: widget-helper.c:8456
do_read_failed_error_s
void do_read_failed_error_s(const char *s, const char *addinfo)
Definition: dialogs.c:4034
on_track_between_click
boolean on_track_between_click(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13947
lives_range_set_value
WIDGET_HELPER_GLOBAL_INLINE boolean lives_range_set_value(LiVESRange *range, double value)
Definition: widget-helper.c:5663
check_encoder_restrictions
boolean check_encoder_restrictions(boolean get_extension, boolean user_audio, boolean save_all)
Definition: plugins.c:1557
lives_signal_handler_unblock
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handler_unblock(livespointer instance, unsigned long handler_id)
Definition: widget-helper.c:947
lives_clip_t
corresponds to one clip in the GUI
Definition: main.h:877
lives_scrolled_window_set_policy
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scrolled_window_set_policy(LiVESScrolledWindow *scrolledwindow, LiVESPolicyType hpolicy, LiVESPolicyType vpolicy)
Definition: widget-helper.c:6263
MT_LAST_FX_NONE
@ MT_LAST_FX_NONE
Definition: multitrack.h:133
on_track_header_release
boolean on_track_header_release(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13936
lives_colRGBA64_t::red
uint16_t red
Definition: main.h:323
mainwindow::is_processing
boolean is_processing
states
Definition: mainwindow.h:820
defer_sigint
void defer_sigint(int signum)
Definition: main.c:282
on_framedraw_enter
boolean on_framedraw_enter(LiVESWidget *widget, LiVESXEventCrossing *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:819
compare_filter_maps
boolean compare_filter_maps(weed_plant_t *fm1, weed_plant_t *fm2, int ctrack)
ctrack can be -1 to compare all events, else we cf for ctrack
Definition: multitrack.c:20384
WEED_GAMMA_MONITOR
#define WEED_GAMMA_MONITOR
Definition: colourspace.h:253
PREF_AR_LAYOUT
#define PREF_AR_LAYOUT
Definition: preferences.h:933
lives_menu_shell_prepend
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_shell_prepend(LiVESMenuShell *menushell, LiVESWidget *child)
Definition: widget-helper.c:6629
layout_audio_is_affected
LiVESList * layout_audio_is_affected(int clipno, double stime, double etime, LiVESList *xlays)
Definition: multitrack.c:22281
lives_clipinfo_t::textview_fsize
LiVESWidget * textview_fsize
Definition: interface.h:84
report_bug_activate
void report_bug_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7053
mainwindow::no_switch_dprint
boolean no_switch_dprint
Definition: mainwindow.h:1536
lives_painter_destroy
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_destroy(lives_painter_t *cr)
Definition: widget-helper.c:412
do_mt_rect_prompt
LIVES_GLOBAL_INLINE boolean do_mt_rect_prompt(void)
Definition: dialogs.c:3627
resize_timeline
boolean resize_timeline(lives_mt *mt)
Definition: multitrack.c:11721
lives_rfx_t::source
void * source
points to the source (e.g. a weed_plant_t)
Definition: plugins.h:651
lives_hpaned_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hpaned_new(void)
Definition: widget-helper.c:3399
mainwindow::mute
boolean mute
Definition: mainwindow.h:770
lives_timer_add
WIDGET_HELPER_GLOBAL_INLINE uint32_t lives_timer_add(uint32_t interval, LiVESWidgetSourceFunc function, livespointer data)
Definition: widget-helper.c:7318
GUI_SCREEN_WIDTH
#define GUI_SCREEN_WIDTH
Definition: mainwindow.h:99
lives_widget_process_updates
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_process_updates(LiVESWidget *widget)
Definition: widget-helper.c:1658
lives_clip_t::stored_layout_audio
double stored_layout_audio
Definition: main.h:1073
mainwindow::fx1_val
double fx1_val
Definition: mainwindow.h:1049
multitrack_view_details
void multitrack_view_details(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15489
amixer_add_channel_slider
LiVESWidget * amixer_add_channel_slider(lives_mt *mt, int i)
Definition: multitrack.c:22694
add_blank_frames_up_to
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)
Definition: multitrack.c:15650
handle_backend_errors
LiVESResponseType handle_backend_errors(boolean can_retry)
Definition: dialogs.c:922
MT_UNDO_INSERT_BLOCK
@ MT_UNDO_INSERT_BLOCK
Definition: multitrack.h:94
on_load_set_activate
char * on_load_set_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:5590
save_event_list_inner
boolean save_event_list_inner(lives_mt *mt, int fd, weed_plant_t *event_list, unsigned char **mem)
Definition: multitrack.c:252
lives_window_present
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_present(LiVESWindow *window)
Definition: widget-helper.c:2858
lives_display_get_window_at_pointer
WIDGET_HELPER_GLOBAL_INLINE LiVESXWindow * lives_display_get_window_at_pointer(LiVESXDevice *device, LiVESXDisplay *display, int *win_x, int *win_y)
Definition: widget-helper.c:7239
lives_clip_t::stored_layout_frame
frames_t stored_layout_frame
experimental for player
Definition: main.h:1071
audio.h
unpaint_lines
void unpaint_lines(lives_mt *mt)
Definition: multitrack.c:14150
event_list_get_end_secs
double event_list_get_end_secs(weed_plant_t *event_list)
Definition: events.c:4607
do_block_context
void do_block_context(lives_mt *mt, LiVESXEventButton *event, track_rect *block)
Definition: multitrack.c:13619
lives_menu_item_get_text
WIDGET_HELPER_GLOBAL_INLINE const char * lives_menu_item_get_text(LiVESWidget *menuitem)
Definition: widget-helper.c:11931
FPS_MAX
#define FPS_MAX
maximum fps we will allow (double)
Definition: main.h:218
lives_standard_button_new_full
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_standard_button_new_full(const char *label, int width, int height, LiVESBox *box, boolean fake_default, const char *ttips)
Definition: widget-helper.c:8366
LIVES_CURSOR_VIDEO_BLOCK
@ LIVES_CURSOR_VIDEO_BLOCK
Definition: widget-helper.h:1304
gamma_convert_layer
LIVES_GLOBAL_INLINE boolean gamma_convert_layer(int gamma_type, weed_layer_t *layer)
Definition: colourspace.c:12195
on_loop_cont_activate
void on_loop_cont_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:8316
lives_range_get_adjustment
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_range_get_adjustment(LiVESRange *range)
Definition: widget-helper.c:5654
lives_standard_check_button_new
LiVESWidget * lives_standard_check_button_new(const char *labeltext, boolean active, LiVESBox *box, const char *tooltip)
Definition: widget-helper.c:9048
MT_LAST_FX_REGION
@ MT_LAST_FX_REGION
Definition: multitrack.h:135
EVENT_MARKER_RECORD_START
#define EVENT_MARKER_RECORD_START
Definition: events.h:355
add_video_track_front
int add_video_track_front(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:10533
check_filewrite_overwrites
boolean check_filewrite_overwrites(void)
Definition: paramspecial.c:617
d_print_done
void d_print_done(void)
Definition: utils.c:2620
mainwindow::currticks
volatile ticks_t currticks
wall clock time, updated whenever lives_get_*_ticks is called
Definition: mainwindow.h:1005
lives_check_button_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_check_button_new(void)
Definition: widget-helper.c:4597
WEED_LEAF_FIRST
#define WEED_LEAF_FIRST
Definition: events.h:64
mainwindow::filter_map
weed_plant_t * filter_map
Definition: mainwindow.h:1298
mt_tlback
boolean mt_tlback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3625
pangotext.h
resize_message_area
boolean resize_message_area(livespointer data)
Definition: main.c:3588
CANCEL_NO_PROPOGATE
@ CANCEL_NO_PROPOGATE
cancel but keep opening
Definition: main.h:707
mainwindow::volume_scale
LiVESWidget * volume_scale
Definition: mainwindow.h:1363
mainwindow::no_configs
boolean no_configs
Definition: mainwindow.h:1803
WEED_LEAF_AUDIO_VOLUME_VALUES
#define WEED_LEAF_AUDIO_VOLUME_VALUES
Definition: events.h:23
set_new_set_name
boolean set_new_set_name(lives_mt *mt)
Definition: multitrack.c:19944
mt_mark_callback
boolean mt_mark_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:18883
mt_backup
void mt_backup(lives_mt *mt, int undo_type, weed_timecode_t tc)
Definition: multitrack.c:5200
mt_trup
boolean mt_trup(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3748
lives_painter_fill
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_fill(lives_painter_t *cr)
Definition: widget-helper.c:367
lives_cursor_new_from_pixbuf
WIDGET_HELPER_GLOBAL_INLINE LiVESXCursor * lives_cursor_new_from_pixbuf(LiVESXDisplay *disp, LiVESPixbuf *pixbuf, int x, int y)
Definition: widget-helper.c:7405
get_box_child_index
int get_box_child_index(LiVESBox *box, LiVESWidget *tchild)
Definition: widget-helper.c:11731
lives_clip_t::asampsize
int asampsize
audio sample size in bits (8 or 16)
Definition: main.h:908
lives_widget_set_fg_color
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_fg_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
Definition: widget-helper.c:2079
lives_widget_object_unref
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_object_unref(livespointer object)
decrease refcount by one: if refcount==0, object is destroyed
Definition: widget-helper.c:815
lives_painter_set_source_rgba
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_rgba(lives_painter_t *cr, double red, double green, double blue, double alpha)
Definition: widget-helper.c:619
WEED_EVENT_IS_FILTER_INIT
#define WEED_EVENT_IS_FILTER_INIT(event)
Definition: events.h:364
TRACK_I_HIDDEN_USER
#define TRACK_I_HIDDEN_USER
Definition: multitrack.h:710
make_autoreload_check
LIVES_GLOBAL_INLINE LiVESWidget * make_autoreload_check(LiVESHBox *hbox, boolean is_active)
Definition: interface.c:4342
repl_workdir
char * repl_workdir(const char *entry, boolean fwd)
Definition: utils.c:3534
ulong
#define ulong
Definition: main.h:178
create_clip_info_window
lives_clipinfo_t * create_clip_info_window(int audio_channels, boolean is_mt)
Definition: interface.c:1048
mt_undo::action
lives_mt_undo_t action
Definition: multitrack.h:692
create_rename_dialog
_entryw * create_rename_dialog(int type)
Definition: interface.c:2792
on_load_event_list_activate
boolean on_load_event_list_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22045
LIVES_FX_CAT_EFFECT
@ LIVES_FX_CAT_EFFECT
Definition: effects.h:24
lives_clip_t::tcache_height
int tcache_height
Definition: main.h:1098
set_timeline_end_secs
void set_timeline_end_secs(lives_mt *mt, double secs)
Definition: multitrack.c:3134
lives_clipinfo_t::textview_vtime
LiVESWidget * textview_vtime
Definition: interface.h:83
weed_to_rfx
lives_rfx_t * weed_to_rfx(weed_plant_t *plant, boolean show_reinits)
Definition: plugins.c:3564
WEED_LEAF_AUDIO_SAMPLE_SIZE
#define WEED_LEAF_AUDIO_SAMPLE_SIZE
Definition: events.h:21
lives_mgeometry_t::disp
LiVESXDisplay * disp
Definition: mainwindow.h:358
_prefs::unstable_fx
boolean unstable_fx
Definition: preferences.h:361
PREF_SHOW_DEVOPTS
#define PREF_SHOW_DEVOPTS
Definition: preferences.h:1067
lives_getgid
LIVES_GLOBAL_INLINE int lives_getgid(void)
Definition: machinestate.c:2420
on_mt_showkeys_activate
void on_mt_showkeys_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22976
mainwindow::drawsrc
int drawsrc
Definition: mainwindow.h:1385
lives_signal_handler_block
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handler_block(livespointer instance, unsigned long handler_id)
Definition: widget-helper.c:933
layer_to_pixbuf
LiVESPixbuf * layer_to_pixbuf(weed_layer_t *layer, boolean realpalette, boolean fordisplay)
Definition: colourspace.c:12210
mainwindow::vol_toolitem
LiVESWidget * vol_toolitem
Definition: mainwindow.h:1364
save_layout_map
void save_layout_map(int *lmap, double *lmap_audio, const char *file, const char *dir)
Definition: multitrack.c:19714
_prefs::mt_def_height
int mt_def_height
Definition: preferences.h:270
pull_lives_pixbuf_at_size
LiVESPixbuf * pull_lives_pixbuf_at_size(int clip, int frame, const char *image_ext, weed_timecode_t tc, int width, int height, LiVESInterpType interp, boolean fordisp)
Definition: main.c:7678
DEF_FILE_PERMS
#define DEF_FILE_PERMS
non-executable, is modified by the umask
Definition: main.h:209
_prefs::letterbox_mt
boolean letterbox_mt
playback with letterbox (multitrack)
Definition: preferences.h:363
BLOCK_SELECTED
@ BLOCK_SELECTED
Definition: multitrack.h:147
mt_undo::data_len
size_t data_len
including this mt_undo
Definition: multitrack.h:695
save_clip_values
boolean save_clip_values(int which_file)
Definition: saveplay.c:103
lives_xwindow_set_cursor
WIDGET_HELPER_GLOBAL_INLINE boolean lives_xwindow_set_cursor(LiVESXWindow *xwin, LiVESXCursor *cursor)
Definition: widget-helper.c:6321
lives_clip_t::arate
int arate
current audio playback rate (varies if the clip rate is changed)
Definition: main.h:906
insert_audio_here_cb
void insert_audio_here_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14284
get_clip_for_block
int get_clip_for_block(track_rect *block)
Definition: multitrack.c:23107
lives_pixbuf_get_rowstride
WIDGET_HELPER_GLOBAL_INLINE int lives_pixbuf_get_rowstride(const LiVESPixbuf *pixbuf)
Definition: widget-helper.c:3096
PREF_MT_DEF_ACHANS
#define PREF_MT_DEF_ACHANS
Definition: preferences.h:1010
TRACK_I_HIDDEN_SCROLLED
#define TRACK_I_HIDDEN_SCROLLED
Definition: multitrack.h:711
poly_tab_to_page
LIVES_INLINE int poly_tab_to_page(uint32_t tab)
Definition: multitrack.c:3775
_palette::mt_timecode_fg
LiVESWidgetColor mt_timecode_fg
Definition: mainwindow.h:333
weed_chantmpl_is_optional
WEED_GLOBAL_INLINE int weed_chantmpl_is_optional(weed_plant_t *chantmpl)
Definition: weed-effects-utils.c:367
LAYOUT_FILENAME
#define LAYOUT_FILENAME
Definition: mainwindow.h:570
widget_opts_t::text_size
const char * text_size
specialised values /////
Definition: widget-helper.h:1415
is_perchannel_multiw
boolean is_perchannel_multiw(weed_plant_t *param)
Definition: effects-weed.c:8693
scroll_track_by_scrollbar
void scroll_track_by_scrollbar(LiVESScrollbar *sbar, livespointer user_data)
Definition: multitrack.c:3650
_prefs::event_window_show_frame_events
boolean event_window_show_frame_events
Definition: preferences.h:258
LIVES_SENSE_STATE_INTERACTIVE
#define LIVES_SENSE_STATE_INTERACTIVE
Definition: mainwindow.h:1708
FALSE
#define FALSE
Definition: videoplugin.h:60
mainwindow::affected_layout_marks
LiVESList * affected_layout_marks
list of pairs of marks in affected_layouts_map, text between them should be deleted when stored_layou...
Definition: mainwindow.h:1474
on_resetp_clicked
void on_resetp_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19140
mainwindow::full_screen
LiVESWidget * full_screen
Definition: mainwindow.h:1172
lives_widget_get_allocation_width
WIDGET_HELPER_GLOBAL_INLINE int lives_widget_get_allocation_width(LiVESWidget *widget)
Definition: widget-helper.c:5455
WEED_LEAF_HOST_DEFAULT
#define WEED_LEAF_HOST_DEFAULT
Definition: effects-weed.h:62
lives_widget_set_size_request
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_size_request(LiVESWidget *widget, int width, int height)
Definition: widget-helper.c:1614
TEXTWIDGET_KEY
#define TEXTWIDGET_KEY
Definition: widget-helper.h:1492
_prefs::mt_undo_buf
int mt_undo_buf
Definition: preferences.h:267
_prefs::msg_textsize
int msg_textsize
Definition: preferences.h:445
weed_reinit_effect
lives_filter_error_t weed_reinit_effect(weed_plant_t *inst, boolean reinit_compound)
Definition: effects-weed.c:1169
mt_delete_clips
void mt_delete_clips(lives_mt *mt, int file)
Definition: multitrack.c:10800
AMIXER_WRATIO
#define AMIXER_WRATIO
audio mixer width ratio (fraction of screen width)
Definition: multitrack.h:44
WEED_LEAF_NEXT
#define WEED_LEAF_NEXT
Definition: events.h:62
calc_maxspect
void calc_maxspect(int rwidth, int rheight, int *cwidth, int *cheight)
Definition: utils.c:2174
on_framedraw_mouse_update
boolean on_framedraw_mouse_update(LiVESWidget *widget, LiVESXEventMotion *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:989
WARN_MASK_MT_ACHANS
#define WARN_MASK_MT_ACHANS
Definition: preferences.h:109
PEB_WRATIO
#define PEB_WRATIO
preview eventbox width ratio (fraction of screen width)
Definition: multitrack.h:41
WEED_LEAF_GAMMA_ENABLED
#define WEED_LEAF_GAMMA_ENABLED
Definition: events.h:67
set_css_value_direct
boolean set_css_value_direct(LiVESWidget *, LiVESWidgetState state, const char *selector, const char *detail, const char *value)
Definition: widget-helper.c:2039
get_prev_fm
weed_plant_t * get_prev_fm(lives_mt *mt, int current_track, weed_plant_t *event)
Definition: multitrack.c:18784
INSERT_MODE_NORMAL
@ INSERT_MODE_NORMAL
the default (only insert if it fits)
Definition: multitrack.h:76
lives_widget_set_text_color
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_text_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
Definition: widget-helper.c:2101
mainwindow::stop
LiVESWidget * stop
Definition: mainwindow.h:1170
on_stop_activate
void on_stop_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:4748
remove_frame_from_event
void remove_frame_from_event(weed_plant_t *event_list, weed_plant_t *event, int track)
Definition: events.c:489
FX_KEYS_MAX
#define FX_KEYS_MAX
the rest of the keys are accessible through the multitrack renderer (must, be > FX_KEYS_MAX_VIRTUAL)
Definition: mainwindow.h:206
mt_idle_add
uint32_t mt_idle_add(lives_mt *mt)
Definition: multitrack.c:901
toggle_sets_pref
void toggle_sets_pref(LiVESWidget *widget, livespointer prefidx)
callback to set to make a togglebutton or check_menu_item directly control a boolean pref widget is e...
Definition: preferences.c:46
_prefs::autoload_subs
boolean autoload_subs
Definition: preferences.h:345
lives_painter_rectangle
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_rectangle(lives_painter_t *cr, double x, double y, double width, double height)
Definition: widget-helper.c:555
_prefs::atrans_fx
int atrans_fx
Definition: preferences.h:353
_prefs::sepwin_type
short sepwin_type
Definition: preferences.h:186
lives_clip_t::handle
char handle[256]
Definition: main.h:881
_
#define _(String)
Definition: support.h:44
layout_map::unique_id
int64_t unique_id
Definition: multitrack.h:747
find_init_event_in_ttable
void * find_init_event_in_ttable(ttable *trans_table, uint64_t in, boolean normal)
Definition: multitrack.c:20253
event_list_get_end_tc
weed_timecode_t event_list_get_end_tc(weed_plant_t *event_list)
Definition: events.c:4601
_future_prefs::letterbox_mt
boolean letterbox_mt
Definition: preferences.h:844
get_region_overlap
void get_region_overlap(lives_mt *mt)
Definition: multitrack.c:18527
LIVES_CURSOR_CENTER_PTR
@ LIVES_CURSOR_CENTER_PTR
Definition: widget-helper.h:1294
IS_NORMAL_CLIP
#define IS_NORMAL_CLIP(clip)
Definition: main.h:833
STYLE_1
#define STYLE_1
turn on theming if set
Definition: mainwindow.h:299
WEED_EVENT_IS_AUDIO_FRAME
#define WEED_EVENT_IS_AUDIO_FRAME(event)
Definition: events.h:362
lives_widget_set_tooltip_text
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_widget_set_tooltip_text(LiVESWidget *widget, const char *tip_text)
Definition: widget-helper.c:4641
mainwindow::error
boolean error
Definition: mainwindow.h:801
POLY_NONE
@ POLY_NONE
Definition: multitrack.h:122
mt_undo
Definition: multitrack.h:691
capability::dclick_dist
int dclick_dist
Definition: main.h:623
remove_layout_files
void remove_layout_files(LiVESList *lmap)
Definition: utils.c:3559
mt_change_vals_activate
void mt_change_vals_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22352
q_dbl
LIVES_GLOBAL_INLINE ticks_t q_dbl(double in, double fps)
Definition: resample.c:41
weed_get_sorted_filter
LIVES_GLOBAL_INLINE int weed_get_sorted_filter(int i)
Definition: effects-weed.c:9862
has_perchannel_multiw
boolean has_perchannel_multiw(weed_plant_t *filter)
Definition: effects-weed.c:8706
lives_rm
int lives_rm(const char *file)
Definition: utils.c:4395
lives_notebook_get_current_page
WIDGET_HELPER_GLOBAL_INLINE int lives_notebook_get_current_page(LiVESNotebook *nbook)
Definition: widget-helper.c:6903
LIVES_INLINE
#define LIVES_INLINE
Definition: main.h:238
mt_clip_from_file
LIVES_INLINE int mt_clip_from_file(lives_mt *mt, int file)
Definition: multitrack.c:221
_future_prefs::audio_src
int audio_src
Definition: preferences.h:828
lives_event_get_time
WIDGET_HELPER_GLOBAL_INLINE int lives_event_get_time(LiVESXEvent *event)
Definition: widget-helper.c:4459
lives_widget_apply_theme2
void lives_widget_apply_theme2(LiVESWidget *widget, LiVESWidgetState state, boolean set_fg)
Definition: widget-helper.c:11169
add_mt_param_box
boolean add_mt_param_box(lives_mt *mt)
Definition: multitrack.c:1740
STARTUP_MT
#define STARTUP_MT
Definition: preferences.h:339
mt_tlfor
boolean mt_tlfor(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3605
lives_menu_add_separator
LiVESWidget * lives_menu_add_separator(LiVESMenu *menu)
Definition: widget-helper.c:11910
lives_param_t::type
lives_param_type_t type
Definition: plugins.h:573
lives_clip_t::achans
int achans
number of audio channels (0, 1 or 2)
Definition: main.h:907
make_backup_space
boolean make_backup_space(lives_mt *mt, size_t space_needed)
Definition: multitrack.c:5157
scroll_tracks
void scroll_tracks(lives_mt *mt, int top_track, boolean set_value)
Definition: multitrack.c:2347
lives_event_box_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_event_box_new(void)
Definition: widget-helper.c:2284
lives_widget_set_base_color
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_base_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
Definition: widget-helper.c:2126
LIVES_EXPAND_DEFAULT_WIDTH
#define LIVES_EXPAND_DEFAULT_WIDTH
Definition: widget-helper.h:1313
AFORM_BIG_ENDIAN
#define AFORM_BIG_ENDIAN
Definition: main.h:787
mt_prepare_for_playback
void mt_prepare_for_playback(lives_mt *mt)
Definition: multitrack.c:17259
get_luma16
double get_luma16(uint16_t r, uint16_t g, uint16_t b)
Definition: colourspace.c:557
lives_widget_show
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show(LiVESWidget *widget)
Definition: widget-helper.c:1505
help_translate_activate
void help_translate_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7063
lives_clipinfo_t::textview_rrate
LiVESWidget * textview_rrate
Definition: interface.h:88
lives_widget_context_update
boolean lives_widget_context_update(void)
Definition: widget-helper.c:11878
on_set_pvals_clicked
void on_set_pvals_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19430
EXPOSE_FN_PROTOTYPE
EXPOSE_FN_PROTOTYPE(expose_vid_event)
event_list_replace_events
void event_list_replace_events(weed_plant_t *event_list, weed_plant_t *new_event_list)
replace events in event_list with events in new_event_list
Definition: events.c:2320
renamew
_entryw * renamew
Definition: interface.h:311
move_event_right
boolean move_event_right(weed_plant_t *event_list, weed_plant_t *event, boolean can_stay, double fps)
Definition: events.c:2030
mainwindow::gens_submenu
LiVESWidget * gens_submenu
Definition: mainwindow.h:1414
render_details::suggestion_followed
boolean suggestion_followed
Definition: events.h:246
lives_strncmp
LIVES_GLOBAL_INLINE boolean lives_strncmp(const char *st1, const char *st2, size_t len)
returns FALSE if strings match
Definition: machinestate.c:1554
set_string_pref
int set_string_pref(const char *key, const char *value)
Definition: preferences.c:290
add_to_playframe
void add_to_playframe(void)
Definition: gui.c:4451
mainwindow::reconfig
boolean reconfig
set to TRUE if a monitor / screen size change is detected
Definition: mainwindow.h:1743
create_event_list_dialog
LiVESWidget * create_event_list_dialog(weed_plant_t *event_list, weed_timecode_t start_tc, weed_timecode_t end_tc)
Definition: events.c:5582
close_clip_cb
void close_clip_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14266
do_mt_keys_window
void do_mt_keys_window(void)
Definition: interface.c:4813
MAINW_MSG_SIZE
#define MAINW_MSG_SIZE
mainw->msg bytesize
Definition: mainwindow.h:702
lives_painter_set_source_rgb_from_lives_rgba
WIDGET_HELPER_GLOBAL_INLINE lives_colRGBA64_t * lives_painter_set_source_rgb_from_lives_rgba(lives_painter_t *cr, lives_colRGBA64_t *col)
Definition: widget-helper.c:11090
load_end_image
void load_end_image(int frame)
Definition: main.c:5922
lives_menu_item_set_text
WIDGET_HELPER_GLOBAL_INLINE void lives_menu_item_set_text(LiVESWidget *menuitem, const char *text, boolean use_mnemonic)
Definition: widget-helper.c:11920
lives_standard_frame_new
LiVESWidget * lives_standard_frame_new(const char *labeltext, float xalign, boolean invis)
Definition: widget-helper.c:8732
compress_files_in_dir
boolean compress_files_in_dir(const char *dir, int method, void *data)
Definition: machinestate.c:898
remove_gaps
void remove_gaps(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14648
lives_pixbuf_get_has_alpha
WIDGET_HELPER_GLOBAL_INLINE boolean lives_pixbuf_get_has_alpha(const LiVESPixbuf *pixbuf)
Definition: widget-helper.c:3162
is_legal_set_name
boolean is_legal_set_name(const char *set_name, boolean allow_dupes, boolean leeway)
Definition: utils.c:2975
STYLE_4
#define STYLE_4
separator col. in mt
Definition: mainwindow.h:302
lives_clip_t::end
frames_t end
Definition: main.h:891
WEED_LEAF_PREVIOUS
#define WEED_LEAF_PREVIOUS
Definition: events.h:63
insert_blank_frame_event_at
LIVES_GLOBAL_INLINE weed_plant_t * insert_blank_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc, weed_plant_t **shortcut)
Definition: events.c:1472
on_track_move
boolean on_track_move(LiVESWidget *widget, LiVESXEventMotion *event, livespointer user_data)
Definition: multitrack.c:14112
startup.h
mt_idle_show_current_frame
boolean mt_idle_show_current_frame(livespointer data)
Definition: multitrack.c:11015
move_event_left
boolean move_event_left(weed_plant_t *event_list, weed_plant_t *event, boolean can_stay, double fps)
Definition: events.c:2103
lives_image_menu_item_set_image
WIDGET_HELPER_GLOBAL_INLINE boolean lives_image_menu_item_set_image(LiVESImageMenuItem *item, LiVESWidget *image)
Definition: widget-helper.c:6556
on_save_event_list_activate
boolean on_save_event_list_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:19990
lives_clip_t::layout_map
LiVESList * layout_map
Definition: main.h:1037
_prefs::show_dev_opts
boolean show_dev_opts
Definition: preferences.h:463
on_open_utube_activate
void on_open_utube_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:770
lives_box_pack_start
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_pack_start(LiVESBox *box, LiVESWidget *child, boolean expand, boolean fill, uint32_t padding)
Definition: widget-helper.c:3281
get_new_handle
boolean get_new_handle(int index, const char *name)
Definition: saveplay.c:3821