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