summaryrefslogtreecommitdiff
path: root/gtk2_ardour
diff options
context:
space:
mode:
authornick_m <mainsbridge@gmail.com>2015-05-24 04:15:32 +1000
committernick_m <mainsbridge@gmail.com>2015-05-24 04:15:32 +1000
commita14c7ff066476aae1a30b3573c90153ed1565e66 (patch)
treeb2599569d53c39dfe6a5fe43ca88e12acf6fc694 /gtk2_ardour
parente44e0b2c9a00035608c10b02696e53cc8d0e535e (diff)
parentb86c3f97b09779e516939c40983977e5e5a0e458 (diff)
Merge branch 'relative_snap'
- Many changes to the "User Interaction" pane wrt key modifiers. - Snap is now relative by default (override to absolute with the "Snap to absolute using:" modifier). - Midi notes now obey both snap modifiers (disable snap and absolute snap) - Timefx drag now selects the primary region (less user confusion). - Includes MIDI event id fix.
Diffstat (limited to 'gtk2_ardour')
-rw-r--r--gtk2_ardour/editor.cc18
-rw-r--r--gtk2_ardour/editor.h6
-rw-r--r--gtk2_ardour/editor_drag.cc288
-rw-r--r--gtk2_ardour/editor_drag.h19
-rw-r--r--gtk2_ardour/keyboard.cc149
-rw-r--r--gtk2_ardour/keyboard.h56
-rw-r--r--gtk2_ardour/midi_region_view.cc102
-rw-r--r--gtk2_ardour/midi_region_view.h17
-rw-r--r--gtk2_ardour/public_editor.h8
-rw-r--r--gtk2_ardour/rc_option_editor.cc431
-rw-r--r--gtk2_ardour/region_view.cc8
-rw-r--r--gtk2_ardour/region_view.h4
12 files changed, 934 insertions, 172 deletions
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index df1421b6ce..474fb4c6f9 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -2133,6 +2133,7 @@ Editor::set_snap_mode (SnapMode mode)
instant_save ();
}
+
void
Editor::set_edit_point_preference (EditPoint ep, bool force)
{
@@ -2596,25 +2597,28 @@ Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, RoundM
return;
}
- if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
+ if (ArdourKeyboard::indicates_snap (event->button.state)) {
if (_snap_mode == SnapOff) {
snap_to_internal (start, direction, for_mark);
}
} else {
if (_snap_mode != SnapOff) {
snap_to_internal (start, direction, for_mark);
+ } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
+ /* SnapOff, but we pressed the snap_delta modifier */
+ snap_to_internal (start, direction, for_mark);
}
}
}
void
-Editor::snap_to (framepos_t& start, RoundMode direction, bool for_mark)
+Editor::snap_to (framepos_t& start, RoundMode direction, bool for_mark, bool ensure_snap)
{
- if (!_session || _snap_mode == SnapOff) {
+ if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
return;
}
- snap_to_internal (start, direction, for_mark);
+ snap_to_internal (start, direction, for_mark, ensure_snap);
}
void
@@ -2684,7 +2688,7 @@ Editor::timecode_snap_to_internal (framepos_t& start, RoundMode direction, bool
}
void
-Editor::snap_to_internal (framepos_t& start, RoundMode direction, bool for_mark)
+Editor::snap_to_internal (framepos_t& start, RoundMode direction, bool for_mark, bool ensure_snap)
{
const framepos_t one_second = _session->frame_rate();
const framepos_t one_minute = _session->frame_rate() * 60;
@@ -2854,6 +2858,10 @@ Editor::snap_to_internal (framepos_t& start, RoundMode direction, bool for_mark)
case SnapMagnetic:
+ if (ensure_snap) {
+ return;
+ }
+
if (presnap > start) {
if (presnap > (start + pixel_to_sample(snap_threshold))) {
start = presnap;
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index eaa9132e86..603103532f 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -436,7 +436,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void snap_to (framepos_t& first,
ARDOUR::RoundMode direction = ARDOUR::RoundNearest,
- bool for_mark = false);
+ bool for_mark = false,
+ bool ensure_snap = false);
void snap_to_with_modifier (framepos_t& first,
GdkEvent const * ev,
@@ -2131,7 +2132,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void snap_to_internal (framepos_t& first,
ARDOUR::RoundMode direction = ARDOUR::RoundNearest,
- bool for_mark = false);
+ bool for_mark = false,
+ bool ensure_snap = false);
void timecode_snap_to_internal (framepos_t& first,
ARDOUR::RoundMode direction = ARDOUR::RoundNearest,
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index 9fbf2efce8..00811f6eea 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -218,6 +218,8 @@ Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
: _editor (e)
, _item (i)
, _pointer_frame_offset (0)
+ , _x_constrained (false)
+ , _y_constrained (false)
, _trackview_only (trackview_only)
, _move_threshold_passed (false)
, _starting_point_passed (false)
@@ -226,6 +228,7 @@ Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
, _raw_grab_frame (0)
, _grab_frame (0)
, _last_pointer_frame (0)
+ , _snap_delta (0)
{
}
@@ -248,22 +251,11 @@ Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*t
void
Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
{
- // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
- if (Keyboard::is_button2_event (&event->button)) {
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
- _y_constrained = true;
- _x_constrained = false;
- } else {
- _y_constrained = false;
- _x_constrained = true;
- }
- } else {
- _x_constrained = false;
- _y_constrained = false;
- }
+ /* we set up x/y dragging constraints on first move */
_raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
+
setup_pointer_frame_offset ();
_grab_frame = adjusted_frame (_raw_grab_frame, event);
_last_pointer_frame = _grab_frame;
@@ -343,6 +335,16 @@ Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
return adjusted_frame (_drags->current_pointer_frame (), event, snap);
}
+frameoffset_t
+Drag::snap_delta (guint state) const
+{
+ if (ArdourKeyboard::indicates_snap_delta (state)) {
+ return 0;
+ }
+
+ return _snap_delta;
+}
+
double
Drag::current_pointer_x() const
{
@@ -359,6 +361,14 @@ Drag::current_pointer_y () const
return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
}
+void
+Drag::setup_snap_delta (framepos_t pos)
+{
+ framepos_t temp = pos;
+ _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
+ _snap_delta = temp - pos;
+}
+
bool
Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
{
@@ -394,6 +404,39 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
} else {
_initially_vertical = false;
}
+ /** check constraints for this drag.
+ * Note that the current convention is to use "contains" for
+ * key modifiers during motion and "equals" when initiating a drag.
+ * In this case we haven't moved yet, so "equals" applies here.
+ */
+ if (Config->get_edit_mode() != Lock) {
+ if (event->motion.state & Gdk::BUTTON2_MASK) {
+ // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
+ if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
+ _x_constrained = false;
+ _y_constrained = true;
+ } else {
+ _x_constrained = true;
+ _y_constrained = false;
+ }
+ } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
+ // if dragging normally, the motion is constrained to the first direction of movement.
+ if (_initially_vertical) {
+ _x_constrained = true;
+ _y_constrained = false;
+ } else {
+ _x_constrained = false;
+ _y_constrained = true;
+ }
+ }
+ } else {
+ if (event->button.state & Gdk::BUTTON2_MASK) {
+ _x_constrained = false;
+ } else {
+ _x_constrained = true;
+ }
+ _y_constrained = false;
+ }
}
if (!from_autoscroll) {
@@ -555,7 +598,6 @@ RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView
, _total_x_delta (0)
, _last_pointer_time_axis_view (0)
, _last_pointer_layer (0)
- , _single_axis (false)
, _ndropzone (0)
, _pdropzone (0)
, _ddropzone (0)
@@ -567,10 +609,7 @@ void
RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
Drag::start_grab (event, cursor);
-
- if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
- _single_axis = true;
- }
+ setup_snap_delta (_last_frame_position);
show_verbose_cursor_time (_last_frame_position);
@@ -588,7 +627,7 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r
/* compute the amount of pointer motion in frames, and where
the region would be if we moved it by that much.
*/
- *pending_region_position = adjusted_current_frame (event);
+ *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
framepos_t sync_frame;
framecnt_t sync_offset;
@@ -600,11 +639,11 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r
*/
if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
- sync_frame = *pending_region_position + (sync_dir*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);
+ *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
} else {
*pending_region_position = _last_frame_position;
@@ -616,8 +655,7 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r
double dx = 0;
- /* in locked edit mode, reverse the usual meaning of _x_constrained */
- bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
+ bool const x_move_allowed = !_x_constrained;
if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
@@ -750,18 +788,6 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
assert (!_views.empty ());
- if (first_move) {
- if (_single_axis) {
- if (initially_vertical()) {
- _y_constrained = false;
- _x_constrained = true;
- } else {
- _y_constrained = true;
- _x_constrained = false;
- }
- }
- }
-
/* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
/* Find the TimeAxisView that the pointer is now over */
@@ -1304,14 +1330,6 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
return;
}
- /* reverse this here so that we have the correct logic to finalize
- the drag.
- */
-
- if (Config->get_edit_mode() == Lock) {
- _x_constrained = !_x_constrained;
- }
-
assert (!_views.empty ());
/* We might have hidden region views so that they weren't visible during the drag
@@ -2318,6 +2336,7 @@ RegionCreateDrag::aborted (bool)
NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
: Drag (e, i)
, region (0)
+ , _snap_delta (0)
{
DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
}
@@ -2342,9 +2361,13 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
region = &cnote->region_view();
+ double temp;
+ temp = region->snap_to_pixel (cnote->x0 (), true);
+ _snap_delta = temp - cnote->x0 ();
+
_item->grab ();
- if (event->motion.state & Keyboard::PrimaryModifier) {
+ if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
relative = false;
} else {
relative = true;
@@ -2377,7 +2400,7 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
}
void
-NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
+NoteResizeDrag::motion (GdkEvent* event, bool /*first_move*/)
{
MidiRegionSelection& ms (_editor->get_selection().midi_regions);
for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
@@ -2385,21 +2408,62 @@ NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
assert (nb);
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
if (mrv) {
- mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
+ double sd = 0.0;
+ bool snap = true;
+ bool apply_snap_delta = !ArdourKeyboard::indicates_snap_delta (event->button.state);
+
+ if (ArdourKeyboard::indicates_snap (event->button.state)) {
+ if (_editor->snap_mode () != SnapOff) {
+ snap = false;
+ }
+ } else {
+ if (_editor->snap_mode () == SnapOff) {
+ snap = false;
+ /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
+ if (!apply_snap_delta) {
+ snap = true;
+ }
+ }
+ }
+
+ if (apply_snap_delta) {
+ sd = _snap_delta;
+ }
+
+ mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
}
}
}
void
-NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
+NoteResizeDrag::finished (GdkEvent* event, bool /*movement_occurred*/)
{
MidiRegionSelection& ms (_editor->get_selection().midi_regions);
for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
assert (nb);
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
+ double sd = 0.0;
+ bool snap = true;
+ bool apply_snap_delta = !ArdourKeyboard::indicates_snap_delta (event->button.state);
if (mrv) {
- mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
+ if (ArdourKeyboard::indicates_snap (event->button.state)) {
+ if (_editor->snap_mode () != SnapOff) {
+ snap = false;
+ }
+ } else {
+ if (_editor->snap_mode () == SnapOff) {
+ snap = false;
+ /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
+ if (!apply_snap_delta) {
+ snap = true;
+ }
+ }
+ }
+ if (apply_snap_delta) {
+ sd = _snap_delta;
+ }
+ mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
}
}
@@ -2597,8 +2661,9 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
framepos_t const pf = adjusted_current_frame (event);
+ setup_snap_delta (region_start);
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
+ if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
/* Move the contents of the region around without changing the region bounds */
_operation = ContentsTrim;
Drag::start_grab (event, _editor->cursors()->trimmer);
@@ -2607,8 +2672,7 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
if (pf < (region_start + region_length/2)) {
/* closer to front */
_operation = StartTrim;
-
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+ if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
} else {
Drag::start_grab (event, _editor->cursors()->left_side_trim);
@@ -2616,17 +2680,18 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
} else {
/* closer to end */
_operation = EndTrim;
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+ if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
} else {
Drag::start_grab (event, _editor->cursors()->right_side_trim);
}
}
}
-
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+ /* jump trim disabled for now
+ if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
_jump_position_when_done = true;
}
+ */
switch (_operation) {
case StartTrim:
@@ -2636,7 +2701,7 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
}
break;
case EndTrim:
- show_verbose_cursor_time (region_end);
+ show_verbose_cursor_duration (region_start, region_end);
break;
case ContentsTrim:
show_verbose_cursor_time (pf);
@@ -2662,8 +2727,8 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
if (tv && tv->is_track()) {
speed = tv->track()->speed();
}
-
- framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
+ framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
+ framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
if (first_move) {
@@ -2709,7 +2774,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
bool non_overlap_trim = false;
- if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+ if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
non_overlap_trim = true;
}
@@ -2795,7 +2860,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
break;
case EndTrim:
- show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
+ show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
break;
case ContentsTrim:
// show_verbose_cursor_time (frame_delta);
@@ -2803,7 +2868,6 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
}
}
-
void
TrimDrag::finished (GdkEvent* event, bool movement_occurred)
{
@@ -3247,10 +3311,11 @@ void
CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
{
Drag::start_grab (event, c);
+ setup_snap_delta (_editor->playhead_cursor->current_frame ());
_grab_zoom = _editor->samples_per_pixel;
- framepos_t where = _editor->canvas_event_sample (event);
+ framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
_editor->snap_to_with_modifier (where, event);
@@ -3288,15 +3353,16 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
}
}
- fake_locate (where);
+ fake_locate (where - snap_delta (event->button.state));
}
void
CursorDrag::motion (GdkEvent* event, bool)
{
- framepos_t const adjusted_frame = adjusted_current_frame (event);
- if (adjusted_frame != last_pointer_frame()) {
- fake_locate (adjusted_frame);
+ framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
+ _editor->snap_to_with_modifier (where, event);
+ if (where != last_pointer_frame()) {
+ fake_locate (where - snap_delta (event->button.state));
}
}
@@ -3347,6 +3413,7 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
+ setup_snap_delta (r->position ());
show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
}
@@ -3363,7 +3430,11 @@ void
FadeInDrag::motion (GdkEvent* event, bool)
{
framecnt_t fade_length;
- framepos_t const pos = adjusted_current_frame (event);
+
+ framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
+ _editor->snap_to_with_modifier (pos, event);
+ pos -= snap_delta (event->button.state);
+
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
if (pos < (region->position() + 64)) {
@@ -3396,8 +3467,9 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
}
framecnt_t fade_length;
-
- framepos_t const pos = adjusted_current_frame (event);
+ framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
+ _editor->snap_to_with_modifier (pos, event);
+ pos -= snap_delta (event->button.state);
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
@@ -3459,6 +3531,7 @@ FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
boost::shared_ptr<AudioRegion> r = arv->audio_region ();
+ setup_snap_delta (r->last_frame ());
show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
}
@@ -3476,7 +3549,9 @@ FadeOutDrag::motion (GdkEvent* event, bool)
{
framecnt_t fade_length;
- framepos_t const pos = adjusted_current_frame (event);
+ framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
+ _editor->snap_to_with_modifier (pos, event);
+ pos -= snap_delta (event->button.state);
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
@@ -3511,7 +3586,9 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
framecnt_t fade_length;
- framepos_t const pos = adjusted_current_frame (event);
+ framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
+ _editor->snap_to_with_modifier (pos, event);
+ pos -= snap_delta (event->button.state);
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
@@ -3707,7 +3784,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
framepos_t const newframe = adjusted_current_frame (event);
framepos_t next = newframe;
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
+ if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
move_both = true;
}
@@ -3965,13 +4042,16 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
_fixed_grab_x = _point->get_x();
_fixed_grab_y = _point->get_y();
+ framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
+ setup_snap_delta (pos);
+
float const fraction = 1 - (_point->get_y() / _point->line().height());
_point->line().start_drag_single (_point, _fixed_grab_x, fraction);
show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
- _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
+ _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
if (!_point->can_slide ()) {
_x_constrained = true;
@@ -3984,7 +4064,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
double dx = _drags->current_pointer_x() - last_pointer_x();
double dy = current_pointer_y() - last_pointer_y();
- if (event->button.state & Keyboard::SecondaryModifier) {
+ if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
dx *= 0.1;
dy *= 0.1;
}
@@ -4017,12 +4097,13 @@ ControlPointDrag::motion (GdkEvent* event, bool)
cy = max (0.0, cy);
cy = min ((double) _point->line().height(), cy);
- framepos_t cx_frames = _editor->pixel_to_sample (cx);
+ framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
if (!_x_constrained) {
_editor->snap_to_with_modifier (cx_frames, event);
}
+ cx_frames -= snap_delta (event->button.state);
cx_frames = min (cx_frames, _point->line().maximum_time());
float const fraction = 1.0 - (cy / _point->line().height());
@@ -4038,8 +4119,7 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
if (!movement_occurred) {
/* just a click */
-
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+ if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
_editor->reset_point_selection ();
}
@@ -4070,9 +4150,9 @@ ControlPointDrag::active (Editing::MouseMode m)
}
LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
- : Drag (e, i),
- _line (0),
- _cumulative_y_drag (0)
+ : Drag (e, i)
+ , _line (0)
+ , _cumulative_y_drag (0)
{
DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
}
@@ -4123,7 +4203,7 @@ LineDrag::motion (GdkEvent* event, bool)
{
double dy = current_pointer_y() - last_pointer_y();
- if (event->button.state & Keyboard::SecondaryModifier) {
+ if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
dy *= 0.1;
}
@@ -4432,7 +4512,12 @@ TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
Drag::start_grab (event, cursor);
- show_verbose_cursor_time (adjusted_current_frame (event));
+ _editor->get_selection().add (_primary);
+
+ framepos_t where = _primary->region()->position();
+ setup_snap_delta (where);
+
+ show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
}
void
@@ -4444,14 +4529,15 @@ TimeFXDrag::motion (GdkEvent* event, bool)
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);
+ framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
+ _editor->snap_to_with_modifier (pf, event);
+ pf -= snap_delta (event->button.state);
if (pf > rv->region()->position()) {
rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
}
- show_verbose_cursor_time (pf);
+ show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
}
void
@@ -4909,7 +4995,7 @@ RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
case CreateTransportMarker:
case CreateCDMarker:
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+ if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
_copy = true;
} else {
_copy = false;
@@ -5130,6 +5216,7 @@ void
NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
{
Drag::start_grab (event);
+ setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
if (!(_was_selected = _primary->selected())) {
@@ -5157,7 +5244,7 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
/** @return Current total drag x change in frames */
frameoffset_t
-NoteDrag::total_dx () const
+NoteDrag::total_dx (const guint state) const
{
/* dx in frames */
frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
@@ -5166,15 +5253,32 @@ NoteDrag::total_dx () const
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;
+ frameoffset_t st = n + dx + snap_delta (state);
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 - rp) + rp - n;
+ /* possibly snap and return corresponding delta */
+
+ bool snap = true;
+
+ if (ArdourKeyboard::indicates_snap (state)) {
+ if (_editor->snap_mode () != SnapOff) {
+ snap = false;
+ }
+ } else {
+ if (_editor->snap_mode () == SnapOff) {
+ snap = false;
+ /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
+ if (ArdourKeyboard::indicates_snap_delta (state)) {
+ snap = true;
+ }
+ }
+ }
+
+ return _region->snap_frame_to_frame (st - rp, snap) + rp - n - snap_delta (state);
}
/** @return Current total drag y change in note number */
@@ -5193,10 +5297,10 @@ NoteDrag::total_dy () const
}
void
-NoteDrag::motion (GdkEvent *, bool)
+NoteDrag::motion (GdkEvent * event, bool)
{
/* Total change in x and y since the start of the drag */
- frameoffset_t const dx = total_dx ();
+ frameoffset_t const dx = total_dx (event->button.state);
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 */
@@ -5265,7 +5369,7 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
}
}
} else {
- _region->note_dropped (_primary, total_dx(), total_dy());
+ _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
}
}
diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h
index e6f800cb52..6fcd92751a 100644
--- a/gtk2_ardour/editor_drag.h
+++ b/gtk2_ardour/editor_drag.h
@@ -213,13 +213,18 @@ protected:
return _last_pointer_y;
}
- double last_pointer_frame () const {
+ ARDOUR::framepos_t last_pointer_frame () const {
return _last_pointer_frame;
}
+ ARDOUR::frameoffset_t snap_delta (guint const) const;
+
double current_pointer_x () const;
double current_pointer_y () const;
+ /* sets snap delta from unsnapped pos */
+ void setup_snap_delta (framepos_t pos);
+
boost::shared_ptr<ARDOUR::Region> add_midi_region (MidiTimeAxisView*);
void show_verbose_cursor_time (framepos_t);
@@ -248,6 +253,11 @@ private:
ARDOUR::framepos_t _raw_grab_frame; ///< unsnapped frame that the mouse was at when start_grab was called, or 0
ARDOUR::framepos_t _grab_frame; ///< adjusted_frame that the mouse was at when start_grab was called, or 0
ARDOUR::framepos_t _last_pointer_frame; ///< adjusted_frame the last time a motion occurred
+
+ /* difference between some key position's snapped and unsnapped
+ * framepos. used for relative snap.
+ */
+ ARDOUR::frameoffset_t _snap_delta;
CursorContext::Handle _cursor_ctx; ///< cursor change context
};
@@ -335,8 +345,6 @@ protected:
double _total_x_delta;
int _last_pointer_time_axis_view;
double _last_pointer_layer;
- bool _single_axis;
-
private:
uint32_t _ndropzone;
uint32_t _pdropzone;
@@ -503,6 +511,7 @@ private:
MidiRegionView* region;
bool relative;
bool at_front;
+ double _snap_delta;
};
/** Drags to move MIDI notes */
@@ -518,7 +527,7 @@ class NoteDrag : public Drag
private:
- ARDOUR::frameoffset_t total_dx () const;
+ ARDOUR::frameoffset_t total_dx (const guint) const;
int8_t total_dy () const;
MidiRegionView* _region;
@@ -541,7 +550,7 @@ public:
void aborted (bool);
bool active (Editing::MouseMode mode) {
- return mode == Editing::MouseDraw;
+ return mode == Editing::MouseDraw || mode == Editing::MouseContent;
}
bool y_movement_matters () const {
diff --git a/gtk2_ardour/keyboard.cc b/gtk2_ardour/keyboard.cc
index ca65a2b3f2..96c73f63d4 100644
--- a/gtk2_ardour/keyboard.cc
+++ b/gtk2_ardour/keyboard.cc
@@ -48,6 +48,14 @@ accel_map_changed (GtkAccelMap* /*map*/,
me->ui.setup_tooltips ();
}
+guint ArdourKeyboard::constraint_mod = Keyboard::SecondaryModifier;
+guint ArdourKeyboard::trim_contents_mod = Keyboard::PrimaryModifier;
+guint ArdourKeyboard::trim_overlap_mod = Keyboard::TertiaryModifier;
+guint ArdourKeyboard::trim_anchored_mod = Keyboard::TertiaryModifier;
+guint ArdourKeyboard::fine_adjust_mod = Keyboard::SecondaryModifier;
+guint ArdourKeyboard::push_points_mod = Keyboard::PrimaryModifier;
+guint ArdourKeyboard::note_size_relative_mod = Keyboard::PrimaryModifier;
+
void
ArdourKeyboard::setup_keybindings ()
{
@@ -174,6 +182,145 @@ ArdourKeyboard::setup_keybindings ()
g_signal_connect (accelmap, "changed", (GCallback) accel_map_changed, this);
}
+XMLNode&
+ArdourKeyboard::get_state (void)
+{
+ XMLNode* node = &Keyboard::get_state ();
+ char buf[32];
+
+ snprintf (buf, sizeof (buf), "%d", constraint_mod);
+ node->add_property ("constraint-modifier", buf);
+ snprintf (buf, sizeof (buf), "%d", trim_contents_mod);
+ node->add_property ("trim-contents-modifier", buf);
+ snprintf (buf, sizeof (buf), "%d", trim_overlap_mod);
+ node->add_property ("trim-overlap-modifier", buf);
+ snprintf (buf, sizeof (buf), "%d", trim_anchored_mod);
+ node->add_property ("trim-anchored-modifier", buf);
+ snprintf (buf, sizeof (buf), "%d", fine_adjust_mod);
+ node->add_property ("fine-adjust-modifier", buf);
+ snprintf (buf, sizeof (buf), "%d", push_points_mod);
+ node->add_property ("push-points-modifier", buf);
+ snprintf (buf, sizeof (buf), "%d", note_size_relative_mod);
+ node->add_property ("note-size-relative-modifier", buf);
+
+ return *node;
+}
+
+int
+ArdourKeyboard::set_state (const XMLNode& node, int version)
+{
+ const XMLProperty* prop;
+
+ if ((prop = node.property ("constraint-modifier")) != 0) {
+ sscanf (prop->value().c_str(), "%d", &constraint_mod);
+ }
+
+ if ((prop = node.property ("trim-contents-modifier")) != 0) {
+ sscanf (prop->value().c_str(), "%d", &trim_contents_mod);
+ }
+
+ if ((prop = node.property ("trim-overlap-modifier")) != 0) {
+ sscanf (prop->value().c_str(), "%d", &trim_overlap_mod);
+ }
+
+ if ((prop = node.property ("trim-anchored-modifier")) != 0) {
+ sscanf (prop->value().c_str(), "%d", &trim_anchored_mod);
+ }
+
+ if ((prop = node.property ("fine-adjust-modifier")) != 0) {
+ sscanf (prop->value().c_str(), "%d", &fine_adjust_mod);
+ }
+
+ if ((prop = node.property ("push-points-modifier")) != 0) {
+ sscanf (prop->value().c_str(), "%d", &push_points_mod);
+ }
+
+ if ((prop = node.property ("note-size-relative-modifier")) != 0) {
+ sscanf (prop->value().c_str(), "%d", &note_size_relative_mod);
+ }
+
+ return Keyboard::set_state (node, version);
+}
+
+/* Snap and snap delta modifiers may contain each other, so we use the
+ * following two methods to sort that out:
+ */
+bool
+ArdourKeyboard::indicates_snap (guint state)
+{
+ const bool contains_s = Keyboard::modifier_state_contains (state, Keyboard::snap_modifier());
+ const bool contains_d = Keyboard::modifier_state_contains (state, Keyboard::snap_delta_modifier());
+ const bool equals_d = Keyboard::modifier_state_equals (state, Keyboard::snap_delta_modifier());
+
+ return (contains_s && ((contains_d && !equals_d) || !contains_d));
+}
+
+bool
+ArdourKeyboard::indicates_snap_delta (guint state)
+{
+ const bool contains_d = Keyboard::modifier_state_contains (state, Keyboard::snap_delta_modifier());
+ const bool contains_s = Keyboard::modifier_state_contains (state, Keyboard::snap_modifier());
+ const bool equals_s = Keyboard::modifier_state_equals (state, Keyboard::snap_modifier());
+
+ return (contains_d && ((contains_s && !equals_s) || !contains_s));
+}
+
+void
+ArdourKeyboard::set_constraint_modifier (guint mod)
+{
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~constraint_mod);
+ constraint_mod = mod;
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | constraint_mod);
+}
+
+void
+ArdourKeyboard::set_trim_contents_modifier (guint mod)
+{
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~trim_contents_mod);
+ trim_contents_mod = mod;
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | trim_contents_mod);
+}
+
+void
+ArdourKeyboard::set_trim_overlap_modifier (guint mod)
+{
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~trim_overlap_mod);
+ trim_overlap_mod = mod;
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | trim_overlap_mod);
+}
+
+void
+ArdourKeyboard::set_trim_anchored_modifier (guint mod)
+{
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~trim_anchored_mod);
+ trim_anchored_mod = mod;
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | trim_anchored_mod);
+}
+
+void
+ArdourKeyboard::set_fine_adjust_modifier (guint mod)
+{
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~fine_adjust_mod);
+ fine_adjust_mod = mod;
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | fine_adjust_mod);
+}
+
+void
+ArdourKeyboard::set_push_points_modifier (guint mod)
+{
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~push_points_mod);
+ push_points_mod = mod;
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | push_points_mod);
+}
+
+void
+ArdourKeyboard::set_note_size_relative_modifier (guint mod)
+{
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~note_size_relative_mod);
+ note_size_relative_mod = mod;
+ RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | note_size_relative_mod);
+}
+
Selection::Operation
ArdourKeyboard::selection_type (guint state)
{
@@ -187,5 +334,3 @@ ArdourKeyboard::selection_type (guint state)
return Selection::Set;
}
}
-
-
diff --git a/gtk2_ardour/keyboard.h b/gtk2_ardour/keyboard.h
index 6a689567e5..9ca60950bb 100644
--- a/gtk2_ardour/keyboard.h
+++ b/gtk2_ardour/keyboard.h
@@ -32,11 +32,67 @@ class ArdourKeyboard : public Gtkmm2ext::Keyboard
public:
ArdourKeyboard(ARDOUR_UI& ardour_ui) : ui(ardour_ui) {}
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&, int version);
+
void setup_keybindings ();
static Selection::Operation selection_type (guint state);
ARDOUR_UI& ui;
+
+ /** @param state The button state from a GdkEvent.
+ * @return true if the modifier state indicates snap modifier
+ */
+ static bool indicates_snap (guint state);
+
+ /** @param state The button state from a GdkEvent.
+ * @return true if the modifier state indicates snap delta
+ */
+ static bool indicates_snap_delta (guint state);
+
+ static void set_constraint_modifier (guint);
+ /** @return Modifier mask to constrain drags in a particular direction;
+ */
+ static ModifierMask constraint_modifier () { return ModifierMask (constraint_mod); }
+
+ static void set_trim_contents_modifier (guint);
+ /** @return Modifier mask to move contents rather than region bounds during trim;
+ */
+ static ModifierMask trim_contents_modifier () { return ModifierMask (trim_contents_mod); }
+
+ static void set_trim_overlap_modifier (guint);
+ /** @return Modifier mask to remove region overlaps during trim;
+ */
+ static ModifierMask trim_overlap_modifier () { return ModifierMask (trim_overlap_mod); }
+
+ static void set_trim_anchored_modifier (guint);
+ /** @return Modifier mask to use anchored trim;
+ */
+ static ModifierMask trim_anchored_modifier () { return ModifierMask (trim_anchored_mod); }
+
+ static void set_fine_adjust_modifier (guint);
+ /** @return Modifier mask to fine adjust (control points only atm);
+ */
+ static ModifierMask fine_adjust_modifier () { return ModifierMask (fine_adjust_mod); }
+
+ static void set_push_points_modifier (guint);
+ /** @return Modifier mask to push proceeding points;
+ */
+ static ModifierMask push_points_modifier () { return ModifierMask (push_points_mod); }
+
+ static void set_note_size_relative_modifier (guint);
+ /** @return Modifier mask to resize notes relatively;
+ */
+ static ModifierMask note_size_relative_modifier () { return ModifierMask (note_size_relative_mod); }
+private:
+ static guint constraint_mod;
+ static guint trim_contents_mod;
+ static guint trim_overlap_mod;
+ static guint trim_anchored_mod;
+ static guint fine_adjust_mod;
+ static guint push_points_mod;
+ static guint note_size_relative_mod;
};
#endif /* __ardour_keyboard_h__ */
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc
index eed9017d0d..7c74b5f1e0 100644
--- a/gtk2_ardour/midi_region_view.cc
+++ b/gtk2_ardour/midi_region_view.cc
@@ -104,6 +104,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
, _current_range_max(0)
, _region_relative_time_converter(r->session().tempo_map(), r->position())
, _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
+ , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
, _active_notes(0)
, _note_group (new ArdourCanvas::Container (group))
, _note_diff_command (0)
@@ -151,6 +152,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
, _current_range_max(0)
, _region_relative_time_converter(r->session().tempo_map(), r->position())
, _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
+ , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
, _active_notes(0)
, _note_group (new ArdourCanvas::Container (group))
, _note_diff_command (0)
@@ -203,6 +205,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
, _current_range_max(0)
, _region_relative_time_converter(other.region_relative_time_converter())
, _source_relative_time_converter(other.source_relative_time_converter())
+ , _region_relative_time_converter_double(other.region_relative_time_converter_double())
, _active_notes(0)
, _note_group (new ArdourCanvas::Container (get_canvas_group()))
, _note_diff_command (0)
@@ -234,6 +237,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
, _current_range_max(0)
, _region_relative_time_converter(other.region_relative_time_converter())
, _source_relative_time_converter(other.source_relative_time_converter())
+ , _region_relative_time_converter_double(other.region_relative_time_converter_double())
, _active_notes(0)
, _note_group (new ArdourCanvas::Container (get_canvas_group()))
, _note_diff_command (0)
@@ -1408,6 +1412,7 @@ MidiRegionView::region_resized (const PropertyChange& what_changed)
if (what_changed.contains (ARDOUR::Properties::position)) {
_region_relative_time_converter.set_origin_b(_region->position());
+ _region_relative_time_converter_double.set_origin_b(_region->position());
set_duration(_region->length(), 0);
if (_enable_display) {
redisplay_model();
@@ -2619,22 +2624,25 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
}
/** @param x Pixel relative to the region position.
+ * @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
+ * Used for inverting the snap logic with key modifiers and snap delta calculation.
* @return Snapped frame relative to the region position.
*/
framepos_t
-MidiRegionView::snap_pixel_to_sample(double x)
+MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
{
PublicEditor& editor (trackview.editor());
- return snap_frame_to_frame (editor.pixel_to_sample (x));
+ return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap);
}
/** @param x Pixel relative to the region position.
+ * @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
* @return Snapped pixel relative to the region position.
*/
double
-MidiRegionView::snap_to_pixel(double x)
+MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
{
- return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
+ return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
}
double
@@ -2685,6 +2693,12 @@ MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
return _region_relative_time_converter.from(frames);
}
+double
+MidiRegionView::region_frames_to_region_beats_double (framepos_t frames) const
+{
+ return _region_relative_time_converter_double.from(frames);
+}
+
void
MidiRegionView::begin_resizing (bool /*at_front*/)
{
@@ -2733,9 +2747,11 @@ MidiRegionView::begin_resizing (bool /*at_front*/)
* a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
* amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
* as the \a primary note.
+ * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
+ * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
*/
void
-MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
+MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
{
bool cursor_set = false;
@@ -2746,15 +2762,15 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
if (at_front) {
if (relative) {
- current_x = canvas_note->x0() + delta_x;
+ current_x = canvas_note->x0() + delta_x + snap_delta;
} else {
- current_x = primary->x0() + delta_x;
+ current_x = primary->x0() + delta_x + snap_delta;
}
} else {
if (relative) {
- current_x = canvas_note->x1() + delta_x;
+ current_x = canvas_note->x1() + delta_x + snap_delta;
} else {
- current_x = primary->x1() + delta_x;
+ current_x = primary->x1() + delta_x + snap_delta;
}
}
@@ -2768,26 +2784,47 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
}
if (at_front) {
- resize_rect->set_x0 (snap_to_pixel(current_x));
+ if (with_snap) {
+ resize_rect->set_x0 (snap_to_pixel(current_x, true) - snap_delta);
+ } else {
+ resize_rect->set_x0 (current_x - snap_delta);
+ }
resize_rect->set_x1 (canvas_note->x1());
} else {
- resize_rect->set_x1 (snap_to_pixel(current_x));
+ if (with_snap) {
+ resize_rect->set_x1 (snap_to_pixel(current_x, true) - snap_delta);
+ } else {
+ resize_rect->set_x1 (current_x - snap_delta);
+ }
resize_rect->set_x0 (canvas_note->x0());
}
if (!cursor_set) {
- const double snapped_x = snap_pixel_to_sample (current_x);
+ /* Convert snap delta from pixels to beats. */
+ framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
+ double snap_delta_beats;
+ int sign = 1;
+
+ /* negative beat offsets aren't allowed */
+ if (snap_delta_samps > 0) {
+ snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
+ } else if (snap_delta_samps < 0) {
+ snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
+ sign = -1;
+ }
+
+ const double snapped_x = (with_snap ? snap_pixel_to_sample (current_x, true) : trackview.editor ().pixel_to_sample (current_x));
Evoral::Beats beats = region_frames_to_region_beats (snapped_x);
Evoral::Beats len = Evoral::Beats();
if (at_front) {
if (beats < canvas_note->note()->end_time()) {
- len = canvas_note->note()->time() - beats;
+ len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
len += canvas_note->note()->length();
}
} else {
if (beats >= canvas_note->note()->time()) {
- len = beats - canvas_note->note()->time();
+ len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
}
}
@@ -2808,7 +2845,7 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
* Parameters the same as for \a update_resizing().
*/
void
-MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
+MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
{
_note_diff_command = _model->new_note_diff_command (_("resize notes"));
@@ -2824,15 +2861,15 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_
if (at_front) {
if (relative) {
- current_x = canvas_note->x0() + delta_x;
+ current_x = canvas_note->x0() + delta_x + snap_delta;
} else {
- current_x = primary->x0() + delta_x;
+ current_x = primary->x0() + delta_x + snap_delta;
}
} else {
if (relative) {
- current_x = canvas_note->x1() + delta_x;
+ current_x = canvas_note->x1() + delta_x + snap_delta;
} else {
- current_x = primary->x1() + delta_x;
+ current_x = primary->x1() + delta_x + snap_delta;
}
}
@@ -2843,16 +2880,27 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_
current_x = trackview.editor().sample_to_pixel(_region->length());
}
- /* Convert that to a frame within the source */
- current_x = snap_pixel_to_sample (current_x) + _region->start ();
+ /* Convert snap delta from pixels to beats with sign. */
+ framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
+ double snap_delta_beats;
+ int sign = 1;
+
+ if (snap_delta_samps > 0) {
+ snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
+ } else if (snap_delta_samps < 0) {
+ snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
+ sign = -1;
+ }
+
+ /* Convert the new x position to a frame within the source */
+ const framepos_t current_fr = snap_pixel_to_sample (current_x, with_snap) + _region->start ();
/* and then to beats */
- const Evoral::Beats x_beats = region_frames_to_region_beats (current_x);
+ const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr);
if (at_front && x_beats < canvas_note->note()->end_time()) {
- note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
-
- Evoral::Beats len = canvas_note->note()->time() - x_beats;
+ note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
+ Evoral::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
len += canvas_note->note()->length();
if (!!len) {
@@ -2861,8 +2909,8 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_
}
if (!at_front) {
- const Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
- x_beats - canvas_note->note()->time());
+ Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
+ x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
}
diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h
index 776543041c..4cab6b16bf 100644
--- a/gtk2_ardour/midi_region_view.h
+++ b/gtk2_ardour/midi_region_view.h
@@ -223,8 +223,8 @@ public:
*/
void begin_resizing(bool at_front);
- void update_resizing (NoteBase*, bool, double, bool);
- void commit_resizing (NoteBase*, bool, double, bool);
+ void update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap);
+ void commit_resizing (NoteBase* primary, bool at_front, double delat_x, bool relative, double snap_delta, bool with_snap);
void abort_resizing ();
/** Change the channel of the selection.
@@ -250,15 +250,18 @@ public:
/** Snap a region relative pixel coordinate to pixel units.
* @param x a pixel coordinate relative to region start
+ * @param ensure_snap do not use magnetic snap (required for snap delta calculation)
* @return the snapped pixel coordinate relative to region start
*/
- double snap_to_pixel(double x);
+ double snap_to_pixel(double x, bool ensure_snap = false);
/** Snap a region relative pixel coordinate to frame units.
* @param x a pixel coordinate relative to region start
+ * @param ensure_snap ignore SnapOff and magnetic snap.
+ * Required for inverting snap logic with modifier keys and snap delta calculation.
* @return the snapped framepos_t coordinate relative to region start
*/
- framepos_t snap_pixel_to_sample(double x);
+ framepos_t snap_pixel_to_sample(double x, bool ensure_snap = false);
/** Convert a timestamp in beats into frames (both relative to region position) */
framepos_t region_beats_to_region_frames(Evoral::Beats beats) const;
@@ -268,6 +271,7 @@ public:
}
/** Convert a timestamp in frames to beats (both relative to region position) */
Evoral::Beats region_frames_to_region_beats(framepos_t) const;
+ double region_frames_to_region_beats_double(framepos_t) const;
/** Convert a timestamp in beats measured from source start into absolute frames */
framepos_t source_beats_to_absolute_frames(Evoral::Beats beats) const;
@@ -286,6 +290,10 @@ public:
return _source_relative_time_converter;
}
+ ARDOUR::DoubleBeatsFramesConverter const & region_relative_time_converter_double () const {
+ return _region_relative_time_converter_double;
+ }
+
void goto_previous_note (bool add_to_selection);
void goto_next_note (bool add_to_selection);
void change_note_lengths (bool, bool, Evoral::Beats beats, bool start, bool end);
@@ -400,6 +408,7 @@ private:
ARDOUR::BeatsFramesConverter _region_relative_time_converter;
ARDOUR::BeatsFramesConverter _source_relative_time_converter;
+ ARDOUR::DoubleBeatsFramesConverter _region_relative_time_converter_double;
boost::shared_ptr<ARDOUR::MidiModel> _model;
Events _events;
diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h
index cf68f35fb9..951c3e9b0e 100644
--- a/gtk2_ardour/public_editor.h
+++ b/gtk2_ardour/public_editor.h
@@ -141,10 +141,14 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
*/
virtual void set_snap_threshold (double t) = 0;
- /** Snap a value according to the current snap setting. */
+ /**
+ * Snap a value according to the current snap setting.
+ * ensure_snap overrides SnapOff and magnetic snap
+ */
virtual void snap_to (framepos_t& first,
ARDOUR::RoundMode direction = ARDOUR::RoundNearest,
- bool for_mark = false) = 0;
+ bool for_mark = false,
+ bool ensure_snap = false) = 0;
/** Undo some transactions.
* @param n Number of transactions to undo.
diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc
index bcefc1260b..9b38b558c9 100644
--- a/gtk2_ardour/rc_option_editor.cc
+++ b/gtk2_ardour/rc_option_editor.cc
@@ -271,20 +271,22 @@ static const struct {
{ "Option", GDK_MOD1_MASK },
{ "Command-Shift", GDK_META_MASK|GDK_SHIFT_MASK },
{ "Command-Option", GDK_MOD1_MASK|GDK_META_MASK },
- { "Shift-Option", GDK_SHIFT_MASK|GDK_MOD1_MASK },
+ { "Option-Shift", GDK_MOD1_MASK|GDK_SHIFT_MASK },
+ { "Control-Shift", GDK_CONTROL_MASK|GDK_SHIFT_MASK },
{ "Shift-Command-Option", GDK_MOD5_MASK|GDK_SHIFT_MASK|GDK_META_MASK },
#else
{ "Key|Shift", GDK_SHIFT_MASK },
{ "Control", GDK_CONTROL_MASK },
- { "Alt (Mod1)", GDK_MOD1_MASK },
+ { "Alt", GDK_MOD1_MASK },
{ "Control-Shift", GDK_CONTROL_MASK|GDK_SHIFT_MASK },
{ "Control-Alt", GDK_CONTROL_MASK|GDK_MOD1_MASK },
{ "Shift-Alt", GDK_SHIFT_MASK|GDK_MOD1_MASK },
{ "Control-Shift-Alt", GDK_CONTROL_MASK|GDK_SHIFT_MASK|GDK_MOD1_MASK },
+ { "Alt-Windows", GDK_MOD1_MASK|GDK_MOD4_MASK },
{ "Mod2", GDK_MOD2_MASK },
{ "Mod3", GDK_MOD3_MASK },
- { "Mod4", GDK_MOD4_MASK },
+ { "Windows", GDK_MOD4_MASK },
{ "Mod5", GDK_MOD5_MASK },
#endif
{ 0, 0 }
@@ -319,25 +321,57 @@ public:
}
}
- Table* t = manage (new Table (4, 4));
+ Table* t = manage (new Table (5, 11));
t->set_spacings (4);
- Label* l = manage (left_aligned_label (_("Edit using:")));
+ int row = 0;
+ int col = 0;
+
+ Label* l = manage (left_aligned_label (_("Select Keyboard layout:")));
l->set_name ("OptionsLabel");
- t->attach (*l, 0, 1, 0, 1, FILL | EXPAND, FILL);
- t->attach (_edit_modifier_combo, 1, 2, 0, 1, FILL | EXPAND, FILL);
+ vector<string> strs;
+
+ for (map<string,string>::iterator bf = Keyboard::binding_files.begin(); bf != Keyboard::binding_files.end(); ++bf) {
+ strs.push_back (bf->first);
+ }
+
+ set_popdown_strings (_keyboard_layout_selector, strs);
+ _keyboard_layout_selector.set_active_text (Keyboard::current_binding_name());
+ _keyboard_layout_selector.signal_changed().connect (sigc::mem_fun (*this, &KeyboardOptions::bindings_changed));
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_keyboard_layout_selector, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 0;
+
+ l = manage (left_aligned_label (_("When Clicking:")));
+ l->set_name ("OptionEditorHeading");
+ t->attach (*l, col, col + 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ l = manage (left_aligned_label (_("Edit using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_edit_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
l = manage (new Label (_("+ button")));
l->set_name ("OptionsLabel");
- t->attach (*l, 3, 4, 0, 1, FILL | EXPAND, FILL);
- t->attach (_edit_button_spin, 4, 5, 0, 1, FILL | EXPAND, FILL);
+ t->attach (*l, col + 3, col + 4, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_edit_button_spin, col + 4, col + 5, row, row + 1, FILL | EXPAND, FILL);
_edit_button_spin.set_name ("OptionsEntry");
_edit_button_adjustment.set_value (Keyboard::edit_button());
_edit_button_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::edit_button_changed));
+ ++row;
+ col = 1;
+
set_popdown_strings (_delete_modifier_combo, dumb);
_delete_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::delete_modifier_chosen));
@@ -351,19 +385,21 @@ public:
l = manage (left_aligned_label (_("Delete using:")));
l->set_name ("OptionsLabel");
- t->attach (*l, 0, 1, 1, 2, FILL | EXPAND, FILL);
- t->attach (_delete_modifier_combo, 1, 2, 1, 2, FILL | EXPAND, FILL);
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_delete_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
l = manage (new Label (_("+ button")));
l->set_name ("OptionsLabel");
- t->attach (*l, 3, 4, 1, 2, FILL | EXPAND, FILL);
- t->attach (_delete_button_spin, 4, 5, 1, 2, FILL | EXPAND, FILL);
+ t->attach (*l, col + 3, col + 4, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_delete_button_spin, col + 4, col + 5, row, row + 1, FILL | EXPAND, FILL);
_delete_button_spin.set_name ("OptionsEntry");
_delete_button_adjustment.set_value (Keyboard::delete_button());
_delete_button_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::delete_button_changed));
+ ++row;
+ col = 1;
set_popdown_strings (_insert_note_modifier_combo, dumb);
_insert_note_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::insert_note_modifier_chosen));
@@ -378,20 +414,165 @@ public:
l = manage (left_aligned_label (_("Insert note using:")));
l->set_name ("OptionsLabel");
- t->attach (*l, 0, 1, 2, 3, FILL | EXPAND, FILL);
- t->attach (_insert_note_modifier_combo, 1, 2, 2, 3, FILL | EXPAND, FILL);
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_insert_note_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
l = manage (new Label (_("+ button")));
l->set_name ("OptionsLabel");
- t->attach (*l, 3, 4, 2, 3, FILL | EXPAND, FILL);
- t->attach (_insert_note_button_spin, 4, 5, 2, 3, FILL | EXPAND, FILL);
+ t->attach (*l, col + 3, col + 4, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_insert_note_button_spin, col + 4, col + 5, row, row + 1, FILL | EXPAND, FILL);
_insert_note_button_spin.set_name ("OptionsEntry");
_insert_note_button_adjustment.set_value (Keyboard::insert_note_button());
_insert_note_button_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::insert_note_button_changed));
+ ++row;
+
+ l = manage (left_aligned_label (_("When Beginning a Drag:")));
+ l->set_name ("OptionEditorHeading");
+ t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ /* copy modifier */
+ set_popdown_strings (_copy_modifier_combo, dumb);
+ _copy_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::copy_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) Keyboard::CopyModifier) {
+ _copy_modifier_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
+
+ l = manage (left_aligned_label (_("Copy items using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_copy_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ /* constraint modifier */
+ set_popdown_strings (_constraint_modifier_combo, dumb);
+ _constraint_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::constraint_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) ArdourKeyboard::constraint_modifier ()) {
+ _constraint_modifier_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
+
+ l = manage (left_aligned_label (_("Constrain drag using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_constraint_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+
+ l = manage (left_aligned_label (_("When Beginning a Trim:")));
+ l->set_name ("OptionEditorHeading");
+ t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ /* trim_contents */
+ set_popdown_strings (_trim_contents_combo, dumb);
+ _trim_contents_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::trim_contents_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) ArdourKeyboard::trim_contents_modifier ()) {
+ _trim_contents_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
+
+ l = manage (left_aligned_label (_("Trim contents using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_trim_contents_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+ /* anchored trim */
+ set_popdown_strings (_trim_anchored_combo, dumb);
+ _trim_anchored_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::trim_anchored_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) ArdourKeyboard::trim_anchored_modifier ()) {
+ _trim_anchored_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
+
+ l = manage (left_aligned_label (_("Anchored trim using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ ++col;
+ t->attach (_trim_anchored_combo, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ /* jump trim disabled for now
+ set_popdown_strings (_trim_jump_combo, dumb);
+ _trim_jump_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::trim_jump_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) Keyboard::trim_jump_modifier ()) {
+ _trim_jump_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
+
+ l = manage (left_aligned_label (_("Jump after trim using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ ++col;
+ t->attach (_trim_jump_combo, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+ */
+
+ /* note resize relative */
+ set_popdown_strings (_note_size_relative_combo, dumb);
+ _note_size_relative_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::note_size_relative_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) ArdourKeyboard::note_size_relative_modifier ()) {
+ _note_size_relative_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
+
+ l = manage (left_aligned_label (_("Resize notes relatively using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ ++col;
+ t->attach (_note_size_relative_combo, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+
+ l = manage (left_aligned_label (_("While Dragging:")));
+ l->set_name ("OptionEditorHeading");
+ t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ /* ignore snap */
set_popdown_strings (_snap_modifier_combo, dumb);
_snap_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::snap_modifier_chosen));
@@ -405,24 +586,100 @@ public:
l = manage (left_aligned_label (_("Ignore snap using:")));
l->set_name ("OptionsLabel");
- t->attach (*l, 0, 1, 3, 4, FILL | EXPAND, FILL);
- t->attach (_snap_modifier_combo, 1, 2, 3, 4, FILL | EXPAND, FILL);
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_snap_modifier_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
- vector<string> strs;
+ ++row;
+ col = 1;
- for (map<string,string>::iterator bf = Keyboard::binding_files.begin(); bf != Keyboard::binding_files.end(); ++bf) {
- strs.push_back (bf->first);
+ /* snap delta */
+ set_popdown_strings (_snap_delta_combo, dumb);
+ _snap_delta_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::snap_delta_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) Keyboard::snap_delta_modifier ()) {
+ _snap_delta_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
}
- set_popdown_strings (_keyboard_layout_selector, strs);
- _keyboard_layout_selector.set_active_text (Keyboard::current_binding_name());
- _keyboard_layout_selector.signal_changed().connect (sigc::mem_fun (*this, &KeyboardOptions::bindings_changed));
+ l = manage (left_aligned_label (_("Snap to absolute using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_snap_delta_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+
+ l = manage (left_aligned_label (_("While Trimming:")));
+ l->set_name ("OptionEditorHeading");
+ t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ /* trim_overlap */
+ set_popdown_strings (_trim_overlap_combo, dumb);
+ _trim_overlap_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::trim_overlap_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) ArdourKeyboard::trim_overlap_modifier ()) {
+ _trim_overlap_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
+
+ l = manage (left_aligned_label (_("Resize overlaped regions using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_trim_overlap_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+
+ l = manage (left_aligned_label (_("While Dragging Control Points:")));
+ l->set_name ("OptionEditorHeading");
+ t->attach (*l, 0, 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ /* fine adjust */
+ set_popdown_strings (_fine_adjust_combo, dumb);
+ _fine_adjust_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::fine_adjust_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) ArdourKeyboard::fine_adjust_modifier ()) {
+ _fine_adjust_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
- l = manage (left_aligned_label (_("Keyboard layout:")));
+ l = manage (left_aligned_label (_("Fine adjust using:")));
l->set_name ("OptionsLabel");
- t->attach (*l, 0, 1, 4, 5, FILL | EXPAND, FILL);
- t->attach (_keyboard_layout_selector, 1, 2, 4, 5, FILL | EXPAND, FILL);
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_fine_adjust_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
+
+ ++row;
+ col = 1;
+
+ /* push points */
+ set_popdown_strings (_push_points_combo, dumb);
+ _push_points_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::push_points_modifier_chosen));
+
+ for (int x = 0; modifiers[x].name; ++x) {
+ if (modifiers[x].modifier == (guint) ArdourKeyboard::push_points_modifier ()) {
+ _push_points_combo.set_active_text (S_(modifiers[x].name));
+ break;
+ }
+ }
+
+ l = manage (left_aligned_label (_("Push points using:")));
+ l->set_name ("OptionsLabel");
+
+ t->attach (*l, col, col + 1, row, row + 1, FILL | EXPAND, FILL);
+ t->attach (_push_points_combo, col + 1, col + 2, row, row + 1, FILL | EXPAND, FILL);
_box->pack_start (*t, false, false);
}
@@ -478,6 +735,18 @@ private:
}
}
+ void copy_modifier_chosen ()
+ {
+ string const txt = _copy_modifier_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ Keyboard::set_copy_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
void insert_note_modifier_chosen ()
{
string const txt = _insert_note_modifier_combo.get_active_text();
@@ -502,6 +771,102 @@ private:
}
}
+ void snap_delta_modifier_chosen ()
+ {
+ string const txt = _snap_delta_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ Keyboard::set_snap_delta_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
+ void constraint_modifier_chosen ()
+ {
+ string const txt = _constraint_modifier_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ ArdourKeyboard::set_constraint_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
+ void trim_contents_modifier_chosen ()
+ {
+ string const txt = _trim_contents_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ ArdourKeyboard::set_trim_contents_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
+ void trim_overlap_modifier_chosen ()
+ {
+ string const txt = _trim_overlap_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ ArdourKeyboard::set_trim_overlap_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
+ void trim_anchored_modifier_chosen ()
+ {
+ string const txt = _trim_anchored_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ ArdourKeyboard::set_trim_anchored_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
+ void fine_adjust_modifier_chosen ()
+ {
+ string const txt = _fine_adjust_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ ArdourKeyboard::set_fine_adjust_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
+ void push_points_modifier_chosen ()
+ {
+ string const txt = _push_points_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ ArdourKeyboard::set_push_points_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
+ void note_size_relative_modifier_chosen ()
+ {
+ string const txt = _note_size_relative_combo.get_active_text();
+
+ for (int i = 0; modifiers[i].name; ++i) {
+ if (txt == _(modifiers[i].name)) {
+ ArdourKeyboard::set_note_size_relative_modifier (modifiers[i].modifier);
+ break;
+ }
+ }
+ }
+
void delete_button_changed ()
{
Keyboard::set_delete_button (_delete_button_spin.get_value_as_int());
@@ -520,8 +885,18 @@ private:
ComboBoxText _keyboard_layout_selector;
ComboBoxText _edit_modifier_combo;
ComboBoxText _delete_modifier_combo;
+ ComboBoxText _copy_modifier_combo;
ComboBoxText _insert_note_modifier_combo;
ComboBoxText _snap_modifier_combo;
+ ComboBoxText _snap_delta_combo;
+ ComboBoxText _constraint_modifier_combo;
+ ComboBoxText _trim_contents_combo;
+ ComboBoxText _trim_overlap_combo;
+ ComboBoxText _trim_anchored_combo;
+ ComboBoxText _trim_jump_combo;
+ ComboBoxText _fine_adjust_combo;
+ ComboBoxText _push_points_combo;
+ ComboBoxText _note_size_relative_combo;
Adjustment _delete_button_adjustment;
SpinButton _delete_button_spin;
Adjustment _edit_button_adjustment;
diff --git a/gtk2_ardour/region_view.cc b/gtk2_ardour/region_view.cc
index be96cd4058..75e091f2d0 100644
--- a/gtk2_ardour/region_view.cc
+++ b/gtk2_ardour/region_view.cc
@@ -939,10 +939,12 @@ RegionView::move_contents (frameoffset_t distance)
/** Snap a frame offset within our region using the current snap settings.
* @param x Frame offset from this region's position.
+ * @param ensure_snap whether to ignore snap_mode (in the case of SnapOff) and magnetic snap.
+ * Used when inverting snap mode logic with key modifiers, or snap distance calculation.
* @return Snapped frame offset from this region's position.
*/
frameoffset_t
-RegionView::snap_frame_to_frame (frameoffset_t x) const
+RegionView::snap_frame_to_frame (frameoffset_t x, bool ensure_snap) const
{
PublicEditor& editor = trackview.editor();
@@ -951,12 +953,12 @@ RegionView::snap_frame_to_frame (frameoffset_t x) const
/* try a snap in either direction */
framepos_t frame = session_frame;
- editor.snap_to (frame, RoundNearest);
+ editor.snap_to (frame, RoundNearest, false, ensure_snap);
/* if we went off the beginning of the region, snap forwards */
if (frame < _region->position ()) {
frame = session_frame;
- editor.snap_to (frame, RoundUpAlways);
+ editor.snap_to (frame, RoundUpAlways, false, ensure_snap);
}
/* back to region relative */
diff --git a/gtk2_ardour/region_view.h b/gtk2_ardour/region_view.h
index 92006c556c..f17e37a72d 100644
--- a/gtk2_ardour/region_view.h
+++ b/gtk2_ardour/region_view.h
@@ -121,8 +121,8 @@ class RegionView : public TimeAxisViewItem
}
};
- ARDOUR::frameoffset_t snap_frame_to_frame (ARDOUR::frameoffset_t) const;
-
+ ARDOUR::frameoffset_t snap_frame_to_frame (ARDOUR::frameoffset_t, bool ensure_snap = false) const;
+
protected:
/** Allows derived types to specify their visibility requirements