rework keyframe hide popup, keyframe auto render, textbox set_selection wide text
[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         detach_transition();
217         transition = new Transition(edl,
218                 this,
219                 title,
220                 track->to_units(edl->session->default_transition_length, 1));
221 }
222
223 void Edit::detach_transition()
224 {
225         if(transition) delete transition;
226         transition = 0;
227 }
228
229 int Edit::silence()
230 {
231         if(asset || nested_edl)
232                 return 0;
233         else
234                 return 1;
235 }
236
237
238 void Edit::copy_from(Edit *edit)
239 {
240         this->nested_edl = edl->nested_edls->get_copy(edit->nested_edl);
241         this->asset = edl->assets->update(edit->asset);
242         this->startsource = edit->startsource;
243         this->startproject = edit->startproject;
244         this->length = edit->length;
245         strcpy (this->user_title, edit->user_title);
246
247         if(edit->transition)
248         {
249                 if(!transition) transition = new Transition(edl,
250                         this,
251                         edit->transition->title,
252                         edit->transition->length);
253                 *this->transition = *edit->transition;
254         }
255         this->channel = edit->channel;
256 }
257
258 void Edit::equivalent_output(Edit *edit, int64_t *result)
259 {
260 // End of edit changed
261         if(startproject + length != edit->startproject + edit->length)
262         {
263                 int64_t new_length = MIN(startproject + length,
264                         edit->startproject + edit->length);
265                 if(*result < 0 || new_length < *result)
266                         *result = new_length;
267         }
268
269         if(
270 // Different nested EDLs
271                 (edit->nested_edl && !nested_edl) ||
272                 (!edit->nested_edl && nested_edl) ||
273 // Different assets
274                 (edit->asset == 0 && asset != 0) ||
275                 (edit->asset != 0 && asset == 0) ||
276 // different transitions
277                 (edit->transition == 0 && transition != 0) ||
278                 (edit->transition != 0 && transition == 0) ||
279 // Position changed
280                 (startproject != edit->startproject) ||
281                 (startsource != edit->startsource) ||
282 // Transition changed
283                 (transition && edit->transition &&
284                         !transition->identical(edit->transition)) ||
285 // Asset changed
286                 (asset && edit->asset &&
287                         !asset->equivalent(*edit->asset, 1, 1)) ||
288 // Nested EDL changed
289                 (nested_edl && edit->nested_edl &&
290                         strcmp(nested_edl->path, edit->nested_edl->path))
291                 )
292         {
293 // Start of edit changed
294                 if(*result < 0 || startproject < *result) *result = startproject;
295         }
296 }
297
298
299 Edit& Edit::operator=(Edit& edit)
300 {
301 //printf("Edit::operator= called\n");
302         copy_from(&edit);
303         return *this;
304 }
305
306 void Edit::synchronize_params(Edit *edit)
307 {
308         copy_from(edit);
309 }
310
311
312 // Comparison for ResourcePixmap drawing
313 int Edit::identical(Edit &edit)
314 {
315         int result = (this->nested_edl == edit.nested_edl &&
316                 this->asset == edit.asset &&
317                 this->startsource == edit.startsource &&
318                 this->startproject == edit.startproject &&
319                 this->length == edit.length &&
320                 this->transition == edit.transition &&
321                 this->channel == edit.channel);
322         return result;
323 }
324
325 int Edit::operator==(Edit &edit)
326 {
327         return identical(edit);
328 }
329
330 double Edit::frames_per_picon()
331 {
332         return Units::round(picon_w()) / frame_w();
333 }
334
335 double Edit::frame_w()
336 {
337         return track->from_units(1) *
338                 edl->session->sample_rate /
339                 edl->local_session->zoom_sample;
340 }
341
342 double Edit::picon_w()
343 {
344         int w = 0, h = 0;
345         if(asset) {
346                 w = asset->width;
347                 h = asset->height;
348         }
349         else if(nested_edl) {
350                 w = nested_edl->session->output_w;
351                 h = nested_edl->session->output_h;
352         }
353         return w>0 && h>0 ? ((double)edl->local_session->zoom_track*w)/h : 0;
354 }
355
356 int Edit::picon_h()
357 {
358         return edl->local_session->zoom_track;
359 }
360
361
362 int Edit::dump(FILE *fp)
363 {
364         fprintf(fp,"     EDIT %p\n", this); fflush(fp);
365         fprintf(fp,"      nested_edl=%p %s asset=%p %s\n",
366                 nested_edl,
367                 nested_edl ? nested_edl->path : "",
368                 asset,
369                 asset ? asset->path : "");
370         fflush(fp);
371         fprintf(fp,"      channel %d\n", channel);
372         if(transition)
373         {
374                 fprintf(fp,"      TRANSITION %p\n", transition);
375                 transition->dump(fp);
376         }
377         fprintf(fp,"      startsource %jd startproject %jd length %jd\n",
378                 startsource, startproject, length); fflush(fp);
379         return 0;
380 }
381
382 int Edit::load_properties(FileXML *file, int64_t &startproject)
383 {
384         startsource = file->tag.get_property("STARTSOURCE", (int64_t)0);
385         length = file->tag.get_property("LENGTH", (int64_t)0);
386         user_title[0] = 0;
387         file->tag.get_property("USER_TITLE", user_title);
388         this->startproject = startproject;
389         load_properties_derived(file);
390         return 0;
391 }
392
393 void Edit::shift(int64_t difference)
394 {
395 //printf("Edit::shift 1 %p %jd %jd\n", this, startproject, difference);
396         startproject += difference;
397 //printf("Edit::shift 2 %jd %jd\n", startproject, difference);
398 }
399
400 int Edit::shift_start_in(int edit_mode,
401         int64_t newposition,
402         int64_t oldposition,
403         int edit_edits,
404         int edit_labels,
405         int edit_plugins,
406         int edit_autos,
407         Edits *trim_edits)
408 {
409         int64_t cut_length = newposition - oldposition;
410         int64_t end_previous_source, end_source;
411
412         if(edit_mode == MOVE_ALL_EDITS)
413         {
414                 if(cut_length < length)
415                 {        // clear partial
416                         edits->clear_recursive(oldposition,
417                                 newposition,
418                                 edit_edits,
419                                 edit_labels,
420                                 edit_plugins,
421                                 edit_autos,
422                                 trim_edits);
423                 }
424                 else
425                 {        // clear entire
426                         edits->clear_recursive(oldposition,
427                                 startproject + length,
428                                 edit_edits,
429                                 edit_labels,
430                                 edit_plugins,
431                                 edit_autos,
432                                 trim_edits);
433                 }
434         }
435         else
436         if(edit_mode == MOVE_ONE_EDIT)
437         {
438 // Paste silence and cut
439 //printf("Edit::shift_start_in 1\n");
440                 if(!previous)
441                 {
442                         Edit *new_edit = edits->create_edit();
443                         new_edit->startproject = this->startproject;
444                         new_edit->length = 0;
445                         edits->insert_before(this,
446                                 new_edit);
447                 }
448 //printf("Edit::shift_start_in 2 %p\n", previous);
449
450                 end_previous_source = previous->get_source_end(previous->startsource + previous->length + cut_length);
451                 if(end_previous_source > 0 &&
452                         previous->startsource + previous->length + cut_length > end_previous_source)
453                         cut_length = end_previous_source - previous->startsource - previous->length;
454
455                 if(cut_length < length)
456                 {               // Move in partial
457                         startproject += cut_length;
458                         startsource += cut_length;
459                         length -= cut_length;
460                         previous->length += cut_length;
461 //printf("Edit::shift_start_in 2\n");
462                 }
463                 else
464                 {               // Clear entire edit
465                         cut_length = length;
466                         previous->length += cut_length;
467                         for(Edit* current_edit = this; current_edit; current_edit = current_edit->next)
468                         {
469                                 current_edit->startproject += cut_length;
470                         }
471                         edits->clear_recursive(oldposition + cut_length,
472                                 startproject + cut_length,
473                                 edit_edits,
474                                 edit_labels,
475                                 edit_plugins,
476                                 edit_autos,
477                                 trim_edits);
478                 }
479 //printf("Edit::shift_start_in 3\n");
480         }
481         else
482         if(edit_mode == MOVE_NO_EDITS)
483         {
484                 end_source = get_source_end(startsource + length + cut_length);
485                 if(end_source > 0 && startsource + length + cut_length > end_source)
486                         cut_length = end_source - startsource - length;
487
488                 startsource += cut_length;
489         }
490         return 0;
491 }
492
493 int Edit::shift_start_out(int edit_mode,
494         int64_t newposition,
495         int64_t oldposition,
496         int edit_edits,
497         int edit_labels,
498         int edit_plugins,
499         int edit_autos,
500         Edits *trim_edits)
501 {
502         int64_t cut_length = oldposition - newposition;
503
504
505         if(asset || nested_edl)
506         {
507                 int64_t end_source = get_source_end(1);
508
509 //printf("Edit::shift_start_out 1 %jd %jd\n", startsource, cut_length);
510                 if(end_source > 0 && startsource < cut_length)
511                 {
512                         cut_length = startsource;
513                 }
514         }
515
516         if(edit_mode == MOVE_ALL_EDITS)
517         {
518 //printf("Edit::shift_start_out 10 %jd\n", cut_length);
519                 startsource -= cut_length;
520                 length += cut_length;
521
522                 if(edit_autos)
523                         edits->shift_keyframes_recursive(startproject,
524                                 cut_length);
525                 if(edit_plugins)
526                         edits->shift_effects_recursive(startproject,
527                                 cut_length,
528                                 edit_autos);
529
530                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
531                 {
532                         current_edit->startproject += cut_length;
533                 }
534         }
535         else
536         if(edit_mode == MOVE_ONE_EDIT)
537         {
538                 if(previous)
539                 {
540                         if(cut_length < previous->length)
541                         {   // Cut into previous edit
542                                 previous->length -= cut_length;
543                                 startproject -= cut_length;
544                                 startsource -= cut_length;
545                                 length += cut_length;
546 printf("Edit::shift_start_out 2\n");
547                         }
548                         else
549                         {   // Clear entire previous edit
550                                 cut_length = previous->length;
551                                 previous->length = 0;
552                                 length += cut_length;
553                                 startsource -= cut_length;
554                                 startproject -= cut_length;
555                         }
556                 }
557         }
558         else
559         if(edit_mode == MOVE_NO_EDITS)
560         {
561                 startsource -= cut_length;
562         }
563
564 // Fix infinite length files
565         if(startsource < 0) startsource = 0;
566         return 0;
567 }
568
569 int Edit::shift_end_in(int edit_mode,
570         int64_t newposition,
571         int64_t oldposition,
572         int edit_edits,
573         int edit_labels,
574         int edit_plugins,
575         int edit_autos,
576         Edits *trim_edits)
577 {
578         int64_t cut_length = oldposition - newposition;
579
580         if(edit_mode == MOVE_ALL_EDITS)
581         {
582 //printf("Edit::shift_end_in 1\n");
583                 if(newposition > startproject)
584                 {        // clear partial edit
585 //printf("Edit::shift_end_in %p %p\n", track->edits, edits);
586                         edits->clear_recursive(newposition,
587                                 oldposition,
588                                 edit_edits,
589                                 edit_labels,
590                                 edit_plugins,
591                                 edit_autos,
592                                 trim_edits);
593                 }
594                 else
595                 {        // clear entire edit
596                         edits->clear_recursive(startproject,
597                                 oldposition,
598                                 edit_edits,
599                                 edit_labels,
600                                 edit_plugins,
601                                 edit_autos,
602                                 trim_edits);
603                 }
604         }
605         else
606         if(edit_mode == MOVE_ONE_EDIT)
607         {
608                 if(next)
609                 {
610                         if(next->asset)
611                         {
612                                 int64_t end_source = next->get_source_end(1);
613
614                                 if(end_source > 0 && next->startsource - cut_length < 0)
615                                 {
616                                         cut_length = next->startsource;
617                                 }
618                         }
619
620                         if(cut_length < length)
621                         {
622                                 length -= cut_length;
623                                 next->startproject -= cut_length;
624                                 next->startsource -= cut_length;
625                                 next->length += cut_length;
626 //printf("Edit::shift_end_in 2 %d\n", cut_length);
627                         }
628                         else
629                         {
630                                 cut_length = length;
631                                 next->length += cut_length;
632                                 next->startsource -= cut_length;
633                                 next->startproject -= cut_length;
634                                 length -= cut_length;
635                         }
636                 }
637                 else
638                 {
639                         if(cut_length < length)
640                         {
641                                 length -= cut_length;
642                         }
643                         else
644                         {
645                                 cut_length = length;
646                                 edits->clear_recursive(startproject,
647                                         oldposition,
648                                         edit_edits,
649                                         edit_labels,
650                                         edit_plugins,
651                                         edit_autos,
652                                         trim_edits);
653                         }
654                 }
655         }
656         else
657 // Does nothing for plugins
658         if(edit_mode == MOVE_NO_EDITS)
659         {
660 //printf("Edit::shift_end_in 3\n");
661                 int64_t end_source = get_source_end(1);
662                 if(end_source > 0 && startsource < cut_length)
663                 {
664                         cut_length = startsource;
665                 }
666                 startsource -= cut_length;
667         }
668         return 0;
669 }
670
671 int Edit::shift_end_out(int edit_mode,
672         int64_t newposition,
673         int64_t oldposition,
674         int edit_edits,
675         int edit_labels,
676         int edit_plugins,
677         int edit_autos,
678         Edits *trim_edits)
679 {
680         int64_t cut_length = newposition - oldposition;
681         int64_t endsource = get_source_end(startsource + length + cut_length);
682
683 // check end of edit against end of source file
684         if(endsource > 0 && startsource + length + cut_length > endsource)
685                 cut_length = endsource - startsource - length;
686
687 //printf("Edit::shift_end_out 1 %jd %d %d %d\n", oldposition, newposition, this->length, cut_length);
688         if(edit_mode == MOVE_ALL_EDITS)
689         {
690 // Extend length
691                 this->length += cut_length;
692
693 // Effects are shifted in length extension
694                 if(edit_plugins)
695                         edits->shift_effects_recursive(oldposition /* startproject */, 
696                                 cut_length,
697                                 edit_autos);
698                 if(edit_autos)
699                         edits->shift_keyframes_recursive(oldposition /* startproject */, 
700                                 cut_length);
701
702                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
703                 {
704                         current_edit->startproject += cut_length;
705                 }
706         }
707         else
708         if(edit_mode == MOVE_ONE_EDIT)
709         {
710                 if(next)
711                 {
712                         if(cut_length < next->length)
713                         {
714                                 length += cut_length;
715                                 next->startproject += cut_length;
716                                 next->startsource += cut_length;
717                                 next->length -= cut_length;
718 //printf("Edit::shift_end_out 2 %d\n", cut_length);
719                         }
720                         else
721                         {
722                                 cut_length = next->length;
723                                 next->length = 0;
724                                 length += cut_length;
725                         }
726                 }
727                 else
728                 {
729                         length += cut_length;
730                 }
731         }
732         else
733         if(edit_mode == MOVE_NO_EDITS)
734         {
735                 startsource += cut_length;
736         }
737         return 0;
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
764
765
766
767
768 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
769 {
770         int64_t left, right, left_unit, right_unit;
771         if(!transition) return 0;
772         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
773
774         if(cursor_x > left && cursor_x < right)
775         {
776 //              transition->popup_transition(cursor_x, cursor_y);
777                 return 1;
778         }
779         return 0;
780 }
781
782 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
783 {
784         int64_t left, right, left_unit, right_unit;
785         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
786
787         int64_t pixel1, pixel2;
788         pixel1 = left;
789         pixel2 = pixel1 + 10;
790
791 // test left edit
792 // cursor_x is faked in acanvas
793         if(cursor_x >= pixel1 && cursor_x <= pixel2)
794         {
795                 selection = left_unit;
796                 return 1;     // left handle
797         }
798
799         //int64_t endproject = startproject + length;
800         pixel2 = right;
801         pixel1 = pixel2 - 10;
802
803 // test right edit      
804         if(cursor_x >= pixel1 && cursor_x <= pixel2)
805         {
806                 selection = right_unit;
807                 return 2;     // right handle
808         }
809         return 0;
810 }
811