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"
32 #include "localsession.h"
34 #include "mainsession.h"
36 #include "trackcanvas.h"
38 #include "transition.h"
47 Edit::Edit(EDL *edl, Track *track)
52 if(track) this->edits = track->edits;
56 Edit::Edit(EDL *edl, Edits *edits)
61 if(edits) this->track = edits->track;
67 //printf("Edit::~Edit 1\n");
68 if(transition) delete transition;
69 //printf("Edit::~Edit 2\n");
93 Indexable* Edit::get_source()
95 if(asset) return asset;
96 if(nested_edl) return nested_edl;
100 int Edit::copy(int64_t start,
103 const char *output_path)
106 //printf("Edit::copy 1\n");
108 int64_t endproject = startproject + length;
111 if((startproject >= start && startproject <= end) || // startproject in range
112 (endproject <= end && endproject >= start) || // endproject in range
113 (startproject <= start && endproject >= end)) // range in project
116 int64_t startproject_in_selection = startproject; // start of edit in selection in project
117 int64_t startsource_in_selection = startsource; // start of source in selection in source
118 //int64_t endsource_in_selection = startsource + length; // end of source in selection
119 int64_t length_in_selection = length; // length of edit in selection
120 //printf("Edit::copy 2\n");
122 if(startproject < start)
123 { // start is after start of edit in project
124 int64_t length_difference = start - startproject;
126 startsource_in_selection += length_difference;
127 startproject_in_selection += length_difference;
128 length_in_selection -= length_difference;
130 //printf("Edit::copy 3\n");
133 { // end is before end of edit in project
134 length_in_selection = end - startproject_in_selection;
137 //printf("Edit::copy 4\n");
138 if(file) // only if not counting
140 file->tag.set_title("EDIT");
141 file->tag.set_property("STARTSOURCE", startsource_in_selection);
142 file->tag.set_property("CHANNEL", (int64_t)channel);
143 file->tag.set_property("LENGTH", length_in_selection);
144 file->tag.set_property("HARD_LEFT", hard_left);
145 file->tag.set_property("HARD_RIGHT", hard_right);
146 file->tag.set_property("COLOR", color);
147 file->tag.set_property("GROUP_ID", group_id);
148 if(user_title[0]) file->tag.set_property("USER_TITLE", user_title);
149 //printf("Edit::copy 5\n");
151 copy_properties_derived(file, length_in_selection);
154 // file->append_newline();
155 //printf("Edit::copy 6\n");
159 file->tag.set_title("NESTED_EDL");
160 file->tag.set_property("SRC", nested_edl->path);
162 file->tag.set_title("/NESTED_EDL");
164 file->append_newline();
169 //printf("Edit::copy 6 %s\n", asset->path);
170 char stored_path[BCTEXTLEN];
171 char asset_directory[BCTEXTLEN];
172 char output_directory[BCTEXTLEN];
175 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
176 fs.extract_dir(asset_directory, asset->path);
177 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
180 fs.extract_dir(output_directory, output_path);
182 output_directory[0] = 0;
183 //printf("Edit::copy %s, %s %s, %s\n", asset->path, asset_directory, output_path, output_directory);
185 if(output_path && !strcmp(asset_directory, output_directory))
186 fs.extract_name(stored_path, asset->path);
188 strcpy(stored_path, asset->path);
190 file->tag.set_title("FILE");
191 file->tag.set_property("SRC", stored_path);
193 file->tag.set_title("/FILE");
197 if(transition && startsource_in_selection == startsource)
199 transition->save_xml(file);
202 //printf("Edit::copy 7\n");
203 file->tag.set_title("/EDIT");
205 file->append_newline();
206 //printf("Edit::copy 8\n");
208 //printf("Edit::copy 9\n");
215 //printf("Edit::copy 10\n");
220 int64_t Edit::get_source_end(int64_t default_)
225 void Edit::insert_transition(char *title)
227 //printf("Edit::insert_transition this=%p title=%p title=%s\n", this, title, title);
229 transition = new Transition(edl, this, title,
230 track->to_units(edl->session->default_transition_length, 1));
233 void Edit::detach_transition()
235 if(transition) delete transition;
241 return (track->data_type != TRACK_SUBTITLE ?
242 asset || nested_edl :
243 *((SEdit *)this)->get_text()) ? 0 : 1;
247 if( track->data_type != TRACK_SUBTITLE ) {
252 *((SEdit *)this)->get_text() = 0;
255 void Edit::set_selected(int v)
258 edl->tracks->set_group_selected(group_id, v);
260 is_selected = v >= 0 ? v : !is_selected ? 1 : 0;
263 void Edit::copy_from(Edit *edit)
265 this->nested_edl = edl->nested_edls.get_nested(edit->nested_edl);
266 this->asset = edl->assets->update(edit->asset);
267 this->startsource = edit->startsource;
268 this->startproject = edit->startproject;
269 this->length = edit->length;
270 this->hard_left = edit->hard_left;
271 this->hard_right = edit->hard_right;
272 this->color = edit->color;
273 this->group_id = edit->group_id;
274 strcpy (this->user_title, edit->user_title);
278 if(!transition) transition = new Transition(edl,
280 edit->transition->title,
281 edit->transition->length);
282 *this->transition = *edit->transition;
284 this->channel = edit->channel;
287 void Edit::equivalent_output(Edit *edit, int64_t *result)
289 // End of edit changed
290 if(startproject + length != edit->startproject + edit->length)
292 int64_t new_length = MIN(startproject + length,
293 edit->startproject + edit->length);
294 if(*result < 0 || new_length < *result)
295 *result = new_length;
299 // Different nested EDLs
300 (edit->nested_edl && !nested_edl) ||
301 (!edit->nested_edl && nested_edl) ||
303 (edit->asset == 0 && asset != 0) ||
304 (edit->asset != 0 && asset == 0) ||
305 // different transitions
306 (edit->transition == 0 && transition != 0) ||
307 (edit->transition != 0 && transition == 0) ||
309 (startproject != edit->startproject) ||
310 (startsource != edit->startsource) ||
311 // Transition changed
312 (transition && edit->transition &&
313 !transition->identical(edit->transition)) ||
315 (asset && edit->asset &&
316 !asset->equivalent(*edit->asset, 1, 1, edl)) ||
317 // Nested EDL changed
318 (nested_edl && edit->nested_edl &&
319 strcmp(nested_edl->path, edit->nested_edl->path))
322 // Start of edit changed
323 if(*result < 0 || startproject < *result) *result = startproject;
328 Edit& Edit::operator=(Edit& edit)
330 //printf("Edit::operator= called\n");
335 void Edit::synchronize_params(Edit *edit)
341 // Comparison for ResourcePixmap drawing
342 int Edit::identical(Edit &edit)
344 int result = (this->nested_edl == edit.nested_edl &&
345 this->asset == edit.asset &&
346 this->startsource == edit.startsource &&
347 this->startproject == edit.startproject &&
348 this->length == edit.length &&
349 this->transition == edit.transition &&
350 this->channel == edit.channel);
354 int Edit::operator==(Edit &edit)
356 return identical(edit);
359 double Edit::frames_per_picon()
361 return Units::round(picon_w()) / frame_w();
364 double Edit::frame_w()
366 return track->from_units(1) *
367 edl->session->sample_rate /
368 edl->local_session->zoom_sample;
371 double Edit::picon_w()
378 else if(nested_edl) {
379 w = nested_edl->session->output_w;
380 h = nested_edl->session->output_h;
382 return w>0 && h>0 ? ((double)edl->local_session->zoom_track*w)/h : 0;
387 return edl->local_session->zoom_track;
391 int Edit::dump(FILE *fp)
393 fprintf(fp," EDIT %p\n", this); fflush(fp);
394 fprintf(fp," nested_edl=%p %s asset=%p %s\n",
396 nested_edl ? nested_edl->path : "",
398 asset ? asset->path : "");
400 fprintf(fp," channel %d, color %08x, group_id %d, is_selected %d\n",
401 channel, color, group_id, is_selected);
404 fprintf(fp," TRANSITION %p\n", transition);
405 transition->dump(fp);
407 fprintf(fp," startsource %jd startproject %jd hard lt/rt %d/%d length %jd\n",
408 startsource, startproject, hard_left, hard_right, length); fflush(fp);
412 int Edit::load_properties(FileXML *file, int64_t &startproject)
414 startsource = file->tag.get_property("STARTSOURCE", (int64_t)0);
415 length = file->tag.get_property("LENGTH", (int64_t)0);
416 hard_left = file->tag.get_property("HARD_LEFT", (int64_t)0);
417 hard_right = file->tag.get_property("HARD_RIGHT", (int64_t)0);
418 color = file->tag.get_property("COLOR", 0);
419 group_id = file->tag.get_property("GROUP_ID", group_id);
421 file->tag.get_property("USER_TITLE", user_title);
422 this->startproject = startproject;
423 load_properties_derived(file);
427 void Edit::shift(int64_t difference)
429 //printf("Edit::shift 1 %p %jd %jd\n", this, startproject, difference);
430 startproject += difference;
431 //printf("Edit::shift 2 %jd %jd\n", startproject, difference);
434 int Edit::shift_start_in(int edit_mode,
443 int64_t cut_length = newposition - oldposition;
444 int64_t end_previous_source, end_source;
446 if(edit_mode == MOVE_ALL_EDITS)
448 if(cut_length < length)
450 edits->clear_recursive(oldposition,
460 edits->clear_recursive(oldposition,
461 startproject + length,
470 if(edit_mode == MOVE_ONE_EDIT)
472 // Paste silence and cut
473 //printf("Edit::shift_start_in 1\n");
476 Edit *new_edit = edits->create_edit();
477 new_edit->startproject = this->startproject;
478 new_edit->length = 0;
479 edits->insert_before(this,
482 //printf("Edit::shift_start_in 2 %p\n", previous);
484 end_previous_source = previous->get_source_end(previous->startsource + previous->length + cut_length);
485 if(end_previous_source > 0 &&
486 previous->startsource + previous->length + cut_length > end_previous_source)
487 cut_length = end_previous_source - previous->startsource - previous->length;
489 if(cut_length < length)
491 startproject += cut_length;
492 startsource += cut_length;
493 length -= cut_length;
494 previous->length += cut_length;
495 //printf("Edit::shift_start_in 2\n");
498 { // Clear entire edit
500 previous->length += cut_length;
501 for(Edit* current_edit = this; current_edit; current_edit = current_edit->next)
503 current_edit->startproject += cut_length;
505 edits->clear_recursive(oldposition + cut_length,
506 startproject + cut_length,
513 //printf("Edit::shift_start_in 3\n");
516 if(edit_mode == MOVE_NO_EDITS)
518 end_source = get_source_end(startsource + length + cut_length);
519 if(end_source > 0 && startsource + length + cut_length > end_source)
520 cut_length = end_source - startsource - length;
522 startsource += cut_length;
527 int Edit::shift_start_out(int edit_mode,
536 int64_t cut_length = oldposition - newposition;
539 if(asset || nested_edl)
541 int64_t end_source = get_source_end(1);
543 //printf("Edit::shift_start_out 1 %jd %jd\n", startsource, cut_length);
544 if(end_source > 0 && startsource < cut_length)
546 cut_length = startsource;
550 if(edit_mode == MOVE_ALL_EDITS)
552 //printf("Edit::shift_start_out 10 %jd\n", cut_length);
553 startsource -= cut_length;
554 length += cut_length;
557 edits->shift_keyframes_recursive(startproject,
560 edits->shift_effects_recursive(startproject,
564 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
566 current_edit->startproject += cut_length;
570 if(edit_mode == MOVE_ONE_EDIT)
574 if(cut_length < previous->length)
575 { // Cut into previous edit
576 previous->length -= cut_length;
577 startproject -= cut_length;
578 startsource -= cut_length;
579 length += cut_length;
582 { // Clear entire previous edit
583 cut_length = previous->length;
584 previous->length = 0;
585 length += cut_length;
586 startsource -= cut_length;
587 startproject -= cut_length;
592 if(edit_mode == MOVE_NO_EDITS)
594 startsource -= cut_length;
597 // Fix infinite length files
598 if(startsource < 0) startsource = 0;
602 int Edit::shift_end_in(int edit_mode,
611 int64_t cut_length = oldposition - newposition;
613 if(edit_mode == MOVE_ALL_EDITS)
615 //printf("Edit::shift_end_in 1\n");
616 if(newposition > startproject)
617 { // clear partial edit
618 //printf("Edit::shift_end_in %p %p\n", track->edits, edits);
619 edits->clear_recursive(newposition,
628 { // clear entire edit
629 edits->clear_recursive(startproject,
639 if(edit_mode == MOVE_ONE_EDIT)
645 int64_t end_source = next->get_source_end(1);
647 if(end_source > 0 && next->startsource - cut_length < 0)
649 cut_length = next->startsource;
653 if(cut_length < length)
655 length -= cut_length;
656 next->startproject -= cut_length;
657 next->startsource -= cut_length;
658 next->length += cut_length;
659 //printf("Edit::shift_end_in 2 %d\n", cut_length);
664 next->length += cut_length;
665 next->startsource -= cut_length;
666 next->startproject -= cut_length;
667 length -= cut_length;
672 if(cut_length < length)
674 length -= cut_length;
679 edits->clear_recursive(startproject,
690 // Does nothing for plugins
691 if(edit_mode == MOVE_NO_EDITS)
693 //printf("Edit::shift_end_in 3\n");
694 int64_t end_source = get_source_end(1);
695 if(end_source > 0 && startsource < cut_length)
697 cut_length = startsource;
699 startsource -= cut_length;
704 int Edit::shift_end_out(int edit_mode,
713 int64_t cut_length = newposition - oldposition;
714 int64_t endsource = get_source_end(startsource + length + cut_length);
716 // check end of edit against end of source file
717 if(endsource > 0 && startsource + length + cut_length > endsource)
718 cut_length = endsource - startsource - length;
720 //printf("Edit::shift_end_out 1 %jd %d %d %d\n", oldposition, newposition, this->length, cut_length);
721 if(edit_mode == MOVE_ALL_EDITS)
724 this->length += cut_length;
726 // Effects are shifted in length extension
728 edits->shift_effects_recursive(oldposition /* startproject */,
732 edits->shift_keyframes_recursive(oldposition /* startproject */,
735 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
737 current_edit->startproject += cut_length;
741 if(edit_mode == MOVE_ONE_EDIT)
745 if(cut_length < next->length)
747 length += cut_length;
748 next->startproject += cut_length;
749 next->startsource += cut_length;
750 next->length -= cut_length;
751 //printf("Edit::shift_end_out %d cut_length=%d\n", __LINE__, cut_length);
755 //printf("Edit::shift_end_out %d cut_length=%d next->length=%d\n", __LINE__, cut_length, next->length);
756 cut_length = next->length;
757 next->startproject += next->length;
758 next->startsource += next->length;
760 length += cut_length;
766 length += cut_length;
770 if(edit_mode == MOVE_NO_EDITS)
772 startsource += cut_length;
777 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
779 int64_t left, right, left_unit, right_unit;
780 if(!transition) return 0;
781 get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
783 if(cursor_x > left && cursor_x < right)
785 // transition->popup_transition(cursor_x, cursor_y);
791 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
793 int64_t left, right, left_unit, right_unit;
794 get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
796 int64_t pixel1, pixel2;
798 pixel2 = pixel1 + 10;
801 // cursor_x is faked in acanvas
802 if(cursor_x >= pixel1 && cursor_x <= pixel2)
804 selection = left_unit;
805 return 1; // left handle
808 //int64_t endproject = startproject + length;
810 pixel1 = pixel2 - 10;
813 if(cursor_x >= pixel1 && cursor_x <= pixel2)
815 selection = right_unit;
816 return 2; // right handle
821 void Edit::get_title(char *title)
823 if( user_title[0] ) {
824 strcpy(title, user_title);
827 Indexable *idxbl = asset ? (Indexable*)asset : (Indexable*)nested_edl;
833 fs.extract_name(title, idxbl->path);
834 if( asset || track->data_type == TRACK_AUDIO ) {
835 char number[BCSTRLEN];
836 sprintf(number, " #%d", channel + 1);
837 strcat(title, number);