X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_drag.cc;h=8f992266e76499d223205784e84f1b9eda0791b4;hb=1c4d00e8b70785ce58f868645dad93afa1855193;hp=42765e1d8740095644e856313e59bfadda789f85;hpb=d5e09076ca69fda1402a12e383de7a7d9bc5f0ec;p=ardour.git diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 42765e1d87..8f992266e7 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -22,6 +22,7 @@ #endif #include +#include #include "pbd/memento_command.h" #include "pbd/basename.h" @@ -29,10 +30,13 @@ #include "gtkmm2ext/utils.h" -#include "ardour/session.h" +#include "ardour/audioengine.h" +#include "ardour/audioregion.h" #include "ardour/dB.h" -#include "ardour/region_factory.h" +#include "ardour/midi_region.h" #include "ardour/operations.h" +#include "ardour/region_factory.h" +#include "ardour/session.h" #include "editor.h" #include "i18n.h" @@ -203,19 +207,15 @@ Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t tim _item = new_item; if (cursor == 0) { - cursor = _editor->which_grabber_cursor (); + _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time); + } else { + _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time); } - - _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time); } void Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor) { - if (cursor == 0) { - cursor = _editor->which_grabber_cursor (); - } - // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained if (Keyboard::is_button2_event (&event->button)) { @@ -238,9 +238,14 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor) _last_pointer_x = _grab_x; _last_pointer_y = _grab_y; - _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, - *cursor, - event->button.time); + if (cursor == 0) { + _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, + event->button.time); + } else { + _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, + *cursor, + event->button.time); + } if (_editor->session() && _editor->session()->transport_rolling()) { _was_rolling = true; @@ -328,7 +333,9 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll) if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) { if (!from_autoscroll) { - _editor->maybe_autoscroll (true, allow_vertical_autoscroll ()); + bool const moving_left = _drags->current_pointer_x() < _last_pointer_x; + bool const moving_up = _drags->current_pointer_y() < _last_pointer_y; + _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up); } motion (event, _move_threshold_passed != old_move_threshold_passed); @@ -393,13 +400,29 @@ Drag::show_verbose_cursor_text (string const & text) ); } +boost::shared_ptr +Drag::add_midi_region (MidiTimeAxisView* view) +{ + 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); + } + + return boost::shared_ptr(); +} struct EditorOrderTimeAxisViewSorter { bool operator() (TimeAxisView* a, TimeAxisView* b) { RouteTimeAxisView* ra = dynamic_cast (a); RouteTimeAxisView* rb = dynamic_cast (b); assert (ra && rb); - return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor")); + return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort); } }; @@ -409,20 +432,19 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, listvisible_order_range (&_visible_y_low, &_visible_y_high); - /* Make a list of non-hidden tracks to refer to during the drag */ + /* Make a list of tracks to refer to during the drag; we include hidden tracks, + as some of the regions we are dragging may be on such tracks. + */ TrackViewList track_views = _editor->track_views; track_views.sort (EditorOrderTimeAxisViewSorter ()); for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - if (!(*i)->hidden()) { - - _time_axis_views.push_back (*i); - - TimeAxisView::Children children_list = (*i)->get_child_list (); - for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { - _time_axis_views.push_back (j->get()); - } + _time_axis_views.push_back (*i); + + TimeAxisView::Children children_list = (*i)->get_child_list (); + for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { + _time_axis_views.push_back (j->get()); } } @@ -433,7 +455,7 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ()); + pair const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ()); _last_pointer_time_axis_view = find_time_axis_view (tv.first); _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; } @@ -551,7 +575,7 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r } bool -RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const +RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const { for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { int const n = i->time_axis_view + delta_track; @@ -566,8 +590,12 @@ RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) cons return false; } - int const l = i->layer + delta_layer; - if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) { + double const l = i->layer + delta_layer; + + /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded' + mode to allow the user to place a region below another on layer 0. + */ + if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) { /* Off the top or bottom layer; note that we only refuse if the track hasn't changed. If it has, the layers will be munged later anyway, so it's ok. */ @@ -585,7 +613,11 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) assert (!_views.empty ()); /* Find the TimeAxisView that the pointer is now over */ - pair const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ()); + pair const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ()); + + if (first_move && tv.first->view()->layer_display() == Stacked) { + tv.first->view()->set_layer_display (Expanded); + } /* Bail early if we're not over a track */ RouteTimeAxisView* rtv = dynamic_cast (tv.first); @@ -598,7 +630,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) /* Here's the current pointer position in terms of time axis view and layer */ int const current_pointer_time_axis_view = find_time_axis_view (tv.first); - layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; + double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; /* Work out the change in x */ framepos_t pending_region_position; @@ -606,7 +638,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) /* Work out the change in y */ int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view; - int delta_layer = current_pointer_layer - _last_pointer_layer; + double delta_layer = current_pointer_layer - _last_pointer_layer; if (!y_movement_allowed (delta_time_axis_view, delta_layer)) { /* this y movement is not allowed, so do no y movement this time */ @@ -627,91 +659,64 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) RegionView* rv = i->view; - if (rv->region()->locked()) { + if (rv->region()->locked() || rv->region()->video_locked()) { continue; } if (first_move) { - /* here we are calculating the y distance from the - top of the first track view to the top of the region - area of the track view that we're working on */ - - /* this x value is just a dummy value so that we have something - to pass to i2w () */ - - double ix1 = 0; - - /* distance from the top of this track view to the region area - of our track view is always 1 */ - - double iy1 = 1; - - /* convert to world coordinates, ie distance from the top of - the ruler section */ - - rv->get_canvas_frame()->i2w (ix1, iy1); - - /* compensate for the ruler section and the vertical scrollbar position */ - iy1 += _editor->get_trackview_group_vertical_offset (); + rv->drag_start (); - // hide any dependent views - - rv->get_time_axis_view().hide_dependent_views (*rv); - - /* - reparent to a non scrolling group so that we can keep the - region selection above all time axis views. - reparenting means we have to move the rv as the two - parent groups have different coordinates. + /* Absolutely no idea why this is necessary, but it is; without + it, the region view disappears after the reparent. + */ + _editor->update_canvas_now (); + + /* Reparent to a non scrolling group so that we can keep the + region selection above all time axis views. + Reparenting means that we will have to move the region view + later, as the two parent groups have different coordinates. */ - rv->get_canvas_group()->property_y() = iy1 - 1; rv->get_canvas_group()->reparent (*(_editor->_region_motion_group)); - + rv->fake_set_opaque (true); } - /* Work out the change in y position of this region view */ - - double y_delta = 0; - /* If we have moved tracks, we'll fudge the layer delta so that the region gets moved back onto layer 0 on its new track; this avoids confusion when dragging regions from non-zero layers onto different tracks. */ - int this_delta_layer = delta_layer; + double this_delta_layer = delta_layer; if (delta_time_axis_view != 0) { this_delta_layer = - i->layer; } - /* Move this region to layer 0 on its old track */ - StreamView* lv = _time_axis_views[i->time_axis_view]->view (); - if (lv->layer_display() == Stacked) { - y_delta -= (lv->layers() - i->layer - 1) * lv->child_height (); - } + /* The TimeAxisView that this region is now on */ + TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view]; - /* Now move it to its right layer on the current track */ - StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view (); - if (cv->layer_display() == Stacked) { - y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height (); + /* Ensure it is moved from stacked -> expanded if appropriate */ + if (tv->view()->layer_display() == Stacked) { + tv->view()->set_layer_display (Expanded); } - - /* Move tracks */ - if (delta_time_axis_view > 0) { - for (int j = 0; j < delta_time_axis_view; ++j) { - y_delta += _time_axis_views[i->time_axis_view + j]->current_height (); - } - } else { - /* start by subtracting the height of the track above where we are now */ - for (int j = 1; j <= -delta_time_axis_view; ++j) { - y_delta -= _time_axis_views[i->time_axis_view - j]->current_height (); - } + + /* We're only allowed to go -ve in layer on Expanded views */ + if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) { + this_delta_layer = - i->layer; } - + /* Set height */ - rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ()); + rv->set_height (tv->view()->child_height ()); + + /* Update show/hidden status as the region view may have come from a hidden track, + or have moved to one. + */ + if (tv->hidden ()) { + rv->get_canvas_group()->hide (); + } else { + rv->get_canvas_group()->show (); + } /* Update the DraggingView */ i->time_axis_view += delta_time_axis_view; @@ -720,7 +725,28 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) if (_brushing) { _editor->mouse_brush_insert_region (rv, pending_region_position); } else { - rv->move (x_delta, y_delta); + double x = 0; + double y = 0; + + /* Get the y coordinate of the top of the track that this region is now on */ + tv->canvas_display()->i2w (x, y); + y += _editor->get_trackview_group_vertical_offset(); + + /* And adjust for the layer that it should be on */ + StreamView* cv = tv->view (); + switch (cv->layer_display ()) { + case Overlaid: + break; + case Stacked: + y += (cv->layers() - i->layer - 1) * cv->child_height (); + break; + case Expanded: + y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height (); + break; + } + + /* Now move the region view */ + rv->move (x_delta, y - rv->get_canvas_group()->property_y()); } } /* foreach region */ @@ -809,8 +835,24 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move) } void -RegionMoveDrag::finished (GdkEvent *, bool movement_occurred) +RegionMotionDrag::finished (GdkEvent *, bool) +{ + for (vector::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) { + if (!(*i)->view()) { + continue; + } + + if ((*i)->view()->layer_display() == Expanded) { + (*i)->view()->set_layer_display (Stacked); + } + } +} + +void +RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred) { + RegionMotionDrag::finished (ev, movement_occurred); + if (!movement_occurred) { /* just a click */ return; @@ -826,10 +868,18 @@ RegionMoveDrag::finished (GdkEvent *, bool movement_occurred) assert (!_views.empty ()); + /* We might have hidden region views so that they weren't visible during the drag + (when they have been reparented). Now everything can be shown again, as region + views are back in their track parent groups. + */ + for (list::iterator i = _views.begin(); i != _views.end(); ++i) { + i->view->get_canvas_group()->show (); + } + bool const changed_position = (_last_frame_position != _primary->region()->position()); bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view()); framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position; - + _editor->update_canvas_now (); if (_copy) { @@ -849,6 +899,8 @@ RegionMoveDrag::finished (GdkEvent *, bool movement_occurred) ); } + + _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start()); } void @@ -878,7 +930,7 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed /* insert the regions into their new playlists */ for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { - if (i->view->region()->locked()) { + if (i->view->region()->locked() || i->view->region()->video_locked()) { continue; } @@ -936,6 +988,7 @@ RegionMoveDrag::finished_no_copy ( RegionSelection new_views; PlaylistSet modified_playlists; PlaylistSet frozen_playlists; + set views_to_update; if (_brushing) { /* all changes were made during motion event handlers */ @@ -954,13 +1007,15 @@ RegionMoveDrag::finished_no_copy ( RegionView* rv = i->view; RouteTimeAxisView* const dest_rtv = dynamic_cast (_time_axis_views[i->time_axis_view]); - layer_t const dest_layer = i->layer; + double const dest_layer = i->layer; - if (rv->region()->locked()) { + if (rv->region()->locked() || rv->region()->video_locked()) { ++i; continue; } + views_to_update.insert (dest_rtv); + framepos_t where; if (changed_position && !_x_constrained) { @@ -1008,15 +1063,14 @@ RegionMoveDrag::finished_no_copy ( rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item()); rv->get_canvas_group()->property_y() = i->initial_y; - rv->get_time_axis_view().reveal_dependent_views (*rv); + rv->drag_end (); /* just change the model */ boost::shared_ptr playlist = dest_rtv->playlist(); - if (dest_rtv->view()->layer_display() == Stacked) { - rv->region()->set_layer (dest_layer); - rv->region()->set_pending_explicit_relayer (true); + if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) { + playlist->set_layer (rv->region(), dest_layer); } /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */ @@ -1088,6 +1142,17 @@ RegionMoveDrag::finished_no_copy ( add_stateful_diff_commands_for_playlists (modified_playlists); _editor->commit_reversible_command (); + + /* We have futzed with the layering of canvas items on our streamviews. + If any region changed layer, this will have resulted in the stream + views being asked to set up their region views, and all will be well. + If not, we might now have badly-ordered region views. Ask the StreamViews + involved to sort themselves out, just in case. + */ + + for (set::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) { + (*i)->view()->playlist_layered ((*i)->track ()); + } } /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary. @@ -1149,9 +1214,8 @@ RegionMoveDrag::insert_region_into_playlist ( dest_playlist->add_region (region, where); - if (dest_rtv->view()->layer_display() == Stacked) { - region->set_layer (dest_layer); - region->set_pending_explicit_relayer (true); + if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) { + dest_playlist->set_layer (region, dest_layer); } c.disconnect (); @@ -1200,6 +1264,12 @@ RegionMoveDrag::aborted (bool movement_occurred) void RegionMotionDrag::aborted (bool) { + for (vector::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) { + if ((*i)->view()->layer_display() == Expanded) { + (*i)->view()->set_layer_display (Stacked); + } + } + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { RegionView* rv = i->view; TimeAxisView* tv = &(rv->get_time_axis_view ()); @@ -1207,7 +1277,7 @@ RegionMotionDrag::aborted (bool) assert (rtv); rv->get_canvas_group()->reparent (*rtv->view()->canvas_item()); rv->get_canvas_group()->property_y() = 0; - rv->get_time_axis_view().reveal_dependent_views (*rv); + rv->drag_end (); rv->fake_set_opaque (false); rv->move (-_total_x_delta, 0); rv->set_height (rtv->view()->child_height ()); @@ -1307,13 +1377,8 @@ RegionSpliceDrag::motion (GdkEvent* event, bool) { /* Which trackview is this ? */ - pair const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ()); + pair const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ()); RouteTimeAxisView* tv = dynamic_cast (tvp.first); - layer_t layer = tvp.second; - - if (tv && tv->layer_display() == Overlaid) { - layer = 0; - } /* The region motion is only processed if the pointer is over an audio track. @@ -1400,7 +1465,7 @@ void RegionCreateDrag::motion (GdkEvent* event, bool first_move) { if (first_move) { - add_region(); + _region = add_midi_region (_view); _view->playlist()->freeze (); } else { if (_region) { @@ -1416,7 +1481,7 @@ RegionCreateDrag::motion (GdkEvent* event, bool first_move) place snapped notes at the start of the region. */ - framecnt_t const len = abs (f - grab_frame () - 1); + framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1); _region->set_length (len < 1 ? 1 : len); } } @@ -1426,29 +1491,10 @@ void RegionCreateDrag::finished (GdkEvent*, bool movement_occurred) { if (!movement_occurred) { - add_region (); + add_midi_region (_view); } else { _view->playlist()->thaw (); } - - if (_region) { - _editor->commit_reversible_command (); - } -} - -void -RegionCreateDrag::add_region () -{ - 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()); - _region = _view->add_region (grab_frame(), len, false); - } } void @@ -1546,37 +1592,178 @@ NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/) void NoteResizeDrag::aborted (bool) { - /* XXX: TODO */ + MidiRegionSelection& ms (_editor->get_selection().midi_regions); + for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) { + (*r)->abort_resizing (); + } +} + +AVDraggingView::AVDraggingView (RegionView* v) + : view (v) +{ + initial_position = v->region()->position (); } -RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i) +VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) { - DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n"); + DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n"); + + RegionSelection rs; + TrackViewList empty; + empty.clear(); + _editor->get_regions_after(rs, (framepos_t) 0, empty); + std::list views = rs.by_layer(); + + for (list::iterator i = views.begin(); i != views.end(); ++i) { + RegionView* rv = (*i); + if (!rv->region()->video_locked()) { + continue; + } + _views.push_back (AVDraggingView (rv)); + } +} + +void +VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*) +{ + Drag::start_grab (event); + if (_editor->session() == 0) { + return; + } + + _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset(); + _max_backwards_drag = ( + ARDOUR_UI::instance()->video_timeline->get_duration() + + ARDOUR_UI::instance()->video_timeline->get_offset() + - ceil(ARDOUR_UI::instance()->video_timeline->get_apv()) + ); + + for (list::iterator i = _views.begin(); i != _views.end(); ++i) { + if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) { + _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position); + } + } + DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag)); + + char buf[128]; + Timecode::Time timecode; + _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ ); + snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames); + _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10); + _editor->verbose_cursor()->show (); } void -RegionGainDrag::motion (GdkEvent* /*event*/, bool) +VideoTimeLineDrag::motion (GdkEvent* event, bool first_move) { + if (_editor->session() == 0) { + return; + } + if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) { + return; + } + framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset; + dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt); + + if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) { + dt = - _max_backwards_drag; + } + + ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt); + ARDOUR_UI::instance()->flush_videotimeline_cache(true); + + for (list::iterator i = _views.begin(); i != _views.end(); ++i) { + RegionView* rv = i->view; + DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt)); + if (first_move) { + rv->drag_start (); + _editor->update_canvas_now (); + rv->fake_set_opaque (true); + rv->region()->clear_changes (); + rv->region()->suspend_property_changes(); + } + rv->region()->set_position(i->initial_position + dt); + rv->region_changed(ARDOUR::Properties::position); + } + + const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset(); + Timecode::Time timecode; + Timecode::Time timediff; + char buf[128]; + _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ ); + _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ ); + snprintf (buf, sizeof (buf), + "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32 + "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32 + , _("Video Start:"), + (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames + , _("Diff:"), + (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames + ); + _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10); + _editor->verbose_cursor()->show (); } void -RegionGainDrag::finished (GdkEvent *, bool) +VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred) { + if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) { + return; + } + + if (!movement_occurred || ! _editor->session()) { + return; + } + + ARDOUR_UI::instance()->flush_videotimeline_cache(true); + + _editor->begin_reversible_command (_("Move Video")); + + XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state(); + ARDOUR_UI::instance()->video_timeline->save_undo(); + XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state(); + _editor->session()->add_command(new MementoCommand(*(ARDOUR_UI::instance()->video_timeline), &before, &after)); + + for (list::iterator i = _views.begin(); i != _views.end(); ++i) { + i->view->drag_end(); + i->view->fake_set_opaque (false); + i->view->region()->resume_property_changes (); + + _editor->session()->add_command (new StatefulDiffCommand (i->view->region())); + } + + _editor->session()->maybe_update_session_range( + std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0), + std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0) + ); + + _editor->commit_reversible_command (); + _editor->update_canvas_now (); } void -RegionGainDrag::aborted (bool) +VideoTimeLineDrag::aborted (bool) { - /* XXX: TODO */ + if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) { + return; + } + ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset); + ARDOUR_UI::instance()->flush_videotimeline_cache(true); + + for (list::iterator i = _views.begin(); i != _views.end(); ++i) { + i->view->region()->resume_property_changes (); + i->view->region()->set_position(i->initial_position); + } } -TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v) +TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v, bool preserve_fade_anchor) : RegionDrag (e, i, p, v) { DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n"); + _preserve_fade_anchor = preserve_fade_anchor; } void @@ -1677,6 +1864,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move) if (arv) { arv->temporarily_hide_envelope (); + arv->drag_start (); } boost::shared_ptr pl = rv->region()->playlist(); @@ -1697,13 +1885,41 @@ TrimDrag::motion (GdkEvent* event, bool first_move) switch (_operation) { case StartTrim: for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { - i->view->trim_front (i->initial_position + dt, non_overlap_trim); + bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim); + if (changed && _preserve_fade_anchor) { + AudioRegionView* arv = dynamic_cast (i->view); + if (arv) { + double distance; + double new_length; + framecnt_t len; + boost::shared_ptr ar (arv->audio_region()); + distance = _drags->current_pointer_x() - grab_x(); + len = ar->fade_in()->back()->when; + new_length = len - _editor->unit_to_frame (distance); + new_length = ar->verify_xfade_bounds (new_length, true /*START*/ ); + arv->reset_fade_in_shape_width (ar, new_length); //the grey shape + } + } } break; case EndTrim: for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { - i->view->trim_end (i->initial_end + dt, non_overlap_trim); + bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim); + if (changed && _preserve_fade_anchor) { + AudioRegionView* arv = dynamic_cast (i->view); + if (arv) { + double distance; + double new_length; + framecnt_t len; + boost::shared_ptr ar (arv->audio_region()); + distance = grab_x() - _drags->current_pointer_x(); + len = ar->fade_out()->back()->when; + new_length = len - _editor->unit_to_frame (distance); + new_length = ar->verify_xfade_bounds (new_length, false /*END*/ ); + arv->reset_fade_out_shape_width (ar, new_length); //the grey shape + } + } } break; @@ -1755,16 +1971,61 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred) if (movement_occurred) { motion (event, false); - /* This must happen before the region's StatefulDiffCommand is created, as it may - `correct' (ahem) the region's _start from being negative to being zero. It - needs to be zero in the undo record. - */ if (_operation == StartTrim) { for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { - i->view->trim_front_ending (); + { + /* This must happen before the region's StatefulDiffCommand is created, as it may + `correct' (ahem) the region's _start from being negative to being zero. It + needs to be zero in the undo record. + */ + i->view->trim_front_ending (); + } + if (_preserve_fade_anchor) { + AudioRegionView* arv = dynamic_cast (i->view); + if (arv) { + double distance; + double new_length; + framecnt_t len; + boost::shared_ptr ar (arv->audio_region()); + distance = _drags->current_pointer_x() - grab_x(); + len = ar->fade_in()->back()->when; + new_length = len - _editor->unit_to_frame (distance); + new_length = ar->verify_xfade_bounds (new_length, true /*START*/ ); + ar->set_fade_in_length(new_length); + } + } + } + } else if (_operation == EndTrim) { + for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { + if (_preserve_fade_anchor) { + AudioRegionView* arv = dynamic_cast (i->view); + if (arv) { + double distance; + double new_length; + framecnt_t len; + boost::shared_ptr ar (arv->audio_region()); + distance = _drags->current_pointer_x() - grab_x(); + len = ar->fade_out()->back()->when; + new_length = len - _editor->unit_to_frame (distance); + new_length = ar->verify_xfade_bounds (new_length, false /*END*/ ); + ar->set_fade_out_length(new_length); + } + } } } + if (!_views.empty()) { + if (_operation == StartTrim) { + _editor->maybe_locate_with_edit_preroll( + _views.begin()->view->region()->position()); + } + if (_operation == EndTrim) { + _editor->maybe_locate_with_edit_preroll( + _views.begin()->view->region()->position() + + _views.begin()->view->region()->length()); + } + } + if (!_editor->selection->selected (_primary)) { _primary->thaw_after_trim (); } else { @@ -1790,6 +2051,7 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred) } } } + for (set >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) { (*p)->thaw (); } @@ -1859,7 +2121,6 @@ MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) _copy (c) { DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n"); - _marker = reinterpret_cast (_item->get_data ("marker")); assert (_marker); } @@ -1867,35 +2128,7 @@ MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) void MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { - if (_copy) { - // create a dummy marker for visual representation of moving the copy. - // The actual copying is not done before we reach the finish callback. - char name[64]; - snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ()); - - MeterMarker* new_marker = new MeterMarker ( - *_editor, - *_editor->meter_group, - ARDOUR_UI::config()->canvasvar_MeterMarker.get(), - name, - *new MeterSection (_marker->meter()) - ); - - _item = &new_marker->the_item (); - _marker = new_marker; - - } else { - - MetricSection& section (_marker->meter()); - - if (!section.movable()) { - return; - } - - } - Drag::start_grab (event, cursor); - show_verbose_cursor_time (adjusted_current_frame(event)); } @@ -1906,12 +2139,46 @@ MeterMarkerDrag::setup_pointer_frame_offset () } void -MeterMarkerDrag::motion (GdkEvent* event, bool) +MeterMarkerDrag::motion (GdkEvent* event, bool first_move) { - framepos_t const pf = adjusted_current_frame (event); + if (first_move) { - _marker->set_position (pf); + // 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; + } + + char name[64]; + snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ()); + + _marker = new MeterMarker ( + *_editor, + *_editor->meter_group, + ARDOUR_UI::config()->canvasvar_MeterMarker.get(), + name, + *new MeterSection (_marker->meter()) + ); + + /* use the new marker for the grab */ + swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME); + + 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); + } + } + framepos_t const pf = adjusted_current_frame (event); + _marker->set_position (pf); show_verbose_cursor_time (pf); } @@ -1928,7 +2195,7 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred) 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(); @@ -1937,23 +2204,35 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred) _editor->session()->add_command(new MementoCommand(map, &before, &after)); _editor->commit_reversible_command (); - // delete the dummy marker we used for visual representation of copying. - // a new visual marker will show up automatically. - delete _marker; } else { _editor->begin_reversible_command (_("move meter mark")); - XMLNode &before = map.get_state(); - map.move_meter (_marker->meter(), when); + + /* 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, &after)); + _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. + delete _marker; } void -MeterMarkerDrag::aborted (bool) +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()); + // delete the dummy marker we used for visual representation while moving. + // a new visual marker will show up automatically. + delete _marker; + } } TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) @@ -1969,14 +2248,35 @@ TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) void TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { - if (_copy) { + Drag::start_grab (event, cursor); + show_verbose_cursor_time (adjusted_current_frame (event)); +} + +void +TempoMarkerDrag::setup_pointer_frame_offset () +{ + _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame(); +} +void +TempoMarkerDrag::motion (GdkEvent* event, bool first_move) +{ + 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. + char name[64]; snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute()); - TempoMarker* new_marker = new TempoMarker ( + TempoSection section (_marker->tempo()); + + _marker = new TempoMarker ( *_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), @@ -1984,25 +2284,18 @@ TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) *new TempoSection (_marker->tempo()) ); - _item = &new_marker->the_item (); - _marker = new_marker; + /* use the new marker for the grab */ + swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME); + 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); + } } - Drag::start_grab (event, cursor); - - show_verbose_cursor_time (adjusted_current_frame (event)); -} - -void -TempoMarkerDrag::setup_pointer_frame_offset () -{ - _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame(); -} - -void -TempoMarkerDrag::motion (GdkEvent* event, bool) -{ framepos_t const pf = adjusted_current_frame (event); _marker->set_position (pf); show_verbose_cursor_time (pf); @@ -2017,10 +2310,11 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) motion (event, false); + TempoMap& map (_editor->session()->tempo_map()); + framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0); Timecode::BBT_Time when; - TempoMap& map (_editor->session()->tempo_map()); - map.bbt_time (last_pointer_frame(), when); + map.bbt_time (beat_time, when); if (_copy == true) { _editor->begin_reversible_command (_("copy tempo mark")); @@ -2030,23 +2324,32 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) _editor->session()->add_command (new MementoCommand(map, &before, &after)); _editor->commit_reversible_command (); - // delete the dummy marker we used for visual representation of copying. - // a new visual marker will show up automatically. - delete _marker; } else { _editor->begin_reversible_command (_("move tempo mark")); - XMLNode &before = map.get_state(); - map.move_tempo (_marker->tempo(), when); + /* 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, &after)); + _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. + delete _marker; } void -TempoMarkerDrag::aborted (bool) +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. + delete _marker; + } } CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s) @@ -2099,9 +2402,20 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) s->cancel_audition (); } - s->request_suspend_timecode_transmission (); - while (!s->timecode_transmission_suspended ()) { - /* twiddle our thumbs */ + + if (AudioEngine::instance()->connected()) { + + /* do this only if we're the engine is connected + * because otherwise this request will never be + * serviced and we'll busy wait forever. likewise, + * notice if we are disconnected while waiting for the + * request to be serviced. + */ + + s->request_suspend_timecode_transmission (); + while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) { + /* twiddle our thumbs */ + } } } @@ -2111,39 +2425,6 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) void CursorDrag::motion (GdkEvent* event, bool) { - if (_drags->current_pointer_y() != last_pointer_y()) { - - /* zoom when we move the pointer up and down */ - - /* y range to operate over (pixels) */ - double const y_range = 512; - /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */ - double const scale_range = 4; - /* dead zone around the grab point in which to do no zooming (pixels) */ - double const dead_zone = 100; - - /* current dy */ - double dy = _drags->current_pointer_y() - grab_y(); - - if (dy < -dead_zone || dy > dead_zone) { - /* we are outside the dead zone; remove it from our calculation */ - if (dy < 0) { - dy += dead_zone; - } else { - dy -= dead_zone; - } - - /* get a number from -1 to 1 as dy ranges from -y_range to y_range */ - double udy = max (min (dy / y_range, 1.0), -1.0); - - /* and zoom, using playhead focus temporarily */ - Editing::ZoomFocus const zf = _editor->get_zoom_focus (); - _editor->set_zoom_focus (Editing::ZoomFocusPlayhead); - _editor->temporal_zoom (_grab_zoom * pow (scale_range, -udy)); - _editor->set_zoom_focus (zf); - } - } - framepos_t const adjusted_frame = adjusted_current_frame (event); if (adjusted_frame != last_pointer_frame()) { fake_locate (adjusted_frame); @@ -2198,8 +2479,6 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) boost::shared_ptr const r = arv->audio_region (); show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32); - - arv->show_fade_line((framepos_t) r->fade_in()->back()->when); } void @@ -2235,8 +2514,7 @@ FadeInDrag::motion (GdkEvent* event, bool) continue; } - tmp->reset_fade_in_shape_width (fade_length); - tmp->show_fade_line((framecnt_t) fade_length); + tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length); } show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32); @@ -2278,7 +2556,6 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred) tmp->audio_region()->set_fade_in_length (fade_length); tmp->audio_region()->set_fade_in_active (true); - tmp->hide_fade_line(); XMLNode &after = alist->get_state(); _editor->session()->add_command(new MementoCommand(*alist.get(), &before, &after)); @@ -2297,8 +2574,7 @@ FadeInDrag::aborted (bool) continue; } - tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when); - tmp->hide_fade_line(); + tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when); } } @@ -2317,8 +2593,6 @@ FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) boost::shared_ptr r = arv->audio_region (); show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame()); - - arv->show_fade_line(r->length() - r->fade_out()->back()->when); } void @@ -2356,8 +2630,7 @@ FadeOutDrag::motion (GdkEvent* event, bool) continue; } - tmp->reset_fade_out_shape_width (fade_length); - tmp->show_fade_line(region->length() - fade_length); + tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length); } show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame()); @@ -2401,7 +2674,6 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred) tmp->audio_region()->set_fade_out_length (fade_length); tmp->audio_region()->set_fade_out_active (true); - tmp->hide_fade_line(); XMLNode &after = alist->get_state(); _editor->session()->add_command(new MementoCommand(*alist.get(), &before, &after)); @@ -2420,8 +2692,7 @@ FadeOutDrag::aborted (bool) continue; } - tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when); - tmp->hide_fade_line(); + tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when); } } @@ -2439,11 +2710,18 @@ MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i) MarkerDrag::~MarkerDrag () { - for (list::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) { - delete *i; + for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) { + delete i->location; } } +MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m) +{ + location = new Location (*l); + markers.push_back (m); + move_both = false; +} + void MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { @@ -2469,7 +2747,7 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) switch (op) { case Selection::Toggle: - _editor->selection->toggle (_marker); + /* we toggle on the button release */ break; case Selection::Set: if (!_editor->selection->selected (_marker)) { @@ -2511,11 +2789,37 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) break; } - /* Set up copies for us to manipulate during the drag */ + /* Set up copies for us to manipulate during the drag + */ for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) { + Location* l = _editor->find_location_from_marker (*i, is_start); - _copied_locations.push_back (new Location (*l)); + + if (!l) { + continue; + } + + if (l->is_mark()) { + _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i)); + } else { + /* range: check that the other end of the range isn't + already there. + */ + CopiedLocationInfo::iterator x; + for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) { + if (*(*x).location == *l) { + break; + } + } + if (x == _copied_locations.end()) { + _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i)); + } else { + (*x).markers.push_back (*i); + (*x).move_both = true; + } + } + } } @@ -2533,33 +2837,31 @@ MarkerDrag::motion (GdkEvent* event, bool) framecnt_t f_delta = 0; bool is_start; bool move_both = false; - Marker* marker; Location *real_location; Location *copy_location = 0; framepos_t const newframe = adjusted_current_frame (event); - framepos_t next = newframe; if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) { move_both = true; } - MarkerSelection::iterator i; - list::iterator x; + CopiedLocationInfo::iterator x; /* find the marker we're dragging, and compute the delta */ - for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); - x != _copied_locations.end() && i != _editor->selection->markers.end(); - ++i, ++x) { + for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) { - copy_location = *x; - marker = *i; + copy_location = (*x).location; - if (marker == _marker) { + if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) { - if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) { + /* this marker is represented by this + * CopiedLocationMarkerInfo + */ + + if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) { /* que pasa ?? */ return; } @@ -2569,7 +2871,7 @@ MarkerDrag::motion (GdkEvent* event, bool) } else { - switch (marker->type()) { + switch (_marker->type()) { case Marker::SessionStart: case Marker::RangeStart: case Marker::LoopStart: @@ -2588,27 +2890,25 @@ MarkerDrag::motion (GdkEvent* event, bool) return; } } + break; } } - if (i == _editor->selection->markers.end()) { + if (x == _copied_locations.end()) { /* hmm, impossible - we didn't find the dragged marker */ return; } /* now move them all */ - for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); - x != _copied_locations.end() && i != _editor->selection->markers.end(); - ++i, ++x) { + for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) { - copy_location = *x; - marker = *i; + copy_location = x->location; /* call this to find out if its the start or end */ - if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) { + if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) { continue; } @@ -2623,13 +2923,13 @@ MarkerDrag::motion (GdkEvent* event, bool) copy_location->set_start (copy_location->start() + f_delta); } else { - + framepos_t new_start = copy_location->start() + f_delta; framepos_t new_end = copy_location->end() + f_delta; - + if (is_start) { // start-of-range marker - - if (move_both) { + + 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()) { @@ -2642,7 +2942,7 @@ MarkerDrag::motion (GdkEvent* event, bool) } else { // end marker - if (move_both) { + if (move_both || (*x).move_both) { copy_location->set_end (new_end); copy_location->set_start (new_start); } else if (new_end > copy_location->start()) { @@ -2656,12 +2956,20 @@ MarkerDrag::motion (GdkEvent* event, bool) } update_item (copy_location); - + + /* now lookup the actual GUI items used to display this + * location and move them to wherever the copy of the location + * is now. This means that the logic in ARDOUR::Location is + * still enforced, even though we are not (yet) modifying + * the real Location itself. + */ + Editor::LocationMarkers* lm = _editor->find_location_markers (real_location); if (lm) { lm->set_position (copy_location->start(), copy_location->end()); } + } assert (!_copied_locations.empty()); @@ -2692,6 +3000,10 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred) break; case Selection::Toggle: + /* we toggle on the button release, click only */ + _editor->selection->toggle (_marker); + break; + case Selection::Extend: case Selection::Add: break; @@ -2706,7 +3018,7 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred) XMLNode &before = _editor->session()->locations()->get_state(); MarkerSelection::iterator i; - list::iterator x; + CopiedLocationInfo::iterator x; bool is_start; for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); @@ -2722,9 +3034,9 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred) } if (location->is_mark()) { - location->set_start ((*x)->start()); + location->set_start (((*x).location)->start()); } else { - location->set ((*x)->start(), (*x)->end()); + location->set (((*x).location)->start(), ((*x).location)->end()); } } } @@ -2741,7 +3053,7 @@ MarkerDrag::aborted (bool) } void -MarkerDrag::update_item (Location* location) +MarkerDrag::update_item (Location*) { /* noop */ } @@ -2780,6 +3092,12 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) event->button.x + 10, event->button.y + 10); _editor->verbose_cursor()->show (); + + _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier); + + if (!_point->can_slide ()) { + _x_constrained = true; + } } void @@ -2831,9 +3149,7 @@ ControlPointDrag::motion (GdkEvent* event, bool) float const fraction = 1.0 - (cy / _point->line().height()); - bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier); - - _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push); + _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, _pushing, _final_index); _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction)); } @@ -2853,7 +3169,7 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred) motion (event, false); } - _point->line().end_drag (); + _point->line().end_drag (_pushing, _final_index); _editor->session()->commit_reversible_command (); } @@ -2944,17 +3260,10 @@ LineDrag::motion (GdkEvent* event, bool) cy = min ((double) _line->height(), cy); double const fraction = 1.0 - (cy / _line->height()); - - bool push; - - if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) { - push = false; - } else { - push = true; - } + uint32_t ignored; /* we are ignoring x position for this drag, so we can just pass in anything */ - _line->drag_motion (0, fraction, true, push); + _line->drag_motion (0, fraction, true, false, ignored); _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction)); } @@ -2963,7 +3272,7 @@ void LineDrag::finished (GdkEvent* event, bool) { motion (event, false); - _line->end_drag (); + _line->end_drag (false, 0); _editor->session()->commit_reversible_command (); } @@ -3058,6 +3367,7 @@ FeatureLineDrag::aborted (bool) RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) + , _vertical_only (false) { DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n"); } @@ -3109,15 +3419,51 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool) double x2 = _editor->frame_to_pixel (end); _editor->rubberband_rect->property_x1() = x1; + if (_vertical_only) { + /* fixed 10 pixel width */ + _editor->rubberband_rect->property_x2() = x1 + 10; + } else { + _editor->rubberband_rect->property_x2() = x2; + } + _editor->rubberband_rect->property_y1() = y1; - _editor->rubberband_rect->property_x2() = x2; _editor->rubberband_rect->property_y2() = y2; _editor->rubberband_rect->show(); _editor->rubberband_rect->raise_to_top(); show_verbose_cursor_time (pf); + + do_select_things (event, true); + } +} + +void +RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress) +{ + framepos_t x1; + framepos_t x2; + + if (grab_frame() < last_pointer_frame()) { + x1 = grab_frame (); + x2 = last_pointer_frame (); + } else { + x2 = grab_frame (); + x1 = last_pointer_frame (); + } + + double y1; + double y2; + + if (_drags->current_pointer_y() < grab_y()) { + y1 = _drags->current_pointer_y(); + y2 = grab_y(); + } else { + y2 = _drags->current_pointer_y(); + y1 = grab_y(); } + + select_things (event->button.state, x1, x2, y1, y2, drag_in_progress); } void @@ -3126,36 +3472,34 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred) if (movement_occurred) { motion (event, false); + do_select_things (event, false); - double y1,y2; - if (_drags->current_pointer_y() < grab_y()) { - y1 = _drags->current_pointer_y(); - y2 = grab_y(); - } else { - y2 = _drags->current_pointer_y(); - y1 = grab_y(); - } - + } else { - Selection::Operation op = ArdourKeyboard::selection_type (event->button.state); + /* just a click */ - _editor->begin_reversible_command (_("rubberband selection")); + bool do_deselect = true; + MidiTimeAxisView* mtv; - if (grab_frame() < last_pointer_frame()) { - _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false); - } else { - _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false); - } + if ((mtv = dynamic_cast(_editor->clicked_axisview)) != 0) { + /* MIDI track */ + if (_editor->selection->empty()) { + /* nothing selected */ + add_midi_region (mtv); + do_deselect = false; + } + } - _editor->commit_reversible_command (); + /* do not deselect if Primary or Tertiary (toggle-select or + * extend-select are pressed. + */ - } else { - if (!getenv("ARDOUR_SAE")) { - _editor->selection->clear_tracks(); + if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) && + !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) && + do_deselect) { + deselect_things (); } - _editor->selection->clear_regions(); - _editor->selection->clear_points (); - _editor->selection->clear_lines (); + } _editor->rubberband_rect->hide(); @@ -3185,11 +3529,16 @@ void TimeFXDrag::motion (GdkEvent* event, bool) { RegionView* rv = _primary; + StreamView* cv = rv->get_time_axis_view().view (); + + pair const tv = _editor->trackview_by_y_position (grab_y()); + int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; + int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers(); framepos_t const pf = adjusted_current_frame (event); if (pf > rv->region()->position()) { - rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf); + rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer); } show_verbose_cursor_time (pf); @@ -3220,13 +3569,16 @@ TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred) } #endif - // XXX how do timeFX on multiple regions ? - - RegionSelection rs; - rs.add (_primary); + if (!_editor->get_selection().regions.empty()) { + /* primary will already be included in the selection, and edit + group shared editing will propagate selection across + equivalent regions, so just use the current region + selection. + */ - if (_editor->time_stretch (rs, percentage) == -1) { - error << _("An error occurred while executing time stretch operation") << endmsg; + if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) { + error << _("An error occurred while executing time stretch operation") << endmsg; + } } } @@ -3272,11 +3624,18 @@ ScrubDrag::aborted (bool) SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o) : Drag (e, i) , _operation (o) - , _copy (false) + , _add (false) + , _extend (false) , _original_pointer_time_axis (-1) , _last_pointer_time_axis (-1) + , _time_selection_at_start (!_editor->get_selection().time.empty()) { DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n"); + + if (_time_selection_at_start) { + start_at_start = _editor->get_selection().time.start(); + end_at_start = _editor->get_selection().time.end_frame(); + } } void @@ -3290,10 +3649,10 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*) switch (_operation) { case CreateSelection: - if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { - _copy = true; + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) { + _add = true; } else { - _copy = false; + _add = false; } cursor = _editor->cursors()->selector; Drag::start_grab (event, cursor); @@ -3316,6 +3675,10 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*) case SelectionMove: Drag::start_grab (event, cursor); break; + + case SelectionExtend: + Drag::start_grab (event, cursor); + break; } if (_operation == SelectionMove) { @@ -3343,6 +3706,9 @@ SelectionDrag::setup_pointer_frame_offset () case SelectionEndTrim: _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end; break; + + case SelectionExtend: + break; } } @@ -3351,7 +3717,8 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) { framepos_t start = 0; framepos_t end = 0; - framecnt_t length; + framecnt_t length = 0; + framecnt_t distance = 0; pair const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()); if (pending_time_axis.first == 0) { @@ -3372,10 +3739,15 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) framepos_t grab = grab_frame (); if (first_move) { - _editor->snap_to (grab); + grab = adjusted_current_frame (event, false); + if (grab < pending_position) { + _editor->snap_to (grab, -1); + } else { + _editor->snap_to (grab, 1); + } } - if (pending_position < grab_frame()) { + if (pending_position < grab) { start = pending_position; end = grab; } else { @@ -3389,12 +3761,12 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) if (first_move) { - if (_copy) { + if (_add) { /* adding to the selection */ _editor->set_selected_track_as_side_effect (Selection::Add); //_editor->selection->add (_editor->clicked_axisview); _editor->clicked_selection = _editor->selection->add (start, end); - _copy = false; + _add = false; } else { /* new selection */ @@ -3462,20 +3834,23 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) } break; - + case SelectionMove: start = _editor->selection->time[_editor->clicked_selection].start; end = _editor->selection->time[_editor->clicked_selection].end; length = end - start; - + distance = pending_position - start; start = pending_position; _editor->snap_to (start); end = start + length; break; + + case SelectionExtend: + break; } if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) { @@ -3483,7 +3858,15 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) } if (start != end) { - _editor->selection->replace (_editor->clicked_selection, start, end); + switch (_operation) { + case SelectionMove: + if (_time_selection_at_start) { + _editor->selection->move_time (distance); + } + break; + default: + _editor->selection->replace (_editor->clicked_selection, start, end); + } } if (_operation == SelectionMove) { @@ -3506,22 +3889,43 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred) } /* XXX what if its a music time selection? */ - if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) { - s->request_play_range (&_editor->selection->time, true); + if (s) { + if ( s->get_play_range() && s->transport_rolling() ) { + s->request_play_range (&_editor->selection->time, true); + } else { + if (Config->get_always_play_range() && !s->transport_rolling()) { + s->request_locate (_editor->get_selection().time.start()); + } + } } - } else { - /* just a click, no pointer movement.*/ - - if (Keyboard::no_modifier_keys_pressed (&event->button)) { - _editor->selection->clear_time(); + /* just a click, no pointer movement. + */ + + if (_operation == SelectionExtend) { + if (_time_selection_at_start) { + framepos_t pos = adjusted_current_frame (event, false); + framepos_t start = min (pos, start_at_start); + framepos_t end = max (pos, end_at_start); + _editor->selection->set (start, end); + } + } else { + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) { + if (_editor->clicked_selection) { + _editor->selection->remove (_editor->clicked_selection); + } + } else { + if (!_editor->clicked_selection) { + _editor->selection->clear_time(); + } + } } if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) { _editor->selection->set (_editor->clicked_axisview); } - + if (s && s->get_play_range () && s->transport_rolling()) { s->request_stop (false, false); } @@ -3529,6 +3933,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred) } _editor->stop_canvas_autoscroll (); + _editor->clicked_selection = 0; } void @@ -3602,7 +4007,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move) crect = _editor->cd_marker_bar_drag_rect; break; default: - cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl; + error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg; return; break; } @@ -3814,9 +4219,9 @@ MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred) motion (event, false); if (grab_frame() < last_pointer_frame()) { - _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom"); + _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame()); } else { - _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom"); + _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame()); } } else { if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) { @@ -3881,43 +4286,33 @@ NoteDrag::total_dx () const frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x()); /* primary note time */ - frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ()); + frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ()); - /* new time of the primary note relative to the region position */ + /* new time of the primary note in session frames */ frameoffset_t st = n + dx; + framepos_t const rp = _region->region()->position (); + /* prevent the note being dragged earlier than the region's position */ - if (st < 0) { - st = 0; - } + st = max (st, rp); /* snap and return corresponding delta */ - return _region->snap_frame_to_frame (st) - n; + return _region->snap_frame_to_frame (st - rp) + rp - n; } -/** @return Current total drag y change in notes */ +/** @return Current total drag y change in note number */ int8_t NoteDrag::total_dy () const { - /* this is `backwards' to make increasing note number go in the right direction */ - double const dy = _drags->current_pointer_y() - grab_y(); - - /* dy in notes */ - int8_t ndy = 0; - - if (abs (dy) >= _note_height) { - if (dy > 0) { - ndy = (int8_t) ceil (dy / _note_height / 2.0); - } else { - ndy = (int8_t) floor (dy / _note_height / 2.0); - } - } - - /* more positive value = higher pitch and higher y-axis position on track, - which is the inverse of the X-centric geometric universe - */ - - return -ndy; + MidiStreamView* msv = _region->midi_stream_view (); + double const y = _region->midi_view()->y_position (); + /* new current note */ + uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y); + /* clamp */ + 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); } void @@ -3925,11 +4320,11 @@ NoteDrag::motion (GdkEvent *, bool) { /* Total change in x and y since the start of the drag */ frameoffset_t const dx = total_dx (); - int8_t const dy = -total_dy (); + 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->frame_to_unit (dx) - _cumulative_dx; - double const tdy = dy * _note_height - _cumulative_dy; + double const tdy = -dy * _note_height - _cumulative_dy; if (tdx || tdy) { _cumulative_dx += tdx; @@ -3939,9 +4334,17 @@ NoteDrag::motion (GdkEvent *, bool) _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]; - snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(), - (int) floor (_primary->note()->note() + note_delta)); + uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127); + + snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(), + (int) floor (new_note)); show_verbose_cursor_text (buf); } @@ -3951,8 +4354,11 @@ void NoteDrag::finished (GdkEvent* ev, bool moved) { if (!moved) { - if (_editor->current_mouse_mode() == Editing::MouseObject) { - + /* no motion - select note */ + + if (_editor->current_mouse_mode() == Editing::MouseObject || + _editor->current_mouse_mode() == Editing::MouseDraw) { + if (_was_selected) { bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier); if (add) { @@ -3982,31 +4388,49 @@ NoteDrag::aborted (bool) /* XXX: TODO */ } -AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list const & r) - : Drag (editor, item) +/** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */ +AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list const & r) + : Drag (editor, atv->base_item ()) , _ranges (r) , _nothing_to_drag (false) { DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n"); + y_origin = atv->y_position(); + setup (atv->lines ()); +} - _atav = reinterpret_cast (_item->get_data ("trackview")); - assert (_atav); +/** Make an AutomationRangeDrag for region gain lines */ +AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list const & r) + : Drag (editor, rv->get_canvas_group ()) + , _ranges (r) + , _nothing_to_drag (false) +{ + DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n"); - /* get all lines in the automation view */ - list > lines = _atav->lines (); + list > lines; + lines.push_back (rv->get_gain_line ()); + y_origin = rv->get_time_axis_view().y_position(); + setup (lines); +} - /* find those that overlap the ranges being dragged */ - list >::iterator i = lines.begin (); +/** @param lines AutomationLines to drag. + * @param offset Offset from the session start to the points in the AutomationLines. + */ +void +AutomationRangeDrag::setup (list > const & lines) +{ + /* find the lines that overlap the ranges being dragged */ + list >::const_iterator i = lines.begin (); while (i != lines.end ()) { - list >::iterator j = i; + list >::const_iterator j = i; ++j; - pair const r = (*i)->get_point_x_range (); + pair r = (*i)->get_point_x_range (); /* check this range against all the AudioRanges that we are using */ list::const_iterator k = _ranges.begin (); while (k != _ranges.end()) { - if (k->coverage (r.first, r.second) != OverlapNone) { + if (k->coverage (r.first, r.second) != Evoral::OverlapNone) { break; } ++k; @@ -4027,6 +4451,12 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* it /* Now ::lines contains the AutomationLines that somehow overlap our drag */ } +double +AutomationRangeDrag::y_fraction (boost::shared_ptr line, double global_y) const +{ + return 1.0 - ((global_y - y_origin) / line->height()); +} + void AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { @@ -4035,6 +4465,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) /* Get line states before we start changing things */ for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { i->state = &i->line->get_state (); + i->original_fraction = y_fraction (i->line, _drags->current_pointer_y()); } if (_ranges.empty()) { @@ -4075,9 +4506,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ()); the_list->add (p, the_list->eval (p)); - j->line->add_always_in_view (p); the_list->add (q, the_list->eval (q)); - j->line->add_always_in_view (q); } /* same thing for the end */ @@ -4103,9 +4532,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ()); the_list->add (p, the_list->eval (p)); - j->line->add_always_in_view (p); the_list->add (q, the_list->eval (q)); - j->line->add_always_in_view (q); } } @@ -4144,7 +4571,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) } for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state); + i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state); } } @@ -4155,11 +4582,12 @@ AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/) return; } - for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - float const f = 1 - (_drags->current_pointer_y() / i->line->height()); - + for (list::iterator l = _lines.begin(); l != _lines.end(); ++l) { + float const f = y_fraction (l->line, _drags->current_pointer_y()); /* we are ignoring x position for this drag, so we can just pass in anything */ - i->line->drag_motion (0, f, true, false); + 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)); } } @@ -4172,8 +4600,7 @@ AutomationRangeDrag::finished (GdkEvent* event, bool) motion (event, false); for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - i->line->end_drag (); - i->line->clear_always_in_view (); + i->line->end_drag (false, 0); } _editor->session()->commit_reversible_command (); @@ -4183,7 +4610,6 @@ void AutomationRangeDrag::aborted (bool) { for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - i->line->clear_always_in_view (); i->line->reset (); } } @@ -4205,7 +4631,9 @@ PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionVie , _patch_change (i) , _cumulative_dx (0) { - DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n"); + DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n", + _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()), + grab_frame())); } void @@ -4216,8 +4644,8 @@ PatchChangeDrag::motion (GdkEvent* ev, bool) f = max (f, r->position ()); f = min (f, r->last_frame ()); - framecnt_t const dxf = f - grab_frame(); - double const dxu = _editor->frame_to_unit (dxf); + framecnt_t const dxf = f - grab_frame(); // permitted dx in frames + double const dxu = _editor->frame_to_unit (dxf); // permitted fx in units _patch_change->move (dxu - _cumulative_dx, 0); _cumulative_dx = dxu; } @@ -4230,14 +4658,13 @@ PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred) } boost::shared_ptr r (_region_view->region ()); - framepos_t f = adjusted_current_frame (ev); f = max (f, r->position ()); f = min (f, r->last_frame ()); _region_view->move_patch_change ( *_patch_change, - _region_view->frames_to_beats (f - r->position() - r->start()) + _region_view->region_frames_to_region_beats (f - (r->position() - r->start())) ); } @@ -4251,6 +4678,301 @@ void PatchChangeDrag::setup_pointer_frame_offset () { boost::shared_ptr region = _region_view->region (); - _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start(); + _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()); +} + +MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv) + : RubberbandSelectDrag (e, rv->get_canvas_frame ()) + , _region_view (rv) +{ + +} + +void +MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/) +{ + framepos_t const p = _region_view->region()->position (); + double const y = _region_view->midi_view()->y_position (); + + x1 = max ((framepos_t) 0, x1 - p); + x2 = max ((framepos_t) 0, x2 - p); + y1 = max (0.0, y1 - y); + y2 = max (0.0, y2 - y); + + _region_view->update_drag_selection ( + _editor->frame_to_pixel (x1), + _editor->frame_to_pixel (x2), + y1, + y2, + Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier) + ); +} + +void +MidiRubberbandSelectDrag::deselect_things () +{ + /* XXX */ +} + +MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv) + : RubberbandSelectDrag (e, rv->get_canvas_frame ()) + , _region_view (rv) +{ + _vertical_only = true; +} + +void +MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/) +{ + double const y = _region_view->midi_view()->y_position (); + + y1 = max (0.0, y1 - y); + y2 = max (0.0, y2 - y); + + _region_view->update_vertical_drag_selection ( + y1, + y2, + Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier) + ); +} + +void +MidiVerticalSelectDrag::deselect_things () +{ + /* XXX */ +} + +EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i) + : RubberbandSelectDrag (e, i) +{ + +} + +void +EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress) +{ + if (drag_in_progress) { + /* We just want to select things at the end of the drag, not during it */ + return; + } + + Selection::Operation op = ArdourKeyboard::selection_type (button_state); + + _editor->begin_reversible_command (_("rubberband selection")); + _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false); + _editor->commit_reversible_command (); +} + +void +EditorRubberbandSelectDrag::deselect_things () +{ + if (!getenv("ARDOUR_SAE")) { + _editor->selection->clear_tracks(); + } + _editor->selection->clear_regions(); + _editor->selection->clear_points (); + _editor->selection->clear_lines (); +} + +NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv) + : Drag (e, i) + , _region_view (rv) + , _drag_rect (0) +{ + +} + +NoteCreateDrag::~NoteCreateDrag () +{ + delete _drag_rect; +} + +framecnt_t +NoteCreateDrag::grid_frames (framepos_t t) const +{ + bool success; + Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t); + if (!success) { + grid_beats = 1; + } + + return _region_view->region_beats_to_region_frames (grid_beats); +} + +void +NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + Drag::start_grab (event, cursor); + + _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ()); + + framepos_t pf = _drags->current_pointer_frame (); + framecnt_t const g = grid_frames (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. + */ + if (_editor->snap_mode() == SnapNormal && pf > g / 2) { + pf -= g / 2; + } + + _note[0] = adjusted_frame (pf, event) - _region_view->region()->position (); + + MidiStreamView* sv = _region_view->midi_stream_view (); + double const x = _editor->frame_to_pixel (_note[0]); + double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y))); + + _drag_rect->property_x1() = x; + _drag_rect->property_y1() = y; + _drag_rect->property_x2() = x; + _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ()); + + _drag_rect->property_outline_what() = 0xff; + _drag_rect->property_outline_color_rgba() = 0xffffff99; + _drag_rect->property_fill_color_rgba() = 0xffffff66; +} + +void +NoteCreateDrag::motion (GdkEvent* event, bool) +{ + _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ()); + double const x = _editor->frame_to_pixel (_note[1]); + if (_note[1] > _note[0]) { + _drag_rect->property_x2() = x; + } else { + _drag_rect->property_x1() = x; + } +} + +void +NoteCreateDrag::finished (GdkEvent*, bool had_movement) +{ + if (!had_movement) { + return; + } + + framepos_t const start = min (_note[0], _note[1]); + framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]); + + framecnt_t const g = grid_frames (start); + double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat; + + if (_editor->snap_mode() == SnapNormal && length < g) { + length = g - one_tick; + } + + double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length)); + + _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false); +} + +double +NoteCreateDrag::y_to_region (double y) const +{ + double x = 0; + _region_view->get_canvas_group()->w2i (x, y); + return y; +} + +void +NoteCreateDrag::aborted (bool) +{ + +} + +CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn) + : Drag (e, i) + , arv (rv) + , start (start_yn) +{ + std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl; +} + +void +CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor) +{ + Drag::start_grab (event, cursor); +} + +void +CrossfadeEdgeDrag::motion (GdkEvent*, bool) +{ + double distance; + double new_length; + framecnt_t len; + + boost::shared_ptr ar (arv->audio_region()); + + if (start) { + distance = _drags->current_pointer_x() - grab_x(); + len = ar->fade_in()->back()->when; + } else { + distance = grab_x() - _drags->current_pointer_x(); + len = ar->fade_out()->back()->when; + } + + /* how long should it be ? */ + + new_length = len + _editor->unit_to_frame (distance); + + /* now check with the region that this is legal */ + + new_length = ar->verify_xfade_bounds (new_length, start); + + if (start) { + arv->redraw_start_xfade_to (ar, new_length); + } else { + arv->redraw_end_xfade_to (ar, new_length); + } +} + +void +CrossfadeEdgeDrag::finished (GdkEvent*, bool) +{ + double distance; + double new_length; + framecnt_t len; + + boost::shared_ptr ar (arv->audio_region()); + + if (start) { + distance = _drags->current_pointer_x() - grab_x(); + len = ar->fade_in()->back()->when; + } else { + distance = grab_x() - _drags->current_pointer_x(); + len = ar->fade_out()->back()->when; + } + + new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start); + + _editor->begin_reversible_command ("xfade trim"); + ar->playlist()->clear_owned_changes (); + + if (start) { + ar->set_fade_in_length (new_length); + } else { + ar->set_fade_out_length (new_length); + } + + /* Adjusting the xfade may affect other regions in the playlist, so we need + to get undo Commands from the whole playlist rather than just the + region. + */ + + vector cmds; + ar->playlist()->rdiff (cmds); + _editor->session()->add_commands (cmds); + _editor->commit_reversible_command (); + +} + +void +CrossfadeEdgeDrag::aborted (bool) +{ + if (start) { + arv->redraw_start_xfade (); + } else { + arv->redraw_end_xfade (); + } }