4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "bcsignals.h"
29 #include "edlsession.h"
31 #include "filesystem.h"
33 #include "localsession.h"
35 #include "mainsession.h"
37 #include "trackcanvas.h"
39 #include "transition.h"
48 Edit::Edit(EDL *edl, Track *track)
53 if(track) this->edits = track->edits;
58 Edit::Edit(EDL *edl, Edits *edits)
63 if(edits) this->track = edits->track;
70 //printf("Edit::~Edit 1\n");
71 if(transition) delete transition;
72 //printf("Edit::~Edit 2\n");
95 Indexable* Edit::get_source()
97 if(asset) return asset;
98 if(nested_edl) return nested_edl;
102 int Edit::copy(int64_t start,
105 const char *output_path)
108 //printf("Edit::copy 1\n");
110 int64_t endproject = startproject + length;
113 if((startproject >= start && startproject <= end) || // startproject in range
114 (endproject <= end && endproject >= start) || // endproject in range
115 (startproject <= start && endproject >= end)) // range in project
118 int64_t startproject_in_selection = startproject; // start of edit in selection in project
119 int64_t startsource_in_selection = startsource; // start of source in selection in source
120 //int64_t endsource_in_selection = startsource + length; // end of source in selection
121 int64_t length_in_selection = length; // length of edit in selection
122 //printf("Edit::copy 2\n");
124 if(startproject < start)
125 { // start is after start of edit in project
126 int64_t length_difference = start - startproject;
128 startsource_in_selection += length_difference;
129 startproject_in_selection += length_difference;
130 length_in_selection -= length_difference;
132 //printf("Edit::copy 3\n");
135 { // end is before end of edit in project
136 length_in_selection = end - startproject_in_selection;
139 //printf("Edit::copy 4\n");
140 if(file) // only if not counting
142 file->tag.set_title("EDIT");
143 file->tag.set_property("STARTSOURCE", startsource_in_selection);
144 file->tag.set_property("CHANNEL", (int64_t)channel);
145 file->tag.set_property("LENGTH", length_in_selection);
146 file->tag.set_property("HARD_LEFT", hard_left);
147 file->tag.set_property("HARD_RIGHT", hard_right);
148 file->tag.set_property("COLOR", color);
149 file->tag.set_property("GROUP_ID", group_id);
150 if(user_title[0]) file->tag.set_property("USER_TITLE", user_title);
151 //printf("Edit::copy 5\n");
153 copy_properties_derived(file, length_in_selection);
156 // file->append_newline();
157 //printf("Edit::copy 6\n");
161 file->tag.set_title("NESTED_EDL");
162 file->tag.set_property("SRC", nested_edl->path);
164 file->tag.set_title("/NESTED_EDL");
166 file->append_newline();
171 //printf("Edit::copy 6 %s\n", asset->path);
172 char stored_path[BCTEXTLEN];
173 char asset_directory[BCTEXTLEN];
174 char output_directory[BCTEXTLEN];
177 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
178 fs.extract_dir(asset_directory, asset->path);
179 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
182 fs.extract_dir(output_directory, output_path);
184 output_directory[0] = 0;
185 //printf("Edit::copy %s, %s %s, %s\n", asset->path, asset_directory, output_path, output_directory);
187 if(output_path && !strcmp(asset_directory, output_directory))
188 fs.extract_name(stored_path, asset->path);
190 strcpy(stored_path, asset->path);
192 file->tag.set_title("FILE");
193 file->tag.set_property("SRC", stored_path);
195 file->tag.set_title("/FILE");
199 if(transition && startsource_in_selection == startsource)
201 transition->save_xml(file);
204 //printf("Edit::copy 7\n");
205 file->tag.set_title("/EDIT");
207 file->append_newline();
208 //printf("Edit::copy 8\n");
210 //printf("Edit::copy 9\n");
217 //printf("Edit::copy 10\n");
222 int64_t Edit::get_source_end(int64_t default_)
227 void Edit::insert_transition(char *title)
229 //printf("Edit::insert_transition this=%p title=%p title=%s\n", this, title, title);
231 transition = new Transition(edl, this, title,
232 track->to_units(edl->session->default_transition_length, 1));
235 void Edit::detach_transition()
239 if( edl->local_session->gang_tracks == GANG_NONE ) return;
240 double pos = track->from_units(startproject);
241 Track *current = edl->tracks->first;
242 for( ; current; current=current->next ) {
243 if( current == track ) continue;
244 if( current->data_type != track->data_type ) continue;
245 if( !current->armed_gang(track) ) continue;
246 int64_t track_pos = current->to_units(pos, 1);
247 Edit *edit = current->edits->editof(track_pos, PLAY_FORWARD, 0);
248 if( !edit ) continue;
249 double edit_pos = track->from_units(edit->startproject);
250 if( !edl->equivalent(pos, edit_pos) ) continue;
251 delete edit->transition;
252 edit->transition = 0;
258 return (track->data_type != TRACK_SUBTITLE ?
259 asset || nested_edl :
260 *((SEdit *)this)->get_text()) ? 0 : 1;
263 void Edit::set_selected(int v)
266 if( v < 0 ) v = !is_selected ? 1 : 0;
267 int gang = edl->local_session->gang_tracks != GANG_NONE ? 1 : 0;
268 select_affected_edits(v, gang);
271 edl->tracks->set_group_selected(group_id, v);
274 // gang<0: rest of tracks, gang==0: this track, gang>0: to next master
275 void Edit::select_affected_edits(int v, int gang)
279 double position = track->from_units(startproject);
280 for( Track *current=track->next; current; current=current->next ) {
281 if( gang > 0 && current->master ) break;
282 if( !current->is_armed() ) continue;
283 for( Edit *edit=current->edits->first; edit; edit=edit->next ) {
284 if( edit->silence() ) continue;
285 double start = current->from_units(edit->startproject);
286 if( edl->equivalent(start, position) ) {
287 edit->is_selected = v;
295 void Edit::copy_from(Edit *edit)
297 this->orig_id = edit->orig_id;
298 this->nested_edl = edl->nested_edls.get_nested(edit->nested_edl);
299 this->asset = edl->assets->update(edit->asset);
300 this->startsource = edit->startsource;
301 this->startproject = edit->startproject;
302 this->length = edit->length;
303 this->hard_left = edit->hard_left;
304 this->hard_right = edit->hard_right;
305 this->color = edit->color;
306 this->group_id = edit->group_id;
307 strcpy (this->user_title, edit->user_title);
311 if(!transition) transition = new Transition(edl,
313 edit->transition->title,
314 edit->transition->length);
315 *this->transition = *edit->transition;
317 this->channel = edit->channel;
320 void Edit::clone_from(Edit *edit)
323 edit->orig_id = edit->id;
326 void Edit::equivalent_output(Edit *edit, int64_t *result)
328 // End of edit changed
329 if(startproject + length != edit->startproject + edit->length)
331 int64_t new_length = MIN(startproject + length,
332 edit->startproject + edit->length);
333 if(*result < 0 || new_length < *result)
334 *result = new_length;
338 // Different nested EDLs
339 (edit->nested_edl && !nested_edl) ||
340 (!edit->nested_edl && nested_edl) ||
342 (edit->asset == 0 && asset != 0) ||
343 (edit->asset != 0 && asset == 0) ||
344 // different transitions
345 (edit->transition == 0 && transition != 0) ||
346 (edit->transition != 0 && transition == 0) ||
348 (startproject != edit->startproject) ||
349 (startsource != edit->startsource) ||
350 // Transition changed
351 (transition && edit->transition &&
352 !transition->identical(edit->transition)) ||
354 (asset && edit->asset &&
355 !asset->equivalent(*edit->asset, 1, 1, edl)) ||
356 // Nested EDL changed
357 (nested_edl && edit->nested_edl &&
358 strcmp(nested_edl->path, edit->nested_edl->path))
361 // Start of edit changed
362 if(*result < 0 || startproject < *result) *result = startproject;
367 Edit& Edit::operator=(Edit& edit)
369 //printf("Edit::operator= called\n");
374 void Edit::synchronize_params(Edit *edit)
380 // Comparison for ResourcePixmap drawing
381 int Edit::identical(Edit &edit)
383 int result = (this->nested_edl == edit.nested_edl &&
384 this->asset == edit.asset &&
385 this->startsource == edit.startsource &&
386 this->startproject == edit.startproject &&
387 this->length == edit.length &&
388 this->transition == edit.transition &&
389 this->channel == edit.channel);
393 int Edit::operator==(Edit &edit)
395 return identical(edit);
398 double Edit::frames_per_picon()
400 return Units::round(picon_w()) / frame_w();
403 double Edit::frame_w()
405 return track->from_units(1) *
406 edl->session->sample_rate /
407 edl->local_session->zoom_sample;
410 double Edit::picon_w()
417 else if(nested_edl) {
418 w = nested_edl->session->output_w;
419 h = nested_edl->session->output_h;
421 return w>0 && h>0 ? ((double)track->data_h*w)/h : 0;
426 return track->data_h;
430 int Edit::dump(FILE *fp)
432 fprintf(fp," EDIT %p\n", this); fflush(fp);
433 fprintf(fp," id %d, orig_id %d, nested_edl=%p %s asset=%p %s\n",
434 id, orig_id, nested_edl, nested_edl ? nested_edl->path : "",
435 asset, asset ? asset->path : "");
437 fprintf(fp," channel %d, color %08x, hard lt/rt %d/%d"
438 " group_id %d, is_selected %d\n",
439 channel, color, hard_left, hard_right, group_id, is_selected);
441 fprintf(fp," TRANSITION %p\n", transition);
442 transition->dump(fp);
444 fprintf(fp," startsource %jd startproject %jd length %jd\n",
445 startsource, startproject, length);
450 int Edit::load_properties(FileXML *file, int64_t &startproject)
452 startsource = file->tag.get_property("STARTSOURCE", (int64_t)0);
453 length = file->tag.get_property("LENGTH", (int64_t)0);
454 hard_left = file->tag.get_property("HARD_LEFT", (int64_t)0);
455 hard_right = file->tag.get_property("HARD_RIGHT", (int64_t)0);
456 color = file->tag.get_property("COLOR", 0);
457 group_id = file->tag.get_property("GROUP_ID", group_id);
459 file->tag.get_property("USER_TITLE", user_title);
460 this->startproject = startproject;
461 load_properties_derived(file);
465 void Edit::shift(int64_t difference)
467 startproject += difference;
470 void Edit::trim(int64_t difference)
472 length += difference;
473 if( startproject < 0 ) {
474 if( (startsource+=startproject) < 0 ) startsource = 0;
475 if( (length+=startproject) < 0 ) length = 0;
478 if( startsource < 0 )
480 int64_t src_len = get_source_end(INT64_MAX);
481 if( startsource + length > src_len ) {
482 length = src_len - startsource;
483 if( length < 0 ) length = 0;
489 int Edit::shift_start(int edit_mode, int64_t newposition, int64_t oldposition,
490 int edit_labels, int edit_autos, int edit_plugins, Edits *trim_edits)
492 int64_t cut_length = newposition - oldposition;
493 int rest_moved = edit_mode == MOVE_RIPPLE || edit_mode == MOVE_EDGE ? 1 : 0;
494 if( cut_length > length )
496 else if( cut_length < -length )
497 cut_length = -length;
499 int64_t start = startproject, end = start + length;
500 Edit *prev = this->previous, *next = this->next;
503 switch( edit_mode ) {
506 startsource += cut_length;
507 cut_length = -cut_length;
508 length += cut_length;
509 for( Edit *edit=next; edit; edit=edit->next )
510 edit->startproject += cut_length;
513 if( prev && prev->length + cut_length < 0 )
514 cut_length = -prev->length;
515 if( prev ) prev->trim(cut_length);
516 startproject += cut_length;
517 startsource += cut_length;
518 length -= cut_length;
522 startsource -= cut_length;
526 if( prev && prev->length + cut_length < 0 )
527 cut_length = -prev->length;
528 if( next && next->length - cut_length < 0 )
529 cut_length = next->length;
530 if( prev ) prev->trim(cut_length);
531 startproject += cut_length;
533 next->startproject += cut_length;
534 next->startsource += cut_length;
535 next->trim(-cut_length);
540 startsource -= cut_length;
541 length += cut_length;
542 for( Edit *edit=next; edit; edit=edit->next )
543 edit->startproject += cut_length;
547 return follow_edits(start, end, cut_length, edits_moved, rest_moved,
548 edit_labels, edit_autos, edit_plugins, trim_edits);
551 int Edit::shift_end(int edit_mode, int64_t newposition, int64_t oldposition,
552 int edit_labels, int edit_autos, int edit_plugins, Edits *trim_edits)
554 int64_t cut_length = newposition - oldposition;
555 int rest_moved = edit_mode == MOVE_RIPPLE || edit_mode == MOVE_EDGE ? 1 : 0;
556 if( cut_length > length ) {
557 if( !rest_moved ) cut_length = length;
559 else if( cut_length < -length )
560 cut_length = -length;
561 int64_t start = startproject, end = start + length;
562 Edit *prev = this->previous, *next = this->next;
565 switch( edit_mode ) {
568 length += cut_length;
569 for( Edit *edit=next; edit; edit=edit->next )
570 edit->startproject += cut_length;
573 if( next && next->length - cut_length < 0 )
574 cut_length = next->length;
575 length += cut_length;
577 next->startproject += cut_length;
578 next->startsource += cut_length;
579 next->trim(-cut_length);
584 startsource -= cut_length;
588 if( prev && prev->length + cut_length < 0 )
589 cut_length = -prev->length;
590 if( next && next->length - cut_length < 0 )
591 cut_length = next->length;
592 if( prev ) prev->trim(cut_length);
593 startproject += cut_length;
595 next->startproject += cut_length;
596 next->startsource += cut_length;
597 next->trim(-cut_length);
602 return follow_edits(start, end, cut_length, edits_moved, rest_moved,
603 edit_labels, edit_autos, edit_plugins, trim_edits);
606 int Edit::follow_edits(int64_t start, int64_t end, int64_t cut_length,
607 int edits_moved, int rest_moved, int edit_labels, int edit_autos,
608 int edit_plugins, Edits *trim_edits)
610 if( edits_moved && rest_moved ) {
612 double cut_len = track->from_units(cut_length);
613 double start_pos = edits->track->from_units(start);
615 edits->edl->labels->clear(start_pos + cut_len, start_pos, 1);
616 else if( cut_len > 0 )
617 edits->edl->labels->insert(start_pos, cut_len);
620 track->clear(start + cut_length, start,
621 0, 0, edit_autos, edit_plugins, trim_edits);
622 else if( cut_length > 0 ) {
624 track->shift_keyframes(start, cut_length);
626 track->shift_effects(start, cut_length, 1, trim_edits);
629 else if( edits_moved ) {
631 double cut_len = track->from_units(cut_length);
632 double start_pos = edits->track->from_units(start);
633 edits->edl->labels->insert(start_pos, cut_len);
634 double end_pos = edits->track->from_units(end);
635 edits->edl->labels->insert(end_pos + cut_len, -cut_len);
638 if( cut_length > 0 ) {
639 track->clear(end, end+cut_length, 0, 0, 0, 1, 0);
640 track->shift_keyframes(start, cut_length);
642 else if( cut_length < 0 ) {
643 track->clear(start+cut_length, start, 0, 0, 0, 1, 0);
644 track->shift_keyframes(end+cut_length, -cut_length);
648 if( cut_length > 0 ) {
649 track->clear(end, end+cut_length, 0, 0, -1, 0, 0);
650 track->shift_effects(start, cut_length, 1, 0);
652 else if( cut_length < 0 ) {
653 track->clear(start+cut_length, start, 0, 0, -1, 0, 0);
654 track->shift_effects(end+cut_length, -cut_length, 1, 0);
658 else if( rest_moved ) {
660 double cut_len = track->from_units(cut_length);
661 double end_pos = edits->track->from_units(end);
663 edits->edl->labels->clear(end_pos + cut_len, end_pos, 1);
664 else if( cut_len > 0 )
665 edits->edl->labels->insert(end_pos, cut_len);
668 track->clear(end + cut_length, end,
669 0, 0, edit_autos, edit_plugins, trim_edits);
670 else if( cut_length > 0 ) {
672 track->shift_keyframes(end, cut_length);
674 track->shift_effects(end, cut_length, 1, trim_edits);
682 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
684 int64_t left, right, left_unit, right_unit;
685 if(!transition) return 0;
686 get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
688 if(cursor_x > left && cursor_x < right)
690 // transition->popup_transition(cursor_x, cursor_y);
696 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
698 int64_t left, right, left_unit, right_unit;
699 get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
701 int64_t pixel1, pixel2;
703 pixel2 = pixel1 + xS(10);
706 // cursor_x is faked in acanvas
707 if(cursor_x >= pixel1 && cursor_x <= pixel2)
709 selection = left_unit;
710 return 1; // left handle
713 //int64_t endproject = startproject + length;
715 pixel1 = pixel2 - xS(10);
718 if(cursor_x >= pixel1 && cursor_x <= pixel2)
720 selection = right_unit;
721 return 2; // right handle
726 void Edit::get_title(char *title)
728 if( user_title[0] ) {
729 strcpy(title, user_title);
732 Indexable *idxbl = asset ? (Indexable*)asset : (Indexable*)nested_edl;
738 fs.extract_name(title, idxbl->path);
739 if( asset || track->data_type == TRACK_AUDIO ) {
740 char number[BCSTRLEN];
741 sprintf(number, " #%d", channel + 1);
742 strcat(title, number);