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;
}
}
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;
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);
/* 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) {
rv->get_time_axis_view().hide_dependent_views (*rv);
-
+
+ /* Absolutely no idea why this is necessary, but it is; without
+ it, the region view disappears after the reparent.
+ */
+ _editor->update_canvas_now ();
+
/* Reparent to a non scrolling group so that we can keep the
region selection above all time axis views.
Reparenting means that we will have to move the region view
later, as the two parent groups have different coordinates.
*/
-
+
rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
rv->fake_set_opaque (true);
-
- if (!rv->get_time_axis_view().hidden()) {
- /* the track that this region view is on is hidden, so hide the region too */
- rv->get_canvas_group()->hide ();
- }
}
/* If we have moved tracks, we'll fudge the layer delta so that the
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;
}
/* The TimeAxisView that this region is now on */
TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
+
+ /* Ensure it is moved from stacked -> expanded if appropriate */
+ if (tv->view()->layer_display() == Stacked) {
+ tv->view()->set_layer_display (Expanded);
+ }
+
+ /* 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 (tv->view()->child_height ());
/* And adjust for the layer that it should be on */
StreamView* cv = tv->view ();
- if (cv->layer_display() == Stacked) {
+ 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());
}
}
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 */
+ if (ev->type == GDK_2BUTTON_PRESS && ev->button.button == 1) {
+ /* double click - internal edit mode */
+ Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
+ act->activate ();
+ }
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 ();
if (_copy) {
RegionSelection new_views;
PlaylistSet modified_playlists;
PlaylistSet frozen_playlists;
+ set<RouteTimeAxisView*> views_to_update;
if (_brushing) {
/* all changes were made during motion event handlers */
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) {
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 */
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.
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 ();
void
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 ());
{
/* 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);
/* The region motion is only processed if the pointer is over
void
NoteResizeDrag::aborted (bool)
{
- /* XXX: TODO */
+ MidiRegionSelection& ms (_editor->get_selection().midi_regions);
+ for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
+ (*r)->abort_resizing ();
+ }
}
RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
_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,
- ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
- name,
- *new MeterSection (_marker->meter())
- );
-
- _item = &new_marker->the_item ();
- _marker = new_marker;
-
- } else {
-
- MetricSection& section (_marker->meter());
-
- if (!section.movable()) {
- return;
- }
-
- }
-
Drag::start_grab (event, cursor);
-
show_verbose_cursor_time (adjusted_current_frame(event));
}
}
void
-MeterMarkerDrag::motion (GdkEvent* event, bool)
+MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
{
- framepos_t const pf = adjusted_current_frame (event);
+ if (first_move) {
- _marker->set_position (pf);
+ // create a dummy marker for visual representation of moving the
+ // section, because whether its a copy or not, we're going to
+ // leave or lose the original marker (leave if its a copy; lose if its
+ // not, because we'll remove it from the map).
+
+ MeterSection section (_marker->meter());
+
+ if (!section.movable()) {
+ return;
+ }
+
+ char name[64];
+ snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
+
+ _marker = new MeterMarker (
+ *_editor,
+ *_editor->meter_group,
+ ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
+ name,
+ *new MeterSection (_marker->meter())
+ );
+
+ /* use the new marker for the grab */
+ swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
+
+ if (!_copy) {
+ TempoMap& map (_editor->session()->tempo_map());
+ /* get current state */
+ before_state = &map.get_state();
+ /* remove the section while we drag it */
+ map.remove_meter (section, true);
+ }
+ }
+ framepos_t const pf = adjusted_current_frame (event);
+ _marker->set_position (pf);
show_verbose_cursor_time (pf);
}
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 (bool)
+MeterMarkerDrag::aborted (bool moved)
{
_marker->set_position (_marker->meter().frame ());
+
+ if (moved) {
+ TempoMap& map (_editor->session()->tempo_map());
+ /* we removed it before, so add it back now */
+ map.add_meter (_marker->meter(), _marker->meter().frame());
+ // delete the dummy marker we used for visual representation while moving.
+ // a new visual marker will show up automatically.
+ delete _marker;
+ }
}
TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
void
TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
- if (_copy) {
+ Drag::start_grab (event, cursor);
+ show_verbose_cursor_time (adjusted_current_frame (event));
+}
+void
+TempoMarkerDrag::setup_pointer_frame_offset ()
+{
+ _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
+}
+
+void
+TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
+{
+ if (first_move) {
+
+ // create a dummy marker for visual representation of moving the
+ // section, because whether its a copy or not, we're going to
+ // leave or lose the original marker (leave if its a copy; lose if its
+ // not, because we'll remove it from the map).
+
// create a dummy marker for visual representation of moving the copy.
// The actual copying is not done before we reach the finish callback.
+
char name[64];
snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
- TempoMarker* new_marker = new TempoMarker (
+ TempoSection section (_marker->tempo());
+
+ _marker = new TempoMarker (
*_editor,
*_editor->tempo_group,
ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
*new TempoSection (_marker->tempo())
);
- _item = &new_marker->the_item ();
- _marker = new_marker;
+ /* use the new marker for the grab */
+ swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
+ if (!_copy) {
+ TempoMap& map (_editor->session()->tempo_map());
+ /* get current state */
+ before_state = &map.get_state();
+ /* remove the section while we drag it */
+ map.remove_tempo (section, true);
+ }
}
- Drag::start_grab (event, cursor);
-
- show_verbose_cursor_time (adjusted_current_frame (event));
-}
-
-void
-TempoMarkerDrag::setup_pointer_frame_offset ()
-{
- _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
-}
-
-void
-TempoMarkerDrag::motion (GdkEvent* event, bool)
-{
framepos_t const pf = adjusted_current_frame (event);
_marker->set_position (pf);
show_verbose_cursor_time (pf);
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 (bool)
+TempoMarkerDrag::aborted (bool moved)
{
_marker->set_position (_marker->tempo().frame());
+ if (moved) {
+ TempoMap& map (_editor->session()->tempo_map());
+ /* we removed it before, so add it back now */
+ map.add_tempo (_marker->tempo(), _marker->tempo().start());
+ // delete the dummy marker we used for visual representation while moving.
+ // a new visual marker will show up automatically.
+ delete _marker;
+ }
}
CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
void
CursorDrag::motion (GdkEvent* event, bool)
{
- if (_drags->current_pointer_y() != last_pointer_y()) {
-
- /* zoom when we move the pointer up and down */
-
- /* y range to operate over (pixels) */
- double const y_range = 512;
- /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
- double const scale_range = 4;
- /* dead zone around the grab point in which to do no zooming (pixels) */
- double const dead_zone = 100;
-
- /* current dy */
- double dy = _drags->current_pointer_y() - grab_y();
-
- if (dy < -dead_zone || dy > dead_zone) {
- /* we are outside the dead zone; remove it from our calculation */
- if (dy < 0) {
- dy += dead_zone;
- } else {
- dy -= dead_zone;
- }
-
- /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
- double udy = max (min (dy / y_range, 1.0), -1.0);
-
- /* and zoom, using playhead focus temporarily */
- Editing::ZoomFocus const zf = _editor->get_zoom_focus ();
- _editor->set_zoom_focus (Editing::ZoomFocusPlayhead);
- _editor->temporal_zoom (_grab_zoom * pow (scale_range, -udy));
- _editor->set_zoom_focus (zf);
- }
- }
-
framepos_t const adjusted_frame = adjusted_current_frame (event);
if (adjusted_frame != last_pointer_frame()) {
fake_locate (adjusted_frame);
_editor->rubberband_rect->raise_to_top();
show_verbose_cursor_time (pf);
+
+ do_select_things (event, true);
}
}
+void
+RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
+{
+ framepos_t x1;
+ framepos_t x2;
+
+ if (grab_frame() < last_pointer_frame()) {
+ x1 = grab_frame ();
+ x2 = last_pointer_frame ();
+ } else {
+ x2 = grab_frame ();
+ x1 = last_pointer_frame ();
+ }
+
+ double y1;
+ double y2;
+
+ if (_drags->current_pointer_y() < grab_y()) {
+ y1 = _drags->current_pointer_y();
+ y2 = grab_y();
+ } else {
+ y2 = _drags->current_pointer_y();
+ y1 = grab_y();
+ }
+
+ select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
+}
+
void
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();
- }
-
-
- Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
-
- _editor->begin_reversible_command (_("rubberband selection"));
-
- if (grab_frame() < last_pointer_frame()) {
- _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
- } else {
- _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
- }
+ } else {
- _editor->commit_reversible_command ();
+ 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();
/* 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, _region->region()->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 note number */
_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 */
+}
+
+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] = 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* event, 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)
+{
+
+}