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