#include "ardour/profile.h"
#include "ardour/region_factory.h"
#include "ardour/session.h"
+#include "ardour/session_playlists.h"
#include "canvas/canvas.h"
#include "canvas/scroll_group.h"
, _grab_frame (0)
, _last_pointer_frame (0)
, _snap_delta (0)
+ , _snap_delta_music (0.0)
, _constraint_pressed (false)
{
return 0;
}
+double
+Drag::snap_delta_music (guint state) const
+{
+ if (ArdourKeyboard::indicates_snap_delta (state)) {
+ return _snap_delta_music;
+ }
+
+ return 0.0;
+}
double
Drag::current_pointer_x() const
}
void
-Drag::setup_snap_delta (framepos_t pos)
+Drag::setup_snap_delta (MusicFrame pos)
{
- MusicFrame snap (pos, 0);
+ TempoMap& map (_editor->session()->tempo_map());
+ MusicFrame snap (pos);
_editor->snap_to (snap, ARDOUR::RoundNearest, false, true);
- _snap_delta = snap.frame - pos;
+ _snap_delta = snap.frame - pos.frame;
+
+ _snap_delta_music = 0.0;
+
+ if (_snap_delta != 0) {
+ _snap_delta_music = map.exact_qn_at_frame (snap.frame, snap.division) - map.exact_qn_at_frame (pos.frame, pos.division);
+ }
}
bool
RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
Drag::start_grab (event, cursor);
- setup_snap_delta (_last_position.frame);
+ setup_snap_delta (_last_position);
show_verbose_cursor_time (_last_position.frame);
/* compute the amount of pointer motion in frames, and where
the region would be if we moved it by that much.
*/
+ if (_x_constrained) {
+ *pending_region_position = _last_position;
+ return 0.0;
+ }
+
*pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
framecnt_t sync_offset;
if ((pending_region_position->frame != _last_position.frame) && x_move_allowed) {
/* x movement since last time (in pixels) */
- dx = (static_cast<double> (pending_region_position->frame) - _last_position.frame) / _editor->samples_per_pixel;
+ dx = _editor->sample_to_pixel_unrounded (pending_region_position->frame - _last_position.frame);
/* total x movement */
- framecnt_t total_dx = pending_region_position->frame;
- if (regions_came_from_canvas()) {
- total_dx = total_dx - grab_frame ();
- }
+ framecnt_t total_dx = _editor->pixel_to_sample (_total_x_delta + dx);
- /* check that no regions have gone off the start of the session */
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
- if ((i->view->region()->position() + total_dx) < 0) {
- dx = 0;
- *pending_region_position = _last_position;
+ frameoffset_t const off = i->view->region()->position() + total_dx;
+ if (off < 0) {
+ dx = dx - _editor->sample_to_pixel_unrounded (off);
+ *pending_region_position = MusicFrame (pending_region_position->frame - off, 0);
break;
}
}
-
}
return dx;
}
/* Work out the change in x */
+ TempoMap& tmap = _editor->session()->tempo_map();
MusicFrame pending_region_position (0, 0);
double const x_delta = compute_x_delta (event, &pending_region_position);
+
+ double const last_pos_qn = tmap.exact_qn_at_frame (_last_position.frame, _last_position.division);
+ double const qn_delta = tmap.exact_qn_at_frame (pending_region_position.frame, pending_region_position.division) - last_pos_qn;
+
_last_position = pending_region_position;
/* calculate hidden tracks in current y-axis delta */
}
/* Now move the region view */
- rv->move (x_delta, y_delta);
+ if (rv->region()->position_lock_style() == MusicTime) {
+ double const last_qn = tmap.quarter_note_at_frame (rv->get_position());
+ framepos_t const x_pos_music = tmap.frame_at_quarter_note (last_qn + qn_delta);
+
+ rv->set_position (x_pos_music, 0);
+ rv->move (0, y_delta);
+ } else {
+ rv->move (x_delta, y_delta);
+ }
} /* foreach region */
PlaylistSet frozen_playlists;
set<RouteTimeAxisView*> views_to_update;
RouteTimeAxisView* new_time_axis_view = 0;
- framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
+ framecnt_t const drag_delta = _primary->region()->position() - last_position.frame;
typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
PlaylistMapping playlist_mapping;
/* insert into new playlist */
RegionView* new_view;
- if (rv == _primary) {
+ if (rv == _primary && !_x_constrained) {
new_view = insert_region_into_playlist (
RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position, last_pos_qn,
modified_playlists, true
void
RegionCreateDrag::motion (GdkEvent* event, bool first_move)
{
+
if (first_move) {
_editor->begin_reversible_command (_("create region"));
_region = add_midi_region (_view, false);
_view->playlist()->freeze ();
} else {
+
if (_region) {
framepos_t const f = adjusted_current_frame (event);
- if (f < grab_frame()) {
+ if (f <= grab_frame()) {
_region->set_initial_position (f);
}
a bit confusing as if a region starts 1 frame after a snap point, one cannot
place snapped notes at the start of the region.
*/
-
- framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
- _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
+ if (f != grab_frame()) {
+ framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
+ _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
+ }
}
}
}
framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
framepos_t const pf = adjusted_current_frame (event);
- setup_snap_delta (region_start);
+ setup_snap_delta (MusicFrame(region_start, 0));
if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
/* Move the contents of the region around without changing the region bounds */
if (insert_result.second) {
pl->freeze();
}
+
+ MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (rv);
+ /* a MRV start trim may change the source length. ensure we cover all playlists here */
+ if (mrv && _operation == StartTrim) {
+ vector<boost::shared_ptr<Playlist> > all_playlists;
+ _editor->session()->playlists->get (all_playlists);
+ for (vector<boost::shared_ptr<Playlist> >::iterator x = all_playlists.begin(); x != all_playlists.end(); ++x) {
+
+ if ((*x)->uses_source (rv->region()->source(0))) {
+ insert_result = _editor->motion_frozen_playlists.insert (*x);
+ if (insert_result.second) {
+ (*x)->clear_owned_changes ();
+ (*x)->freeze();
+ }
+
+ }
+ }
+ }
}
}
if (!_editor->selection->selected (_primary)) {
_primary->thaw_after_trim ();
} else {
-
- set<boost::shared_ptr<Playlist> > diffed_playlists;
-
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
i->view->thaw_after_trim ();
i->view->enable_display (true);
-
- /* Trimming one region may affect others on the playlist, so we need
- to get undo Commands from the whole playlist rather than just the
- region. Use diffed_playlists to make sure we don't diff a given
- playlist more than once.
- */
- boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
- if (diffed_playlists.find (p) == diffed_playlists.end()) {
- vector<Command*> cmds;
- p->rdiff (cmds);
- _editor->session()->add_commands (cmds);
- diffed_playlists.insert (p);
- }
}
}
for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
+ /* Trimming one region may affect others on the playlist, so we need
+ to get undo Commands from the whole playlist rather than just the
+ region. Use motion_frozen_playlists (a set) to make sure we don't
+ diff a given playlist more than once.
+ */
+
+ vector<Command*> cmds;
+ (*p)->rdiff (cmds);
+ _editor->session()->add_commands (cmds);
(*p)->thaw ();
}
TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
: Drag (e, i)
, _copy (c)
- , _grab_bpm (0.0)
- , before_state (0)
+ , _grab_bpm (120.0, 4.0)
+ , _grab_qn (0.0)
+ , _before_state (0)
{
DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
_marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
_real_section = &_marker->tempo();
_movable = !_real_section->initial();
- _grab_bpm = _real_section->note_types_per_minute();
+ _grab_bpm = Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), _real_section->end_note_types_per_minute());
+ _grab_qn = _real_section->pulse() * 4.0;
assert (_marker);
}
if (!_real_section->active()) {
return;
}
+ TempoMap& map (_editor->session()->tempo_map());
if (first_move) {
swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
_marker->hide();
- TempoMap& map (_editor->session()->tempo_map());
/* get current state */
- before_state = &map.get_state();
+ _before_state = &map.get_state();
if (!_copy) {
_editor->begin_reversible_command (_("move tempo mark"));
} else {
const Tempo tempo (_marker->tempo());
const framepos_t frame = adjusted_current_frame (event) + 1;
- const TempoSection::Type type = _real_section->type();
_editor->begin_reversible_command (_("copy tempo mark"));
if (_real_section->position_lock_style() == MusicTime) {
const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
- _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
+ _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, MusicTime);
} else {
- _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
+ _real_section = map.add_tempo (tempo, 0.0, frame, AudioTime);
}
if (!_real_section) {
}
}
+ if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
+ double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
+ stringstream strs;
+ _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm));
+ strs << "end:" << fixed << setprecision(3) << new_bpm;
+ show_verbose_cursor_text (strs.str());
- if (ArdourKeyboard::indicates_constraint (event->button.state)) {
+ } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
/* use vertical movement to alter tempo .. should be log */
- double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
+ double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
stringstream strs;
- _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
- strs << new_bpm;
+ _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute()));
+ strs << "start:" << fixed << setprecision(3) << new_bpm;
show_verbose_cursor_text (strs.str());
} else if (_movable && !_real_section->locked_to_meter()) {
pf = adjusted_current_frame (event);
}
- TempoMap& map (_editor->session()->tempo_map());
-
/* snap to beat is 1, snap to bar is -1 (sorry) */
const int sub_num = _editor->get_grid_music_divisions (event->button.state);
TempoMap& map (_editor->session()->tempo_map());
XMLNode &after = map.get_state();
- _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &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.
_marker->set_position (_marker->tempo().frame());
if (moved) {
TempoMap& map (_editor->session()->tempo_map());
- map.set_state (*before_state, Stateful::current_state_version);
+ map.set_state (*_before_state, Stateful::current_state_version);
// delete the dummy (hidden) marker we used for events while moving.
delete _marker;
}
: Drag (e, i)
, _grab_qn (0.0)
, _tempo (0)
- , before_state (0)
+ , _before_state (0)
{
DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
Drag::start_grab (event, cursor);
TempoMap& map (_editor->session()->tempo_map());
_tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
+ _editor->tempo_curve_selected (_tempo, true);
+
ostringstream sstr;
+ if (_tempo->clamped()) {
+ TempoSection* prev = map.previous_tempo_section (_tempo);
+ if (prev) {
+ _editor->tempo_curve_selected (prev, true);
+ sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
+ }
+ }
- sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
- sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
show_verbose_cursor_text (sstr.str());
- finished (event, false);
}
void
BBTRulerDrag::setup_pointer_frame_offset ()
{
TempoMap& map (_editor->session()->tempo_map());
+ /* get current state */
+ _before_state = &map.get_state();
+
const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
const uint32_t divisions = _editor->get_grid_beat_divisions (0);
double beat = 0.0;
TempoMap& map (_editor->session()->tempo_map());
if (first_move) {
- /* get current state */
- before_state = &map.get_state();
_editor->begin_reversible_command (_("stretch tempo"));
}
/* adjust previous tempo to match pointer frame */
_editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
}
+
ostringstream sstr;
- sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
- sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
+ if (_tempo->clamped()) {
+ TempoSection* prev = map.previous_tempo_section (_tempo);
+ if (prev) {
+ _editor->tempo_curve_selected (prev, true);
+ sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
+ }
+ }
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
show_verbose_cursor_text (sstr.str());
}
TempoMap& map (_editor->session()->tempo_map());
XMLNode &after = map.get_state();
- _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
+ _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
_editor->commit_reversible_command ();
+ _editor->tempo_curve_selected (_tempo, false);
+
+ if (_tempo->clamped()) {
+ TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
+ if (prev_tempo) {
+ _editor->tempo_curve_selected (prev_tempo, false);
+ }
+ }
}
void
BBTRulerDrag::aborted (bool moved)
{
if (moved) {
- _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
+ _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
}
}
+TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
+ : Drag (e, i)
+ , _grab_qn (0.0)
+ , _grab_tempo (0.0)
+ , _tempo (0)
+ , _next_tempo (0)
+ , _drag_valid (true)
+ , _before_state (0)
+{
+ DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
+
+}
+
+void
+TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
+{
+ Drag::start_grab (event, cursor);
+ TempoMap& map (_editor->session()->tempo_map());
+ /* get current state */
+ _before_state = &map.get_state();
+ _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
+
+ _next_tempo = map.next_tempo_section (_tempo);
+ if (_next_tempo) {
+ if (!map.next_tempo_section (_next_tempo)) {
+ _drag_valid = false;
+ finished (event, false);
+
+ return;
+ }
+ _editor->tempo_curve_selected (_tempo, true);
+ _editor->tempo_curve_selected (_next_tempo, true);
+
+ ostringstream sstr;
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
+ sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
+ sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
+ show_verbose_cursor_text (sstr.str());
+ } else {
+ _drag_valid = false;
+ }
+
+ _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
+}
+
+void
+TempoTwistDrag::setup_pointer_frame_offset ()
+{
+ TempoMap& map (_editor->session()->tempo_map());
+ const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
+ const uint32_t divisions = _editor->get_grid_beat_divisions (0);
+ double beat = 0.0;
+
+ if (divisions > 0) {
+ beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
+ } else {
+ /* while it makes some sense for the user to determine the division to 'grab',
+ grabbing a bar often leads to confusing results wrt the actual tempo section being altered
+ and the result over steep tempo curves. Use sixteenths.
+ */
+ beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
+ }
+
+ _grab_qn = map.quarter_note_at_beat (beat);
+
+ _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
+
+}
+
+void
+TempoTwistDrag::motion (GdkEvent* event, bool first_move)
+{
+
+ if (!_next_tempo || !_drag_valid) {
+ return;
+ }
+
+ TempoMap& map (_editor->session()->tempo_map());
+
+ if (first_move) {
+ _editor->begin_reversible_command (_("twist tempo"));
+ }
+
+ framepos_t pf;
+
+ if (_editor->snap_musical()) {
+ pf = adjusted_current_frame (event, false);
+ } else {
+ pf = adjusted_current_frame (event);
+ }
+
+ /* adjust this and the next tempi to match pointer frame */
+ double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
+ _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
+
+ ostringstream sstr;
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
+ sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
+ sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
+ show_verbose_cursor_text (sstr.str());
+}
+
+void
+TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
+{
+ TempoMap& map (_editor->session()->tempo_map());
+
+ if (!movement_occurred || !_drag_valid) {
+ return;
+ }
+
+ _editor->tempo_curve_selected (_tempo, false);
+ _editor->tempo_curve_selected (_next_tempo, false);
+
+ XMLNode &after = map.get_state();
+ _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
+ _editor->commit_reversible_command ();
+}
+
+void
+TempoTwistDrag::aborted (bool moved)
+{
+ if (moved) {
+ _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
+ }
+}
+
+TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
+ : Drag (e, i)
+ , _grab_qn (0.0)
+ , _tempo (0)
+ , _before_state (0)
+{
+ DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
+ TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
+ _tempo = &marker->tempo();
+ _grab_qn = _tempo->pulse() * 4.0;
+}
+
+void
+TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
+{
+ Drag::start_grab (event, cursor);
+ TempoMap& tmap (_editor->session()->tempo_map());
+
+ /* get current state */
+ _before_state = &tmap.get_state();
+
+
+ ostringstream sstr;
+
+ TempoSection* prev = 0;
+ if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
+ _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
+ sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+ }
+
+ if (_tempo->clamped()) {
+ _editor->tempo_curve_selected (_tempo, true);
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
+ }
+
+ show_verbose_cursor_text (sstr.str());
+}
+
+void
+TempoEndDrag::setup_pointer_frame_offset ()
+{
+ TempoMap& map (_editor->session()->tempo_map());
+
+ _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
+
+}
+
+void
+TempoEndDrag::motion (GdkEvent* event, bool first_move)
+{
+ TempoMap& map (_editor->session()->tempo_map());
+
+ if (first_move) {
+ _editor->begin_reversible_command (_("stretch end tempo"));
+ }
+
+
+
+ framepos_t const pf = adjusted_current_frame (event, false);
+ map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
+
+ ostringstream sstr;
+ sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+
+ if (_tempo->clamped()) {
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
+ }
+
+ show_verbose_cursor_text (sstr.str());
+}
+
+void
+TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
+{
+ if (!movement_occurred) {
+ return;
+ }
+
+ TempoMap& tmap (_editor->session()->tempo_map());
+
+ XMLNode &after = tmap.get_state();
+ _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
+ _editor->commit_reversible_command ();
+
+ TempoSection* prev = 0;
+ if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
+ _editor->tempo_curve_selected (prev, false);
+ }
+
+ if (_tempo->clamped()) {
+ _editor->tempo_curve_selected (_tempo, false);
+
+ }
+}
+
+void
+TempoEndDrag::aborted (bool moved)
+{
+ if (moved) {
+ _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
+ }
+}
CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
: Drag (e, &c.track_canvas_item(), false)
CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
{
Drag::start_grab (event, c);
- setup_snap_delta (_editor->playhead_cursor->current_frame());
+ setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
_grab_zoom = _editor->samples_per_pixel;
_editor->snap_to_with_modifier (where, event);
_editor->_dragging_playhead = true;
+ _editor->_control_scroll_target = where.frame;
Session* s = _editor->session ();
}
- if (AudioEngine::instance()->connected()) {
+ if (AudioEngine::instance()->running()) {
/* do this only if we're the engine is connected
* because otherwise this request will never be
*/
s->request_suspend_timecode_transmission ();
- while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
+ while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
/* twiddle our thumbs */
}
}
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
- setup_snap_delta (r->position());
+ setup_snap_delta (MusicFrame (r->position(), 0));
show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
}
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
boost::shared_ptr<AudioRegion> r = arv->audio_region ();
- setup_snap_delta (r->last_frame());
+ setup_snap_delta (MusicFrame (r->last_frame(), 0));
show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
}
} else {
show_verbose_cursor_time (location->end());
}
- setup_snap_delta (is_start ? location->start() : location->end());
+ setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
_fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
_fixed_grab_y = _point->get_y();
- setup_snap_delta (_editor->pixel_to_sample (_fixed_grab_x));
+ setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
float const fraction = 1 - (_point->get_y() / _point->line().height());
show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
_editor->get_selection().add (_primary);
- framepos_t where = _primary->region()->position();
+ MusicFrame where (_primary->region()->position(), 0);
setup_snap_delta (where);
- show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
+ show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
}
void
//( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
TrackViewList tracks_to_add;
TrackViewList tracks_to_remove;
+ vector<RouteGroup*> selected_route_groups;
if (!first_move) {
for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
tracks_to_remove.push_back (*i);
+ } else {
+ RouteGroup* rg = (*i)->route_group();
+ if (rg && rg->is_active() && rg->is_select()) {
+ selected_route_groups.push_back (rg);
+ }
}
}
}
for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
if (!_editor->selection->tracks.contains (*i)) {
tracks_to_add.push_back (*i);
+ RouteGroup* rg = (*i)->route_group();
+
+ if (rg && rg->is_active() && rg->is_select()) {
+ selected_route_groups.push_back (rg);
+ }
}
}
_editor->selection->add (tracks_to_add);
if (!tracks_to_remove.empty()) {
+
+ /* check all these to-be-removed tracks against the
+ * possibility that they are selected by being
+ * in the same group as an approved track.
+ */
+
+ for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
+ RouteGroup* rg = (*i)->route_group();
+
+ if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
+ i = tracks_to_remove.erase (i);
+ } else {
+ ++i;
+ }
+ }
+
+ /* remove whatever is left */
+
_editor->selection->remove (tracks_to_remove);
}
}
: Drag (e, i)
, _cumulative_dx (0)
, _cumulative_dy (0)
+ , _earliest (0.0)
, _was_selected (false)
, _copy (false)
{
_note_height = _region->midi_stream_view()->note_height ();
}
+void
+NoteDrag::setup_pointer_frame_offset ()
+{
+ _pointer_frame_offset = raw_grab_frame()
+ - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
+}
+
void
NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
{
Drag::start_grab (event);
- if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
+ if (ArdourKeyboard::indicates_copy (event->button.state)) {
_copy = true;
} else {
_copy = false;
}
- setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
+ setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
if (!(_was_selected = _primary->selected())) {
}
}
-/** @return Current total drag x change in frames */
-frameoffset_t
-NoteDrag::total_dx (const guint state) const
+/** @return Current total drag x change in quarter notes */
+double
+NoteDrag::total_dx (GdkEvent * event) const
{
if (_x_constrained) {
return 0;
}
+
TempoMap& map (_editor->session()->tempo_map());
/* dx in frames */
frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
/* primary note time */
- double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
- frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
-
- /* new time of the primary note in session frames */
- frameoffset_t st = n + dx + snap_delta (state);
-
- framepos_t const rp = _region->region()->position ();
+ frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
- /* prevent the note being dragged earlier than the region's position */
- st = max (st, rp);
+ /* primary note time in quarter notes */
+ double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
- /* possibly snap and return corresponding delta */
+ /* new time of the primary note in session frames */
+ frameoffset_t st = n + dx + snap_delta (event->button.state);
- bool snap = true;
+ /* possibly snap and return corresponding delta in quarter notes */
+ MusicFrame snap (st, 0);
+ _editor->snap_to_with_modifier (snap, event);
+ double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
- 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;
- }
- }
+ /* prevent the earliest note being dragged earlier than the region's start position */
+ if (_earliest + ret < _region->midi_region()->start_beats()) {
+ ret -= (_earliest + ret) - _region->midi_region()->start_beats();
}
- frameoffset_t ret;
- if (snap) {
- bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
- ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
- } else {
- ret = st - n - snap_delta (state);
- }
return ret;
}
void
NoteDrag::motion (GdkEvent * event, bool first_move)
{
- if (_copy && first_move) {
- /* make copies of all the selected notes */
- _primary = _region->copy_selection ();
+ if (first_move) {
+ _earliest = _region->earliest_in_selection().to_double();
+ if (_copy) {
+ /* make copies of all the selected notes */
+ _primary = _region->copy_selection (_primary);
+ }
}
/* Total change in x and y since the start of the drag */
- frameoffset_t const dx = total_dx (event->button.state);
+ double const dx_qn = total_dx (event);
int8_t const dy = total_dy ();
/* Now work out what we have to do to the note canvas items to set this new drag delta */
- double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
+ double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
if (tdx || tdy) {
- _cumulative_dx += tdx;
+ _cumulative_dx = dx_qn;
_cumulative_dy += tdy;
int8_t note_delta = total_dy();
if (tdx || tdy) {
if (_copy) {
- _region->move_copies (tdx, tdy, note_delta);
+ _region->move_copies (dx_qn, tdy, note_delta);
} else {
- _region->move_selection (tdx, tdy, note_delta);
+ _region->move_selection (dx_qn, tdy, note_delta);
}
/* the new note value may be the same as the old one, but we
}
}
} else {
- _region->note_dropped (_primary, total_dx (ev->button.state), total_dy(), _copy);
+ _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
}
}
const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
+ _editor->begin_reversible_command (_("Create Note"));
+ _region_view->clear_editor_note_selection();
_region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
+ _editor->commit_reversible_command ();
}
double
: Drag (e, i)
, _region_view (rv)
, _last_pos (0)
- , _last_y (0.0)
+ , _y (0.0)
{
}
TempoMap& map (_editor->session()->tempo_map());
+ _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
+
const framepos_t pf = _drags->current_pointer_frame ();
const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
}
const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
- const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
-
Evoral::Beats length = _region_view->get_grid_beats (pf);
- _region_view->create_note_at (start, y, length, event->button.state, false);
+ _editor->begin_reversible_command (_("Create Hit"));
+ _region_view->clear_editor_note_selection();
+ _region_view->create_note_at (start, _y, length, event->button.state, false);
_last_pos = start;
- _last_y = y;
}
void
const double eqaf = map.exact_qn_at_frame (pf, divisions);
const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
- const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
- if (_last_pos == start && y == _last_y) {
+ if (_last_pos == start) {
return;
}
Evoral::Beats length = _region_view->get_grid_beats (pf);
boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
+
if (eqaf >= mr->quarter_note() + mr->length_beats()) {
return;
}
- _region_view->create_note_at (start, y, length, event->button.state, false);
+ _region_view->create_note_at (start, _y, length, event->button.state, false);
_last_pos = start;
- _last_y = y;
}
void
HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
{
+ _editor->commit_reversible_command ();
}