#include "gtkmm2ext/utils.h"
-#include "ardour/session.h"
+#include "ardour/audioregion.h"
#include "ardour/dB.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"
#include "keyboard.h"
#include "debug.h"
#include "editor_cursors.h"
#include "mouse_cursors.h"
+#include "verbose_cursor.h"
using namespace std;
using namespace ARDOUR;
using Gtkmm2ext::Keyboard;
-double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
+double ControlPointDrag::_zero_gain_fraction = -1.0;
DragManager::DragManager (Editor* e)
: _editor (e)
, _ending (false)
, _current_pointer_frame (0)
{
-
}
DragManager::~DragManager ()
DragManager::abort ()
{
_ending = true;
-
+
for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
(*i)->abort ();
delete *i;
}
+ if (!_drags.empty ()) {
+ _editor->set_follow_playhead (_old_follow_playhead, false);
+ }
+
_drags.clear ();
_ending = false;
void
DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
{
+ /* Prevent follow playhead during the drag to be nice to the user */
+ _old_follow_playhead = _editor->follow_playhead ();
+ _editor->set_follow_playhead (false);
+
_current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
-
+
for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
(*i)->start_grab (e, c);
}
DragManager::end_grab (GdkEvent* e)
{
_ending = true;
-
+
bool r = false;
for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
bool const t = (*i)->end_grab (e);
_drags.clear ();
_ending = false;
-
+
+ _editor->set_follow_playhead (_old_follow_playhead, false);
+
return r;
}
bool r = false;
_current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
-
+
for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
bool const t = (*i)->motion_handler (e, from_autoscroll);
if (t) {
r = true;
}
-
+
}
return r;
return j != _drags.end ();
}
-Drag::Drag (Editor* e, ArdourCanvas::Item* i)
+Drag::Drag (Editor* e, ArdourCanvas::Item* i)
: _editor (e)
, _item (i)
, _pointer_frame_offset (0)
_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)) {
_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;
finished (event, _move_threshold_passed);
- _editor->hide_verbose_canvas_cursor();
+ _editor->verbose_cursor()->hide ();
return _move_threshold_passed;
}
if (!from_autoscroll && !_move_threshold_passed) {
- bool const xp = (::llabs (_drags->current_pointer_frame () - _grab_frame) >= threshold.first);
+ 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);
_move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
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);
_last_pointer_x = _drags->current_pointer_x ();
_last_pointer_y = _drags->current_pointer_y ();
_last_pointer_frame = adjusted_current_frame (event);
-
+
return true;
}
}
_item->ungrab (0);
}
- aborted ();
+ aborted (_move_threshold_passed);
_editor->stop_canvas_autoscroll ();
- _editor->hide_verbose_canvas_cursor ();
+ _editor->verbose_cursor()->hide ();
+}
+
+void
+Drag::show_verbose_cursor_time (framepos_t frame)
+{
+ _editor->verbose_cursor()->set_time (
+ frame,
+ _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
+ _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
+ );
+
+ _editor->verbose_cursor()->show ();
+}
+
+void
+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 - _editor->horizontal_position(),
+ _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
+ );
+}
+
+void
+Drag::show_verbose_cursor_text (string const & text)
+{
+ _editor->verbose_cursor()->show ();
+
+ _editor->verbose_cursor()->set (
+ text,
+ _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
+ _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
+ );
+}
+
+boost::shared_ptr<Region>
+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<Region>();
}
struct EditorOrderTimeAxisViewSorter {
{
_editor->visible_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());
}
}
/* the list of views can be empty at this point if this is a region list-insert drag
*/
-
+
for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
_views.push_back (DraggingView (*i, this));
}
-
- RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
+
+ RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
}
void
}
}
-/** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
+/** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
+ * or -1 if it is not found.
+ */
int
RegionDrag::find_time_axis_view (TimeAxisView* t) const
{
if (i == N) {
return -1;
}
-
+
return i;
}
{
Drag::start_grab (event, cursor);
- _editor->show_verbose_time_cursor (_last_frame_position, 10);
+ show_verbose_cursor_time (_last_frame_position);
- pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ pair<TimeAxisView*, double> 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;
}
framepos_t sync_frame;
framecnt_t sync_offset;
int32_t sync_dir;
-
+
sync_offset = _primary->region()->sync_offset (sync_dir);
-
+
/* we don't handle a sync point that lies before zero.
*/
if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
-
+
sync_frame = *pending_region_position + (sync_dir*sync_offset);
-
+
_editor->snap_to_with_modifier (sync_frame, event);
-
+
*pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
-
+
} else {
*pending_region_position = _last_frame_position;
}
if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
- /* x movement since last time */
+ /* x movement since last time (in pixels) */
dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
/* total x movement */
framecnt_t total_dx = *pending_region_position;
if (regions_came_from_canvas()) {
- total_dx = total_dx - grab_frame () + _pointer_frame_offset;
+ total_dx = total_dx - grab_frame ();
}
/* check that no regions have gone off the start of the session */
}
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<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
int const n = i->time_axis_view + delta_track;
/* not a track, or the wrong type */
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.
*/
assert (!_views.empty ());
/* Find the TimeAxisView that the pointer is now over */
- pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ pair<TimeAxisView*, double> 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<RouteTimeAxisView*> (tv.first);
if (!rtv || !rtv->is_track()) {
- _editor->hide_verbose_canvas_cursor ();
+ _editor->verbose_cursor()->hide ();
return;
}
/* 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;
/* 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 */
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 ();
-
- // 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.
+ rv->drag_start ();
+
+ /* Absolutely no idea why this is necessary, but it is; without
+ it, the region view disappears after the reparent.
*/
+ _editor->update_canvas_now ();
- rv->get_canvas_group()->property_y() = iy1 - 1;
+ /* 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()->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;
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 */
_total_x_delta += x_delta;
-
+
if (first_move) {
_editor->cursor_group->raise_to_top();
}
if (x_delta != 0 && !_brushing) {
- _editor->show_verbose_time_cursor (_last_frame_position, 10);
+ show_verbose_cursor_time (_last_frame_position);
}
_last_pointer_time_axis_view += delta_time_axis_view;
list<DraggingView> new_regionviews;
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-
+
RegionView* rv = i->view;
AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
const boost::shared_ptr<const Region> original = rv->region();
boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
- region_copy->set_position (original->position(), this);
-
+ region_copy->set_position (original->position());
+
RegionView* nrv;
if (arv) {
boost::shared_ptr<AudioRegion> audioregion_copy
= boost::dynamic_pointer_cast<AudioRegion>(region_copy);
-
+
nrv = new AudioRegionView (*arv, audioregion_copy);
} else if (mrv) {
boost::shared_ptr<MidiRegion> midiregion_copy
} else {
continue;
}
-
+
nrv->get_canvas_group()->show ();
new_regionviews.push_back (DraggingView (nrv, this));
-
+
/* swap _primary to the copy */
-
+
if (rv == _primary) {
_primary = nrv;
}
-
+
/* ..and deselect the one we copied */
-
+
rv->set_selected (false);
}
-
+
if (!new_regionviews.empty()) {
-
+
/* reflect the fact that we are dragging the copies */
-
+
_views = new_regionviews;
-
+
swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
-
+
/*
sync the canvas to what we think is its current state
without it, the canvas seems to
}
void
-RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
+RegionMotionDrag::finished (GdkEvent *, bool)
+{
+ for (vector<TimeAxisView*>::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;
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<DraggingView>::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 ();
+ _editor->update_canvas_now ();
+
if (_copy) {
-
+
finished_copy (
changed_position,
changed_tracks,
drag_delta
);
-
+
} else {
-
+
finished_no_copy (
changed_position,
changed_tracks,
drag_delta
);
-
+
+ }
+
+ if (_editor->session() && Config->get_always_play_range()) {
+ _editor->session()->request_locate (_editor->get_selection().regions.start());
}
}
}
if (_x_constrained) {
- _editor->begin_reversible_command (_("fixed time region copy"));
+ _editor->begin_reversible_command (Operations::fixed_time_region_copy);
} else {
- _editor->begin_reversible_command (_("region copy"));
+ _editor->begin_reversible_command (Operations::region_copy);
}
/* insert the regions into their new playlists */
RegionView* new_view = insert_region_into_playlist (
i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
);
-
+
if (new_view == 0) {
continue;
}
new_views.push_back (new_view);
-
+
/* we don't need the copied RegionView any more */
views_to_delete.push_back (i->view);
}
delete *i;
}
- /* If we've created new regions either by copying or moving
- to a new track, we want to replace the old selection with the new ones
+ /* If we've created new regions either by copying or moving
+ to a new track, we want to replace the old selection with the new ones
*/
if (new_views.size() > 0) {
RegionSelection new_views;
PlaylistSet modified_playlists;
PlaylistSet frozen_playlists;
+ set<RouteTimeAxisView*> views_to_update;
if (_brushing) {
/* all changes were made during motion event handlers */
if (_x_constrained) {
_editor->begin_reversible_command (_("fixed time region drag"));
} else {
- _editor->begin_reversible_command (_("region drag"));
+ _editor->begin_reversible_command (Operations::region_drag);
}
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
RegionView* rv = i->view;
RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
- layer_t const dest_layer = i->layer;
+ double const dest_layer = i->layer;
if (rv->region()->locked()) {
++i;
continue;
}
+ views_to_update.insert (dest_rtv);
+
framepos_t where;
if (changed_position && !_x_constrained) {
remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
} else {
-
+
rv->region()->clear_changes ();
/*
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> 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 */
pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
/* this movement may result in a crossfade being modified, so we need to get undo
data from the playlist as well as the region.
*/
-
+
r = modified_playlists.insert (playlist);
if (r.second) {
playlist->clear_changes ();
}
- rv->region()->set_position (where, (void*) this);
+ rv->region()->set_position (where);
_editor->session()->add_command (new StatefulDiffCommand (rv->region()));
}
if (changed_tracks) {
-
+
/* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
was selected in all of them, then removing it from a playlist will have removed all
trace of it from _views (i.e. there were N regions selected, we removed 1,
we can just iterate.
*/
-
+
if (_views.empty()) {
break;
} else {
}
}
- /* If we've created new regions either by copying or moving
- to a new track, we want to replace the old selection with the new ones
+ /* If we've created new regions either by copying or moving
+ to a new track, we want to replace the old selection with the new ones
*/
if (new_views.size() > 0) {
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<RouteTimeAxisView*>::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.
playlist->remove_region (region);
}
-
+
/** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
* clearing the playlist's diff history first if necessary.
* @param dest_layer Destination layer.
* @param where Destination position.
* @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
- * that clear_changes () is only called once per playlist.
+ * that clear_changes () is only called once per playlist.
* @return New RegionView, or 0 if no insert was performed.
*/
RegionView *
_new_region_view = 0;
sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
- /* clear history for the playlist we are about to insert to, provided we haven't already done so */
+ /* clear history for the playlist we are about to insert to, provided we haven't already done so */
pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
if (r.second) {
dest_playlist->clear_changes ();
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 ();
for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
StatefulDiffCommand* c = new StatefulDiffCommand (*i);
if (!c->empty()) {
- _editor->session()->add_command (new StatefulDiffCommand (*i));
+ _editor->session()->add_command (c);
} else {
delete c;
}
void
-RegionMoveDrag::aborted ()
+RegionMoveDrag::aborted (bool movement_occurred)
{
if (_copy) {
_views.clear ();
} else {
- RegionMotionDrag::aborted ();
+ RegionMotionDrag::aborted (movement_occurred);
}
}
void
-RegionMotionDrag::aborted ()
+RegionMotionDrag::aborted (bool)
{
+ for (vector<TimeAxisView*>::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<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
RegionView* rv = i->view;
TimeAxisView* tv = &(rv->get_time_axis_view ());
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 ());
_editor->update_canvas_now ();
}
-
+
+/** @param b true to brush, otherwise false.
+ * @param c true to make copies of the regions being moved, otherwise false.
+ */
RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
: RegionMotionDrag (e, i, p, v, b),
_copy (c)
{
DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
-
+
double speed = 1;
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
if (rtv && rtv->is_track()) {
: RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
{
DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
-
+
assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
(boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
- _editor->begin_reversible_command (_("insert region"));
+ _editor->begin_reversible_command (Operations::insert_region);
playlist->clear_changes ();
playlist->add_region (_primary->region (), _last_frame_position);
_editor->session()->add_command (new StatefulDiffCommand (playlist));
}
void
-RegionInsertDrag::aborted ()
+RegionInsertDrag::aborted (bool)
{
delete _primary;
_primary = 0;
{
/* Which trackview is this ? */
- pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (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.
/* To make sure we hide the verbose canvas cursor when the mouse is
not held over and audiotrack.
*/
- _editor->hide_verbose_canvas_cursor ();
+ _editor->verbose_cursor()->hide ();
return;
}
}
void
-RegionSpliceDrag::aborted ()
+RegionSpliceDrag::aborted (bool)
{
/* XXX: TODO */
}
_view (dynamic_cast<MidiTimeAxisView*> (v))
{
DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
-
+
assert (_view);
}
RegionCreateDrag::motion (GdkEvent* event, bool first_move)
{
if (first_move) {
- add_region();
+ _region = add_midi_region (_view);
+ _view->playlist()->freeze ();
} else {
if (_region) {
framepos_t const f = adjusted_current_frame (event);
if (f < grab_frame()) {
- _region->set_position (f, this);
+ _region->set_position (f);
}
-
- /* again, don't use a zero-length region (see above) */
- framecnt_t const len = abs (f - grab_frame ());
- _region->set_length (len < 1 ? 1 : len, this);
+
+ /* Don't use a zero-length region, and subtract 1 frame from the snapped length
+ so that if this region is duplicated, its duplicate starts on
+ a snap point rather than 1 frame after a snap point. Otherwise things get
+ a bit confusing as if a region starts 1 frame after a snap point, one cannot
+ place snapped notes at the start of the region.
+ */
+
+ framecnt_t const len = abs (f - grab_frame () - 1);
+ _region->set_length (len < 1 ? 1 : len);
}
}
}
RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
{
if (!movement_occurred) {
- add_region ();
- }
-
- if (_region) {
- _editor->commit_reversible_command ();
+ add_midi_region (_view);
+ } else {
+ _view->playlist()->thaw ();
}
}
void
-RegionCreateDrag::add_region ()
+RegionCreateDrag::aborted (bool)
{
- 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);
+ if (_region) {
+ _view->playlist()->thaw ();
}
-}
-void
-RegionCreateDrag::aborted ()
-{
/* XXX */
}
}
void
-NoteResizeDrag::aborted ()
+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 ();
+ }
}
-RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
- : Drag (e, i)
+TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
+ : RegionDrag (e, i, p, v)
{
- DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
+ DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
}
void
-RegionGainDrag::motion (GdkEvent* /*event*/, bool)
+TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
{
+ double speed = 1.0;
+ TimeAxisView* tvp = &_primary->get_time_axis_view ();
+ RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
-}
-
-void
-RegionGainDrag::finished (GdkEvent *, bool)
-{
-
-}
-
-void
-RegionGainDrag::aborted ()
-{
- /* XXX: TODO */
-}
-
-TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
- : RegionDrag (e, i, p, v)
- , _have_transaction (false)
-{
- DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
-}
-
-void
-TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
-{
- double speed = 1.0;
- TimeAxisView* tvp = &_primary->get_time_axis_view ();
- RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
-
- if (tv && tv->is_track()) {
- speed = tv->track()->speed();
- }
+ if (tv && tv->is_track()) {
+ speed = tv->track()->speed();
+ }
framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
switch (_operation) {
case StartTrim:
- _editor->show_verbose_time_cursor (region_start, 10);
+ show_verbose_cursor_time (region_start);
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
i->view->trim_front_starting ();
}
break;
case EndTrim:
- _editor->show_verbose_time_cursor (region_end, 10);
+ show_verbose_cursor_time (region_end);
break;
case ContentsTrim:
- _editor->show_verbose_time_cursor (pf, 10);
+ show_verbose_cursor_time (pf);
break;
}
}
_editor->begin_reversible_command (trim_type);
- _have_transaction = true;
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
RegionView* rv = i->view;
rv->fake_set_opaque (false);
rv->enable_display (false);
- rv->region()->clear_changes ();
+ rv->region()->playlist()->clear_owned_changes ();
AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
if (arv) {
arv->temporarily_hide_envelope ();
+ arv->drag_start ();
}
boost::shared_ptr<Playlist> pl = rv->region()->playlist();
}
framecnt_t frame_delta = 0;
-
+
bool left_direction = false;
if (last_pointer_frame() > adjusted_current_frame(event)) {
left_direction = true;
switch (_operation) {
case StartTrim:
- _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
+ show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
break;
case EndTrim:
- _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
+ show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
break;
case ContentsTrim:
- _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+ show_verbose_cursor_time (adjusted_current_frame (event));
break;
}
}
i->view->trim_front_ending ();
}
}
-
+
if (!_editor->selection->selected (_primary)) {
_primary->thaw_after_trim ();
} else {
+ set<boost::shared_ptr<Playlist> > diffed_playlists;
+
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
- i->view->thaw_after_trim ();
+ i->view->thaw_after_trim ();
i->view->enable_display (true);
i->view->fake_set_opaque (true);
- if (_have_transaction) {
- _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
+
+ /* Trimming one region may affect others on the playlist, so we need
+ to get undo Commands from the whole playlist rather than just the
+ region. Use diffed_playlists to make sure we don't diff a given
+ playlist more than once.
+ */
+ boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
+ if (diffed_playlists.find (p) == diffed_playlists.end()) {
+ vector<Command*> cmds;
+ p->rdiff (cmds);
+ _editor->session()->add_commands (cmds);
+ diffed_playlists.insert (p);
}
}
}
}
_editor->motion_frozen_playlists.clear ();
- if (_have_transaction) {
- _editor->commit_reversible_command();
+ _editor->commit_reversible_command();
+
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ i->view->drag_end ();
}
} else {
if (_operation == StartTrim) {
i->view->trim_front_ending ();
}
-
+
i->view->region()->resume_property_changes ();
}
}
void
-TrimDrag::aborted ()
+TrimDrag::aborted (bool movement_occurred)
{
/* Our motion method is changing model state, so use the Undo system
to cancel. Perhaps not ideal, as this will leave an Undo point
*/
finished (0, true);
-
- if (_have_transaction) {
+
+ if (movement_occurred) {
_editor->undo ();
}
case EndTrim:
_pointer_frame_offset = raw_grab_frame() - i->initial_end;
break;
+ case ContentsTrim:
+ break;
}
}
_copy (c)
{
DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
-
_marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
assert (_marker);
}
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,
- *_editor->cursor_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);
-
- _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
+ show_verbose_cursor_time (adjusted_current_frame(event));
}
void
}
void
-MeterMarkerDrag::motion (GdkEvent* event, bool)
+MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
{
- framepos_t const pf = adjusted_current_frame (event);
+ if (first_move) {
+
+ // create a dummy marker for visual representation of moving the
+ // section, because whether its a copy or not, we're going to
+ // leave or lose the original marker (leave if its a copy; lose if its
+ // not, because we'll remove it from the map).
+
+ MeterSection section (_marker->meter());
+ if (!section.movable()) {
+ return;
+ }
+
+ 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);
-
- _editor->show_verbose_time_cursor (pf, 10);
+ show_verbose_cursor_time (pf);
}
void
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();
_editor->session()->add_command(new MementoCommand<TempoMap>(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<TempoMap>(map, &before, &after));
+ _editor->session()->add_command(new MementoCommand<TempoMap>(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 ()
+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)
_copy (c)
{
DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
-
+
_marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
assert (_marker);
}
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,
- *_editor->cursor_group,
ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
name,
*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);
-
- _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
-}
-
-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);
- _editor->show_verbose_time_cursor (pf, 10);
+ show_verbose_cursor_time (pf);
}
void
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"));
_editor->session()->add_command (new MementoCommand<TempoMap>(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<TempoMap>(map, &before, &after));
+ _editor->session()->add_command (new MementoCommand<TempoMap>(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 ()
+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)
CursorDrag::fake_locate (framepos_t t)
{
_editor->playhead_cursor->set_position (t);
-
+
Session* s = _editor->session ();
if (s->timecode_transmission_suspended ()) {
framepos_t const f = _editor->playhead_cursor->current_frame;
s->send_full_time_code (f);
}
- _editor->show_verbose_time_cursor (t, 10);
+ show_verbose_cursor_time (t);
_editor->UpdateAllTransportClocks (t);
}
{
Drag::start_grab (event, c);
+ _grab_zoom = _editor->frames_per_unit;
+
framepos_t where = _editor->event_frame (event, 0, 0);
_editor->snap_to_with_modifier (where, event);
_editor->_dragging_playhead = true;
-
+
Session* s = _editor->session ();
-
+
if (s) {
if (_was_rolling && _stop) {
s->request_stop ();
}
-
+
if (s->is_auditioning()) {
s->cancel_audition ();
}
-
+
s->request_suspend_timecode_transmission ();
while (!s->timecode_transmission_suspended ()) {
/* twiddle our thumbs */
}
}
-
+
fake_locate (where);
}
CursorDrag::motion (GdkEvent* event, bool)
{
framepos_t const adjusted_frame = adjusted_current_frame (event);
-
- if (adjusted_frame == last_pointer_frame()) {
- return;
- }
-
- fake_locate (adjusted_frame);
-
+ if (adjusted_frame != last_pointer_frame()) {
+ fake_locate (adjusted_frame);
#ifdef GTKOSX
- _editor->update_canvas_now ();
+ _editor->update_canvas_now ();
#endif
+ }
}
void
}
void
-CursorDrag::aborted ()
+CursorDrag::aborted (bool)
{
if (_editor->_dragging_playhead) {
_editor->session()->request_resume_timecode_transmission ();
_editor->_dragging_playhead = false;
}
-
+
_editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
}
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
- _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
-
+ 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);
}
tmp->show_fade_line((framecnt_t) fade_length);
}
- _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
+ show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
}
void
}
void
-FadeInDrag::aborted ()
+FadeInDrag::aborted (bool)
{
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
boost::shared_ptr<AudioRegion> r = arv->audio_region ();
- _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
-
+ 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);
}
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
boost::shared_ptr<AudioRegion> r = arv->audio_region ();
_pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
-}
+}
void
FadeOutDrag::motion (GdkEvent* event, bool)
tmp->show_fade_line(region->length() - fade_length);
}
- _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
+ show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
}
void
}
void
-FadeOutDrag::aborted ()
+FadeOutDrag::aborted (bool)
{
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
: Drag (e, i)
{
DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
-
+
_marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
assert (_marker);
_points.push_back (Gnome::Art::Point (0, 0));
_points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
-
- _line = new ArdourCanvas::Line (*_editor->timebar_group);
- _line->property_width_pixels() = 1;
- _line->property_points () = _points;
- _line->hide ();
-
- _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
}
MarkerDrag::~MarkerDrag ()
// _line->raise_to_top();
if (is_start) {
- _editor->show_verbose_time_cursor (location->start(), 10);
+ show_verbose_cursor_time (location->start());
} else {
- _editor->show_verbose_time_cursor (location->end(), 10);
+ show_verbose_cursor_time (location->end());
}
Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
framepos_t next = newframe;
- if (newframe == last_pointer_frame()) {
- return;
- }
-
if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
move_both = true;
}
copy_location->set_end (new_end);
} else if (new_start < copy_location->end()) {
copy_location->set_start (new_start);
- } else {
+ } else if (newframe > 0) {
_editor->snap_to (next, 1, true);
copy_location->set_end (next);
copy_location->set_start (newframe);
assert (!_copied_locations.empty());
- _editor->show_verbose_time_cursor (newframe, 10);
+ show_verbose_cursor_time (newframe);
#ifdef GTKOSX
_editor->update_canvas_now ();
XMLNode &after = _editor->session()->locations()->get_state();
_editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
_editor->commit_reversible_command ();
-
- _line->hide();
}
void
-MarkerDrag::aborted ()
+MarkerDrag::aborted (bool)
{
/* XXX: TODO */
}
void
-MarkerDrag::update_item (Location* location)
+MarkerDrag::update_item (Location*)
{
- double const x1 = _editor->frame_to_pixel (location->start());
-
- _points.front().set_x(x1);
- _points.back().set_x(x1);
- _line->property_points() = _points;
+ /* noop */
}
ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
_cumulative_x_drag (0),
_cumulative_y_drag (0)
{
+ if (_zero_gain_fraction < 0.0) {
+ _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
+ }
+
DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
-
+
_point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
assert (_point);
}
_point->line().start_drag_single (_point, _fixed_grab_x, fraction);
- _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
- event->button.x + 10, event->button.y + 10);
+ _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
+ event->button.x + 10, event->button.y + 10);
- _editor->show_verbose_canvas_cursor ();
+ _editor->verbose_cursor()->show ();
+
+ if (!_point->can_slide ()) {
+ _x_constrained = true;
+ }
}
void
cy = min ((double) _point->line().height(), cy);
framepos_t cx_frames = _editor->unit_to_frame (cx);
-
+
if (!_x_constrained) {
_editor->snap_to_with_modifier (cx_frames, event);
}
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, push);
- _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
+ _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
}
void
} else {
motion (event, false);
}
-
+
_point->line().end_drag ();
_editor->session()->commit_reversible_command ();
}
void
-ControlPointDrag::aborted ()
+ControlPointDrag::aborted (bool)
{
_point->line().reset ();
}
uint32_t before;
uint32_t after;
-
+
if (!_line->control_points_adjacent (frame_within_region, before, after)) {
/* no adjacent points */
return;
_line->start_drag_line (before, after, fraction);
- _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
- event->button.x + 10, event->button.y + 10);
+ _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
+ event->button.x + 10, event->button.y + 10);
- _editor->show_verbose_canvas_cursor ();
+ _editor->verbose_cursor()->show ();
}
void
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;
- }
+ bool const push = !Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
/* we are ignoring x position for this drag, so we can just pass in anything */
_line->drag_motion (0, fraction, true, push);
- _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
+ _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
}
void
}
void
-LineDrag::aborted ()
+LineDrag::aborted (bool)
{
_line->reset ();
}
FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
{
Drag::start_grab (event);
-
- _line = reinterpret_cast<SimpleLine*> (_item);
+
+ _line = reinterpret_cast<Line*> (_item);
assert (_line);
/* need to get x coordinate in terms of parent (AudioRegionView) origin. */
/* store grab start in parent frame */
_region_view_grab_x = cx;
-
- _before = _line->property_x1();
-
+
+ _before = *(float*) _item->get_data ("position");
+
_arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
-
+
_max_x = _editor->frame_to_pixel(_arv->get_duration());
}
FeatureLineDrag::motion (GdkEvent*, bool)
{
double dx = _drags->current_pointer_x() - last_pointer_x();
-
+
double cx = _region_view_grab_x + _cumulative_x_drag + dx;
-
+
_cumulative_x_drag += dx;
-
+
/* Clamp the min and max extent of the drag to keep it within the region view bounds */
-
+
if (cx > _max_x){
cx = _max_x;
}
else if(cx < 0){
cx = 0;
}
-
- _line->property_x1() = cx;
- _line->property_x2() = cx;
- _before = _line->property_x1();
+ ArdourCanvas::Points points;
+
+ double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+
+ _line->get_bounds(x1, y2, x2, y2);
+
+ points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
+ points.push_back(Gnome::Art::Point(cx, y2 - y1));
+
+ _line->property_points() = points;
+
+ float *pos = new float;
+ *pos = cx;
+
+ _line->set_data ("position", pos);
+
+ _before = cx;
}
void
FeatureLineDrag::finished (GdkEvent*, bool)
{
_arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
- _arv->update_transient(_before, _line->property_x1());
+ _arv->update_transient(_before, _before);
}
void
-FeatureLineDrag::aborted ()
+FeatureLineDrag::aborted (bool)
{
//_line->reset ();
}
RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
: Drag (e, i)
+ , _vertical_only (false)
{
DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
}
RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
{
Drag::start_grab (event);
- _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+ show_verbose_cursor_time (adjusted_current_frame (event));
}
void
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();
- _editor->show_verbose_time_cursor (pf, 10);
+ 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
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);
- bool committed;
+ /* just a click */
- _editor->begin_reversible_command (_("rubberband selection"));
+ bool do_deselect = true;
+ MidiTimeAxisView* mtv;
- if (grab_frame() < last_pointer_frame()) {
- committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
- } else {
- committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
- }
+ if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
+ /* MIDI track */
+ if (_editor->selection->empty()) {
+ /* nothing selected */
+ add_midi_region (mtv);
+ do_deselect = false;
+ }
+ }
- if (!committed) {
- _editor->commit_reversible_command ();
+ if (do_deselect) {
+ deselect_things ();
}
- } else {
- if (!getenv("ARDOUR_SAE")) {
- _editor->selection->clear_tracks();
- }
- _editor->selection->clear_regions();
- _editor->selection->clear_points ();
- _editor->selection->clear_lines ();
}
_editor->rubberband_rect->hide();
}
void
-RubberbandSelectDrag::aborted ()
+RubberbandSelectDrag::aborted (bool)
{
_editor->rubberband_rect->hide ();
}
{
Drag::start_grab (event, cursor);
- _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+ show_verbose_cursor_time (adjusted_current_frame (event));
}
void
TimeFXDrag::motion (GdkEvent* event, bool)
{
RegionView* rv = _primary;
+ StreamView* cv = rv->get_time_axis_view().view ();
+
+ pair<TimeAxisView*, double> 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);
}
- _editor->show_verbose_time_cursor (pf, 10);
+ show_verbose_cursor_time (pf);
}
void
}
#endif
- _editor->begin_reversible_command (_("timestretch"));
-
- // 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;
+ }
}
}
void
-TimeFXDrag::aborted ()
+TimeFXDrag::aborted (bool)
{
_primary->get_time_axis_view().hide_timestretch ();
}
}
void
-ScrubDrag::aborted ()
+ScrubDrag::aborted (bool)
{
/* XXX: TODO */
}
, _copy (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 (_operation == SelectionMove) {
- _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
+ show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
} else {
- _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+ show_verbose_cursor_time (adjusted_current_frame (event));
}
_original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
if (pending_time_axis.first == 0) {
return;
}
-
+
framepos_t const pending_position = adjusted_current_frame (event);
/* only alter selection if things have changed */
//_editor->selection->set (_editor->clicked_axisview);
_editor->set_selected_track_as_side_effect (Selection::Set);
}
-
+
_editor->clicked_selection = _editor->selection->set (start, end);
}
}
/* deselect any tracks that this drag no longer includes, being careful to only deselect
tracks that we selected in the first place.
*/
-
+
int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
list<TimeAxisView*>::iterator tmp = i;
++tmp;
-
+
if ((*i)->order() < min_order || (*i)->order() > max_order) {
_editor->selection->remove (*i);
_added_time_axes.remove (*i);
}
if (_operation == SelectionMove) {
- _editor->show_verbose_time_cursor(start, 10);
+ show_verbose_cursor_time(start);
} else {
- _editor->show_verbose_time_cursor(pending_position, 10);
+ show_verbose_cursor_time(pending_position);
}
}
}
/* 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->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
+ s->request_play_range (&_editor->selection->time, true);
+ } else {
+ if (Config->get_always_play_range()) {
+ if (_editor->doing_range_stuff()) {
+ s->request_locate (_editor->get_selection().time.start());
+ }
+ }
+ }
}
-
} else {
- /* just a click, no pointer movement.*/
+ /* just a click, no pointer movement.
+ */
if (Keyboard::no_modifier_keys_pressed (&event->button)) {
- _editor->selection->clear_time();
+ if (!_time_selection_at_start) {
+ if (_editor->clicked_regionview) {
+ if (_editor->get_selection().selected (_editor->clicked_regionview)) {
+ /* range select the entire current
+ region selection
+ */
+ _editor->select_range (_editor->get_selection().regions.start(),
+ _editor->get_selection().regions.end_frame());
+ } else {
+ /* range select this (unselected)
+ * region
+ */
+ _editor->select_range (_editor->clicked_regionview->region()->position(),
+ _editor->clicked_regionview->region()->last_frame());
+ }
+ }
+ } else {
+ _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);
}
+ if (Config->get_always_play_range()) {
+ if (_editor->doing_range_stuff()) {
+ s->request_locate (_editor->get_selection().time.start());
+ }
+ }
}
_editor->stop_canvas_autoscroll ();
}
void
-SelectionDrag::aborted ()
+SelectionDrag::aborted (bool)
{
/* XXX: TODO */
}
_copy (false)
{
DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
-
- _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
+
+ _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
physical_screen_height (_editor->get_window()));
_drag_rect->hide ();
Drag::start_grab (event, cursor);
- _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+ show_verbose_cursor_time (adjusted_current_frame (event));
}
void
if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
framepos_t grab = grab_frame ();
_editor->snap_to (grab);
-
+
if (pf < grab_frame()) {
start = pf;
end = grab;
update_item (_editor->temp_location);
}
- _editor->show_verbose_time_cursor (pf, 10);
+ show_verbose_cursor_time (pf);
}
newloc = new Location (
*_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
);
-
+
_editor->session()->locations()->add (newloc, true);
XMLNode &after = _editor->session()->locations()->get_state();
_editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
}
void
-RangeMarkerBarDrag::aborted ()
+RangeMarkerBarDrag::aborted (bool)
{
/* XXX: TODO */
}
Drag::start_grab (event, _editor->cursors()->zoom_in);
_zoom_out = false;
}
-
- _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+
+ show_verbose_cursor_time (adjusted_current_frame (event));
}
void
_editor->reposition_zoom_rect(start, end);
- _editor->show_verbose_time_cursor (pf, 10);
+ show_verbose_cursor_time (pf);
}
}
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 {
- _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
+ 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 ()
+MouseZoomDrag::aborted (bool)
{
_editor->zoom_rect->hide ();
}
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 ());
-
- /* new time of the primary note relative to the region position */
- frameoffset_t const st = n + dx;
+ frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
+
+ /* 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 */
+ 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
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;
_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));
-
- _editor->show_verbose_canvas_cursor_with (buf);
+ 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);
}
}
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) {
}
void
-NoteDrag::aborted ()
+NoteDrag::aborted (bool)
{
/* XXX: TODO */
}
-AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
- : Drag (editor, item)
+/** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
+AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
+ : Drag (editor, atv->base_item ())
+ , _ranges (r)
+ , _nothing_to_drag (false)
+{
+ DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
+
+ setup (atv->lines ());
+}
+
+/** Make an AutomationRangeDrag for region gain lines */
+AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
+ : Drag (editor, rv->get_canvas_group ())
, _ranges (r)
, _nothing_to_drag (false)
{
DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
-
- _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
- assert (_atav);
- /* get all lines in the automation view */
- list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
+ list<boost::shared_ptr<AutomationLine> > lines;
+ lines.push_back (rv->get_gain_line ());
+ setup (lines);
+}
- /* find those that overlap the ranges being dragged */
- list<boost::shared_ptr<AutomationLine> >::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<boost::shared_ptr<AutomationLine> > const & lines)
+{
+ /* find the lines that overlap the ranges being dragged */
+ list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
while (i != lines.end ()) {
- list<boost::shared_ptr<AutomationLine> >::iterator j = i;
+ list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
++j;
- pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
+ pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
/* check this range against all the AudioRanges that we are using */
list<AudioRange>::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;
i->points.push_back (i->line->nth (j));
}
}
-
+
} else {
for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
framecnt_t const half = (i->start + i->end) / 2;
-
+
/* find the line that this audio range starts in */
list<Line>::iterator j = _lines.begin();
while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
if (j != _lines.end()) {
boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
-
+
/* j is the line that this audio range starts in; fade into it;
64 samples length plucked out of thin air.
*/
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 */
-
+
j = _lines.begin();
while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
++j;
if (j != _lines.end()) {
boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
-
+
/* j is the line that this audio range starts in; fade out of it;
64 samples length plucked out of thin air.
*/
-
+
framepos_t b = i->end - 64;
if (b < half) {
b = half;
double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
-
+
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);
}
}
if (_nothing_to_drag) {
return;
}
-
+
motion (event, false);
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
i->line->end_drag ();
- i->line->clear_always_in_view ();
}
_editor->session()->commit_reversible_command ();
}
void
-AutomationRangeDrag::aborted ()
+AutomationRangeDrag::aborted (bool)
{
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
- i->line->clear_always_in_view ();
i->line->reset ();
}
}
initial_end = v->region()->position () + v->region()->length ();
}
-ProgramChangeDrag::ProgramChangeDrag (Editor* e, CanvasProgramChange* i, MidiRegionView* r)
+PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
: Drag (e, i)
, _region_view (r)
- , _program_change (i)
+ , _patch_change (i)
, _cumulative_dx (0)
{
- DEBUG_TRACE (DEBUG::Drags, "New ProgramChangeDrag\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
-ProgramChangeDrag::motion (GdkEvent* ev, bool)
+PatchChangeDrag::motion (GdkEvent* ev, bool)
{
framepos_t f = adjusted_current_frame (ev);
boost::shared_ptr<Region> r = _region_view->region ();
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);
- _program_change->move (dxu - _cumulative_dx, 0);
+
+ 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;
}
void
-ProgramChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
+PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
{
if (!movement_occurred) {
return;
}
boost::shared_ptr<Region> 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_program_change (
- MidiRegionView::PCEvent (_program_change->event_time(), _program_change->program(), _program_change->channel()),
- _region_view->frames_to_beats (f - r->position() - r->start())
+
+ _region_view->move_patch_change (
+ *_patch_change,
+ _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
);
}
void
-ProgramChangeDrag::aborted ()
+PatchChangeDrag::aborted (bool)
{
- _program_change->move (-_cumulative_dx, 0);
+ _patch_change->move (-_cumulative_dx, 0);
}
void
-ProgramChangeDrag::setup_pointer_frame_offset ()
+PatchChangeDrag::setup_pointer_frame_offset ()
{
boost::shared_ptr<Region> region = _region_view->region ();
- _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_program_change->event_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 = abs (_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)
+{
+}
+
+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<AudioRegion> 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<AudioRegion> 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<Command*> 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 ();
+ }
}