bug fix for In/Out pointer Attach Effect on empty track/location - Andrew+Anon
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / mwindowedit.C
index 7e6c644548f55b4d741836c455f272f39994df44..458ced964e8f3418505b7d27ccf19adb80662dd0 100644 (file)
@@ -85,7 +85,8 @@
 void MWindow::add_audio_track_entry(int above, Track *dst)
 {
        undo_before();
-       add_audio_track(above, dst);
+       Track *track = add_audio_track(above, dst);
+       track->master = 1;
        save_backup();
        undo_after(_("add track"), LOAD_ALL);
 
@@ -95,10 +96,11 @@ void MWindow::add_audio_track_entry(int above, Track *dst)
        cwindow->refresh_frame(CHANGE_EDL);
 }
 
-void MWindow::add_video_track_entry(Track *dst)
+void MWindow::add_video_track_entry(int above, Track *dst)
 {
        undo_before();
-       add_video_track(1, dst);
+       Track *track = add_video_track(above, dst);
+       track->master = 1;
        undo_after(_("add track"), LOAD_ALL);
 
        restart_brender();
@@ -109,10 +111,11 @@ void MWindow::add_video_track_entry(Track *dst)
        save_backup();
 }
 
-void MWindow::add_subttl_track_entry(Track *dst)
+void MWindow::add_subttl_track_entry(int above, Track *dst)
 {
        undo_before();
-       add_subttl_track(1, dst);
+       Track *track = add_subttl_track(above, dst);
+       track->master = 1;
        undo_after(_("add track"), LOAD_ALL);
 
        restart_brender();
@@ -124,28 +127,28 @@ void MWindow::add_subttl_track_entry(Track *dst)
 }
 
 
-int MWindow::add_audio_track(int above, Track *dst)
+Track *MWindow::add_audio_track(int above, Track *dst)
 {
-       edl->tracks->add_audio_track(above, dst);
+       Track *track = edl->tracks->add_audio_track(above, dst);
        edl->tracks->update_y_pixels(theme);
        save_backup();
-       return 0;
+       return track;
 }
 
-int MWindow::add_video_track(int above, Track *dst)
+Track *MWindow::add_video_track(int above, Track *dst)
 {
-       edl->tracks->add_video_track(above, dst);
+       Track *track = edl->tracks->add_video_track(above, dst);
        edl->tracks->update_y_pixels(theme);
        save_backup();
-       return 0;
+       return track;
 }
 
-int MWindow::add_subttl_track(int above, Track *dst)
+Track *MWindow::add_subttl_track(int above, Track *dst)
 {
-       edl->tracks->add_subttl_track(above, dst);
+       Track *track = edl->tracks->add_subttl_track(above, dst);
        edl->tracks->update_y_pixels(theme);
        save_backup();
-       return 0;
+       return track;
 }
 
 void MWindow::asset_to_all()
@@ -179,7 +182,7 @@ void MWindow::asset_to_all()
 
                        for( Track *current=edl->tracks->first; current; current=NEXT ) {
                                if( current->data_type == TRACK_VIDEO /* &&
-                                       current->record */  ) {
+                                       current->is_armed() */  ) {
                                        current->track_w = w;
                                        current->track_h = h;
                                }
@@ -286,7 +289,8 @@ void MWindow::clear_entry()
 
        edl->optimize();
        save_backup();
-       undo_after(_("clear"), LOAD_EDITS | LOAD_TIMEBAR);
+       undo_after(_("clear"),
+                       LOAD_AUTOMATION + LOAD_EDITS + LOAD_TIMEBAR);
 
        restart_brender();
        update_plugin_guis();
@@ -333,7 +337,7 @@ void MWindow::set_automation_mode(int mode)
                edl->local_session->get_selectionstart(),
                edl->local_session->get_selectionend(),
                mode);
-       int changed_edl = speed_after(1);
+       int changed_edl = speed_after(1, 1);
        save_backup();
        char string[BCSTRLEN];
        sprintf(string,"set %s", FloatAuto::curve_name(mode));
@@ -349,7 +353,7 @@ void MWindow::clear_automation()
        speed_before();
        edl->tracks->clear_automation(edl->local_session->get_selectionstart(),
                edl->local_session->get_selectionend());
-       int changed_edl = speed_after(1);
+       int changed_edl = speed_after(1, 1);
        save_backup();
        undo_after(_("clear keyframes"),
                !changed_edl ? LOAD_AUTOMATION :
@@ -362,7 +366,7 @@ int MWindow::clear_default_keyframe()
        undo_before();
        speed_before();
        edl->tracks->clear_default_keyframe();
-       int changed_edl = speed_after(1);
+       int changed_edl = speed_after(1, 1);
        save_backup();
        undo_after(_("clear default keyframe"),
                !changed_edl ? LOAD_AUTOMATION :
@@ -422,6 +426,14 @@ void MWindow::clear_select()
        gui->draw_overlays(1);
 }
 
+void MWindow::select_edits(int v)
+{
+       double start = edl->local_session->get_selectionstart();
+       double end = edl->local_session->get_selectionend();
+       edl->tracks->select_edits(start, end, v);
+       gui->draw_overlays(1);
+}
+
 void MWindow::concatenate_tracks()
 {
        undo_before();
@@ -665,7 +677,7 @@ int MWindow::cut_automation()
        copy_automation();
        edl->tracks->clear_automation(edl->local_session->get_selectionstart(),
                edl->local_session->get_selectionend());
-       int changed_edl = speed_after(1);
+       int changed_edl = speed_after(1, 1);
        save_backup();
        undo_after(_("cut keyframes"),
                !changed_edl ? LOAD_AUTOMATION :
@@ -681,7 +693,7 @@ int MWindow::cut_default_keyframe()
        speed_before();
        copy_default_keyframe();
        edl->tracks->clear_default_keyframe();
-       int changed_edl = speed_after(1);
+       int changed_edl = speed_after(1, 1);
        save_backup();
        undo_after(_("cut default keyframe"),
                !changed_edl ? LOAD_AUTOMATION :
@@ -761,19 +773,40 @@ void MWindow::insert(double position, FileXML *file,
 //printf("MWindow::insert 6 %p\n", vwindow->get_edl());
 }
 
-void MWindow::insert_effects_canvas(double start,
-       double length)
+void MWindow::insert_effects_canvas(Track *dest_track, double start, double length)
 {
-       Track *dest_track = session->track_highlighted;
-       if( !dest_track ) return;
-
        undo_before();
 
-       for( int i=0; i<session->drag_pluginservers->total; ++i ) {
-               PluginServer *plugin = session->drag_pluginservers->values[i];
-               insert_effect(plugin->title, 0, dest_track,
-                       i == 0 ? session->pluginset_highlighted : 0,
-                       start, length, PLUGIN_STANDALONE);
+       ArrayList<SharedLocation> shared_locations;
+       PluginSet *pluginset = session->pluginset_highlighted;
+       int gang = edl->local_session->gang_tracks != GANG_NONE ? 1 : 0;
+       int data_type = dest_track->data_type;
+       int first_track = 1;
+
+       for( Track *track=dest_track; track; track=track->next ) {
+               if( gang && track->master && !first_track ) break;
+               if( track->data_type != data_type ) continue;
+               if( !track->is_armed() ) continue;
+               int module = edl->tracks->number_of(track);
+               for( int i=0; i<session->drag_pluginservers->total; ++i ) {
+                       PluginServer *plugin = session->drag_pluginservers->values[i];
+                       int shared = gang; // && plugin->multichannel ? 1 : 0;
+                       int plugin_type = !first_track && shared ?
+                               PLUGIN_SHAREDPLUGIN : PLUGIN_STANDALONE;
+                       SharedLocation *shared_location = !first_track ?
+                               &shared_locations[i] : &shared_locations.append();
+                       insert_effect(plugin->title, shared_location, track,
+                                       pluginset, start, length, plugin_type);
+                       if( first_track && shared ) {
+                               shared_location->module = module;
+                               shared_location->plugin = pluginset ?
+                                       track->plugin_set.number_of(pluginset) :
+                                       track->plugin_set.total-1 ;
+                       }
+               }
+               if( !gang ) break;
+               first_track = 0;
+               pluginset = 0;
        }
 
        save_backup();
@@ -787,8 +820,6 @@ void MWindow::insert_effects_cwindow(Track *dest_track)
 {
        if( !dest_track ) return;
 
-       undo_before();
-
        double start = 0;
        double length = dest_track->get_length();
 
@@ -799,55 +830,45 @@ void MWindow::insert_effects_cwindow(Track *dest_track)
                        edl->local_session->get_selectionstart();
        }
 
-       for( int i=0; i<session->drag_pluginservers->total; ++i ) {
-               PluginServer *plugin = session->drag_pluginservers->values[i];
-               insert_effect(plugin->title, 0, dest_track, 0,
-                       start, length, PLUGIN_STANDALONE);
-       }
-
-       save_backup();
-       undo_after(_("insert effect"), LOAD_EDITS | LOAD_PATCHES);
-       restart_brender();
-       sync_parameters(CHANGE_EDL);
+       insert_effects_canvas(dest_track, start, length);
        gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
 }
 
-void MWindow::insert_effect(char *title,
-       SharedLocation *shared_location,
-       int data_type,
-       int plugin_type,
-       int single_standalone)
+void MWindow::insert_effect(char *title, SharedLocation *shared_location,
+               int data_type, int plugin_type, int single_standalone)
 {
        Track *current = edl->tracks->first;
        SharedLocation shared_location_local;
        shared_location_local.copy_from(shared_location);
        int first_track = 1;
+       double start_pos = edl->local_session->get_selectionstart();
+       double end_pos = edl->local_session->get_selectionend();
        for( ; current; current=NEXT ) {
-               if( current->data_type == data_type &&
-                       current->record ) {
-                       insert_effect(title, &shared_location_local,
-                               current, 0, 0, 0, plugin_type);
-
-                       if( first_track ) {
-                               if( plugin_type == PLUGIN_STANDALONE && single_standalone ) {
-                                       plugin_type = PLUGIN_SHAREDPLUGIN;
-                                       shared_location_local.module = edl->tracks->number_of(current);
-                                       shared_location_local.plugin = current->plugin_set.total - 1;
-                               }
-                               first_track = 0;
+               if( current->data_type != data_type ) continue;
+               if( !current->is_armed() ) continue;
+               double start = start_pos, end = end_pos;
+               if( plugin_type == PLUGIN_STANDALONE && start >= end ) {
+                       start = 0;
+                       end = current->get_length();
+               }
+               double length = end - start;
+               insert_effect(title, &shared_location_local,
+                               current, 0, start, length, plugin_type);
+               if( first_track ) {
+                       if( plugin_type == PLUGIN_STANDALONE && single_standalone ) {
+                               plugin_type = PLUGIN_SHAREDPLUGIN;
+                               shared_location_local.module = edl->tracks->number_of(current);
+                               shared_location_local.plugin = current->plugin_set.total - 1;
+                               start_pos = start;  end_pos = end;
                        }
+                       first_track = 0;
                }
        }
 }
 
-
 void MWindow::insert_effect(char *title,
-       SharedLocation *shared_location,
-       Track *track,
-       PluginSet *plugin_set,
-       double start,
-       double length,
-       int plugin_type)
+               SharedLocation *shared_location, Track *track, PluginSet *plugin_set,
+               double start, double length, int plugin_type)
 {
        KeyFrame *default_keyframe = 0;
        PluginServer *server = 0;
@@ -860,9 +881,8 @@ void MWindow::insert_effect(char *title,
                server->save_data(default_keyframe);
        }
 // Insert plugin object
-       track->insert_effect(title, shared_location,
-               default_keyframe, plugin_set,
-               start, length, plugin_type);
+       track->insert_effect(title, shared_location, default_keyframe,
+                       plugin_set, start, length, plugin_type);
        track->optimize();
 
        if( plugin_type == PLUGIN_STANDALONE ) {
@@ -940,6 +960,34 @@ void MWindow::finish_modify_handles()
        cwindow->update(1, 0, 0, 0, 1);
 }
 
+int MWindow::modify_transnhandles()
+{
+       gui->reset_default_message();
+       gui->default_message();
+       Transition *transition = session->drag_transition;
+       if( !transition ) return 1;
+       int64_t length = transition->length;
+       Track *track = transition->edit->track;
+       int64_t start_pos = track->to_units(session->drag_start, 0);
+       int64_t end_pos = track->to_units(session->drag_position, 0);
+       length += end_pos - start_pos;
+       if( length < 0 ) length = 0;
+       if( length == transition->length ) return 0;
+
+       undo_before();
+       transition->length = length;
+       undo_after(_("trans handle"), LOAD_EDITS);
+
+       save_backup();
+       restart_brender();
+       sync_parameters(CHANGE_EDL);
+       update_plugin_guis();
+       gui->update(1, FORCE_REDRAW, 1, 1, 1, 1, 0);
+       cwindow->update(1, 0, 0, 0, 1);
+
+       return 0;
+}
+
 void MWindow::match_output_size(Track *track)
 {
        undo_before();
@@ -1144,12 +1192,62 @@ void MWindow::move_plugins_down(PluginSet *plugin_set)
        sync_parameters(CHANGE_EDL);
 }
 
+void MWindow::roll_track_down(Track *track)
+{
+       undo_before();
+       edl->tracks->roll_track_down(track);
+       save_backup();
+       undo_after(_("move track down"), LOAD_ALL);
+
+       restart_brender();
+       gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
+       sync_parameters(CHANGE_EDL);
+       save_backup();
+}
+
+void MWindow::roll_tracks_down()
+{
+       undo_before();
+       edl->tracks->roll_tracks_down();
+       save_backup();
+       undo_after(_("move tracks down"), LOAD_ALL);
+
+       restart_brender();
+       gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
+       sync_parameters(CHANGE_EDL);
+       save_backup();
+}
+
+void MWindow::roll_track_up(Track *track)
+{
+       undo_before();
+       edl->tracks->roll_track_up(track);
+       save_backup();
+       undo_after(_("move track up"), LOAD_ALL);
+       restart_brender();
+       gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
+       sync_parameters(CHANGE_EDL);
+       save_backup();
+}
+
+void MWindow::roll_tracks_up()
+{
+       undo_before();
+       edl->tracks->roll_tracks_up();
+       save_backup();
+       undo_after(_("move tracks up"), LOAD_ALL);
+       restart_brender();
+       gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
+       sync_parameters(CHANGE_EDL);
+}
+
+
 void MWindow::move_track_down(Track *track)
 {
        undo_before();
        edl->tracks->move_track_down(track);
        save_backup();
-       undo_after(_("move track down"), LOAD_ALL);
+       undo_after(_("swap track down"), LOAD_ALL);
 
        restart_brender();
        gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
@@ -1162,7 +1260,7 @@ void MWindow::move_tracks_down()
        undo_before();
        edl->tracks->move_tracks_down();
        save_backup();
-       undo_after(_("move tracks down"), LOAD_ALL);
+       undo_after(_("swap tracks down"), LOAD_ALL);
 
        restart_brender();
        gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
@@ -1175,7 +1273,7 @@ void MWindow::move_track_up(Track *track)
        undo_before();
        edl->tracks->move_track_up(track);
        save_backup();
-       undo_after(_("move track up"), LOAD_ALL);
+       undo_after(_("swap track up"), LOAD_ALL);
        restart_brender();
        gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
        sync_parameters(CHANGE_EDL);
@@ -1187,7 +1285,7 @@ void MWindow::move_tracks_up()
        undo_before();
        edl->tracks->move_tracks_up();
        save_backup();
-       undo_after(_("move tracks up"), LOAD_ALL);
+       undo_after(_("swap tracks up"), LOAD_ALL);
        restart_brender();
        gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
        sync_parameters(CHANGE_EDL);
@@ -1265,7 +1363,8 @@ void MWindow::overwrite(EDL *source, int all)
        edl->local_session->set_selectionend(dst_start + overwrite_len);
 
        save_backup();
-       undo_after(_("overwrite"), LOAD_EDITS);
+       undo_after(_("overwrite"),
+                       LOAD_AUTOMATION + LOAD_EDITS + LOAD_TIMEBAR);
 
        restart_brender();
        update_plugin_guis();
@@ -1318,7 +1417,8 @@ void MWindow::paste(double start, Track *first_track, int clear_selection, int o
 
                save_backup();
 
-               undo_after(_("paste"), LOAD_EDITS | LOAD_TIMEBAR);
+               undo_after(_("paste"),
+                               LOAD_AUTOMATION + LOAD_EDITS + LOAD_TIMEBAR);
                restart_brender();
                update_plugin_guis();
                gui->update(1, FORCE_REDRAW, 1, 1, 0, 1, 0);
@@ -1355,7 +1455,8 @@ int MWindow::paste_assets(double position, Track *dest_track, int overwrite)
 
        save_backup();
 
-       undo_after(_("paste assets"), LOAD_EDITS);
+       undo_after(_("paste assets"),
+                       LOAD_AUTOMATION + LOAD_EDITS + LOAD_TIMEBAR);
        restart_brender();
        gui->update(1, FORCE_REDRAW, 1, 0, 0, 1, 0);
        sync_parameters(CHANGE_EDL);
@@ -1429,7 +1530,7 @@ int MWindow::paste_automation()
                edl->tracks->clear_automation(start, end);
                edl->tracks->paste_automation(start, &file, 0, 1,
                        edl->session->typeless_keyframes);
-               int changed_edl = speed_after(1);
+               int changed_edl = speed_after(1, 0);
                save_backup();
                undo_after(_("paste keyframes"),
                        !changed_edl ? LOAD_AUTOMATION :
@@ -1456,7 +1557,7 @@ int MWindow::paste_default_keyframe()
                edl->tracks->paste_automation(start, &file, 1, 0,
                        edl->session->typeless_keyframes);
 //             edl->tracks->paste_default_keyframe(&file);
-               int changed_edl = speed_after(1);
+               int changed_edl = speed_after(1, 1);
                undo_after(_("paste default keyframe"),
                        !changed_edl ? LOAD_AUTOMATION :
                                LOAD_AUTOMATION + LOAD_EDITS + LOAD_TIMEBAR);
@@ -1489,7 +1590,7 @@ int MWindow::paste_edls(ArrayList<EDL*> *new_edls, int load_mode,
 // Delete current project
        if( load_mode == LOADMODE_REPLACE ||
            load_mode == LOADMODE_REPLACE_CONCATENATE ) {
-               reset_caches();
+               reset_caches(1);
                edl->save_defaults(defaults);
                hide_plugins();
                edl->Garbage::remove_user();
@@ -1522,6 +1623,7 @@ int MWindow::paste_edls(ArrayList<EDL*> *new_edls, int load_mode,
                need_new_tracks = 1;
                for( int i=0; i<new_edls->total; ++i ) {
                        EDL *new_edl = new_edls->values[i];
+                       int first_track = 1;
                        for( Track *current=new_edl->tracks->first; current; current=NEXT ) {
                                switch( current->data_type ) {
                                case TRACK_VIDEO:
@@ -1537,6 +1639,10 @@ int MWindow::paste_edls(ArrayList<EDL*> *new_edls, int load_mode,
                                default:
                                        continue;
                                }
+                               if( first_track ) {
+                                       edl->tracks->last->master = 1;
+                                       first_track = 0;
+                               }
 // re-label only if not already labeled
                                if( new_edl->local_session->asset2edl )
                                        strcpy(current->title, edl->tracks->last->title);
@@ -1554,7 +1660,7 @@ int MWindow::paste_edls(ArrayList<EDL*> *new_edls, int load_mode,
            load_mode == LOADMODE_PASTE ) {
                Track *current = first_track ? first_track : edl->tracks->first;
                for( ; current; current=NEXT ) {
-                       if( current->record ) {
+                       if( current->is_armed() ) {
                                destination_tracks.append(current);
                        }
                }
@@ -1658,14 +1764,14 @@ int MWindow::paste_edls(ArrayList<EDL*> *new_edls, int load_mode,
                                if( destination_track < destination_tracks.total &&
                                    destination_tracks.values[destination_track]->data_type == new_track->data_type ) {
                                        Track *track = destination_tracks.values[destination_track];
-
 // Replace default keyframes if first EDL and new tracks were created.
 // This means data copied from one track and pasted to another won't retain
 // the camera position unless it's a keyframe.  If it did, previous data in the
 // track might get unknowingly corrupted.  Ideally we would detect when differing
 // default keyframes existed and create discrete keyframes for both.
                                        int replace_default = (i == 0) && need_new_tracks;
-
+// master tracks are the first track in each new edl when new tracks are created
+                                       int master = track->master;
 //printf("MWindow::paste_edls 1 %d\n", replace_default);
 // Insert new track at current position
                                        switch( load_mode ) {
@@ -1691,6 +1797,7 @@ int MWindow::paste_edls(ArrayList<EDL*> *new_edls, int load_mode,
 //PRINT_TRACE
                                        track->insert_track(new_track, current_position, replace_default,
                                                edit_plugins, edit_autos, edl_length);
+                                       if( master ) track->master = 1;
 //PRINT_TRACE
                                }
 
@@ -1770,7 +1877,8 @@ void MWindow::paste_silence()
                edl->session->autos_follow_edits);
        edl->optimize();
        save_backup();
-       undo_after(_("silence"), LOAD_EDITS | LOAD_TIMEBAR);
+       undo_after(_("silence"),
+                       LOAD_AUTOMATION + LOAD_EDITS + LOAD_TIMEBAR);
 
        update_plugin_guis();
        restart_brender();
@@ -2141,7 +2249,8 @@ void MWindow::splice(EDL *source, int all)
        edl->local_session->set_selectionend(start + source_end - source_start);
 
        save_backup();
-       undo_after(_("splice"), LOAD_EDITS | LOAD_TIMEBAR);
+       undo_after(_("splice"),
+                       LOAD_AUTOMATION + LOAD_EDITS + LOAD_TIMEBAR);
        update_plugin_guis();
        restart_brender();
        gui->update(1, NORMAL_DRAW, 1, 1, 0, 1, 0);
@@ -2164,7 +2273,7 @@ void MWindow::save_clip(EDL *new_edl, const char *txt)
        Track *track = new_edl->tracks->first;
        const char *path = edl->path;
        for( ; (!path || !*path) && track; track=track->next ) {
-               if( !track->record ) continue;
+               if( !track->is_armed() ) continue;
                Edit *edit = track->edits->first;
                if( !edit ) continue;
                Indexable *indexable = edit->get_source();
@@ -2272,7 +2381,8 @@ void MWindow::trim_selection()
                edl->session->autos_follow_edits);
 
        save_backup();
-       undo_after(_("trim selection"), LOAD_EDITS | LOAD_TIMEBAR);
+       undo_after(_("trim selection"),
+                       LOAD_AUTOMATION + LOAD_EDITS + LOAD_TIMEBAR);
        update_plugin_guis();
        gui->update(1, FORCE_REDRAW, 1, 1, 1, 1, 0);
        cwindow->update(1, 0, 0, 0, 1);
@@ -2391,7 +2501,7 @@ void MWindow::remap_audio(int pattern)
        int current_track = 0;
        for( Track *current=edl->tracks->first; current; current=NEXT ) {
                if( current->data_type == TRACK_AUDIO &&
-                       current->record ) {
+                       current->is_armed() ) {
                        Autos *pan_autos = current->automation->autos[AUTOMATION_PAN];
                        PanAuto *pan_auto = (PanAuto*)pan_autos->get_auto_for_editing(-1);
 
@@ -2500,9 +2610,13 @@ void MWindow::cut_commercials()
 #endif
 }
 
-int MWindow::normalize_speed(EDL *old_edl, EDL *new_edl)
+int MWindow::normalize_speed(EDL *old_edl, EDL *new_edl, int edit_speed)
 {
-       int result = 0;
+       int edit_plugins = edl->session->plugins_follow_edits;
+       int edit_autos = edl->session->autos_follow_edits;
+       int edit_labels = edl->session->labels_follow_edits;
+       if( !edit_autos ) edit_speed = 0;
+       int result = 0, first_track = 1;
        Track *old_track = old_edl->tracks->first;
        Track *new_track = new_edl->tracks->first;
        for( ; old_track && new_track; old_track=old_track->next, new_track=new_track->next ) {
@@ -2516,20 +2630,107 @@ int MWindow::normalize_speed(EDL *old_edl, EDL *new_edl)
                        old_speed = (FloatAuto *)old_speed->next;
                        new_speed = (FloatAuto *)new_speed->next;
                }
-               Edit *old_edit = old_track->edits->first;
-               Edit *new_edit = new_track->edits->first;
-               for( ; old_edit && new_edit; old_edit=old_edit->next, new_edit=new_edit->next ) {
-                       int64_t edit_start = old_edit->startproject, edit_end = edit_start + old_edit->length;
-                       if( old_speed || new_speed ) {
-                               double orig_start = old_speeds->automation_integral(0, edit_start, PLAY_FORWARD);
-                               double orig_end   = old_speeds->automation_integral(0, edit_end, PLAY_FORWARD);
-                               edit_start = new_speeds->speed_position(orig_start);
-                               edit_end = new_speeds->speed_position(orig_end);
-                               result = 1;
+               if( !old_speed && !new_speed ) continue;
+               result = 1;
+               if( edit_speed ) {
+                       Autos *old_autos = old_track->automation->autos[AUTOMATION_SPEED];
+                       Autos *new_autos = new_track->automation->autos[AUTOMATION_SPEED];
+                       Auto *old_auto = old_autos ? old_autos->first : 0;
+                       for( ; old_auto; old_auto=old_auto->next ) {
+                               Auto *new_auto = new_autos->get_auto(old_auto->orig_id);
+                               if( !new_auto ) continue;
+                               int64_t auto_pos = old_auto->position;
+                               double orig_pos = old_speeds->automation_integral(0, auto_pos, PLAY_FORWARD);
+                               auto_pos = new_track->frame_align(new_speeds->speed_position(orig_pos), 1);
+                               new_auto->position = auto_pos;
                        }
+               }
+               Edit *old_edit = old_track->edits->first;
+               for( ; old_edit; old_edit=old_edit->next ) {
+                       Edit *new_edit = new_track->edits->get_edit(old_edit->orig_id);
+                       if( !new_edit ) continue;
+                       int64_t edit_start = old_edit->startproject;
+                       int64_t edit_end = edit_start + old_edit->length;
+                       double orig_start = old_speeds->automation_integral(0, edit_start, PLAY_FORWARD);
+                       double orig_end   = old_speeds->automation_integral(0, edit_end, PLAY_FORWARD);
+                       edit_start = new_track->frame_align(new_speeds->speed_position(orig_start), 1);
+                       edit_end = new_track->frame_align(new_speeds->speed_position(orig_end), 1);
                        new_edit->startproject = edit_start;
                        new_edit->length = edit_end - edit_start;
                }
+               if( first_track && old_track->is_armed() ) {
+                       Labels *old_labels = old_edl->labels;
+                       Labels *new_labels = new_edl->labels;
+                       if( edit_labels && old_labels && new_labels ) {
+                               Label *old_label = old_labels->first;
+                               for( ; old_label; old_label=old_label->next ) {
+                                       Label *new_label = new_labels->get_label(old_label->orig_id);
+                                       if( !new_label ) continue;
+                                       int64_t label_pos = old_track->to_units(old_label->position, 1);
+                                       double orig_pos = old_speeds->automation_integral(0, label_pos, PLAY_FORWARD);
+                                       label_pos = new_track->frame_align(new_speeds->speed_position(orig_pos), 1);
+                                       new_label->position = new_track->from_units(label_pos);
+                               }
+                       }
+                       first_track = 0;
+               }
+               if( edit_plugins ) {
+                       int old_size = old_track->plugin_set.size();
+                       int new_size = new_track->plugin_set.size();
+                       int n = bmin(old_size, new_size);
+                       for( int i=0; i<n; ++i ) {
+                               PluginSet *old_plugin_set = old_track->plugin_set[i];
+                               if( !old_plugin_set ) continue;
+                               PluginSet *new_plugin_set = new_track->plugin_set[i];
+                               if( !new_plugin_set ) continue;
+                               Plugin *old_plugin = (Plugin *)old_plugin_set->first;
+                               for( ; old_plugin; old_plugin=(Plugin *)old_plugin->next ) {
+                                       Plugin *new_plugin = (Plugin *)new_plugin_set->get_edit(old_plugin->orig_id);
+                                       if( !new_plugin ) continue;
+                                       int64_t plugin_start = old_plugin->startproject;
+                                       int64_t plugin_end = plugin_start + old_plugin->length;
+                                       double orig_start = old_speeds->automation_integral(0, plugin_start, PLAY_FORWARD);
+                                       double orig_end   = old_speeds->automation_integral(0, plugin_end, PLAY_FORWARD);
+                                       plugin_start = new_track->frame_align(new_speeds->speed_position(orig_start), 1);
+                                       plugin_end = new_track->frame_align(new_speeds->speed_position(orig_end), 1);
+                                       new_plugin->startproject = plugin_start;
+                                       new_plugin->length = plugin_end - plugin_start;
+                                       if( edit_autos ) {
+                                               KeyFrames *old_keyframes = old_plugin->keyframes;
+                                               if( !old_keyframes ) continue;
+                                               KeyFrames *new_keyframes = new_plugin->keyframes;
+                                               if( !new_keyframes ) continue;
+                                               Auto *old_auto = old_keyframes->first;
+                                               for( ; old_auto; old_auto=old_auto->next ) {
+                                                       Auto *new_auto = new_keyframes->get_auto(old_auto->orig_id);
+                                                       if( !new_auto ) continue;
+                                                       int64_t auto_pos = old_auto->position;
+                                                       double orig_pos = old_speeds->automation_integral(0, auto_pos, PLAY_FORWARD);
+                                                       auto_pos = new_track->frame_align(new_speeds->speed_position(orig_pos), 1);
+                                                       new_auto->position = auto_pos;
+                                                       old_auto = old_auto->next;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               if( edit_autos ) { // speed must be last
+                       for( int i=0; i<AUTOMATION_SPEED; ++i ) {
+                               Autos *old_autos = old_track->automation->autos[i];
+                               if( !old_autos ) continue;
+                               Autos *new_autos = new_track->automation->autos[i];
+                               if( !new_autos ) continue;
+                               Auto *old_auto = old_autos->first;
+                               for( ; old_auto; old_auto=old_auto->next ) {
+                                       Auto *new_auto = new_autos->get_auto(old_auto->orig_id);
+                                       if( !new_auto ) continue;
+                                       int64_t auto_pos = old_auto->position;
+                                       double orig_pos = old_speeds->automation_integral(0, auto_pos, PLAY_FORWARD);
+                                       auto_pos = new_track->frame_align(new_speeds->speed_position(orig_pos), 1);
+                                       new_auto->position = auto_pos;
+                               }
+                       }
+               }
        }
        return result;
 }
@@ -2543,12 +2744,12 @@ void MWindow::speed_before()
        speed_edl->copy_all(edl);
 }
 
-int MWindow::speed_after(int done)
+int MWindow::speed_after(int done, int edit_speed)
 {
        int result = 0;
        if( speed_edl ) {
                if( done >= 0 )
-                       result = normalize_speed(speed_edl, edl);
+                       result = normalize_speed(speed_edl, edl, edit_speed);
                if( done != 0 ) {
                        speed_edl->remove_user();
                        speed_edl = 0;
@@ -2643,3 +2844,124 @@ void MWindow::paste_effects()
        group->remove_user();
 }
 
+void MWindow::align_timecodes()
+{
+       undo_before();
+       double offset = edl->tracks->align_timecodes();
+       set_timecode_offset(offset);
+       save_backup();
+       undo_after(_("align timecodes"), LOAD_ALL);
+       restart_brender();
+       cwindow->refresh_frame(CHANGE_EDL);
+       update_plugin_guis();
+}
+
+
+int MWindow::masters_to_mixers()
+{
+       Track *master_track = edl->tracks->first;
+       while( master_track && !master_track->master )
+               master_track = master_track->next;
+       while( master_track ) { // test for track/mixer conflicts
+               int failed = 0;
+               Track *mixer_last = master_track;
+               Track *track = master_track->next;
+               for( ; track && !track->master; track=track->next )
+                       mixer_last = track;
+               Track *next_track = track;
+               if( !master_track->armed ) {
+                       master_track = next_track;
+                       continue;
+               }
+               Mixer *master_mixer = 0;
+               for( int i=0, n=edl->mixers.size(); i<n; ++i ) {
+                       if( master_track->index_in(edl->mixers[i]) >= 0 ) {
+                               master_mixer = edl->mixers[i];
+                               break;
+                       }
+               }
+               if( master_mixer ) { // existing mixer track group
+                       for( track=master_track; !failed && track; track=track->next ) {
+                               if( track->index_in(master_mixer) < 0 ) {
+                                       eprintf("Mixer: %s missing track: %s",
+                                               master_mixer->title, track->title);
+                                       failed = 1;
+                               }
+                               if( track == mixer_last ) break;
+                       }
+                       for( int i=0, n=master_mixer->mixer_ids.size(); !failed && i<n; ++i ) {
+                               int mixer_id = master_mixer->mixer_ids[i], found = 0;
+                               for( track=master_track; track; track=track->next ) {
+                                       if( track->mixer_id == mixer_id ) {
+                                               found = 1;
+                                               break;
+                                       }
+                                       if( track == mixer_last ) break;
+                               }
+                               if( !found ) {
+                                       eprintf("Mixer: %s track missing: %s",
+                                               master_mixer->title, track->title);
+                                       failed = 1;
+                               }
+                       }
+               }
+               else { // create mixer
+                       for( track=master_track->next; !failed && track; track=track->next ) {
+                               for( int i=0, n=edl->mixers.size(); !failed && i<n; ++i ) {
+                                       Mixer *mixer = edl->mixers[i];
+                                       if( track->index_in(mixer) >= 0 ) {
+                                               eprintf("Track: %s already exists in mixer: %s",
+                                                       track->title, mixer->title);
+                                               failed = 1;
+                                               break;
+                                       }
+                               }
+                               if( track == mixer_last ) break;
+                       }
+                       if( !failed ) { // new mixer
+                               ZWindow *zwindow = get_mixer(master_mixer);
+                               zwindow->set_title(master_track->title);
+                               sprintf(master_track->title, _("Mixer %d"), zwindow->idx);
+                               for( track=master_track; track; track=track->next ) {
+                                       track->play = track->armed = 0;
+                                       master_mixer->mixer_ids.append(track->get_mixer_id());
+                                       if( track == mixer_last ) break;
+                               }
+                               zwindow->start();
+                       }
+               }
+               master_track = next_track;
+        }
+       return 0;
+}
+
+void MWindow::mix_masters()
+{
+       undo_before();
+       masters_to_mixers();
+       undo_after(_("mix masters"), LOAD_ALL);
+
+       restart_brender();
+       gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
+       gui->activate_timeline();
+       cwindow->refresh_frame(CHANGE_EDL);
+       save_backup();
+}
+
+void MWindow::create_keyframes(int mask, int mode)
+{
+       undo_before();
+       double start = edl->local_session->get_selectionstart();
+       edl->tracks->create_keyframes(start, mask, mode);
+       double end = edl->local_session->get_selectionend();
+       if( end != start )
+               edl->tracks->create_keyframes(end, mask, mode);
+       undo_after(_("create kyfrms"), LOAD_AUTOMATION);
+
+       restart_brender();
+       gui->update(1, NORMAL_DRAW, 0, 0, 1, 0, 0);
+       gui->activate_timeline();
+       cwindow->refresh_frame(CHANGE_EDL);
+       save_backup();
+}
+