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