Remove dead code.
[ardour.git] / gtk2_ardour / editor_ops.cc
index b3b6b0bb452305f22c06fa68d47e7e452659ab07..f0de126c6735046a66bfa37be53d424fe17dd106 100644 (file)
@@ -24,6 +24,7 @@
 #include <cstdlib>
 #include <cmath>
 #include <string>
+#include <limits>
 #include <map>
 #include <set>
 
@@ -63,6 +64,7 @@
 #include "audio_region_view.h"
 #include "audio_streamview.h"
 #include "audio_time_axis.h"
+#include "automation_region_view.h"
 #include "automation_time_axis.h"
 #include "control_point.h"
 #include "debug.h"
@@ -75,6 +77,7 @@
 #include "gui_thread.h"
 #include "insert_time_dialog.h"
 #include "interthread_progress_window.h"
+#include "item_counts.h"
 #include "keyboard.h"
 #include "midi_region_view.h"
 #include "mixer_strip.h"
@@ -646,7 +649,7 @@ Editor::build_region_boundary_cache ()
                break;
        default:
                fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
-               /*NOTREACHED*/
+               abort(); /*NOTREACHED*/
                return;
        }
 
@@ -1367,7 +1370,7 @@ Editor::scroll_tracks_up_line ()
 bool
 Editor::scroll_down_one_track ()
 {
-       TrackViewList::reverse_iterator next = track_views.rbegin();
+       TrackViewList::reverse_iterator next = track_views.rend();
        std::pair<TimeAxisView*,double> res;
        const double top_of_trackviews = vertical_adjustment.get_value();
 
@@ -1376,10 +1379,6 @@ Editor::scroll_down_one_track ()
                        continue;
                }
 
-               next = t;
-               if (next != track_views.rbegin()) {
-                       --next; // moves "next" towards the lower/later tracks since it is a reverse iterator
-               }
 
                /* If this is the upper-most visible trackview, we want to display
                   the one above it (next)
@@ -1390,11 +1389,12 @@ Editor::scroll_down_one_track ()
                if (res.first) {
                        break;
                }
+               next = t;
        }
 
        /* move to the track below the first one that covers the */
        
-       if (next != track_views.rbegin()) {
+       if (next != track_views.rend()) {
                ensure_time_axis_view_is_visible (**next, true);
                return true;
        }
@@ -1533,7 +1533,7 @@ Editor::temporal_zoom (framecnt_t fpp)
        // would be 4147200000 samples, so 2592000 samples per pixel.
 
        nfpp = min (fpp, (framecnt_t) 2592000);
-       nfpp = max ((framecnt_t) 1, fpp);
+       nfpp = max ((framecnt_t) 1, nfpp);
 
        new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
        half_page_size = new_page_size / 2;
@@ -3846,26 +3846,8 @@ Editor::cut_copy (CutCopyOp op)
 
        bool did_edit = false;
 
-       if (!selection->points.empty()) {
-               begin_reversible_command (opname + _(" points"));
-               did_edit = true;
-               cut_copy_points (op);
-               if (op == Cut || op == Delete) {
-                       selection->clear_points ();
-               }
-       } else if (!selection->regions.empty() || !selection->points.empty()) {
-
-               string thing_name;
-
-               if (selection->regions.empty()) {
-                       thing_name = _("points");
-               } else if (selection->points.empty()) {
-                       thing_name = _("regions");
-               } else {
-                       thing_name = _("objects");
-               }
-       
-               begin_reversible_command (opname + ' ' + thing_name);
+       if (!selection->regions.empty() || !selection->points.empty()) {
+               begin_reversible_command (opname + ' ' + _("objects"));
                did_edit = true;
 
                if (!selection->regions.empty()) {
@@ -3892,7 +3874,7 @@ Editor::cut_copy (CutCopyOp op)
                        selection->set (start, end);
                }
        } else if (!selection->time.empty()) {
-               begin_reversible_command (opname + _(" range"));
+               begin_reversible_command (opname + ' ' + _("range"));
 
                did_edit = true;
                cut_copy_ranges (op);
@@ -3903,6 +3885,9 @@ Editor::cut_copy (CutCopyOp op)
        }
        
        if (did_edit) {
+               /* reset repeated paste state */
+               paste_count    = 0;
+               last_paste_pos = 0;
                commit_reversible_command ();   
        }
        
@@ -3912,10 +3897,11 @@ Editor::cut_copy (CutCopyOp op)
 }
 
 struct AutomationRecord {
-       AutomationRecord () : state (0) {}
-       AutomationRecord (XMLNode* s) : state (s) {}
+       AutomationRecord () : state (0) , line(NULL) {}
+       AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
        
        XMLNode* state; ///< state before any operation
+       const AutomationLine* line; ///< line this came from
        boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
 };
 
@@ -3938,12 +3924,13 @@ Editor::cut_copy_points (CutCopyOp op)
 
        /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
        for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
-               boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
+               const AutomationLine&                   line = (*i)->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
                           taking a copy of its current state, in case this is needed for undo later.
                        */
-                       lists[al] = AutomationRecord (&al->get_state ());
+                       lists[al] = AutomationRecord (&al->get_state (), &line);
                }
        }
 
@@ -3951,22 +3938,33 @@ Editor::cut_copy_points (CutCopyOp op)
                /* This operation will involve putting things in the cut buffer, so create an empty
                   ControlList for each of our source lists to put the cut buffer data in.
                */
+               framepos_t start = std::numeric_limits<framepos_t>::max();
                for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
                        i->second.copy = i->first->create (i->first->parameter ());
+
+                       /* Calculate earliest start position of any point in selection. */
+                       start = std::min(start, i->second.line->session_position(i->first->begin()));
                }
 
                /* Add all selected points to the relevant copy ControlLists */
                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 ();
-                       lists[al].copy->add ((*j)->when, (*j)->value);
+                       lists[al].copy->fast_simple_add ((*j)->when, (*j)->value);
                }
 
+               /* Snap start time backwards, so copy/paste is snap aligned. */
+               snap_to(start, RoundDownMaybe);
+
                for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
-                       /* Correct this copy list so that it starts at time 0 */
-                       double const start = i->second.copy->front()->when;
+                       /* Correct this copy list so that it is relative to the earliest
+                          start time, so relative ordering between points is preserved
+                          when copying from several lists. */
+                       const AutomationLine* line        = i->second.line;
+                       const double          line_offset = line->time_converter().from(start);
+
                        for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
-                               (*j)->when -= start;
+                               (*j)->when -= line_offset;
                        }
 
                        /* And add it to the cut buffer */
@@ -4003,12 +4001,22 @@ void
 Editor::cut_copy_midi (CutCopyOp op)
 {
        for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
-               MidiRegionView* mrv = *i;
-               mrv->cut_copy_clear (op);
-       }
-}
+               MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
+               if (mrv) {
+                       mrv->cut_copy_clear (op);
 
+                       /* XXX: not ideal, as there may be more than one track involved in the selection */
+                       _last_cut_copy_source_track = &mrv->get_time_axis_view();
+               }
+       }
 
+       if (!selection->points.empty()) {
+               cut_copy_points (op);
+               if (op == Cut || op == Delete) {
+                       selection->clear_points ();
+               }
+       }
+}
 
 struct lt_playlist {
     bool operator () (const PlaylistState& a, const PlaylistState& b) {
@@ -4349,14 +4357,8 @@ Editor::paste_internal (framepos_t position, float times)
 {
         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
 
-       if (internal_editing()) {
-               if (cut_buffer->midi_notes.empty()) {
-                       return;
-               }
-       } else {
-               if (cut_buffer->empty()) {
-                       return;
-               }
+       if (cut_buffer->empty(internal_editing())) {
+               return;
        }
 
        if (position == max_framepos) {
@@ -4364,45 +4366,90 @@ Editor::paste_internal (framepos_t position, float times)
                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
        }
 
-       TrackViewList ts;
-       TrackViewList::iterator i;
-       size_t nth;
+       if (position == last_paste_pos) {
+               /* repeated paste in the same position */
+               ++paste_count;
+       } else {
+               /* paste in new location, reset repeated paste state */
+               paste_count = 0;
+               last_paste_pos = position;
+       }
 
        /* get everything in the correct order */
 
-       if (_edit_point == Editing::EditAtMouse && entered_track) {
-               /* With the mouse edit point, paste onto the track under the mouse */
-               ts.push_back (entered_track);
-       } else if (!selection->tracks.empty()) {
-               /* Otherwise, if there are some selected tracks, paste to them */
+       TrackViewList ts;
+       if (!selection->tracks.empty()) {
+               /* If there is a track selection, paste into exactly those tracks and
+                  only those tracks.  This allows the user to be explicit and override
+                  the below "do the reasonable thing" logic. */
                ts = selection->tracks.filter_to_unique_playlists ();
                sort_track_selection (ts);
-       } else if (_last_cut_copy_source_track) {
-               /* Otherwise paste to the track that the cut/copy came from;
-                  see discussion in mantis #3333.
-               */
-               ts.push_back (_last_cut_copy_source_track);
+       } else {
+               /* Figure out which track to base the paste at. */
+               TimeAxisView* base_track = NULL;
+               if (_edit_point == Editing::EditAtMouse && entered_track) {
+                       /* With the mouse edit point, paste onto the track under the mouse. */
+                       base_track = entered_track;
+               } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
+                       /* With the mouse edit point, paste onto the track of the region under the mouse. */
+                       base_track = &entered_regionview->get_time_axis_view();
+               } else if (_last_cut_copy_source_track) {
+                       /* Paste to the track that the cut/copy came from (see mantis #333). */
+                       base_track = _last_cut_copy_source_track;
+               } else {
+                       /* This is "impossible" since we've copied... well, do nothing. */
+                       return;
+               }
+
+               /* Walk up to parent if necessary, so base track is a route. */
+               while (base_track->get_parent()) {
+                       base_track = base_track->get_parent();
+               }
+
+               /* Add base track and all tracks below it.  The paste logic will select
+                  the appropriate object types from the cut buffer in relative order. */
+               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+                       if ((*i)->order() >= base_track->order()) {
+                               ts.push_back(*i);
+                       }
+               }
+
+               /* Sort tracks so the nth track of type T will pick the nth object of type T. */
+               sort_track_selection (ts);
+
+               /* Add automation children of each track in order, for pasting several lines. */
+               for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
+                       /* Add any automation children for pasting several lines */
+                       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
+                       if (!rtv) {
+                               continue;
+                       }
+
+                       typedef RouteTimeAxisView::AutomationTracks ATracks;
+                       const ATracks& atracks = rtv->automation_tracks();
+                       for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
+                               i = ts.insert(i, a->second.get());
+                               ++i;
+                       }
+               }
+
+               /* We now have a list of trackviews starting at base_track, including
+                  automation children, in the order shown in the editor, e.g. R1,
+                  R1.A1, R1.A2, R2, R2.A1, ... */
        }
 
        if (internal_editing ()) {
 
                /* undo/redo is handled by individual tracks/regions */
 
-               for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
+               RegionSelection rs;
+               get_regions_at (rs, position, ts);
 
-                       RegionSelection rs;
-                       RegionSelection::iterator r;
-                       MidiNoteSelection::iterator cb;
-
-                       get_regions_at (rs, position, ts);
-
-                       for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
-                            cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
-                               MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
-                               if (mrv) {
-                                       mrv->paste (position, times, **cb);
-                                       ++cb;
-                               }
+               ItemCounts counts;
+               for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
+                       MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
+                       if (mrv) {
+                               mrv->paste (position, paste_count, times, *cut_buffer, counts);
                        }
                }
 
@@ -4412,8 +4459,9 @@ Editor::paste_internal (framepos_t position, float times)
 
                begin_reversible_command (Operations::paste);
 
-               for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
-                       (*i)->paste (position, times, *cut_buffer, nth);
+               ItemCounts counts;
+               for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
+                       (*i)->paste (position, paste_count, times, *cut_buffer, counts);
                }
 
                commit_reversible_command ();
@@ -4801,8 +4849,8 @@ Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv
        vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
        v.push_back (selected);
 
-       framepos_t pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
-       double     pos_beats  = _session->tempo_map().framewalk_to_beats(0, pos_frames);
+       framepos_t          pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
+       Evoral::MusicalTime pos_beats  = _session->tempo_map().framewalk_to_beats(0, pos_frames);
 
        return op (mrv.midi_region()->model(), pos_beats, v);
 }
@@ -4915,7 +4963,7 @@ Editor::quantize_region ()
        qd->hide ();
 
        if (r == Gtk::RESPONSE_OK) {
-               Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
+               Quantize quant (qd->snap_start(), qd->snap_end(),
                                qd->start_grid_size(), qd->end_grid_size(),
                                qd->strength(), qd->swing(), qd->threshold());
 
@@ -4940,7 +4988,7 @@ Editor::insert_patch_change (bool from_context)
        */
        MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
 
-       Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
+       Evoral::PatchChange<Evoral::MusicalTime> empty (Evoral::MusicalTime(), 0, 0, 0);
         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
 
        if (d.run() == RESPONSE_CANCEL) {
@@ -6454,7 +6502,7 @@ Editor::playhead_forward_to_grid ()
        framepos_t pos = playhead_cursor->current_frame ();
        if (pos < max_framepos - 1) {
                pos += 2;
-               snap_to_internal (pos, 1, false);
+               snap_to_internal (pos, RoundUpAlways, false);
                _session->request_locate (pos);
        }
 }
@@ -6470,7 +6518,7 @@ Editor::playhead_backward_to_grid ()
        framepos_t pos = playhead_cursor->current_frame ();
        if (pos > 2) {
                pos -= 2;
-               snap_to_internal (pos, -1, false);
+               snap_to_internal (pos, RoundDownAlways, false);
                _session->request_locate (pos);
        }
 }