version update, Features5 update, proxy tweak
[goodguy/history.git] / cinelerra-5.1 / cinelerra / edl.C
index 1b00471bce325a5c89a3dd8ba647e054991f68ef..a756e6e576bae9999d136b7d27ffe2ef6666c318 100644 (file)
 #include "atrack.h"
 #include "autoconf.h"
 #include "automation.h"
-#include "awindowgui.inc"
-#include "bcsignals.h"
-#include "clip.h"
+#include "awindowgui.h"
 #include "bccmodels.h"
 #include "bchash.h"
+#include "bcsignals.h"
+#include "clip.h"
+#include "cstrdup.h"
+#include "clipedls.h"
 #include "edits.h"
 #include "edl.h"
 #include "edlsession.h"
 #include "filexml.h"
+#include "floatauto.h"
+#include "floatautos.h"
 #include "guicast.h"
 #include "indexstate.h"
 #include "interlacemodes.h"
 #include "labels.h"
 #include "localsession.h"
+#include "maskautos.h"
 #include "mutex.h"
-#include "nestededls.h"
 #include "panauto.h"
 #include "panautos.h"
 #include "playbackconfig.h"
 
 
 
-Mutex* EDL::id_lock = 0;
-
-
-
 EDL::EDL(EDL *parent_edl)
  : Indexable(0)
 {
@@ -70,15 +70,6 @@ EDL::EDL(EDL *parent_edl)
        tracks = 0;
        labels = 0;
        local_session = 0;
-//     vwindow_edl = 0;
-//     vwindow_edl_shared = 0;
-
-       folders.set_array_delete();
-
-// persistent for now
-//     new_folder(CLIP_FOLDER);
-//     new_folder(MEDIA_FOLDER);
-
        id = next_id();
        path[0] = 0;
 }
@@ -87,60 +78,24 @@ EDL::EDL(EDL *parent_edl)
 EDL::~EDL()
 {
 
-       if(tracks)
-       {
-               delete tracks;
-       }
-       if(labels)
-       {
-               delete labels;
-       }
-
-       if(local_session)
-       {
-               delete local_session;
-       }
-
-
+       delete tracks;
+       delete labels;
+       delete local_session;
        remove_vwindow_edls();
-
-//     if(vwindow_edl && !vwindow_edl_shared)
-//             vwindow_edl->Garbage::remove_user();
-
-       if(!parent_edl)
-       {
+       if( !parent_edl ) {
                delete assets;
                delete session;
        }
-
-
-       folders.remove_all_objects();
-       for(int i = 0; i < clips.size(); i++)
-               clips.get(i)->Garbage::remove_user();
-       clips.remove_all();
-       delete nested_edls;
 }
 
 
-
 void EDL::create_objects()
 {
        tracks = new Tracks(this);
-       if(!parent_edl)
-       {
-               assets = new Assets(this);
-               session = new EDLSession(this);
-       }
-       else
-       {
-               assets = parent_edl->assets;
-               session = parent_edl->session;
-       }
-
+       assets = !parent_edl ? new Assets(this) : parent_edl->assets;
+       session = !parent_edl ? new EDLSession(this) : parent_edl->session;
        local_session = new LocalSession(this);
        labels = new Labels(this, "LABELS");
-       nested_edls = new NestedEDLs;
-//     last_playback_position = 0;
 }
 
 EDL& EDL::operator=(EDL &edl)
@@ -152,7 +107,7 @@ printf("EDL::operator= 1\n");
 
 int EDL::load_defaults(BC_Hash *defaults)
 {
-       if(!parent_edl)
+       if( !parent_edl )
                session->load_defaults(defaults);
 
        local_session->load_defaults(defaults);
@@ -161,7 +116,7 @@ int EDL::load_defaults(BC_Hash *defaults)
 
 int EDL::save_defaults(BC_Hash *defaults)
 {
-       if(!parent_edl)
+       if( !parent_edl )
                session->save_defaults(defaults);
 
        local_session->save_defaults(defaults);
@@ -177,29 +132,21 @@ void EDL::boundaries()
 int EDL::create_default_tracks()
 {
 
-       for(int i = 0; i < session->video_tracks; i++)
-       {
+       for( int i=0; i<session->video_tracks; ++i ) {
                tracks->add_video_track(0, 0);
        }
-       for(int i = 0; i < session->audio_tracks; i++)
-       {
+       for( int i=0; i<session->audio_tracks; ++i ) {
                tracks->add_audio_track(0, 0);
        }
        return 0;
 }
 
-int EDL::load_xml(FileXML *file,
-       uint32_t load_flags)
+int EDL::load_xml(FileXML *file, uint32_t load_flags)
 {
        int result = 0;
-// Track numbering offset for replacing undo data.
-       int track_offset = 0;
-
-// Clear objects
-       folders.remove_all_objects();
+       folders.clear();
 
-       if((load_flags & LOAD_ALL) == LOAD_ALL)
-       {
+       if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
                remove_vwindow_edls();
        }
 
@@ -212,172 +159,170 @@ int EDL::load_xml(FileXML *file,
 // The parent_edl test is required to make EDL loading work because
 // when loading an EDL the EDL tag is already read by the parent.
 
-       if(!parent_edl)
-       {
-               do{
+       if( !parent_edl ) {
+               do {
                  result = file->read_tag();
-               }while(!result &&
+               } while(!result &&
                        !file->tag.title_is("XML") &&
                        !file->tag.title_is("EDL"));
        }
+       return result ? result : read_xml(file, load_flags);
+}
+
+int EDL::read_xml(FileXML *file, uint32_t load_flags)
+{
+       int result = 0;
+// Track numbering offset for replacing undo data.
+       int track_offset = 0;
 
-       if(!result)
-       {
 // Get path for backups
-//             path[0] = 0;
-               file->tag.get_property("path", path);
+       file->tag.get_property("path", path);
 
 // Erase everything
-               if((load_flags & LOAD_ALL) == LOAD_ALL ||
-                       (load_flags & LOAD_EDITS) == LOAD_EDITS)
-               {
-                       while(tracks->last) delete tracks->last;
-               }
+       if( (load_flags & LOAD_ALL) == LOAD_ALL ||
+               (load_flags & LOAD_EDITS) == LOAD_EDITS ) {
+               while(tracks->last) delete tracks->last;
+       }
 
-               if((load_flags & LOAD_ALL) == LOAD_ALL)
-               {
-                       for(int i = 0; i < clips.size(); i++)
-                               clips.get(i)->Garbage::remove_user();
-                       clips.remove_all();
-               }
+       if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
+               clips.clear();
+               mixers.remove_all_objects();
+       }
 
-               if(load_flags & LOAD_TIMEBAR)
-               {
-                       while(labels->last) delete labels->last;
-                       local_session->unset_inpoint();
-                       local_session->unset_outpoint();
-               }
+       if( load_flags & LOAD_TIMEBAR ) {
+               while(labels->last) delete labels->last;
+               local_session->unset_inpoint();
+               local_session->unset_outpoint();
+       }
 
 // This was originally in LocalSession::load_xml
-               if(load_flags & LOAD_SESSION)
-               {
-                       local_session->clipboard_length = 0;
-               }
+       if( load_flags & LOAD_SESSION ) {
+               local_session->clipboard_length = 0;
+       }
 
-               do{
-                       result = file->read_tag();
+       do {
+               result = file->read_tag();
 
-                       if(!result)
-                       {
-                               if(file->tag.title_is("/XML") ||
-                                       file->tag.title_is("/EDL") ||
-                                       file->tag.title_is("/CLIP_EDL") ||
-                                       file->tag.title_is("/VWINDOW_EDL"))
-                               {
-                                       result = 1;
-                               }
-                               else
-                               if(file->tag.title_is("CLIPBOARD"))
-                               {
-                                       local_session->clipboard_length =
-                                               file->tag.get_property("LENGTH", (double)0);
-                               }
-                               else
-                               if(file->tag.title_is("VIDEO"))
-                               {
-                                       if((load_flags & LOAD_VCONFIG) &&
-                                               (load_flags & LOAD_SESSION))
-                                               session->load_video_config(file, 0, load_flags);
-                                       else
-                                               result = file->skip_tag();
-                               }
-                               else
-                               if(file->tag.title_is("AUDIO"))
-                               {
-                                       if((load_flags & LOAD_ACONFIG) &&
-                                               (load_flags & LOAD_SESSION))
-                                               session->load_audio_config(file, 0, load_flags);
-                                       else
-                                               result = file->skip_tag();
-                               }
+               if( !result ) {
+                       if( file->tag.title_is("/XML") ||
+                               file->tag.title_is("/EDL") ||
+                               file->tag.title_is("/CLIP_EDL") ||
+                               file->tag.title_is("/NESTED_EDL") ||
+                               file->tag.title_is("/VWINDOW_EDL") ) {
+                               result = 1;
+                       }
+                       else
+                       if( file->tag.title_is("CLIPBOARD") ) {
+                               local_session->clipboard_length =
+                                       file->tag.get_property("LENGTH", (double)0);
+                       }
+                       else
+                       if( file->tag.title_is("VIDEO") ) {
+                               if( (load_flags & LOAD_VCONFIG) &&
+                                       (load_flags & LOAD_SESSION) )
+                                       session->load_video_config(file, 0, load_flags);
                                else
-                               if(file->tag.title_is("FOLDER"))
-                               {
-                                       char folder[BCTEXTLEN];
-                                       strcpy(folder, file->read_text());
-                                       new_folder(folder);
-                               }
+                                       result = file->skip_tag();
+                       }
+                       else
+                       if( file->tag.title_is("AUDIO") ) {
+                               if( (load_flags & LOAD_ACONFIG) &&
+                                       (load_flags & LOAD_SESSION) )
+                                       session->load_audio_config(file, 0, load_flags);
                                else
-                               if(file->tag.title_is("ASSETS"))
-                               {
-                                       if(load_flags & LOAD_ASSETS)
-                                               assets->load(file, load_flags);
-                                       else
-                                               result = file->skip_tag();
-                               }
+                                       result = file->skip_tag();
+                       }
+                       else
+                       if( file->tag.title_is("FOLDERS") ) {
+                               result = folders.load_xml(file);
+                       }
+                       else
+                       if( file->tag.title_is("MIXERS") ) {
+                               if( (load_flags & LOAD_SESSION) )
+                                       mixers.load(file);
                                else
-                               if(file->tag.title_is(labels->xml_tag))
-                               {
-                                       if(load_flags & LOAD_TIMEBAR)
-                                               labels->load(file, load_flags);
-                                       else
-                                               result = file->skip_tag();
-                               }
+                                       result = file->skip_tag();
+                       }
+                       else
+                       if( file->tag.title_is("ASSETS") ) {
+                               if( load_flags & LOAD_ASSETS )
+                                       assets->load(file, load_flags);
                                else
-                               if(file->tag.title_is("LOCALSESSION"))
-                               {
-                                       if((load_flags & LOAD_SESSION) ||
-                                               (load_flags & LOAD_TIMEBAR))
-                                               local_session->load_xml(file, load_flags);
-                                       else
-                                               result = file->skip_tag();
-                               }
+                                       result = file->skip_tag();
+                       }
+                       else
+                       if( file->tag.title_is(labels->xml_tag) ) {
+                               if( load_flags & LOAD_TIMEBAR )
+                                       labels->load(file, load_flags);
                                else
-                               if(file->tag.title_is("SESSION"))
-                               {
-                                       if((load_flags & LOAD_SESSION) &&
-                                               !parent_edl)
-                                               session->load_xml(file, 0, load_flags);
-                                       else
-                                               result = file->skip_tag();
-                               }
+                                       result = file->skip_tag();
+                       }
+                       else
+                       if( file->tag.title_is("LOCALSESSION") ) {
+                               if( (load_flags & LOAD_SESSION) ||
+                                       (load_flags & LOAD_TIMEBAR) )
+                                       local_session->load_xml(file, load_flags);
                                else
-                               if(file->tag.title_is("TRACK"))
-                               {
-                                       tracks->load(file, track_offset, load_flags);
-                               }
+                                       result = file->skip_tag();
+                       }
+                       else
+                       if( file->tag.title_is("SESSION") ) {
+                               if( (load_flags & LOAD_SESSION) &&
+                                       !parent_edl )
+                                       session->load_xml(file, 0, load_flags);
                                else
+                                       result = file->skip_tag();
+                       }
+                       else
+                       if( file->tag.title_is("TRACK") ) {
+                               tracks->load(file, track_offset, load_flags);
+                       }
+                       else
 // Sub EDL.
 // Causes clip creation to fail because that involves an opening EDL tag.
-                               if(file->tag.title_is("CLIP_EDL") && !parent_edl)
-                               {
-                                       EDL *new_edl = new EDL(this);
-                                       new_edl->create_objects();
-                                       new_edl->load_xml(file, LOAD_ALL);
-
-                                       if((load_flags & LOAD_ALL) == LOAD_ALL)
-                                               clips.append(new_edl);
-                                       else
-                                               new_edl->Garbage::remove_user();
-                               }
-                               else
-                               if(file->tag.title_is("VWINDOW_EDL") && !parent_edl)
-                               {
-                                       EDL *new_edl = new EDL(this);
-                                       new_edl->create_objects();
-                                       new_edl->load_xml(file, LOAD_ALL);
+                       if( file->tag.title_is("CLIP_EDL") && !parent_edl ) {
+                               EDL *new_edl = new EDL(this);
+                               new_edl->create_objects();
+                               new_edl->read_xml(file, LOAD_ALL);
+                               if( (load_flags & LOAD_ALL) == LOAD_ALL )
+                                       clips.add_clip(new_edl);
+                               new_edl->remove_user();
+                       }
+                       else
+                       if( file->tag.title_is("NESTED_EDL") ) {
+                               EDL *nested_edl = new EDL;
+                               nested_edl->create_objects();
+                               nested_edl->read_xml(file, LOAD_ALL);
+                               if( (load_flags & LOAD_ALL) == LOAD_ALL )
+                                       nested_edls.get_nested(nested_edl);
+                               nested_edl->remove_user();
+                       }
+                       else
+                       if( file->tag.title_is("VWINDOW_EDL") && !parent_edl ) {
+                               EDL *new_edl = new EDL(this);
+                               new_edl->create_objects();
+                               new_edl->read_xml(file, LOAD_ALL);
 
 
-                                       if((load_flags & LOAD_ALL) == LOAD_ALL)
-                                       {
-//                                             if(vwindow_edl && !vwindow_edl_shared)
-//                                                     vwindow_edl->Garbage::remove_user();
+                               if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
+//                                             if( vwindow_edl && !vwindow_edl_shared )
+//                                                     vwindow_edl->remove_user();
 //                                             vwindow_edl_shared = 0;
 //                                             vwindow_edl = new_edl;
 
-                                               append_vwindow_edl(new_edl, 0);
+                                       append_vwindow_edl(new_edl, 0);
 
-                                       }
-                                       else
+                               }
+                               else
 // Discard if not replacing EDL
-                                       {
-                                               new_edl->Garbage::remove_user();
-                                               new_edl = 0;
-                                       }
+                               {
+                                       new_edl->remove_user();
+                                       new_edl = 0;
                                }
                        }
-               }while(!result);
-       }
+               }
+       } while(!result);
+
        boundaries();
 //dump();
 
@@ -388,30 +333,21 @@ int EDL::load_xml(FileXML *file,
 // It is a "" if complete names should be used.
 // Called recursively by copy for clips, thus the string can't be terminated.
 // The string is not terminated in this call.
-int EDL::save_xml(FileXML *file,
-       const char *output_path,
-       int is_clip,
-       int is_vwindow)
-{
-       copy(0,
-               tracks->total_length(),
-               1,
-               is_clip,
-               is_vwindow,
-               file,
-               output_path,
-               0);
+int EDL::save_xml(FileXML *file, const char *output_path)
+{
+       copy(1, file, output_path, 0);
        return 0;
 }
 
 int EDL::copy_all(EDL *edl)
 {
-       if(this == edl) return 0;
+       if( this == edl ) return 0;
        update_index(edl);
-       nested_edls->clear();
        copy_session(edl);
        copy_assets(edl);
        copy_clips(edl);
+       copy_nested(edl);
+       copy_mixers(edl);
        tracks->copy_from(edl->tracks);
        labels->copy_from(edl->labels);
        return 0;
@@ -419,67 +355,62 @@ int EDL::copy_all(EDL *edl)
 
 void EDL::copy_clips(EDL *edl)
 {
-       if(this == edl) return;
+       if( this == edl ) return;
 
        remove_vwindow_edls();
 
-//     if(vwindow_edl && !vwindow_edl_shared)
-//             vwindow_edl->Garbage::remove_user();
+//     if( vwindow_edl && !vwindow_edl_shared )
+//             vwindow_edl->remove_user();
 //     vwindow_edl = 0;
 //     vwindow_edl_shared = 0;
 
-       for(int i = 0; i < edl->total_vwindow_edls(); i++)
-       {
+       for( int i=0; i<edl->total_vwindow_edls(); ++i ) {
                EDL *new_edl = new EDL(this);
                new_edl->create_objects();
                new_edl->copy_all(edl->get_vwindow_edl(i));
                append_vwindow_edl(new_edl, 0);
        }
 
-       for(int i = 0; i < clips.size(); i++)
-               clips.get(i)->Garbage::remove_user();
-       clips.remove_all();
-       for(int i = 0; i < edl->clips.total; i++)
-       {
-               add_clip(edl->clips.values[i]);
-       }
+       clips.clear();
+       for( int i=0; i<edl->clips.size(); ++i ) add_clip(edl->clips[i]);
+}
+
+void EDL::copy_nested(EDL *edl)
+{
+       if( this == edl ) return;
+       nested_edls.copy_nested(edl->nested_edls);
 }
 
 void EDL::copy_assets(EDL *edl)
 {
-       if(this == edl) return;
+       if( this == edl ) return;
 
-       if(!parent_edl)
-       {
+       if( !parent_edl ) {
                assets->copy_from(edl->assets);
        }
 }
 
+void EDL::copy_mixers(EDL *edl)
+{
+       if( this == edl ) return;
+       mixers.copy_from(edl->mixers);
+}
+
 void EDL::copy_session(EDL *edl, int session_only)
 {
-       if(this == edl) return;
+       if( this == edl ) return;
 
-       if(!session_only)
-       {
+       if( !session_only ) {
                strcpy(this->path, edl->path);
-//printf("EDL::copy_session %p %s\n", this, this->path);
-
-               folders.remove_all_objects();
-               for(int i = 0; i < edl->folders.total; i++)
-               {
-                       char *new_folder;
-                       folders.append(new_folder = new char[strlen(edl->folders.values[i]) + 1]);
-                       strcpy(new_folder, edl->folders.values[i]);
-               }
+               awindow_folder = edl->awindow_folder;
+               folders.copy_from(&edl->folders);
        }
 
-       if(!parent_edl)
-       {
+       if( !parent_edl ) {
                session->copy(edl->session);
        }
 
-       if(!session_only)
-       {
+       if( session_only <= 0 ) {
                local_session->copy_from(edl->local_session);
        }
 }
@@ -498,37 +429,22 @@ int EDL::copy_assets(double start,
        file->append_newline();
 
 // Copy everything for a save
-       if(all)
-       {
-               for(Asset *asset = assets->first;
-                       asset;
-                       asset = asset->next)
-               {
+       if( all ) {
+               for( Asset *asset=assets->first; asset; asset=asset->next ) {
                        asset_list.append(asset);
                }
        }
-       else
+       else {
 // Copy just the ones being used.
-       {
-               for(current = tracks->first;
-                       current;
-                       current = NEXT)
-               {
-                       if(current->record)
-                       {
-                               current->copy_assets(start,
-                                       end,
-                                       &asset_list);
-                       }
+               for( current = tracks->first; current; current = NEXT ) {
+                       if( !current->record ) continue;
+                       current->copy_assets(start, end, &asset_list);
                }
        }
 
 // Paths relativised here
-       for(int i = 0; i < asset_list.total; i++)
-       {
-               asset_list.values[i]->write(file,
-                       0,
-                       output_path);
+       for( int i=0; i<asset_list.size(); ++i ) {
+               asset_list[i]->write(file, 0, output_path);
        }
 
        file->tag.set_title("/ASSETS");
@@ -538,39 +454,68 @@ int EDL::copy_assets(double start,
        return 0;
 }
 
-int EDL::copy(double start,
-       double end,
-       int all,
-       int is_clip,
-       int is_vwindow,
-       FileXML *file,
-       const char *output_path,
-       int rewind_it)
+
+int EDL::copy(double start, double end, int all,
+       FileXML *file, const char *output_path, int rewind_it)
 {
-//printf("EDL::copy 1\n");
-// begin file
-       if(is_clip)
-               file->tag.set_title("CLIP_EDL");
-       else
-       if(is_vwindow)
-               file->tag.set_title("VWINDOW_EDL");
-       else
-       {
-               file->tag.set_title("EDL");
-               file->tag.set_property("VERSION", CINELERRA_VERSION);
+       file->tag.set_title("EDL");
+       file->tag.set_property("VERSION", CINELERRA_VERSION);
 // Save path for restoration of the project title from a backup.
-               if(this->path[0])
-               {
-                       file->tag.set_property("PATH", path);
-               }
-       }
+       if( this->path[0] ) file->tag.set_property("PATH", path);
+       return copy(start, end, all,
+               "/EDL", file, output_path, rewind_it);
+}
+int EDL::copy(int all, FileXML *file, const char *output_path, int rewind_it)
+{
+       return copy(0, tracks->total_length(), all, file, output_path, rewind_it);
+}
 
+int EDL::copy_clip(double start, double end, int all,
+       FileXML *file, const char *output_path, int rewind_it)
+{
+       file->tag.set_title("CLIP_EDL");
+       return copy(start, end, all,
+               "/CLIP_EDL", file, output_path, rewind_it);
+}
+int EDL::copy_clip(int all, FileXML *file, const char *output_path, int rewind_it)
+{
+       return copy_clip(0, tracks->total_length(), all, file, output_path, rewind_it);
+}
+
+int EDL::copy_nested_edl(double start, double end, int all,
+       FileXML *file, const char *output_path, int rewind_it)
+{
+       file->tag.set_title("NESTED_EDL");
+       if( this->path[0] ) file->tag.set_property("PATH", path);
+       return copy(start, end, all,
+               "/NESTED_EDL", file, output_path, rewind_it);
+}
+int EDL::copy_nested_edl(int all, FileXML *file, const char *output_path, int rewind_it)
+{
+       return copy_nested_edl(0, tracks->total_length(), all, file, output_path, rewind_it);
+}
+
+int EDL::copy_vwindow_edl(double start, double end, int all,
+       FileXML *file, const char *output_path, int rewind_it)
+{
+       file->tag.set_title("VWINDOW_EDL");
+       return copy(start, end, all,
+               "/VWINDOW_EDL", file, output_path, rewind_it);
+}
+int EDL::copy_vwindow_edl(int all, FileXML *file, const char *output_path, int rewind_it)
+{
+       return copy_vwindow_edl(0, tracks->total_length(), all, file, output_path, rewind_it);
+}
+
+
+int EDL::copy(double start, double end, int all,
+       const char *closer, FileXML *file,
+       const char *output_path, int rewind_it)
+{
        file->append_tag();
        file->append_newline();
-
 // Set clipboard samples only if copying to clipboard
-       if(!all)
-       {
+       if( !all ) {
                file->tag.set_title("CLIPBOARD");
                file->tag.set_property("LENGTH", end - start);
                file->append_tag();
@@ -594,93 +539,119 @@ int EDL::copy(double start,
                session->save_xml(file);
                session->save_video_config(file);
                session->save_audio_config(file);
+               folders.save_xml(file);
 
-// Folders
-               for(int i = 0; i < folders.total; i++)
-               {
-                       file->tag.set_title("FOLDER");
-                       file->append_tag();
-                       file->append_text(folders.values[i]);
-                       file->tag.set_title("/FOLDER");
-                       file->append_tag();
-                       file->append_newline();
-               }
+               if( !parent_edl )
+                       copy_assets(start, end, file, all, output_path);
 
-// Media
-// Don't replicate all assets for every clip.
-// The assets for the clips are probably in the mane EDL.
-               if(!is_clip)
-                       copy_assets(start,
-                               end,
-                               file,
-                               all,
-                               output_path);
+               for( int i=0; i<nested_edls.size(); ++i )
+                       nested_edls[i]->copy_nested_edl(0, tracks->total_length(), 1,
+                               file, output_path, 0);
 
 // Clips
 // Don't want this if using clipboard
-               if(all)
-               {
-                       for(int i = 0; i < total_vwindow_edls(); i++)
-                       {
-                               get_vwindow_edl(i)->save_xml(file,
-                                       output_path,
-                                       0,
-                                       1);
-                       }
+               if( all ) {
+                       for( int i=0; i<total_vwindow_edls(); ++i )
+                               get_vwindow_edl(i)->copy_vwindow_edl(1, file, output_path, 0);
+
+                       for( int i=0; i<clips.size(); ++i )
+                               clips[i]->copy_clip(1, file, output_path, 0);
 
-                       for(int i = 0; i < clips.total; i++)
-                               clips.values[i]->save_xml(file,
-                                       output_path,
-                                       1,
-                                       0);
+                       mixers.save(file);
                }
 
                file->append_newline();
                file->append_newline();
        }
 
-
-//printf("EDL::copy 1\n");
-
        labels->copy(start, end, file);
-//printf("EDL::copy 1\n");
        tracks->copy(start, end, all, file, output_path);
-//printf("EDL::copy 2\n");
 
 // terminate file
-       if(is_clip)
-               file->tag.set_title("/CLIP_EDL");
-       else
-       if(is_vwindow)
-               file->tag.set_title("/VWINDOW_EDL");
-       else
-               file->tag.set_title("/EDL");
+       file->tag.set_title(closer);
        file->append_tag();
        file->append_newline();
 
-
 // For editing operations we want to rewind it for immediate pasting.
 // For clips and saving to disk leave it alone.
-       if(rewind_it)
-       {
+       if( rewind_it ) {
                file->terminate_string();
                file->rewind();
        }
        return 0;
 }
 
+void EDL::copy_indexables(EDL *edl)
+{
+       for( Track *track=edl->tracks->first; track; track=track->next ) {
+               for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
+                       if( edit->asset )
+                               assets->update(edit->asset);
+                       if( edit->nested_edl )
+                               nested_edls.get_nested(edit->nested_edl);
+               }
+       }
+}
+
+EDL *EDL::new_nested(EDL *edl, const char *path)
+{
+       EDL *nested = new EDL;  // no parent for nested edl
+       nested->create_objects();
+       nested->copy_session(edl);
+       nested->set_path(path);
+       nested->update_index(edl);
+       nested->copy_indexables(edl);
+       nested->tracks->copy_from(edl->tracks);
+       nested_edls.append(nested);
+       return nested;
+}
+
+EDL *EDL::create_nested_clip(EDL *nested)
+{
+       EDL *new_edl = new EDL(this);  // parent for clip edl
+       new_edl->create_objects();
+       new_edl->create_nested(nested);
+       return new_edl;
+}
+
+void EDL::create_nested(EDL *nested)
+{
+// Keep frame rate, sample rate, and output size unchanged.
+// Nest all video & audio outputs
+       session->video_tracks = 1;
+       session->audio_tracks = nested->session->audio_channels;
+       create_default_tracks();
+       insert_asset(0, nested, 0, 0, 0);
+}
+
+void EDL::retrack()
+{
+       int min_w = session->output_w, min_h = session->output_h;
+       for( Track *track=tracks->first; track!=0; track=track->next ) {
+               if( track->data_type != TRACK_VIDEO ) continue;
+               int w = min_w, h = min_h;
+               for( Edit *current=track->edits->first; current!=0; current=NEXT ) {
+                       Indexable* indexable = current->get_source();
+                       if( !indexable ) continue;
+                       int edit_w = indexable->get_w(), edit_h = indexable->get_h();
+                       if( w < edit_w ) w = edit_w;
+                       if( h < edit_h ) h = edit_h;
+               }
+               if( track->track_w == w && track->track_h == h ) continue;
+               ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->
+                       translate_masks( (w - track->track_w) / 2, (h - track->track_h) / 2);
+               track->track_w = w;  track->track_h = h;
+       }
+}
+
 void EDL::rechannel()
 {
-       for(Track *current = tracks->first; current; current = NEXT)
-       {
-               if(current->data_type == TRACK_AUDIO)
-               {
+       for( Track *current=tracks->first; current; current=NEXT ) {
+               if( current->data_type == TRACK_AUDIO ) {
                        PanAutos *autos = (PanAutos*)current->automation->autos[AUTOMATION_PAN];
                        ((PanAuto*)autos->default_auto)->rechannel();
-                       for(PanAuto *keyframe = (PanAuto*)autos->first;
-                               keyframe;
-                               keyframe = (PanAuto*)keyframe->next)
-                       {
+                       for( PanAuto *keyframe = (PanAuto*)autos->first;
+                            keyframe; keyframe = (PanAuto*)keyframe->next ) {
                                keyframe->rechannel();
                        }
                }
@@ -689,10 +660,8 @@ void EDL::rechannel()
 
 void EDL::resample(double old_rate, double new_rate, int data_type)
 {
-       for(Track *current = tracks->first; current; current = NEXT)
-       {
-               if(current->data_type == data_type)
-               {
+       for( Track *current=tracks->first; current; current=NEXT ) {
+               if( current->data_type == data_type ) {
                        current->resample(old_rate, new_rate);
                }
        }
@@ -702,11 +671,9 @@ void EDL::resample(double old_rate, double new_rate, int data_type)
 void EDL::synchronize_params(EDL *edl)
 {
        local_session->synchronize_params(edl->local_session);
-       for(Track *this_track = tracks->first, *that_track = edl->tracks->first;
-               this_track && that_track;
-               this_track = this_track->next,
-               that_track = that_track->next)
-       {
+       for( Track *this_track=tracks->first, *that_track=edl->tracks->first;
+            this_track && that_track;
+            this_track=this_track->next, that_track=that_track->next ) {
                this_track->synchronize_params(that_track);
        }
 }
@@ -717,8 +684,7 @@ int EDL::trim_selection(double start,
        int edit_plugins,
        int edit_autos)
 {
-       if(start != end)
-       {
+       if( start != end ) {
 // clear the data
                clear(0,
                        start,
@@ -737,16 +703,9 @@ int EDL::trim_selection(double start,
 
 int EDL::equivalent(double position1, double position2)
 {
-       double threshold = (double).5 / session->frame_rate;
-       if(session->cursor_on_frames)
-               threshold = (double).5 / session->frame_rate;
-       else
-               threshold = (double)1 / session->sample_rate;
-
-       if(fabs(position2 - position1) < threshold)
-       return 1;
-    else
-        return 0;
+       double threshold = session->cursor_on_frames ?
+               0.5 / session->frame_rate : 1.0 / session->sample_rate;
+       return fabs(position2 - position1) < threshold ? 1 : 0;
 }
 
 double EDL::equivalent_output(EDL *edl)
@@ -758,50 +717,53 @@ double EDL::equivalent_output(EDL *edl)
 }
 
 
-void EDL::set_path(char *path)
+void EDL::set_path(const char *path)
 {
+       if( &this->path[0] == path ) return;
        strcpy(this->path, path);
 }
 
 void EDL::set_inpoint(double position)
 {
-       if(equivalent(local_session->get_inpoint(), position) &&
-               local_session->get_inpoint() >= 0)
-       {
+       if( equivalent(local_session->get_inpoint(), position) &&
+               local_session->get_inpoint() >= 0 ) {
                local_session->unset_inpoint();
        }
-       else
-       {
+       else {
                local_session->set_inpoint(align_to_frame(position, 0));
-               if(local_session->get_outpoint() <= local_session->get_inpoint())
+               if( local_session->get_outpoint() <= local_session->get_inpoint() )
                        local_session->unset_outpoint();
        }
 }
 
 void EDL::set_outpoint(double position)
 {
-       if(equivalent(local_session->get_outpoint(), position) &&
-               local_session->get_outpoint() >= 0)
-       {
+       if( equivalent(local_session->get_outpoint(), position) &&
+               local_session->get_outpoint() >= 0 ) {
                local_session->unset_outpoint();
        }
-       else
-       {
+       else {
                local_session->set_outpoint(align_to_frame(position, 0));
-               if(local_session->get_inpoint() >= local_session->get_outpoint())
+               if( local_session->get_inpoint() >= local_session->get_outpoint() )
                        local_session->unset_inpoint();
        }
 }
 
+void EDL::unset_inoutpoint()
+{
+       local_session->unset_inpoint();
+       local_session->unset_outpoint();
+}
 
-int EDL::clear(double start,
-       double end,
-       int clear_labels,
-       int clear_plugins,
-       int edit_autos)
+int EDL::blade(double position)
 {
-       if(start == end)
-       {
+       return tracks->blade(position);
+}
+
+int EDL::clear(double start, double end,
+       int clear_labels, int clear_plugins, int edit_autos)
+{
+       if( start == end ) {
                double distance = 0;
                tracks->clear_handle(start,
                        end,
@@ -809,17 +771,16 @@ int EDL::clear(double start,
                        clear_labels,
                        clear_plugins,
                        edit_autos);
-               if(clear_labels && distance > 0)
+               if( clear_labels && distance > 0 )
                        labels->paste_silence(start,
                                start + distance);
        }
-       else
-       {
+       else {
                tracks->clear(start,
                        end,
                        clear_plugins,
                        edit_autos);
-               if(clear_labels)
+               if( clear_labels )
                        labels->clear(start,
                                end,
                                1);
@@ -879,7 +840,7 @@ void EDL::paste_silence(double start,
        int edit_plugins,
        int edit_autos)
 {
-       if(edit_labels)
+       if( edit_labels )
                labels->paste_silence(start, end);
        tracks->paste_silence(start,
                end,
@@ -890,60 +851,43 @@ void EDL::paste_silence(double start,
 
 void EDL::remove_from_project(ArrayList<EDL*> *clips)
 {
-       for(int i = 0; i < clips->size(); i++)
-       {
-               for(int j = 0; j < this->clips.size(); j++)
-               {
-                       if(this->clips.get(j) == clips->values[i])
-                       {
-                               EDL *clip = this->clips.get(j);
-                               this->clips.remove(clip);
-                               clip->Garbage::remove_user();
-                       }
-               }
+       for( int i=0; i<clips->size(); ++i ) {
+               this->clips.remove_clip(clips->get(i));
        }
 }
 
 void EDL::remove_from_project(ArrayList<Indexable*> *assets)
 {
 // Remove from clips
-       if(!parent_edl)
-               for(int j = 0; j < clips.total; j++)
-               {
-                       clips.values[j]->remove_from_project(assets);
+       if( !parent_edl )
+               for( int j=0; j<clips.size(); ++j ) {
+                       clips[j]->remove_from_project(assets);
                }
 
 // Remove from VWindow EDLs
-       for(int i = 0; i < total_vwindow_edls(); i++)
+       for( int i=0; i<total_vwindow_edls(); ++i )
                get_vwindow_edl(i)->remove_from_project(assets);
 
-       for(int i = 0; i < assets->size(); i++)
-       {
+       for( int i=0; i<assets->size(); ++i ) {
 // Remove from tracks
-               for(Track *track = tracks->first; track; track = track->next)
-               {
+               for( Track *track=tracks->first; track; track=track->next ) {
                        track->remove_asset(assets->get(i));
                }
 
 // Remove from assets
-               if(!parent_edl && assets->get(i)->is_asset)
-               {
+               if( !parent_edl && assets->get(i)->is_asset ) {
                        this->assets->remove_asset((Asset*)assets->get(i));
                }
                else
-               if(!parent_edl && !assets->get(i)->is_asset)
-               {
-                       this->nested_edls->remove_edl((EDL*)assets->get(i));
+               if( !parent_edl && !assets->get(i)->is_asset ) {
+                       this->nested_edls.remove_clip((EDL*)assets->get(i));
                }
        }
 }
 
 void EDL::update_assets(EDL *src)
 {
-       for(Asset *current = src->assets->first;
-               current;
-               current = NEXT)
-       {
+       for( Asset *current=src->assets->first; current; current=NEXT ) {
                assets->update(current);
        }
 }
@@ -951,10 +895,7 @@ void EDL::update_assets(EDL *src)
 int EDL::get_tracks_height(Theme *theme)
 {
        int total_pixels = 0;
-       for(Track *current = tracks->first;
-               current;
-               current = NEXT)
-       {
+       for( Track *current=tracks->first; current; current=NEXT ) {
                total_pixels += current->vertical_span(theme);
        }
        return total_pixels;
@@ -963,12 +904,9 @@ int EDL::get_tracks_height(Theme *theme)
 int64_t EDL::get_tracks_width()
 {
        int64_t total_pixels = 0;
-       for(Track *current = tracks->first;
-               current;
-               current = NEXT)
-       {
+       for( Track *current=tracks->first; current; current=NEXT ) {
                int64_t pixels = current->horizontal_span();
-               if(pixels > total_pixels) total_pixels = pixels;
+               if( pixels > total_pixels ) total_pixels = pixels;
        }
 //printf("EDL::get_tracks_width %d\n", total_pixels);
        return total_pixels;
@@ -976,24 +914,24 @@ int64_t EDL::get_tracks_width()
 
 // int EDL::calculate_output_w(int single_channel)
 // {
-//     if(single_channel) return session->output_w;
+//     if( single_channel ) return session->output_w;
 //
 //     int widest = 0;
-//     for(int i = 0; i < session->video_channels; i++)
+//     for( int i=0; i<session->video_channels; ++i )
 //     {
-//             if(session->vchannel_x[i] + session->output_w > widest) widest = session->vchannel_x[i] + session->output_w;
+//             if( session->vchannel_x[i] + session->output_w > widest ) widest = session->vchannel_x[i] + session->output_w;
 //     }
 //     return widest;
 // }
 //
 // int EDL::calculate_output_h(int single_channel)
 // {
-//     if(single_channel) return session->output_h;
+//     if( single_channel ) return session->output_h;
 //
 //     int tallest = 0;
-//     for(int i = 0; i < session->video_channels; i++)
+//     for( int i=0; i<session->video_channels; ++i )
 //     {
-//             if(session->vchannel_y[i] + session->output_h > tallest) tallest = session->vchannel_y[i] + session->output_h;
+//             if( session->vchannel_y[i] + session->output_h > tallest ) tallest = session->vchannel_y[i] + session->output_h;
 //     }
 //     return tallest;
 // }
@@ -1001,19 +939,10 @@ int64_t EDL::get_tracks_width()
 // Get the total output size scaled to aspect ratio
 void EDL::calculate_conformed_dimensions(int single_channel, float &w, float &h)
 {
-       w = session->output_w;
-       h = session->output_h;
-
-       if((float)session->output_w / session->output_h > get_aspect_ratio())
-       {
-               h = (float)h *
-                       (session->output_w / get_aspect_ratio() / session->output_h);
-       }
+       if( (float)session->output_w / session->output_h > get_aspect_ratio() )
+               h = (w = session->output_w) / get_aspect_ratio();
        else
-       {
-               w = (float)w *
-                       (h * get_aspect_ratio() / session->output_w);
-       }
+               w = (h = session->output_h) * get_aspect_ratio();
 }
 
 float EDL::get_aspect_ratio()
@@ -1023,7 +952,7 @@ float EDL::get_aspect_ratio()
 
 int EDL::dump(FILE *fp)
 {
-       if(parent_edl)
+       if( parent_edl )
                fprintf(fp,"CLIP\n");
        else
                fprintf(fp,"EDL\n");
@@ -1034,15 +963,13 @@ int EDL::dump(FILE *fp)
                local_session->get_selectionend(1),
                local_session->loop_start,
                local_session->loop_end);
-       for(int i = 0; i < TOTAL_PANES; i++)
-       {
+       for( int i=0; i<TOTAL_PANES; ++i ) {
                fprintf(fp,"  pane %d view_start=%jd track_start=%d\n", i,
                        local_session->view_start[i],
                        local_session->track_start[i]);
        }
 
-       if(!parent_edl)
-       {
+       if( !parent_edl ) {
                fprintf(fp,"audio_channels: %d audio_tracks: %d sample_rate: %jd\n",
                        session->audio_channels,
                        session->audio_tracks,
@@ -1051,9 +978,9 @@ int EDL::dump(FILE *fp)
                        "  video_tracks: %d\n"
                        "  frame_rate: %.2f\n"
                        "  frames_per_foot: %.2f\n"
-                       "  output_w: %d\n"
-                       "  output_h: %d\n"
-                       "  aspect_w: %f\n"
+                       "  output_w: %d\n"
+                       "  output_h: %d\n"
+                       "  aspect_w: %f\n"
                        "  aspect_h: %f\n"
                        "  color_model: %d\n",
                                session->video_channels,
@@ -1061,26 +988,27 @@ int EDL::dump(FILE *fp)
                                session->frame_rate,
                                session->frames_per_foot,
                                session->output_w,
-                               session->output_h,
-                               session->aspect_w,
-                               session->aspect_h,
+                               session->output_h,
+                               session->aspect_w,
+                               session->aspect_h,
                                session->color_model);
 
-               fprintf(fp," CLIPS\n");
-               fprintf(fp,"  total: %d\n", clips.total);
-
-               for(int i = 0; i < clips.total; i++)
-               {
+               fprintf(fp," CLIPS");
+               fprintf(fp,"  total: %d\n", clips.size());
+               for( int i=0; i<clips.size(); ++i ) {
                        fprintf(fp,"\n\n");
-                       clips.values[i]->dump(fp);
+                       clips[i]->dump(fp);
                        fprintf(fp,"\n\n");
                }
+               fprintf(fp," NESTED_EDLS");
+               fprintf(fp,"  total: %d\n", nested_edls.size());
+               for( int i=0; i<nested_edls.size(); ++i )
+                       fprintf(fp,"   %s\n", nested_edls[i]->path);
 
-               fprintf(fp," VWINDOW EDLS\n");
+               fprintf(fp," VWINDOW EDLS");
                fprintf(fp,"  total: %d\n", total_vwindow_edls());
 
-               for(int i = 0; i < total_vwindow_edls(); i++)
-               {
+               for( int i=0; i<total_vwindow_edls(); ++i ) {
                        fprintf(fp,"   %s\n", get_vwindow_edl(i)->local_session->clip_title);
                }
 
@@ -1115,8 +1043,8 @@ void EDL::insert_asset(Asset *asset,
        Asset *new_asset = 0;
        EDL *new_nested_edl = 0;
 
-       if(asset) new_asset = assets->update(asset);
-       if(nested_edl) new_nested_edl = nested_edls->get_copy(nested_edl);
+       if( asset ) new_asset = assets->update(asset);
+       if( nested_edl ) new_nested_edl = nested_edls.get_nested(nested_edl);
 
 // Paste video
        int vtrack = 0;
@@ -1128,18 +1056,15 @@ void EDL::insert_asset(Asset *asset,
        int layers = 0;
        int channels = 0;
 
-       if(new_nested_edl)
-       {
-               length = new_nested_edl->tracks->total_playable_length();
+       if( new_nested_edl ) {
+               length = new_nested_edl->tracks->total_length();
                layers = 1;
                channels = new_nested_edl->session->audio_channels;
        }
 
-       if(new_asset)
-       {
+       if( new_asset ) {
 // Insert 1 frame for undefined length
-               if(new_asset->video_length < 0)
-               {
+               if( new_asset->video_length < 0 ) {
                        length = session->si_useduration ?
                                session->si_duration :
                                1.0 / session->frame_rate;
@@ -1153,30 +1078,17 @@ void EDL::insert_asset(Asset *asset,
                channels = new_asset->channels;
        }
 
-       for( ;
-               current && vtrack < layers;
-               current = NEXT)
-       {
-               if(!current->record ||
-                       current->data_type != TRACK_VIDEO)
-                       continue;
-
-               current->insert_asset(new_asset,
-                       new_nested_edl,
-                       length,
-                       position,
-                       vtrack);
-
-               vtrack++;
+       for( ; current && vtrack<layers; current=NEXT ) {
+               if( !current->record || current->data_type != TRACK_VIDEO ) continue;
+               current->insert_asset(new_asset, new_nested_edl,
+                       length, position, vtrack++);
        }
 
        int atrack = 0;
-       if(new_asset)
-       {
-               if(new_asset->audio_length < 0)
-               {
+       if( new_asset ) {
+               if( new_asset->audio_length < 0 ) {
 // Insert 1 frame for undefined length & video
-                       if(new_asset->video_data)
+                       if( new_asset->video_data )
                                length = (double)1.0 / new_asset->frame_rate;
                        else
 // Insert 1 second for undefined length & no video
@@ -1187,29 +1099,16 @@ void EDL::insert_asset(Asset *asset,
                                        new_asset->sample_rate;
        }
 
-       for(current = tracks->first;
-               current && atrack < channels;
-               current = NEXT)
-       {
-               if(!current->record ||
-                       current->data_type != TRACK_AUDIO)
-                       continue;
-
-               current->insert_asset(new_asset,
-                       new_nested_edl,
-                       length,
-                       position,
-                       atrack);
-
-
-               atrack++;
+       current = tracks->first;
+       for( ; current && atrack < channels; current=NEXT ) {
+               if( !current->record || current->data_type != TRACK_AUDIO ) continue;
+               current->insert_asset(new_asset, new_nested_edl,
+                       length, position, atrack++);
        }
 
 // Insert labels from a recording window.
-       if(labels)
-       {
-               for(RecordLabel *label = labels->first; label; label = label->next)
-               {
+       if( labels ) {
+               for( RecordLabel *label=labels->first; label; label=label->next ) {
                        this->labels->toggle_label(label->position, label->position);
                }
        }
@@ -1219,31 +1118,34 @@ void EDL::insert_asset(Asset *asset,
 
 void EDL::set_index_file(Indexable *indexable)
 {
-       if(indexable->is_asset)
+       if( indexable->is_asset )
                assets->update_index((Asset*)indexable);
        else
-               nested_edls->update_index((EDL*)indexable);
+               nested_edls.update_index((EDL*)indexable);
 }
 
 void EDL::optimize()
 {
 //printf("EDL::optimize 1\n");
-       if(local_session->preview_start < 0) local_session->preview_start = 0;
        double length = tracks->total_length();
-       if(local_session->preview_end > length) local_session->preview_end = length;
-       if(local_session->preview_start >= local_session->preview_end ) {
-               local_session->preview_start = 0;
-               local_session->preview_end = length;
-       }
-       for(Track *current = tracks->first; current; current = NEXT)
+       double preview_start = local_session->preview_start;
+       double preview_end = local_session->preview_end;
+       if( preview_end < 0 || preview_end > length )
+               preview_end = length;
+       if( preview_start == 0 && preview_end >= length )
+               local_session->preview_end = -1;
+       if( preview_start > preview_end )
+               local_session->preview_start = preview_end;
+       for( Track *current=tracks->first; current; current=NEXT )
                current->optimize();
 }
 
 int EDL::next_id()
 {
-       id_lock->lock("EDL::next_id");
+       static Mutex id_lock;
+       id_lock.lock("EDL::next_id");
        int result = EDLSession::current_id++;
-       id_lock->unlock();
+       id_lock.unlock();
        return result;
 }
 
@@ -1252,97 +1154,104 @@ void EDL::get_shared_plugins(Track *source,
        int omit_recordable,
        int data_type)
 {
-       for(Track *track = tracks->first; track; track = track->next)
-       {
-               if(!track->record || !omit_recordable)
-               {
-                       if(track != source &&
-                               track->data_type == data_type)
-                       {
-                               for(int i = 0; i < track->plugin_set.total; i++)
-                               {
-                                       Plugin *plugin = track->get_current_plugin(
-                                               local_session->get_selectionstart(1),
-                                               i,
-                                               PLAY_FORWARD,
-                                               1,
-                                               0);
-                                       if(plugin && plugin->plugin_type == PLUGIN_STANDALONE)
-                                       {
-                                               plugin_locations->append(new SharedLocation(tracks->number_of(track), i));
-                                       }
-                               }
-                       }
+       for( Track *track=tracks->first; track; track=track->next ) {
+               if( track->record && omit_recordable ) continue;
+               if( track == source || track->data_type != data_type ) continue;
+               for( int i=0; i<track->plugin_set.size(); ++i ) {
+                       Plugin *plugin = track->get_current_plugin(
+                               local_session->get_selectionstart(1),
+                               i, PLAY_FORWARD, 1, 0);
+                       if( plugin && plugin->plugin_type != PLUGIN_STANDALONE ) continue;
+                       plugin_locations->append(new SharedLocation(tracks->number_of(track), i));
                }
        }
 }
 
 void EDL::get_shared_tracks(Track *track,
        ArrayList<SharedLocation*> *module_locations,
-       int omit_recordable,
-       int data_type)
+       int omit_recordable, int data_type)
 {
-       for(Track *current = tracks->first; current; current = NEXT)
-       {
-               if(!omit_recordable || !current->record)
-               {
-                       if(current != track &&
-                               current->data_type == data_type)
-                       {
-                               module_locations->append(new SharedLocation(tracks->number_of(current), 0));
-                       }
-               }
+       for( Track *current=tracks->first; current; current=NEXT ) {
+               if( omit_recordable && current->record ) continue;
+               if( current == track || current->data_type != data_type ) continue;
+               module_locations->append(new SharedLocation(tracks->number_of(current), 0));
        }
 }
 
-// Convert position to frames if cursor alignment is enabled
+// aligned frame time
+double EDL::frame_align(double position, int round)
+{
+       double frame_pos = position * session->frame_rate;
+       frame_pos = (int64_t)(frame_pos + (round ? 0.5 : 1e-6));
+       position = frame_pos / session->frame_rate;
+       return position;
+}
+
+// Convert position to frames if alignment is enabled.
 double EDL::align_to_frame(double position, int round)
 {
-       if( session->cursor_on_frames && session->frame_rate > 0 ) {
-               double frame_no = position * session->frame_rate;
-               int64_t frame_pos = frame_no + (round ? 0.5 : 1e-6);
-               double pos = frame_pos / session->frame_rate;
-               if( !EQUIV(pos, position) ) position = pos;
-       }
+       if( session->cursor_on_frames )
+               position = frame_align(position, round);
        return position;
 }
 
 
-void EDL::new_folder(const char *folder)
+BinFolder *EDL::get_folder(int no)
 {
-       for(int i = 0; i < folders.total; i++)
-       {
-               if(!strcasecmp(folders.values[i], folder)) return;
+       for( int i=0; i<folders.size(); ++i ) {
+               BinFolder *fp = folders[i];
+               if( no == fp->awindow_folder ) return fp;
        }
-
-       char *new_folder;
-       folders.append(new_folder = new char[strlen(folder) + 1]);
-       strcpy(new_folder, folder);
+       return 0;
 }
 
-void EDL::delete_folder(const char *folder)
+int EDL::get_folder_number(const char *title)
 {
-       int i;
-       for(i = 0; i < folders.total; i++)
-       {
-               if(!strcasecmp(folders.values[i], folder))
-               {
-                       break;
-               }
+       for( int i=0; i<AWINDOW_FOLDERS; ++i ) {
+               if( !strcmp(title, AWindowGUI::folder_names[i]) )
+                       return i;
+       }
+       for( int i=0; i<folders.size(); ++i ) {
+               if( !strcmp(title, folders[i]->title) )
+                       return folders[i]->awindow_folder;
        }
+        return AW_NO_FOLDER;
+}
 
-       if(i < folders.total) delete folders.values[i];
+const char *EDL::get_folder_name(int no)
+{
+       if( no >= 0 && no<AWINDOW_FOLDERS )
+               return AWindowGUI::folder_names[no];
+       BinFolder *fp = get_folder(no);
+       return !fp ? "" : fp->title;
+}
 
-       for( ; i < folders.total - 1; i++)
-       {
-               folders.values[i] = folders.values[i + 1];
+int EDL::new_folder(const char *title, int is_clips)
+{
+       if( !title[0] ) return 1;
+       int ret = get_folder_number(title);
+       if( ret >= 0 ) return 1;
+       int idx = AWINDOW_FOLDERS;
+       for( int i=0; i<folders.size(); ++i ) {
+               BinFolder *fp = folders[i];
+               int no = fp->awindow_folder;
+               if( no >= idx ) idx = no+1;
        }
+       folders.append(new BinFolder(idx, is_clips, title));
+       return 0;
+}
+
+int EDL::delete_folder(const char *title)
+{
+       int k = folders.size();
+       while( --k >= 0 && strcmp(title, folders[k]->title) );
+       if( k >= 0 )
+               folders.remove_object_number(k);
+       return k;
 }
 
 int EDL::get_use_vconsole(VEdit* *playable_edit,
-       int64_t position,
-       int direction,
-       PlayableTracks *playable_tracks)
+       int64_t position, int direction, PlayableTracks *playable_tracks)
 {
        int share_playable_tracks = 1;
        int result = 0;
@@ -1351,57 +1260,47 @@ int EDL::get_use_vconsole(VEdit* *playable_edit,
        *playable_edit = 0;
 
 // Calculate playable tracks when being called as a nested EDL
-       if(!playable_tracks)
-       {
+       if( !playable_tracks ) {
                share_playable_tracks = 0;
                playable_tracks = new PlayableTracks(this,
-                       position,
-                       direction,
-                       TRACK_VIDEO,
-                       1);
+                       position, direction, TRACK_VIDEO, 1);
        }
 
 
 // Total number of playable tracks is 1
-       if(playable_tracks->size() != 1)
-       {
+       if( playable_tracks->size() != 1 ) {
                result = 1;
        }
-       else
-       {
+       else {
                playable_track = (VTrack*)playable_tracks->get(0);
        }
 
 // Don't need playable tracks anymore
-       if(!share_playable_tracks)
-       {
+       if( !share_playable_tracks ) {
                delete playable_tracks;
        }
 
-if(debug) printf("EDL::get_use_vconsole %d playable_tracks->size()=%d\n",
-__LINE__,
-playable_tracks->size());
-       if(result) return 1;
+if( debug ) printf("EDL::get_use_vconsole %d playable_tracks->size()=%d\n",
+ __LINE__, playable_tracks->size());
+       if( result ) return 1;
 
 
 // Test mutual conditions between direct copy rendering and this.
-       if(!playable_track->direct_copy_possible(position,
+       if( !playable_track->direct_copy_possible(position,
                direction,
-               1))
+               1) )
                return 1;
-if(debug) printf("EDL::get_use_vconsole %d\n", __LINE__);
+if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
 
        *playable_edit = (VEdit*)playable_track->edits->editof(position,
-               direction,
-               0);
+               direction, 0);
 // No edit at current location
-       if(!*playable_edit) return 1;
-if(debug) printf("EDL::get_use_vconsole %d\n", __LINE__);
+       if( !*playable_edit ) return 1;
+if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
 
 
 // Edit is nested EDL
-       if((*playable_edit)->nested_edl)
-       {
+       if( (*playable_edit)->nested_edl ) {
 // Test nested EDL
                EDL *nested_edl = (*playable_edit)->nested_edl;
                int64_t nested_position = (int64_t)((position -
@@ -1412,23 +1311,23 @@ if(debug) printf("EDL::get_use_vconsole %d\n", __LINE__);
 
 
                VEdit *playable_edit_temp = 0;
-               if(session->output_w != nested_edl->session->output_w ||
+               if( session->output_w != nested_edl->session->output_w ||
                        session->output_h != nested_edl->session->output_h ||
                        nested_edl->get_use_vconsole(&playable_edit_temp,
                                nested_position,
                                direction,
-                               0))
+                               0) )
                        return 1;
 
                return 0;
        }
 
-if(debug) printf("EDL::get_use_vconsole %d\n", __LINE__);
+if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
 // Edit is not a nested EDL
        Asset *asset = (*playable_edit)->asset;
 // Edit is silence
-       if(!asset) return 1;
-if(debug) printf("EDL::get_use_vconsole %d\n", __LINE__);
+       if( !asset ) return 1;
+if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
 
 // Asset and output device must have the same dimensions
        if( asset->width != session->output_w ||
@@ -1436,7 +1335,7 @@ if(debug) printf("EDL::get_use_vconsole %d\n", __LINE__);
                return 1;
 
 
-if(debug) printf("EDL::get_use_vconsole %d\n", __LINE__);
+if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
 // Asset and output device must have same resulting de-interlacing method
        if( ilaceautofixmethod2(session->interlace_mode,
            asset->interlace_autofixoption, asset->interlace_mode,
@@ -1462,7 +1361,7 @@ int EDL::get_sample_rate()
 
 int64_t EDL::get_audio_samples()
 {
-       return (int64_t)(tracks->total_playable_length() *
+       return (int64_t)(tracks->total_length() *
                session->sample_rate);
 }
 
@@ -1499,26 +1398,22 @@ int EDL::get_video_layers()
 
 int64_t EDL::get_video_frames()
 {
-       return (int64_t)(tracks->total_playable_length() *
+       return (int64_t)(tracks->total_length() *
                session->frame_rate);
 }
 
-
 void EDL::remove_vwindow_edls()
 {
-       for(int i = 0; i < total_vwindow_edls(); i++)
-       {
-               get_vwindow_edl(i)->Garbage::remove_user();
+       for( int i=0; i<total_vwindow_edls(); ++i ) {
+               get_vwindow_edl(i)->remove_user();
        }
        vwindow_edls.remove_all();
 }
 
 void EDL::remove_vwindow_edl(EDL *edl)
 {
-       if(vwindow_edls.number_of(edl) >= 0)
-       {
-               edl->Garbage::remove_user();
-
+       if( vwindow_edls.number_of(edl) >= 0 ) {
+               edl->remove_user();
                vwindow_edls.remove(edl);
        }
 }
@@ -1538,8 +1433,195 @@ void EDL::append_vwindow_edl(EDL *edl, int increase_counter)
 {
        if(vwindow_edls.number_of(edl) >= 0) return;
 
-       if(increase_counter) edl->Garbage::add_user();
+       if(increase_counter) edl->add_user();
        vwindow_edls.append(edl);
 }
 
 
+double EDL::next_edit(double position)
+{
+       Units::fix_double(&position);
+       double new_position = tracks->total_length();
+
+       double max_rate = get_frame_rate();
+       int sample_rate = get_sample_rate();
+       if( sample_rate > max_rate ) max_rate = sample_rate;
+       double min_movement = max_rate > 0 ? 1. / max_rate : 1e-6;
+
+// Test for edit handles after position
+       for( Track *track=tracks->first; track; track=track->next ) {
+               if( !track->record ) continue;
+               for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
+                       double edit_end = track->from_units(edit->startproject + edit->length);
+                       Units::fix_double(&edit_end);
+                       if( fabs(edit_end-position) < min_movement ) continue;
+                       if( edit_end > position && edit_end < new_position )
+                               new_position = edit_end;
+               }
+       }
+       return new_position;
+}
+
+double EDL::prev_edit(double position)
+{
+       Units::fix_double(&position);
+       double new_position = -1;
+
+       double max_rate = get_frame_rate();
+       int sample_rate = get_sample_rate();
+       if( sample_rate > max_rate ) max_rate = sample_rate;
+       double min_movement = max_rate > 0 ? 1. / max_rate : 1e-6;
+
+// Test for edit handles before cursor position
+       for( Track *track=tracks->first; track; track=track->next ) {
+               if( !track->record ) continue;
+               for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
+                       double edit_end = track->from_units(edit->startproject);
+                       Units::fix_double(&edit_end);
+                       if( fabs(edit_end-position) < min_movement ) continue;
+                       if( edit_end < position && edit_end > new_position )
+                               new_position = edit_end;
+               }
+       }
+       return new_position;
+}
+
+void EDL::rescale_proxy(int orig_scale, int new_scale)
+{
+       if( orig_scale == new_scale ) return;
+// project size
+       float orig_w = (float)session->output_w * orig_scale;
+       float orig_h = (float)session->output_h * orig_scale;
+       if( !parent_edl ) {
+               session->output_w = Units::round(orig_w / new_scale);
+               session->output_h = Units::round(orig_h / new_scale);
+       }
+
+// track sizes
+       for( Track *track=tracks->first; track; track=track->next ) {
+               if( track->data_type != TRACK_VIDEO ) continue;
+               orig_w = (float)track->track_w * orig_scale;
+               orig_h = (float)track->track_h * orig_scale;
+               track->track_w = Units::round(orig_w / new_scale);
+               track->track_h = Units::round(orig_h / new_scale);
+               ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->
+                       set_proxy(orig_scale, new_scale);
+               ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_X])->
+                       set_proxy(orig_scale, new_scale);
+               ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_Y])->
+                       set_proxy(orig_scale, new_scale);
+               ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_X])->
+                       set_proxy(orig_scale, new_scale);
+               ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_Y])->
+                       set_proxy(orig_scale, new_scale);
+       }
+}
+
+void EDL::set_proxy(int new_scale, int use_scaler,
+       ArrayList<Indexable*> *orig_assets, ArrayList<Indexable*> *proxy_assets)
+{
+       int orig_scale = session->proxy_scale;
+       int orig_use_scaler = session->proxy_use_scaler;
+
+// rescale to full size asset in read_frame
+       session->proxy_scale = new_scale;
+       session->proxy_use_scaler = use_scaler;
+
+       if( use_scaler ) {
+               for( int i=0; i<proxy_assets->size(); ++i ) {
+                       Asset *proxy_asset = (Asset *)proxy_assets->get(i);
+                       proxy_asset->width = orig_assets->get(i)->get_w();
+                       proxy_asset->height = orig_assets->get(i)->get_h();
+               }
+               new_scale = 1;
+       }
+
+       if( !orig_use_scaler )
+               rescale_proxy(orig_scale, new_scale);
+
+// change original assets to proxy assets
+       int awindow_folder = use_scaler || new_scale != 1 ? AW_PROXY_FOLDER : AW_MEDIA_FOLDER;
+       for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
+               const char *orig_path = orig_assets->get(i)->path;
+               Indexable *proxy_idxbl = proxy_assets->get(i);
+               proxy_idxbl->awindow_folder = awindow_folder;
+               Asset *proxy_asset = proxy_idxbl->is_asset ? assets->update((Asset *)proxy_idxbl) : 0;
+               if( proxy_asset && proxy_idxbl ) {
+                       proxy_asset->width = proxy_idxbl->get_w();
+                       proxy_asset->height = proxy_idxbl->get_h();
+               }
+               EDL *proxy_edl = !proxy_idxbl->is_asset ? (EDL *)proxy_idxbl : 0;
+// replace track contents
+               for( Track *track=tracks->first; track; track=track->next ) {
+                       if( track->data_type != TRACK_VIDEO ) continue;
+                       for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
+                               Indexable *idxbl = (Indexable *)edit->asset;
+                               if( !idxbl ) idxbl = (Indexable *)edit->nested_edl;
+                               if( !idxbl ) continue;
+                               if( strcmp(idxbl->path, orig_path) ) continue;
+                               edit->asset = proxy_asset;
+                               edit->nested_edl = proxy_edl;
+                       }
+               }
+               for( int j=0,m=clips.size(); j<m; ++j ) {
+                       EDL *clip = clips[j];
+                       int has_proxy = 0;
+                       for( Track *track=clip->tracks->first; track; track=track->next ) {
+                               if( track->data_type != TRACK_VIDEO ) continue;
+                               for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
+                                       Indexable *idxbl = (Indexable *)edit->asset;
+                                       if( !idxbl ) idxbl = (Indexable *)edit->nested_edl;
+                                       if( !idxbl ) continue;
+                                       if( strcmp(idxbl->path, orig_path) ) continue;
+                                       edit->asset = proxy_asset;
+                                       edit->nested_edl = proxy_edl;
+                                       has_proxy = 1;
+                               }
+                       }
+                       if( has_proxy && !orig_use_scaler )
+                               clip->rescale_proxy(orig_scale, new_scale);
+               }
+       }
+}
+
+void EDL::add_proxy(int use_scaler,
+       ArrayList<Indexable*> *orig_assets, ArrayList<Indexable*> *proxy_assets)
+{
+       if( use_scaler ) {
+               for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
+                       Asset *proxy_asset = (Asset *)proxy_assets->get(i);
+                       proxy_asset->width = orig_assets->get(i)->get_w();
+                       proxy_asset->height = orig_assets->get(i)->get_h();
+               }
+       }
+
+// change original assets to proxy assets
+       for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
+               Asset *proxy_asset = assets->update((Asset *)proxy_assets->get(i));
+               proxy_asset->awindow_folder = AW_PROXY_FOLDER;
+// replace track contents
+               for( Track *track=tracks->first; track; track=track->next ) {
+                       if( track->data_type != TRACK_VIDEO ) continue;
+                       for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
+                               if( !edit->asset ) continue;
+                               if( !strcmp(edit->asset->path, orig_assets->get(i)->path) ) {
+                                       edit->asset = proxy_asset;
+                               }
+                       }
+               }
+       }
+}
+
+Asset *EDL::get_proxy_asset()
+{
+       return awindow_folder == AW_PROXY_FOLDER ?
+               tracks->first->edits->first->asset : 0;
+}
+
+double EDL::get_cursor_position(int cursor_x, int pane_no)
+{
+       return (double)cursor_x * local_session->zoom_sample / session->sample_rate +
+               (double)local_session->view_start[pane_no] *
+                       local_session->zoom_sample / session->sample_rate;
+}
+