X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=inline;f=gtk2_ardour%2Feditor_drag.cc;h=f7b75a5855dee56e32f4991e194bba91b26ccebb;hb=238ca58c937f0d265de7a2a40b1e019bab35e11d;hp=d0f9f260ee3e8801b72729ac7c4041fb53380ce6;hpb=9242ca7a64cc1f418a7316afa16fea639f724dba;p=ardour.git diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index d0f9f260ee..f7b75a5855 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -44,7 +44,7 @@ #include "canvas/scroll_group.h" #include "editor.h" -#include "i18n.h" +#include "pbd/i18n.h" #include "keyboard.h" #include "audio_region_view.h" #include "automation_region_view.h" @@ -64,6 +64,7 @@ #include "mouse_cursors.h" #include "note_base.h" #include "patch_change.h" +#include "ui_config.h" #include "verbose_cursor.h" using namespace std; @@ -81,7 +82,10 @@ double ControlPointDrag::_zero_gain_fraction = -1.0; DragManager::DragManager (Editor* e) : _editor (e) , _ending (false) + , _current_pointer_x (0.0) + , _current_pointer_y (0.0) , _current_pointer_frame (0) + , _old_follow_playhead (false) { } @@ -96,8 +100,6 @@ DragManager::abort () { _ending = true; - cerr << "Aborting drag\n"; - for (list::const_iterator i = _drags.begin(); i != _drags.end(); ++i) { (*i)->abort (); delete *i; @@ -216,16 +218,26 @@ DragManager::have_item (ArdourCanvas::Item* i) const Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only) : _editor (e) + , _drags (0) , _item (i) , _pointer_frame_offset (0) + , _x_constrained (false) + , _y_constrained (false) + , _was_rolling (false) , _trackview_only (trackview_only) , _move_threshold_passed (false) , _starting_point_passed (false) , _initially_vertical (false) , _was_double_click (false) + , _grab_x (0.0) + , _grab_y (0.0) + , _last_pointer_x (0.0) + , _last_pointer_y (0.0) , _raw_grab_frame (0) , _grab_frame (0) , _last_pointer_frame (0) + , _snap_delta (0) + , _constraint_pressed (false) { } @@ -248,22 +260,12 @@ Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*t void Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor) { - // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained - if (Keyboard::is_button2_event (&event->button)) { - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) { - _y_constrained = true; - _x_constrained = false; - } else { - _y_constrained = false; - _x_constrained = true; - } - } else { - _x_constrained = false; - _y_constrained = false; - } + /* we set up x/y dragging constraints on first move */ + _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state); _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y); + setup_pointer_frame_offset (); _grab_frame = adjusted_frame (_raw_grab_frame, event); _last_pointer_frame = _grab_frame; @@ -343,6 +345,16 @@ Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const return adjusted_frame (_drags->current_pointer_frame (), event, snap); } +frameoffset_t +Drag::snap_delta (guint state) const +{ + if (ArdourKeyboard::indicates_snap_delta (state)) { + return _snap_delta; + } + + return 0; +} + double Drag::current_pointer_x() const { @@ -359,6 +371,14 @@ Drag::current_pointer_y () const return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y; } +void +Drag::setup_snap_delta (framepos_t pos) +{ + framepos_t temp = pos; + _editor->snap_to (temp, ARDOUR::RoundNearest, false, true); + _snap_delta = temp - pos; +} + bool Drag::motion_handler (GdkEvent* event, bool from_autoscroll) { @@ -394,6 +414,39 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll) } else { _initially_vertical = false; } + /** check constraints for this drag. + * Note that the current convention is to use "contains" for + * key modifiers during motion and "equals" when initiating a drag. + * In this case we haven't moved yet, so "equals" applies here. + */ + if (Config->get_edit_mode() != Lock) { + if (event->motion.state & Gdk::BUTTON2_MASK) { + // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained + if (_constraint_pressed) { + _x_constrained = false; + _y_constrained = true; + } else { + _x_constrained = true; + _y_constrained = false; + } + } else if (_constraint_pressed) { + // if dragging normally, the motion is constrained to the first direction of movement. + if (_initially_vertical) { + _x_constrained = true; + _y_constrained = false; + } else { + _x_constrained = false; + _y_constrained = true; + } + } + } else { + if (event->button.state & Gdk::BUTTON2_MASK) { + _x_constrained = false; + } else { + _x_constrained = true; + } + _y_constrained = false; + } } if (!from_autoscroll) { @@ -413,7 +466,7 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll) _last_pointer_x = _drags->current_pointer_x (); _last_pointer_y = current_pointer_y (); - _last_pointer_frame = adjusted_current_frame (event); + _last_pointer_frame = adjusted_current_frame (event, false); } return true; @@ -459,28 +512,24 @@ Drag::show_verbose_cursor_text (string const & text) } boost::shared_ptr -Drag::add_midi_region (MidiTimeAxisView* view) +Drag::add_midi_region (MidiTimeAxisView* view, bool commit, const int32_t sub_num) { if (_editor->session()) { const TempoMap& map (_editor->session()->tempo_map()); framecnt_t pos = grab_frame(); - const Meter& m = map.meter_at (pos); /* not that the frame rate used here can be affected by pull up/down which might be wrong. */ - framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate()); - return view->add_region (grab_frame(), len, true); + framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos; + return view->add_region (grab_frame(), len, commit, sub_num); } return boost::shared_ptr(); } -struct EditorOrderTimeAxisViewSorter { +struct PresentationInfoTimeAxisViewSorter { bool operator() (TimeAxisView* a, TimeAxisView* b) { - RouteTimeAxisView* ra = dynamic_cast (a); - RouteTimeAxisView* rb = dynamic_cast (b); - assert (ra && rb); - return ra->route()->order_key () < rb->route()->order_key (); + return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order(); } }; @@ -496,7 +545,7 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, listtrack_views; - track_views.sort (EditorOrderTimeAxisViewSorter ()); + track_views.sort (PresentationInfoTimeAxisViewSorter ()); for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { _time_axis_views.push_back (*i); @@ -552,10 +601,10 @@ RegionDrag::find_time_axis_view (TimeAxisView* t) const RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v, bool b) : RegionDrag (e, i, p, v) , _brushing (b) + , _ignore_video_lock (false) , _total_x_delta (0) , _last_pointer_time_axis_view (0) , _last_pointer_layer (0) - , _single_axis (false) , _ndropzone (0) , _pdropzone (0) , _ddropzone (0) @@ -567,10 +616,7 @@ void RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { Drag::start_grab (event, cursor); - - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) { - _single_axis = true; - } + setup_snap_delta (_last_frame_position); show_verbose_cursor_time (_last_frame_position); @@ -580,6 +626,15 @@ RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) assert(_last_pointer_time_axis_view >= 0); _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; } + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) { + _ignore_video_lock = true; + } + + if (_brushing) { + /* cross track dragging seems broken here. disabled for now. */ + _y_constrained = true; + } } double @@ -588,7 +643,7 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r /* compute the amount of pointer motion in frames, and where the region would be if we moved it by that much. */ - *pending_region_position = adjusted_current_frame (event); + *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false); framepos_t sync_frame; framecnt_t sync_offset; @@ -600,11 +655,12 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r */ if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) { - sync_frame = *pending_region_position + (sync_dir*sync_offset); + framecnt_t const sd = snap_delta (event->button.state); + sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd; _editor->snap_to_with_modifier (sync_frame, event); - *pending_region_position = _primary->region()->adjust_to_sync (sync_frame); + *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd; } else { *pending_region_position = _last_frame_position; @@ -616,8 +672,7 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r double dx = 0; - /* in locked edit mode, reverse the usual meaning of _x_constrained */ - bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained; + bool const x_move_allowed = !_x_constrained; if ((*pending_region_position != _last_frame_position) && x_move_allowed) { @@ -750,18 +805,6 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) assert (!_views.empty ()); - if (first_move) { - if (_single_axis) { - if (initially_vertical()) { - _y_constrained = false; - _x_constrained = true; - } else { - _y_constrained = true; - _x_constrained = false; - } - } - } - /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */ /* Find the TimeAxisView that the pointer is now over */ @@ -802,7 +845,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) RouteTimeAxisView* rtv = dynamic_cast (tv); if (!rtv || !rtv->is_track()) { - /* ignore busses early on. we can't move any regions on them */ + /* ignore non-tracks early on. we can't move any regions on them */ } else if (_last_pointer_time_axis_view < 0) { /* Was in the drop-zone, now over a track. * Hence it must be an upward move (from the bottom) @@ -847,7 +890,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) } /* for automation lanes, there is a TimeAxisView but no ->view() - * if (!tv) -> dropzone + * if (!tv) -> dropzone */ else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) { /* Moving into the drop-zone.. */ @@ -979,7 +1022,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) y_delta = 0; - if (rv->region()->locked() || rv->region()->video_locked()) { + if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) { continue; } @@ -1010,8 +1053,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) this_delta_layer = - i->layer; } - int this_delta_time_axis_view = delta_time_axis_view; - this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view; + int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view; int track_index = i->time_axis_view + this_delta_time_axis_view; assert(track_index >= 0); @@ -1147,7 +1189,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) * We do not use negative _last_pointer_time_axis_view because * the dropzone is "packed" (the actual track offset is ignored) * - * As opposed to the actual number + * As opposed to the actual number * of elements in the dropzone (_ndropzone) * _pdropzone is not constrained. This is necessary * to allow moving multiple regions with y-distance @@ -1173,8 +1215,9 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) /* if all logic and maths are correct, there is no need to assign the 'current' pointer. * the current position can be calculated as follows: */ - assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view); - // robin crosses his fingers, and looks at busses. + // a well placed oofus attack can still throw this off. + // likley auto-scroll related, printf() debugging may tell, commented out for now. + //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view); } } else { /* last motion event was also over a time axis view */ @@ -1197,13 +1240,13 @@ void RegionMoveDrag::motion (GdkEvent* event, bool first_move) { if (_copy && first_move) { - - if (_x_constrained) { + if (_x_constrained && !_brushing) { _editor->begin_reversible_command (Operations::fixed_time_region_copy); - } else { + } else if (!_brushing) { _editor->begin_reversible_command (Operations::region_copy); + } else if (_brushing) { + _editor->begin_reversible_command (Operations::drag_region_brush); } - /* duplicate the regionview(s) and region(s) */ list new_regionviews; @@ -1215,8 +1258,8 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move) MidiRegionView* mrv = dynamic_cast(rv); const boost::shared_ptr original = rv->region(); - boost::shared_ptr region_copy = RegionFactory::create (original, true); - region_copy->set_position (original->position()); + boost::shared_ptr region_copy = RegionFactory::create (original, true + , _editor->get_grid_music_divisions (event->button.state)); /* need to set this so that the drop zone code can work. This doesn't actually put the region into the playlist, but just sets a weak pointer to it. @@ -1261,14 +1304,14 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move) } } else if (!_copy && first_move) { - - if (_x_constrained) { + if (_x_constrained && !_brushing) { _editor->begin_reversible_command (_("fixed time region drag")); - } else { + } else if (!_brushing) { _editor->begin_reversible_command (Operations::region_drag); + } else if (_brushing) { + _editor->begin_reversible_command (Operations::drag_region_brush); } } - RegionMotionDrag::motion (event, first_move); } @@ -1304,14 +1347,6 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred) return; } - /* reverse this here so that we have the correct logic to finalize - the drag. - */ - - if (Config->get_edit_mode() == Lock) { - _x_constrained = !_x_constrained; - } - assert (!_views.empty ()); /* We might have hidden region views so that they weren't visible during the drag @@ -1331,7 +1366,8 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred) finished_copy ( changed_position, changed_tracks, - drag_delta + drag_delta, + ev->button.state ); } else { @@ -1339,7 +1375,8 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred) finished_no_copy ( changed_position, changed_tracks, - drag_delta + drag_delta, + ev->button.state ); } @@ -1361,21 +1398,23 @@ RegionMoveDrag::create_destination_time_axis (boost::shared_ptr region, if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) { output_chan = _editor->session()->master_out()->n_inputs().n_audio(); } - audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name()); - RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front()); - if (rtav) { - rtav->set_height (original->current_height()); + audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order); + TimeAxisView* tav =_editor->axis_view_from_stripable (audio_tracks.front()); + if (tav) { + tav->set_height (original->current_height()); } - return rtav; + return dynamic_cast(tav); } else { ChanCount one_midi_port (DataType::MIDI, 1); list > midi_tracks; - midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr(), ARDOUR::Normal, 0, 1, region->name()); - RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front()); - if (rtav) { - rtav->set_height (original->current_height()); + midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr(), + (ARDOUR::Plugin::PresetRecord*) 0, + (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order); + TimeAxisView* tav = _editor->axis_view_from_stripable (midi_tracks.front()); + if (tav) { + tav->set_height (original->current_height()); } - return rtav; + return dynamic_cast (tav); } } catch (...) { error << _("Could not create new track after region placed in the drop zone") << endmsg; @@ -1384,7 +1423,7 @@ RegionMoveDrag::create_destination_time_axis (boost::shared_ptr region, } void -RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta) +RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state) { RegionSelection new_views; PlaylistSet modified_playlists; @@ -1409,7 +1448,7 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed RouteTimeAxisView* dest_rtv = 0; - if (i->view->region()->locked() || i->view->region()->video_locked()) { + if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) { continue; } @@ -1441,7 +1480,9 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed } if (dest_rtv != 0) { - RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists); + RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, + modified_playlists, _editor->get_grid_music_divisions (ev_state)); + if (new_view != 0) { new_views.push_back (new_view); } @@ -1475,7 +1516,8 @@ void RegionMoveDrag::finished_no_copy ( bool const changed_position, bool const changed_tracks, - framecnt_t const drag_delta + framecnt_t const drag_delta, + int32_t const ev_state ) { RegionSelection new_views; @@ -1484,12 +1526,6 @@ RegionMoveDrag::finished_no_copy ( set views_to_update; RouteTimeAxisView* new_time_axis_view = 0; - if (_brushing) { - /* all changes were made during motion event handlers */ - _editor->commit_reversible_command (); - return; - } - typedef map, RouteTimeAxisView*> PlaylistMapping; PlaylistMapping playlist_mapping; @@ -1498,7 +1534,7 @@ RegionMoveDrag::finished_no_copy ( RegionView* rv = i->view; RouteTimeAxisView* dest_rtv = 0; - if (rv->region()->locked() || rv->region()->video_locked()) { + if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) { ++i; continue; } @@ -1542,7 +1578,8 @@ RegionMoveDrag::finished_no_copy ( /* insert into new playlist */ RegionView* new_view = insert_region_into_playlist ( - RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists + RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, + modified_playlists, _editor->get_grid_music_divisions (ev_state) ); if (new_view == 0) { @@ -1602,8 +1639,7 @@ RegionMoveDrag::finished_no_copy ( playlist->freeze (); } - rv->region()->set_position (where); - + rv->region()->set_position (where, _editor->get_grid_music_divisions (ev_state)); _editor->session()->add_command (new StatefulDiffCommand (rv->region())); } @@ -1652,7 +1688,7 @@ RegionMoveDrag::finished_no_copy ( /* write commands for the accumulated diffs for all our modified playlists */ add_stateful_diff_commands_for_playlists (modified_playlists); - + /* applies to _brushing */ _editor->commit_reversible_command (); /* We have futzed with the layering of canvas items on our streamviews. @@ -1706,7 +1742,8 @@ RegionMoveDrag::insert_region_into_playlist ( RouteTimeAxisView* dest_rtv, layer_t dest_layer, framecnt_t where, - PlaylistSet& modified_playlists + PlaylistSet& modified_playlists, + const int32_t sub_num ) { boost::shared_ptr dest_playlist = dest_rtv->playlist (); @@ -1723,8 +1760,7 @@ RegionMoveDrag::insert_region_into_playlist ( if (r.second) { dest_playlist->clear_changes (); } - - dest_playlist->add_region (region, where); + dest_playlist->add_region (region, where, 1.0, false, sub_num); if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) { dest_playlist->set_layer (region, dest_layer); @@ -1809,6 +1845,7 @@ RegionMotionDrag::aborted (bool) RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v, bool b, bool c) : RegionMotionDrag (e, i, p, v, b) , _copy (c) + , _new_region_view (0) { DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n"); @@ -1847,7 +1884,7 @@ RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr r, Rout } void -RegionInsertDrag::finished (GdkEvent *, bool) +RegionInsertDrag::finished (GdkEvent * event, bool) { int pos = _views.front().time_axis_view; assert(pos >= 0 && pos < (int)_time_axis_views.size()); @@ -2273,13 +2310,14 @@ void RegionCreateDrag::motion (GdkEvent* event, bool first_move) { if (first_move) { - _region = add_midi_region (_view); + _editor->begin_reversible_command (_("create region")); + _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state)); _view->playlist()->freeze (); } else { if (_region) { framepos_t const f = adjusted_current_frame (event); if (f < grab_frame()) { - _region->set_position (f); + _region->set_initial_position (f); } /* Don't use a zero-length region, and subtract 1 frame from the snapped length @@ -2290,18 +2328,19 @@ RegionCreateDrag::motion (GdkEvent* event, bool first_move) */ framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1)); - _region->set_length (len < 1 ? 1 : len); + _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state)); } } } void -RegionCreateDrag::finished (GdkEvent*, bool movement_occurred) +RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred) { if (!movement_occurred) { - add_midi_region (_view); + add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state)); } else { _view->playlist()->thaw (); + _editor->commit_reversible_command(); } } @@ -2318,6 +2357,10 @@ RegionCreateDrag::aborted (bool) NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) , region (0) + , relative (false) + , at_front (true) + , _was_selected (false) + , _snap_delta (0) { DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n"); } @@ -2342,64 +2385,171 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/) region = &cnote->region_view(); + double temp; + temp = region->snap_to_pixel (cnote->x0 (), true); + _snap_delta = temp - cnote->x0 (); + _item->grab (); - if (event->motion.state & Keyboard::PrimaryModifier) { + if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) { relative = false; } else { relative = true; } - MidiRegionSelection& ms (_editor->get_selection().midi_regions); - if (ms.size() > 1) { /* has to be relative, may make no sense otherwise */ relative = true; } - /* select this note; if it is already selected, preserve the existing selection, - otherwise make this note the only one selected. - */ - region->note_selected (cnote, cnote->selected ()); + if (!(_was_selected = cnote->selected())) { + + /* tertiary-click means extend selection - we'll do that on button release, + so don't add it here, because otherwise we make it hard to figure + out the "extend-to" range. + */ - _editor->begin_reversible_command (_("resize notes")); + bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier); - for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) { - MidiRegionSelection::iterator next; - next = r; - ++next; - MidiRegionView* mrv = dynamic_cast(*r); - if (mrv) { - mrv->begin_resizing (at_front); + if (!extend) { + bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier); + + if (add) { + region->note_selected (cnote, true); + } else { + _editor->get_selection().clear_points(); + region->unique_select (cnote); + } } - r = next; } } void -NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/) +NoteResizeDrag::motion (GdkEvent* event, bool first_move) { MidiRegionSelection& ms (_editor->get_selection().midi_regions); + if (first_move) { + _editor->begin_reversible_command (_("resize notes")); + + for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) { + MidiRegionSelection::iterator next; + next = r; + ++next; + MidiRegionView* mrv = dynamic_cast(*r); + if (mrv) { + mrv->begin_resizing (at_front); + } + r = next; + } + } + for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) { NoteBase* nb = reinterpret_cast (_item->get_data ("notebase")); assert (nb); MidiRegionView* mrv = dynamic_cast(*r); if (mrv) { - mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative); + double sd = 0.0; + bool snap = true; + bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state); + + if (ArdourKeyboard::indicates_snap (event->button.state)) { + if (_editor->snap_mode () != SnapOff) { + snap = false; + } + } else { + if (_editor->snap_mode () == SnapOff) { + snap = false; + /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */ + if (apply_snap_delta) { + snap = true; + } + } + } + + if (apply_snap_delta) { + sd = _snap_delta; + } + + mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap); } } } void -NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/) +NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred) { + if (!movement_occurred) { + /* no motion - select note */ + NoteBase* cnote = reinterpret_cast (_item->get_data ("notebase")); + if (_editor->current_mouse_mode() == Editing::MouseContent || + _editor->current_mouse_mode() == Editing::MouseDraw) { + + bool changed = false; + + if (_was_selected) { + bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier); + if (add) { + region->note_deselected (cnote); + changed = true; + } else { + _editor->get_selection().clear_points(); + region->unique_select (cnote); + changed = true; + } + } else { + bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier); + bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier); + + if (!extend && !add && region->selection_size() > 1) { + _editor->get_selection().clear_points(); + region->unique_select (cnote); + changed = true; + } else if (extend) { + region->note_selected (cnote, true, true); + changed = true; + } else { + /* it was added during button press */ + changed = true; + } + } + + if (changed) { + _editor->begin_reversible_selection_op(X_("Resize Select Note Release")); + _editor->commit_reversible_selection_op(); + } + } + + return; + } + MidiRegionSelection& ms (_editor->get_selection().midi_regions); for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) { NoteBase* nb = reinterpret_cast (_item->get_data ("notebase")); assert (nb); MidiRegionView* mrv = dynamic_cast(*r); + double sd = 0.0; + bool snap = true; + bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state); if (mrv) { - mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative); + if (ArdourKeyboard::indicates_snap (event->button.state)) { + if (_editor->snap_mode () != SnapOff) { + snap = false; + } + } else { + if (_editor->snap_mode () == SnapOff) { + snap = false; + /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */ + if (apply_snap_delta) { + snap = true; + } + } + } + + if (apply_snap_delta) { + sd = _snap_delta; + } + + mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap); } } @@ -2435,11 +2585,15 @@ VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i) _editor->get_regions_after(rs, (framepos_t) 0, empty); std::list views = rs.by_layer(); + _stuck = false; for (list::iterator i = views.begin(); i != views.end(); ++i) { RegionView* rv = (*i); if (!rv->region()->video_locked()) { continue; } + if (rv->region()->locked()) { + _stuck = true; + } _views.push_back (AVDraggingView (rv)); } } @@ -2452,6 +2606,16 @@ VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*) return; } + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) { + _stuck = false; + _views.clear(); + } + + if (_stuck) { + show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved.")); + return; + } + _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset(); _max_backwards_drag = ( ARDOUR_UI::instance()->video_timeline->get_duration() @@ -2482,6 +2646,10 @@ VideoTimeLineDrag::motion (GdkEvent* event, bool first_move) if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) { return; } + if (_stuck) { + show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved.")); + return; + } framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset; dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset; @@ -2528,6 +2696,9 @@ VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred) if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) { return; } + if (_stuck) { + return; + } if (!movement_occurred || ! _editor->session()) { return; @@ -2575,6 +2746,7 @@ VideoTimeLineDrag::aborted (bool) TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v, bool preserve_fade_anchor) : RegionDrag (e, i, p, v) + , _operation (StartTrim) , _preserve_fade_anchor (preserve_fade_anchor) , _jump_position_when_done (false) { @@ -2597,8 +2769,9 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*) framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed); framepos_t const pf = adjusted_current_frame (event); + setup_snap_delta (region_start); - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) { /* Move the contents of the region around without changing the region bounds */ _operation = ContentsTrim; Drag::start_grab (event, _editor->cursors()->trimmer); @@ -2607,8 +2780,7 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*) if (pf < (region_start + region_length/2)) { /* closer to front */ _operation = StartTrim; - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) { Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim); } else { Drag::start_grab (event, _editor->cursors()->left_side_trim); @@ -2616,27 +2788,25 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*) } else { /* closer to end */ _operation = EndTrim; - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) { Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim); } else { Drag::start_grab (event, _editor->cursors()->right_side_trim); } } } - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + /* jump trim disabled for now + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) { _jump_position_when_done = true; } + */ switch (_operation) { case StartTrim: show_verbose_cursor_time (region_start); - for (list::iterator i = _views.begin(); i != _views.end(); ++i) { - i->view->trim_front_starting (); - } break; case EndTrim: - show_verbose_cursor_time (region_end); + show_verbose_cursor_duration (region_start, region_end); break; case ContentsTrim: show_verbose_cursor_time (pf); @@ -2662,8 +2832,8 @@ TrimDrag::motion (GdkEvent* event, bool first_move) if (tv && tv->is_track()) { speed = tv->track()->speed(); } - - framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset; + framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true); + framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state); if (first_move) { @@ -2688,9 +2858,12 @@ TrimDrag::motion (GdkEvent* event, bool first_move) for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { RegionView* rv = i->view; - rv->enable_display (false); rv->region()->playlist()->clear_owned_changes (); + if (_operation == StartTrim) { + rv->trim_front_starting (); + } + AudioRegionView* const arv = dynamic_cast (rv); if (arv) { @@ -2709,7 +2882,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move) bool non_overlap_trim = false; - if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) { non_overlap_trim = true; } @@ -2745,7 +2918,9 @@ TrimDrag::motion (GdkEvent* event, bool first_move) switch (_operation) { case StartTrim: for (list::iterator i = _views.begin(); i != _views.end(); ++i) { - bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim); + bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim + , _editor->get_grid_music_divisions (event->button.state)); + if (changed && _preserve_fade_anchor) { AudioRegionView* arv = dynamic_cast (i->view); if (arv) { @@ -2763,7 +2938,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move) case EndTrim: for (list::iterator i = _views.begin(); i != _views.end(); ++i) { - bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim); + bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, _editor->get_grid_music_divisions (event->button.state)); if (changed && _preserve_fade_anchor) { AudioRegionView* arv = dynamic_cast (i->view); if (arv) { @@ -2795,7 +2970,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move) show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed)); break; case EndTrim: - show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed)); + show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed); break; case ContentsTrim: // show_verbose_cursor_time (frame_delta); @@ -2803,7 +2978,6 @@ TrimDrag::motion (GdkEvent* event, bool first_move) } } - void TrimDrag::finished (GdkEvent* event, bool movement_occurred) { @@ -2895,14 +3069,12 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred) } else { /* no mouse movement */ - _editor->point_trim (event, adjusted_current_frame (event)); + if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) { + _editor->point_trim (event, adjusted_current_frame (event)); + } } for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { - if (_operation == StartTrim) { - i->view->trim_front_ending (); - } - i->view->region()->resume_property_changes (); } } @@ -2915,10 +3087,11 @@ TrimDrag::aborted (bool movement_occurred) behind which may be slightly odd from the user's point of view. */ - finished (0, true); + GdkEvent ev; + finished (&ev, true); if (movement_occurred) { - _editor->undo (); + _editor->session()->undo (1); } for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { @@ -2951,12 +3124,17 @@ TrimDrag::setup_pointer_frame_offset () } MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) - : Drag (e, i), - _copy (c) + : Drag (e, i) + , _copy (c) + , _old_snap_type (e->snap_type()) + , _old_snap_mode (e->snap_mode()) + , before_state (0) { DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n"); _marker = reinterpret_cast (_item->get_data ("marker")); assert (_marker); + _real_section = &_marker->meter(); + } void @@ -2975,22 +3153,8 @@ MeterMarkerDrag::setup_pointer_frame_offset () void MeterMarkerDrag::motion (GdkEvent* event, bool first_move) { - if (!_marker->meter().movable()) { - return; - } - if (first_move) { - - // create a dummy marker for visual representation of moving the - // section, because whether its a copy or not, we're going to - // leave or lose the original marker (leave if its a copy; lose if its - // not, because we'll remove it from the map). - - MeterSection section (_marker->meter()); - - if (!section.movable()) { - return; - } + // create a dummy marker to catch events, then hide it. char name[64]; snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ()); @@ -2998,27 +3162,61 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move) _marker = new MeterMarker ( *_editor, *_editor->meter_group, - ARDOUR_UI::config()->color ("meter marker"), + UIConfiguration::instance().color ("meter marker"), name, *new MeterSection (_marker->meter()) ); /* use the new marker for the grab */ swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME); + _marker->hide(); + + TempoMap& map (_editor->session()->tempo_map()); + /* get current state */ + before_state = &map.get_state(); if (!_copy) { - TempoMap& map (_editor->session()->tempo_map()); - /* get current state */ - before_state = &map.get_state(); - /* remove the section while we drag it */ - map.remove_meter (section, true); + _editor->begin_reversible_command (_("move meter mark")); + } else { + _editor->begin_reversible_command (_("copy meter mark")); + + Timecode::BBT_Time bbt = _real_section->bbt(); + + /* we can't add a meter where one currently exists */ + if (_real_section->frame() < adjusted_current_frame (event, false)) { + ++bbt.bars; + } else { + --bbt.bars; + } + const double beat = map.beat_at_bbt (bbt); + _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor()) + , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style()); + if (!_real_section) { + aborted (true); + return; + } + + } + /* only snap to bars. leave snap mode alone for audio locked meters.*/ + if (_real_section->position_lock_style() != AudioTime) { + _editor->set_snap_to (SnapToBar); + _editor->set_snap_mode (SnapNormal); } } - framepos_t const pf = adjusted_current_frame (event); + framepos_t pf = adjusted_current_frame (event); - _marker->set_position (pf); - show_verbose_cursor_time (pf); + if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) { + /* never snap to music for audio locked */ + pf = adjusted_current_frame (event, false); + } + + _editor->session()->tempo_map().gui_move_meter (_real_section, pf); + + /* fake marker meeds to stay under the mouse, unlike the real one. */ + _marker->set_position (adjusted_current_frame (event, false)); + + show_verbose_cursor_time (_real_section->frame()); } void @@ -3031,35 +3229,15 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred) return; } - if (!_marker->meter().movable()) { - return; - } - - motion (event, false); - - Timecode::BBT_Time when; + /* reinstate old snap setting */ + _editor->set_snap_to (_old_snap_type); + _editor->set_snap_mode (_old_snap_mode); TempoMap& map (_editor->session()->tempo_map()); - map.bbt_time (last_pointer_frame(), when); - - if (_copy == true) { - _editor->begin_reversible_command (_("copy meter mark")); - XMLNode &before = map.get_state(); - map.add_meter (_marker->meter(), when); - XMLNode &after = map.get_state(); - _editor->session()->add_command(new MementoCommand(map, &before, &after)); - _editor->commit_reversible_command (); - - } else { - _editor->begin_reversible_command (_("move meter mark")); - /* we removed it before, so add it back now */ - - map.add_meter (_marker->meter(), when); - XMLNode &after = map.get_state(); - _editor->session()->add_command(new MementoCommand(map, before_state, &after)); - _editor->commit_reversible_command (); - } + XMLNode &after = map.get_state(); + _editor->session()->add_command(new MementoCommand(map, before_state, &after)); + _editor->commit_reversible_command (); // delete the dummy marker we used for visual representation while moving. // a new visual marker will show up automatically. @@ -3070,11 +3248,12 @@ void MeterMarkerDrag::aborted (bool moved) { _marker->set_position (_marker->meter().frame ()); - if (moved) { - TempoMap& map (_editor->session()->tempo_map()); - /* we removed it before, so add it back now */ - map.add_meter (_marker->meter(), _marker->meter().frame()); + /* reinstate old snap setting */ + _editor->set_snap_to (_old_snap_type); + _editor->set_snap_mode (_old_snap_mode); + + _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version); // delete the dummy marker we used for visual representation while moving. // a new visual marker will show up automatically. delete _marker; @@ -3082,12 +3261,15 @@ MeterMarkerDrag::aborted (bool moved) } TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) - : Drag (e, i), - _copy (c) + : Drag (e, i) + , _copy (c) + , before_state (0) { DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n"); _marker = reinterpret_cast (_item->get_data ("marker")); + _real_section = &_marker->tempo(); + _movable = _real_section->movable(); assert (_marker); } @@ -3095,31 +3277,29 @@ void TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { Drag::start_grab (event, cursor); - show_verbose_cursor_time (adjusted_current_frame (event)); + if (!_real_section->active()) { + show_verbose_cursor_text (_("inactive")); + } else { + show_verbose_cursor_time (adjusted_current_frame (event)); + } } void TempoMarkerDrag::setup_pointer_frame_offset () { - _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame(); + _pointer_frame_offset = raw_grab_frame() - _real_section->frame(); } void TempoMarkerDrag::motion (GdkEvent* event, bool first_move) { - if (!_marker->tempo().movable()) { + if (!_real_section->active()) { return; } if (first_move) { - // create a dummy marker for visual representation of moving the - // section, because whether its a copy or not, we're going to - // leave or lose the original marker (leave if its a copy; lose if its - // not, because we'll remove it from the map). - - // create a dummy marker for visual representation of moving the copy. - // The actual copying is not done before we reach the finish callback. + // mvc drag - create a dummy marker to catch events, hide it. char name[64]; snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute()); @@ -3129,31 +3309,82 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) _marker = new TempoMarker ( *_editor, *_editor->tempo_group, - ARDOUR_UI::config()->color ("tempo marker"), + UIConfiguration::instance().color ("tempo marker"), name, *new TempoSection (_marker->tempo()) ); /* use the new marker for the grab */ swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME); + _marker->hide(); + + TempoMap& map (_editor->session()->tempo_map()); + /* get current state */ + before_state = &map.get_state(); if (!_copy) { - TempoMap& map (_editor->session()->tempo_map()); - /* get current state */ - before_state = &map.get_state(); - /* remove the section while we drag it */ - map.remove_tempo (section, true); + _editor->begin_reversible_command (_("move tempo mark")); + + } else { + const Tempo tempo (_marker->tempo()); + const framepos_t frame = adjusted_current_frame (event) + 1; + const TempoSection::Type type = _real_section->type(); + + _editor->begin_reversible_command (_("copy tempo mark")); + + if (_real_section->position_lock_style() == MusicTime) { + _real_section = map.add_tempo (tempo, map.pulse_at_frame (frame), 0, type, MusicTime); + } else { + _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime); + } + + if (!_real_section) { + aborted (true); + return; + } } + } - framepos_t const pf = adjusted_current_frame (event); - _marker->set_position (pf); - show_verbose_cursor_time (pf); + if (ArdourKeyboard::indicates_constraint (event->button.state)) { + /* use vertical movement to alter tempo .. should be log */ + double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0); + stringstream strs; + + _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type())); + strs << new_bpm; + show_verbose_cursor_text (strs.str()); + + } else if (_movable && !_real_section->locked_to_meter()) { + framepos_t pf; + + if (_editor->snap_musical()) { + /* we can't snap to a grid that we are about to move. + * gui_move_tempo() will sort out snap using the supplied beat divisions. + */ + pf = adjusted_current_frame (event, false); + } else { + pf = adjusted_current_frame (event); + } + + TempoMap& map (_editor->session()->tempo_map()); + + /* snap to beat is 1, snap to bar is -1 (sorry) */ + const int sub_num = _editor->get_grid_music_divisions (event->button.state); + + map.gui_move_tempo (_real_section, pf, sub_num); + + show_verbose_cursor_time (_real_section->frame()); + } + _marker->set_position (adjusted_current_frame (event, false)); } void TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) { + if (!_real_section->active()) { + return; + } if (!movement_occurred) { if (was_double_click()) { _editor->edit_tempo_marker (*_marker); @@ -3161,34 +3392,11 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) return; } - if (!_marker->tempo().movable()) { - return; - } - - motion (event, false); - TempoMap& map (_editor->session()->tempo_map()); - framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest); - Timecode::BBT_Time when; - map.bbt_time (beat_time, when); - - if (_copy == true) { - _editor->begin_reversible_command (_("copy tempo mark")); - XMLNode &before = map.get_state(); - map.add_tempo (_marker->tempo(), when); - XMLNode &after = map.get_state(); - _editor->session()->add_command (new MementoCommand(map, &before, &after)); - _editor->commit_reversible_command (); - - } else { - _editor->begin_reversible_command (_("move tempo mark")); - /* we removed it before, so add it back now */ - map.add_tempo (_marker->tempo(), when); - XMLNode &after = map.get_state(); - _editor->session()->add_command (new MementoCommand(map, before_state, &after)); - _editor->commit_reversible_command (); - } + XMLNode &after = map.get_state(); + _editor->session()->add_command (new MementoCommand(map, before_state, &after)); + _editor->commit_reversible_command (); // delete the dummy marker we used for visual representation while moving. // a new visual marker will show up automatically. @@ -3201,20 +3409,119 @@ TempoMarkerDrag::aborted (bool moved) _marker->set_position (_marker->tempo().frame()); if (moved) { TempoMap& map (_editor->session()->tempo_map()); - /* we removed it before, so add it back now */ - map.add_tempo (_marker->tempo(), _marker->tempo().start()); - // delete the dummy marker we used for visual representation while moving. - // a new visual marker will show up automatically. + map.set_state (*before_state, Stateful::current_state_version); + // delete the dummy (hidden) marker we used for events while moving. delete _marker; } } -CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s) - : Drag (e, &c.track_canvas_item(), false) - , _cursor (c) - , _stop (s) +BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i) + : Drag (e, i) + , _pulse (0.0) + , _tempo (0) + , before_state (0) { - DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n"); + DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n"); + +} + +void +BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + Drag::start_grab (event, cursor); + TempoMap& map (_editor->session()->tempo_map()); + _tempo = const_cast (&map.tempo_section_at_frame (raw_grab_frame())); + ostringstream sstr; + + sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n"; + sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute(); + show_verbose_cursor_text (sstr.str()); + finished (event, false); +} + +void +BBTRulerDrag::setup_pointer_frame_offset () +{ + TempoMap& map (_editor->session()->tempo_map()); + const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame())); + const uint32_t divisions = _editor->get_grid_beat_divisions (0); + double beat = 0.0; + + if (divisions > 0) { + beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions); + } else { + /* while it makes some sense for the user to determine the division to 'grab', + grabbing a bar often leads to confusing results wrt the actual tempo section being altered + and the result over steep tempo curves. Use sixteenths. + */ + beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4); + } + + _pulse = map.pulse_at_beat (beat); + + _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse); + +} + +void +BBTRulerDrag::motion (GdkEvent* event, bool first_move) +{ + TempoMap& map (_editor->session()->tempo_map()); + + if (first_move) { + /* get current state */ + before_state = &map.get_state(); + _editor->begin_reversible_command (_("dilate tempo")); + } + + framepos_t pf; + + if (_editor->snap_musical()) { + pf = adjusted_current_frame (event, false); + } else { + pf = adjusted_current_frame (event); + } + + if (ArdourKeyboard::indicates_constraint (event->button.state)) { + /* adjust previous tempo to match pointer frame */ + _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse); + } + ostringstream sstr; + sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n"; + sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute(); + show_verbose_cursor_text (sstr.str()); +} + +void +BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred) +{ + if (!movement_occurred) { + return; + } + + TempoMap& map (_editor->session()->tempo_map()); + + XMLNode &after = map.get_state(); + _editor->session()->add_command(new MementoCommand(map, before_state, &after)); + _editor->commit_reversible_command (); +} + +void +BBTRulerDrag::aborted (bool moved) +{ + if (moved) { + _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version); + } +} + + +CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s) + : Drag (e, &c.track_canvas_item(), false) + , _cursor (c) + , _stop (s) + , _grab_zoom (0.0) +{ + DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n"); } /** Do all the things we do when dragging the playhead to make it look as though @@ -3224,6 +3531,10 @@ CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s) void CursorDrag::fake_locate (framepos_t t) { + if (_editor->session () == 0) { + return; + } + _editor->playhead_cursor->set_position (t); Session* s = _editor->session (); @@ -3247,10 +3558,11 @@ void CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) { Drag::start_grab (event, c); + setup_snap_delta (_editor->playhead_cursor->current_frame ()); _grab_zoom = _editor->samples_per_pixel; - framepos_t where = _editor->canvas_event_sample (event); + framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state); _editor->snap_to_with_modifier (where, event); @@ -3288,15 +3600,16 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) } } - fake_locate (where); + fake_locate (where - snap_delta (event->button.state)); } void CursorDrag::motion (GdkEvent* event, bool) { - framepos_t const adjusted_frame = adjusted_current_frame (event); - if (adjusted_frame != last_pointer_frame()) { - fake_locate (adjusted_frame); + framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state); + _editor->snap_to_with_modifier (where, event); + if (where != last_pointer_frame()) { + fake_locate (where - snap_delta (event->button.state)); } } @@ -3347,6 +3660,7 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) AudioRegionView* arv = dynamic_cast (_primary); boost::shared_ptr const r = arv->audio_region (); + setup_snap_delta (r->position ()); show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32); } @@ -3363,7 +3677,11 @@ void FadeInDrag::motion (GdkEvent* event, bool) { framecnt_t fade_length; - framepos_t const pos = adjusted_current_frame (event); + + framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state); + _editor->snap_to_with_modifier (pos, event); + pos -= snap_delta (event->button.state); + boost::shared_ptr region = boost::dynamic_pointer_cast (_primary->region ()); if (pos < (region->position() + 64)) { @@ -3396,8 +3714,9 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred) } framecnt_t fade_length; - - framepos_t const pos = adjusted_current_frame (event); + framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state); + _editor->snap_to_with_modifier (pos, event); + pos -= snap_delta (event->button.state); boost::shared_ptr region = boost::dynamic_pointer_cast (_primary->region ()); @@ -3409,7 +3728,7 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred) fade_length = pos - region->position(); } - _editor->begin_reversible_command (_("change fade in length")); + bool in_command = false; for (list::iterator i = _views.begin(); i != _views.end(); ++i) { @@ -3425,11 +3744,17 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred) tmp->audio_region()->set_fade_in_length (fade_length); tmp->audio_region()->set_fade_in_active (true); + if (!in_command) { + _editor->begin_reversible_command (_("change fade in length")); + in_command = true; + } XMLNode &after = alist->get_state(); _editor->session()->add_command(new MementoCommand(*alist.get(), &before, &after)); } - _editor->commit_reversible_command (); + if (in_command) { + _editor->commit_reversible_command (); + } } void @@ -3459,6 +3784,7 @@ FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) AudioRegionView* arv = dynamic_cast (_primary); boost::shared_ptr r = arv->audio_region (); + setup_snap_delta (r->last_frame ()); show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame()); } @@ -3476,7 +3802,9 @@ FadeOutDrag::motion (GdkEvent* event, bool) { framecnt_t fade_length; - framepos_t const pos = adjusted_current_frame (event); + framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state); + _editor->snap_to_with_modifier (pos, event); + pos -= snap_delta (event->button.state); boost::shared_ptr region = boost::dynamic_pointer_cast (_primary->region ()); @@ -3511,7 +3839,9 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred) framecnt_t fade_length; - framepos_t const pos = adjusted_current_frame (event); + framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state); + _editor->snap_to_with_modifier (pos, event); + pos -= snap_delta (event->button.state); boost::shared_ptr region = boost::dynamic_pointer_cast (_primary->region ()); @@ -3523,7 +3853,7 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred) fade_length = region->last_frame() - pos; } - _editor->begin_reversible_command (_("change fade out length")); + bool in_command = false; for (list::iterator i = _views.begin(); i != _views.end(); ++i) { @@ -3539,11 +3869,17 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred) tmp->audio_region()->set_fade_out_length (fade_length); tmp->audio_region()->set_fade_out_active (true); + if (!in_command) { + _editor->begin_reversible_command (_("change fade out length")); + in_command = true; + } XMLNode &after = alist->get_state(); _editor->session()->add_command(new MementoCommand(*alist.get(), &before, &after)); } - _editor->commit_reversible_command (); + if (in_command) { + _editor->commit_reversible_command (); + } } void @@ -3562,14 +3898,17 @@ FadeOutDrag::aborted (bool) MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) + , _selection_changed (false) { DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n"); + Gtk::Window* toplevel = _editor->current_toplevel(); + _marker = reinterpret_cast (_item->get_data ("marker")); - _marker = reinterpret_cast (_item->get_data ("marker")); assert (_marker); _points.push_back (ArdourCanvas::Duple (0, 0)); - _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window()))); + + _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900)); } MarkerDrag::~MarkerDrag () @@ -3579,7 +3918,7 @@ MarkerDrag::~MarkerDrag () } } -MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m) +MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m) { location = new Location (*l); markers.push_back (m); @@ -3606,6 +3945,7 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) } else { show_verbose_cursor_time (location->end()); } + setup_snap_delta (is_start ? location->start() : location->end()); Selection::Operation op = ArdourKeyboard::selection_type (event->button.state); @@ -3616,12 +3956,13 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) case Selection::Set: if (!_editor->selection->selected (_marker)) { _editor->selection->set (_marker); + _selection_changed = true; } break; case Selection::Extend: { Locations::LocationList ll; - list to_add; + list to_add; framepos_t s, e; _editor->selection->markers.range (s, e); s = min (_marker->position(), s); @@ -3645,11 +3986,14 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) } if (!to_add.empty()) { _editor->selection->add (to_add); + _selection_changed = true; } break; } case Selection::Add: _editor->selection->add (_marker); + _selection_changed = true; + break; } @@ -3703,11 +4047,12 @@ MarkerDrag::motion (GdkEvent* event, bool) bool move_both = false; Location *real_location; Location *copy_location = 0; + framecnt_t const sd = snap_delta (event->button.state); - framepos_t const newframe = adjusted_current_frame (event); + framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd; framepos_t next = newframe; - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { + if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) { move_both = true; } @@ -3736,17 +4081,17 @@ MarkerDrag::motion (GdkEvent* event, bool) switch (_marker->type()) { - case Marker::SessionStart: - case Marker::RangeStart: - case Marker::LoopStart: - case Marker::PunchIn: + case ArdourMarker::SessionStart: + case ArdourMarker::RangeStart: + case ArdourMarker::LoopStart: + case ArdourMarker::PunchIn: f_delta = newframe - copy_location->start(); break; - case Marker::SessionEnd: - case Marker::RangeEnd: - case Marker::LoopEnd: - case Marker::PunchOut: + case ArdourMarker::SessionEnd: + case ArdourMarker::RangeEnd: + case ArdourMarker::LoopEnd: + case ArdourMarker::PunchOut: f_delta = newframe - copy_location->end(); break; default: @@ -3794,10 +4139,10 @@ MarkerDrag::motion (GdkEvent* event, bool) if (move_both || (*x).move_both) { copy_location->set_start (new_start); copy_location->set_end (new_end); - } else if (new_start < copy_location->end()) { + } else if (new_start < copy_location->end()) { copy_location->set_start (new_start); } else if (newframe > 0) { - _editor->snap_to (next, RoundUpAlways, true); + //_editor->snap_to (next, RoundUpAlways, true); copy_location->set_end (next); copy_location->set_start (newframe); } @@ -3810,7 +4155,7 @@ MarkerDrag::motion (GdkEvent* event, bool) } else if (new_end > copy_location->start()) { copy_location->set_end (new_end); } else if (newframe > 0) { - _editor->snap_to (next, RoundDownAlways, true); + //_editor->snap_to (next, RoundDownAlways, true); copy_location->set_start (next); copy_location->set_end (newframe); } @@ -3854,17 +4199,19 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred) */ Selection::Operation op = ArdourKeyboard::selection_type (event->button.state); - switch (op) { case Selection::Set: if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) { _editor->selection->set (_marker); + _selection_changed = true; } break; case Selection::Toggle: /* we toggle on the button release, click only */ _editor->selection->toggle (_marker); + _selection_changed = true; + break; case Selection::Extend: @@ -3872,13 +4219,18 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred) break; } + if (_selection_changed) { + _editor->begin_reversible_selection_op(X_("Select Marker Release")); + _editor->commit_reversible_selection_op(); + } + return; } _editor->_dragging_edit_point = false; - _editor->begin_reversible_command ( _("move marker") ); XMLNode &before = _editor->session()->locations()->get_state(); + bool in_command = false; MarkerSelection::iterator i; CopiedLocationInfo::iterator x; @@ -3893,26 +4245,35 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred) if (location) { if (location->locked()) { - return; + continue; + } + if (!in_command) { + _editor->begin_reversible_command ( _("move marker") ); + in_command = true; } - if (location->is_mark()) { location->set_start (((*x).location)->start()); } else { location->set (((*x).location)->start(), ((*x).location)->end()); } + + if (location->is_session_range()) { + _editor->session()->set_end_is_free (false); + } } } - XMLNode &after = _editor->session()->locations()->get_state(); - _editor->session()->add_command(new MementoCommand(*(_editor->session()->locations()), &before, &after)); - _editor->commit_reversible_command (); + if (in_command) { + XMLNode &after = _editor->session()->locations()->get_state(); + _editor->session()->add_command(new MementoCommand(*(_editor->session()->locations()), &before, &after)); + _editor->commit_reversible_command (); + } } void -MarkerDrag::aborted (bool movement_occured) +MarkerDrag::aborted (bool movement_occurred) { - if (!movement_occured) { + if (!movement_occurred) { return; } @@ -3921,7 +4282,7 @@ MarkerDrag::aborted (bool movement_occured) /* move all markers to their original location */ - for (vector::iterator m = x->markers.begin(); m != x->markers.end(); ++m) { + for (vector::iterator m = x->markers.begin(); m != x->markers.end(); ++m) { bool is_start; Location * location = _editor->find_location_from_marker (*m, is_start); @@ -3940,9 +4301,13 @@ MarkerDrag::update_item (Location*) } ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i) - : Drag (e, i), - _cumulative_x_drag (0), - _cumulative_y_drag (0) + : Drag (e, i) + , _fixed_grab_x (0.0) + , _fixed_grab_y (0.0) + , _cumulative_x_drag (0.0) + , _cumulative_y_drag (0.0) + , _pushing (false) + , _final_index (0) { if (_zero_gain_fraction < 0.0) { _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain()); @@ -3965,13 +4330,13 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) _fixed_grab_x = _point->get_x(); _fixed_grab_y = _point->get_y(); - float const fraction = 1 - (_point->get_y() / _point->line().height()); - - _point->line().start_drag_single (_point, _fixed_grab_x, fraction); + framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x); + setup_snap_delta (pos); + float const fraction = 1 - (_point->get_y() / _point->line().height()); show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction)); - _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier); + _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ()); if (!_point->can_slide ()) { _x_constrained = true; @@ -3979,14 +4344,16 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) } void -ControlPointDrag::motion (GdkEvent* event, bool) +ControlPointDrag::motion (GdkEvent* event, bool first_motion) { double dx = _drags->current_pointer_x() - last_pointer_x(); double dy = current_pointer_y() - last_pointer_y(); + bool need_snap = true; - if (event->button.state & Keyboard::SecondaryModifier) { + if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) { dx *= 0.1; dy *= 0.1; + need_snap = false; } /* coordinate in pixels relative to the start of the region (for region-based automation) @@ -3998,11 +4365,6 @@ ControlPointDrag::motion (GdkEvent* event, bool) // positive side of zero double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01; - // make sure we hit zero when passing through - if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) { - cy = zero_gain_y; - } - if (_x_constrained) { cx = _fixed_grab_x; } @@ -4017,19 +4379,31 @@ ControlPointDrag::motion (GdkEvent* event, bool) cy = max (0.0, cy); cy = min ((double) _point->line().height(), cy); - framepos_t cx_frames = _editor->pixel_to_sample (cx); + // make sure we hit zero when passing through + if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) { + cy = zero_gain_y; + } + + framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state); - if (!_x_constrained) { + if (!_x_constrained && need_snap) { _editor->snap_to_with_modifier (cx_frames, event); } + cx_frames -= snap_delta (event->button.state); cx_frames = min (cx_frames, _point->line().maximum_time()); float const fraction = 1.0 - (cy / _point->line().height()); - _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index); + if (first_motion) { + float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height()); + _editor->begin_reversible_command (_("automation event move")); + _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction); + } + pair result; + result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index); - show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction)); + show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second)); } void @@ -4038,17 +4412,14 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred) if (!movement_occurred) { /* just a click */ - - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) { _editor->reset_point_selection (); } } else { - motion (event, false); + _point->line().end_drag (_pushing, _final_index); + _editor->commit_reversible_command (); } - - _point->line().end_drag (_pushing, _final_index); - _editor->commit_reversible_command (); } void @@ -4070,9 +4441,13 @@ ControlPointDrag::active (Editing::MouseMode m) } LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i) - : Drag (e, i), - _line (0), - _cumulative_y_drag (0) + : Drag (e, i) + , _line (0) + , _fixed_grab_x (0.0) + , _fixed_grab_y (0.0) + , _cumulative_y_drag (0) + , _before (0) + , _after (0) { DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n"); } @@ -4089,41 +4464,41 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) origin, and ditto for y. */ - double cx = event->button.x; - double cy = event->button.y; - - _line->parent_group().canvas_to_item (cx, cy); + double mx = event->button.x; + double my = event->button.y; - framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel); + _line->grab_item().canvas_to_item (mx, my); - uint32_t before; - uint32_t after; + framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel); - if (!_line->control_points_adjacent (frame_within_region, before, after)) { + if (!_line->control_points_adjacent (frame_within_region, _before, _after)) { /* no adjacent points */ return; } Drag::start_grab (event, _editor->cursors()->fader); - /* store grab start in parent frame */ + /* store grab start in item frame */ + double const bx = _line->nth (_before)->get_x(); + double const ax = _line->nth (_after)->get_x(); + double const click_ratio = (ax - mx) / (ax - bx); + + double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio))); - _fixed_grab_x = cx; + _fixed_grab_x = mx; _fixed_grab_y = cy; double fraction = 1.0 - (cy / _line->height()); - _line->start_drag_line (before, after, fraction); - show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction)); } void -LineDrag::motion (GdkEvent* event, bool) +LineDrag::motion (GdkEvent* event, bool first_move) { double dy = current_pointer_y() - last_pointer_y(); - if (event->button.state & Keyboard::SecondaryModifier) { + if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) { dy *= 0.1; } @@ -4137,32 +4512,49 @@ LineDrag::motion (GdkEvent* event, bool) double const fraction = 1.0 - (cy / _line->height()); uint32_t ignored; + if (first_move) { + float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height()); + + _editor->begin_reversible_command (_("automation range move")); + _line->start_drag_line (_before, _after, initial_fraction); + } + /* we are ignoring x position for this drag, so we can just pass in anything */ - _line->drag_motion (0, fraction, true, false, ignored); + pair result; - show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction)); + result = _line->drag_motion (0, fraction, true, false, ignored); + show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second)); } void -LineDrag::finished (GdkEvent* event, bool movement_occured) +LineDrag::finished (GdkEvent* event, bool movement_occurred) { - if (movement_occured) { + if (movement_occurred) { motion (event, false); _line->end_drag (false, 0); + _editor->commit_reversible_command (); } else { /* add a new control point on the line */ AutomationTimeAxisView* atv; - _line->end_drag (false, 0); - if ((atv = dynamic_cast(_editor->clicked_axisview)) != 0) { - framepos_t where = _editor->window_event_sample (event, 0, 0); - atv->add_automation_event (event, where, event->button.y, false); + framepos_t where = grab_frame (); + + double cx = 0; + double cy = _fixed_grab_y; + + _line->grab_item().item_to_canvas (cx, cy); + + atv->add_automation_event (event, where, cy, false); + } else if (dynamic_cast(_editor->clicked_axisview) != 0) { + AudioRegionView* arv; + + if ((arv = dynamic_cast(_editor->clicked_regionview)) != 0) { + arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false); + } } } - - _editor->commit_reversible_command (); } void @@ -4174,7 +4566,11 @@ LineDrag::aborted (bool) FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i), _line (0), - _cumulative_x_drag (0) + _arv (0), + _region_view_grab_x (0.0), + _cumulative_x_drag (0), + _before (0.0), + _max_x (0) { DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n"); } @@ -4258,7 +4654,7 @@ void RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *) { Drag::start_grab (event); - show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid())); + show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid())); } void @@ -4269,10 +4665,10 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool) double y1; double y2; - framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()); + framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()); framepos_t grab = grab_frame (); - if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) { + if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) { _editor->snap_to_with_modifier (grab, event); } else { grab = raw_grab_frame (); @@ -4349,7 +4745,7 @@ RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress) framepos_t grab = grab_frame (); framepos_t lpf = last_pointer_frame (); - if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) { + if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) { grab = raw_grab_frame (); lpf = _editor->pixel_to_sample_from_event (last_pointer_x()); } @@ -4395,7 +4791,7 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred) /* MIDI track */ if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) { /* nothing selected */ - add_midi_region (mtv); + add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state)); do_deselect = false; } } @@ -4432,7 +4828,12 @@ TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { Drag::start_grab (event, cursor); - show_verbose_cursor_time (adjusted_current_frame (event)); + _editor->get_selection().add (_primary); + + framepos_t where = _primary->region()->position(); + setup_snap_delta (where); + + show_verbose_cursor_duration (where, adjusted_current_frame (event), 0); } void @@ -4444,40 +4845,51 @@ TimeFXDrag::motion (GdkEvent* event, bool) pair const tv = _editor->trackview_by_y_position (grab_y()); int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers(); - - framepos_t const pf = adjusted_current_frame (event); + framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state); + _editor->snap_to_with_modifier (pf, event); + pf -= snap_delta (event->button.state); if (pf > rv->region()->position()) { rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer); } - show_verbose_cursor_time (pf); + show_verbose_cursor_duration (_primary->region()->position(), pf, 0); } void -TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred) +TimeFXDrag::finished (GdkEvent* event, bool movement_occurred) { - _primary->get_time_axis_view().hide_timestretch (); + /* this may have been a single click, no drag. We still want the dialog + to show up in that case, so that the user can manually edit the + parameters for the timestretch. + */ - if (!movement_occurred) { - return; - } + float fraction = 1.0; - if (last_pointer_frame() < _primary->region()->position()) { - /* backwards drag of the left edge - not usable */ - return; - } + if (movement_occurred) { - framecnt_t newlen = last_pointer_frame() - _primary->region()->position(); + motion (event, false); + + _primary->get_time_axis_view().hide_timestretch (); + + framepos_t adjusted_frame_pos = adjusted_current_frame (event); - float percentage = (double) newlen / (double) _primary->region()->length(); + if (adjusted_frame_pos < _primary->region()->position()) { + /* backwards drag of the left edge - not usable */ + return; + } + + framecnt_t newlen = adjusted_frame_pos - _primary->region()->position(); + + fraction = (double) newlen / (double) _primary->region()->length(); #ifndef USE_RUBBERBAND - // Soundtouch uses percentage / 100 instead of normal (/ 1) - if (_primary->region()->data_type() == DataType::AUDIO) { - percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f; - } + // Soundtouch uses fraction / 100 instead of normal (/ 1) + if (_primary->region()->data_type() == DataType::AUDIO) { + fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f; + } #endif + } if (!_editor->get_selection().regions.empty()) { /* primary will already be included in the selection, and edit @@ -4486,7 +4898,7 @@ TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred) selection. */ - if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) { + if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) { error << _("An error occurred while executing time stretch operation") << endmsg; } } @@ -4706,7 +5118,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) TrackViewList grouped_add = new_selection; for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) { RouteTimeAxisView *n = dynamic_cast(*i); - if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) { + if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) { for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) { RouteTimeAxisView *check = dynamic_cast(*j); if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) ) @@ -4734,7 +5146,6 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) case SelectionStartTrim: - start = _editor->selection->time[_editor->clicked_selection].start; end = _editor->selection->time[_editor->clicked_selection].end; if (pending_position > end) { @@ -4747,7 +5158,6 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) case SelectionEndTrim: start = _editor->selection->time[_editor->clicked_selection].start; - end = _editor->selection->time[_editor->clicked_selection].end; if (pending_position < start) { end = start; @@ -4809,16 +5219,22 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred) /* XXX what if its a music time selection? */ if (s) { - if ( s->get_play_range() && s->transport_rolling() ) { + if (s->get_play_range() && s->transport_rolling()) { s->request_play_range (&_editor->selection->time, true); - } else { - if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) { + } else if (!s->config.get_external_sync()) { + if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) { if (_operation == SelectionEndTrim) _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame()); else s->request_locate (_editor->get_selection().time.start()); } } + + if (_editor->get_selection().time.length() != 0) { + s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame()); + } else { + s->clear_range_selection (); + } } } else { @@ -4874,11 +5290,11 @@ RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operat _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, - physical_screen_height (_editor->get_window()))); + physical_screen_height (_editor->current_toplevel()->get_window()))); _drag_rect->hide (); - _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect")); - _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect")); + _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect")); + _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect")); } RangeMarkerBarDrag::~RangeMarkerBarDrag() @@ -4909,7 +5325,7 @@ RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *) case CreateTransportMarker: case CreateCDMarker: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) { _copy = true; } else { _copy = false; @@ -5096,9 +5512,9 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred) } void -RangeMarkerBarDrag::aborted (bool movement_occured) +RangeMarkerBarDrag::aborted (bool movement_occurred) { - if (movement_occured) { + if (movement_occurred) { _drag_rect->hide (); } } @@ -5117,6 +5533,7 @@ NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) , _cumulative_dx (0) , _cumulative_dy (0) + , _was_selected (false) { DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n"); @@ -5130,6 +5547,7 @@ void NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *) { Drag::start_grab (event); + setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ())); if (!(_was_selected = _primary->selected())) { @@ -5146,41 +5564,73 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *) if (add) { _region->note_selected (_primary, true); } else { + _editor->get_selection().clear_points(); _region->unique_select (_primary); } - - _editor->begin_reversible_selection_op(X_("Select Note Press")); - _editor->commit_reversible_selection_op(); } } } /** @return Current total drag x change in frames */ frameoffset_t -NoteDrag::total_dx () const +NoteDrag::total_dx (const guint state) const { + if (_x_constrained) { + return 0; + } + TempoMap& map (_editor->session()->tempo_map()); + /* dx in frames */ frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x()); /* primary note time */ - frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ()); + double const quarter_note_start = map.quarter_note_at_beat (_region->region()->beat() - _region->midi_region()->start_beats().to_double()); + frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double()); /* new time of the primary note in session frames */ - frameoffset_t st = n + dx; + frameoffset_t st = n + dx + snap_delta (state); framepos_t const rp = _region->region()->position (); /* prevent the note being dragged earlier than the region's position */ st = max (st, rp); - /* snap and return corresponding delta */ - return _region->snap_frame_to_frame (st - rp) + rp - n; + /* possibly snap and return corresponding delta */ + + bool snap = true; + + if (ArdourKeyboard::indicates_snap (state)) { + if (_editor->snap_mode () != SnapOff) { + snap = false; + } + } else { + if (_editor->snap_mode () == SnapOff) { + snap = false; + /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */ + if (ArdourKeyboard::indicates_snap_delta (state)) { + snap = true; + } + } + } + + frameoffset_t ret; + if (snap) { + bool const ensure_snap = _editor->snap_mode () != SnapMagnetic; + ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state); + } else { + ret = st - n - snap_delta (state); + } + return ret; } /** @return Current total drag y change in note number */ int8_t NoteDrag::total_dy () const { + if (_y_constrained) { + return 0; + } + MidiStreamView* msv = _region->midi_stream_view (); double const y = _region->midi_view()->y_position (); /* new current note */ @@ -5193,15 +5643,15 @@ NoteDrag::total_dy () const } void -NoteDrag::motion (GdkEvent *, bool) +NoteDrag::motion (GdkEvent * event, bool) { /* Total change in x and y since the start of the drag */ - frameoffset_t const dx = total_dx (); + frameoffset_t const dx = total_dx (event->button.state); int8_t const dy = total_dy (); /* Now work out what we have to do to the note canvas items to set this new drag delta */ - double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx; - double const tdy = -dy * _note_height - _cumulative_dy; + double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx; + double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy; if (tdx || tdy) { _cumulative_dx += tdx; @@ -5209,21 +5659,19 @@ NoteDrag::motion (GdkEvent *, bool) int8_t note_delta = total_dy(); - _region->move_selection (tdx, tdy, note_delta); - - /* the new note value may be the same as the old one, but we - * don't know what that means because the selection may have - * involved more than one note and we might be doing something - * odd with them. so show the note value anyway, always. - */ + if (tdx || tdy) { + _region->move_selection (tdx, tdy, note_delta); - char buf[12]; - uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127); + /* the new note value may be the same as the old one, but we + * don't know what that means because the selection may have + * involved more than one note and we might be doing something + * odd with them. so show the note value anyway, always. + */ - snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(), - (int) floor ((double)new_note)); + uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127); - show_verbose_cursor_text (buf); + _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note); + } } } @@ -5233,7 +5681,7 @@ NoteDrag::finished (GdkEvent* ev, bool moved) if (!moved) { /* no motion - select note */ - if (_editor->current_mouse_mode() == Editing::MouseObject || + if (_editor->current_mouse_mode() == Editing::MouseContent || _editor->current_mouse_mode() == Editing::MouseDraw) { bool changed = false; @@ -5243,12 +5691,17 @@ NoteDrag::finished (GdkEvent* ev, bool moved) if (add) { _region->note_deselected (_primary); changed = true; + } else { + _editor->get_selection().clear_points(); + _region->unique_select (_primary); + changed = true; } } else { bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier); bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier); if (!extend && !add && _region->selection_size() > 1) { + _editor->get_selection().clear_points(); _region->unique_select (_primary); changed = true; } else if (extend) { @@ -5256,6 +5709,8 @@ NoteDrag::finished (GdkEvent* ev, bool moved) changed = true; } else { /* it was added during button press */ + changed = true; + } } @@ -5265,7 +5720,7 @@ NoteDrag::finished (GdkEvent* ev, bool moved) } } } else { - _region->note_dropped (_primary, total_dx(), total_dy()); + _region->note_dropped (_primary, total_dx (ev->button.state), total_dy()); } } @@ -5384,123 +5839,142 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) } } - } else { + } - for (list::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) { + if (_nothing_to_drag) { + return; + } +} - framecnt_t const half = (i->start + i->end) / 2; +void +AutomationRangeDrag::motion (GdkEvent*, bool first_move) +{ + if (_nothing_to_drag && !first_move) { + return; + } - /* find the line that this audio range starts in */ - list::iterator j = _lines.begin(); - while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) { - ++j; - } + if (first_move) { + _editor->begin_reversible_command (_("automation range move")); + + if (!_ranges.empty()) { + + for (list::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) { + + framecnt_t const half = (i->start + i->end) / 2; - if (j != _lines.end()) { - boost::shared_ptr the_list = j->line->the_list (); + /* find the line that this audio range starts in */ + list::iterator j = _lines.begin(); + while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) { + ++j; + } + + if (j != _lines.end()) { + boost::shared_ptr the_list = j->line->the_list (); /* j is the line that this audio range starts in; fade into it; 64 samples length plucked out of thin air. */ - framepos_t a = i->start + 64; - if (a > half) { - a = half; - } + framepos_t a = i->start + 64; + if (a > half) { + a = half; + } - double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ()); - double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ()); + double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ()); + double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ()); - the_list->editor_add (p, value (the_list, p)); - the_list->editor_add (q, value (the_list, q)); - } + XMLNode &before = the_list->get_state(); + bool const add_p = the_list->editor_add (p, value (the_list, p), false); + bool const add_q = the_list->editor_add (q, value (the_list, q), false); - /* same thing for the end */ + if (add_p || add_q) { + _editor->session()->add_command ( + new MementoCommand(*the_list.get (), &before, &the_list->get_state())); + } + } - j = _lines.begin(); - while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) { - ++j; - } + /* same thing for the end */ - if (j != _lines.end()) { - boost::shared_ptr the_list = j->line->the_list (); + j = _lines.begin(); + while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) { + ++j; + } - /* j is the line that this audio range starts in; fade out of it; - 64 samples length plucked out of thin air. - */ + if (j != _lines.end()) { + boost::shared_ptr the_list = j->line->the_list (); - framepos_t b = i->end - 64; - if (b < half) { - b = half; - } + /* j is the line that this audio range starts in; fade out of it; + 64 samples length plucked out of thin air. + */ - double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ()); - double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ()); + framepos_t b = i->end - 64; + if (b < half) { + b = half; + } + + double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ()); + double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ()); - the_list->editor_add (p, value (the_list, p)); - the_list->editor_add (q, value (the_list, q)); + XMLNode &before = the_list->get_state(); + bool const add_p = the_list->editor_add (p, value (the_list, p), false); + bool const add_q = the_list->editor_add (q, value (the_list, q), false); + + if (add_p || add_q) { + _editor->session()->add_command ( + new MementoCommand(*the_list.get (), &before, &the_list->get_state())); + } + } } - } - _nothing_to_drag = true; + _nothing_to_drag = true; - /* Find all the points that should be dragged and put them in the relevant - points lists in the Line structs. - */ + /* Find all the points that should be dragged and put them in the relevant + points lists in the Line structs. + */ - for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { + for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - uint32_t const N = i->line->npoints (); - for (uint32_t j = 0; j < N; ++j) { + uint32_t const N = i->line->npoints (); + for (uint32_t j = 0; j < N; ++j) { - /* here's a control point on this line */ - ControlPoint* p = i->line->nth (j); - double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b (); + /* here's a control point on this line */ + ControlPoint* p = i->line->nth (j); + double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b (); - /* see if it's inside a range */ - list::const_iterator k = _ranges.begin (); - while (k != _ranges.end() && (k->start >= w || k->end <= w)) { - ++k; - } + /* see if it's inside a range */ + list::const_iterator k = _ranges.begin (); + while (k != _ranges.end() && (k->start >= w || k->end <= w)) { + ++k; + } - if (k != _ranges.end()) { - /* dragging this point */ - _nothing_to_drag = false; - i->points.push_back (p); + if (k != _ranges.end()) { + /* dragging this point */ + _nothing_to_drag = false; + i->points.push_back (p); + } } } } - } - if (_nothing_to_drag) { - return; - } - - for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state); - } -} - -void -AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/) -{ - if (_nothing_to_drag) { - return; + for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { + i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state); + } } for (list::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 result; uint32_t ignored; - l->line->drag_motion (0, f, true, false, ignored); - show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f)); + 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)); } } void -AutomationRangeDrag::finished (GdkEvent* event, bool) +AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred) { - if (_nothing_to_drag) { + if (_nothing_to_drag || !motion_occurred) { return; } @@ -5564,6 +6038,9 @@ void PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred) { if (!movement_occurred) { + if (was_double_click()) { + _region_view->edit_patch_change (_patch_change); + } return; } @@ -5708,18 +6185,33 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) Drag::start_grab (event, cursor); _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ()); + TempoMap& map (_editor->session()->tempo_map()); - framepos_t pf = _drags->current_pointer_frame (); - framecnt_t const g = grid_frames (pf); + const framepos_t pf = _drags->current_pointer_frame (); + const int32_t divisions = _editor->get_grid_music_divisions (event->button.state); - /* Hack so that we always snap to the note that we are over, instead of snapping - to the next one if we're more than halfway through the one we're over. - */ - if (_editor->snap_mode() == SnapNormal && pf > g / 2) { - pf -= g / 2; + double eqaf = map.exact_qn_at_frame (pf, divisions); + + if (divisions != 0) { + bool success = false; + Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf); + if (!success) { + grid_beats = Evoral::Beats(1); + } + + const double qaf = map.quarter_note_at_frame (pf); + + /* Hack so that we always snap to the note that we are over, instead of snapping + to the next one if we're more than halfway through the one we're over. + */ + + const double rem = eqaf - qaf; + if (rem >= 0.0 && eqaf - grid_beats.to_double() > _region_view->region()->pulse() * 4.0) { + eqaf -= grid_beats.to_double(); + } } - _note[0] = adjusted_frame (pf, event) - _region_view->region()->position (); + _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position(); _note[1] = _note[0]; MidiStreamView* sv = _region_view->midi_stream_view (); @@ -5743,26 +6235,28 @@ NoteCreateDrag::motion (GdkEvent* event, bool) } void -NoteCreateDrag::finished (GdkEvent*, bool had_movement) +NoteCreateDrag::finished (GdkEvent* ev, bool had_movement) { if (!had_movement) { return; } framepos_t const start = min (_note[0], _note[1]); + framepos_t const start_sess_rel = start + _region_view->region()->position(); framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1])); framecnt_t const g = grid_frames (start); Evoral::Beats const one_tick = Evoral::Beats::ticks(1); - if (_editor->snap_mode() == SnapNormal && length < g) { + if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) { length = g; } - Evoral::Beats length_beats = max ( - one_tick, _region_view->region_frames_to_region_beats (length) - one_tick); + TempoMap& map (_editor->session()->tempo_map()); + const double qn_length = map.quarter_note_at_frame (start_sess_rel + length) - map.quarter_note_at_frame (start_sess_rel); - _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false); + Evoral::Beats qn_length_beats = max (one_tick, Evoral::Beats (qn_length)); + _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false); } double @@ -5898,7 +6392,7 @@ RegionCutDrag::motion (GdkEvent*, bool) } void -RegionCutDrag::finished (GdkEvent*, bool) +RegionCutDrag::finished (GdkEvent* event, bool) { _editor->get_track_canvas()->canvas()->re_enter(); @@ -5912,7 +6406,7 @@ RegionCutDrag::finished (GdkEvent*, bool) return; } - _editor->split_regions_at (pos, rs); + _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state)); } void