vwindow replay, append tracks to proj, multi asset info, subtitle bug
[goodguy/history.git] / cinelerra-5.1 / cinelerra / edit.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "asset.h"
23 #include "assets.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "edit.h"
27 #include "edits.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "filexml.h"
31 #include "filesystem.h"
32 #include "localsession.h"
33 #include "plugin.h"
34 #include "mainsession.h"
35 #include "nestededls.h"
36 #include "strack.h"
37 #include "trackcanvas.h"
38 #include "tracks.h"
39 #include "transition.h"
40 #include <string.h>
41
42
43 Edit::Edit()
44 {
45         reset();
46 }
47
48 Edit::Edit(EDL *edl, Track *track)
49 {
50         reset();
51         this->edl = edl;
52         this->track = track;
53         if(track) this->edits = track->edits;
54         id = EDL::next_id();
55 }
56
57 Edit::Edit(EDL *edl, Edits *edits)
58 {
59         reset();
60         this->edl = edl;
61         this->edits = edits;
62         if(edits) this->track = edits->track;
63         id = EDL::next_id();
64 }
65
66 Edit::~Edit()
67 {
68 //printf("Edit::~Edit 1\n");
69         if(transition) delete transition;
70 //printf("Edit::~Edit 2\n");
71 }
72
73 void Edit::reset()
74 {
75         edl = 0;
76         track = 0;
77         edits = 0;
78         startsource = 0;
79         startproject = 0;
80         length = 0;
81         asset = 0;
82         transition = 0;
83         channel = 0;
84         user_title[0] = 0;
85         nested_edl = 0;
86         is_plugin = 0;
87         hard_left = 0;
88         hard_right = 0;
89 }
90
91 Indexable* Edit::get_source()
92 {
93         if(asset) return asset;
94         if(nested_edl) return nested_edl;
95         return 0;
96 }
97
98 int Edit::copy(int64_t start,
99         int64_t end,
100         FileXML *file,
101         const char *output_path)
102 {
103 // variables
104 //printf("Edit::copy 1\n");
105
106         int64_t endproject = startproject + length;
107         int result;
108
109         if((startproject >= start && startproject <= end) ||  // startproject in range
110                  (endproject <= end && endproject >= start) ||     // endproject in range
111                  (startproject <= start && endproject >= end))    // range in project
112         {
113 // edit is in range
114                 int64_t startproject_in_selection = startproject; // start of edit in selection in project
115                 int64_t startsource_in_selection = startsource; // start of source in selection in source
116                 //int64_t endsource_in_selection = startsource + length; // end of source in selection
117                 int64_t length_in_selection = length;             // length of edit in selection
118 //printf("Edit::copy 2\n");
119
120                 if(startproject < start)
121                 {         // start is after start of edit in project
122                         int64_t length_difference = start - startproject;
123
124                         startsource_in_selection += length_difference;
125                         startproject_in_selection += length_difference;
126                         length_in_selection -= length_difference;
127                 }
128 //printf("Edit::copy 3\n");
129
130                 if(endproject > end)
131                 {         // end is before end of edit in project
132                         length_in_selection = end - startproject_in_selection;
133                 }
134
135 //printf("Edit::copy 4\n");
136                 if(file)    // only if not counting
137                 {
138                         file->tag.set_title("EDIT");
139                         file->tag.set_property("STARTSOURCE", startsource_in_selection);
140                         file->tag.set_property("CHANNEL", (int64_t)channel);
141                         file->tag.set_property("LENGTH", length_in_selection);
142                         file->tag.set_property("HARD_LEFT", hard_left);
143                         file->tag.set_property("HARD_RIGHT", hard_right);
144                         if(user_title[0]) file->tag.set_property("USER_TITLE", user_title);
145 //printf("Edit::copy 5\n");
146
147                         copy_properties_derived(file, length_in_selection);
148
149                         file->append_tag();
150 //                      file->append_newline();
151 //printf("Edit::copy 6\n");
152
153                         if(nested_edl)
154                         {
155                                 file->tag.set_title("NESTED_EDL");
156                                 file->tag.set_property("SRC", nested_edl->path);
157                                 file->append_tag();
158                                 file->tag.set_title("/NESTED_EDL");
159                                 file->append_tag();
160                                 file->append_newline();
161                         }
162
163                         if(asset)
164                         {
165 //printf("Edit::copy 6 %s\n", asset->path);
166                                 char stored_path[BCTEXTLEN];
167                                 char asset_directory[BCTEXTLEN];
168                                 char output_directory[BCTEXTLEN];
169                                 FileSystem fs;
170
171 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
172                                 fs.extract_dir(asset_directory, asset->path);
173 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
174
175                                 if(output_path)
176                                         fs.extract_dir(output_directory, output_path);
177                                 else
178                                         output_directory[0] = 0;
179 //printf("Edit::copy %s, %s %s, %s\n", asset->path, asset_directory, output_path, output_directory);
180
181                                 if(output_path && !strcmp(asset_directory, output_directory))
182                                         fs.extract_name(stored_path, asset->path);
183                                 else
184                                         strcpy(stored_path, asset->path);
185
186                                 file->tag.set_title("FILE");
187                                 file->tag.set_property("SRC", stored_path);
188                                 file->append_tag();
189                                 file->tag.set_title("/FILE");
190                                 file->append_tag();
191                         }
192
193                         if(transition)
194                         {
195                                 transition->save_xml(file);
196                         }
197
198 //printf("Edit::copy 7\n");
199                         file->tag.set_title("/EDIT");
200                         file->append_tag();
201                         file->append_newline();
202 //printf("Edit::copy 8\n");
203                 }
204 //printf("Edit::copy 9\n");
205                 result = 1;
206         }
207         else
208         {
209                 result = 0;
210         }
211 //printf("Edit::copy 10\n");
212         return result;
213 }
214
215
216 int64_t Edit::get_source_end(int64_t default_)
217 {
218         return default_;
219 }
220
221 void Edit::insert_transition(char *title)
222 {
223 //printf("Edit::insert_transition this=%p title=%p title=%s\n", this, title, title);
224         delete transition;
225         transition = new Transition(edl, this, title,
226                 track->to_units(edl->session->default_transition_length, 1));
227 }
228
229 void Edit::detach_transition()
230 {
231         if(transition) delete transition;
232         transition = 0;
233 }
234
235 int Edit::silence()
236 {
237         return (track->data_type != TRACK_SUBTITLE ?
238                 asset || nested_edl :
239                 *((SEdit *)this)->get_text()) ? 0 : 1;
240 }
241
242
243 void Edit::copy_from(Edit *edit)
244 {
245         this->nested_edl = edl->nested_edls->get_copy(edit->nested_edl);
246         this->asset = edl->assets->update(edit->asset);
247         this->startsource = edit->startsource;
248         this->startproject = edit->startproject;
249         this->length = edit->length;
250         this->hard_left = edit->hard_left;
251         this->hard_right = edit->hard_right;
252         strcpy (this->user_title, edit->user_title);
253
254         if(edit->transition)
255         {
256                 if(!transition) transition = new Transition(edl,
257                         this,
258                         edit->transition->title,
259                         edit->transition->length);
260                 *this->transition = *edit->transition;
261         }
262         this->channel = edit->channel;
263 }
264
265 void Edit::equivalent_output(Edit *edit, int64_t *result)
266 {
267 // End of edit changed
268         if(startproject + length != edit->startproject + edit->length)
269         {
270                 int64_t new_length = MIN(startproject + length,
271                         edit->startproject + edit->length);
272                 if(*result < 0 || new_length < *result)
273                         *result = new_length;
274         }
275
276         if(
277 // Different nested EDLs
278                 (edit->nested_edl && !nested_edl) ||
279                 (!edit->nested_edl && nested_edl) ||
280 // Different assets
281                 (edit->asset == 0 && asset != 0) ||
282                 (edit->asset != 0 && asset == 0) ||
283 // different transitions
284                 (edit->transition == 0 && transition != 0) ||
285                 (edit->transition != 0 && transition == 0) ||
286 // Position changed
287                 (startproject != edit->startproject) ||
288                 (startsource != edit->startsource) ||
289 // Transition changed
290                 (transition && edit->transition &&
291                         !transition->identical(edit->transition)) ||
292 // Asset changed
293                 (asset && edit->asset &&
294                         !asset->equivalent(*edit->asset, 1, 1, edl)) ||
295 // Nested EDL changed
296                 (nested_edl && edit->nested_edl &&
297                         strcmp(nested_edl->path, edit->nested_edl->path))
298                 )
299         {
300 // Start of edit changed
301                 if(*result < 0 || startproject < *result) *result = startproject;
302         }
303 }
304
305
306 Edit& Edit::operator=(Edit& edit)
307 {
308 //printf("Edit::operator= called\n");
309         copy_from(&edit);
310         return *this;
311 }
312
313 void Edit::synchronize_params(Edit *edit)
314 {
315         copy_from(edit);
316 }
317
318
319 // Comparison for ResourcePixmap drawing
320 int Edit::identical(Edit &edit)
321 {
322         int result = (this->nested_edl == edit.nested_edl &&
323                 this->asset == edit.asset &&
324                 this->startsource == edit.startsource &&
325                 this->startproject == edit.startproject &&
326                 this->length == edit.length &&
327                 this->hard_left == edit.hard_left &&
328                 this->hard_right == edit.hard_right &&
329                 this->transition == edit.transition &&
330                 this->channel == edit.channel);
331         return result;
332 }
333
334 int Edit::operator==(Edit &edit)
335 {
336         return identical(edit);
337 }
338
339 double Edit::frames_per_picon()
340 {
341         return Units::round(picon_w()) / frame_w();
342 }
343
344 double Edit::frame_w()
345 {
346         return track->from_units(1) *
347                 edl->session->sample_rate /
348                 edl->local_session->zoom_sample;
349 }
350
351 double Edit::picon_w()
352 {
353         int w = 0, h = 0;
354         if(asset) {
355                 w = asset->width;
356                 h = asset->height;
357         }
358         else if(nested_edl) {
359                 w = nested_edl->session->output_w;
360                 h = nested_edl->session->output_h;
361         }
362         return w>0 && h>0 ? ((double)edl->local_session->zoom_track*w)/h : 0;
363 }
364
365 int Edit::picon_h()
366 {
367         return edl->local_session->zoom_track;
368 }
369
370
371 int Edit::dump(FILE *fp)
372 {
373         fprintf(fp,"     EDIT %p\n", this); fflush(fp);
374         fprintf(fp,"      nested_edl=%p %s asset=%p %s\n",
375                 nested_edl,
376                 nested_edl ? nested_edl->path : "",
377                 asset,
378                 asset ? asset->path : "");
379         fflush(fp);
380         fprintf(fp,"      channel %d\n", channel);
381         if(transition)
382         {
383                 fprintf(fp,"      TRANSITION %p\n", transition);
384                 transition->dump(fp);
385         }
386         fprintf(fp,"      startsource %jd startproject %jd hard lt/rt %d/%d length %jd\n",
387                 startsource, startproject, hard_left, hard_right, length); fflush(fp);
388         return 0;
389 }
390
391 int Edit::load_properties(FileXML *file, int64_t &startproject)
392 {
393         startsource = file->tag.get_property("STARTSOURCE", (int64_t)0);
394         length = file->tag.get_property("LENGTH", (int64_t)0);
395         hard_left = file->tag.get_property("HARD_LEFT", (int64_t)0);
396         hard_right = file->tag.get_property("HARD_RIGHT", (int64_t)0);
397         user_title[0] = 0;
398         file->tag.get_property("USER_TITLE", user_title);
399         this->startproject = startproject;
400         load_properties_derived(file);
401         return 0;
402 }
403
404 void Edit::shift(int64_t difference)
405 {
406 //printf("Edit::shift 1 %p %jd %jd\n", this, startproject, difference);
407         startproject += difference;
408 //printf("Edit::shift 2 %jd %jd\n", startproject, difference);
409 }
410
411 int Edit::shift_start_in(int edit_mode,
412         int64_t newposition,
413         int64_t oldposition,
414         int edit_edits,
415         int edit_labels,
416         int edit_plugins,
417         int edit_autos,
418         Edits *trim_edits)
419 {
420         int64_t cut_length = newposition - oldposition;
421         int64_t end_previous_source, end_source;
422
423         if(edit_mode == MOVE_ALL_EDITS)
424         {
425                 if(cut_length < length)
426                 {        // clear partial
427                         edits->clear_recursive(oldposition,
428                                 newposition,
429                                 edit_edits,
430                                 edit_labels,
431                                 edit_plugins,
432                                 edit_autos,
433                                 trim_edits);
434                 }
435                 else
436                 {        // clear entire
437                         edits->clear_recursive(oldposition,
438                                 startproject + length,
439                                 edit_edits,
440                                 edit_labels,
441                                 edit_plugins,
442                                 edit_autos,
443                                 trim_edits);
444                 }
445         }
446         else
447         if(edit_mode == MOVE_ONE_EDIT)
448         {
449 // Paste silence and cut
450 //printf("Edit::shift_start_in 1\n");
451                 if(!previous)
452                 {
453                         Edit *new_edit = edits->create_edit();
454                         new_edit->startproject = this->startproject;
455                         new_edit->length = 0;
456                         edits->insert_before(this,
457                                 new_edit);
458                 }
459 //printf("Edit::shift_start_in 2 %p\n", previous);
460
461                 end_previous_source = previous->get_source_end(previous->startsource + previous->length + cut_length);
462                 if(end_previous_source > 0 &&
463                         previous->startsource + previous->length + cut_length > end_previous_source)
464                         cut_length = end_previous_source - previous->startsource - previous->length;
465
466                 if(cut_length < length)
467                 {               // Move in partial
468                         startproject += cut_length;
469                         startsource += cut_length;
470                         length -= cut_length;
471                         previous->length += cut_length;
472 //printf("Edit::shift_start_in 2\n");
473                 }
474                 else
475                 {               // Clear entire edit
476                         cut_length = length;
477                         previous->length += cut_length;
478                         for(Edit* current_edit = this; current_edit; current_edit = current_edit->next)
479                         {
480                                 current_edit->startproject += cut_length;
481                         }
482                         edits->clear_recursive(oldposition + cut_length,
483                                 startproject + cut_length,
484                                 edit_edits,
485                                 edit_labels,
486                                 edit_plugins,
487                                 edit_autos,
488                                 trim_edits);
489                 }
490 //printf("Edit::shift_start_in 3\n");
491         }
492         else
493         if(edit_mode == MOVE_NO_EDITS)
494         {
495                 end_source = get_source_end(startsource + length + cut_length);
496                 if(end_source > 0 && startsource + length + cut_length > end_source)
497                         cut_length = end_source - startsource - length;
498
499                 startsource += cut_length;
500         }
501         return 0;
502 }
503
504 int Edit::shift_start_out(int edit_mode,
505         int64_t newposition,
506         int64_t oldposition,
507         int edit_edits,
508         int edit_labels,
509         int edit_plugins,
510         int edit_autos,
511         Edits *trim_edits)
512 {
513         int64_t cut_length = oldposition - newposition;
514
515
516         if(asset || nested_edl)
517         {
518                 int64_t end_source = get_source_end(1);
519
520 //printf("Edit::shift_start_out 1 %jd %jd\n", startsource, cut_length);
521                 if(end_source > 0 && startsource < cut_length)
522                 {
523                         cut_length = startsource;
524                 }
525         }
526
527         if(edit_mode == MOVE_ALL_EDITS)
528         {
529 //printf("Edit::shift_start_out 10 %jd\n", cut_length);
530                 startsource -= cut_length;
531                 length += cut_length;
532
533                 if(edit_autos)
534                         edits->shift_keyframes_recursive(startproject,
535                                 cut_length);
536                 if(edit_plugins)
537                         edits->shift_effects_recursive(startproject,
538                                 cut_length,
539                                 edit_autos);
540
541                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
542                 {
543                         current_edit->startproject += cut_length;
544                 }
545         }
546         else
547         if(edit_mode == MOVE_ONE_EDIT)
548         {
549                 if(previous)
550                 {
551                         if(cut_length < previous->length)
552                         {   // Cut into previous edit
553                                 previous->length -= cut_length;
554                                 startproject -= cut_length;
555                                 startsource -= cut_length;
556                                 length += cut_length;
557 printf("Edit::shift_start_out 2\n");
558                         }
559                         else
560                         {   // Clear entire previous edit
561                                 cut_length = previous->length;
562                                 previous->length = 0;
563                                 length += cut_length;
564                                 startsource -= cut_length;
565                                 startproject -= cut_length;
566                         }
567                 }
568         }
569         else
570         if(edit_mode == MOVE_NO_EDITS)
571         {
572                 startsource -= cut_length;
573         }
574
575 // Fix infinite length files
576         if(startsource < 0) startsource = 0;
577         return 0;
578 }
579
580 int Edit::shift_end_in(int edit_mode,
581         int64_t newposition,
582         int64_t oldposition,
583         int edit_edits,
584         int edit_labels,
585         int edit_plugins,
586         int edit_autos,
587         Edits *trim_edits)
588 {
589         int64_t cut_length = oldposition - newposition;
590
591         if(edit_mode == MOVE_ALL_EDITS)
592         {
593 //printf("Edit::shift_end_in 1\n");
594                 if(newposition > startproject)
595                 {        // clear partial edit
596 //printf("Edit::shift_end_in %p %p\n", track->edits, edits);
597                         edits->clear_recursive(newposition,
598                                 oldposition,
599                                 edit_edits,
600                                 edit_labels,
601                                 edit_plugins,
602                                 edit_autos,
603                                 trim_edits);
604                 }
605                 else
606                 {        // clear entire edit
607                         edits->clear_recursive(startproject,
608                                 oldposition,
609                                 edit_edits,
610                                 edit_labels,
611                                 edit_plugins,
612                                 edit_autos,
613                                 trim_edits);
614                 }
615         }
616         else
617         if(edit_mode == MOVE_ONE_EDIT)
618         {
619                 if(next)
620                 {
621                         if(next->asset)
622                         {
623                                 int64_t end_source = next->get_source_end(1);
624
625                                 if(end_source > 0 && next->startsource - cut_length < 0)
626                                 {
627                                         cut_length = next->startsource;
628                                 }
629                         }
630
631                         if(cut_length < length)
632                         {
633                                 length -= cut_length;
634                                 next->startproject -= cut_length;
635                                 next->startsource -= cut_length;
636                                 next->length += cut_length;
637 //printf("Edit::shift_end_in 2 %d\n", cut_length);
638                         }
639                         else
640                         {
641                                 cut_length = length;
642                                 next->length += cut_length;
643                                 next->startsource -= cut_length;
644                                 next->startproject -= cut_length;
645                                 length -= cut_length;
646                         }
647                 }
648                 else
649                 {
650                         if(cut_length < length)
651                         {
652                                 length -= cut_length;
653                         }
654                         else
655                         {
656                                 cut_length = length;
657                                 edits->clear_recursive(startproject,
658                                         oldposition,
659                                         edit_edits,
660                                         edit_labels,
661                                         edit_plugins,
662                                         edit_autos,
663                                         trim_edits);
664                         }
665                 }
666         }
667         else
668 // Does nothing for plugins
669         if(edit_mode == MOVE_NO_EDITS)
670         {
671 //printf("Edit::shift_end_in 3\n");
672                 int64_t end_source = get_source_end(1);
673                 if(end_source > 0 && startsource < cut_length)
674                 {
675                         cut_length = startsource;
676                 }
677                 startsource -= cut_length;
678         }
679         return 0;
680 }
681
682 int Edit::shift_end_out(int edit_mode,
683         int64_t newposition,
684         int64_t oldposition,
685         int edit_edits,
686         int edit_labels,
687         int edit_plugins,
688         int edit_autos,
689         Edits *trim_edits)
690 {
691         int64_t cut_length = newposition - oldposition;
692         int64_t endsource = get_source_end(startsource + length + cut_length);
693
694 // check end of edit against end of source file
695         if(endsource > 0 && startsource + length + cut_length > endsource)
696                 cut_length = endsource - startsource - length;
697
698 //printf("Edit::shift_end_out 1 %jd %d %d %d\n", oldposition, newposition, this->length, cut_length);
699         if(edit_mode == MOVE_ALL_EDITS)
700         {
701 // Extend length
702                 this->length += cut_length;
703
704 // Effects are shifted in length extension
705                 if(edit_plugins)
706                         edits->shift_effects_recursive(oldposition /* startproject */,
707                                 cut_length,
708                                 edit_autos);
709                 if(edit_autos)
710                         edits->shift_keyframes_recursive(oldposition /* startproject */,
711                                 cut_length);
712
713                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
714                 {
715                         current_edit->startproject += cut_length;
716                 }
717         }
718         else
719         if(edit_mode == MOVE_ONE_EDIT)
720         {
721                 if(next)
722                 {
723                         if(cut_length < next->length)
724                         {
725                                 length += cut_length;
726                                 next->startproject += cut_length;
727                                 next->startsource += cut_length;
728                                 next->length -= cut_length;
729 //printf("Edit::shift_end_out %d cut_length=%d\n", __LINE__, cut_length);
730                         }
731                         else
732                         {
733 //printf("Edit::shift_end_out %d cut_length=%d next->length=%d\n", __LINE__, cut_length, next->length);
734                                 cut_length = next->length;
735                                 next->startproject += next->length;
736                                 next->startsource += next->length;
737                                 next->length = 0;
738                                 length += cut_length;
739 //track->dump();
740                         }
741                 }
742                 else
743                 {
744                         length += cut_length;
745                 }
746         }
747         else
748         if(edit_mode == MOVE_NO_EDITS)
749         {
750                 startsource += cut_length;
751         }
752         return 0;
753 }
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
784 {
785         int64_t left, right, left_unit, right_unit;
786         if(!transition) return 0;
787         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
788
789         if(cursor_x > left && cursor_x < right)
790         {
791 //              transition->popup_transition(cursor_x, cursor_y);
792                 return 1;
793         }
794         return 0;
795 }
796
797 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
798 {
799         int64_t left, right, left_unit, right_unit;
800         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
801
802         int64_t pixel1, pixel2;
803         pixel1 = left;
804         pixel2 = pixel1 + 10;
805
806 // test left edit
807 // cursor_x is faked in acanvas
808         if(cursor_x >= pixel1 && cursor_x <= pixel2)
809         {
810                 selection = left_unit;
811                 return 1;     // left handle
812         }
813
814         //int64_t endproject = startproject + length;
815         pixel2 = right;
816         pixel1 = pixel2 - 10;
817
818 // test right edit
819         if(cursor_x >= pixel1 && cursor_x <= pixel2)
820         {
821                 selection = right_unit;
822                 return 2;     // right handle
823         }
824         return 0;
825 }
826