fix crash when copy'ing latent plugins
[ardour.git] / gtk2_ardour / editor_ops.cc
index ccb77558b82344cff4310d9c5f8f6e5385960177..c208e551654e1ce44640f76ec907df676d06972c 100644 (file)
 #include "transform_dialog.h"
 #include "ui_config.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -151,7 +151,7 @@ Editor::redo (uint32_t n)
 }
 
 void
-Editor::split_regions_at (framepos_t where, RegionSelection& regions)
+Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int32_t sub_num)
 {
        bool frozen = false;
 
@@ -226,7 +226,7 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions)
 
                if (pl) {
                        pl->clear_changes ();
-                       pl->split_region ((*a)->region(), where);
+                       pl->split_region ((*a)->region(), where, sub_num);
                        _session->add_command (new StatefulDiffCommand (pl));
                }
 
@@ -433,6 +433,9 @@ Editor::nudge_forward (bool next, bool force_playhead)
                                        } else {
                                                loc->set_end (max_framepos);
                                        }
+                                       if (loc->is_session_range()) {
+                                               _session->set_end_is_free (false);
+                                       }
                                }
                                if (!in_command) {
                                        begin_reversible_command (_("nudge location forward"));
@@ -524,6 +527,9 @@ Editor::nudge_backward (bool next, bool force_playhead)
                                        } else {
                                                loc->set_end (loc->length());
                                        }
+                                       if (loc->is_session_range()) {
+                                               _session->set_end_is_free (false);
+                                       }
                                }
                                if (!in_command) {
                                        begin_reversible_command (_("nudge location forward"));
@@ -2233,6 +2239,8 @@ Editor::set_session_end_from_playhead ()
 
                commit_reversible_command ();
        }
+
+       _session->set_end_is_free (false);
 }
 
 void
@@ -4170,6 +4178,7 @@ Editor::cut_copy (CutCopyOp op)
        }
 }
 
+
 struct AutomationRecord {
        AutomationRecord () : state (0) , line(NULL) {}
        AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
@@ -4178,7 +4187,11 @@ struct AutomationRecord {
        const AutomationLine* line; ///< line this came from
        boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
 };
-
+struct PointsSelectionPositionSorter {
+       bool operator() (ControlPoint* a, ControlPoint* b) {
+               return (*(a->model()))->when < (*(b->model()))->when;
+       }
+};
 /** Cut, copy or clear selected automation points.
  *  @param op Operation (Cut, Copy or Clear)
  */
@@ -4196,9 +4209,12 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
        typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
        Lists lists;
 
+       /* user could select points in any order */
+       selection->points.sort(PointsSelectionPositionSorter ());
+       
        /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
-       for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
-               const AutomationLine&                   line = (*i)->line();
+       for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+               const AutomationLine&                   line = (*sel_point)->line();
                const boost::shared_ptr<AutomationList> al   = line.the_list();
                if (lists.find (al) == lists.end ()) {
                        /* We haven't seen this list yet, so make a record for it.  This includes
@@ -4218,17 +4234,17 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
 
                /* Add all selected points to the relevant copy ControlLists */
                framepos_t start = std::numeric_limits<framepos_t>::max();
-               for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
-                       boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
-                       AutomationList::const_iterator    j  = (*i)->model();
+               for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+                       boost::shared_ptr<AutomationList>    al = (*sel_point)->line().the_list();
+                       AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
 
-                       lists[al].copy->fast_simple_add ((*j)->when, (*j)->value);
+                       lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
                        if (midi) {
                                /* Update earliest MIDI start time in beats */
-                               earliest = std::min(earliest, Evoral::Beats((*j)->when));
+                               earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
                        } else {
                                /* Update earliest session start time in frames */
-                               start = std::min(start, (*i)->line().session_position(j));
+                               start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
                        }
                }
 
@@ -4251,12 +4267,13 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                           start time, so relative ordering between points is preserved
                           when copying from several lists and the paste starts at the
                           earliest copied piece of data. */
-                       for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
-                               (*j)->when -= line_offset;
+                       boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
+                       for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
+                               (*ctrl_evt)->when -= line_offset;
                        }
 
                        /* And add it to the cut buffer */
-                       cut_buffer->add (i->second.copy);
+                       cut_buffer->add (al_cpy);
                }
        }
 
@@ -4268,9 +4285,22 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                }
 
                /* Remove each selected point from its AutomationList */
-               for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
-                       boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
-                       al->erase ((*i)->model ());
+               for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+                       AutomationLine& line = (*sel_point)->line ();
+                       boost::shared_ptr<AutomationList> al = line.the_list();
+
+                       bool erase = true;
+                       
+                       if (dynamic_cast<AudioRegionGainLine*> (&line)) {
+                               /* removing of first and last gain point in region gain lines is prohibited*/
+                               if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
+                                       erase = false;
+                               }
+                       }
+
+                       if(erase) {
+                               al->erase ((*sel_point)->model ());
+                       }
                }
 
                /* Thaw the lists and add undo records for them */
@@ -4635,7 +4665,7 @@ Editor::paste (float times, bool from_context)
 {
         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
 
-       paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times);
+       paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
 }
 
 void
@@ -4649,11 +4679,11 @@ Editor::mouse_paste ()
        }
 
        snap_to (where);
-       paste_internal (where, 1);
+       paste_internal (where, 1, get_grid_music_divisions (0));
 }
 
 void
-Editor::paste_internal (framepos_t position, float times)
+Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
 {
         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
 
@@ -4746,7 +4776,7 @@ Editor::paste_internal (framepos_t position, float times)
               "greedy" paste from one automation type to another. */
 
            PasteContext ctx(paste_count, times, ItemCounts(), true);
-           ts.front()->paste (position, *cut_buffer, ctx);
+           ts.front()->paste (position, *cut_buffer, ctx, sub_num);
 
        } else {
 
@@ -4754,7 +4784,7 @@ Editor::paste_internal (framepos_t position, float times)
 
                PasteContext ctx(paste_count, times, ItemCounts(), false);
                for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
-                       (*i)->paste (position, *cut_buffer, ctx);
+                       (*i)->paste (position, *cut_buffer, ctx, sub_num);
                }
        }
 
@@ -6151,7 +6181,11 @@ Editor::split_region ()
                        return;
                }
 
-               split_regions_at (where, rs);
+               if (snap_musical()) {
+                       split_regions_at (where, rs, get_grid_music_divisions (0));
+               } else {
+                       split_regions_at (where, rs, 0);
+               }
        }
 }
 
@@ -6168,6 +6202,7 @@ Editor::select_next_route()
        RouteUI *rui;
        do {
                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+
                        if (*i == current) {
                                ++i;
                                if (i != track_views.end()) {
@@ -6179,10 +6214,12 @@ Editor::select_next_route()
                                break;
                        }
                }
+
                rui = dynamic_cast<RouteUI *>(current);
-       } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
 
-       selection->set(current);
+       } while (current->hidden() || (rui == NULL) || !rui->route()->active());
+
+       selection->set (current);
 
        ensure_time_axis_view_is_visible (*current, false);
 }
@@ -6200,6 +6237,7 @@ Editor::select_prev_route()
        RouteUI *rui;
        do {
                for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
+
                        if (*i == current) {
                                ++i;
                                if (i != track_views.rend()) {
@@ -6211,7 +6249,8 @@ Editor::select_prev_route()
                        }
                }
                rui = dynamic_cast<RouteUI *>(current);
-       } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
+
+       } while (current->hidden() || (rui == NULL) || !rui->route()->active());
 
        selection->set (current);
 
@@ -6278,11 +6317,11 @@ Editor::set_session_extents_from_selection ()
 
        Location* loc;
        if ((loc = _session->locations()->session_range_location()) == 0) {
-               _session->set_session_extents ( start, end );  // this will create a new session range;  no need for UNDO
+               _session->set_session_extents (start, end);  // this will create a new session range;  no need for UNDO
        } else {
                XMLNode &before = loc->get_state();
 
-               _session->set_session_extents ( start, end );
+               _session->set_session_extents (start, end);
 
                XMLNode &after = loc->get_state();
 
@@ -6292,6 +6331,8 @@ Editor::set_session_extents_from_selection ()
 
                commit_reversible_command ();
        }
+
+       _session->set_end_is_free (false);
 }
 
 void
@@ -6548,7 +6589,8 @@ Editor::define_one_bar (framepos_t start, framepos_t end)
        } else if (t.frame() == start) {
                _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
        } else {
-               _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), 0.0, start, TempoSection::Constant, AudioTime);
+               const Tempo tempo (beats_per_minute, t.note_type());
+               _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
        }
 
        XMLNode& after (_session->tempo_map().get_state());
@@ -7289,7 +7331,8 @@ Editor::insert_time (
                        (*i)->clear_owned_changes ();
 
                        if (opt == SplitIntersected) {
-                               (*i)->split (pos);
+                               /* non musical split */
+                               (*i)->split (pos, 0);
                        }
 
                        (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
@@ -7888,7 +7931,7 @@ Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total,
 {
        Timers::TimerSuspender t;
        label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
-       Gtkmm2ext::UI::instance()->flush_pending ();
+       Gtkmm2ext::UI::instance()->flush_pending (1);
 }
 
 void
@@ -7908,7 +7951,7 @@ Editor::bring_all_sources_into_session ()
         */
 
        Timers::TimerSuspender t;
-       Gtkmm2ext::UI::instance()->flush_pending ();
+       Gtkmm2ext::UI::instance()->flush_pending (3);
 
        cerr << " Do it\n";