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
98static uint32_t event_list_get_byte_size(lives_mt *mt, weed_plant_t *event_list, boolean nxprev, int *num_events);
99
100static EXPOSE_FN_PROTOTYPE(expose_timeline_reg_event)
101static EXPOSE_FN_PROTOTYPE(mt_expose_audtrack_event)
102
103static boolean mt_add_block_effect_idle(livespointer mt);
104static boolean mt_add_region_effect_idle(livespointer mt);
105static boolean mt_fx_edit_idle(livespointer mt);
106
107static void paint_lines(lives_mt *mt, double currtime, boolean unpaint, lives_painter_t *cr);
108
109static int *update_layout_map(weed_plant_t *event_list);
110static double *update_layout_map_audio(weed_plant_t *event_list);
111
112static boolean check_can_resetp(lives_mt *mt);
113
115static int renumbered_clips[MAX_FILES + 1];
116
117static double lfps[MAX_FILES + 1];
118static void **pchain;
119
120static int xachans, xarate, xasamps, xse;
121static boolean ptaud;
122static int btaud;
123
124static int aofile;
125static int afd;
126
127static int dclick_time = 0;
128
129static boolean force_pertrack_audio;
130static int force_backing_tracks;
131
132static int clips_to_files[MAX_FILES];
133
134static boolean pb_audio_needs_prerender;
135static weed_plant_t *pb_loop_event, *pb_filter_map, *pb_afilter_map;
136
137static boolean nb_ignore = FALSE;
138
139static LiVESWidget *dummy_menuitem;
140
141static boolean doubleclick = FALSE;
142
143static uint32_t last_press_time = 0;
144static int last_x = 0;
145static int last_y = 0;
146
147static boolean needs_clear;
148
149static LiVESList *pkg_list;
150
151static LiVESWidget *mainw_message_box;
152static LiVESWidget *mainw_msg_area;
153static LiVESWidget *mainw_msg_scrollbar;
154static LiVESAdjustment *mainw_msg_adj;
155//static ulong mainw_sw_func;
156
158
159// menuitem callbacks
160static void multitrack_adj_start_end(LiVESMenuItem *, livespointer mt);
161boolean multitrack_audio_insert(LiVESMenuItem *, livespointer mt);
162static void multitrack_view_events(LiVESMenuItem *, livespointer mt);
163static void multitrack_view_sel_events(LiVESMenuItem *, livespointer mt);
164static void on_prerender_aud_activate(LiVESMenuItem *, livespointer mt);
165static void on_jumpnext_activate(LiVESMenuItem *, livespointer mt);
166static void on_jumpback_activate(LiVESMenuItem *, livespointer mt);
167static void on_jumpnext_mark_activate(LiVESMenuItem *, livespointer mt);
168static void on_jumpback_mark_activate(LiVESMenuItem *, livespointer mt);
169static void on_delblock_activate(LiVESMenuItem *, livespointer mt);
170static void on_seltrack_activate(LiVESMenuItem *, livespointer mt);
171static void multitrack_view_details(LiVESMenuItem *, livespointer mt);
172static void mt_add_region_effect(LiVESMenuItem *, livespointer mt);
173static void mt_add_block_effect(LiVESMenuItem *, livespointer mt);
174static void on_clear_event_list_activate(LiVESMenuItem *, livespointer mt);
175static void show_frame_events_activate(LiVESMenuItem *, livespointer);
176static void mt_load_vals_toggled(LiVESMenuItem *, livespointer mt);
177static void mt_load_vals_toggled(LiVESMenuItem *, livespointer mt);
178static void mt_render_vid_toggled(LiVESMenuItem *, livespointer mt);
179static void mt_render_aud_toggled(LiVESMenuItem *, livespointer mt);
180static void mt_norm_aud_toggled(LiVESMenuItem *, livespointer mt);
181static void mt_fplay_toggled(LiVESMenuItem *, livespointer mt);
182static void mt_change_vals_activate(LiVESMenuItem *, livespointer mt);
183static void on_set_pvals_clicked(LiVESWidget *button, livespointer mt);
184static void on_move_fx_changed(LiVESMenuItem *, livespointer mt);
185static void select_all_time(LiVESMenuItem *, livespointer mt);
186static void select_from_zero_time(LiVESMenuItem *, livespointer mt);
187static void select_to_end_time(LiVESMenuItem *, livespointer mt);
188static void select_all_vid(LiVESMenuItem *, livespointer mt);
189static void select_no_vid(LiVESMenuItem *, livespointer mt);
190static void on_split_sel_activate(LiVESMenuItem *, livespointer mt);
191static void on_split_curr_activate(LiVESMenuItem *, livespointer mt);
192static void multitrack_undo(LiVESMenuItem *, livespointer mt);
193static void multitrack_redo(LiVESMenuItem *, livespointer mt);
194static void on_mt_showkeys_activate(LiVESMenuItem *, livespointer);
195static void on_mt_list_fx_activate(LiVESMenuItem *, livespointer mt);
196static void on_mt_delfx_activate(LiVESMenuItem *, livespointer mt);
197static void on_mt_fx_edit_activate(LiVESMenuItem *, livespointer mt);
198static void mt_view_audio_toggled(LiVESMenuItem *, livespointer mt);
199static void mt_ign_ins_sel_toggled(LiVESMenuItem *, livespointer mt);
200static void mt_change_max_disp_tracks(LiVESMenuItem *, livespointer mt);
201
202static void mt_ac_audio_toggled(LiVESMenuItem *, livespointer mt);
203
205
206#define LIVES_AVOL_SCALE ((double)1000000.)
207
208void maybe_add_mt_idlefunc(void) {
212 }
213}
214
215
216LIVES_INLINE int mt_file_from_clip(lives_mt *mt, int clip) {
217 return clips_to_files[clip];
218}
219
220
221LIVES_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
231int 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
236LIVES_INLINE boolean is_empty_track(LiVESWidgetObject *track) {
237 return (lives_widget_object_get_data(track, "blocks") == NULL);
238}
239
240double 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
246void 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
252boolean 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
405LiVESPixbuf *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
489LiVESPixbuf *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
526static 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) {
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
668boolean 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(),
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
705static 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
720static 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,
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,
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
782static 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;
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
852static 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
863boolean 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
901uint32_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
923void 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
934 lives_rm(eload_file);
935 lives_free(eload_file);
936
937 if (is_startup) do_after_crash_warning();
938}
939
940
941boolean mt_load_recovery_layout(lives_mt *mt) {
942 boolean recovered = TRUE;
943 char *aload_file = NULL, *eload_file;
944
945 if (mt) {
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(),
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
1010boolean 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
1033void **mt_get_pchain(void) {
1034 return pchain;
1035}
1036
1037
1038char *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
1048LIVES_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
1082static 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
1097static 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
1102static 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;
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
1137static 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
1148void 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
1176static 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);
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
1324 }
1326 } else {
1327 if (i + width > offset_end) width = offset_end - i;
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));
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);
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
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));
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);
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
1425static 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
1549static 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
1572static 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
1623draw1:
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);
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);
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}
1707EXPOSE_FN_END
1708
1709
1710static 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
1740boolean 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);
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
1827static 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
1845static 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
1878static 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
1941void 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) {
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
2120if (mt->poly_state == POLY_FX_STACK) polymorph(mt, POLY_FX_STACK);
2121else 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;
2160 } else mt_show_current_frame(mt, FALSE);
2161 } else polymorph(mt, POLY_FX_STACK);
2162}
2163}
2164
2165
2166static 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
2198static 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
2212static 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
2225static 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
2250static 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
2312static 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
2330static 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
2342
2343 return FALSE;
2344}
2345
2346
2347void 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
2807boolean 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
2844void multitrack_view_clips(LiVESMenuItem * menuitem, livespointer user_data) {
2845 lives_mt *mt = (lives_mt *)user_data;
2846 polymorph(mt, POLY_CLIPS);
2847}
2848
2849
2850void 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) {
2855 }
2856}
2857
2858
2859static 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
2874static 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
2884static 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
2893static 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
2965static 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
3024void 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) {
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
3096boolean 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
3107boolean 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
3118static 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
3134void 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
3160static 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
3188
3189 // set actual playback start time, from mt->ptr_time
3190 tc = q_gint64(mt->ptr_time * TICKS_PER_SECOND_DBL, mainw->files[mt->render_file]->fps);
3191
3192 if (!mt->is_paused)
3193 mt->pb_unpaused_start_time = mt->ptr_time;
3194
3195 mt->pb_start_time = mt->ptr_time;
3196
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
3214void 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;
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;
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) {
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
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) {
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
3510static 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
3517static 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
3524static 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
3530static 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
3538static 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
3595void 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
3600LIVES_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
3605boolean 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
3615boolean 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
3625boolean 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
3634boolean 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
3644static 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
3650void scroll_track_by_scrollbar(LiVESScrollbar * sbar, livespointer user_data) {
3651 lives_mt *mt = (lives_mt *)user_data;
3653 track_select(mt);
3654}
3655
3656
3657static 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
3700static 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
3710boolean 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
3748boolean 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
3770LIVES_INLINE int poly_page_to_tab(uint32_t page) {
3771 return ++page;
3772}
3773
3774
3776 return --tab;
3777}
3778
3779
3782}
3783
3784
3785static 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
3833static 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
3870static 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);
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
3962void 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
3973static 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
4028LIVES_LOCAL_INLINE int pkg_in_list(char *pkgstring) {
4029 return lives_list_strcmp_index(pkg_list, pkgstring, FALSE) + 1;
4030}
4031
4032
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
4047static void populate_filter_box(int ninchans, lives_mt * mt, int pkgnum);
4048
4049static 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
4082static 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
4219static 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
4254static 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
4388static 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
4412void 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
4419void 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
4429void 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
4439static 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
4445static 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
4452void 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
4506void 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
4562static 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
4605static 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) {
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
4652 lives_menu_popup(LIVES_MENU(menu), event);
4653}
4654
4655
4656static 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
4718static 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) {
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
4836static 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
4844void 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);
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
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
4916void 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
4946void 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
4966static 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 }
4975}
4976
4977
4978static 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
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
5012void 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
5047static 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
5063static 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
5077static char *get_undo_text(int action, void *extra) {
5078 char *filtname, *ret;
5079
5080 switch (action) {
5082 return (_("Close gaps"));
5083 case MT_UNDO_MOVE_BLOCK:
5084 return (_("Move block"));
5086 return (_("Move audio block"));
5088 return (_("Delete block"));
5090 return (_("Delete audio block"));
5092 return (_("Split tracks"));
5093 case MT_UNDO_SPLIT:
5094 return (_("Split block"));
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;
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;
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
5118static 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
5138static 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
5157boolean 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
5200void 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;
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
5289void 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
5303static void destroy_widget(LiVESWidget * widget, livespointer user_data) {
5304 lives_widget_destroy(widget);
5305}
5306
5307
5308void 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
5369static 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
5458static 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);
5497}
5498
5499
5500static 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
5516void 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
5540static 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)
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
5768char *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
5827void 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
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
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 }
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);
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 }
5893 }
5894}
5895
5896
5897void 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]) {
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
5921LIVES_INLINE void print_layout_wiped(void) {d_print(_("Layout was wiped.\n"));}
5922
5923
5924boolean 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
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
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;
5980 }
5982 return TRUE;
5983}
5984
5985
5986void 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
5996void 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
6008static 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);
6013 lives_free(wtxt);
6014}
6015
6016
6017static 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
6072static 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
6080void 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);
6086 lives_free(atrans_hash);
6087 }
6089}
6090
6091
6092static 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
6102static 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
6115static 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
6138static 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
6148void 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,
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,
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,
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
6431static void on_solo_check_toggled(LiVESWidget * widg, livespointer user_data) {
6432 lives_mt *mt = (lives_mt *)user_data;
6434}
6435
6436
6437static 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
6448lives_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
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
6954 lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->undo);
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
6975 lives_container_add(LIVES_CONTAINER(mt->edit_menu), mt->redo);
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
7112 lives_container_add(LIVES_CONTAINER(mt->play_menu), mt->stop);
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
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));
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 {
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
8993void 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
9060static 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
9093static 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
9180boolean 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
9204boolean 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
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
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
9485 mainw->last_dprint_file = -1;
9486
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);
9512 load_end_image(0);
9513 }
9514
9516
9517 lives_free(mt);
9518
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
9544static 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
9566static 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
9623static 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
9630static 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
9638static 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
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
9655static 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
9679void 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
10121void 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
10166LiVESWidget *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();
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
10209
10210 arrow = lives_arrow_new(LIVES_ARROW_RIGHT, LIVES_SHADOW_OUT);
10211 lives_widget_set_tooltip_text(arrow, _("Show/hide audio details"));
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
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);
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);
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
10355static 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
10366void 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
10374static 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"));
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);
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
10523int 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
10533int 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
10543void 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);
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
10554static boolean mt_fx_edit_idle(livespointer user_data) {
10555 on_mt_fx_edit_activate(NULL, user_data);
10556 return FALSE;
10557}
10558
10559
10560static 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
10567static 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
10573void 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) {
10611 lives_widget_apply_theme2(menu, LIVES_WIDGET_STATE_NORMAL, TRUE);
10612 }
10613
10615 lives_menu_popup(LIVES_MENU(menu), event);
10616}
10617
10618
10619static 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:
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
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;
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 {
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
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
10720static 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
10742void mt_clear_timeline(lives_mt * mt) {
10743 int i;
10744 char *msg;
10745
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
10797}
10798
10799
10800void 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
10854 lives_widget_set_sensitive(mt->adjust_start_end, FALSE);
10855 }
10856}
10857
10858
10859void 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
10987static 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
11015boolean 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
11024boolean 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
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))) {
11101 prefs->mt_def_arate = xarate;
11103 prefs->mt_def_achans = xachans;
11105 prefs->mt_def_asamps = xasamps;
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
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
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
11369boolean 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
11383static 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
11401static 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
11417track_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
11620void 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
11661static 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,
11683 lives_widget_set_bg_color(lives_bin_get_child(LIVES_BIN(mt->context_scroll)), LIVES_WIDGET_STATE_NORMAL,
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
11693void clear_context(lives_mt * mt) {
11694 main_thread_execute((lives_funcptr_t)_clear_context, 0, NULL, "v", mt);
11695}
11696
11697
11698void 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
11721boolean 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
11739static 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
11809static 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
11843static 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),
11873 set_drawing_area_from_pixbuf(mt->out_image, thumb, mt->outsurface);
11874 if (thumb) lives_widget_object_unref(thumb);
11875}
11876
11877
11878boolean 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
11897void 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;
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
12174void 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;
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
12500void 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
12548void 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
12683void 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
12729void 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
12777void 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) :
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;
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,
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:
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) {
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
13446static 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
13478static 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
13490static 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);
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
13619void 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) {
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
13691 lives_menu_popup(LIVES_MENU(menu), event);
13692}
13693
13694
13695void 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) {
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
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
13773boolean 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
13908track_rel_done:
13909
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
13925boolean 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
13936boolean 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
13947boolean 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
13958boolean 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
13969boolean 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
14112boolean 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
14121boolean 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
14130void 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
14150void unpaint_lines(lives_mt * mt) {
14151 if (!mt->is_ready) return;
14152 unpaint_line(mt, mt->timeline_table);
14153 return;
14154}
14155
14156
14157static 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
14164static 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
14186static 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
14223void animate_multitrack(lives_mt * mt) {
14224 main_thread_execute((lives_funcptr_t)_animate_multitrack, 0, NULL, "v", mt);
14225}
14226
14228// menuitem callbacks
14229
14230static 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
14237void 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
14244void 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
14251void 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
14259void 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
14266void 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
14273void 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
14284void 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
14291void 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
14299void 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
14306void 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
14313void 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
14323static 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
14329static 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
14337static 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
14345static 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
14353static 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
14389static 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
14425static 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
14432static 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
14439static 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
14447static 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
14453static 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
14466static 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
14472static 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
14640void 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
14648void remove_gaps(LiVESMenuItem * menuitem, livespointer user_data) {
14649 remove_gaps_inner(menuitem, user_data, FALSE);
14650}
14651
14652
14653static 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
14718static 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
14982void 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
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
15029void 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
15073void 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);
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
15298void 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);
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
15489void 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
15544static 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) {
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
15650weed_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
15670void 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:
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
15780static 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
15787void 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
15835static 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
15842void 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;
15846}
15847
15848
15849void 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:
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
15940static 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
15995void 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
16001void 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
16007static 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
16025void 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
16031void 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
16037void 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
16071void 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
16078boolean 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;
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);
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
16358void 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
16367void 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
16552void 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
16591void 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
16632void 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
16669static 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
16863void 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
16871void 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
16962void 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
16979void 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);
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);
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);
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
17052void 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);
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);
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
17206void 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
17250void multitrack_preview_clicked(LiVESWidget * button, livespointer user_data) {
17251 //preview during rendering
17252 lives_mt *mt = (lives_mt *)user_data;
17253
17256}
17257
17258
17259void 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
17277 lives_widget_set_sensitive(mt->rewind, FALSE);
17279 lives_widget_set_sensitive(mt->playall, TRUE);
17281}
17282
17283
17284void mt_post_playback(lives_mt * mt) {
17285 // called from on_preview_clicked
17287
17289
17291 mainw->cancelled == CANCEL_NO_MORE_PREVIEW) && mt->is_paused)) {
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) {
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
17345void 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);
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
17449void 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
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
17473void multitrack_adj_start_end(LiVESMenuItem * menuitem, livespointer user_data) {
17474 lives_mt *mt = (lives_mt *)user_data;
17475 unselect_all(mt);
17477}
17478
17479
17480boolean 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
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
17632boolean 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
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
17810void 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
18059void 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
18122void 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
18137void 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
18159void 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));
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));
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);
18219 lives_widget_queue_draw(mt->timeline_reg);
18220}
18221
18222
18223static 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}
18259EXPOSE_FN_END
18260
18261
18262static 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);
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
18369static 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;
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);
18423 }
18424
18425 if (bgimage) lives_painter_surface_destroy(bgimage);
18426
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);
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}
18453EXPOSE_FN_END
18454
18455
18457
18458// functions for moving and clicking on the timeline
18459
18460boolean 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
18498boolean 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
18527void 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
18583void 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
18603void 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
18615void 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
18622boolean 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
18739boolean 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
18784weed_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
18838weed_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
18861static 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
18883boolean 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
18897boolean 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
18907void 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
18920void 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
18933void 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
18947}
18948
18949
18950void 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
18964}
18965
18966
18967static 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
18986static 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
19006static 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
19024void 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
19092static 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
19140void 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));
19203}
19204
19205
19206// node buttons
19207void 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
19221void 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
19235void 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
19321void 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
19349static 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
19363static 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
19400void 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
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
19430void 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),
19457 after_param_text_changed(textwidget, mt->current_rfx);
19458 }
19459 if (mt->framedraw) {
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:
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
19581static int free_tt_key;
19582static int elist_errors;
19583
19584LiVESList *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
19714void 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
19877void 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
19944boolean 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
19990boolean 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) {
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
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) {
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
20218static 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
20244static 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
20253void *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
20265static 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
20293void 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
20384boolean 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
20488static 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
20526static 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
20563static 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
20578static 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
20597static LiVESList *atrack_list;
20598
20599static 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
20621static 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
20643static 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
20697static 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
20713boolean 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
21623char *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
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
21693weed_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;
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) {
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
21918void 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
21936void 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)) {
21956 }
21957
21958 event_list_free(mt->event_list);
21959 mt->event_list = NULL;
21960
21962
21964
21965 mt_sensitise(mt);
21966
21967 mt->idlefunc = mt_idle_add(mt);
21968}
21969
21970
21971void 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
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
22045boolean 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
22115void 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
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
22247LiVESList *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
22281LiVESList *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
22314void 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
22325void show_frame_events_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22327}
22328
22329
22330void 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
22340void 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
22346static 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
22352void mt_change_vals_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22353 lives_mt *mt = (lives_mt *)user_data;
22354 boolean response;
22355 char *msg;
22356
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))) {
22416 prefs->mt_def_arate = xarate;
22418 prefs->mt_def_achans = xachans;
22420 prefs->mt_def_asamps = xasamps;
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
22481static 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
22536void 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
22571static 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
22605static 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
22610void 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
22694LiVESWidget *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
22757void 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
22976void on_mt_showkeys_activate(LiVESMenuItem * menuitem, livespointer user_data) {
22978}
22979
22980
22981LiVESWidget *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
22994static 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
23012track_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
23042boolean 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
23048boolean 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
23065int 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
23084double 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
23092double 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
23099track_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
23107int 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
23119void 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_GLOBAL_INLINE char * get_achannel_name(int totchans, int idx)
Definition: audio.c:28
float get_float_audio_val_at_time(int fnum, int afd, double secs, int chnum, int chans)
Definition: audio.c:374
LIVES_GLOBAL_INLINE char * lives_get_audio_file_name(int fnum)
Definition: audio.c:55
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
#define lives_vol_to_linear(vol)
Definition: audio.h:270
#define is_realtime_aplayer(ptype)
Definition: audio.h:236
#define lives_vol_from_linear(vol)
Definition: audio.h:269
void on_boolean_toggled(LiVESWidgetObject *obj, livespointer user_data)
Definition: callbacks.c:8278
void email_author_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7048
void on_rewind_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:4730
void suggest_feature_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7058
void on_sepwin_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:8055
boolean config_event(LiVESWidget *widget, LiVESXEventConfigure *event, livespointer user_data)
Definition: callbacks.c:10090
void on_open_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7418
void show_manual_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7043
void on_show_file_info_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:6776
void on_open_vcd_activate(LiVESMenuItem *menuitem, livespointer device_type)
Definition: callbacks.c:720
void help_translate_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7063
void on_show_messages_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:6771
void on_mute_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:8399
void donate_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7068
void popup_lmap_errors(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:9335
boolean all_expose(LiVESWidget *widget, lives_painter_t *cr, livespointer psurf)
Definition: callbacks.c:9856
void on_open_loc_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:745
void on_open_utube_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:770
void on_preview_clicked(LiVESButton *button, livespointer user_data)
Definition: callbacks.c:10245
boolean on_mouse_scroll(LiVESWidget *widget, LiVESXEventScroll *event, livespointer user_data)
Definition: callbacks.c:10602
void on_full_screen_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7990
void on_loop_cont_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:8316
LIVES_GLOBAL_INLINE void lives_notify_int(int msgnumber, int msgint)
Definition: callbacks.c:77
void on_quit_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:2133
void on_about_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:6977
void on_cleardisk_activate(LiVESWidget *widget, livespointer user_data)
Definition: callbacks.c:6139
boolean freeze_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: callbacks.c:11135
void on_close_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:1432
void on_playall_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:4530
void report_bug_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:7053
void on_capture_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:11400
char * on_load_set_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:5590
void on_open_sel_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:619
void on_stop_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: callbacks.c:4748
EXPOSE_FN_PROTOTYPE(expose_vid_event)
void on_resample_vid_ok(LiVESButton *, LiVESEntry *entry)
Definition: resample.c:1379
LIVES_GLOBAL_INLINE weed_layer_t * lives_layer_new_for_frame(int clip, frames_t frame)
Definition: colourspace.c:9833
LiVESPixbuf * layer_to_pixbuf(weed_layer_t *layer, boolean realpalette, boolean fordisplay)
boolean letterbox_layer(weed_layer_t *layer, int nwidth, int nheight, int width, int height, LiVESInterpType interp, int tpal, int tclamp)
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_free(weed_layer_t *layer)
frees pixel_data for a layer, then the layer itself
LIVES_GLOBAL_INLINE int weed_layer_get_height(weed_layer_t *layer)
LIVES_GLOBAL_INLINE boolean gamma_convert_layer(int gamma_type, weed_layer_t *layer)
LIVES_GLOBAL_INLINE boolean lives_pixbuf_is_all_black(LiVESPixbuf *pixbuf)
Definition: colourspace.c:2193
LIVES_GLOBAL_INLINE boolean weed_palette_has_alpha(int pal)
Definition: colourspace.c:1466
boolean resize_layer(weed_layer_t *layer, int width, int height, LiVESInterpType interp, int opal_hint, int oclamp_hint)
resize a layer
double get_luma16(uint16_t r, uint16_t g, uint16_t b)
Definition: colourspace.c:557
LIVES_GLOBAL_INLINE int weed_layer_get_width_pixels(weed_layer_t *layer)
LIVES_GLOBAL_INLINE int weed_layer_get_palette(weed_layer_t *layer)
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
#define WEED_GAMMA_MONITOR
Definition: colourspace.h:253
frames_t virtual_to_images(int sfileno, frames_t sframe, frames_t eframe, boolean update_progress, LiVESPixbuf **pbr)
Definition: cvirtual.c:719
boolean is_virtual_frame(int sfileno, frames_t frame)
Definition: cvirtual.c:1063
boolean do_yesno_dialog(const char *text)
Definition: dialogs.c:655
boolean do_header_write_error(int clip)
Definition: dialogs.c:4169
LIVES_GLOBAL_INLINE void do_mt_undo_buf_error(void)
Definition: dialogs.c:3587
LiVESResponseType do_read_failed_error_s_with_retry(const char *fname, const char *errtext)
Definition: dialogs.c:4122
void do_threaded_dialog(const char *trans_text, boolean has_cancel)
Definition: dialogs.c:3849
LIVES_GLOBAL_INLINE void do_mt_no_jack_error(int warn_mask)
Definition: dialogs.c:3619
LIVES_GLOBAL_INLINE boolean do_mt_rect_prompt(void)
Definition: dialogs.c:3627
LiVESResponseType do_write_failed_error_s_with_retry(const char *fname, const char *errtext)
Definition: dialogs.c:4058
LIVES_GLOBAL_INLINE void do_mt_audchan_error(int warn_mask)
Definition: dialogs.c:3606
LIVES_GLOBAL_INLINE void do_mt_no_audchan_error(void)
Definition: dialogs.c:3614
void end_threaded_dialog(void)
Definition: dialogs.c:3883
LIVES_GLOBAL_INLINE boolean do_event_list_warning(void)
Definition: dialogs.c:3707
LIVES_GLOBAL_INLINE void do_mt_backup_space_error(lives_mt *mt, int memreq_mb)
Definition: dialogs.c:3560
LIVES_GLOBAL_INLINE void do_after_crash_warning(void)
Definition: dialogs.c:3742
LiVESResponseType handle_backend_errors(boolean can_retry)
Definition: dialogs.c:922
boolean do_mt_lb_warn(boolean lb)
Definition: dialogs.c:3451
LIVES_GLOBAL_INLINE void do_mt_undo_mem_error(void)
Definition: dialogs.c:3579
LIVES_GLOBAL_INLINE void do_bad_layout_error(void)
Definition: dialogs.c:3635
void do_read_failed_error_s(const char *s, const char *addinfo)
Definition: dialogs.c:4034
LIVES_GLOBAL_INLINE void do_layout_ascrap_file_error(void)
Definition: dialogs.c:3059
LIVES_GLOBAL_INLINE LiVESResponseType do_error_dialog(const char *text)
Definition: dialogs.c:749
void threaded_dialog_spin(double fraction)
Definition: dialogs.c:3823
boolean check_storage_space(int clipno, boolean is_processing)
Definition: dialogs.c:1086
boolean do_yesno_dialog_with_check(const char *text, uint64_t warn_mask_number)
Definition: dialogs.c:595
LIVES_GLOBAL_INLINE void do_layout_scrap_file_error(void)
Definition: dialogs.c:3053
char * make_weed_hashname(int filter_idx, boolean fullname, boolean use_extra_authors, char sep, boolean subs)
return value should be freed after use
int rte_get_numfilters(void)
int enabled_out_channels(weed_plant_t *plant, boolean count_repeats)
boolean has_video_chans_in(weed_plant_t *filter, boolean count_opt)
Definition: effects-weed.c:620
void weed_generator_end(weed_plant_t *inst)
boolean is_perchannel_multiw(weed_plant_t *param)
boolean has_perchannel_multiw(weed_plant_t *filter)
weed_plant_t * rte_keymode_get_instance(int key, int mode)
returns refcounted filter_instance bound to key/mode (or NULL)
weed_plant_t * weed_plant_deserialise(int fd, unsigned char **mem, weed_plant_t *plant)
char * weed_filter_idx_get_name(int idx, boolean add_subcats, boolean add_notes)
void deinit_render_effects(void)
switch off effects after render preview during rendering/render preview, we use the "keys" FX_KEYS_MA...
int weed_get_idx_for_hashname(const char *hashname, boolean fullname)
fullname includes author and version
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...
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...
size_t weed_plant_serialise(int fd, weed_plant_t *plant, unsigned char **mem)
int num_in_params(weed_plant_t *plant, boolean skip_hidden, boolean skip_internal)
boolean is_audio_channel_in(weed_plant_t *inst, int chnum)
Definition: effects-weed.c:577
void backup_weed_instances(void)
for multitrack
Definition: effects-weed.c:410
weed_plant_t ** weed_params_create(weed_plant_t *filter, boolean in)
int enabled_in_channels(weed_plant_t *plant, boolean count_repeats)
boolean has_non_alpha_palette(weed_plant_t *ctmpl, weed_plant_t *filter)
Definition: effects-weed.c:143
void restore_weed_instances(void)
Definition: effects-weed.c:423
char * weed_filter_idx_get_package_name(int idx)
boolean has_video_chans_out(weed_plant_t *filter, boolean count_opt)
Definition: effects-weed.c:676
LIVES_GLOBAL_INLINE int weed_instance_unref(weed_plant_t *inst)
weed_plant_t * get_weed_filter(int idx)
int get_transition_param(weed_plant_t *filter, boolean skip_internal)
weed_plant_t * weed_inst_in_param(weed_plant_t *inst, int param_num, boolean skip_hidden, boolean skip_internal)
LIVES_GLOBAL_INLINE int weed_get_sorted_filter(int i)
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
weed_plant_t * weed_instance_from_filter(weed_plant_t *filter)
lives_filter_error_t weed_reinit_effect(weed_plant_t *inst, boolean reinit_compound)
#define WEED_LEAF_HOST_TAG
Definition: effects-weed.h:66
#define WEED_LEAF_HOST_DEFAULT
Definition: effects-weed.h:62
#define WEED_LEAF_HOST_MENU_HIDE
Definition: effects-weed.h:61
char * lives_fx_cat_to_text(lives_fx_cat_t cat, boolean plural)
Definition: effects.c:40
@ LIVES_FX_CAT_COMPOSITOR
Definition: effects.h:28
@ LIVES_FX_CAT_VIDEO_EFFECT
Definition: effects.h:25
@ LIVES_FX_CAT_TRANSITION
Definition: effects.h:20
@ LIVES_FX_CAT_AUDIO_TRANSITION
Definition: effects.h:23
@ LIVES_FX_CAT_AUDIO_EFFECT
Definition: effects.h:26
@ LIVES_FX_CAT_EFFECT
Definition: effects.h:24
@ LIVES_FX_CAT_VIDEO_TRANSITION
Definition: effects.h:22
@ LIVES_FX_CAT_AV_TRANSITION
Definition: effects.h:21
void remove_audio_for_track(weed_plant_t *event, int track)
Definition: events.c:1349
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
double get_audio_frame_seek(weed_plant_t *event, int track)
returns velocity for track (track==-1 is backing audio)
Definition: events.c:187
boolean has_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc, weed_plant_t **shortcut)
Definition: events.c:129
void insert_filter_init_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event)
Definition: events.c:1021
void add_init_event_to_filter_map(weed_plant_t *fmap, weed_plant_t *event, void **hints)
Definition: events.c:1731
void add_track_to_avol_init(weed_plant_t *filter, weed_plant_t *event, int nbtracks, boolean behind)
Definition: events.c:2435
boolean init_event_is_process_last(weed_plant_t *event)
Definition: events.c:1718
weed_plant_t * append_filter_map_event(weed_plant_t *event_list, weed_timecode_t tc, void **init_events)
Definition: events.c:2968
void backup_host_tags(weed_plant_t *event_list, weed_timecode_t curr_tc)
Definition: events.c:1590
void insert_filter_deinit_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event)
Definition: events.c:1043
weed_plant_t * get_last_frame_event(weed_plant_t *event_list)
Definition: events.c:419
void delete_event(weed_plant_t *event_list, weed_plant_t *event)
Definition: events.c:311
void unlink_event(weed_plant_t *event_list, weed_plant_t *event)
Definition: events.c:297
weed_plant_t * get_prev_frame_event(weed_plant_t *event)
Definition: events.c:368
double event_list_get_end_secs(weed_plant_t *event_list)
Definition: events.c:4607
void ** get_init_events_before(weed_plant_t *event, weed_plant_t *init_event, boolean add)
Definition: events.c:936
void insert_param_change_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event)
Definition: events.c:1116
boolean move_event_right(weed_plant_t *event_list, weed_plant_t *event, boolean can_stay, double fps)
Definition: events.c:2030
boolean render_to_clip(boolean new_clip, boolean transcode)
rendering
Definition: events.c:4635
boolean filter_init_has_owner(weed_plant_t *init_event, int track)
Definition: events.c:1570
LIVES_GLOBAL_INLINE weed_timecode_t weed_event_get_timecode(weed_event_t *event)
Definition: events.c:89
frames_t get_frame_event_frame(weed_plant_t *event, int layer)
Definition: events.c:224
int get_frame_event_clip(weed_plant_t *event, int layer)
Definition: events.c:209
LIVES_GLOBAL_INLINE boolean init_event_in_list(void **init_events, int num_inits, weed_plant_t *event)
Definition: events.c:1550
render_details * create_render_details(int type)
Definition: events.c:6252
LIVES_GLOBAL_INLINE weed_plant_t * get_prev_event(weed_plant_t *event)
Definition: events.c:109
weed_plant_t * process_events(weed_plant_t *next_event, boolean process_audio, weed_timecode_t curr_tc)
Definition: events.c:3082
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
void insert_audio_event_at(weed_plant_t *event, int track, int clipnum, double seek, double vel)
Definition: events.c:1243
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
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
LIVES_GLOBAL_INLINE weed_plant_t * get_first_event(weed_plant_t *event_list)
Definition: events.c:119
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
LIVES_GLOBAL_INLINE weed_timecode_t get_event_timecode(weed_plant_t *plant)
Definition: events.c:98
void remove_filter_from_event_list(weed_plant_t *event_list, weed_plant_t *init_event)
Definition: events.c:1480
void remove_frame_from_event(weed_plant_t *event_list, weed_plant_t *event, int track)
Definition: events.c:489
LIVES_GLOBAL_INLINE weed_plant_t * get_last_event(weed_plant_t *event_list)
Definition: events.c:124
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
boolean is_blank_frame(weed_plant_t *event, boolean count_audio)
Definition: events.c:523
LIVES_GLOBAL_INLINE int get_event_type(weed_plant_t *plant)
Definition: events.c:103
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
double get_audio_frame_vel(weed_plant_t *event, int track)
returns velocity for track (track==-1 is backing audio)
Definition: events.c:165
void event_list_free(weed_plant_t *event_list)
Definition: events.c:2313
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
weed_plant_t * get_filter_map_before(weed_plant_t *event, int ctrack, weed_plant_t *stop_event)
Definition: events.c:895
weed_plant_t * get_first_frame_event(weed_plant_t *event_list)
Definition: events.c:404
weed_plant_t * get_filter_map_after(weed_plant_t *event, int ctrack)
Definition: events.c:821
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
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
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
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
boolean filter_map_after_frame(weed_plant_t *fmap)
Definition: events.c:803
LIVES_GLOBAL_INLINE weed_plant_t * get_next_event(weed_plant_t *event)
Definition: events.c:114
weed_plant_t * get_next_frame_event(weed_plant_t *event)
Definition: events.c:356
boolean init_event_is_relevant(weed_plant_t *init_event, int ctrack)
Definition: events.c:859
LIVES_GLOBAL_INLINE weed_timecode_t weed_event_set_timecode(weed_event_t *event, weed_timecode_t tc)
Definition: events.c:83
void update_filter_maps(weed_plant_t *event, weed_plant_t *end_event, weed_plant_t *init_event)
Definition: events.c:1009
void event_list_add_track(weed_plant_t *event_list, int layer)
Definition: events.c:2497
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
weed_event_t * lives_event_list_new(weed_event_t *elist, const char *cdate)
lib-ish stuff
Definition: events.c:240
weed_timecode_t event_list_get_end_tc(weed_plant_t *event_list)
Definition: events.c:4601
void restore_host_tags(weed_plant_t *event_list, weed_timecode_t curr_tc)
Definition: events.c:1605
LIVES_GLOBAL_INLINE int weed_frame_event_get_audio_tracks(weed_event_t *event, int **clips, double **seeks)
Definition: events.c:59
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
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
LiVESWidget * create_event_list_dialog(weed_plant_t *event_list, weed_timecode_t start_tc, weed_timecode_t end_tc)
Definition: events.c:5582
boolean move_event_left(weed_plant_t *event_list, weed_plant_t *event, boolean can_stay, double fps)
Definition: events.c:2103
void remove_end_blank_frames(weed_plant_t *event_list, boolean remove_filter_inits)
Definition: events.c:546
#define WEED_LEAF_HOST_AUDIO_TRANSITION
Definition: events.h:86
#define WEED_LEAF_FIRST
Definition: events.h:64
#define WEED_LEAF_AUDIO_VOLUME_VALUES
Definition: events.h:23
#define WEED_LEAF_NEXT
Definition: events.h:62
#define WEED_LEAF_TRACKS
Definition: events.h:80
#define WEED_LEAF_AUDIO_VOLUME_TRACKS
Definition: events.h:22
#define EVENT_MARKER_RECORD_START
Definition: events.h:355
#define WEED_EVENT_IS_FRAME(event)
Definition: events.h:361
#define WEED_LEAF_GAMMA_ENABLED
Definition: events.h:67
#define WEED_LEAF_LAST
Definition: events.h:65
#define WEED_LEAF_IS_DEF_VALUE
Definition: events.h:73
#define WEED_LEAF_INIT_EVENTS
Definition: events.h:55
#define WEED_LEAF_PREV_CHANGE
Definition: events.h:72
#define WEED_LEAF_AUDIO_SEEKS
Definition: events.h:41
#define WEED_LEAF_DEINIT_EVENT
Definition: events.h:76
#define WEED_LEAF_NEEDS_SET
Definition: events.h:66
#define WEED_EVENT_IS_AUDIO_FRAME(event)
Definition: events.h:362
#define WEED_LEAF_NEXT_CHANGE
Definition: events.h:71
#define WEED_EVENT_IS_FILTER_DEINIT(event)
Definition: events.h:365
#define WEED_LEAF_AUDIO_CLIPS
Definition: events.h:40
#define WEED_EVENT_IS_MARKER(event)
Definition: events.h:368
#define EVENT_MARKER_RECORD_END
Definition: events.h:356
#define WEED_LEAF_INIT_EVENT
Definition: events.h:52
#define WEED_LEAF_WEED_EVENT_API_VERSION
parts of this may eventually become libweed-events
Definition: events.h:18
#define WEED_LEAF_PTRSIZE
deprecated
Definition: events.h:84
#define WEED_LEAF_AUDIO_SAMPLE_SIZE
Definition: events.h:21
#define WEED_LEAF_TRACK_LABEL_TRACKS
Definition: events.h:24
render_details * rdet
Definition: events.h:256
#define WEED_LEAF_PREVIOUS
Definition: events.h:63
#define WEED_LEAF_OUT_COUNT
Definition: events.h:46
#define WEED_EVENT_IS_FILTER_INIT(event)
Definition: events.h:364
#define WEED_LEAF_EVENT_ID
Definition: events.h:49
#define WEED_LEAF_FILTER
Definition: events.h:44
#define WEED_LEAF_IN_COUNT
Definition: events.h:45
#define WEED_LEAF_IN_TRACKS
Definition: events.h:47
#define WEED_LEAF_LIVES_TYPE
Definition: events.h:79
#define WEED_LEAF_AUDIO_SIGNED
Definition: events.h:19
#define WEED_PLANT_IS_EVENT_LIST(plant)
Definition: events.h:359
weed_plant_t weed_event_t
Definition: events.h:97
#define WEED_LEAF_TRACK_LABEL_VALUES
Definition: events.h:25
#define WEED_LEAF_HINT
for backwards compat.
Definition: events.h:13
#define LIVES_TRACK_ANY
Definition: events.h:92
#define WEED_LEAF_INDEX
Definition: events.h:58
#define WEED_LEAF_AUDIO_ENDIAN
Definition: events.h:20
#define WEED_EVENT_IS_PARAM_CHANGE(event)
Definition: events.h:367
#define EVENT_MARKER_BLOCK_START
Definition: events.h:353
#define WEED_LEAF_OUT_TRACKS
Definition: events.h:48
#define WEED_EVENT_IS_FILTER_MAP(event)
Definition: events.h:366
#define WEED_LEAF_FRAMES
Definition: events.h:38
#define EVENT_MARKER_BLOCK_UNORDERED
Definition: events.h:354
#define WEED_LEAF_CLIPS
Definition: events.h:39
boolean on_framedraw_enter(LiVESWidget *widget, LiVESXEventCrossing *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:819
boolean on_framedraw_mouse_start(LiVESWidget *widget, LiVESXEventButton *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:871
boolean on_framedraw_scroll(LiVESWidget *widget, LiVESXEventScroll *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:1263
boolean on_framedraw_mouse_update(LiVESWidget *widget, LiVESXEventMotion *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:989
boolean on_framedraw_mouse_reset(LiVESWidget *widget, LiVESXEventButton *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:1156
boolean on_framedraw_leave(LiVESWidget *widget, LiVESXEventCrossing *event, lives_special_framedraw_rect_t *framedraw)
Definition: framedraw.c:855
void show_lives(void)
Definition: gui.c:3006
void set_interactive(boolean interactive)
Definition: gui.c:3072
void add_to_playframe(void)
Definition: gui.c:4451
void resize_play_window(void)
Definition: gui.c:4349
void make_play_window(void)
actually in gui.c
Definition: gui.c:3932
void add_to_clipmenu(void)
Definition: gui.c:4512
char * get_menu_name(lives_clip_t *sfile, boolean add_setname)
Definition: gui.c:4487
char * choose_file(const char *dir, const char *fname, char **const filt, LiVESFileChooserAction act, const char *title, LiVESWidget *extra_widget)
Definition: interface.c:4080
LiVESWidget * create_cdtrack_dialog(int type, livespointer user_data)
Definition: interface.c:3138
boolean redraw_tl_idle(void *data)
Definition: interface.c:3457
lives_clipinfo_t * create_clip_info_window(int audio_channels, boolean is_mt)
Definition: interface.c:1048
_entryw * create_rename_dialog(int type)
Definition: interface.c:2792
LIVES_GLOBAL_INLINE LiVESWidget * make_autoreload_check(LiVESHBox *hbox, boolean is_active)
Definition: interface.c:4342
boolean reshow_msg_area(LiVESWidget *widget, lives_painter_t *cr, livespointer psurf)
Definition: interface.c:7242
boolean on_msg_area_scroll(LiVESWidget *widget, LiVESXEventScroll *event, livespointer user_data)
Definition: interface.c:7298
_entryw * create_cds_dialog(int type)
Definition: interface.c:4348
void do_mt_keys_window(void)
Definition: interface.c:4813
void msg_area_scroll(LiVESAdjustment *adj, livespointer userdata)
Definition: interface.c:7284
void run_diskspace_dialog_cb(LiVESWidget *w, livespointer data)
Definition: interface.c:5748
#define LIVES_PREVIEW_TYPE_VIDEO_ONLY
Definition: interface.h:171
_entryw * renamew
Definition: interface.h:311
void on_open_fw_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: ldvgrab.c:300
#define CAM_FORMAT_HDV
Definition: ldvgrab.h:15
#define CAM_FORMAT_DV
Definition: ldvgrab.h:14
error("LSD_RANDFUNC(ptr, size) must be defined")
LIVES_GLOBAL_INLINE int lives_getgid(void)
char * lives_datetime(uint64_t secs, boolean use_local)
Definition: machinestate.c:860
LIVES_GLOBAL_INLINE uint64_t lives_random(void)
Definition: machinestate.c:58
off_t sget_file_size(const char *name)
Definition: machinestate.c:962
LIVES_GLOBAL_INLINE ticks_t lives_get_current_ticks(void)
Definition: machinestate.c:835
LIVES_GLOBAL_INLINE int lives_strappend(const char *string, int len, const char *xnew)
LIVES_GLOBAL_INLINE int lives_getuid(void)
boolean compress_files_in_dir(const char *dir, int method, void *data)
Definition: machinestate.c:898
uint64_t gen_unique_id(void)
Definition: machinestate.c:68
LIVES_GLOBAL_INLINE boolean lives_strncmp(const char *st1, const char *st2, size_t len)
returns FALSE if strings match
void * main_thread_execute(lives_funcptr_t func, int return_type, void *retval, const char *args_fmt,...)
#define lives_calloc
Definition: machinestate.h:67
#define THREADVAR(var)
Definition: machinestate.h:531
#define lives_free
Definition: machinestate.h:52
#define lives_memset
Definition: machinestate.h:61
#define lives_malloc
Definition: machinestate.h:46
#define lives_memcpy
Definition: machinestate.h:55
#define PRIu64
Definition: machinestate.h:170
void *(* lives_funcptr_t)(void *)
Definition: machinestate.h:378
#define PRId64
Definition: machinestate.h:169
#define lives_memmove
Definition: machinestate.h:64
void defer_sigint(int signum)
Definition: main.c:282
LIVES_GLOBAL_INLINE void free_track_decoders(void)
Definition: main.c:7826
void load_end_image(int frame)
Definition: main.c:5922
void set_drawing_area_from_pixbuf(LiVESWidget *widget, LiVESPixbuf *pixbuf, lives_painter_surface_t *surface)
Definition: main.c:5525
void sensitize(void)
Definition: main.c:5078
void load_start_image(int frame)
Definition: main.c:5650
_palette * palette
interface colour settings
Definition: main.c:101
boolean resize_message_area(livespointer data)
Definition: main.c:3588
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
void catch_sigint(int signum)
Definition: main.c:296
ssize_t sizdbl
Definition: main.c:102
void set_signal_handlers(SignalHandlerPointer sigfunc)
Definition: main.c:4077
void resize(double scale)
Definition: main.c:10230
void init_track_decoders(void)
Definition: main.c:7816
ssize_t sizint
type sizes
Definition: main.c:102
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
void break_me(const char *brkstr)
Definition: main.c:159
boolean check_layer_ready(weed_layer_t *layer)
block until layer pixel_data is ready.
Definition: main.c:7528
mainwindow * mainw
Definition: main.c:103
void desensitize(void)
Definition: main.c:5302
void switch_to_file(int old_file, int new_file)
Definition: main.c:9646
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
ssize_t lives_read_buffered(int fd, void *buf, ssize_t count, boolean allow_less)
Definition: utils.c:924
#define LIVES_WARN(x)
Definition: main.h:1862
int lives_close_buffered(int fd)
Definition: utils.c:716
int frames_t
Definition: main.h:99
#define IMG_TYPE_BEST
Definition: main.h:781
void get_basename(char *filename)
Definition: utils.c:3194
ssize_t lives_popen(const char *com, boolean allow_error, char *buff, ssize_t buflen)
Definition: utils.c:194
void(* SignalHandlerPointer)(int)
Definition: main.h:1464
void lives_list_free_all(LiVESList **)
Definition: utils.c:4873
size_t get_token_count(const char *string, int delim)
Definition: utils.c:5430
#define MAX_FILES
max files is actually 1 more than this, since file 0 is the clipboard
Definition: main.h:184
void d_print_file_error_failed(void)
Definition: utils.c:2625
double calc_time_from_frame(int clip, int frame)
Definition: utils.c:1756
#define LIVES_DEBUG(x)
Definition: main.h:1848
int lives_touch(const char *tfile)
Definition: utils.c:4455
#define LIVES_GLOBAL_INLINE
Definition: main.h:239
boolean check_for_ratio_fps(double fps)
Definition: utils.c:5361
void maybe_add_mt_idlefunc(void)
ssize_t lives_write_buffered(int fd, const char *buf, ssize_t count, boolean allow_fail)
Definition: utils.c:1226
#define DEF_FILE_PERMS
non-executable, is modified by the umask
Definition: main.h:209
#define LIVES_IS_PLAYING
Definition: main.h:840
#define IS_NORMAL_CLIP(clip)
Definition: main.h:833
void close_scrap_file(boolean remove)
Definition: saveplay.c:5583
void close_ascrap_file(boolean remove)
Definition: saveplay.c:5612
int lives_create_buffered(const char *pathname, int mode)
Definition: utils.c:698
#define LIVES_INLINE
Definition: main.h:238
ssize_t lives_read_le_buffered(int fd, void *buf, ssize_t count, boolean allow_less)
Definition: utils.c:1158
#define CURRENT_CLIP_HAS_AUDIO
Definition: main.h:818
#define ulong
Definition: main.h:178
#define LIVES_LOCAL_INLINE
Definition: main.h:246
int lives_system(const char *com, boolean allow_error)
Definition: utils.c:145
void set_undoable(const char *what, boolean sensitive)
Definition: utils.c:4784
char * repl_workdir(const char *entry, boolean fwd)
Definition: utils.c:3534
#define N_RECENT_FILES
Definition: main.h:657
ssize_t lives_write_le_buffered(int fd, livesconstpointer buf, ssize_t count, boolean allow_fail)
boolean get_new_handle(int index, const char *name)
Definition: saveplay.c:3821
#define AFORM_UNSIGNED
Definition: main.h:786
int count_resampled_frames(int in_frames, double orig_fps, double resampled_fps)
Definition: resample.c:72
const char * get_image_ext_for_type(lives_img_type_t imgtype)
Definition: utils.c:3025
LiVESInterpType get_interp_value(short quality, boolean low_for_mt)
Definition: utils.c:5744
void remove_layout_files(LiVESList *lmap)
Definition: utils.c:3559
#define FPS_MAX
maximum fps we will allow (double)
Definition: main.h:218
int lives_open_buffered_rdonly(const char *pathname)
Definition: utils.c:636
int lives_mv(const char *from, const char *to)
Definition: utils.c:4446
boolean save_clip_values(int which_file)
Definition: saveplay.c:103
@ CLIP_DETAILS_UNIQUE_ID
Definition: main.h:1147
void set_sel_label(LiVESWidget *label)
Definition: utils.c:4838
lives_direction_t
use REVERSE / FORWARD when a sign is used, BACKWARD / FORWARD when a parity is used
Definition: main.h:851
@ LIVES_DIRECTION_FORWARD
Definition: main.h:854
@ LIVES_DIRECTION_BACKWARD
Definition: main.h:853
int lives_rmdir(const char *dir, boolean force)
Definition: utils.c:4366
#define IS_VALID_CLIP(clip)
Definition: main.h:808
boolean int_array_contains_value(int *array, int num_elems, int value)
Definition: utils.c:4284
int lives_rm(const char *file)
Definition: utils.c:4395
int64_t ticks_t
Definition: main.h:97
void d_print_failed(void)
Definition: utils.c:2615
capability * capable
Definition: main.h:627
#define cfile
Definition: main.h:1833
int lives_open2(const char *pathname, int flags)
Definition: utils.c:99
void reset_clipmenu(void)
Definition: utils.c:4290
int lives_list_strcmp_index(LiVESList *list, livesconstpointer data, boolean case_sensitive)
Definition: utils.c:4678
void d_print(const char *fmt,...)
Definition: utils.c:2542
#define LIVES_ERROR(x)
Definition: main.h:1870
@ IMG_TYPE_JPEG
Definition: main.h:776
void add_to_recovery_file(const char *handle)
Definition: saveplay.c:6460
void lives_buffered_rdonly_slurp(int fd, off_t skip)
Definition: utils.c:671
LiVESList * lives_list_append_unique(LiVESList *xlist, const char *add)
Definition: utils.c:5776
#define AFORM_BIG_ENDIAN
Definition: main.h:787
#define CURRENT_CLIP_IS_VALID
Definition: main.h:809
char * ensure_extension(const char *fname, const char *ext) WARN_UNUSED
Definition: utils.c:3232
void calc_maxspect(int rwidth, int rheight, int *cwidth, int *cheight)
Definition: utils.c:2174
void d_print_done(void)
Definition: utils.c:2620
#define PATH_MAX
Definition: main.h:255
#define LIVES_DIR_SEP
Definition: main.h:197
boolean lives_freep(void **ptr)
Definition: utils.c:1411
boolean is_legal_set_name(const char *set_name, boolean allow_dupes, boolean leeway)
Definition: utils.c:2975
@ CLIP_TYPE_FILE
unimported video, not or partially broken in frames
Definition: main.h:765
@ CLIP_TYPE_GENERATOR
frames from generator plugin
Definition: main.h:766
double lives_fix(double val, int decimals) GNU_CONST
Definition: utils.c:1446
char * get_dir(const char *filename)
Definition: utils.c:3185
boolean save_clip_value(int which, lives_clip_details_t, void *val)
Definition: utils.c:5175
#define CLIP_TOTAL_TIME(clip)
Definition: main.h:830
@ CANCEL_NO_PROPOGATE
cancel but keep opening
Definition: main.h:707
@ CANCEL_NONE
no cancel
Definition: main.h:701
@ CANCEL_NO_MORE_PREVIEW
ran out of preview frames
Definition: main.h:716
@ CANCEL_VID_END
video playback completed
Definition: main.h:725
@ CANCEL_USER
user pressed stop
Definition: main.h:704
@ CANCEL_USER_PAUSED
cancelled and paused
Definition: main.h:746
int calc_frame_from_time(int filenum, double time)
nearest frame [1, frames]
Definition: utils.c:1759
#define MAINW_MSG_SIZE
mainw->msg bytesize
Definition: mainwindow.h:702
#define STYLE_3
style is lightish - allow themeing of widgets with dark text, otherwise use menu bg
Definition: mainwindow.h:301
#define DEF_BUTTON_HEIGHT
Definition: mainwindow.h:183
#define LAYOUTS_DIRNAME
Definition: mainwindow.h:619
#define WORKDIR_LITERAL
Definition: mainwindow.h:540
#define MAIN_SPIN_SPACER
pixel spacing for start/end spins for clip and multitrack editors
Definition: mainwindow.h:164
#define MIN_MSGBAR_HEIGHT
Definition: mainwindow.h:135
#define LAYOUT_FILENAME
Definition: mainwindow.h:570
#define TICKS_PER_SECOND
ticks per second - GLOBAL TIMEBASE
Definition: mainwindow.h:36
#define LIVES_IS_INTERACTIVE
Definition: mainwindow.h:1710
#define UNREC_LAYOUTS_DIR
Definition: mainwindow.h:587
#define LIVES_MAIN_WINDOW_WIDGET
Definition: mainwindow.h:188
#define DEF_IDLE_MAX
Definition: mainwindow.h:1740
@ LIVES_DEVICE_INTERNAL
Definition: mainwindow.h:263
#define STYLE_4
separator col. in mt
Definition: mainwindow.h:302
#define TICKS_PER_SECOND_DBL
actually microseconds / 100.
Definition: mainwindow.h:37
@ LIVES_STRING_CONSTANT_NONE
Definition: mainwindow.h:371
@ LIVES_STRING_CONSTANT_CL
"the current layout"
Definition: mainwindow.h:374
#define LIVES_SENSE_STATE_INTERACTIVE
Definition: mainwindow.h:1708
#define LIVES_FILE_EXT_LAYOUT
Definition: mainwindow.h:513
#define DEF_BUTTON_WIDTH
Definition: mainwindow.h:182
#define MENU_HIDE_LIM
Definition: mainwindow.h:86
#define STYLE_LIGHT
Definition: mainwindow.h:304
#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
#define MAX_SET_NAME_LEN
sets
Definition: mainwindow.h:748
#define GUI_SCREEN_WIDTH
Definition: mainwindow.h:99
#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
#define STYLE_1
turn on theming if set
Definition: mainwindow.h:299
_fx_dialog * fx_dialog[2]
Definition: mainwindow.h:1851
#define STYLE_2
colour the spinbuttons on the front page if set
Definition: mainwindow.h:300
#define LIVES_SENSE_STATE_INSENSITIZED
Definition: mainwindow.h:1705
#define LAYOUT_MAP_FILENAME
Definition: mainwindow.h:571
#define LAYOUT_NUMBERING_FILENAME
Definition: mainwindow.h:572
#define LIVES_SENSE_STATE_SENSITIZED
Definition: mainwindow.h:1707
#define COMBOWIDTHCHARS
char width of combo entries (default)
Definition: mainwindow.h:74
#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
#define FN_KEYS
number of function keys
Definition: mainwindow.h:195
#define LIVES_FILENAME_NOREMOVE
Definition: mainwindow.h:577
#define GUI_SCREEN_HEIGHT
Definition: mainwindow.h:100
boolean mt_track_is_audio(lives_mt *mt, int ntrack)
return TRUE if ntrack is a valid backing audio track
Definition: multitrack.c:23048
void unpaint_line(lives_mt *mt, LiVESWidget *eventbox)
Definition: multitrack.c:14130
int mt_get_block_count(lives_mt *mt, int ntrack)
count blocks in track
Definition: multitrack.c:23065
void mt_load_vals_toggled(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22340
void mt_set_autotrans(int idx)
Definition: multitrack.c:6080
char * get_eload_filename(lives_mt *mt, boolean allow_auto_reload)
Definition: multitrack.c:21623
boolean mt_mark_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:18883
void on_split_sel_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16632
LIVES_LOCAL_INLINE void free_pkg_list(void)
Definition: multitrack.c:4043
void on_amixer_close_clicked(LiVESButton *button, lives_mt *mt)
Definition: multitrack.c:22536
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
void mt_tl_move(lives_mt *mt, double pos)
Definition: multitrack.c:3595
boolean on_track_between_release(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13958
void mt_clip_select(lives_mt *mt, boolean scroll)
Definition: multitrack.c:3024
void mt_clear_timeline(lives_mt *mt)
Definition: multitrack.c:10742
void mt_spin_start_value_changed(LiVESSpinButton *spinbutton, livespointer user_data)
Definition: multitrack.c:4452
void mt_backup(lives_mt *mt, int undo_type, weed_timecode_t tc)
Definition: multitrack.c:5200
boolean on_track_click(LiVESWidget *eventbox, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13969
boolean mt_auto_backup(livespointer user_data)
Definition: multitrack.c:863
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
void remove_gaps(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14648
void on_clear_event_list_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:21971
LiVESWidget * get_eventbox_for_track(lives_mt *mt, int ntrack)
Definition: multitrack.c:22981
LIVES_INLINE int poly_page_to_tab(uint32_t page)
Definition: multitrack.c:3770
void on_set_pvals_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19430
LIVES_INLINE int poly_tab_to_page(uint32_t tab)
Definition: multitrack.c:3775
void on_next_node_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19207
void clear_context(lives_mt *mt)
Definition: multitrack.c:11693
void multitrack_view_sel_events(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:18137
LIVES_INLINE int mt_file_from_clip(lives_mt *mt, int clip)
Definition: multitrack.c:216
boolean mt_track_is_video(lives_mt *mt, int ntrack)
return TRUE if ntrack is a valid video track
Definition: multitrack.c:23042
boolean resize_timeline(lives_mt *mt)
Definition: multitrack.c:11721
void multitrack_preview_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:17250
weed_plant_t * get_prev_fm(lives_mt *mt, int current_track, weed_plant_t *event)
Definition: multitrack.c:18784
boolean multitrack_insert(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:17480
boolean all_present(weed_plant_t *event, LiVESList *sel)
Definition: multitrack.c:18498
void show_clipinfo_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14273
void mt_fixup_events(lives_mt *mt, weed_plant_t *old_event, weed_plant_t *new_event)
Definition: multitrack.c:19321
uint32_t mt_idle_add(lives_mt *mt)
Definition: multitrack.c:901
boolean show_in_out_images(livespointer user_data)
Definition: multitrack.c:11878
LIVES_LOCAL_INLINE int pkg_in_list(char *pkgstring)
Definition: multitrack.c:4028
boolean on_track_header_move(LiVESWidget *widget, LiVESXEventMotion *event, livespointer user_data)
Definition: multitrack.c:14121
track_rect * find_block_by_uid(lives_mt *mt, ulong uid)
Definition: multitrack.c:23012
void on_mt_showkeys_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22976
void reset_renumbering(void)
Definition: multitrack.c:9646
void on_fx_insa_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:18907
void on_split_curr_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16591
int add_video_track_behind(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:10523
void multitrack_view_in_out(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:2850
void on_jumpback_mark_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16025
void mt_change_vals_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22352
void mt_desensitise(lives_mt *mt)
Definition: multitrack.c:16979
boolean multitrack_audio_insert(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:17632
void do_fx_list_context(lives_mt *mt, int fxcount)
Definition: multitrack.c:18603
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
void set_timeline_end_secs(lives_mt *mt, double secs)
Definition: multitrack.c:3134
boolean mt_tlfor_frame(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3615
void mt_add_region_effect(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15670
void recover_layout_cancelled(boolean is_startup)
Definition: multitrack.c:923
void on_next_fm_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:18950
void multitrack_playall(lives_mt *mt)
Definition: multitrack.c:17345
void remove_first_gaps(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14640
void selblock_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14306
void unpaint_lines(lives_mt *mt)
Definition: multitrack.c:14150
void stored_event_list_free_undos(void)
Definition: multitrack.c:5842
boolean recover_layout(void)
Definition: multitrack.c:1010
void mt_delete_clips(lives_mt *mt, int file)
Definition: multitrack.c:10800
void activate_mt_preview(lives_mt *mt)
sensitize Show Preview and Apply buttons
Definition: multitrack.c:19400
#define POLY_WIDTH_MARGIN
Definition: multitrack.c:12775
boolean mt_trup(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3748
void insert_at_ctx_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14251
void mt_add_block_effect(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15787
void list_fx_here_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14313
void polymorph(lives_mt *mt, lives_mt_poly_state_t poly)
Definition: multitrack.c:12777
void on_prev_fm_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:18933
void save_layout_map(int *lmap, double *lmap_audio, const char *file, const char *dir)
Definition: multitrack.c:19714
boolean on_track_release(LiVESWidget *eventbox, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13773
boolean on_render_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16078
void track_select(lives_mt *mt)
must call after setting mt->current_track
Definition: multitrack.c:1941
void mt_do_autotransition(lives_mt *mt, track_rect *block)
call this on a block to apply autotransition on it
Definition: multitrack.c:23119
void wipe_layout(lives_mt *mt)
Definition: multitrack.c:21936
LIVES_INLINE boolean is_empty_track(LiVESWidgetObject *track)
Definition: multitrack.c:236
void add_context_label(lives_mt *mt, const char *text)
Definition: multitrack.c:11698
void out_anchor_toggled(LiVESToggleButton *togglebutton, livespointer user_data)
Definition: multitrack.c:12729
void draw_region(lives_mt *mt)
Definition: multitrack.c:18159
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
void mt_sensitise(lives_mt *mt)
Definition: multitrack.c:17052
void insert_here_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14244
void in_out_end_changed(LiVESWidget *widget, livespointer user_data)
Definition: multitrack.c:12174
void mt_post_playback(lives_mt *mt)
Definition: multitrack.c:17284
LIVES_LOCAL_INLINE char * get_pkg_name(int pkgnum)
Definition: multitrack.c:4038
void mt_prepare_for_playback(lives_mt *mt)
Definition: multitrack.c:17259
void update_insert_mode(lives_mt *mt)
Definition: multitrack.c:4946
void set_mt_play_sizes_cfg(lives_mt *mt)
Definition: multitrack.c:5516
void do_fx_move_context(lives_mt *mt)
Definition: multitrack.c:18615
lives_mt * multitrack(weed_plant_t *event_list, int orig_file, double fps)
create and return lives_mt struct
Definition: multitrack.c:6448
void mt_change_max_disp_tracks(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22330
void show_frame_events_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22325
track_rect * move_block(lives_mt *mt, track_rect *block, double timesecs, int old_track, int new_track)
Definition: multitrack.c:11417
void multitrack_play_sel(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:17449
void set_mt_colours(lives_mt *mt)
Definition: multitrack.c:6148
void mt_spin_end_value_changed(LiVESSpinButton *spinbutton, livespointer user_data)
Definition: multitrack.c:4506
void set_poly_tab(lives_mt *mt, uint32_t tab)
Definition: multitrack.c:3962
EXPOSE_FN_END boolean on_timeline_update(LiVESWidget *widget, LiVESXEventMotion *event, livespointer user_data)
Definition: multitrack.c:18460
boolean on_track_move(LiVESWidget *widget, LiVESXEventMotion *event, livespointer user_data)
Definition: multitrack.c:14112
LIVES_GLOBAL_INLINE double mt_get_effect_time(lives_mt *mt)
Definition: multitrack.c:1735
void set_mixer_track_vol(lives_mt *mt, int trackno, double vol)
Definition: multitrack.c:246
void mt_selection_lock(LiVESMenuItem *menuitem, livespointer user_data)
lock the time selection
Definition: multitrack.c:16863
boolean save_event_list_inner(lives_mt *mt, int fd, weed_plant_t *event_list, unsigned char **mem)
Definition: multitrack.c:252
boolean on_timeline_press(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:18739
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
void on_amixer_slider_changed(LiVESAdjustment *adj, lives_mt *mt)
Definition: multitrack.c:22610
void on_jumpnext_mark_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16031
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
void on_jumpnext_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16001
LiVESWidget * amixer_add_channel_slider(lives_mt *mt, int i)
Definition: multitrack.c:22694
boolean on_track_header_release(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13936
boolean event_list_rectify(lives_mt *mt, weed_plant_t *event_list)
Definition: multitrack.c:20713
boolean used_in_current_layout(lives_mt *mt, int file)
Definition: multitrack.c:9180
void amixer_show(LiVESButton *button, livespointer user_data)
Definition: multitrack.c:22757
boolean multitrack_delete(lives_mt *mt, boolean save_layout)
Definition: multitrack.c:9204
void multitrack_undo(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15073
boolean mt_idle_show_current_frame(livespointer data)
Definition: multitrack.c:11015
LIVES_INLINE int mt_clip_from_file(lives_mt *mt, int file)
Definition: multitrack.c:221
LiVESPixbuf * make_thumb(lives_mt *mt, int file, int width, int height, frames_t frame, LiVESInterpType interp, boolean noblanks)
Definition: multitrack.c:405
void on_insgap_sel_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14982
weed_plant_t * load_event_list(lives_mt *mt, char *eload_file)
Definition: multitrack.c:21693
void multitrack_redo(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15298
void multitrack_adj_start_end(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:17473
void get_region_overlap(lives_mt *mt)
Definition: multitrack.c:18527
void on_mt_fx_edit_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:10543
LiVESList * layout_audio_is_affected(int clipno, double stime, double etime, LiVESList *xlays)
Definition: multitrack.c:22281
void delete_block_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14299
void on_seltrack_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16871
boolean mt_prevclip(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3096
int get_track_for_block(track_rect *block)
return track number for a given block
Definition: multitrack.c:231
boolean on_track_header_click(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13925
boolean on_save_event_list_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:19990
void on_seltrack_toggled(LiVESWidget *checkbutton, livespointer user_data)
Definition: multitrack.c:16962
void migrate_layouts(const char *old_set_name, const char *new_set_name)
Definition: multitrack.c:22115
void mt_swap_play_pause(lives_mt *mt, boolean put_pause)
Definition: multitrack.c:17206
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
void on_jumpback_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15995
void multitrack_view_details(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15489
void on_prev_node_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19221
void delete_audio_track(lives_mt *mt, LiVESWidget *eventbox, boolean full)
Definition: multitrack.c:8993
LIVES_INLINE void set_params_unchanged(lives_mt *mt, lives_rfx_t *rfx)
Definition: multitrack.c:1057
void on_mt_delfx_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15849
boolean on_load_event_list_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:22045
void delete_audio_tracks(lives_mt *mt, LiVESList *list, boolean full)
Definition: multitrack.c:5986
boolean on_track_between_click(LiVESWidget *widget, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:13947
void mt_change_disp_tracks_ok(LiVESButton *button, livespointer user_data)
Definition: multitrack.c:22314
void multitrack_end_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14237
void set_track_label_string(lives_mt *mt, int track, const char *label)
Definition: multitrack.c:10366
void scroll_track_by_scrollbar(LiVESScrollbar *sbar, livespointer user_data)
Definition: multitrack.c:3650
boolean on_timeline_release(LiVESWidget *eventbox, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:18622
void avel_spin_changed(LiVESSpinButton *spinbutton, livespointer user_data)
Definition: multitrack.c:12548
void add_aparam_menuitems(lives_mt *mt)
Definition: multitrack.c:5308
void add_markers(lives_mt *mt, weed_plant_t *event_list, boolean add_block_ids)
Definition: multitrack.c:19877
boolean set_new_set_name(lives_mt *mt)
Definition: multitrack.c:19944
double get_mixer_track_vol(lives_mt *mt, int trackno)
Definition: multitrack.c:240
void do_sel_context(lives_mt *mt)
Definition: multitrack.c:18583
void on_cback_audio_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16071
#define LIVES_AVOL_SCALE
void on_del_node_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19235
void on_insgap_cur_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15029
LIVES_INLINE lives_mt_poly_state_t get_poly_state_from_page(lives_mt *mt)
Definition: multitrack.c:3780
char * set_values_from_defs(lives_mt *mt, boolean from_prefs)
Definition: multitrack.c:5768
LiVESList * load_layout_map(void)
Definition: multitrack.c:19584
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
void remove_markers(weed_plant_t *event_list)
Definition: multitrack.c:21918
void insert_audio_at_ctx_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14291
void edit_start_end_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14259
LIVES_INLINE void print_layout_wiped(void)
Definition: multitrack.c:5921
void multitrack_view_events(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:18122
void do_effect_context(lives_mt *mt, LiVESXEventButton *event)
Definition: multitrack.c:10573
void on_fx_insb_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:18920
void mt_zoom_in(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:4419
void mt_quit_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:5996
boolean make_backup_space(lives_mt *mt, size_t space_needed)
Definition: multitrack.c:5157
void multitrack_view_clips(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:2844
void insert_audio_here_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14284
void on_resetp_clicked(LiVESWidget *button, livespointer user_data)
Definition: multitrack.c:19140
void do_block_context(lives_mt *mt, LiVESXEventButton *event, track_rect *block)
Definition: multitrack.c:13619
void stored_event_list_free_all(boolean wiped)
Definition: multitrack.c:5897
boolean check_for_layout_del(lives_mt *mt, boolean exiting)
Definition: multitrack.c:5924
void mt_zoom_out(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:4429
void free_thumb_cache(int fnum, frames_t fromframe)
Definition: multitrack.c:1148
weed_plant_t * get_next_fm(lives_mt *mt, int current_track, weed_plant_t *event)
Definition: multitrack.c:18838
void do_track_context(lives_mt *mt, LiVESXEventButton *event, double timesecs, int track)
Definition: multitrack.c:13695
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
char * get_track_name(lives_mt *mt, int track_num, boolean is_audio)
Definition: multitrack.c:1038
ulong mt_get_last_block_uid(lives_mt *mt)
get index of last inserted (wallclock time) block for track
Definition: multitrack.c:23054
LiVESPixbuf * make_thumb_fast_between(lives_mt *mt, int fileno, int width, int height, int tframe, int range)
Definition: multitrack.c:489
LIVES_INLINE void mt_tl_move_relative(lives_mt *mt, double pos_rel)
Definition: multitrack.c:3600
LiVESWidget * add_audio_track(lives_mt *mt, int track, boolean behind)
Definition: multitrack.c:10166
void in_out_start_changed(LiVESWidget *widget, livespointer user_data)
Definition: multitrack.c:11897
void scroll_tracks(lives_mt *mt, int top_track, boolean set_value)
Definition: multitrack.c:2347
void on_prerender_aud_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16358
void delete_video_track(lives_mt *mt, int layer, boolean full)
Definition: multitrack.c:10121
void ** mt_get_pchain(void)
Definition: multitrack.c:1033
boolean add_mt_param_box(lives_mt *mt)
Definition: multitrack.c:1740
void mt_aparam_view_toggled(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:5289
LiVESList * layout_frame_is_affected(int clipno, int start, int end, LiVESList *xlays)
Definition: multitrack.c:22247
void unselect_all(lives_mt *mt)
unselect all blocks
Definition: multitrack.c:11620
void on_mt_list_fx_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:15842
LIVES_INLINE double get_time_from_x(lives_mt *mt, int x)
Definition: multitrack.c:1048
#define WEED_AUDIO_BIG_ENDIAN
Definition: multitrack.c:94
void close_clip_cb(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:14266
void mt_init_start_end_spins(lives_mt *mt)
Definition: multitrack.c:4844
LIVES_LOCAL_INLINE void reset_mt_play_sizes(lives_mt *mt)
Definition: multitrack.c:518
boolean mt_tcoverlay_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:18897
void in_anchor_toggled(LiVESToggleButton *togglebutton, livespointer user_data)
Definition: multitrack.c:12683
boolean track_arrow_pressed(LiVESWidget *ebox, LiVESXEventButton *event, livespointer user_data)
Definition: multitrack.c:2807
void animate_multitrack(lives_mt *mt)
Definition: multitrack.c:14223
void remove_current_from_affected_layouts(lives_mt *mt)
Definition: multitrack.c:5851
void mouse_mode_context(lives_mt *mt)
Definition: multitrack.c:4916
LIVES_LOCAL_INLINE int add_to_pkg_list(char *pkgstring)
Definition: multitrack.c:4033
void mt_center_on_cursor(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:4412
boolean mt_tlback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3625
void on_split_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16552
int add_video_track_front(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:10533
void on_node_spin_value_changed(LiVESSpinButton *spinbutton, livespointer user_data)
Definition: multitrack.c:19024
boolean block_overlap(LiVESWidget *eventbox, double time_start, double time_end)
Definition: multitrack.c:11369
boolean on_multitrack_activate(LiVESMenuItem *menuitem, weed_plant_t *event_list)
menuitem callback
Definition: multitrack.c:11024
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
void mt_show_current_frame(lives_mt *mt, boolean return_layer)
preview the current frame
Definition: multitrack.c:3214
void * find_init_event_in_ttable(ttable *trans_table, uint64_t in, boolean normal)
Definition: multitrack.c:20253
void update_grav_mode(lives_mt *mt)
Definition: multitrack.c:5012
boolean mt_trdown(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3710
boolean mt_load_recovery_layout(lives_mt *mt)
Definition: multitrack.c:941
boolean mt_tlfor(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3605
#define WEED_AUDIO_LITTLE_ENDIAN
Definition: multitrack.c:93
void event_list_free_undos(lives_mt *mt)
Definition: multitrack.c:5827
void mt_init_clips(lives_mt *mt, int orig_file, boolean add)
Definition: multitrack.c:10859
void on_rename_track_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: multitrack.c:16037
boolean mt_tlback_frame(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3634
int get_clip_for_block(track_rect *block)
Definition: multitrack.c:23107
boolean write_backup_layout_numbering(lives_mt *mt)
Definition: multitrack.c:668
void avel_reverse_toggled(LiVESToggleButton *togglebutton, livespointer user_data)
Definition: multitrack.c:12500
boolean mt_nextclip(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod, livespointer user_data)
Definition: multitrack.c:3107
#define MT_INOUT_TIME
min milliseconds to save autobackup when changing in / out spins
Definition: multitrack.h:49
#define TRACK_I_HIDDEN_SCROLLED
Definition: multitrack.h:711
#define QUANT_TIME(dbltime)
Definition: multitrack.h:1049
#define FX_BLOCK_HEIGHT
Definition: multitrack.h:31
lives_mt_insert_mode_t
Definition: multitrack.h:75
@ INSERT_MODE_NORMAL
the default (only insert if it fits)
Definition: multitrack.h:76
@ INSERT_MODE_OVERWRITE
overwite existing blocks
Definition: multitrack.h:79
@ MT_LAST_FX_BLOCK
Definition: multitrack.h:134
@ MT_LAST_FX_REGION
Definition: multitrack.h:135
@ MT_LAST_FX_NONE
Definition: multitrack.h:133
@ FX_ORD_AFTER
Definition: multitrack.h:141
@ FX_ORD_BEFORE
Definition: multitrack.h:140
@ FX_ORD_NONE
Definition: multitrack.h:139
#define AMIXER_WRATIO
audio mixer width ratio (fraction of screen width)
Definition: multitrack.h:44
#define MAX_TRACKS
Definition: multitrack.h:1044
#define TIMELINE_TABLE_COLUMNS
Definition: multitrack.h:37
#define MAX_AUDIO_TRACKS
Definition: multitrack.h:1046
#define QUANT_TICKS(ticks)
Definition: multitrack.h:1052
#define BLOCK_DRAW_SIMPLE
Definition: multitrack.h:51
#define MAX_VIDEO_TRACKS
Definition: multitrack.h:1045
#define AMIXER_HRATIO
audio mixer height ratio (fraction of screen height)
Definition: multitrack.h:45
lives_mt_undo_t
Definition: multitrack.h:90
@ MT_UNDO_DELETE_FILTER
Definition: multitrack.h:99
@ MT_UNDO_INSERT_AUDIO_BLOCK
Definition: multitrack.h:95
@ MT_UNDO_SPLIT
Definition: multitrack.h:100
@ MT_UNDO_MOVE_AUDIO_BLOCK
Definition: multitrack.h:109
@ MT_UNDO_INSERT_GAP
Definition: multitrack.h:110
@ MT_UNDO_DELETE_BLOCK
Definition: multitrack.h:105
@ MT_UNDO_FILTER_MAP_CHANGE
Definition: multitrack.h:102
@ MT_UNDO_SPLIT_MULTI
Definition: multitrack.h:101
@ MT_UNDO_DELETE_AUDIO_BLOCK
Definition: multitrack.h:108
@ MT_UNDO_NONE
no event_list
Definition: multitrack.h:93
@ MT_UNDO_MOVE_BLOCK
Definition: multitrack.h:106
@ MT_UNDO_APPLY_FILTER
Definition: multitrack.h:98
@ MT_UNDO_REMOVE_GAPS
Definition: multitrack.h:107
@ MT_UNDO_INSERT_BLOCK
Definition: multitrack.h:94
#define BLOCK_DRAW_TYPE
Definition: multitrack.h:53
@ BLOCK_UNSELECTED
Definition: multitrack.h:146
@ BLOCK_SELECTED
Definition: multitrack.h:147
#define CLIP_LABEL_LENGTH
Definition: multitrack.h:17
#define MT_TRACK_HEIGHT
Definition: multitrack.h:33
#define PEB_HRATIO
preview eventbox height ratio (fraction of screen height)
Definition: multitrack.h:42
@ GRAV_MODE_RIGHT
Definition: multitrack.h:87
@ GRAV_MODE_LEFT
Definition: multitrack.h:86
@ GRAV_MODE_NORMAL
Definition: multitrack.h:85
@ MOUSE_MODE_SELECT
Definition: multitrack.h:71
@ MOUSE_MODE_MOVE
Definition: multitrack.h:70
#define BLOCK_THUMB_WIDTH
Definition: multitrack.h:19
#define FX_BLOCK_WIDTH
Definition: multitrack.h:30
#define CLIP_THUMB_HEIGHT
Definition: multitrack.h:15
#define PEB_WRATIO
preview eventbox width ratio (fraction of screen width)
Definition: multitrack.h:41
#define TIMECODE_LENGTH
length of timecode text entry, must be > 12
Definition: multitrack.h:35
#define MENUBAR_MIN
Definition: multitrack.h:39
lives_mt_nb_error_t
Definition: multitrack.h:113
@ NB_ERROR_NOTRANS
Definition: multitrack.h:116
@ NB_ERROR_NOCLIP
Definition: multitrack.h:118
@ NB_ERROR_NOEFFECT
Definition: multitrack.h:115
@ NB_ERROR_SEL
Definition: multitrack.h:114
@ NB_ERROR_NOCOMP
Definition: multitrack.h:117
#define DEF_TIME
default seconds when there is no event_list
Definition: multitrack.h:57
#define SELBLOCK_ALPHA
Definition: multitrack.h:55
lives_mt_poly_state_t
Definition: multitrack.h:121
@ POLY_CLIPS
Definition: multitrack.h:123
@ POLY_TRANS
Definition: multitrack.h:128
@ POLY_FX_STACK
Definition: multitrack.h:125
@ POLY_IN_OUT
Definition: multitrack.h:124
@ POLY_EFFECTS
Definition: multitrack.h:127
@ POLY_NONE
Definition: multitrack.h:122
@ POLY_PARAMS
Definition: multitrack.h:126
@ POLY_COMP
Definition: multitrack.h:129
#define CLIP_THUMB_WIDTH
Definition: multitrack.h:14
#define TRACK_I_HIDDEN_USER
Definition: multitrack.h:710
#define LIVES_OSC_NOTIFY_MODE_CHANGED
mode changed to clip editor or to multitrack
Definition: osc_notify.h:56
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
@ LIVES_TEXT_MODE_FOREGROUND_ONLY
Definition: pangotext.h:50
boolean check_filewrite_overwrites(void)
Definition: paramspecial.c:617
boolean special_cleanup(boolean is_ok)
Definition: paramspecial.c:641
LiVESPixbuf * mt_framedraw(lives_mt *mt, weed_layer_t *layer)
Definition: paramspecial.c:741
void on_paramwindow_button_clicked(LiVESButton *button, lives_rfx_t *rfx)
Definition: paramwindow.c:90
void update_visual_params(lives_rfx_t *rfx, boolean update_hidden)
apply internal value changes to interface widgets
Definition: paramwindow.c:3361
void after_param_text_changed(LiVESWidget *textwidget, lives_rfx_t *rfx)
Definition: paramwindow.c:2635
boolean make_param_box(LiVESVBox *top_vbox, lives_rfx_t *rfx)
make a dynamic parameter window
Definition: paramwindow.c:1015
void rfx_free(lives_rfx_t *rfx)
Definition: plugins.c:2987
boolean check_encoder_restrictions(boolean get_extension, boolean user_audio, boolean save_all)
Definition: plugins.c:1557
lives_rfx_t * weed_to_rfx(weed_plant_t *plant, boolean show_reinits)
Definition: plugins.c:3564
@ LIVES_PARAM_NUM
Definition: plugins.h:502
#define FX_CANDIDATE_AUDIO_VOL
Definition: plugins.h:694
#define LIVES_SEEK_FAST
good
Definition: plugins.h:312
@ LIVES_RFX_SOURCE_WEED
Definition: plugins.h:514
#define HIDDEN_MULTI
Definition: plugins.h:552
#define RFX_FLAGS_NO_RESET
Definition: plugins.h:647
void on_preferences_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: preferences.c:5772
int set_boolean_pref(const char *key, boolean value)
Definition: preferences.c:354
int set_double_pref(const char *key, double value)
Definition: preferences.c:346
int set_string_pref(const char *key, const char *value)
Definition: preferences.c:290
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
boolean pref_factory_int(const char *prefidx, int newval, boolean permanent)
Definition: preferences.c:1053
boolean pref_factory_bool(const char *prefidx, boolean newval, boolean permanent)
Definition: preferences.c:717
int set_int_pref(const char *key, int value)
Definition: preferences.c:329
#define WARN_MASK_LAYOUT_WIPE
Definition: preferences.h:124
#define STARTUP_CE
Definition: preferences.h:338
#define JACK_OPTS_TIMEBASE_START
jack sets play start position
Definition: preferences.h:238
#define WARN_MASK_MT_NO_JACK
Definition: preferences.h:118
#define PREF_MAX_DISP_VTRACKS
Definition: preferences.h:983
#define WARN_MASK_MT_ACHANS
Definition: preferences.h:109
#define PREF_REC_EXT_AUDIO
Definition: preferences.h:892
#define PREF_MT_SHOW_CTX
Definition: preferences.h:1018
#define PREF_MT_DEF_FPS
Definition: preferences.h:1091
#define JACK_OPTS_TRANSPORT_CLIENT
jack can start/stop
Definition: preferences.h:233
_prefs * prefs
Definition: preferences.h:847
#define SEPWIN_TYPE_NON_STICKY
Definition: preferences.h:187
#define PREF_MT_BACKAUDIO
Definition: preferences.h:1017
#define PREF_SHOW_DEVOPTS
Definition: preferences.h:1067
#define PREF_MT_DEF_ASAMPS
Definition: preferences.h:1011
#define PREF_MT_DEF_ARATE
Definition: preferences.h:1009
#define PREF_MT_DEF_ACHANS
Definition: preferences.h:1010
#define WARN_MASK_LAYOUT_LB
Definition: preferences.h:128
#define PREF_MT_DEF_HEIGHT
Definition: preferences.h:1008
#define PREF_AR_LAYOUT
Definition: preferences.h:933
#define STARTUP_MT
Definition: preferences.h:339
#define PREF_MT_DEF_WIDTH
Definition: preferences.h:1007
#define SEPWIN_TYPE_STICKY
Definition: preferences.h:188
#define PREF_MT_DEF_SIGNED_ENDIAN
Definition: preferences.h:1012
#define WARN_MASK_EXIT_MT
off by default on a fresh install
Definition: preferences.h:106
#define PREF_MT_PERTRACK_AUDIO
Definition: preferences.h:1036
#define PREF_MT_ENTER_PROMPT
Definition: preferences.h:1033
_future_prefs * future_prefs
Definition: preferences.h:848
#define AUDIO_SRC_EXT
Definition: preferences.h:206
#define PREF_LETTERBOXMT
Definition: preferences.h:1070
#define PREF_ACTIVE_AUTOTRANS
Definition: preferences.h:936
#define PREF_SEPWIN_TYPE
Definition: preferences.h:894
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
LIVES_GLOBAL_INLINE ticks_t q_gint64(ticks_t in, double fps)
Definition: resample.c:25
LIVES_GLOBAL_INLINE ticks_t q_dbl(double in, double fps)
Definition: resample.c:41
_resaudw * resaudw
Definition: resample.h:38
void check_string_choice_params(weed_plant_t *inst)
Definition: rte_window.c:1774
boolean on_rtew_delete_event(LiVESWidget *widget, LiVESXEventDelete *event, livespointer user_data)
Definition: rte_window.c:1846
LiVESWidget * rte_window
Definition: rte_window.h:58
void explain_missing_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: startup.c:1397
void on_troubleshoot_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: startup.c:1362
LiVESWidget * entry
Definition: interface.h:95
LiVESWidget * dialog
Definition: interface.h:94
boolean letterbox_mt
Definition: preferences.h:844
short sepwin_type
Definition: preferences.h:832
lives_rfx_t * rfx
Definition: mainwindow.h:1843
LiVESWidgetColor mt_timecode_bg
Definition: mainwindow.h:332
LiVESWidgetColor normal_fore
Definition: mainwindow.h:325
int style
Definition: mainwindow.h:297
lives_colRGBA64_t mt_evbox
Definition: mainwindow.h:346
lives_colRGBA64_t mt_timeline_reg
Definition: mainwindow.h:342
LiVESWidgetColor menu_and_bars
Definition: mainwindow.h:327
lives_colRGBA64_t audcol
Definition: mainwindow.h:339
LiVESWidgetColor white
Definition: mainwindow.h:306
LiVESWidgetColor normal_back
Definition: mainwindow.h:324
LiVESWidgetColor black
Definition: mainwindow.h:307
LiVESWidgetColor dark_red
Definition: mainwindow.h:311
LiVESWidgetColor light_green
Definition: mainwindow.h:310
lives_colRGBA64_t mt_mark
Definition: mainwindow.h:345
LiVESWidgetColor menu_and_bars_fore
Definition: mainwindow.h:328
lives_colRGBA64_t fxcol
Definition: mainwindow.h:341
lives_colRGBA64_t vidcol
Definition: mainwindow.h:340
LiVESWidgetColor mt_timecode_fg
Definition: mainwindow.h:333
boolean vj_mode
Definition: preferences.h:459
int mt_def_arate
Definition: preferences.h:273
double mt_def_fps
Definition: preferences.h:271
int startup_interface
Definition: preferences.h:336
int mt_auto_back
time diff to backup (-1 == never, 0 == after every change, > 0 == seconds)
Definition: preferences.h:281
boolean crash_recovery
TRUE==maintain mainw->recovery file.
Definition: preferences.h:259
int audio_src
Definition: preferences.h:204
boolean event_window_show_frame_events
Definition: preferences.h:258
boolean interactive
Definition: preferences.h:478
uint64_t warning_mask
Definition: preferences.h:80
boolean mt_show_ctx
Definition: preferences.h:277
boolean show_gui
Definition: preferences.h:290
int mt_backaudio
Definition: preferences.h:279
boolean ar_layout
Definition: preferences.h:284
boolean letterbox_mt
playback with letterbox (multitrack)
Definition: preferences.h:363
boolean back_compat
Definition: preferences.h:471
char workdir[PATH_MAX]
kept in locale encoding
Definition: preferences.h:61
boolean show_msg_area
Definition: preferences.h:225
boolean mt_exit_render
Definition: preferences.h:275
int max_disp_vtracks
Definition: preferences.h:430
int mt_def_signed_endian
Definition: preferences.h:273
boolean mt_enter_prompt
Definition: preferences.h:268
int mt_def_achans
Definition: preferences.h:273
boolean unstable_fx
Definition: preferences.h:361
boolean force64bit
< force system clock (rather than soundcard) for timing ( better for high framerates )
Definition: preferences.h:368
uint32_t jack_opts
Definition: preferences.h:232
short sepwin_type
Definition: preferences.h:186
int atrans_fx
Definition: preferences.h:353
int gui_monitor
Definition: preferences.h:305
int sleep_time
Definition: preferences.h:176
char ar_layout_name[PATH_MAX]
locale
Definition: preferences.h:286
int mt_def_asamps
Definition: preferences.h:273
int mt_def_width
Definition: preferences.h:270
int mt_def_height
Definition: preferences.h:270
boolean show_dev_opts
Definition: preferences.h:463
boolean mt_pertrack_audio
Definition: preferences.h:278
boolean apply_gamma
Definition: preferences.h:451
boolean use_screen_gamma
Definition: preferences.h:452
char backend_sync[PATH_MAX *4]
Definition: preferences.h:410
int mt_undo_buf
Definition: preferences.h:267
short audio_player
Definition: preferences.h:40
short pb_quality
Definition: preferences.h:31
int msg_textsize
Definition: preferences.h:445
boolean open_maximised
Definition: preferences.h:28
boolean lamp_buttons
Definition: preferences.h:343
boolean autoload_subs
Definition: preferences.h:345
LiVESWidget * aud_checkbutton
Definition: resample.h:32
LiVESWidget * entry_asamps
Definition: resample.h:22
LiVESWidget * rb_unsigned
Definition: resample.h:24
LiVESWidget * rb_bigend
Definition: resample.h:25
LiVESWidget * entry_arate
Definition: resample.h:20
LiVESWidget * entry_achans
Definition: resample.h:21
pid_t mainpid
Definition: main.h:591
lives_checkstatus_t has_mplayer2
Definition: main.h:512
int dclick_dist
Definition: main.h:623
lives_checkstatus_t has_mplayer
Definition: main.h:511
mode_t umask
Definition: main.h:597
char * name
Definition: multitrack.h:748
char * handle
Definition: multitrack.h:746
int64_t unique_id
Definition: multitrack.h:747
LiVESList * list
Definition: multitrack.h:749
int seek_flag
plugin can change per frame
Definition: plugins.h:397
corresponds to one clip in the GUI
Definition: main.h:877
int arps
audio physical sample rate (i.e the "normal" sample rate of the clip when played at 1,...
Definition: main.h:905
frames_t frames
number of video frames
Definition: main.h:890
frames_t tcache_dubious_from
height for thumbnail cache (width is fixed, but if this changes, invalidate)
Definition: main.h:1099
frames_t end
Definition: main.h:891
void * ext_src
points to opaque source for non-disk types
Definition: main.h:1040
lives_clip_type_t clip_type
Definition: main.h:886
int asampsize
audio sample size in bits (8 or 16)
Definition: main.h:908
lives_img_type_t img_type
Definition: main.h:887
frames_t stored_layout_frame
experimental for player
Definition: main.h:1071
double stored_layout_audio
Definition: main.h:1073
int vsize
frame height (vertical) in pixels
Definition: main.h:897
int tcache_height
Definition: main.h:1098
int achans
number of audio channels (0, 1 or 2)
Definition: main.h:907
LiVESList * layout_map
Definition: main.h:1037
boolean ratio_fps
framerate of the clip
Definition: main.h:894
LiVESList * tcache
set by clip alterations, frames from here onwards should be freed
Definition: main.h:1100
double laudio_time
Definition: main.h:929
uint32_t signed_endian
bitfield
Definition: main.h:909
double pb_fps
current playback rate, may vary from fps, can be 0. or negative
Definition: main.h:1007
uint64_t unique_id
this and the handle can be used to uniquely id a file
Definition: main.h:880
boolean changed
Definition: main.h:915
char info_file[PATH_MAX]
used for asynch communication with externals
Definition: main.h:1009
int hsize
frame width (horizontal) in pixels (NOT macropixels !)
Definition: main.h:896
char name[CLIP_NAME_MAXLEN]
the display name
Definition: main.h:922
double stored_layout_fps
Definition: main.h:1074
int bpp
bits per pixel of the image frames, 24 or 32
Definition: main.h:901
int arate
current audio playback rate (varies if the clip rate is changed)
Definition: main.h:906
double raudio_time
Definition: main.h:929
int stored_layout_idx
M highest value used.
Definition: main.h:1072
lives_painter_surface_t * raudio_drawable
Definition: main.h:1084
double fps
Definition: main.h:893
weed_plant_t * next_event
Definition: main.h:1035
weed_plant_t * event_list
Definition: main.h:1033
char handle[256]
Definition: main.h:881
lives_painter_surface_t * laudio_drawable
Definition: main.h:1084
frames_t start
Definition: main.h:891
LiVESWidget * textview_lrate
Definition: interface.h:87
LiVESWidget * textview_fsize
Definition: interface.h:84
LiVESWidget * textview_rrate
Definition: interface.h:88
LiVESWidget * textview_type
Definition: interface.h:79
LiVESWidget * textview_fps
Definition: interface.h:80
LiVESWidget * textview_frames
Definition: interface.h:82
LiVESWidget * textview_vtime
Definition: interface.h:83
LiVESWidget * textview_size
Definition: interface.h:81
uint16_t alpha
Definition: main.h:326
uint16_t blue
Definition: main.h:325
uint16_t green
Definition: main.h:324
uint16_t red
Definition: main.h:323
int delegate
offset in list of current delegate
Definition: plugins.h:688
LiVESList * list
list of filter_idx from which user can delegate
Definition: plugins.h:687
LiVESXDevice * mouse_device
unused for gtk+ < 3.0.0
Definition: mainwindow.h:357
LiVESXDisplay * disp
Definition: mainwindow.h:358
lives_param_type_t type
Definition: plugins.h:573
char * name
Definition: plugins.h:540
boolean changed
Definition: plugins.h:597
uint32_t hidden
Definition: plugins.h:546
int num_params
Definition: plugins.h:644
lives_param_t * params
Definition: plugins.h:649
void * source
points to the source (e.g. a weed_plant_t)
Definition: plugins.h:651
Definition: main.h:1104
LiVESPixbuf * pixbuf
Definition: main.h:1107
frames_t frame
list of entries in clip thumbnail cache (for multitrack timeline)
Definition: main.h:1106
LiVESList * xlays
immediately (to be) affected layout maps
Definition: mainwindow.h:1477
LiVESWidget * m_sepwinbutton
Definition: mainwindow.h:1369
LiVESWidget * m_rewindbutton
Definition: mainwindow.h:1369
weed_plant_t * frame_layer
Definition: mainwindow.h:948
LiVESWidget * sepwin
Definition: mainwindow.h:1176
boolean stored_event_list_auto_changed
Definition: mainwindow.h:806
LiVESTextBuffer * layout_textbuffer
stores layout errors
Definition: mainwindow.h:1468
size_t sl_undo_buffer_used
Definition: mainwindow.h:811
LiVESWidget * recent_submenu
Definition: mainwindow.h:1128
uint32_t signal_caught
Definition: mainwindow.h:1673
char msg[MAINW_MSG_SIZE]
Definition: mainwindow.h:724
ulong fullscreen_cb_func
Definition: mainwindow.h:1073
volatile ticks_t currticks
wall clock time, updated whenever lives_get_*_ticks is called
Definition: mainwindow.h:1005
int last_dprint_file
message output settings
Definition: mainwindow.h:1535
char recent_file[PATH_MAX]
Definition: mainwindow.h:737
unsigned char * sl_undo_mem
Definition: mainwindow.h:812
LiVESWidget * vol_toolitem
Definition: mainwindow.h:1364
LiVESWidget * help_menu
Definition: mainwindow.h:1408
lives_clip_t * files[MAX_FILES+1]
+1 for the clipboard
Definition: mainwindow.h:729
LiVESWidget * top_vbox
Definition: mainwindow.h:1352
boolean block_param_updates
block visual param changes from updating real values
Definition: mainwindow.h:1550
int def_trans_idx
Definition: mainwindow.h:1747
boolean no_configs
Definition: mainwindow.h:1803
boolean is_rendering
Definition: mainwindow.h:821
LiVESWidget * recent[N_RECENT_FILES]
Definition: mainwindow.h:1129
lives_mgeometry_t * mgeom
multi-head support
Definition: mainwindow.h:1576
LiVESAccelGroup * accel_group
Definition: mainwindow.h:1228
boolean reconfig
set to TRUE if a monitor / screen size change is detected
Definition: mainwindow.h:1743
boolean go_away
Definition: mainwindow.h:1614
volatile boolean loop_cont
Definition: mainwindow.h:764
lives_painter_surface_t * raudio_drawable
Definition: mainwindow.h:1386
char * string_constants[NUM_LIVES_STRING_CONSTANTS]
Definition: mainwindow.h:1539
mt_opts multi_opts
some multitrack options that survive between mt calls
Definition: mainwindow.h:1492
boolean no_interp
block interpolation (for single frame previews)
Definition: mainwindow.h:1551
LiVESWidget * playframe
Definition: mainwindow.h:1098
LiVESList * cliplist
hash table of clips in menu order
Definition: mainwindow.h:743
LiVESWidget * volume_scale
Definition: mainwindow.h:1363
volatile lives_cancel_t cancelled
Definition: mainwindow.h:798
int sl_undo_offset
Definition: mainwindow.h:813
LiVESWidget * plug
Definition: mainwindow.h:1099
int current_file
Definition: mainwindow.h:727
boolean is_ready
Definition: mainwindow.h:787
LiVESPixbuf * imframe
Definition: mainwindow.h:1102
LiVESWidget * gens_menu
Definition: mainwindow.h:1413
ulong mute_audio_func
Definition: mainwindow.h:1072
LiVESWidget * message_box
Definition: mainwindow.h:1323
LiVESWidget * gens_submenu
Definition: mainwindow.h:1414
LiVESWidget * play_window
Definition: mainwindow.h:947
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
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
LiVESAdjustment * msg_adj
Definition: mainwindow.h:1326
volatile boolean is_exiting
set during shutdown (inverse of only_close then)
Definition: mainwindow.h:1440
ulong sepwin_cb_func
Definition: mainwindow.h:1074
boolean no_switch_dprint
Definition: mainwindow.h:1536
weed_plant_t * filter_map
Definition: mainwindow.h:1298
boolean fs
Definition: mainwindow.h:762
LiVESWidget * full_screen
Definition: mainwindow.h:1172
ticks_t last_display_ticks
Definition: mainwindow.h:1012
LiVESWidget * m_mutebutton
Definition: mainwindow.h:1370
uint32_t sense_state
Definition: mainwindow.h:1713
boolean recoverable_layout
Definition: mainwindow.h:1483
int pre_src_file
video file we were playing before any ext input started
Definition: mainwindow.h:971
boolean was_set
Definition: mainwindow.h:750
LiVESWidget * m_stopbutton
Definition: mainwindow.h:1369
double fx1_val
Definition: mainwindow.h:1049
LiVESWidget * textwidget_focus
Definition: mainwindow.h:1569
LiVESWidget * btoolbar
button toolbar - clip editor
Definition: mainwindow.h:1368
boolean error
Definition: mainwindow.h:801
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
boolean sep_win
Definition: mainwindow.h:761
LiVESWidget * m_loopbutton
Definition: mainwindow.h:1370
LiVESWidget * show_layout_errors
Definition: mainwindow.h:1225
weed_event_t * stored_event_list
stored mt -> clip editor
Definition: mainwindow.h:804
LiVESWidget * mute_audio
Definition: mainwindow.h:1177
boolean unordered_blocks
are we recording unordered blocks ?
Definition: mainwindow.h:1488
LiVESWidget * preview_box
Definition: mainwindow.h:1304
LiVESWidget * stop
Definition: mainwindow.h:1170
LiVESWidget * spinbutton_start
Definition: mainwindow.h:1288
LiVESList * current_layouts_map
map of all layouts for set
Definition: mainwindow.h:1470
LiVESWidget * vol_label
Definition: mainwindow.h:1365
boolean jack_can_stop
Definition: mainwindow.h:934
boolean recording_recovered
Definition: mainwindow.h:1486
int ascrap_file
scrap file for recording audio scraps
Definition: mainwindow.h:875
LiVESWidget * msg_area
Definition: mainwindow.h:1324
lives_mt * multitrack
holds a pointer to the entire multitrack environment; NULL in Clip Edit mode
Definition: mainwindow.h:1087
xprocess * proc_ptr
Definition: mainwindow.h:1090
LiVESList * stored_layout_undos
Definition: mainwindow.h:810
boolean stored_event_list_changed
Definition: mainwindow.h:805
lives_painter_surface_t * laudio_drawable
Definition: mainwindow.h:1386
ulong loop_cont_func
Definition: mainwindow.h:1071
boolean internal_messaging
internal fx
Definition: mainwindow.h:1043
boolean is_processing
states
Definition: mainwindow.h:820
char set_name[256]
Definition: mainwindow.h:749
lives_painter_surface_t * play_surface
Definition: mainwindow.h:950
int clipstore[FN_KEYS - 1][2]
stored clips (bookmarks) [0] = clip, [1] = frame
Definition: mainwindow.h:986
lives_painter_surface_t * msg_surface
Definition: mainwindow.h:1328
int first_free_file
Definition: mainwindow.h:728
LiVESWidget * m_playbutton
Definition: mainwindow.h:1369
LiVESWidget * play_image
Definition: mainwindow.h:946
weed_plant_t * afilter_map
Definition: mainwindow.h:1299
LiVESWidget * pl_eventbox
Definition: mainwindow.h:1100
char stored_layout_name[PATH_MAX]
Definition: mainwindow.h:808
LiVESList * affected_layouts_map
map of layouts with errors
Definition: mainwindow.h:1469
boolean mute
Definition: mainwindow.h:770
boolean mt_needs_idlefunc
set if we need to re-add the idlefunc for autobackup
Definition: mainwindow.h:1088
int playing_file
which number file we are playing (or -1) [generally mainw->current_file]
Definition: mainwindow.h:943
LiVESPixbuf * imsep
Definition: mainwindow.h:1104
LiVESWidget * msg_scrollbar
Definition: mainwindow.h:1325
boolean pretty_colours
Definition: mainwindow.h:1796
weed_event_t * event_list
current event_list, for recording
Definition: mainwindow.h:803
LiVESWidget * playarea
Definition: mainwindow.h:1321
LiVESWidget * loop_continue
Definition: mainwindow.h:1174
size_t data_len
including this mt_undo
Definition: multitrack.h:695
lives_mt_undo_t action
Definition: multitrack.h:692
ticks_t tc
Definition: multitrack.h:693
void * extra
Definition: multitrack.h:694
double fps
Definition: events.h:218
boolean enc_changed
Definition: events.h:244
LiVESWidget * okbutton
Definition: events.h:221
LiVESWidget * pertrack_checkbutton
Definition: events.h:231
LiVESWidget * dialog
Definition: events.h:220
LiVESWidget * backaudio_checkbutton
Definition: events.h:232
LiVESWidget * always_checkbutton
Definition: events.h:233
char * encoder_name
Definition: events.h:245
boolean suggestion_followed
Definition: events.h:246
void * out
Definition: multitrack.h:741
uint64_t in
Definition: multitrack.h:740
LiVESWidget * last_container
container which wraps last widget created + subwidgets (READONLY)
int icon_size
icon size for tooltips image, warn image, toolbar img, etc.
LiVESJustification justify
justify for labels
const char * text_size
specialised values /////
LiVESWidget * last_label
commonly adjusted values //////
double scale
scale factor for all sizes
int packing_height
vertical pixels between widgets
boolean mnemonic_label
if underscore in label text should be mnemonic accelerator
boolean pack_end
pack widget at end or start
int monitor
monitor we are displaying on
lives_expand_t expand
how much space to apply between widgets
int packing_width
horizontal pixels between widgets
boolean use_markup
whether markup should be used in labels
int border_width
border width in pixels
boolean line_wrap
line wrapping for labels
int apply_theme
theming variation for widget (0 -> no theme, 1 -> normal colours, 2+ -> theme variants)
LiVESWidget * preview_button
Definition: mainwindow.h:713
#define lives_strdup_printf(fmt,...)
Definition: support.c:27
#define _(String)
Definition: support.h:44
#define P_(String, StringPlural, n)
Definition: support.h:46
#define TRUE
Definition: videoplugin.h:59
#define FALSE
Definition: videoplugin.h:60
#define ABS(a)
Definition: videoplugin.h:63
WEED_GLOBAL_INLINE int weed_filter_hints_unstable(weed_plant_t *filter)
WEED_GLOBAL_INLINE weed_plant_t * weed_param_get_template(weed_plant_t *param)
WEED_GLOBAL_INLINE int weed_chantmpl_is_optional(weed_plant_t *chantmpl)
WEED_GLOBAL_INLINE int weed_paramtmpl_get_type(weed_plant_t *paramtmpl)
WEED_GLOBAL_INLINE weed_plant_t ** weed_filter_get_out_chantmpls(weed_plant_t *filter, int *ntmpls)
WEED_GLOBAL_INLINE int weed_param_is_hidden(weed_plant_t *param, int temporary)
WEED_GLOBAL_INLINE char * weed_filter_get_package_name(weed_plant_t *filter)
WEED_GLOBAL_INLINE weed_plant_t ** weed_filter_get_in_paramtmpls(weed_plant_t *filter, int *ntmpls)
WEED_GLOBAL_INLINE weed_plant_t ** weed_instance_get_in_params(weed_plant_t *instance, int *nparams)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_present(LiVESWindow *window)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_app_paintable(LiVESWidget *widget, boolean paintable)
WIDGET_HELPER_GLOBAL_INLINE double lives_ruler_set_value(LiVESRuler *ruler, double value)
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_range_get_adjustment(LiVESRange *range)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_remove(LiVESContainer *container, LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_window_new(LiVESWindowType wintype)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_remove_accel_group(LiVESWindow *window, LiVESAccelGroup *group)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_hexpand(LiVESWidget *widget, boolean state)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_label_set_mnemonic_widget(LiVESLabel *label, LiVESWidget *widget)
LiVESWidget * lives_menu_add_separator(LiVESMenu *menu)
void lives_set_cursor_style(lives_cursor_t cstyle, LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_set_row_homogeneous(LiVESTable *table, boolean homogeneous)
WIDGET_HELPER_GLOBAL_INLINE void lives_menu_item_set_text(LiVESWidget *menuitem, const char *text, boolean use_mnemonic)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_rectangle(lives_painter_t *cr, double x, double y, double width, double height)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_image_new_from_pixbuf(LiVESPixbuf *pixbuf)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toolbar_set_icon_size(LiVESToolbar *toolbar, LiVESIconSize icon_size)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_image_menu_item_set_image(LiVESImageMenuItem *item, LiVESWidget *image)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_toolbar_new(void)
WIDGET_HELPER_GLOBAL_INLINE LiVESXDisplay * lives_widget_get_display(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_range_set_range(LiVESRange *range, double min, double max)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_set_digits(LiVESSpinButton *button, uint32_t digits)
WIDGET_HELPER_GLOBAL_INLINE int lives_widget_get_allocation_width(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE int lives_event_get_time(LiVESXEvent *event)
WIDGET_HELPER_GLOBAL_INLINE void set_child_alt_colour(LiVESWidget *widget, boolean set_all)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_set_value(LiVESSpinButton *button, double value)
WIDGET_HELPER_GLOBAL_INLINE boolean menu_sets_visible(LiVESCheckMenuItem *mi, LiVESWidget *widget, boolean invert)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_range_set_inverted(LiVESRange *range, boolean invert)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_bg_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
WIDGET_HELPER_GLOBAL_INLINE lives_painter_surface_t * lives_painter_get_target(lives_painter_t *cr)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_shell_append(LiVESMenuShell *menushell, LiVESWidget *child)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_event_box_new(void)
LiVESToolItem * lives_standard_menu_tool_button_new(LiVESWidget *icon, const char *label)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_image_new_from_stock(const char *stock_id, LiVESIconSize size)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_range_set_value(LiVESRange *range, double value)
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_spin_button_get_adjustment(LiVESSpinButton *button)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_widget_set_tooltip_text(LiVESWidget *widget, const char *tip_text)
boolean label_act_toggle(LiVESWidget *widget, LiVESXEventButton *event, LiVESWidget *togglebutton)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_set_title(LiVESWindow *window, const char *title)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_shell_prepend(LiVESMenuShell *menushell, LiVESWidget *child)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_rgba(lives_painter_t *cr, double red, double green, double blue, double alpha)
WIDGET_HELPER_GLOBAL_INLINE LiVESScrollDirection lives_get_scroll_direction(LiVESXEventScroll *event)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_accel_group_connect(LiVESAccelGroup *group, uint32_t key, LiVESXModifierType mod, LiVESAccelFlags flags, LiVESWidgetClosure *closure)
LiVESWidget * lives_standard_hruler_new(void)
boolean lives_window_center(LiVESWindow *window)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_text_buffer_delete_mark(LiVESTextBuffer *tbuff, LiVESTextMark *mark)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toolbar_set_show_arrow(LiVESToolbar *toolbar, boolean show)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_fill(lives_painter_t *cr)
void lives_cool_toggled(LiVESWidget *tbutton, livespointer user_data)
LiVESWidget * lives_standard_image_menu_item_new_with_label(const char *label)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_is_visible(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_clip(lives_painter_t *cr)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_vbox_new(boolean homogeneous, int spacing)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_is_sensitive(LiVESWidget *widget)
boolean lives_spin_button_configure(LiVESSpinButton *spinbutton, double value, double lower, double upper, double step_increment, double page_increment)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_label_new(const char *text)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_notebook_set_tab_label(LiVESNotebook *nbook, LiVESWidget *child, LiVESWidget *tablabel)
void lives_tooltips_copy(LiVESWidget *dest, LiVESWidget *source)
WIDGET_HELPER_GLOBAL_INLINE LiVESAccelGroup * lives_accel_group_new(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_popup(LiVESMenu *menu, LiVESXEventButton *event)
LiVESWidget * lives_standard_entry_new(const char *labeltext, const char *txt, int dispwidth, int maxchars, LiVESBox *box, const char *tooltip)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_opacity(LiVESWidget *widget, double opacity)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_update(LiVESSpinButton *button)
int get_box_child_index(LiVESBox *box, LiVESWidget *tchild)
WIDGET_HELPER_GLOBAL_INLINE const char * lives_label_get_text(LiVESLabel *label)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_range_set_increments(LiVESRange *range, double step, double page)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_get_active(LiVESToggleButton *button)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_surface(lives_painter_t *cr, lives_painter_surface_t *surface, double x, double y)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_set_row_spacings(LiVESTable *table, uint32_t spacing)
WIDGET_HELPER_GLOBAL_INLINE LiVESPixbuf * lives_pixbuf_scale_simple(const LiVESPixbuf *src, int dest_width, int dest_height, LiVESInterpType interp_type)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_pixbuf(lives_painter_t *cr, const LiVESPixbuf *pixbuf, double pixbuf_x, double pixbuf_y)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scale_set_value_pos(LiVESScale *scale, LiVESPositionType ptype)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_object_ref_sink(livespointer object)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_image_new(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_ruler_set_range(LiVESRuler *ruler, double lower, double upper, double position, double max_size)
WIDGET_HELPER_GLOBAL_INLINE LiVESPixbuf * lives_pixbuf_new(boolean has_alpha, int width, int height)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_cursor_unref(LiVESXCursor *cursor)
WIDGET_HELPER_GLOBAL_INLINE LiVESResponseType lives_dialog_run(LiVESDialog *dialog)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_toolbar_insert_space(LiVESToolbar *bar)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_pack_start(LiVESBox *box, LiVESWidget *child, boolean expand, boolean fill, uint32_t padding)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_get_modmask(LiVESXDevice *device, LiVESWidget *widget, LiVESXModifierType *modmask)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_menu_new(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_toggle(LiVESToggleButton *tbutton)
WIDGET_HELPER_GLOBAL_INLINE boolean clear_widget_bg(LiVESWidget *widget, lives_painter_surface_t *s)
WIDGET_HELPER_GLOBAL_INLINE const char * lives_menu_item_get_text(LiVESWidget *menuitem)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_text_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_set_hide_titlebar_when_maximized(LiVESWindow *window, boolean setting)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_paned_pack(int where, LiVESPaned *paned, LiVESWidget *child, boolean resize, boolean shrink)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handler_unblock(livespointer instance, unsigned long handler_id)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_events(LiVESWidget *widget, int events)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_queue_draw(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_fg_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_queue_draw_area(LiVESWidget *widget, int x, int y, int width, int height)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_vscale_new(LiVESAdjustment *adj)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_xwindow_set_cursor(LiVESXWindow *xwin, LiVESXCursor *cursor)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_unparent(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_notebook_get_nth_page(LiVESNotebook *nbook, int pagenum)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_check_menu_item_get_active(LiVESCheckMenuItem *item)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_paned_set_position(LiVESPaned *paned, int pos)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_new_path(lives_painter_t *cr)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_set_col_spacings(LiVESTable *table, uint32_t spacing)
WIDGET_HELPER_GLOBAL_INLINE const char * lives_entry_get_text(LiVESEntry *entry)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_sensitive(LiVESWidget *widget, boolean state)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_surface_destroy(lives_painter_surface_t *surf)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handlers_unblock_by_func(livespointer instance, LiVESGuiCallback func, livespointer data)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_table_new(uint32_t rows, uint32_t cols, boolean homogeneous)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tool_button_set_icon_widget(LiVESToolButton *button, LiVESWidget *icon)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_set_mode(LiVESToggleButton *button, boolean drawind)
WIDGET_HELPER_GLOBAL_INLINE void lives_widget_object_set_data_widget_object(LiVESWidgetObject *obj, const char *key, livespointer other)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_label_set_text(LiVESLabel *label, const char *text)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handler_block(livespointer instance, unsigned long handler_id)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_add_events(LiVESWidget *widget, int events)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toolbar_insert(LiVESToolbar *toolbar, LiVESToolItem *item, int pos)
WIDGET_HELPER_GLOBAL_INLINE LiVESXCursor * lives_cursor_new_from_pixbuf(LiVESXDisplay *disp, LiVESPixbuf *pixbuf, int x, int y)
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_spin_button_set_adjustment(LiVESSpinButton *button, LiVESAdjustment *adj)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_object_unref(livespointer object)
decrease refcount by one: if refcount==0, object is destroyed
WIDGET_HELPER_GLOBAL_INLINE boolean lives_adjustment_set_value(LiVESAdjustment *adj, double value)
WIDGET_HELPER_GLOBAL_INLINE LiVESList * lives_container_get_children(LiVESContainer *cont)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_label_set_markup(LiVESLabel *label, const char *markup)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_queue_resize(LiVESWidget *widget)
LiVESWidget * lives_standard_hscale_new(LiVESAdjustment *adj)
LiVESWidget * lives_glowing_check_button_new(const char *labeltext, LiVESBox *box, const char *tooltip, boolean *togglevalue)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_set_border_width(LiVESContainer *container, uint32_t width)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_halign(LiVESWidget *widget, LiVESAlign align)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_check_menu_item_set_active(LiVESCheckMenuItem *item, boolean state)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_display_warp_pointer(LiVESXDevice *device, LiVESXDisplay *display, LiVESXScreen *screen, int x, int y)
LiVESWidget * lives_standard_notebook_new(const LiVESWidgetColor *bg_color, const LiVESWidgetColor *act_color)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_pack_top(LiVESBox *box, LiVESWidget *child, boolean expand, boolean fill, uint32_t padding)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_check_button_new_with_label(const char *label)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hbutton_box_new(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show_now(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show_all_from_bg(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_foreach(LiVESContainer *cont, LiVESWidgetCallback callback, livespointer cb_data)
WIDGET_HELPER_GLOBAL_INLINE LiVESIconSize lives_toolbar_get_icon_size(LiVESToolbar *toolbar)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_check_button_new(void)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_frame_new(const char *label)
WIDGET_HELPER_GLOBAL_INLINE int lives_notebook_get_current_page(LiVESNotebook *nbook)
LiVESWidget * add_hsep_to_box(LiVESBox *box)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_set_title(LiVESMenu *menu, const char *title)
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_scrolled_window_get_hadjustment(LiVESScrolledWindow *swindow)
LiVESWidget * add_fill_to_box(LiVESBox *box)
WIDGET_HELPER_GLOBAL_INLINE int lives_pixbuf_get_rowstride(const LiVESPixbuf *pixbuf)
WIDGET_HELPER_GLOBAL_INLINE double lives_adjustment_get_upper(LiVESAdjustment *adj)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_line_width(lives_painter_t *cr, double width)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_base_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
LiVESWidget * lives_standard_menu_item_new(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_paint(lives_painter_t *cr)
WIDGET_HELPER_GLOBAL_INLINE int lives_spin_button_get_value_as_int(LiVESSpinButton *button)
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_adjustment_new(double value, double lower, double upper, double step_increment, double page_increment, double page_size)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_surface_flush(lives_painter_surface_t *surf)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_adjustment_set_page_size(LiVESAdjustment *adj, double page_size)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_reparent(LiVESWidget *widget, LiVESWidget *new_parent)
boolean draw_cool_toggle(LiVESWidget *widget, lives_painter_t *cr, livespointer data)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_get_position(LiVESWidget *widget, int *x, int *y)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidgetColor * lives_rgba_to_widget_color(LiVESWidgetColor *color, lives_colRGBA64_t *lcolor)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_object_ref(livespointer object)
increase refcount by one
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_scrolled_window_new(LiVESAdjustment *hadj, LiVESAdjustment *vadj)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_get_pointer(LiVESXDevice *device, LiVESWidget *widget, int *x, int *y)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_vexpand(LiVESWidget *widget, boolean state)
void lives_widget_apply_theme2(LiVESWidget *widget, LiVESWidgetState state, boolean set_fg)
char * lives_big_and_bold(const char *fmt,...)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scrolled_window_set_policy(LiVESScrolledWindow *scrolledwindow, LiVESPolicyType hpolicy, LiVESPolicyType vpolicy)
LiVESWidget * lives_standard_drawing_area_new(LiVESGuiCallback callback, lives_painter_surface_t **ppsurf)
boolean lives_text_view_set_text(LiVESTextView *textview, const char *text, int len)
LIVES_GLOBAL_INLINE boolean lives_widget_destroy(LiVESWidget *widget)
LiVESWidget * lives_standard_label_new_with_mnemonic_widget(const char *text, LiVESWidget *mnemonic_widget)
LiVESWidget * lives_standard_scrolled_window_new(int width, int height, LiVESWidget *child)
LiVESWidget * lives_dialog_add_button_from_stock(LiVESDialog *dialog, const char *stock_id, const char *label, int response_id)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_add(LiVESContainer *container, LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_button_box_set_layout(LiVESButtonBox *bbox, LiVESButtonBoxStyle bstyle)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handlers_block_by_func(livespointer instance, LiVESGuiCallback func, livespointer data)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scale_set_digits(LiVESScale *scale, int digits)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_rgb(lives_painter_t *cr, double red, double green, double blue)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_no_show_all(LiVESWidget *widget, boolean set)
boolean set_css_value_direct(LiVESWidget *, LiVESWidgetState state, const char *selector, const char *detail, const char *value)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_detach(LiVESMenu *menu)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_set_active(LiVESToggleButton *button, boolean active)
WIDGET_HELPER_GLOBAL_INLINE double lives_spin_button_get_value(LiVESSpinButton *button)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_bin_get_child(LiVESBin *bin)
WIDGET_HELPER_GLOBAL_INLINE double lives_adjustment_get_page_size(LiVESAdjustment *adj)
WIDGET_HELPER_GLOBAL_INLINE lives_painter_t * lives_painter_create_from_surface(lives_painter_surface_t *target)
WIDGET_HELPER_GLOBAL_INLINE LiVESXWindow * lives_display_get_window_at_pointer(LiVESXDevice *device, LiVESXDisplay *display, int *win_x, int *win_y)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tool_button_set_use_underline(LiVESToolButton *button, boolean use_underline)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_arrow_new(LiVESArrowType arrow_type, LiVESShadowType shadow_type)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_hide(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_text_buffer_get_iter_at_mark(LiVESTextBuffer *tbuff, LiVESTextIter *iter, LiVESTextMark *mark)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_adjustment_clamp_page(LiVESAdjustment *adj, double lower, double upper)
LiVESWidget * lives_standard_image_menu_item_new_from_stock(const char *stock_id, LiVESAccelGroup *accel_group)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show_all(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_text_buffer_delete(LiVESTextBuffer *tbuff, LiVESTextIter *start, LiVESTextIter *end)
WIDGET_HELPER_GLOBAL_INLINE double lives_ruler_get_value(LiVESRuler *ruler)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scrolled_window_add_with_viewport(LiVESScrolledWindow *scrolledwindow, LiVESWidget *child)
WIDGET_HELPER_GLOBAL_INLINE double lives_range_get_value(LiVESRange *range)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidgetColor * lives_painter_set_source_rgb_from_lives_widget_color(lives_painter_t *cr, LiVESWidgetColor *wcol)
WIDGET_HELPER_GLOBAL_INLINE double lives_ruler_set_upper(LiVESRuler *ruler, double value)
LiVESWidget * lives_standard_frame_new(const char *labeltext, float xalign, boolean invis)
boolean lives_widget_context_update(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_move_to(lives_painter_t *cr, double x, double y)
WIDGET_HELPER_GLOBAL_INLINE lives_painter_surface_t * lives_widget_create_painter_surface(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE uint32_t lives_timer_add(uint32_t interval, LiVESWidgetSourceFunc function, livespointer data)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_can_focus(LiVESWidget *widget, boolean state)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_destroy(lives_painter_t *cr)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_widget_get_parent(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hseparator_new(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_margin_left(LiVESWidget *widget, int margin)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handlers_disconnect_by_func(livespointer instance, LiVESGuiCallback func, livespointer data)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hbox_new(boolean homogeneous, int spacing)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_display_get_pointer(LiVESXDevice *device, LiVESXDisplay *display, LiVESXScreen **screen, int *x, int *y, LiVESXModifierType *mask)
WIDGET_HELPER_GLOBAL_INLINE lives_colRGBA64_t * lives_painter_set_source_rgb_from_lives_rgba(lives_painter_t *cr, lives_colRGBA64_t *col)
WIDGET_HELPER_GLOBAL_INLINE boolean unhide_cursor(LiVESXWindow *window)
LiVESWidget * lives_standard_menu_item_new_with_label(const char *label)
boolean get_border_size(LiVESWidget *win, int *bx, int *by)
LiVESWidget * lives_standard_label_new(const char *text)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_tool_button_set_menu(LiVESMenuToolButton *toolbutton, LiVESWidget *menu)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_entry_set_width_chars(LiVESEntry *entry, int nchars)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_entry_set_text(LiVESEntry *entry, const char *text)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_scale_button_set_orientation(LiVESScaleButton *scale, LiVESOrientation orientation)
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)
WIDGET_HELPER_GLOBAL_INLINE unsigned char * lives_pixbuf_get_pixels(const LiVESPixbuf *pixbuf)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_valign(LiVESWidget *widget, LiVESAlign align)
LiVESWidget * add_spring_to_box(LiVESBox *box, int min)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_set_snap_to_ticks(LiVESSpinButton *button, boolean snap)
WIDGET_HELPER_GLOBAL_INLINE double lives_ruler_set_lower(LiVESRuler *ruler, double value)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_line_to(lives_painter_t *cr, double x, double y)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_set_transient_for(LiVESWindow *window, LiVESWindow *parent)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_operator(lives_painter_t *cr, lives_painter_operator_t op)
WIDGET_HELPER_GLOBAL_INLINE double lives_adjustment_get_value(LiVESAdjustment *adj)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_vscrollbar_new(LiVESAdjustment *adj)
WIDGET_HELPER_GLOBAL_INLINE int lives_widget_get_allocation_height(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_child_set_shrinkable(LiVESContainer *c, LiVESWidget *child, boolean val)
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)
WIDGET_HELPER_GLOBAL_INLINE LiVESXWindow * lives_widget_get_xwindow(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tool_button_set_label_widget(LiVESToolButton *button, LiVESWidget *label)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_size_request(LiVESWidget *widget, int width, int height)
LiVESWidget * lives_standard_check_button_new(const char *labeltext, boolean active, LiVESBox *box, const char *tooltip)
boolean lives_tool_button_set_border_color(LiVESWidget *button, LiVESWidgetState state, LiVESWidgetColor *colour)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_grab_focus(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_reorder_child(LiVESBox *box, LiVESWidget *child, int pos)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toolbar_set_style(LiVESToolbar *toolbar, LiVESToolbarStyle style)
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)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_source_remove(uint32_t handle)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_stroke(lives_painter_t *cr)
WIDGET_HELPER_GLOBAL_INLINE void set_child_colour(LiVESWidget *widget, boolean set_all)
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)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_adjustment_set_upper(LiVESAdjustment *adj, double upper)
boolean set_submenu_colours(LiVESMenu *menu, LiVESWidgetColor *colf, LiVESWidgetColor *colb)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_maximize(LiVESWindow *window)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_pack_end(LiVESBox *box, LiVESWidget *child, boolean expand, boolean fill, uint32_t padding)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_process_updates(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_image_set_from_pixbuf(LiVESImage *image, LiVESPixbuf *pixbuf)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_menu_item_set_submenu(LiVESMenuItem *menuitem, LiVESWidget *menu)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_frame_get_label_widget(LiVESFrame *frame)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_can_focus_and_default(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE int lives_pixbuf_get_height(const LiVESPixbuf *pixbuf)
LiVESWidget * lives_standard_check_menu_item_new_with_label(const char *label, boolean active)
WIDGET_HELPER_GLOBAL_INLINE int lives_pixbuf_get_width(const LiVESPixbuf *pixbuf)
WIDGET_HELPER_GLOBAL_INLINE void lives_widget_object_set_data_auto(LiVESWidgetObject *obj, const char *key, livespointer data)
void lives_general_button_clicked(LiVESButton *button, livespointer data_to_free)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_menu_bar_new(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_add_accel_group(LiVESWindow *window, LiVESAccelGroup *group)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_accel_group_disconnect(LiVESAccelGroup *group, LiVESWidgetClosure *closure)
LiVESPixbuf * lives_pixbuf_new_from_stock_at_size(const char *stock_id, LiVESIconSize size, int x, int y)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_standard_vpaned_new(void)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_spin_button_set_range(LiVESSpinButton *button, double min, double max)
void lives_widget_apply_theme(LiVESWidget *widget, LiVESWidgetState state)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_pixbuf_get_has_alpha(const LiVESPixbuf *pixbuf)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_notebook_set_current_page(LiVESNotebook *nbook, int pagenum)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hscrollbar_new(LiVESAdjustment *adj)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hpaned_new(void)
WIDGET_HELPER_GLOBAL_INLINE int lives_paned_get_position(LiVESPaned *paned)
#define LIVES_EXPAND_DEFAULT_HEIGHT
#define LIVES_EXPAND_EXTRA_HEIGHT
#define LIVES_TEXT_SIZE_LARGE
#define lives_standard_button_new_with_label(l, w, h)
lives_cursor_t
@ LIVES_CURSOR_SB_H_DOUBLE_ARROW
@ LIVES_CURSOR_NORMAL
must be zero
@ LIVES_CURSOR_HAND2
@ LIVES_CURSOR_VIDEO_BLOCK
@ LIVES_CURSOR_BLOCK
non-standard cursors
@ LIVES_CURSOR_BUSY
@ LIVES_CURSOR_CENTER_PTR
@ LIVES_CURSOR_AUDIO_BLOCK
@ LIVES_CURSOR_FX_BLOCK
#define LIVES_LIVES_STOCK_AUDIO
char LIVES_STOCK_LABEL_QUIT[32]
#define TEXTWIDGET_KEY
#define LIVES_TEXT_SIZE_MEDIUM
#define LIVES_EXPAND_DEFAULT
ulong lives_signal_connect(LiVESWidget *, const char *signal_name, ulong funcptr, livespointer data)
widget_opts_t widget_opts
#define HIDDEN_KEY
#define LIVES_EXPAND_DEFAULT_WIDTH
#define LIVES_JUSTIFY_DEFAULT
lives_painter_t * lives_painter_create_from_widget(LiVESWidget *)