shuttle keydef tweak, add copy_flags to copy edl, zoombar layout tweak
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / edl.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2012 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 "atrack.h"
25 #include "autoconf.h"
26 #include "automation.h"
27 #include "awindowgui.h"
28 #include "bccmodels.h"
29 #include "bchash.h"
30 #include "bcsignals.h"
31 #include "clip.h"
32 #include "cstrdup.h"
33 #include "clipedls.h"
34 #include "edits.h"
35 #include "edl.h"
36 #include "edlsession.h"
37 #include "filexml.h"
38 #include "floatauto.h"
39 #include "floatautos.h"
40 #include "guicast.h"
41 #include "indexstate.h"
42 #include "interlacemodes.h"
43 #include "labels.h"
44 #include "localsession.h"
45 #include "maskautos.h"
46 #include "mutex.h"
47 #include "panauto.h"
48 #include "panautos.h"
49 #include "playbackconfig.h"
50 #include "playabletracks.h"
51 #include "plugin.h"
52 #include "pluginset.h"
53 #include "preferences.h"
54 #include "recordconfig.h"
55 #include "recordlabel.h"
56 #include "sharedlocation.h"
57 #include "theme.h"
58 #include "tracks.h"
59 #include "transportque.inc"
60 #include "versioninfo.h"
61 #include "vedit.h"
62 #include "vtrack.h"
63
64
65
66
67 EDL::EDL(EDL *parent_edl)
68  : Indexable(0)
69 {
70         this->parent_edl = parent_edl;
71         tracks = 0;
72         labels = 0;
73         local_session = 0;
74         id = next_id();
75         path[0] = 0;
76 }
77
78
79 EDL::~EDL()
80 {
81
82         delete tracks;
83         delete labels;
84         delete local_session;
85         remove_vwindow_edls();
86         if( !parent_edl ) {
87                 delete assets;
88                 delete session;
89         }
90 }
91
92
93 void EDL::create_objects()
94 {
95         tracks = new Tracks(this);
96         assets = !parent_edl ? new Assets(this) : parent_edl->assets;
97         session = !parent_edl ? new EDLSession(this) : parent_edl->session;
98         local_session = new LocalSession(this);
99         labels = new Labels(this, "LABELS");
100 }
101
102 EDL& EDL::operator=(EDL &edl)
103 {
104 printf("EDL::operator= 1\n");
105         copy_all(&edl);
106         return *this;
107 }
108
109 int EDL::load_defaults(BC_Hash *defaults)
110 {
111         if( !parent_edl )
112                 session->load_defaults(defaults);
113
114         local_session->load_defaults(defaults);
115         return 0;
116 }
117
118 int EDL::save_defaults(BC_Hash *defaults)
119 {
120         if( !parent_edl )
121                 session->save_defaults(defaults);
122
123         local_session->save_defaults(defaults);
124         return 0;
125 }
126
127 void EDL::boundaries()
128 {
129         session->boundaries();
130         local_session->boundaries();
131 }
132
133 int EDL::create_default_tracks()
134 {
135
136         for( int i=0; i<session->video_tracks; ++i ) {
137                 tracks->add_video_track(0, 0);
138         }
139         for( int i=0; i<session->audio_tracks; ++i ) {
140                 tracks->add_audio_track(0, 0);
141         }
142         return 0;
143 }
144
145 int EDL::load_xml(FileXML *file, uint32_t load_flags)
146 {
147         int result = 0;
148         folders.clear();
149
150         if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
151                 remove_vwindow_edls();
152         }
153
154
155 // Search for start of master EDL.
156
157 // The parent_edl test caused clip creation to fail since those XML files
158 // contained an EDL tag.
159
160 // The parent_edl test is required to make EDL loading work because
161 // when loading an EDL the EDL tag is already read by the parent.
162
163         if( !parent_edl ) {
164                 do {
165                   result = file->read_tag();
166                 } while(!result &&
167                         !file->tag.title_is("XML") &&
168                         !file->tag.title_is("EDL"));
169         }
170         return result ? result : read_xml(file, load_flags);
171 }
172
173 int EDL::read_xml(FileXML *file, uint32_t load_flags)
174 {
175         int result = 0;
176 // Track numbering offset for replacing undo data.
177         int track_offset = 0;
178
179 // Get path for backups
180         file->tag.get_property("path", path);
181
182 // Erase everything
183         if( (load_flags & LOAD_ALL) == LOAD_ALL ||
184                 (load_flags & LOAD_EDITS) == LOAD_EDITS ) {
185                 while(tracks->last) delete tracks->last;
186         }
187
188         if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
189                 clips.clear();
190                 mixers.remove_all_objects();
191         }
192
193         if( load_flags & LOAD_TIMEBAR ) {
194                 while(labels->last) delete labels->last;
195                 local_session->unset_inpoint();
196                 local_session->unset_outpoint();
197         }
198
199 // This was originally in LocalSession::load_xml
200         if( load_flags & LOAD_SESSION ) {
201                 local_session->clipboard_length = 0;
202         }
203
204         do {
205                 result = file->read_tag();
206
207                 if( !result ) {
208                         if( file->tag.title_is("/XML") ||
209                                 file->tag.title_is("/EDL") ||
210                                 file->tag.title_is("/CLIP_EDL") ||
211                                 file->tag.title_is("/NESTED_EDL") ||
212                                 file->tag.title_is("/VWINDOW_EDL") ) {
213                                 result = 1;
214                         }
215                         else
216                         if( file->tag.title_is("CLIPBOARD") ) {
217                                 local_session->clipboard_length =
218                                         file->tag.get_property("LENGTH", (double)0);
219                         }
220                         else
221                         if( file->tag.title_is("VIDEO") ) {
222                                 if( (load_flags & LOAD_VCONFIG) &&
223                                         (load_flags & LOAD_SESSION) )
224                                         session->load_video_config(file, 0, load_flags);
225                                 else
226                                         result = file->skip_tag();
227                         }
228                         else
229                         if( file->tag.title_is("AUDIO") ) {
230                                 if( (load_flags & LOAD_ACONFIG) &&
231                                         (load_flags & LOAD_SESSION) )
232                                         session->load_audio_config(file, 0, load_flags);
233                                 else
234                                         result = file->skip_tag();
235                         }
236                         else
237                         if( file->tag.title_is("FOLDERS") ) {
238                                 result = folders.load_xml(file);
239                         }
240                         else
241                         if( file->tag.title_is("MIXERS") ) {
242                                 if( (load_flags & LOAD_SESSION) )
243                                         mixers.load(file);
244                                 else
245                                         result = file->skip_tag();
246                         }
247                         else
248                         if( file->tag.title_is("ASSETS") ) {
249                                 if( load_flags & LOAD_ASSETS )
250                                         assets->load(file, load_flags);
251                                 else
252                                         result = file->skip_tag();
253                         }
254                         else
255                         if( file->tag.title_is(labels->xml_tag) ) {
256                                 if( load_flags & LOAD_TIMEBAR )
257                                         labels->load(file, load_flags);
258                                 else
259                                         result = file->skip_tag();
260                         }
261                         else
262                         if( file->tag.title_is("LOCALSESSION") ) {
263                                 if( (load_flags & LOAD_SESSION) ||
264                                         (load_flags & LOAD_TIMEBAR) )
265                                         local_session->load_xml(file, load_flags);
266                                 else
267                                         result = file->skip_tag();
268                         }
269                         else
270                         if( file->tag.title_is("SESSION") ) {
271                                 if( (load_flags & LOAD_SESSION) &&
272                                         !parent_edl )
273                                         session->load_xml(file, 0, load_flags);
274                                 else
275                                         result = file->skip_tag();
276                         }
277                         else
278                         if( file->tag.title_is("TRACK") ) {
279                                 tracks->load(file, track_offset, load_flags);
280                         }
281                         else
282 // Sub EDL.
283 // Causes clip creation to fail because that involves an opening EDL tag.
284                         if( file->tag.title_is("CLIP_EDL") && !parent_edl ) {
285                                 EDL *new_edl = new EDL(this);
286                                 new_edl->create_objects();
287                                 new_edl->read_xml(file, LOAD_ALL);
288                                 if( (load_flags & LOAD_ALL) == LOAD_ALL )
289                                         clips.add_clip(new_edl);
290                                 new_edl->remove_user();
291                         }
292                         else
293                         if( file->tag.title_is("NESTED_EDL") ) {
294                                 EDL *nested_edl = new EDL;
295                                 nested_edl->create_objects();
296                                 nested_edl->read_xml(file, LOAD_ALL);
297                                 if( (load_flags & LOAD_ALL) == LOAD_ALL )
298                                         nested_edls.get_nested(nested_edl);
299                                 nested_edl->remove_user();
300                         }
301                         else
302                         if( file->tag.title_is("VWINDOW_EDL") && !parent_edl ) {
303                                 EDL *new_edl = new EDL(this);
304                                 new_edl->create_objects();
305                                 new_edl->read_xml(file, LOAD_ALL);
306
307
308                                 if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
309 //                                              if( vwindow_edl && !vwindow_edl_shared )
310 //                                                      vwindow_edl->remove_user();
311 //                                              vwindow_edl_shared = 0;
312 //                                              vwindow_edl = new_edl;
313
314                                         append_vwindow_edl(new_edl, 0);
315
316                                 }
317                                 else
318 // Discard if not replacing EDL
319                                 {
320                                         new_edl->remove_user();
321                                         new_edl = 0;
322                                 }
323                         }
324                 }
325         } while(!result);
326
327         boundaries();
328 //dump();
329
330         return 0;
331 }
332
333 // Output path is the path of the output file if name truncation is desired.
334 // It is a "" if complete names should be used.
335 // Called recursively by copy for clips, thus the string can't be terminated.
336 // The string is not terminated in this call.
337 int EDL::save_xml(FileXML *file, const char *output_path)
338 {
339         copy(COPY_EDL, file, output_path, 0);
340         return 0;
341 }
342
343 int EDL::copy_all(EDL *edl)
344 {
345         if( this == edl ) return 0;
346         folder_no = edl->folder_no;
347         update_index(edl);
348         copy_session(edl);
349         copy_assets(edl);
350         copy_clips(edl);
351         copy_nested(edl);
352         copy_mixers(edl);
353         tracks->copy_from(edl->tracks);
354         labels->copy_from(edl->labels);
355         return 0;
356 }
357
358 void EDL::copy_clips(EDL *edl)
359 {
360         if( this == edl ) return;
361
362         remove_vwindow_edls();
363
364 //      if( vwindow_edl && !vwindow_edl_shared )
365 //              vwindow_edl->remove_user();
366 //      vwindow_edl = 0;
367 //      vwindow_edl_shared = 0;
368
369         for( int i=0; i<edl->total_vwindow_edls(); ++i ) {
370                 EDL *new_edl = new EDL(this);
371                 new_edl->create_objects();
372                 new_edl->copy_all(edl->get_vwindow_edl(i));
373                 append_vwindow_edl(new_edl, 0);
374         }
375
376         clips.clear();
377         for( int i=0; i<edl->clips.size(); ++i ) add_clip(edl->clips[i]);
378 }
379
380 void EDL::copy_nested(EDL *edl)
381 {
382         if( this == edl ) return;
383         nested_edls.copy_nested(edl->nested_edls);
384 }
385
386 void EDL::copy_assets(EDL *edl)
387 {
388         if( this == edl ) return;
389
390         if( !parent_edl ) {
391                 assets->copy_from(edl->assets);
392         }
393 }
394
395 void EDL::copy_mixers(EDL *edl)
396 {
397         if( this == edl ) return;
398         mixers.copy_from(edl->mixers);
399 }
400
401 void EDL::copy_session(EDL *edl, int session_only)
402 {
403         if( this == edl ) return;
404
405         if( !session_only ) {
406                 strcpy(this->path, edl->path);
407                 folders.copy_from(&edl->folders);
408         }
409
410         if( !parent_edl ) {
411                 session->copy(edl->session);
412         }
413
414         if( session_only <= 0 ) {
415                 local_session->copy_from(edl->local_session);
416         }
417 }
418
419 int EDL::copy_assets(int copy_flags, double start, double end,
420                 FileXML *file, const char *output_path)
421 {
422         ArrayList<Asset*> asset_list;
423         Track* current;
424
425         file->tag.set_title("ASSETS");
426         file->append_tag();
427         file->append_newline();
428
429 // Copy everything for a save
430         if( (copy_flags & COPY_ALL_ASSETS) ) {
431                 for( Asset *asset=assets->first; asset; asset=asset->next ) {
432                         asset_list.append(asset);
433                 }
434         }
435         if( (copy_flags & COPY_USED_ASSETS) ) {
436 // Copy just the ones being used.
437                 for( current = tracks->first; current; current = NEXT ) {
438                         if( !current->record ) continue;
439                         current->copy_assets(start, end, &asset_list);
440                 }
441         }
442
443 // Paths relativised here
444         for( int i=0; i<asset_list.size(); ++i ) {
445                 asset_list[i]->write(file, 0, output_path);
446         }
447
448         file->tag.set_title("/ASSETS");
449         file->append_tag();
450         file->append_newline();
451         file->append_newline();
452         return 0;
453 }
454
455
456 int EDL::copy(int copy_flags, double start, double end,
457         FileXML *file, const char *output_path, int rewind_it)
458 {
459         file->tag.set_title("EDL");
460         file->tag.set_property("VERSION", CINELERRA_VERSION);
461 // Save path for restoration of the project title from a backup.
462         if( this->path[0] ) file->tag.set_property("PATH", path);
463         return copy_xml(copy_flags, start, end, file, "/EDL", output_path, rewind_it);
464 }
465 int EDL::copy(int copy_flags, FileXML *file, const char *output_path, int rewind_it)
466 {
467         return copy(copy_flags, 0., tracks->total_length(),
468                 file, output_path, rewind_it);
469 }
470
471 int EDL::copy_clip(int copy_flags, double start, double end,
472         FileXML *file, const char *output_path, int rewind_it)
473 {
474         file->tag.set_title("CLIP_EDL");
475         return copy_xml(copy_flags, start, end, file, "/CLIP_EDL", output_path, rewind_it);
476 }
477 int EDL::copy_clip(int copy_flags, FileXML *file, const char *output_path, int rewind_it)
478 {
479         return copy_clip(copy_flags, 0., tracks->total_length(),
480                 file, output_path, rewind_it);
481 }
482
483 int EDL::copy_nested(int copy_flags, double start, double end,
484         FileXML *file, const char *output_path, int rewind_it)
485 {
486         file->tag.set_title("NESTED_EDL");
487         if( this->path[0] ) file->tag.set_property("PATH", path);
488         return copy_xml(copy_flags, start, end, file, "/NESTED_EDL", output_path, rewind_it);
489 }
490 int EDL::copy_nested(int copy_flags, FileXML *file, const char *output_path, int rewind_it)
491 {
492         return copy_nested(copy_flags, 0., tracks->total_length(),
493                 file, output_path, rewind_it);
494 }
495
496 int EDL::copy_vwindow(int copy_flags, double start, double end,
497         FileXML *file, const char *output_path, int rewind_it)
498 {
499         file->tag.set_title("VWINDOW_EDL");
500         return copy_xml(copy_flags, start, end, file, "/VWINDOW_EDL", output_path, rewind_it);
501 }
502 int EDL::copy_vwindow(int copy_flags, FileXML *file, const char *output_path, int rewind_it)
503 {
504         return copy_vwindow(copy_flags, 0., tracks->total_length(),
505                 file, output_path, rewind_it);
506 }
507
508 int EDL::copy_xml(int copy_flags, double start, double end,
509          FileXML *file, const char *closer, const char *output_path,
510         int rewind_it)
511 {
512         file->append_tag();
513         file->append_newline();
514 // Set clipboard samples only if copying to clipboard
515         if( (copy_flags & COPY_LENGTH) ) {
516                 file->tag.set_title("CLIPBOARD");
517                 file->tag.set_property("LENGTH", end - start);
518                 file->append_tag();
519                 file->tag.set_title("/CLIPBOARD");
520                 file->append_tag();
521                 file->append_newline();
522                 file->append_newline();
523         }
524 //printf("EDL::copy 1\n");
525
526 // Sessions
527         if( (copy_flags & COPY_LOCAL_SESSION) )
528                 local_session->save_xml(file, start);
529
530 // Top level stuff.
531 // Need to copy all this from child EDL if pasting is desired.
532
533         if( (copy_flags & COPY_SESSION) )
534                 session->save_xml(file);
535
536         if( (copy_flags & COPY_VIDEO_CONFIG) )
537                 session->save_video_config(file);
538
539         if( (copy_flags & COPY_AUDIO_CONFIG) )
540                 session->save_audio_config(file);
541
542         if( (copy_flags & COPY_FOLDERS) )
543                 folders.save_xml(file);
544
545         if( (copy_flags & (COPY_ALL_ASSETS | COPY_USED_ASSETS)) )
546                 copy_assets(copy_flags, start, end, file, output_path);
547
548         if( (copy_flags & COPY_NESTED_EDL) ) {
549                 for( int i=0; i<nested_edls.size(); ++i )
550                         nested_edls[i]->copy_nested(copy_flags,
551                                 file, output_path, 0);
552         }
553 // Clips
554         if( (copy_flags & COPY_CLIPS) ) {
555                 for( int i=0; i<clips.size(); ++i )
556                         clips[i]->copy_clip(copy_flags, file, output_path, 0);
557         }
558
559         if( (copy_flags & COPY_VWINDOWS) ) {
560                 for( int i=0; i<total_vwindow_edls(); ++i )
561                         get_vwindow_edl(i)->copy_vwindow(copy_flags,
562                                 file, output_path, 0);
563         }
564
565         if( (copy_flags & COPY_MIXERS) )
566                 mixers.save(file);
567
568         file->append_newline();
569         file->append_newline();
570
571         if( (copy_flags & COPY_LABELS) )
572                 labels->copy(start, end, file);
573
574         tracks->copy(copy_flags, start, end, file, output_path);
575
576 // terminate file
577         file->tag.set_title(closer);
578         file->append_tag();
579         file->append_newline();
580
581 // For editing operations we want to rewind it for immediate pasting.
582 // For clips and saving to disk leave it alone.
583         if( rewind_it ) {
584                 file->terminate_string();
585                 file->rewind();
586         }
587         return 0;
588 }
589
590 void EDL::copy_indexables(EDL *edl)
591 {
592         for( Track *track=edl->tracks->first; track; track=track->next ) {
593                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
594                         if( edit->asset )
595                                 assets->update(edit->asset);
596                         if( edit->nested_edl )
597                                 nested_edls.get_nested(edit->nested_edl);
598                 }
599         }
600 }
601
602 EDL *EDL::new_nested(EDL *edl, const char *path)
603 {
604         EDL *nested = new EDL;  // no parent for nested edl
605         nested->create_objects();
606         nested->copy_session(edl);
607         nested->set_path(path);
608         nested->update_index(edl);
609         nested->copy_indexables(edl);
610         nested->tracks->copy_from(edl->tracks);
611         nested_edls.append(nested);
612         return nested;
613 }
614
615 EDL *EDL::create_nested_clip(EDL *nested)
616 {
617         EDL *new_edl = new EDL(this);  // parent for clip edl
618         new_edl->create_objects();
619         new_edl->create_nested(nested);
620         return new_edl;
621 }
622
623 void EDL::create_nested(EDL *nested)
624 {
625 // Keep frame rate, sample rate, and output size unchanged.
626 // Nest all video & audio outputs
627         session->video_tracks = 1;
628         session->audio_tracks = nested->session->audio_channels;
629         create_default_tracks();
630         insert_asset(0, nested, 0, 0, 0);
631 }
632
633 void EDL::retrack()
634 {
635         int min_w = session->output_w, min_h = session->output_h;
636         for( Track *track=tracks->first; track!=0; track=track->next ) {
637                 if( track->data_type != TRACK_VIDEO ) continue;
638                 int w = min_w, h = min_h;
639                 for( Edit *current=track->edits->first; current!=0; current=NEXT ) {
640                         Indexable* indexable = current->get_source();
641                         if( !indexable ) continue;
642                         int edit_w = indexable->get_w(), edit_h = indexable->get_h();
643                         if( w < edit_w ) w = edit_w;
644                         if( h < edit_h ) h = edit_h;
645                 }
646                 if( track->track_w == w && track->track_h == h ) continue;
647                 ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->
648                         translate_masks( (w - track->track_w) / 2, (h - track->track_h) / 2);
649                 track->track_w = w;  track->track_h = h;
650         }
651 }
652
653 void EDL::rechannel()
654 {
655         for( Track *current=tracks->first; current; current=NEXT ) {
656                 if( current->data_type == TRACK_AUDIO ) {
657                         PanAutos *autos = (PanAutos*)current->automation->autos[AUTOMATION_PAN];
658                         ((PanAuto*)autos->default_auto)->rechannel();
659                         for( PanAuto *keyframe = (PanAuto*)autos->first;
660                              keyframe; keyframe = (PanAuto*)keyframe->next ) {
661                                 keyframe->rechannel();
662                         }
663                 }
664         }
665 }
666
667 void EDL::resample(double old_rate, double new_rate, int data_type)
668 {
669         for( Track *current=tracks->first; current; current=NEXT ) {
670                 if( current->data_type == data_type ) {
671                         current->resample(old_rate, new_rate);
672                 }
673         }
674 }
675
676
677 void EDL::synchronize_params(EDL *edl)
678 {
679         local_session->synchronize_params(edl->local_session);
680         for( Track *this_track=tracks->first, *that_track=edl->tracks->first;
681              this_track && that_track;
682              this_track=this_track->next, that_track=that_track->next ) {
683                 this_track->synchronize_params(that_track);
684         }
685 }
686
687 int EDL::trim_selection(double start,
688         double end,
689         int edit_labels,
690         int edit_plugins,
691         int edit_autos)
692 {
693         if( start != end ) {
694 // clear the data
695                 clear(0,
696                         start,
697                         edit_labels,
698                         edit_plugins,
699                         edit_autos);
700                 clear(end - start,
701                         tracks->total_length(),
702                         edit_labels,
703                         edit_plugins,
704                         edit_autos);
705         }
706         return 0;
707 }
708
709
710 int EDL::equivalent(double position1, double position2)
711 {
712         double threshold = session->cursor_on_frames ?
713                 0.5 / session->frame_rate : 1.0 / session->sample_rate;
714         return fabs(position2 - position1) < threshold ? 1 : 0;
715 }
716
717 double EDL::equivalent_output(EDL *edl)
718 {
719         double result = -1;
720         session->equivalent_output(edl->session, &result);
721         tracks->equivalent_output(edl->tracks, &result);
722         return result;
723 }
724
725
726 void EDL::set_path(const char *path)
727 {
728         if( &this->path[0] == path ) return;
729         strcpy(this->path, path);
730 }
731
732 void EDL::set_inpoint(double position)
733 {
734         if( equivalent(local_session->get_inpoint(), position) &&
735                 local_session->get_inpoint() >= 0 ) {
736                 local_session->unset_inpoint();
737         }
738         else {
739                 local_session->set_inpoint(align_to_frame(position, 0));
740                 if( local_session->get_outpoint() <= local_session->get_inpoint() )
741                         local_session->unset_outpoint();
742         }
743 }
744
745 void EDL::set_outpoint(double position)
746 {
747         if( equivalent(local_session->get_outpoint(), position) &&
748                 local_session->get_outpoint() >= 0 ) {
749                 local_session->unset_outpoint();
750         }
751         else {
752                 local_session->set_outpoint(align_to_frame(position, 0));
753                 if( local_session->get_inpoint() >= local_session->get_outpoint() )
754                         local_session->unset_inpoint();
755         }
756 }
757
758 void EDL::unset_inoutpoint()
759 {
760         local_session->unset_inpoint();
761         local_session->unset_outpoint();
762 }
763
764 int EDL::blade(double position)
765 {
766         return tracks->blade(position);
767 }
768
769 int EDL::clear(double start, double end,
770         int clear_labels, int clear_plugins, int edit_autos)
771 {
772         if( start == end ) {
773                 double distance = 0;
774                 tracks->clear_handle(start,
775                         end,
776                         distance,
777                         clear_labels,
778                         clear_plugins,
779                         edit_autos);
780                 if( clear_labels && distance > 0 )
781                         labels->paste_silence(start,
782                                 start + distance);
783         }
784         else {
785                 tracks->clear(start,
786                         end,
787                         clear_plugins,
788                         edit_autos);
789                 if( clear_labels )
790                         labels->clear(start,
791                                 end,
792                                 1);
793         }
794
795 // Need to put at beginning so a subsequent paste operation starts at the
796 // right position.
797         double position = local_session->get_selectionstart();
798         local_session->set_selectionend(position);
799         local_session->set_selectionstart(position);
800         return 0;
801 }
802
803 static int dead_edit_cmp(Edit**ap, Edit**bp)
804 {
805         Edit *a = *ap, *b = *bp;
806         if( a->track != b->track ) return 0;
807         return a->startproject > b->startproject ? -1 : 1;
808 }
809
810 void EDL::delete_edits(ArrayList<Edit*> *edits, int collapse)
811 {
812         edits->sort(dead_edit_cmp);
813         for( int i=0; i<edits->size(); ++i ) {
814                 Edit *edit = edits->get(i);
815                 Track *track = edit->track;
816                 int64_t start = edit->startproject;
817                 int64_t length = edit->length;
818                 int64_t end = start + length;
819                 if( session->autos_follow_edits ) {
820                         track->automation->clear(start, end, 0, collapse);
821                 }
822                 if( session->plugins_follow_edits ) {
823                         for( int k=0; k<track->plugin_set.size(); ++k ) {
824                                 PluginSet *plugin_set = track->plugin_set[k];
825                                 plugin_set->clear(start, end, 1);
826                                 if( !collapse )
827                                         plugin_set->paste_silence(start, end, 1);
828                                 plugin_set->optimize();
829                         }
830                 }
831                 Edit *dead_edit = edit;
832                 if( collapse ) {
833                         while( (edit=edit->next) )
834                                 edit->startproject -= length;
835                 }
836                 delete dead_edit;
837         }
838         optimize();
839 }
840
841 class Range {
842 public:
843         static int cmp(Range *ap, Range *bp);
844         double start, end;
845         bool operator ==(Range &that) { return this->start == that.start; }
846         bool operator >(Range &that) { return this->start > that.start; }
847 };
848 int Range::cmp(Range *ap, Range *bp) {
849         return ap->start < bp->start ? -1 : ap->start == bp->start ? 0 : 1;
850 }
851
852 static void get_edit_regions(ArrayList<Edit*> *edits, ArrayList<Range> &regions)
853 {
854 // move edit inclusive labels by regions
855         for( int i=0; i<edits->size(); ++i ) {
856                 Edit *edit = edits->get(i);
857                 double pos = edit->track->from_units(edit->startproject);
858                 double end = edit->track->from_units(edit->startproject + edit->length);
859                 int n = regions.size(), k = n;
860                 while( --k >= 0 ) {
861                         Range &range = regions[k];
862                         if( pos >= range.end ) continue;
863                         if( range.start >= end ) continue;
864                         int expand = 0;
865                         if( range.start > pos ) { range.start = pos;  expand = 1; }
866                         if( range.end < end ) { range.end = end;  expand = 1; }
867                         if( !expand ) break;
868                         k = n;
869                 }
870                 if( k < 0 ) {
871                         Range &range = regions.append();
872                         range.start = pos;  range.end = end;
873                 }
874         }
875         regions.sort(Range::cmp);
876 }
877
878 void EDL::delete_edit_labels(ArrayList<Edit*> *edits, int collapse)
879 {
880         ArrayList<Range> regions;
881         get_edit_regions(edits, regions);
882         int n = regions.size(), k = n;
883         while( --k >= 0 ) {
884                 Range &range = regions[k];
885                 labels->clear(range.start, range.end, collapse);
886         }
887 }
888
889 void EDL::move_edit_labels(ArrayList<Edit*> *edits, double dist)
890 {
891         ArrayList<Range> regions;
892         get_edit_regions(edits, regions);
893         int n = regions.size(), k = n;
894         Labels moved(this, 0);
895         while( --k >= 0 ) {
896                 Range &range = regions[k];
897                 Label *label = labels->label_of(range.start);
898                 for( Label *next=0; label && label->position <= range.end; label=next ) {
899                         next = label->next;
900                         labels->remove_pointer(label);
901                         label->position += dist;
902                         moved.append(label);
903                 }
904                 Label *current = labels->first;
905                 while( (label=moved.first) ) {
906                         moved.remove_pointer(label);
907                         while( current && current->position < label->position )
908                                 current = current->next;
909                         if( current && current->position == label->position ) {
910                                 delete label;  continue;
911                         }
912                         labels->insert_before(current, label);
913                 }
914         }
915 }
916
917 void EDL::modify_edithandles(double oldposition, double newposition,
918         int currentend, int handle_mode, int edit_labels,
919         int edit_plugins, int edit_autos, int group_id)
920 {
921         tracks->modify_edithandles(oldposition, newposition,
922                 currentend, handle_mode, edit_labels,
923                 edit_plugins, edit_autos, group_id);
924 }
925
926 void EDL::modify_pluginhandles(double oldposition, double newposition,
927         int currentend, int handle_mode, int edit_labels,
928         int edit_autos, Edits *trim_edits)
929 {
930         tracks->modify_pluginhandles(oldposition, newposition,
931                 currentend, handle_mode, edit_labels,
932                 edit_autos, trim_edits);
933         optimize();
934 }
935
936 void EDL::paste_silence(double start, double end,
937         int edit_labels, int edit_plugins, int edit_autos)
938 {
939         if( edit_labels )
940                 labels->paste_silence(start, end);
941         tracks->paste_silence(start, end, edit_plugins, edit_autos);
942 }
943
944
945 void EDL::remove_from_project(ArrayList<EDL*> *clips)
946 {
947         for( int i=0; i<clips->size(); ++i ) {
948                 this->clips.remove_clip(clips->get(i));
949         }
950 }
951
952 void EDL::remove_from_project(ArrayList<Indexable*> *assets)
953 {
954 // Remove from clips
955         if( !parent_edl )
956                 for( int j=0; j<clips.size(); ++j ) {
957                         clips[j]->remove_from_project(assets);
958                 }
959
960 // Remove from VWindow EDLs
961         for( int i=0; i<total_vwindow_edls(); ++i )
962                 get_vwindow_edl(i)->remove_from_project(assets);
963
964         for( int i=0; i<assets->size(); ++i ) {
965 // Remove from tracks
966                 for( Track *track=tracks->first; track; track=track->next ) {
967                         track->remove_asset(assets->get(i));
968                 }
969
970 // Remove from assets
971                 if( !parent_edl && assets->get(i)->is_asset ) {
972                         this->assets->remove_asset((Asset*)assets->get(i));
973                 }
974                 else
975                 if( !parent_edl && !assets->get(i)->is_asset ) {
976                         this->nested_edls.remove_clip((EDL*)assets->get(i));
977                 }
978         }
979 }
980
981 void EDL::update_assets(EDL *src)
982 {
983         for( Asset *current=src->assets->first; current; current=NEXT ) {
984                 assets->update(current);
985         }
986 }
987
988 int EDL::get_tracks_height(Theme *theme)
989 {
990         int total_pixels = 0;
991         for( Track *current=tracks->first; current; current=NEXT ) {
992                 total_pixels += current->vertical_span(theme);
993         }
994         return total_pixels;
995 }
996
997 int64_t EDL::get_tracks_width()
998 {
999         int64_t total_pixels = 0;
1000         for( Track *current=tracks->first; current; current=NEXT ) {
1001                 int64_t pixels = current->horizontal_span();
1002                 if( pixels > total_pixels ) total_pixels = pixels;
1003         }
1004 //printf("EDL::get_tracks_width %d\n", total_pixels);
1005         return total_pixels;
1006 }
1007
1008 // int EDL::calculate_output_w(int single_channel)
1009 // {
1010 //      if( single_channel ) return session->output_w;
1011 //
1012 //      int widest = 0;
1013 //      for( int i=0; i<session->video_channels; ++i )
1014 //      {
1015 //              if( session->vchannel_x[i] + session->output_w > widest ) widest = session->vchannel_x[i] + session->output_w;
1016 //      }
1017 //      return widest;
1018 // }
1019 //
1020 // int EDL::calculate_output_h(int single_channel)
1021 // {
1022 //      if( single_channel ) return session->output_h;
1023 //
1024 //      int tallest = 0;
1025 //      for( int i=0; i<session->video_channels; ++i )
1026 //      {
1027 //              if( session->vchannel_y[i] + session->output_h > tallest ) tallest = session->vchannel_y[i] + session->output_h;
1028 //      }
1029 //      return tallest;
1030 // }
1031
1032 // Get the total output size scaled to aspect ratio
1033 void EDL::calculate_conformed_dimensions(int single_channel, float &w, float &h)
1034 {
1035         if( (float)session->output_w / session->output_h > get_aspect_ratio() )
1036                 h = (w = session->output_w) / get_aspect_ratio();
1037         else
1038                 w = (h = session->output_h) * get_aspect_ratio();
1039 }
1040
1041 float EDL::get_aspect_ratio()
1042 {
1043         return session->aspect_w / session->aspect_h;
1044 }
1045
1046 int EDL::dump(FILE *fp)
1047 {
1048         if( parent_edl )
1049                 fprintf(fp,"CLIP\n");
1050         else
1051                 fprintf(fp,"EDL\n");
1052         fprintf(fp,"  clip_title: %s\n"
1053                 "  parent_edl: %p\n", local_session->clip_title, parent_edl);
1054         fprintf(fp,"  selectionstart %f\n  selectionend %f\n  loop_start %f\n  loop_end %f\n",
1055                 local_session->get_selectionstart(1),
1056                 local_session->get_selectionend(1),
1057                 local_session->loop_start,
1058                 local_session->loop_end);
1059         for( int i=0; i<TOTAL_PANES; ++i ) {
1060                 fprintf(fp,"  pane %d view_start=%jd track_start=%d\n", i,
1061                         local_session->view_start[i],
1062                         local_session->track_start[i]);
1063         }
1064
1065         if( !parent_edl ) {
1066                 fprintf(fp,"audio_channels: %d audio_tracks: %d sample_rate: %jd\n",
1067                         session->audio_channels,
1068                         session->audio_tracks,
1069                         session->sample_rate);
1070                 fprintf(fp,"  video_channels: %d\n"
1071                         "  video_tracks: %d\n"
1072                         "  frame_rate: %.2f\n"
1073                         "  frames_per_foot: %.2f\n"
1074                         "  output_w: %d\n"
1075                         "  output_h: %d\n"
1076                         "  aspect_w: %f\n"
1077                         "  aspect_h: %f\n"
1078                         "  color_model: %d\n",
1079                                 session->video_channels,
1080                                 session->video_tracks,
1081                                 session->frame_rate,
1082                                 session->frames_per_foot,
1083                                 session->output_w,
1084                                 session->output_h,
1085                                 session->aspect_w,
1086                                 session->aspect_h,
1087                                 session->color_model);
1088
1089                 fprintf(fp," CLIPS");
1090                 fprintf(fp,"  total: %d\n", clips.size());
1091                 for( int i=0; i<clips.size(); ++i ) {
1092                         fprintf(fp,"\n\n");
1093                         clips[i]->dump(fp);
1094                         fprintf(fp,"\n\n");
1095                 }
1096                 fprintf(fp," NESTED_EDLS");
1097                 fprintf(fp,"  total: %d\n", nested_edls.size());
1098                 for( int i=0; i<nested_edls.size(); ++i )
1099                         fprintf(fp,"   %s\n", nested_edls[i]->path);
1100
1101                 fprintf(fp," VWINDOW EDLS");
1102                 fprintf(fp,"  total: %d\n", total_vwindow_edls());
1103
1104                 for( int i=0; i<total_vwindow_edls(); ++i ) {
1105                         fprintf(fp,"   %s\n", get_vwindow_edl(i)->local_session->clip_title);
1106                 }
1107
1108                 fprintf(fp," ASSETS\n");
1109                 assets->dump(fp);
1110         }
1111         fprintf(fp," LABELS\n");
1112         labels->dump(fp);
1113         fprintf(fp," TRACKS\n");
1114         tracks->dump(fp);
1115 //printf("EDL::dump 2\n");
1116         return 0;
1117 }
1118
1119 EDL* EDL::add_clip(EDL *edl)
1120 {
1121 // Copy argument.  New edls are deleted from MWindow::load_filenames.
1122         EDL *new_edl = new EDL(this);
1123         new_edl->create_objects();
1124         new_edl->copy_all(edl);
1125         new_edl->folder_no = AW_CLIP_FOLDER;
1126         clips.append(new_edl);
1127         return new_edl;
1128 }
1129
1130 void EDL::insert_asset(Asset *asset,
1131         EDL *nested_edl,
1132         double position,
1133         Track *first_track,
1134         RecordLabels *labels)
1135 {
1136 // Insert asset into asset table
1137         Asset *new_asset = 0;
1138         EDL *new_nested_edl = 0;
1139
1140         if( asset ) new_asset = assets->update(asset);
1141         if( nested_edl ) new_nested_edl = nested_edls.get_nested(nested_edl);
1142
1143 // Paste video
1144         int vtrack = 0;
1145         Track *current = first_track ? first_track : tracks->first;
1146
1147
1148 // Fix length of single frame
1149         double length = 0.;
1150         int layers = 0;
1151         int channels = 0;
1152
1153         if( new_nested_edl ) {
1154                 length = new_nested_edl->tracks->total_length();
1155                 layers = 1;
1156                 channels = new_nested_edl->session->audio_channels;
1157         }
1158
1159         if( new_asset ) {
1160 // Insert 1 frame for undefined length
1161                 if( new_asset->video_length < 0 ) {
1162                         length = session->si_useduration ?
1163                                 session->si_duration :
1164                                 1.0 / session->frame_rate;
1165                 }
1166                 else {
1167                         length = new_asset->frame_rate > 0 ?
1168                                 (double)new_asset->video_length / new_asset->frame_rate :
1169                                 1.0 / session->frame_rate;
1170                 }
1171                 layers = new_asset->layers;
1172                 channels = new_asset->channels;
1173         }
1174
1175         for( ; current && vtrack<layers; current=NEXT ) {
1176                 if( !current->record || current->data_type != TRACK_VIDEO ) continue;
1177                 current->insert_asset(new_asset, new_nested_edl,
1178                         length, position, vtrack++);
1179         }
1180
1181         int atrack = 0;
1182         if( new_asset ) {
1183                 if( new_asset->audio_length < 0 ) {
1184 // Insert 1 frame for undefined length & video
1185                         if( new_asset->video_data )
1186                                 length = (double)1.0 / new_asset->frame_rate;
1187                         else
1188 // Insert 1 second for undefined length & no video
1189                                 length = 1.0;
1190                 }
1191                 else
1192                         length = (double)new_asset->audio_length /
1193                                         new_asset->sample_rate;
1194         }
1195
1196         current = tracks->first;
1197         for( ; current && atrack < channels; current=NEXT ) {
1198                 if( !current->record || current->data_type != TRACK_AUDIO ) continue;
1199                 current->insert_asset(new_asset, new_nested_edl,
1200                         length, position, atrack++);
1201         }
1202
1203 // Insert labels from a recording window.
1204         if( labels ) {
1205                 for( RecordLabel *label=labels->first; label; label=label->next ) {
1206                         this->labels->toggle_label(label->position, label->position);
1207                 }
1208         }
1209 }
1210
1211
1212
1213 void EDL::set_index_file(Indexable *indexable)
1214 {
1215         if( indexable->is_asset )
1216                 assets->update_index((Asset*)indexable);
1217         else
1218                 nested_edls.update_index((EDL*)indexable);
1219 }
1220
1221 void EDL::optimize()
1222 {
1223 //printf("EDL::optimize 1\n");
1224         double length = tracks->total_length();
1225         double preview_start = local_session->preview_start;
1226         double preview_end = local_session->preview_end;
1227         if( preview_end < 0 || preview_end > length )
1228                 preview_end = length;
1229         if( preview_start == 0 && preview_end >= length )
1230                 local_session->preview_end = -1;
1231         if( preview_start > preview_end )
1232                 local_session->preview_start = preview_end;
1233         for( Track *current=tracks->first; current; current=NEXT )
1234                 current->optimize();
1235 }
1236
1237 int EDL::next_id()
1238 {
1239         static Mutex id_lock;
1240         id_lock.lock("EDL::next_id");
1241         int result = EDLSession::current_id++;
1242         id_lock.unlock();
1243         return result;
1244 }
1245
1246 void EDL::get_shared_plugins(Track *source,
1247         ArrayList<SharedLocation*> *plugin_locations,
1248         int omit_recordable,
1249         int data_type)
1250 {
1251         for( Track *track=tracks->first; track; track=track->next ) {
1252                 if( track->record && omit_recordable ) continue;
1253                 if( track == source || track->data_type != data_type ) continue;
1254                 for( int i=0; i<track->plugin_set.size(); ++i ) {
1255                         Plugin *plugin = track->get_current_plugin(
1256                                 local_session->get_selectionstart(1),
1257                                 i, PLAY_FORWARD, 1, 0);
1258                         if( plugin && plugin->plugin_type != PLUGIN_STANDALONE ) continue;
1259                         plugin_locations->append(new SharedLocation(tracks->number_of(track), i));
1260                 }
1261         }
1262 }
1263
1264 void EDL::get_shared_tracks(Track *track,
1265         ArrayList<SharedLocation*> *module_locations,
1266         int omit_recordable, int data_type)
1267 {
1268         for( Track *current=tracks->first; current; current=NEXT ) {
1269                 if( omit_recordable && current->record ) continue;
1270                 if( current == track || current->data_type != data_type ) continue;
1271                 module_locations->append(new SharedLocation(tracks->number_of(current), 0));
1272         }
1273 }
1274
1275 // aligned frame time, account for sample truncation
1276 double EDL::frame_align(double position, int round)
1277 {
1278         if( !round && session->sample_rate > 0 ) {
1279                 int64_t sample_pos = position * session->sample_rate;
1280                 position = (sample_pos+2.) / session->sample_rate;
1281         }
1282         int64_t frame_pos = (position * session->frame_rate + (round ? 0.5 : 1e-6));
1283         return frame_pos / session->frame_rate;
1284 }
1285
1286 // Convert position to frames if alignment is enabled.
1287 double EDL::align_to_frame(double position, int round)
1288 {
1289         if( session->cursor_on_frames )
1290                 position = frame_align(position, round);
1291         return position;
1292 }
1293
1294
1295 BinFolder *EDL::get_folder(int no)
1296 {
1297         for( int i=0; i<folders.size(); ++i ) {
1298                 BinFolder *fp = folders[i];
1299                 if( no == fp->awindow_folder ) return fp;
1300         }
1301         return 0;
1302 }
1303
1304 int EDL::get_folder_number(const char *title)
1305 {
1306         for( int i=0; i<AWINDOW_FOLDERS; ++i ) {
1307                 if( !strcmp(title, AWindowGUI::folder_names[i]) )
1308                         return i;
1309         }
1310         for( int i=0; i<folders.size(); ++i ) {
1311                 if( !strcmp(title, folders[i]->title) )
1312                         return folders[i]->awindow_folder;
1313         }
1314         return AW_NO_FOLDER;
1315 }
1316
1317 const char *EDL::get_folder_name(int no)
1318 {
1319         if( no >= 0 && no<AWINDOW_FOLDERS )
1320                 return AWindowGUI::folder_names[no];
1321         BinFolder *fp = get_folder(no);
1322         return !fp ? "" : fp->title;
1323 }
1324
1325 int EDL::new_folder(const char *title, int is_clips)
1326 {
1327         if( !title[0] ) return 1;
1328         int ret = get_folder_number(title);
1329         if( ret >= 0 ) return 1;
1330         int idx = AWINDOW_FOLDERS;
1331         for( int i=0; i<folders.size(); ++i ) {
1332                 BinFolder *fp = folders[i];
1333                 int no = fp->awindow_folder;
1334                 if( no >= idx ) idx = no+1;
1335         }
1336         folders.append(new BinFolder(idx, is_clips, title));
1337         return 0;
1338 }
1339
1340 int EDL::delete_folder(const char *title)
1341 {
1342         int k = folders.size();
1343         while( --k >= 0 && strcmp(title, folders[k]->title) );
1344         if( k >= 0 )
1345                 folders.remove_object_number(k);
1346         return k;
1347 }
1348
1349 int EDL::get_use_vconsole(VEdit* *playable_edit,
1350         int64_t position, int direction, PlayableTracks *playable_tracks)
1351 {
1352         int share_playable_tracks = 1;
1353         int result = 0;
1354         VTrack *playable_track = 0;
1355         const int debug = 0;
1356         *playable_edit = 0;
1357
1358 // Calculate playable tracks when being called as a nested EDL
1359         if( !playable_tracks ) {
1360                 share_playable_tracks = 0;
1361                 playable_tracks = new PlayableTracks(this,
1362                         position, direction, TRACK_VIDEO, 1);
1363         }
1364
1365
1366 // Total number of playable tracks is 1
1367         if( playable_tracks->size() != 1 ) {
1368                 result = 1;
1369         }
1370         else {
1371                 playable_track = (VTrack*)playable_tracks->get(0);
1372         }
1373
1374 // Don't need playable tracks anymore
1375         if( !share_playable_tracks ) {
1376                 delete playable_tracks;
1377         }
1378
1379 if( debug ) printf("EDL::get_use_vconsole %d playable_tracks->size()=%d\n",
1380  __LINE__, playable_tracks->size());
1381         if( result ) return 1;
1382
1383
1384 // Test mutual conditions between direct copy rendering and this.
1385         if( !playable_track->direct_copy_possible(position,
1386                 direction,
1387                 1) )
1388                 return 1;
1389 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1390
1391         *playable_edit = (VEdit*)playable_track->edits->editof(position,
1392                 direction, 0);
1393 // No edit at current location
1394         if( !*playable_edit ) return 1;
1395 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1396
1397
1398 // Edit is nested EDL
1399         if( (*playable_edit)->nested_edl ) {
1400 // Test nested EDL
1401                 EDL *nested_edl = (*playable_edit)->nested_edl;
1402                 int64_t nested_position = (int64_t)((position -
1403                                 (*playable_edit)->startproject +
1404                                 (*playable_edit)->startsource) *
1405                         nested_edl->session->frame_rate /
1406                         session->frame_rate);
1407
1408
1409                 VEdit *playable_edit_temp = 0;
1410                 if( session->output_w != nested_edl->session->output_w ||
1411                         session->output_h != nested_edl->session->output_h ||
1412                         nested_edl->get_use_vconsole(&playable_edit_temp,
1413                                 nested_position,
1414                                 direction,
1415                                 0) )
1416                         return 1;
1417
1418                 return 0;
1419         }
1420
1421 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1422 // Edit is not a nested EDL
1423         Asset *asset = (*playable_edit)->asset;
1424 // Edit is silence
1425         if( !asset ) return 1;
1426 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1427
1428 // Asset and output device must have the same dimensions
1429         if( asset->width != session->output_w ||
1430             asset->height != session->output_h )
1431                 return 1;
1432
1433
1434 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1435 // Asset and output device must have same resulting de-interlacing method
1436         if( ilaceautofixmethod2(session->interlace_mode,
1437             asset->interlace_autofixoption, asset->interlace_mode,
1438             asset->interlace_fixmethod) != ILACE_FIXMETHOD_NONE )
1439                 return 1;
1440
1441 // If we get here the frame is going to be directly copied.  Whether it is
1442 // decompressed in hardware depends on the colormodel.
1443         return 0;
1444 }
1445
1446
1447 // For Indexable
1448 int EDL::get_audio_channels()
1449 {
1450         return session->audio_channels;
1451 }
1452
1453 int EDL::get_sample_rate()
1454 {
1455         return session->sample_rate;
1456 }
1457
1458 int64_t EDL::get_audio_samples()
1459 {
1460         return (int64_t)(tracks->total_length() *
1461                 session->sample_rate);
1462 }
1463
1464 int EDL::have_audio()
1465 {
1466         return 1;
1467 }
1468
1469 int EDL::have_video()
1470 {
1471         return 1;
1472 }
1473
1474
1475 int EDL::get_w()
1476 {
1477         return session->output_w;
1478 }
1479
1480 int EDL::get_h()
1481 {
1482         return session->output_h;
1483 }
1484
1485 double EDL::get_frame_rate()
1486 {
1487         return session->frame_rate;
1488 }
1489
1490 int EDL::get_video_layers()
1491 {
1492         return 1;
1493 }
1494
1495 int64_t EDL::get_video_frames()
1496 {
1497         return (int64_t)(tracks->total_length() *
1498                 session->frame_rate);
1499 }
1500
1501 void EDL::remove_vwindow_edls()
1502 {
1503         for( int i=0; i<total_vwindow_edls(); ++i ) {
1504                 get_vwindow_edl(i)->remove_user();
1505         }
1506         vwindow_edls.remove_all();
1507 }
1508
1509 void EDL::remove_vwindow_edl(EDL *edl)
1510 {
1511         if( vwindow_edls.number_of(edl) >= 0 ) {
1512                 edl->remove_user();
1513                 vwindow_edls.remove(edl);
1514         }
1515 }
1516
1517
1518 EDL* EDL::get_vwindow_edl(int number)
1519 {
1520         return vwindow_edls.get(number);
1521 }
1522
1523 int EDL::total_vwindow_edls()
1524 {
1525         return vwindow_edls.size();
1526 }
1527
1528 void EDL::append_vwindow_edl(EDL *edl, int increase_counter)
1529 {
1530         if(vwindow_edls.number_of(edl) >= 0) return;
1531
1532         if(increase_counter) edl->add_user();
1533         vwindow_edls.append(edl);
1534 }
1535
1536
1537 double EDL::next_edit(double position)
1538 {
1539         Units::fix_double(&position);
1540         double new_position = tracks->total_length();
1541
1542         double max_rate = get_frame_rate();
1543         int sample_rate = get_sample_rate();
1544         if( sample_rate > max_rate ) max_rate = sample_rate;
1545         double min_movement = max_rate > 0 ? 1. / max_rate : 1e-6;
1546
1547 // Test for edit handles after position
1548         for( Track *track=tracks->first; track; track=track->next ) {
1549                 if( !track->record ) continue;
1550                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1551                         double edit_end = track->from_units(edit->startproject + edit->length);
1552                         Units::fix_double(&edit_end);
1553                         if( fabs(edit_end-position) < min_movement ) continue;
1554                         if( edit_end > position && edit_end < new_position )
1555                                 new_position = edit_end;
1556                 }
1557         }
1558         return new_position;
1559 }
1560
1561 double EDL::prev_edit(double position)
1562 {
1563         Units::fix_double(&position);
1564         double new_position = -1;
1565
1566         double max_rate = get_frame_rate();
1567         int sample_rate = get_sample_rate();
1568         if( sample_rate > max_rate ) max_rate = sample_rate;
1569         double min_movement = max_rate > 0 ? 1. / max_rate : 1e-6;
1570
1571 // Test for edit handles before cursor position
1572         for( Track *track=tracks->first; track; track=track->next ) {
1573                 if( !track->record ) continue;
1574                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1575                         double edit_end = track->from_units(edit->startproject);
1576                         Units::fix_double(&edit_end);
1577                         if( fabs(edit_end-position) < min_movement ) continue;
1578                         if( edit_end < position && edit_end > new_position )
1579                                 new_position = edit_end;
1580                 }
1581         }
1582         return new_position;
1583 }
1584
1585 void EDL::rescale_proxy(int orig_scale, int new_scale)
1586 {
1587         if( orig_scale == new_scale ) return;
1588 // project size
1589         float orig_w = (float)session->output_w * orig_scale;
1590         float orig_h = (float)session->output_h * orig_scale;
1591         if( !parent_edl ) {
1592                 session->output_w = Units::round(orig_w / new_scale);
1593                 session->output_h = Units::round(orig_h / new_scale);
1594         }
1595
1596 // track sizes
1597         for( Track *track=tracks->first; track; track=track->next ) {
1598                 if( track->data_type != TRACK_VIDEO ) continue;
1599                 orig_w = (float)track->track_w * orig_scale;
1600                 orig_h = (float)track->track_h * orig_scale;
1601                 track->track_w = Units::round(orig_w / new_scale);
1602                 track->track_h = Units::round(orig_h / new_scale);
1603                 ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->
1604                         set_proxy(orig_scale, new_scale);
1605                 ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_X])->
1606                         set_proxy(orig_scale, new_scale);
1607                 ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_Y])->
1608                         set_proxy(orig_scale, new_scale);
1609                 ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_X])->
1610                         set_proxy(orig_scale, new_scale);
1611                 ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_Y])->
1612                         set_proxy(orig_scale, new_scale);
1613         }
1614 }
1615
1616 void EDL::set_proxy(int new_scale, int use_scaler,
1617         ArrayList<Indexable*> *orig_assets, ArrayList<Indexable*> *proxy_assets)
1618 {
1619         int orig_scale = session->proxy_scale;
1620         int orig_use_scaler = session->proxy_use_scaler;
1621
1622 // rescale to full size asset in read_frame
1623         session->proxy_scale = new_scale;
1624         session->proxy_use_scaler = use_scaler;
1625
1626         if( use_scaler ) {
1627                 for( int i=0; i<proxy_assets->size(); ++i ) {
1628                         Asset *proxy_asset = (Asset *)proxy_assets->get(i);
1629                         proxy_asset->width = orig_assets->get(i)->get_w();
1630                         proxy_asset->height = orig_assets->get(i)->get_h();
1631                 }
1632                 new_scale = 1;
1633         }
1634
1635         if( !orig_use_scaler )
1636                 rescale_proxy(orig_scale, new_scale);
1637
1638 // change original assets to proxy assets
1639         int folder_no = use_scaler || new_scale != 1 ? AW_PROXY_FOLDER : AW_MEDIA_FOLDER;
1640         for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
1641                 Indexable *proxy_idxbl = proxy_assets->get(i);
1642                 proxy_idxbl->folder_no = folder_no;
1643                 if( !proxy_idxbl->is_asset ) continue;
1644                 Asset *proxy_asset = assets->update((Asset *)proxy_idxbl);
1645                 if( proxy_asset == (Asset *)proxy_idxbl ) continue;
1646                 proxy_asset->width = proxy_idxbl->get_w();
1647                 proxy_asset->height = proxy_idxbl->get_h();
1648         }
1649 // replace track contents
1650         for( Track *track=tracks->first; track; track=track->next ) {
1651                 if( track->data_type != TRACK_VIDEO ) continue;
1652                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1653                         Indexable *idxbl = (Indexable *)edit->asset;
1654                         if( !idxbl ) idxbl = (Indexable *)edit->nested_edl;
1655                         if( !idxbl ) continue;
1656                         int i = orig_assets->size();
1657                         while( --i>=0 && strcmp(orig_assets->get(i)->path, idxbl->path) );
1658                         if( i < 0 ) continue;
1659                         Indexable *proxy_idxbl = proxy_assets->get(i);
1660                         Asset *proxy_asset = proxy_idxbl->is_asset ?
1661                                 assets->update((Asset *)proxy_idxbl) : 0;
1662                         EDL *proxy_edl = !proxy_idxbl->is_asset ?
1663                                 (EDL *)proxy_idxbl : 0;
1664                         edit->asset = proxy_asset;
1665                         edit->nested_edl = proxy_edl;
1666                 }
1667         }
1668         for( int j=0,n=clips.size(); j<n; ++j ) {
1669                 EDL *clip = clips[j];
1670                 int has_proxy = 0;
1671                 for( Track *track=clip->tracks->first; track; track=track->next ) {
1672                         if( track->data_type != TRACK_VIDEO ) continue;
1673                         for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1674                                 Indexable *idxbl = (Indexable *)edit->asset;
1675                                 if( !idxbl ) idxbl = (Indexable *)edit->nested_edl;
1676                                 if( !idxbl ) continue;
1677                                 int i = orig_assets->size();
1678                                 while( --i>=0 && strcmp(orig_assets->get(i)->path, idxbl->path) );
1679                                 if( i < 0 ) continue;
1680                                 Indexable *proxy_idxbl = proxy_assets->get(i);
1681                                 Asset *proxy_asset = proxy_idxbl->is_asset ?
1682                                         assets->update((Asset *)proxy_idxbl) : 0;
1683                                 EDL *proxy_edl = !proxy_idxbl->is_asset ?
1684                                         (EDL *)proxy_idxbl : 0;
1685                                 edit->asset = proxy_asset;
1686                                 edit->nested_edl = proxy_edl;
1687                                 has_proxy = 1;
1688                         }
1689                 }
1690                 if( has_proxy && !orig_use_scaler )
1691                         clip->rescale_proxy(orig_scale, new_scale);
1692         }
1693 }
1694
1695 void EDL::add_proxy(int use_scaler,
1696         ArrayList<Indexable*> *orig_assets, ArrayList<Indexable*> *proxy_assets)
1697 {
1698         if( use_scaler ) {
1699                 for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
1700                         Asset *proxy_asset = (Asset *)proxy_assets->get(i);
1701                         proxy_asset->width = orig_assets->get(i)->get_w();
1702                         proxy_asset->height = orig_assets->get(i)->get_h();
1703                 }
1704         }
1705
1706 // change original assets to proxy assets
1707         for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
1708                 Asset *proxy_asset = assets->update((Asset *)proxy_assets->get(i));
1709                 proxy_asset->folder_no = AW_PROXY_FOLDER;
1710 // replace track contents
1711                 for( Track *track=tracks->first; track; track=track->next ) {
1712                         if( track->data_type != TRACK_VIDEO ) continue;
1713                         for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1714                                 if( !edit->asset ) continue;
1715                                 if( !strcmp(edit->asset->path, orig_assets->get(i)->path) ) {
1716                                         edit->asset = proxy_asset;
1717                                 }
1718                         }
1719                 }
1720         }
1721 }
1722
1723 Asset *EDL::get_proxy_asset()
1724 {
1725         return folder_no == AW_PROXY_FOLDER ?
1726                 tracks->first->edits->first->asset : 0;
1727 }
1728
1729 Track *EDL::add_new_track(int data_type)
1730 {
1731         Track *new_track = 0;
1732         switch( data_type ) {
1733         case TRACK_VIDEO:
1734                 ++session->video_tracks;
1735                 new_track = tracks->add_video_track(0, 0);
1736                 break;
1737         case TRACK_AUDIO:
1738                 ++session->audio_tracks;
1739                 new_track = tracks->add_audio_track(0, 0);
1740                 break;
1741         case TRACK_SUBTITLE:
1742                 new_track = tracks->add_subttl_track(0, 0);
1743                 break;
1744         }
1745         return new_track;
1746 }
1747
1748 double EDL::get_cursor_position(int cursor_x, int pane_no)
1749 {
1750         return (double)cursor_x * local_session->zoom_sample / session->sample_rate +
1751                 (double)local_session->view_start[pane_no] *
1752                         local_session->zoom_sample / session->sample_rate;
1753 }
1754 int64_t EDL::get_position_cursorx(double position, int pane_no)
1755 {
1756         return (int64_t)(position * session->sample_rate / local_session->zoom_sample)
1757                         - local_session->view_start[pane_no];
1758 }
1759
1760 int EDL::in_use(Indexable *indexable)
1761 {
1762         for( Track *track=tracks->first; track; track=track->next ) {
1763                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1764                         Indexable *idxbl = (Indexable *)edit->asset;
1765                         if( !idxbl ) idxbl = (Indexable *)edit->nested_edl;
1766                         if( !idxbl ) continue;
1767                         if( idxbl->id == indexable->id ) return 1;
1768                         if( !indexable->is_asset != !idxbl->is_asset ) continue;
1769                         if( !strcmp(idxbl->path, indexable->path) ) return 1;
1770                 }
1771         }
1772         for( int i=0; i<clips.size(); ++i )
1773                 if( clips[i]->in_use(indexable) ) return 1;
1774         for( int i=0; i<nested_edls.size(); ++i )
1775                 if( nested_edls[i]->in_use(indexable) ) return 1;
1776         return 0;
1777 }
1778
1779 int EDL::regroup(int next_id)
1780 {
1781         ArrayList<int> new_groups;
1782         for( Track *track=tracks->first; track; track=track->next ) {
1783                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1784                         if( !edit->group_id ) continue;
1785                         int k = new_groups.size();
1786                         while( --k >= 0 && new_groups[k] != edit->group_id );
1787                         if( k >= 0 ) continue;
1788                         new_groups.append(edit->group_id);
1789                 }
1790         }
1791         for( Track *track=tracks->first; track; track=track->next ) {
1792                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1793                         if( !edit->group_id ) continue;
1794                         int k = new_groups.size();
1795                         while( --k >= 0 && new_groups[k] != edit->group_id );
1796                         if( k < 0 ) continue;
1797                         edit->group_id = k + next_id;
1798                 }
1799         }
1800         return new_groups.size();
1801 }
1802