X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_drag.cc;h=34e169b133c2de77e7e962609454c5fdbf80661c;hb=ade1c4923ca58a56c5160826253091f67f03fd2f;hp=44b50d0131f65ce6599a4c77d6059c7e118d3d4d;hpb=e0533e9dd7df504236892fe0d8693e7ad9a6a278;p=ardour.git diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 44b50d0131..34e169b133 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -1,4 +1,4 @@ -* +/* Copyright (C) 2009 Paul Davis This program is free software; you can redistribute it and/or modify @@ -40,17 +40,18 @@ #include "ardour/region_factory.h" #include "ardour/session.h" +#include "canvas/canvas.h" #include "canvas/scroll_group.h" #include "editor.h" #include "i18n.h" #include "keyboard.h" #include "audio_region_view.h" +#include "automation_region_view.h" #include "midi_region_view.h" #include "ardour_ui.h" #include "gui_thread.h" #include "control_point.h" -#include "utils.h" #include "region_gain_line.h" #include "editor_drag.h" #include "audio_time_axis.h" @@ -95,6 +96,8 @@ DragManager::abort () { _ending = true; + cerr << "Aborting drag\n"; + for (list::const_iterator i = _drags.begin(); i != _drags.end(); ++i) { (*i)->abort (); delete *i; @@ -177,28 +180,19 @@ DragManager::motion_handler (GdkEvent* e, bool from_autoscroll) { bool r = false; - _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y); - - for (list::iterator i = _drags.begin(); i != _drags.end(); ++i) { - bool const t = (*i)->motion_handler (e, from_autoscroll); - if (t) { - r = true; - } - - } - - return r; -} - -bool -DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll) -{ - bool r = false; + /* calling this implies that we expect the event to have canvas + * coordinates + * + * Can we guarantee that this is true? + */ _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y); for (list::iterator i = _drags.begin(); i != _drags.end(); ++i) { bool const t = (*i)->motion_handler (e, from_autoscroll); + /* run all handlers; return true if at least one of them + returns true (indicating that the event has been handled). + */ if (t) { r = true; } @@ -219,10 +213,11 @@ DragManager::have_item (ArdourCanvas::Item* i) const return j != _drags.end (); } -Drag::Drag (Editor* e, ArdourCanvas::Item* i) +Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only) : _editor (e) , _item (i) , _pointer_frame_offset (0) + , _trackview_only (trackview_only) , _move_threshold_passed (false) , _was_double_click (false) , _raw_grab_frame (0) @@ -268,15 +263,19 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor) _grab_frame = adjusted_frame (_raw_grab_frame, event); _last_pointer_frame = _grab_frame; _last_pointer_x = _grab_x; + + if (_trackview_only) { + _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y; + } + _last_pointer_y = _grab_y; if (cursor == 0) { _item->grab (); - } else { /* CAIROCANVAS need a variant here that passes *cursor */ _item->grab (); - + _cursor_ctx = CursorContext::create(*_editor, cursor); } if (_editor->session() && _editor->session()->transport_rolling()) { @@ -313,6 +312,7 @@ Drag::end_grab (GdkEvent* event) finished (event, _move_threshold_passed); _editor->verbose_cursor()->hide (); + _cursor_ctx.reset(); return _move_threshold_passed; } @@ -339,13 +339,29 @@ Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const return adjusted_frame (_drags->current_pointer_frame (), event, snap); } +double +Drag::current_pointer_x() const +{ + return _drags->current_pointer_x (); +} + +double +Drag::current_pointer_y () const +{ + if (!_trackview_only) { + return _drags->current_pointer_y (); + } + + return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y; +} + bool Drag::motion_handler (GdkEvent* event, bool from_autoscroll) { /* check to see if we have moved in any way that matters since the last motion event */ if (_move_threshold_passed && - (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) && - (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) { + (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) && + (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) { return false; } @@ -356,7 +372,7 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll) if (!from_autoscroll && !_move_threshold_passed) { bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first); - bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second); + bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second); _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters())); } @@ -372,13 +388,14 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll) motion (event, _move_threshold_passed != old_move_threshold_passed); _last_pointer_x = _drags->current_pointer_x (); - _last_pointer_y = _drags->current_pointer_y (); + _last_pointer_y = current_pointer_y (); _last_pointer_frame = adjusted_current_frame (event); } return true; } } + return false; } @@ -399,37 +416,22 @@ Drag::abort () void Drag::show_verbose_cursor_time (framepos_t frame) { - _editor->verbose_cursor()->set_time ( - frame, - _drags->current_pointer_x() + 10, - _drags->current_pointer_y() + 10 - ); - + _editor->verbose_cursor()->set_time (frame); _editor->verbose_cursor()->show (); } void -Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset) +Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/) { - _editor->verbose_cursor()->show (xoffset); - - _editor->verbose_cursor()->set_duration ( - start, end, - _drags->current_pointer_x() + 10, - _drags->current_pointer_y() + 10 - ); + _editor->verbose_cursor()->set_duration (start, end); + _editor->verbose_cursor()->show (); } void Drag::show_verbose_cursor_text (string const & text) { + _editor->verbose_cursor()->set (text); _editor->verbose_cursor()->show (); - - _editor->verbose_cursor()->set ( - text, - _drags->current_pointer_x() + 10, - _drags->current_pointer_y() + 10 - ); } boost::shared_ptr @@ -484,7 +486,7 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list::const_iterator i = v.begin(); i != v.end(); ++i) { - _views.push_back (DraggingView (*i, this)); + _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view())); } RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context()); @@ -539,7 +541,7 @@ RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) show_verbose_cursor_time (_last_frame_position); - pair const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ()); + pair const tv = _editor->trackview_by_y_position (current_pointer_y ()); if (tv.first) { _last_pointer_time_axis_view = find_time_axis_view (tv.first); _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; @@ -603,7 +605,6 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r } } - _last_frame_position = *pending_region_position; } return dx; @@ -653,12 +654,12 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */ /* Find the TimeAxisView that the pointer is now over */ - pair const r = _editor->trackview_by_y_position (_drags->current_pointer_y ()); + pair const r = _editor->trackview_by_y_position (current_pointer_y ()); TimeAxisView* tv = r.first; if (tv && tv->view()) { double layer = r.second; - + if (first_move && tv->view()->layer_display() == Stacked) { tv->view()->set_layer_display (Expanded); } @@ -671,11 +672,12 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view; delta_layer = current_pointer_layer - _last_pointer_layer; - } + } /* Work out the change in x */ framepos_t pending_region_position; double const x_delta = compute_x_delta (event, &pending_region_position); + _last_frame_position = pending_region_position; /* Verify change in y */ if (!y_movement_allowed (delta_time_axis_view, delta_layer)) { @@ -684,7 +686,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) delta_layer = 0; } - if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) { + if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) { /* haven't reached next snap point, and we're not switching trackviews nor layers. nothing to do. */ @@ -703,8 +705,19 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) if (first_move) { rv->drag_start (); - rv->fake_set_opaque (true); - rv->raise_to_top (); + + /* reparent the regionview into a group above all + * others + */ + + ArdourCanvas::Item* rvg = rv->get_canvas_group(); + Duple rv_canvas_offset = rvg->parent()->canvas_origin (); + Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin (); + rv->get_canvas_group()->reparent (_editor->_drag_motion_group); + /* move the item so that it continues to appear at the + same location now that its parent has changed. + */ + rvg->move (rv_canvas_offset - dmg_canvas_offset); } /* If we have moved tracks, we'll fudge the layer delta so that the @@ -719,14 +732,20 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) if (tv) { - int track_index = i->time_axis_view + delta_time_axis_view; + int track_index; + + if (i->time_axis_view >= 0) { + track_index = i->time_axis_view + delta_time_axis_view; + } else { + track_index = _time_axis_views.size() - 1 + delta_time_axis_view; + } if (track_index < 0 || track_index >= (int) _time_axis_views.size()) { continue; } /* The TimeAxisView that this region is now over */ - TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view]; + TimeAxisView* current_tv = _time_axis_views[track_index]; /* Ensure it is moved from stacked -> expanded if appropriate */ if (current_tv->view()->layer_display() == Stacked) { @@ -751,7 +770,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) } /* Update the DraggingView */ - i->time_axis_view += delta_time_axis_view; + i->time_axis_view = track_index; i->layer += this_delta_layer; if (_brushing) { @@ -782,7 +801,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) */ /* Now move the region view */ - rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0, 0)).y); + rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y); } } else { @@ -790,13 +809,17 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) * is down there. */ - if (_drags->current_pointer_y() >= _editor->get_trackview_group()->item_to_canvas (Duple (0,0)).y) { - Duple track_origin; + if (current_pointer_y() >= 0) { - TimeAxisView* last = _time_axis_views.back(); - track_origin = last->canvas_display()->item_to_canvas (track_origin); - track_origin.y += last->effective_height(); - rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0,0)).y); + Coord last_track_bottom_edge; + if (!_time_axis_views.empty()) { + TimeAxisView* last = _time_axis_views.back(); + last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height(); + } else { + last_track_bottom_edge = 0; + } + + rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y); i->time_axis_view = -1; } } @@ -818,6 +841,12 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move) { if (_copy && first_move) { + if (_x_constrained) { + _editor->begin_reversible_command (Operations::fixed_time_region_copy); + } else { + _editor->begin_reversible_command (Operations::region_copy); + } + /* duplicate the regionview(s) and region(s) */ list new_regionviews; @@ -847,7 +876,7 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move) } nrv->get_canvas_group()->show (); - new_regionviews.push_back (DraggingView (nrv, this)); + new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view)); /* swap _primary to the copy */ @@ -868,6 +897,14 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move) swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0); } + + } else if (!_copy && first_move) { + + if (_x_constrained) { + _editor->begin_reversible_command (_("fixed time region drag")); + } else { + _editor->begin_reversible_command (Operations::region_drag); + } } RegionMotionDrag::motion (event, first_move); @@ -949,7 +986,7 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred) } RouteTimeAxisView* -RegionMoveDrag::create_destination_time_axis (boost::shared_ptr region) +RegionMoveDrag::create_destination_time_axis (boost::shared_ptr region, TimeAxisView* original) { /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the new track. @@ -959,12 +996,20 @@ RegionMoveDrag::create_destination_time_axis (boost::shared_ptr region) if (boost::dynamic_pointer_cast (region)) { list > audio_tracks; audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name()); - return _editor->axis_view_from_route (audio_tracks.front()); + RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front()); + if (rtav) { + rtav->set_height (original->current_height()); + } + return rtav; } else { ChanCount one_midi_port (DataType::MIDI, 1); list > midi_tracks; midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr(), ARDOUR::Normal, 0, 1, region->name()); - return _editor->axis_view_from_route (midi_tracks.front()); + RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front()); + if (rtav) { + rtav->set_height (original->current_height()); + } + return rtav; } } catch (...) { error << _("Could not create new track after region placed in the drop zone") << endmsg; @@ -990,12 +1035,6 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed return; } - if (_x_constrained) { - _editor->begin_reversible_command (Operations::fixed_time_region_copy); - } else { - _editor->begin_reversible_command (Operations::region_copy); - } - /* insert the regions into their new playlists */ for (list::const_iterator i = _views.begin(); i != _views.end();) { @@ -1015,7 +1054,7 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed if (i->time_axis_view < 0) { if (!new_time_axis_view) { - new_time_axis_view = create_destination_time_axis (i->view->region()); + new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view); } dest_rtv = new_time_axis_view; } else { @@ -1072,12 +1111,6 @@ RegionMoveDrag::finished_no_copy ( return; } - if (_x_constrained) { - _editor->begin_reversible_command (_("fixed time region drag")); - } else { - _editor->begin_reversible_command (Operations::region_drag); - } - for (list::const_iterator i = _views.begin(); i != _views.end(); ) { RegionView* rv = i->view; @@ -1090,7 +1123,7 @@ RegionMoveDrag::finished_no_copy ( if (i->time_axis_view < 0) { if (!new_time_axis_view) { - new_time_axis_view = create_destination_time_axis (rv->region()); + new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view); } dest_rtv = new_time_axis_view; } else { @@ -1134,7 +1167,7 @@ RegionMoveDrag::finished_no_copy ( visible. */ rv->hide_region_editor(); - rv->fake_set_opaque (false); + remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists); @@ -1260,7 +1293,7 @@ RegionMoveDrag::remove_region_from_playlist ( playlist->clear_changes (); } - playlist->remove_region (region); + playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag } @@ -1369,7 +1402,6 @@ RegionMotionDrag::aborted (bool) rv->get_canvas_group()->reparent (rtv->view()->canvas_item()); rv->get_canvas_group()->set_y_position (0); rv->drag_end (); - rv->fake_set_opaque (false); rv->move (-_total_x_delta, 0); rv->set_height (rtv->view()->child_height ()); } @@ -1411,7 +1443,7 @@ RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr r, Rout _primary->get_canvas_group()->show (); _primary->set_position (pos, 0); - _views.push_back (DraggingView (_primary, this)); + _views.push_back (DraggingView (_primary, this, v)); _last_frame_position = pos; @@ -1431,6 +1463,12 @@ RegionInsertDrag::finished (GdkEvent *, bool) _editor->begin_reversible_command (Operations::insert_region); playlist->clear_changes (); playlist->add_region (_primary->region (), _last_frame_position); + + // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably + if (Config->get_edit_mode() == Ripple) { + playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region()); + } + _editor->session()->add_command (new StatefulDiffCommand (playlist)); _editor->commit_reversible_command (); @@ -1464,7 +1502,7 @@ 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 (current_pointer_y ()); RouteTimeAxisView* tv = dynamic_cast (tvp.first); /* The region motion is only processed if the pointer is over @@ -1473,10 +1511,12 @@ RegionSpliceDrag::motion (GdkEvent* event, bool) if (!tv || !tv->is_track()) { /* To make sure we hide the verbose canvas cursor when the mouse is - not held over and audiotrack. + not held over an audio track. */ _editor->verbose_cursor()->hide (); return; + } else { + _editor->verbose_cursor()->show (); } int dir; @@ -1487,10 +1527,8 @@ RegionSpliceDrag::motion (GdkEvent* event, bool) dir = -1; } - RegionSelection copy (_editor->selection->regions); - - RegionSelectionByPosition cmp; - copy.sort (cmp); + RegionSelection copy; + _editor->selection->regions.by_position(copy); framepos_t const pf = adjusted_current_frame (event); @@ -1539,6 +1577,290 @@ RegionSpliceDrag::aborted (bool) /* XXX: TODO */ } +/*** + * ripple mode... + */ + +void +RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress) +{ + + boost::shared_ptr rl = tav->playlist()->regions_with_start_within (Evoral::Range(where, max_framepos)); + + RouteTimeAxisView* rtv = dynamic_cast(tav); + RegionSelection to_ripple; + for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) { + if ((*i)->position() >= where) { + to_ripple.push_back (rtv->view()->find_view(*i)); + } + } + + for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) { + if (!exclude.contains (*i)) { + // the selection has already been added to _views + + if (drag_in_progress) { + // do the same things that RegionMotionDrag::motion does when + // first_move is true, for the region views that we're adding + // to _views this time + + (*i)->drag_start(); + ArdourCanvas::Item* rvg = (*i)->get_canvas_group(); + Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0)); + Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin (); + rvg->reparent (_editor->_drag_motion_group); + + // we only need to move in the y direction + Duple fudge = rv_canvas_offset - dmg_canvas_offset; + fudge.x = 0; + rvg->move (fudge); + + } + _views.push_back (DraggingView (*i, this, tav)); + } + } +} + +void +RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions) +{ + + for (std::list::iterator i = _views.begin(); i != _views.end(); ) { + // we added all the regions after the selection + + std::list::iterator to_erase = i++; + if (!_editor->selection->regions.contains (to_erase->view)) { + // restore the non-selected regions to their original playlist & positions, + // and then ripple them back by the length of the regions that were dragged away + // do the same things as RegionMotionDrag::aborted + + RegionView *rv = to_erase->view; + TimeAxisView* tv = &(rv->get_time_axis_view ()); + RouteTimeAxisView* rtv = dynamic_cast (tv); + assert (rtv); + + // plonk them back onto their own track + rv->get_canvas_group()->reparent(rtv->view()->canvas_item()); + rv->get_canvas_group()->set_y_position (0); + rv->drag_end (); + + if (move_regions) { + // move the underlying region to match the view + rv->region()->set_position (rv->region()->position() + amount); + } else { + // restore the view to match the underlying region's original position + rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK + } + + rv->set_height (rtv->view()->child_height ()); + _views.erase (to_erase); + } + } +} + +bool +RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const +{ + if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) { + if (delta_track) { + return allow_moves_across_tracks; + } else { + return true; + } + } + return false; +} + +RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v) + : RegionMoveDrag (e, i, p, v, false, false) +{ + DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n"); + // compute length of selection + RegionSelection selected_regions = _editor->selection->regions; + selection_length = selected_regions.end_frame() - selected_regions.start(); + + // we'll only allow dragging to another track in ripple mode if all the regions + // being dragged start off on the same track + allow_moves_across_tracks = (selected_regions.playlists().size() == 1); + prev_tav = NULL; + prev_amount = 0; + exclude = new RegionList; + for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) { + exclude->push_back((*i)->region()); + } + + // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple + RegionSelection copy; + selected_regions.by_position(copy); // get selected regions sorted by position into copy + + std::set > playlists = copy.playlists(); + std::set >::const_iterator pi; + + for (pi = playlists.begin(); pi != playlists.end(); ++pi) { + // find ripple start point on each applicable playlist + RegionView *first_selected_on_this_track = NULL; + for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) { + if ((*i)->region()->playlist() == (*pi)) { + // region is on this playlist - it's the first, because they're sorted + first_selected_on_this_track = *i; + break; + } + } + assert (first_selected_on_this_track); // we should always find the region in one of the playlists... + add_all_after_to_views ( + &first_selected_on_this_track->get_time_axis_view(), + first_selected_on_this_track->region()->position(), + selected_regions, false); + } + + if (allow_moves_across_tracks) { + orig_tav = &(*selected_regions.begin())->get_time_axis_view(); + } else { + orig_tav = NULL; + } + +} + +void +RegionRippleDrag::motion (GdkEvent* event, bool first_move) +{ + /* Which trackview is this ? */ + + pair const tvp = _editor->trackview_by_y_position (current_pointer_y ()); + RouteTimeAxisView* tv = dynamic_cast (tvp.first); + + /* The region motion is only processed if the pointer is over + an audio track. + */ + + if (!tv || !tv->is_track()) { + /* To make sure we hide the verbose canvas cursor when the mouse is + not held over an audiotrack. + */ + _editor->verbose_cursor()->hide (); + return; + } + + framepos_t where = adjusted_current_frame (event); + assert (where >= 0); + framepos_t after; + double delta = compute_x_delta (event, &after); + + framecnt_t amount = _editor->pixel_to_sample (delta); + + if (allow_moves_across_tracks) { + // all the originally selected regions were on the same track + + framecnt_t adjust = 0; + if (prev_tav && tv != prev_tav) { + // dragged onto a different track + // remove the unselected regions from _views, restore them to their original positions + // and add the regions after the drop point on the new playlist to _views instead. + // undo the effect of rippling the previous playlist, and include the effect of removing + // the dragged region(s) from this track + + remove_unselected_from_views (prev_amount, false); + // ripple previous playlist according to the regions that have been removed onto the new playlist + prev_tav->playlist()->ripple(prev_position, -selection_length, exclude); + prev_amount = 0; + + // move just the selected regions + RegionMoveDrag::motion(event, first_move); + + // ensure that the ripple operation on the new playlist inserts selection_length time + adjust = selection_length; + // ripple the new current playlist + tv->playlist()->ripple (where, amount+adjust, exclude); + + // add regions after point where drag entered this track to subsequent ripples + add_all_after_to_views (tv, where, _editor->selection->regions, true); + + } else { + // motion on same track + RegionMoveDrag::motion(event, first_move); + } + prev_tav = tv; + + // remember what we've done to this playlist so we can undo it if the selection is dragged to another track + prev_position = where; + } else { + // selection encompasses multiple tracks - just drag + // cross-track drags are forbidden + RegionMoveDrag::motion(event, first_move); + } + + if (!_x_constrained) { + prev_amount += amount; + } + + _last_frame_position = after; +} + +void +RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred) +{ + if (!movement_occurred) { + + /* just a click */ + + if (was_double_click() && !_views.empty()) { + DraggingView dv = _views.front(); + dv.view->show_region_editor (); + + } + + return; + } + + _editor->begin_reversible_command(_("Ripple drag")); + + // remove the regions being rippled from the dragging view, updating them to + // their new positions + remove_unselected_from_views (prev_amount, true); + + if (allow_moves_across_tracks) { + if (orig_tav) { + // if regions were dragged across tracks, we've rippled any later + // regions on the track the regions were dragged off, so we need + // to add the original track to the undo record + orig_tav->playlist()->clear_changes(); + vector cmds; + orig_tav->playlist()->rdiff (cmds); + _editor->session()->add_commands (cmds); + } + if (prev_tav && prev_tav != orig_tav) { + prev_tav->playlist()->clear_changes(); + vector cmds; + prev_tav->playlist()->rdiff (cmds); + _editor->session()->add_commands (cmds); + } + } else { + // selection spanned multiple tracks - all will need adding to undo record + + std::set > playlists = _editor->selection->regions.playlists(); + std::set >::const_iterator pi; + + for (pi = playlists.begin(); pi != playlists.end(); ++pi) { + (*pi)->clear_changes(); + vector cmds; + (*pi)->rdiff (cmds); + _editor->session()->add_commands (cmds); + } + } + + // other modified playlists are added to undo by RegionMoveDrag::finished() + RegionMoveDrag::finished (event, movement_occurred); + _editor->commit_reversible_command(); +} + +void +RegionRippleDrag::aborted (bool movement_occurred) +{ + RegionMoveDrag::aborted (movement_occurred); + _views.clear (); +} + + RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v) : Drag (e, i), _view (dynamic_cast (v)) @@ -1611,25 +1933,16 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/) if (x_fraction > 0.0 && x_fraction < 0.25) { cursor = _editor->cursors()->left_side_trim; + at_front = true; } else { cursor = _editor->cursors()->right_side_trim; + at_front = false; } Drag::start_grab (event, cursor); region = &cnote->region_view(); - double const region_start = region->get_position_pixels(); - double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L; - - if (grab_x() <= middle_point) { - cursor = _editor->cursors()->left_side_trim; - at_front = true; - } else { - cursor = _editor->cursors()->right_side_trim; - at_front = false; - } - _item->grab (); if (event->motion.state & Keyboard::PrimaryModifier) { @@ -1650,11 +1963,16 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/) */ region->note_selected (cnote, cnote->selected ()); + _editor->begin_reversible_command (_("resize notes")); + for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) { MidiRegionSelection::iterator next; next = r; ++next; - (*r)->begin_resizing (at_front); + MidiRegionView* mrv = dynamic_cast(*r); + if (mrv) { + mrv->begin_resizing (at_front); + } r = next; } } @@ -1666,7 +1984,10 @@ NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/) for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) { NoteBase* nb = reinterpret_cast (_item->get_data ("notebase")); assert (nb); - (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative); + MidiRegionView* mrv = dynamic_cast(*r); + if (mrv) { + mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative); + } } } @@ -1677,8 +1998,13 @@ NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/) for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) { NoteBase* nb = reinterpret_cast (_item->get_data ("notebase")); assert (nb); - (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative); + MidiRegionView* mrv = dynamic_cast(*r); + if (mrv) { + mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative); + } } + + _editor->commit_reversible_command (); } void @@ -1686,7 +2012,10 @@ NoteResizeDrag::aborted (bool) { MidiRegionSelection& ms (_editor->get_selection().midi_regions); for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) { - (*r)->abort_resizing (); + MidiRegionView* mrv = dynamic_cast(*r); + if (mrv) { + mrv->abort_resizing (); + } } } @@ -1742,8 +2071,7 @@ VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*) 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 (); + show_verbose_cursor_text (buf); } void @@ -1771,7 +2099,6 @@ VideoTimeLineDrag::motion (GdkEvent* event, bool first_move) DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt)); if (first_move) { rv->drag_start (); - rv->fake_set_opaque (true); rv->region()->clear_changes (); rv->region()->suspend_property_changes(); } @@ -1793,8 +2120,7 @@ VideoTimeLineDrag::motion (GdkEvent* event, bool first_move) , _("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 (); + show_verbose_cursor_text (buf); } void @@ -1819,7 +2145,6 @@ VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred) 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())); @@ -1883,11 +2208,20 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*) if (pf < (region_start + region_length/2)) { /* closer to front */ _operation = StartTrim; - Drag::start_grab (event, _editor->cursors()->left_side_trim); + + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim); + } else { + Drag::start_grab (event, _editor->cursors()->left_side_trim); + } } else { /* closer to end */ _operation = EndTrim; - Drag::start_grab (event, _editor->cursors()->right_side_trim); + if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) { + Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim); + } else { + Drag::start_grab (event, _editor->cursors()->right_side_trim); + } } } @@ -1955,7 +2289,6 @@ TrimDrag::motion (GdkEvent* event, bool first_move) for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { RegionView* rv = i->view; - rv->fake_set_opaque (false); rv->enable_display (false); rv->region()->playlist()->clear_owned_changes (); @@ -2138,7 +2471,6 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred) for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { i->view->thaw_after_trim (); i->view->enable_display (true); - i->view->fake_set_opaque (true); /* Trimming one region may affect others on the playlist, so we need to get undo Commands from the whole playlist rather than just the @@ -2267,7 +2599,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move) _marker = new MeterMarker ( *_editor, *_editor->meter_group, - ARDOUR_UI::config()->get_canvasvar_MeterMarker(), + ARDOUR_UI::config()->color ("meter marker"), name, *new MeterSection (_marker->meter()) ); @@ -2285,6 +2617,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move) } framepos_t const pf = adjusted_current_frame (event); + _marker->set_position (pf); show_verbose_cursor_time (pf); } @@ -2397,7 +2730,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) _marker = new TempoMarker ( *_editor, *_editor->tempo_group, - ARDOUR_UI::config()->get_canvasvar_TempoMarker(), + ARDOUR_UI::config()->color ("tempo marker"), name, *new TempoSection (_marker->tempo()) ); @@ -2436,7 +2769,7 @@ 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); + framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest); Timecode::BBT_Time when; map.bbt_time (beat_time, when); @@ -2478,7 +2811,7 @@ TempoMarkerDrag::aborted (bool moved) } CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s) - : Drag (e, &c.track_canvas_item()) + : Drag (e, &c.track_canvas_item(), false) , _cursor (c) , _stop (s) { @@ -3073,7 +3406,7 @@ MarkerDrag::motion (GdkEvent* event, bool) } else if (new_start < copy_location->end()) { copy_location->set_start (new_start); } else if (newframe > 0) { - _editor->snap_to (next, 1, true); + _editor->snap_to (next, RoundUpAlways, true); copy_location->set_end (next); copy_location->set_start (newframe); } @@ -3086,7 +3419,7 @@ MarkerDrag::motion (GdkEvent* event, bool) } else if (new_end > copy_location->start()) { copy_location->set_end (new_end); } else if (newframe > 0) { - _editor->snap_to (next, -1, true); + _editor->snap_to (next, RoundDownAlways, true); copy_location->set_start (next); copy_location->set_end (newframe); } @@ -3227,10 +3560,7 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) _point->line().start_drag_single (_point, _fixed_grab_x, fraction); - _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction), - event->button.x + 10, event->button.y + 10); - - _editor->verbose_cursor()->show (); + show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction)); _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier); @@ -3243,7 +3573,7 @@ void ControlPointDrag::motion (GdkEvent* event, bool) { double dx = _drags->current_pointer_x() - last_pointer_x(); - double dy = _drags->current_pointer_y() - last_pointer_y(); + double dy = current_pointer_y() - last_pointer_y(); if (event->button.state & Keyboard::SecondaryModifier) { dx *= 0.1; @@ -3290,7 +3620,7 @@ ControlPointDrag::motion (GdkEvent* event, bool) _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index); - _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction)); + show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction)); } void @@ -3309,7 +3639,7 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred) } _point->line().end_drag (_pushing, _final_index); - _editor->session()->commit_reversible_command (); + _editor->commit_reversible_command (); } void @@ -3321,8 +3651,8 @@ ControlPointDrag::aborted (bool) bool ControlPointDrag::active (Editing::MouseMode m) { - if (m == Editing::MouseGain) { - /* always active in mouse gain */ + if (m == Editing::MouseDraw) { + /* always active in mouse draw */ return true; } @@ -3376,16 +3706,13 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) _line->start_drag_line (before, after, fraction); - _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction), - event->button.x + 10, event->button.y + 10); - - _editor->verbose_cursor()->show (); + show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction)); } void LineDrag::motion (GdkEvent* event, bool) { - double dy = _drags->current_pointer_y() - last_pointer_y(); + double dy = current_pointer_y() - last_pointer_y(); if (event->button.state & Keyboard::SecondaryModifier) { dy *= 0.1; @@ -3404,7 +3731,7 @@ LineDrag::motion (GdkEvent* event, bool) /* we are ignoring x position for this drag, so we can just pass in anything */ _line->drag_motion (0, fraction, true, false, ignored); - _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction)); + show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction)); } void @@ -3426,7 +3753,7 @@ LineDrag::finished (GdkEvent* event, bool movement_occured) } } - _editor->session()->commit_reversible_command (); + _editor->commit_reversible_command (); } void @@ -3533,10 +3860,10 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool) double y1; double y2; - framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ()); + framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()); framepos_t grab = grab_frame (); - if (Config->get_rubberbanding_snaps_to_grid ()) { + if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) { _editor->snap_to_with_modifier (grab, event); } @@ -3550,43 +3877,50 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool) start = grab; } - if (_drags->current_pointer_y() < grab_y()) { - y1 = _drags->current_pointer_y(); + if (current_pointer_y() < grab_y()) { + y1 = current_pointer_y(); y2 = grab_y(); } else { - y2 = _drags->current_pointer_y(); + y2 = current_pointer_y(); y1 = grab_y(); } - if (start != end || y1 != y2) { double x1 = _editor->sample_to_pixel (start); double x2 = _editor->sample_to_pixel (end); const double min_dimension = 2.0; - _editor->rubberband_rect->set_x0 (x1); if (_vertical_only) { /* fixed 10 pixel width */ - _editor->rubberband_rect->set_x1 (x1 + 10); + x2 = x1 + 10; } else { if (x2 < x1) { x2 = min (x1 - min_dimension, x2); } else { x2 = max (x1 + min_dimension, x2); } - _editor->rubberband_rect->set_x1 (x2); } - _editor->rubberband_rect->set_y0 (y1); if (y2 < y1) { y2 = min (y1 - min_dimension, y2); } else { y2 = max (y1 + min_dimension, y2); } - _editor->rubberband_rect->set_y1 (y2); - + /* translate rect into item space and set */ + + ArdourCanvas::Rect r (x1, y1, x2, y2); + + /* this drag is a _trackview_only == true drag, so the y1 and + * y2 (computed using current_pointer_y() and grab_y()) will be + * relative to the top of the trackview group). The + * rubberband rect has the same parent/scroll offset as the + * the trackview group, so we can use the "r" rect directly + * to set the shape of the rubberband. + */ + + _editor->rubberband_rect->set (r); _editor->rubberband_rect->show(); _editor->rubberband_rect->raise_to_top(); @@ -3613,11 +3947,11 @@ RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress) double y1; double y2; - if (_drags->current_pointer_y() < grab_y()) { - y1 = _drags->current_pointer_y(); + if (current_pointer_y() < grab_y()) { + y1 = current_pointer_y(); y2 = grab_y(); } else { - y2 = _drags->current_pointer_y(); + y2 = current_pointer_y(); y1 = grab_y(); } @@ -3641,7 +3975,7 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred) if ((mtv = dynamic_cast(_editor->clicked_axisview)) != 0) { /* MIDI track */ - if (_editor->selection->empty()) { + if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) { /* nothing selected */ add_midi_region (mtv); do_deselect = false; @@ -3783,9 +4117,7 @@ SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o) : Drag (e, i) , _operation (o) , _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"); @@ -3845,7 +4177,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*) show_verbose_cursor_time (adjusted_current_frame (event)); } - _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order (); + _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order (); } void @@ -3892,9 +4224,9 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) if (first_move) { grab = adjusted_current_frame (event, false); if (grab < pending_position) { - _editor->snap_to (grab, -1); + _editor->snap_to (grab, RoundDownMaybe); } else { - _editor->snap_to (grab, 1); + _editor->snap_to (grab, RoundUpMaybe); } } @@ -3931,30 +4263,56 @@ SelectionDrag::motion (GdkEvent* event, bool first_move) } } + //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff, + // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this + AutomationTimeAxisView *atest = dynamic_cast(_editor->clicked_axisview); + if (atest) { + _editor->selection->add (atest); + break; + } + /* select all tracks within the rectangle that we've marked out so far */ - TrackViewList to_be_added_to_selection; - TrackViewList to_be_removed_from_selection; + TrackViewList new_selection; TrackViewList& all_tracks (_editor->track_views); - for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) { - - if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) { - if (!(*i)->get_selected()) { - to_be_added_to_selection.push_back (*i); + ArdourCanvas::Coord const top = grab_y(); + ArdourCanvas::Coord const bottom = current_pointer_y(); + + if (top >= 0 && bottom >= 0) { + + //first, find the tracks that are covered in the y range selection + for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) { + if ((*i)->covered_by_y_range (top, bottom)) { + new_selection.push_back (*i); } - } else { - if ((*i)->get_selected()) { - to_be_removed_from_selection.push_back (*i); + } + + //now find any tracks that are GROUPED with the tracks we selected + TrackViewList grouped_add = new_selection; + for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) { + RouteTimeAxisView *n = dynamic_cast(*i); + if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) { + for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) { + RouteTimeAxisView *check = dynamic_cast(*j); + if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) ) + grouped_add.push_back (*j); + } } } - } - if (!to_be_added_to_selection.empty()) { - _editor->selection->add (to_be_added_to_selection); - } + //now compare our list with the current selection, and add or remove as necessary + //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky ) + TrackViewList tracks_to_add; + TrackViewList tracks_to_remove; + for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i) + if ( !_editor->selection->tracks.contains ( *i ) ) + tracks_to_add.push_back ( *i ); + for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) + if ( !grouped_add.contains ( *i ) ) + tracks_to_remove.push_back ( *i ); + _editor->selection->add(tracks_to_add); + _editor->selection->remove(tracks_to_remove); - if (!to_be_removed_from_selection.empty()) { - _editor->selection->remove (to_be_removed_from_selection); } } break; @@ -4026,6 +4384,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred) { Session* s = _editor->session(); + _editor->begin_reversible_selection_op (_("Change Time Selection")); if (movement_occurred) { motion (event, false); /* XXX this is not object-oriented programming at all. ick */ @@ -4038,8 +4397,11 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred) 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()); + if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) { + if (_operation == SelectionEndTrim) + _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame()); + else + s->request_locate (_editor->get_selection().time.start()); } } } @@ -4079,6 +4441,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred) _editor->stop_canvas_autoscroll (); _editor->clicked_selection = 0; + _editor->commit_reversible_selection_op (); } void @@ -4088,7 +4451,7 @@ SelectionDrag::aborted (bool) } RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o) - : Drag (e, i), + : Drag (e, i, false), _operation (o), _copy (false) { @@ -4099,8 +4462,8 @@ RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operat physical_screen_height (_editor->get_window()))); _drag_rect->hide (); - _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect()); - _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect()); + _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect")); + _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect")); } void @@ -4117,6 +4480,7 @@ RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *) } switch (_operation) { + case CreateSkipMarker: case CreateRangeMarker: case CreateTransportMarker: case CreateCDMarker: @@ -4143,6 +4507,9 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move) ArdourCanvas::Rectangle *crect; switch (_operation) { + case CreateSkipMarker: + crect = _editor->range_bar_drag_rect; + break; case CreateRangeMarker: crect = _editor->range_bar_drag_rect; break; @@ -4160,7 +4527,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move) framepos_t const pf = adjusted_current_frame (event); - if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) { + if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) { framepos_t grab = grab_frame (); _editor->snap_to (grab); @@ -4216,17 +4583,24 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred) _drag_rect->hide(); switch (_operation) { + case CreateSkipMarker: case CreateRangeMarker: case CreateCDMarker: { - _editor->begin_reversible_command (_("new range marker")); XMLNode &before = _editor->session()->locations()->get_state(); - _editor->session()->locations()->next_available_name(rangename,"unnamed"); - if (_operation == CreateCDMarker) { + if (_operation == CreateSkipMarker) { + _editor->begin_reversible_command (_("new skip marker")); + _editor->session()->locations()->next_available_name(rangename,_("skip")); + flags = Location::IsRangeMarker | Location::IsSkip; + _editor->range_bar_drag_rect->hide(); + } else if (_operation == CreateCDMarker) { + _editor->session()->locations()->next_available_name(rangename, _("CD")); + _editor->begin_reversible_command (_("new CD marker")); flags = Location::IsRangeMarker | Location::IsCDMarker; _editor->cd_marker_bar_drag_rect->hide(); - } - else { + } else { + _editor->begin_reversible_command (_("new skip marker")); + _editor->session()->locations()->next_available_name(rangename, _("unnamed")); flags = Location::IsRangeMarker; _editor->range_bar_drag_rect->hide(); } @@ -4262,7 +4636,7 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred) /* didn't drag, but mark is already created so do * nothing */ - } else { /* operation == CreateRangeMarker */ + } else { /* operation == CreateRangeMarker || CreateSkipMarker */ framepos_t start; @@ -4314,88 +4688,6 @@ RangeMarkerBarDrag::update_item (Location* location) _drag_rect->set_x1 (x2); } -MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i) - : Drag (e, i) - , _zoom_out (false) -{ - DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n"); -} - -void -MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *) -{ - if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) { - Drag::start_grab (event, _editor->cursors()->zoom_out); - _zoom_out = true; - } else { - Drag::start_grab (event, _editor->cursors()->zoom_in); - _zoom_out = false; - } - - show_verbose_cursor_time (adjusted_current_frame (event)); -} - -void -MouseZoomDrag::motion (GdkEvent* event, bool first_move) -{ - framepos_t start; - framepos_t end; - - framepos_t const pf = adjusted_current_frame (event); - - framepos_t grab = grab_frame (); - _editor->snap_to_with_modifier (grab, event); - - /* base start and end on initial click position */ - if (pf < grab) { - start = pf; - end = grab; - } else { - end = pf; - start = grab; - } - - if (start != end) { - - if (first_move) { - _editor->zoom_rect->show(); - _editor->zoom_rect->raise_to_top(); - } - - _editor->reposition_zoom_rect(start, end); - - show_verbose_cursor_time (pf); - } -} - -void -MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred) -{ - if (movement_occurred) { - motion (event, false); - - if (grab_frame() < last_pointer_frame()) { - _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame()); - } else { - _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame()); - } - } else { - if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) { - _editor->tav_zoom_step (_zoom_out); - } else { - _editor->temporal_zoom_to_frame (_zoom_out, grab_frame()); - } - } - - _editor->zoom_rect->hide(); -} - -void -MouseZoomDrag::aborted (bool) -{ - _editor->zoom_rect->hide (); -} - NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) , _cumulative_dx (0) @@ -4431,6 +4723,9 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *) } else { _region->unique_select (_primary); } + + _editor->begin_reversible_selection_op(_("Select Note Press")); + _editor->commit_reversible_selection_op(); } } } @@ -4464,7 +4759,7 @@ NoteDrag::total_dy () const 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); + uint8_t n = msv->y_to_note (current_pointer_y () - y); /* clamp */ n = max (msv->lowest_note(), n); n = min (msv->highest_note(), n); @@ -4512,14 +4807,17 @@ NoteDrag::finished (GdkEvent* ev, bool moved) { if (!moved) { /* no motion - select note */ - + if (_editor->current_mouse_mode() == Editing::MouseObject || _editor->current_mouse_mode() == Editing::MouseDraw) { - + + bool changed = false; + if (_was_selected) { bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier); if (add) { _region->note_deselected (_primary); + changed = true; } } else { bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier); @@ -4527,12 +4825,19 @@ NoteDrag::finished (GdkEvent* ev, bool moved) if (!extend && !add && _region->selection_size() > 1) { _region->unique_select (_primary); + changed = true; } else if (extend) { _region->note_selected (_primary, true, true); + changed = true; } else { /* it was added during button press */ } } + + if (changed) { + _editor->begin_reversible_selection_op(_("Select Note Release")); + _editor->commit_reversible_selection_op(); + } } } else { _region->note_dropped (_primary, total_dx(), total_dy()); @@ -4549,24 +4854,36 @@ NoteDrag::aborted (bool) AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list const & r) : Drag (editor, atv->base_item ()) , _ranges (r) + , _y_origin (atv->y_position()) , _nothing_to_drag (false) { DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n"); - y_origin = atv->y_position(); setup (atv->lines ()); } -/** Make an AutomationRangeDrag for region gain lines */ -AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list const & r) +/** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */ +AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list const & r) : Drag (editor, rv->get_canvas_group ()) , _ranges (r) + , _y_origin (rv->get_time_axis_view().y_position()) , _nothing_to_drag (false) + , _integral (false) { DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n"); list > lines; - lines.push_back (rv->get_gain_line ()); - y_origin = rv->get_time_axis_view().y_position(); + + AudioRegionView* audio_view; + AutomationRegionView* automation_view; + if ((audio_view = dynamic_cast(rv))) { + lines.push_back (audio_view->get_gain_line ()); + } else if ((automation_view = dynamic_cast(rv))) { + lines.push_back (automation_view->line ()); + _integral = true; + } else { + error << _("Automation range drag created for invalid region type") << endmsg; + } + setup (lines); } @@ -4611,7 +4928,14 @@ AutomationRangeDrag::setup (list > const & lin double AutomationRangeDrag::y_fraction (boost::shared_ptr line, double global_y) const { - return 1.0 - ((global_y - y_origin) / line->height()); + return 1.0 - ((global_y - _y_origin) / line->height()); +} + +double +AutomationRangeDrag::value (boost::shared_ptr list, double x) const +{ + const double v = list->eval(x); + return _integral ? rint(v) : v; } void @@ -4622,7 +4946,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()); + i->original_fraction = y_fraction (i->line, current_pointer_y()); } if (_ranges.empty()) { @@ -4662,8 +4986,8 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ()); double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ()); - the_list->add (p, the_list->eval (p)); - the_list->add (q, the_list->eval (q)); + the_list->editor_add (p, value (the_list, p)); + the_list->editor_add (q, value (the_list, q)); } /* same thing for the end */ @@ -4688,8 +5012,8 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ()); double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ()); - the_list->add (p, the_list->eval (p)); - the_list->add (q, the_list->eval (q)); + the_list->editor_add (p, value (the_list, p)); + the_list->editor_add (q, value (the_list, q)); } } @@ -4728,7 +5052,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, y_fraction (i->line, _drags->current_pointer_y()), i->state); + i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state); } } @@ -4740,7 +5064,7 @@ AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/) } for (list::iterator l = _lines.begin(); l != _lines.end(); ++l) { - float const f = y_fraction (l->line, _drags->current_pointer_y()); + float const f = y_fraction (l->line, current_pointer_y()); /* we are ignoring x position for this drag, so we can just pass in anything */ uint32_t ignored; l->line->drag_motion (0, f, true, false, ignored); @@ -4760,7 +5084,7 @@ AutomationRangeDrag::finished (GdkEvent* event, bool) i->line->end_drag (false, 0); } - _editor->session()->commit_reversible_command (); + _editor->commit_reversible_command (); } void @@ -4771,9 +5095,13 @@ AutomationRangeDrag::aborted (bool) } } -DraggingView::DraggingView (RegionView* v, RegionDrag* parent) +DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav) : view (v) + , initial_time_axis_view (itav) { + /* note that time_axis_view may be null if the regionview was created + * as part of a copy operation. + */ time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ()); layer = v->region()->layer (); initial_y = v->get_canvas_group()->position().y; @@ -4848,21 +5176,9 @@ MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* r 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->sample_to_pixel (x1), - _editor->sample_to_pixel (x2), - y1, - y2, - Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier) - ); + x1, x2, y1, y2, + Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)); } void @@ -4914,21 +5230,26 @@ EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, fram } Selection::Operation op = ArdourKeyboard::selection_type (button_state); - - _editor->begin_reversible_command (_("rubberband selection")); + + _editor->begin_reversible_selection_op (_("rubberband selection")); + _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false); - _editor->commit_reversible_command (); + + _editor->commit_reversible_selection_op (); } void EditorRubberbandSelectDrag::deselect_things () { - if (!getenv("ARDOUR_SAE")) { - _editor->selection->clear_tracks(); - } + _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)")); + + _editor->selection->clear_tracks(); _editor->selection->clear_regions(); _editor->selection->clear_points (); _editor->selection->clear_lines (); + _editor->selection->clear_midi_notes (); + + _editor->commit_reversible_selection_op(); } NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv) @@ -4936,7 +5257,7 @@ NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView , _region_view (rv) , _drag_rect (0) { - + _note[0] = _note[1] = 0; } NoteCreateDrag::~NoteCreateDrag () @@ -4948,9 +5269,9 @@ framecnt_t NoteCreateDrag::grid_frames (framepos_t t) const { bool success; - Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t); + Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t); if (!success) { - grid_beats = 1; + grid_beats = Evoral::Beats(1); } return _region_view->region_beats_to_region_frames (grid_beats); @@ -4974,6 +5295,7 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) } _note[0] = adjusted_frame (pf, event) - _region_view->region()->position (); + _note[1] = _note[0]; MidiStreamView* sv = _region_view->midi_stream_view (); double const x = _editor->sample_to_pixel (_note[0]); @@ -4989,12 +5311,10 @@ void NoteCreateDrag::motion (GdkEvent* event, bool) { _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ()); - double const x = _editor->sample_to_pixel (_note[1]); - if (_note[1] > _note[0]) { - _drag_rect->set_x1 (x); - } else { - _drag_rect->set_x0 (x); - } + double const x0 = _editor->sample_to_pixel (_note[0]); + double const x1 = _editor->sample_to_pixel (_note[1]); + _drag_rect->set_x0 (std::min(x0, x1)); + _drag_rect->set_x1 (std::max(x0, x1)); } void @@ -5008,13 +5328,14 @@ NoteCreateDrag::finished (GdkEvent*, bool had_movement) framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1])); framecnt_t const g = grid_frames (start); - double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat; + Evoral::Beats const one_tick = Evoral::Beats::ticks(1); if (_editor->snap_mode() == SnapNormal && length < g) { - length = g - one_tick; + length = g; } - double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length)); + Evoral::Beats length_beats = max ( + one_tick, _region_view->region_frames_to_region_beats (length) - one_tick); _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false); } @@ -5129,3 +5450,47 @@ CrossfadeEdgeDrag::aborted (bool) } } +RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos) + : Drag (e, item, true) + , line (new EditorCursor (*e)) +{ + line->set_position (pos); + line->show (); +} + +RegionCutDrag::~RegionCutDrag () +{ + delete line; +} + +void +RegionCutDrag::motion (GdkEvent*, bool) +{ + framepos_t where = _drags->current_pointer_frame(); + _editor->snap_to (where); + + line->set_position (where); +} + +void +RegionCutDrag::finished (GdkEvent*, bool) +{ + _editor->get_track_canvas()->canvas()->re_enter(); + + framepos_t pos = _drags->current_pointer_frame(); + + line->hide (); + + RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos); + + if (rs.empty()) { + return; + } + + _editor->split_regions_at (pos, rs); +} + +void +RegionCutDrag::aborted (bool) +{ +}