X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=gtk2_ardour%2Feditor_drag.cc;h=018c8c1f382f740506a5b3d566f278988dc6bdf7;hb=4c4061359cb33011e3acab016e8e604cd413a93d;hp=63b22bcc1457b50a9e0341bcf5c30c4023d88646;hpb=24846f478bf6a44f3f0e117d12e08864f579ed23;p=ardour.git diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 63b22bcc14..018c8c1f38 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -598,6 +598,29 @@ 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) @@ -1099,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 */ @@ -1160,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 */ @@ -1259,7 +1294,7 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move) const boost::shared_ptr original = rv->region(); boost::shared_ptr region_copy = RegionFactory::create (original, true - , _editor->get_grid_music_divisions (event->button.state)); + , 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. @@ -1481,7 +1516,7 @@ 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, _editor->get_grid_music_divisions (ev_state)); + modified_playlists, current_music_divisor (where, ev_state)); if (new_view != 0) { new_views.push_back (new_view); @@ -1529,6 +1564,7 @@ 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; @@ -1539,6 +1575,13 @@ RegionMoveDrag::finished_no_copy ( 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 */ @@ -1579,7 +1622,7 @@ RegionMoveDrag::finished_no_copy ( RegionView* new_view = insert_region_into_playlist ( RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, - modified_playlists, _editor->get_grid_music_divisions (ev_state) + modified_playlists, current_music_divisor (where, ev_state) ); if (new_view == 0) { @@ -1639,7 +1682,7 @@ RegionMoveDrag::finished_no_copy ( playlist->freeze (); } - rv->region()->set_position (where, _editor->get_grid_music_divisions (ev_state)); + rv->region()->set_position (where, current_music_divisor (where, ev_state)); _editor->session()->add_command (new StatefulDiffCommand (rv->region())); } @@ -2914,12 +2957,13 @@ 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 - , _editor->get_grid_music_divisions (event->button.state)); + , divisions); if (changed && _preserve_fade_anchor) { AudioRegionView* arv = dynamic_cast (i->view); @@ -2938,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, _editor->get_grid_music_divisions (event->button.state)); + 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) { @@ -3190,7 +3234,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move) } 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()); + , beat, bbt, _real_section->position_lock_style()); if (!_real_section) { aborted (true); return; @@ -3263,6 +3307,7 @@ MeterMarkerDrag::aborted (bool moved) TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) : Drag (e, i) , _copy (c) + , _grab_bpm (0.0) , before_state (0) { DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n"); @@ -3270,6 +3315,7 @@ TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) _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); } @@ -3302,7 +3348,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) // 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()); @@ -3333,7 +3379,8 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) _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); + 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); } @@ -3348,9 +3395,8 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) 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); + 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()); @@ -3417,7 +3463,7 @@ TempoMarkerDrag::aborted (bool moved) BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) - , _pulse (0.0) + , _grab_qn (0.0) , _tempo (0) , before_state (0) { @@ -3433,8 +3479,8 @@ BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) _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(); + 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); } @@ -3457,9 +3503,9 @@ BBTRulerDrag::setup_pointer_frame_offset () beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4); } - _pulse = map.pulse_at_beat (beat); + _grab_qn = map.quarter_note_at_beat (beat); - _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse); + _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn); } @@ -3484,11 +3530,11 @@ BBTRulerDrag::motion (GdkEvent* event, bool first_move) if (ArdourKeyboard::indicates_constraint (event->button.state)) { /* adjust previous tempo to match pointer frame */ - _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse); + _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).beats_per_minute() << "\n"; - sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute(); + 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()); } @@ -5583,7 +5629,7 @@ NoteDrag::total_dx (const guint state) const frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x()); /* primary note time */ - double const quarter_note_start = (_region->region()->pulse() * 4.0) - _region->midi_region()->start_beats(); + 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 */ @@ -5630,15 +5676,15 @@ NoteDrag::total_dy () const return 0; } - MidiStreamView* msv = _region->midi_stream_view (); 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 @@ -6169,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 @@ -6189,14 +6234,11 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) const framepos_t pf = _drags->current_pointer_frame (); const int32_t divisions = _editor->get_grid_music_divisions (event->button.state); + const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf); + 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); @@ -6211,13 +6253,14 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) } _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position(); - _note[1] = _note[0]; + /* 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); @@ -6232,11 +6275,8 @@ NoteCreateDrag::motion (GdkEvent* event, bool) 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 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 @@ -6261,25 +6301,20 @@ NoteCreateDrag::motion (GdkEvent* event, bool) void 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]); framepos_t const start_sess_rel = start + _region_view->region()->position(); - framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1])); - + 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->get_grid_music_divisions (ev->button.state) != 0 && length < g) { length = g; } 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); + 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)); - 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); } @@ -6297,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) @@ -6399,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 () @@ -6407,12 +6537,19 @@ RegionCutDrag::~RegionCutDrag () } void -RegionCutDrag::motion (GdkEvent*, bool) +RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) +{ + Drag::start_grab (event, c); + motion (event, false); +} + +void +RegionCutDrag::motion (GdkEvent* event, bool) { - framepos_t where = _drags->current_pointer_frame(); - _editor->snap_to (where); + framepos_t pos = _drags->current_pointer_frame(); + _editor->snap_to_with_modifier (pos, event); - line->set_position (where); + line->set_position (pos); } void @@ -6421,6 +6558,7 @@ 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 (); @@ -6430,10 +6568,64 @@ RegionCutDrag::finished (GdkEvent* event, bool) return; } - _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state)); + _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) +{ +}