Fix session-open after selecting new, template, then back
[ardour.git] / gtk2_ardour / editor_drag.cc
index 1538457f7ebb6347eeab71227ca145b6f90541b5..02f827b5792874ffada6eb70dc335899c2aa514e 100644 (file)
@@ -40,6 +40,7 @@
 #include "ardour/profile.h"
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
+#include "ardour/session_playlists.h"
 
 #include "canvas/canvas.h"
 #include "canvas/scroll_group.h"
@@ -545,9 +546,11 @@ Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
        return boost::shared_ptr<Region>();
 }
 
-struct PresentationInfoTimeAxisViewSorter {
-       bool operator() (TimeAxisView* a, TimeAxisView* b) {
-               return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
+struct TimeAxisViewStripableSorter {
+       bool operator() (TimeAxisView* tav_a, TimeAxisView* tav_b) {
+               boost::shared_ptr<ARDOUR::Stripable> const& a = tav_a->stripable ();
+               boost::shared_ptr<ARDOUR::Stripable> const& b = tav_b->stripable ();
+               return ARDOUR::Stripable::Sorter () (a, b);
        }
 };
 
@@ -563,7 +566,7 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<Re
        */
 
        TrackViewList track_views = _editor->track_views;
-       track_views.sort (PresentationInfoTimeAxisViewSorter ());
+       track_views.sort (TimeAxisViewStripableSorter ());
 
        for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
                _time_axis_views.push_back (*i);
@@ -662,6 +665,11 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, MusicFrame* pending_r
        /* compute the amount of pointer motion in frames, and where
           the region would be if we moved it by that much.
        */
+       if (_x_constrained) {
+               *pending_region_position = _last_position;
+               return 0.0;
+       }
+
        *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
 
        framecnt_t sync_offset;
@@ -1437,7 +1445,7 @@ RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region,
                                output_chan =  _editor->session()->master_out()->n_inputs().n_audio();
                        }
                        audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
-                       tav =_editor->axis_view_from_stripable (audio_tracks.front());
+                       tav =_editor->time_axis_view_from_stripable (audio_tracks.front());
                } else {
                        ChanCount one_midi_port (DataType::MIDI, 1);
                        list<boost::shared_ptr<MidiTrack> > midi_tracks;
@@ -1446,7 +1454,7 @@ RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region,
                                                                          boost::shared_ptr<ARDOUR::PluginInfo>(),
                                                                          (ARDOUR::Plugin::PresetRecord*) 0,
                                                                          (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
-                       tav = _editor->axis_view_from_stripable (midi_tracks.front());
+                       tav = _editor->time_axis_view_from_stripable (midi_tracks.front());
                }
 
                if (tav) {
@@ -1586,7 +1594,7 @@ RegionMoveDrag::finished_no_copy (
        PlaylistSet frozen_playlists;
        set<RouteTimeAxisView*> views_to_update;
        RouteTimeAxisView* new_time_axis_view = 0;
-       framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
+       framecnt_t const drag_delta = _primary->region()->position() - last_position.frame;
 
        typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
        PlaylistMapping playlist_mapping;
@@ -2420,14 +2428,16 @@ RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisVi
 void
 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
 {
+
        if (first_move) {
                _editor->begin_reversible_command (_("create region"));
                _region = add_midi_region (_view, false);
                _view->playlist()->freeze ();
        } else {
+
                if (_region) {
                        framepos_t const f = adjusted_current_frame (event);
-                       if (f < grab_frame()) {
+                       if (f <= grab_frame()) {
                                _region->set_initial_position (f);
                        }
 
@@ -2437,9 +2447,10 @@ RegionCreateDrag::motion (GdkEvent* event, bool first_move)
                           a bit confusing as if a region starts 1 frame after a snap point, one cannot
                           place snapped notes at the start of the region.
                        */
-
-                       framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
-                       _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
+                       if (f != grab_frame()) {
+                               framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
+                               _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
+                       }
                }
        }
 }
@@ -2988,6 +2999,24 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                        if (insert_result.second) {
                                pl->freeze();
                        }
+
+                       MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (rv);
+                       /* a MRV start trim may change the source length. ensure we cover all playlists here */
+                       if (mrv && _operation == StartTrim) {
+                               vector<boost::shared_ptr<Playlist> > all_playlists;
+                               _editor->session()->playlists->get (all_playlists);
+                               for (vector<boost::shared_ptr<Playlist> >::iterator x = all_playlists.begin(); x != all_playlists.end(); ++x) {
+
+                                       if ((*x)->uses_source (rv->region()->source(0))) {
+                                               insert_result = _editor->motion_frozen_playlists.insert (*x);
+                                               if (insert_result.second) {
+                                                       (*x)->clear_owned_changes ();
+                                                       (*x)->freeze();
+                                               }
+
+                                       }
+                               }
+                       }
                }
        }
 
@@ -3136,29 +3165,22 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                if (!_editor->selection->selected (_primary)) {
                        _primary->thaw_after_trim ();
                } else {
-
-                       set<boost::shared_ptr<Playlist> > diffed_playlists;
-
                        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                                i->view->thaw_after_trim ();
                                i->view->enable_display (true);
-
-                               /* Trimming one region may affect others on the playlist, so we need
-                                  to get undo Commands from the whole playlist rather than just the
-                                  region.  Use diffed_playlists to make sure we don't diff a given
-                                  playlist more than once.
-                               */
-                               boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
-                               if (diffed_playlists.find (p) == diffed_playlists.end()) {
-                                       vector<Command*> cmds;
-                                       p->rdiff (cmds);
-                                       _editor->session()->add_commands (cmds);
-                                       diffed_playlists.insert (p);
-                               }
                        }
                }
 
                for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
+                       /* Trimming one region may affect others on the playlist, so we need
+                          to get undo Commands from the whole playlist rather than just the
+                          region.  Use motion_frozen_playlists (a set) to make sure we don't
+                          diff a given playlist more than once.
+                       */
+
+                       vector<Command*> cmds;
+                       (*p)->rdiff (cmds);
+                       _editor->session()->add_commands (cmds);
                        (*p)->thaw ();
                }
 
@@ -3286,10 +3308,9 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
                        } else {
                                --bbt.bars;
                        }
-                       const double beat = map.beat_at_bbt (bbt);
-                       const framepos_t frame = map.frame_at_beat (beat);
+                       const framepos_t frame = map.frame_at_bbt (bbt);
                        _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
-                                                      , beat, bbt, frame, _real_section->position_lock_style());
+                                                      , bbt, frame, _real_section->position_lock_style());
                        if (!_real_section) {
                                aborted (true);
                                return;
@@ -3526,6 +3547,7 @@ BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
        , _grab_qn (0.0)
        , _tempo (0)
        , _before_state (0)
+       , _drag_valid (true)
 {
        DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
 
@@ -3537,13 +3559,18 @@ BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        Drag::start_grab (event, cursor);
        TempoMap& map (_editor->session()->tempo_map());
        _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
+
+       if (adjusted_current_frame (event, false) <= _tempo->frame()) {
+               _drag_valid = false;
+               return;
+       }
+
        _editor->tempo_curve_selected (_tempo, true);
 
        ostringstream sstr;
        if (_tempo->clamped()) {
                TempoSection* prev = map.previous_tempo_section (_tempo);
                if (prev) {
-                       _editor->tempo_curve_selected (prev, true);
                        sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
                }
        }
@@ -3582,12 +3609,15 @@ BBTRulerDrag::setup_pointer_frame_offset ()
 void
 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
 {
-       TempoMap& map (_editor->session()->tempo_map());
+       if (!_drag_valid) {
+               return;
+       }
 
        if (first_move) {
                _editor->begin_reversible_command (_("stretch tempo"));
        }
 
+       TempoMap& map (_editor->session()->tempo_map());
        framepos_t pf;
 
        if (_editor->snap_musical()) {
@@ -3598,7 +3628,7 @@ BBTRulerDrag::motion (GdkEvent* event, bool first_move)
 
        if (ArdourKeyboard::indicates_constraint (event->button.state)) {
                /* adjust previous tempo to match pointer frame */
-               _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
+               _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf, _grab_qn, map.quarter_note_at_frame (pf));
        }
 
        ostringstream sstr;
@@ -3622,17 +3652,22 @@ BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
 
        TempoMap& map (_editor->session()->tempo_map());
 
-       XMLNode &after = map.get_state();
-       _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
-       _editor->commit_reversible_command ();
        _editor->tempo_curve_selected (_tempo, false);
-
        if (_tempo->clamped()) {
                TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
                if (prev_tempo) {
                        _editor->tempo_curve_selected (prev_tempo, false);
                }
        }
+
+       if (!movement_occurred || !_drag_valid) {
+               return;
+       }
+
+       XMLNode &after = map.get_state();
+       _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
+       _editor->commit_reversible_command ();
+
 }
 
 void
@@ -3665,6 +3700,11 @@ TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        _before_state = &map.get_state();
        _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
 
+       if (_tempo->locked_to_meter()) {
+               _drag_valid = false;
+               return;
+       }
+
        _next_tempo = map.next_tempo_section (_tempo);
        if (_next_tempo) {
                if (!map.next_tempo_section (_next_tempo)) {
@@ -3748,8 +3788,6 @@ TempoTwistDrag::motion (GdkEvent* event, bool first_move)
 void
 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       TempoMap& map (_editor->session()->tempo_map());
-
        if (!movement_occurred || !_drag_valid) {
                return;
        }
@@ -3757,6 +3795,7 @@ TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
        _editor->tempo_curve_selected (_tempo, false);
        _editor->tempo_curve_selected (_next_tempo, false);
 
+       TempoMap& map (_editor->session()->tempo_map());
        XMLNode &after = map.get_state();
        _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
        _editor->commit_reversible_command ();
@@ -3775,6 +3814,7 @@ TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
        , _grab_qn (0.0)
        , _tempo (0)
        , _before_state (0)
+       , _drag_valid (true)
 {
        DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
        TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
@@ -3791,6 +3831,10 @@ TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        /* get current state */
        _before_state = &tmap.get_state();
 
+       if (_tempo->locked_to_meter()) {
+               _drag_valid = false;
+               return;
+       }
 
        ostringstream sstr;
 
@@ -3820,14 +3864,16 @@ TempoEndDrag::setup_pointer_frame_offset ()
 void
 TempoEndDrag::motion (GdkEvent* event, bool first_move)
 {
+       if (!_drag_valid) {
+               return;
+       }
+
        TempoMap& map (_editor->session()->tempo_map());
 
        if (first_move) {
                _editor->begin_reversible_command (_("stretch end tempo"));
        }
 
-
-
        framepos_t const pf = adjusted_current_frame (event, false);
        map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
 
@@ -3844,7 +3890,7 @@ TempoEndDrag::motion (GdkEvent* event, bool first_move)
 void
 TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (!movement_occurred) {
+       if (!movement_occurred || !_drag_valid) {
                return;
        }
 
@@ -3942,7 +3988,7 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
                }
 
 
-               if (AudioEngine::instance()->connected()) {
+               if (AudioEngine::instance()->running()) {
 
                        /* do this only if we're the engine is connected
                         * because otherwise this request will never be
@@ -3952,13 +3998,15 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
                         */
 
                        s->request_suspend_timecode_transmission ();
-                       while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
+                       while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
                                /* twiddle our thumbs */
                        }
                }
        }
 
        fake_locate (where.frame - snap_delta (event->button.state));
+       
+       _last_y_delta = 0;
 }
 
 void
@@ -3971,6 +4019,39 @@ CursorDrag::motion (GdkEvent* event, bool)
        if (where.frame != last_pointer_frame()) {
                fake_locate (where.frame - snap_delta (event->button.state));
        }
+       
+       //maybe do zooming, too, if the option is enabled
+       if (UIConfiguration::instance ().get_use_time_rulers_to_zoom_with_vertical_drag () ) {
+       
+               //To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
+               //we use screen coordinates for this, not canvas-based grab_x
+               double mx = event->button.x;
+               double dx = fabs(mx - _last_mx);
+               double my = event->button.y;
+               double dy = fabs(my - _last_my);
+
+               {
+                       //do zooming in windowed "steps" so it feels more reversible ?
+                       const int stepsize = 2;  //stepsize ==1  means "trigger on every pixel of movement"
+                       int y_delta = grab_y() - current_pointer_y();
+                       y_delta = y_delta / stepsize;
+
+                       //if all requirements are met, do the actual zoom
+                       const double scale = 1.2;
+                       if ( (dy>dx) && (_last_dx ==0) && (y_delta != _last_y_delta) ) {
+                               if ( _last_y_delta > y_delta ) {
+                                       _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
+                               } else {
+                                       _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
+                               }
+                               _last_y_delta = y_delta;
+                       }
+               }
+
+               _last_my = my;
+               _last_mx = mx;
+               _last_dx = dx;
+       }
 }
 
 void
@@ -4661,7 +4742,7 @@ MarkerDrag::aborted (bool movement_occurred)
 void
 MarkerDrag::update_item (Location*)
 {
-        /* noop */
+       /* noop */
 }
 
 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
@@ -4763,10 +4844,9 @@ ControlPointDrag::motion (GdkEvent* event, bool first_motion)
                _editor->begin_reversible_command (_("automation event move"));
                _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
        }
-       pair<double, float> result;
+       pair<float, float> result;
        result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
-
-       show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
+       show_verbose_cursor_text (_point->line().get_verbose_cursor_relative_string (result.first, result.second));
 }
 
 void
@@ -4883,10 +4963,10 @@ LineDrag::motion (GdkEvent* event, bool first_move)
        }
 
        /* we are ignoring x position for this drag, so we can just pass in anything */
-       pair<double, float> result;
+       pair<float, float> result;
 
        result = _line->drag_motion (0, fraction, true, false, ignored);
-       show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
+       show_verbose_cursor_text (_line->get_verbose_cursor_relative_string (result.first, result.second));
 }
 
 void
@@ -5825,7 +5905,7 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
                case CreateSkipMarker:
                case CreateRangeMarker:
                case CreateCDMarker:
-                   {
+               {
                        XMLNode &before = _editor->session()->locations()->get_state();
                        if (_operation == CreateSkipMarker) {
                                _editor->begin_reversible_command (_("new skip marker"));
@@ -5852,7 +5932,7 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
                        _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
                        _editor->commit_reversible_command ();
                        break;
-                   }
+               }
 
                case CreateTransportMarker:
                        // popup menu to pick loop or punch
@@ -6374,10 +6454,10 @@ AutomationRangeDrag::motion (GdkEvent*, bool first_move)
        for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
                float const f = y_fraction (l->line, current_pointer_y());
                /* we are ignoring x position for this drag, so we can just pass in anything */
-               pair<double, float> result;
+               pair<float, float> result;
                uint32_t ignored;
                result = l->line->drag_motion (0, f, true, false, ignored);
-               show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
+               show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (result.first, result.second));
        }
 }
 
@@ -6670,7 +6750,7 @@ NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
        framepos_t const start = min (_note[0], _note[1]);
        framepos_t const start_sess_rel = start + _region_view->region()->position();
        framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
-       framecnt_t const g = grid_frames (start);
+       framecnt_t const g = grid_frames (start_sess_rel);
 
        if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
                length = g;
@@ -6944,56 +7024,3 @@ void
 RegionCutDrag::aborted (bool)
 {
 }
-
-RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
-       : Drag (e, item, true)
-{
-}
-
-void
-RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
-{
-       Drag::start_grab (event, c);
-
-       framepos_t where = _editor->canvas_event_sample(event);
-
-       _editor->_dragging_playhead = true;
-
-       _editor->playhead_cursor->set_position (where);
-}
-
-void
-RulerZoomDrag::motion (GdkEvent* event, bool)
-{
-       framepos_t where = _editor->canvas_event_sample(event);
-
-       _editor->playhead_cursor->set_position (where);
-
-       const double movement_limit = 20.0;
-       const double scale = 1.08;
-       const double y_delta = last_pointer_y() - current_pointer_y();
-
-       if (y_delta > 0 && y_delta < movement_limit) {
-               _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
-       } else if (y_delta < 0 && y_delta > -movement_limit) {
-               _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
-       }
-}
-
-void
-RulerZoomDrag::finished (GdkEvent*, bool)
-{
-       _editor->_dragging_playhead = false;
-
-       Session* s = _editor->session ();
-       if (s) {
-               s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
-               _editor->_pending_locate_request = true;
-       }
-
-}
-
-void
-RulerZoomDrag::aborted (bool)
-{
-}