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