X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_drag.cc;h=018c8c1f382f740506a5b3d566f278988dc6bdf7;hb=4c4061359cb33011e3acab016e8e604cd413a93d;hp=0877a8b4bece9185ba597f640c58833e598969c0;hpb=46c83693284ece4a732d26e62113ea4ac584d539;p=ardour.git diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 0877a8b4be..018c8c1f38 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) { } @@ -214,19 +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) { } @@ -251,6 +262,7 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor) { /* 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); @@ -403,21 +415,21 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll) _initially_vertical = false; } /** check constraints for this drag. - * Note that the current convention is to use "contains" for + * 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 (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) { + if (_constraint_pressed) { _x_constrained = false; _y_constrained = true; } else { _x_constrained = true; _y_constrained = false; } - } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) { + } else if (_constraint_pressed) { // if dragging normally, the motion is constrained to the first direction of movement. if (_initially_vertical) { _x_constrained = true; @@ -454,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; @@ -500,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(); } }; @@ -537,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); @@ -590,9 +598,33 @@ RegionDrag::find_time_axis_view (TimeAxisView* t) const return i; } +/** determines if @pos is the closest frame to an exact musical division when using SnapMagnetic. + * (SnapMagnetic may snap to an exact division or no division at all). + * this is a hotfix for musical region position/trim. we should really + * deliver musical divisors when snapping magnetically to avoid this. +*/ +int32_t +RegionDrag::current_music_divisor (framepos_t pos, int32_t button_state) +{ + int32_t divisions = _editor->get_grid_music_divisions (button_state); + + if (_editor->snap_mode() == Editing::SnapMagnetic && !ArdourKeyboard::indicates_snap (button_state)) { + TempoMap& tmap (_editor->session()->tempo_map()); + const framepos_t exact_qn_pos = tmap.frame_at_quarter_note (tmap.exact_qn_at_frame (pos, divisions)); + + if (pos != exact_qn_pos) { + /* magnetic has not snapped */ + divisions = 0; + } + } + + return divisions; +} + 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) @@ -618,6 +650,10 @@ RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) _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; @@ -630,7 +666,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_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true); + *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false); framepos_t sync_frame; framecnt_t sync_offset; @@ -642,11 +678,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) - snap_delta (event->button.state); + *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd; } else { *pending_region_position = _last_frame_position; @@ -831,7 +868,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) @@ -876,7 +913,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.. */ @@ -1008,7 +1045,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; } @@ -1085,6 +1122,10 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) y_delta = yposition - rv->get_canvas_group()->canvas_origin().y; } + MidiRegionView* mrv = dynamic_cast(rv); + if (mrv) { + mrv->apply_note_range (60, 71, true); + } } else { /* The TimeAxisView that this region is now over */ @@ -1146,6 +1187,14 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y; } + + MidiRegionView* mrv = dynamic_cast(rv); + if (mrv) { + MidiStreamView* msv; + if ((msv = dynamic_cast (current_tv->view())) != 0) { + mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true); + } + } } /* Now move the region view */ @@ -1175,7 +1224,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 @@ -1244,8 +1293,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 + , current_music_divisor (original->position(), 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. @@ -1352,7 +1401,8 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred) finished_copy ( changed_position, changed_tracks, - drag_delta + drag_delta, + ev->button.state ); } else { @@ -1360,7 +1410,8 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred) finished_no_copy ( changed_position, changed_tracks, - drag_delta + drag_delta, + ev->button.state ); } @@ -1382,21 +1433,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; @@ -1405,7 +1458,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; @@ -1430,7 +1483,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; } @@ -1462,7 +1515,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, current_music_divisor (where, ev_state)); + if (new_view != 0) { new_views.push_back (new_view); } @@ -1496,7 +1551,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; @@ -1508,15 +1564,23 @@ RegionMoveDrag::finished_no_copy ( typedef map, RouteTimeAxisView*> PlaylistMapping; PlaylistMapping playlist_mapping; + std::set > uniq; for (list::const_iterator i = _views.begin(); i != _views.end(); ) { 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; + } + + if (uniq.find (rv->region()) != uniq.end()) { + /* prevent duplicate moves when selecting regions from shared playlists */ ++i; continue; } + uniq.insert(rv->region()); if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) { /* dragged to drop zone */ @@ -1557,7 +1621,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, current_music_divisor (where, ev_state) ); if (new_view == 0) { @@ -1617,7 +1682,7 @@ RegionMoveDrag::finished_no_copy ( playlist->freeze (); } - rv->region()->set_position (where); + rv->region()->set_position (where, current_music_divisor (where, ev_state)); _editor->session()->add_command (new StatefulDiffCommand (rv->region())); } @@ -1720,7 +1785,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 (); @@ -1737,8 +1803,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); @@ -1823,6 +1888,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"); @@ -1861,7 +1927,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()); @@ -2287,13 +2353,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 @@ -2304,18 +2371,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(); } } @@ -2334,6 +2402,7 @@ NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i) , region (0) , relative (false) , at_front (true) + , _was_selected (false) , _snap_delta (0) { DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n"); @@ -2375,10 +2444,27 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/) /* 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. + */ + + bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier); + + 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); + } + } + } } void @@ -2436,6 +2522,46 @@ void 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; } @@ -2502,11 +2628,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)); } } @@ -2519,6 +2649,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() @@ -2549,6 +2689,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; @@ -2595,6 +2739,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; @@ -2642,6 +2789,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) { @@ -2699,9 +2847,6 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*) 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_duration (region_start, region_end); @@ -2756,9 +2901,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) { @@ -2809,11 +2957,14 @@ TrimDrag::motion (GdkEvent* event, bool first_move) } } + int32_t divisions = current_music_divisor (adj_frame, event->button.state); 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 + , divisions); + if (changed && _preserve_fade_anchor) { AudioRegionView* arv = dynamic_cast (i->view); if (arv) { @@ -2831,7 +2982,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, divisions); if (changed && _preserve_fade_anchor) { AudioRegionView* arv = dynamic_cast (i->view); if (arv) { @@ -2962,14 +3113,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 (); } } @@ -2982,10 +3131,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) { @@ -3018,12 +3168,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 @@ -3042,22 +3197,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 ()); @@ -3065,27 +3206,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, _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 @@ -3098,35 +3273,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. @@ -3137,11 +3292,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; @@ -3149,12 +3305,17 @@ MeterMarkerDrag::aborted (bool moved) } TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) - : Drag (e, i), - _copy (c) + : Drag (e, i) + , _copy (c) + , _grab_bpm (0.0) + , 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(); + _grab_bpm = _real_section->note_types_per_minute(); assert (_marker); } @@ -3162,65 +3323,114 @@ 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()); + snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute()); TempoSection section (_marker->tempo()); _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) { + const int32_t divisions = _editor->get_grid_music_divisions (event->button.state); + _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 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 = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, 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); @@ -3228,34 +3438,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. @@ -3268,18 +3455,117 @@ 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; } } +BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i) + : Drag (e, i) + , _grab_qn (0.0) + , _tempo (0) + , before_state (0) +{ + 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)).note_types_per_minute() << "\n"; + sstr << "<" << fixed << setprecision(3) << _tempo->note_types_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); + } + + _grab_qn = map.quarter_note_at_beat (beat); + + _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn); + +} + +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_quarter_note (_grab_qn), pf); + } + ostringstream sstr; + sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n"; + sstr << "<" << fixed << setprecision(3) << _tempo->note_types_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"); } @@ -3291,6 +3577,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 (); @@ -3627,7 +3917,7 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred) if (!in_command) { _editor->begin_reversible_command (_("change fade out length")); - in_command = false; + in_command = true; } XMLNode &after = alist->get_state(); _editor->session()->add_command(new MementoCommand(*alist.get(), &before, &after)); @@ -3654,14 +3944,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 () @@ -3671,7 +3964,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); @@ -3698,6 +3991,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); @@ -3708,12 +4002,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); @@ -3737,11 +4032,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; } @@ -3795,8 +4093,9 @@ 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_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) { @@ -3828,17 +4127,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: @@ -3886,10 +4185,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); } @@ -3902,7 +4201,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); } @@ -3946,17 +4245,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: @@ -3964,6 +4265,11 @@ 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; } @@ -3996,6 +4302,10 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred) } else { location->set (((*x).location)->start(), ((*x).location)->end()); } + + if (location->is_session_range()) { + _editor->session()->set_end_is_free (false); + } } } @@ -4007,9 +4317,9 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred) } void -MarkerDrag::aborted (bool movement_occured) +MarkerDrag::aborted (bool movement_occurred) { - if (!movement_occured) { + if (!movement_occurred) { return; } @@ -4018,7 +4328,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); @@ -4037,9 +4347,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()); @@ -4059,7 +4373,7 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) // start the grab at the center of the control point so // the point doesn't 'jump' to the mouse after the first drag - _fixed_grab_x = _point->get_x(); + _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset()); _fixed_grab_y = _point->get_y(); framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x); @@ -4080,10 +4394,12 @@ 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 & ArdourKeyboard::fine_adjust_modifier ()) { + 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) @@ -4105,34 +4421,34 @@ ControlPointDrag::motion (GdkEvent* event, bool first_motion) _cumulative_x_drag = cx - _fixed_grab_x; _cumulative_y_drag = cy - _fixed_grab_y; + cx = max (0.0, cx); + cy = max (0.0, cy); + cy = min ((double) _point->line().height(), cy); + // 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; } - cx = max (0.0, cx); - cy = max (0.0, cy); - cy = min ((double) _point->line().height(), cy); - 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()); + cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset()); float const fraction = 1.0 - (cy / _point->line().height()); 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, fraction); + _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); - _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 @@ -4146,7 +4462,6 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred) } } else { - motion (event, false); _point->line().end_drag (_pushing, _final_index); _editor->commit_reversible_command (); } @@ -4173,6 +4488,8 @@ ControlPointDrag::active (Editing::MouseMode m) LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) , _line (0) + , _fixed_grab_x (0.0) + , _fixed_grab_y (0.0) , _cumulative_y_drag (0) , _before (0) , _after (0) @@ -4192,12 +4509,12 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) origin, and ditto for y. */ - double cx = event->button.x; - double cy = event->button.y; + double mx = event->button.x; + double my = event->button.y; - _line->parent_group().canvas_to_item (cx, cy); + _line->grab_item().canvas_to_item (mx, my); - framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel); + framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel); if (!_line->control_points_adjacent (frame_within_region, _before, _after)) { /* no adjacent points */ @@ -4206,9 +4523,14 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) 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()); @@ -4221,7 +4543,7 @@ LineDrag::motion (GdkEvent* event, bool first_move) { double dy = current_pointer_y() - last_pointer_y(); - if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) { + if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) { dy *= 0.1; } @@ -4236,20 +4558,23 @@ LineDrag::motion (GdkEvent* event, bool first_move) 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, fraction); + _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 (); @@ -4259,8 +4584,20 @@ LineDrag::finished (GdkEvent* event, bool movement_occured) AutomationTimeAxisView* atv; 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); + } } } } @@ -4274,7 +4611,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"); } @@ -4358,7 +4699,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 @@ -4369,10 +4710,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 (); @@ -4449,7 +4790,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()); } @@ -4495,7 +4836,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; } } @@ -4561,29 +4902,39 @@ TimeFXDrag::motion (GdkEvent* event, bool) } 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) { + + motion (event, false); + + _primary->get_time_axis_view().hide_timestretch (); + + framepos_t adjusted_frame_pos = adjusted_current_frame (event); + + if (adjusted_frame_pos < _primary->region()->position()) { + /* backwards drag of the left edge - not usable */ + return; + } - framecnt_t newlen = last_pointer_frame() - _primary->region()->position(); + framecnt_t newlen = adjusted_frame_pos - _primary->region()->position(); - float percentage = (double) newlen / (double) _primary->region()->length(); + 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 @@ -4592,7 +4943,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; } } @@ -4812,7 +5163,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()) ) @@ -4840,7 +5191,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) { @@ -4853,7 +5203,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; @@ -4917,8 +5266,8 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred) if (s) { 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 @@ -4932,7 +5281,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred) s->clear_range_selection (); } } - + } else { /* just a click, no pointer movement. */ @@ -4986,11 +5335,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() @@ -5208,9 +5557,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 (); } } @@ -5229,6 +5578,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"); @@ -5259,11 +5609,9 @@ 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(); } } } @@ -5272,11 +5620,17 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *) frameoffset_t 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 = _region->region()->quarter_note() - _region->midi_region()->start_beats(); + 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 + snap_delta (state); @@ -5306,7 +5660,8 @@ NoteDrag::total_dx (const guint state) const frameoffset_t ret; if (snap) { - ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state); + 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); } @@ -5317,15 +5672,19 @@ NoteDrag::total_dx (const guint state) const int8_t NoteDrag::total_dy () const { - MidiStreamView* msv = _region->midi_stream_view (); + if (_y_constrained) { + return 0; + } + double const y = _region->midi_view()->y_position (); /* new current note */ - uint8_t n = msv->y_to_note (current_pointer_y () - y); + uint8_t n = _region->y_to_note (current_pointer_y () - y); /* clamp */ + MidiStreamView* msv = _region->midi_stream_view (); n = max (msv->lowest_note(), n); n = min (msv->highest_note(), n); /* and work out delta */ - return n - msv->y_to_note (grab_y() - y); + return n - _region->y_to_note (grab_y() - y); } void @@ -5336,8 +5695,8 @@ NoteDrag::motion (GdkEvent * event, bool) 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; @@ -5345,21 +5704,19 @@ NoteDrag::motion (GdkEvent * event, bool) int8_t note_delta = total_dy(); - _region->move_selection (tdx, tdy, note_delta); + if (tdx || tdy) { + _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. - */ - - 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); + } } } @@ -5369,7 +5726,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; @@ -5379,12 +5736,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) { @@ -5392,6 +5754,8 @@ NoteDrag::finished (GdkEvent* ev, bool moved) changed = true; } else { /* it was added during button press */ + changed = true; + } } @@ -5520,108 +5884,123 @@ 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()) { - if (j != _lines.end()) { - boost::shared_ptr the_list = j->line->the_list (); + for (list::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) { + + framecnt_t const half = (i->start + i->end) / 2; + + /* 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), false); - the_list->editor_add (q, value (the_list, q), false); - } + 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. + */ + + 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 ()); + 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), false); - the_list->editor_add (q, value (the_list, q), false); + 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; - } -} - -void -AutomationRangeDrag::motion (GdkEvent*, bool first_move) -{ - if (_nothing_to_drag) { - return; - } - - if (first_move) { - _editor->begin_reversible_command (_("automation range move")); 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); } @@ -5630,9 +6009,10 @@ AutomationRangeDrag::motion (GdkEvent*, bool first_move) 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)); } } @@ -5703,6 +6083,9 @@ void PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred) { if (!movement_occurred) { + if (was_double_click()) { + _region_view->edit_patch_change (_patch_change); + } return; } @@ -5832,13 +6215,12 @@ NoteCreateDrag::~NoteCreateDrag () framecnt_t NoteCreateDrag::grid_frames (framepos_t t) const { - bool success; - Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t); - if (!success) { - grid_beats = Evoral::Beats(1); - } - return _region_view->region_beats_to_region_frames (grid_beats); + const Evoral::Beats grid_beats = _region_view->get_grid_beats (t); + const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t); + + return _region_view->region_beats_to_region_frames (t_beats + grid_beats) + - _region_view->region_beats_to_region_frames (t_beats); } void @@ -5847,25 +6229,38 @@ 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; + const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf); + + double eqaf = map.exact_qn_at_frame (pf, divisions); + + if (divisions != 0) { + + 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(); + } } - _note[0] = adjusted_frame (pf, event) - _region_view->region()->position (); - _note[1] = _note[0]; + _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position(); + /* minimum initial length is grid beats */ + _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position(); - MidiStreamView* sv = _region_view->midi_stream_view (); - double const x = _editor->sample_to_pixel (_note[0]); - double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y))); + double const x0 = _editor->sample_to_pixel (_note[0]); + double const x1 = _editor->sample_to_pixel (_note[1]); + double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y))); - _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ()))); + _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ()))); _drag_rect->set_outline_all (); _drag_rect->set_outline_color (0xffffff99); _drag_rect->set_fill_color (0xffffff66); @@ -5874,7 +6269,29 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) void NoteCreateDrag::motion (GdkEvent* event, bool) { - _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ()); + TempoMap& map (_editor->session()->tempo_map()); + const framepos_t pf = _drags->current_pointer_frame (); + const int32_t divisions = _editor->get_grid_music_divisions (event->button.state); + double eqaf = map.exact_qn_at_frame (pf, divisions); + + if (divisions != 0) { + + const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf); + + 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(); + } + + eqaf += grid_beats.to_double(); + } + _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ()); + double const x0 = _editor->sample_to_pixel (_note[0]); double const x1 = _editor->sample_to_pixel (_note[1]); _drag_rect->set_x0 (std::min(x0, x1)); @@ -5882,26 +6299,23 @@ NoteCreateDrag::motion (GdkEvent* event, bool) } void -NoteCreateDrag::finished (GdkEvent*, bool had_movement) +NoteCreateDrag::finished (GdkEvent* ev, bool had_movement) { - if (!had_movement) { - return; - } - + /* we create a note even if there was no movement */ framepos_t const start = min (_note[0], _note[1]); - framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1])); - + framepos_t const start_sess_rel = start + _region_view->region()->position(); + framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1]))); framecnt_t const g = grid_frames (start); - 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_notes_between_frames (start_sess_rel, start_sess_rel + length); + Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length)); - _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false); + _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false); } double @@ -5918,6 +6332,100 @@ NoteCreateDrag::aborted (bool) } +HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv) + : Drag (e, i) + , _region_view (rv) + , _last_pos (0) + , _last_y (0.0) +{ +} + +HitCreateDrag::~HitCreateDrag () +{ +} + +void +HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + Drag::start_grab (event, cursor); + + TempoMap& map (_editor->session()->tempo_map()); + + const framepos_t pf = _drags->current_pointer_frame (); + const int32_t divisions = _editor->get_grid_music_divisions (event->button.state); + + const double eqaf = map.exact_qn_at_frame (pf, divisions); + + boost::shared_ptr mr = _region_view->midi_region(); + + if (eqaf >= mr->quarter_note() + mr->length_beats()) { + return; + } + + const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position(); + const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y))); + + Evoral::Beats length = _region_view->get_grid_beats (pf); + + _region_view->create_note_at (start, y, length, event->button.state, false); + + _last_pos = start; + _last_y = y; +} + +void +HitCreateDrag::motion (GdkEvent* event, bool) +{ + TempoMap& map (_editor->session()->tempo_map()); + + const framepos_t pf = _drags->current_pointer_frame (); + const int32_t divisions = _editor->get_grid_music_divisions (event->button.state); + + if (divisions == 0) { + return; + } + + const double eqaf = map.exact_qn_at_frame (pf, divisions); + const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position (); + const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y))); + + if (_last_pos == start && y == _last_y) { + return; + } + + Evoral::Beats length = _region_view->get_grid_beats (pf); + + boost::shared_ptr mr = _region_view->midi_region(); + if (eqaf >= mr->quarter_note() + mr->length_beats()) { + return; + } + + _region_view->create_note_at (start, y, length, event->button.state, false); + + _last_pos = start; + _last_y = y; +} + +void +HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */) +{ + +} + +double +HitCreateDrag::y_to_region (double y) const +{ + double x = 0; + _region_view->get_canvas_group()->canvas_to_item (x, y); + return y; +} + +void +HitCreateDrag::aborted (bool) +{ + // umm.. +} + CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn) : Drag (e, i) , arv (rv) @@ -6020,6 +6528,7 @@ RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t po { line->set_position (pos); line->show (); + line->track_canvas_item().reparent (_editor->_drag_motion_group); } RegionCutDrag::~RegionCutDrag () @@ -6028,20 +6537,28 @@ RegionCutDrag::~RegionCutDrag () } void -RegionCutDrag::motion (GdkEvent*, bool) +RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) { - framepos_t where = _drags->current_pointer_frame(); - _editor->snap_to (where); + Drag::start_grab (event, c); + motion (event, false); +} - line->set_position (where); +void +RegionCutDrag::motion (GdkEvent* event, bool) +{ + framepos_t pos = _drags->current_pointer_frame(); + _editor->snap_to_with_modifier (pos, event); + + line->set_position (pos); } void -RegionCutDrag::finished (GdkEvent*, bool) +RegionCutDrag::finished (GdkEvent* event, bool) { _editor->get_track_canvas()->canvas()->re_enter(); framepos_t pos = _drags->current_pointer_frame(); + _editor->snap_to_with_modifier (pos, event); line->hide (); @@ -6051,10 +6568,64 @@ 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), + false); } void RegionCutDrag::aborted (bool) { } + +RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item) + : Drag (e, item, true) +{ +} + +void +RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) +{ + Drag::start_grab (event, c); + + framepos_t where = _editor->canvas_event_sample(event); + + _editor->_dragging_playhead = true; + + _editor->playhead_cursor->set_position (where); +} + +void +RulerZoomDrag::motion (GdkEvent* event, bool) +{ + framepos_t where = _editor->canvas_event_sample(event); + + _editor->playhead_cursor->set_position (where); + + const double movement_limit = 20.0; + const double scale = 1.08; + const double y_delta = last_pointer_y() - current_pointer_y(); + + if (y_delta > 0 && y_delta < movement_limit) { + _editor->temporal_zoom_step_mouse_focus_scale (true, scale); + } else if (y_delta < 0 && y_delta > -movement_limit) { + _editor->temporal_zoom_step_mouse_focus_scale (false, scale); + } +} + +void +RulerZoomDrag::finished (GdkEvent*, bool) +{ + _editor->_dragging_playhead = false; + + Session* s = _editor->session (); + if (s) { + s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling); + _editor->_pending_locate_request = true; + } + +} + +void +RulerZoomDrag::aborted (bool) +{ +}