Add small GUI to select Mackie / BCF2000 emulation for the mackie surface.
[ardour2.git] / gtk2_ardour / editor_drag.cc
blobed821c8f151a491e6ba36294a3fe58e451d4c55a
1 /*
2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
24 #include <stdint.h>
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/session.h"
33 #include "ardour/dB.h"
34 #include "ardour/region_factory.h"
35 #include "ardour/operations.h"
37 #include "editor.h"
38 #include "i18n.h"
39 #include "keyboard.h"
40 #include "audio_region_view.h"
41 #include "midi_region_view.h"
42 #include "ardour_ui.h"
43 #include "gui_thread.h"
44 #include "control_point.h"
45 #include "utils.h"
46 #include "region_gain_line.h"
47 #include "editor_drag.h"
48 #include "audio_time_axis.h"
49 #include "midi_time_axis.h"
50 #include "canvas-note.h"
51 #include "selection.h"
52 #include "midi_selection.h"
53 #include "automation_time_axis.h"
54 #include "debug.h"
55 #include "editor_cursors.h"
56 #include "mouse_cursors.h"
57 #include "verbose_cursor.h"
59 using namespace std;
60 using namespace ARDOUR;
61 using namespace PBD;
62 using namespace Gtk;
63 using namespace Gtkmm2ext;
64 using namespace Editing;
65 using namespace ArdourCanvas;
67 using Gtkmm2ext::Keyboard;
69 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
71 DragManager::DragManager (Editor* e)
72 : _editor (e)
73 , _ending (false)
74 , _current_pointer_frame (0)
79 DragManager::~DragManager ()
81 abort ();
84 /** Call abort for each active drag */
85 void
86 DragManager::abort ()
88 _ending = true;
90 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
91 (*i)->abort ();
92 delete *i;
95 _drags.clear ();
97 _editor->set_follow_playhead (_old_follow_playhead, false);
99 _ending = false;
102 void
103 DragManager::add (Drag* d)
105 d->set_manager (this);
106 _drags.push_back (d);
109 void
110 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
112 d->set_manager (this);
113 _drags.push_back (d);
114 start_grab (e, c);
117 void
118 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
120 /* Prevent follow playhead during the drag to be nice to the user */
121 _old_follow_playhead = _editor->follow_playhead ();
122 _editor->set_follow_playhead (false);
124 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
126 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
127 (*i)->start_grab (e, c);
131 /** Call end_grab for each active drag.
132 * @return true if any drag reported movement having occurred.
134 bool
135 DragManager::end_grab (GdkEvent* e)
137 _ending = true;
139 bool r = false;
140 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
141 bool const t = (*i)->end_grab (e);
142 if (t) {
143 r = true;
145 delete *i;
148 _drags.clear ();
150 _ending = false;
152 _editor->set_follow_playhead (_old_follow_playhead, false);
154 return r;
157 bool
158 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
160 bool r = false;
162 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
164 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
165 bool const t = (*i)->motion_handler (e, from_autoscroll);
166 if (t) {
167 r = true;
172 return r;
175 bool
176 DragManager::have_item (ArdourCanvas::Item* i) const
178 list<Drag*>::const_iterator j = _drags.begin ();
179 while (j != _drags.end() && (*j)->item () != i) {
180 ++j;
183 return j != _drags.end ();
186 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
187 : _editor (e)
188 , _item (i)
189 , _pointer_frame_offset (0)
190 , _move_threshold_passed (false)
191 , _raw_grab_frame (0)
192 , _grab_frame (0)
193 , _last_pointer_frame (0)
198 void
199 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
201 _item->ungrab (0);
202 _item = new_item;
204 if (cursor == 0) {
205 cursor = _editor->which_grabber_cursor ();
208 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
211 void
212 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
214 if (cursor == 0) {
215 cursor = _editor->which_grabber_cursor ();
218 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
220 if (Keyboard::is_button2_event (&event->button)) {
221 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
222 _y_constrained = true;
223 _x_constrained = false;
224 } else {
225 _y_constrained = false;
226 _x_constrained = true;
228 } else {
229 _x_constrained = false;
230 _y_constrained = false;
233 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
234 setup_pointer_frame_offset ();
235 _grab_frame = adjusted_frame (_raw_grab_frame, event);
236 _last_pointer_frame = _grab_frame;
237 _last_pointer_x = _grab_x;
238 _last_pointer_y = _grab_y;
240 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
241 *cursor,
242 event->button.time);
244 if (_editor->session() && _editor->session()->transport_rolling()) {
245 _was_rolling = true;
246 } else {
247 _was_rolling = false;
250 switch (_editor->snap_type()) {
251 case SnapToRegionStart:
252 case SnapToRegionEnd:
253 case SnapToRegionSync:
254 case SnapToRegionBoundary:
255 _editor->build_region_boundary_cache ();
256 break;
257 default:
258 break;
262 /** Call to end a drag `successfully'. Ungrabs item and calls
263 * subclass' finished() method.
265 * @param event GDK event, or 0.
266 * @return true if some movement occurred, otherwise false.
268 bool
269 Drag::end_grab (GdkEvent* event)
271 _editor->stop_canvas_autoscroll ();
273 _item->ungrab (event ? event->button.time : 0);
275 finished (event, _move_threshold_passed);
277 _editor->verbose_cursor()->hide ();
279 return _move_threshold_passed;
282 framepos_t
283 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
285 framepos_t pos = 0;
287 if (f > _pointer_frame_offset) {
288 pos = f - _pointer_frame_offset;
291 if (snap) {
292 _editor->snap_to_with_modifier (pos, event);
295 return pos;
298 framepos_t
299 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
301 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
304 bool
305 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
307 /* check to see if we have moved in any way that matters since the last motion event */
308 if (_move_threshold_passed &&
309 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
310 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
311 return false;
314 pair<framecnt_t, int> const threshold = move_threshold ();
316 bool const old_move_threshold_passed = _move_threshold_passed;
318 if (!from_autoscroll && !_move_threshold_passed) {
320 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
321 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
323 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
326 if (active (_editor->mouse_mode) && _move_threshold_passed) {
328 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
329 if (!from_autoscroll) {
330 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
333 motion (event, _move_threshold_passed != old_move_threshold_passed);
335 _last_pointer_x = _drags->current_pointer_x ();
336 _last_pointer_y = _drags->current_pointer_y ();
337 _last_pointer_frame = adjusted_current_frame (event);
339 return true;
342 return false;
345 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
346 void
347 Drag::abort ()
349 if (_item) {
350 _item->ungrab (0);
353 aborted (_move_threshold_passed);
355 _editor->stop_canvas_autoscroll ();
356 _editor->verbose_cursor()->hide ();
359 void
360 Drag::show_verbose_cursor_time (framepos_t frame)
362 _editor->verbose_cursor()->set_time (
363 frame,
364 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
365 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
368 _editor->verbose_cursor()->show ();
371 void
372 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end)
374 _editor->verbose_cursor()->set_duration (
375 start, end,
376 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
377 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
380 _editor->verbose_cursor()->show ();
383 void
384 Drag::show_verbose_cursor_text (string const & text)
386 _editor->verbose_cursor()->set (
387 text,
388 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
389 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
392 _editor->verbose_cursor()->show ();
396 struct EditorOrderTimeAxisViewSorter {
397 bool operator() (TimeAxisView* a, TimeAxisView* b) {
398 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
399 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
400 assert (ra && rb);
401 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
405 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
406 : Drag (e, i),
407 _primary (p)
409 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
411 /* Make a list of non-hidden tracks to refer to during the drag */
413 TrackViewList track_views = _editor->track_views;
414 track_views.sort (EditorOrderTimeAxisViewSorter ());
416 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
417 if (!(*i)->hidden()) {
419 _time_axis_views.push_back (*i);
421 TimeAxisView::Children children_list = (*i)->get_child_list ();
422 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
423 _time_axis_views.push_back (j->get());
428 /* the list of views can be empty at this point if this is a region list-insert drag
431 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
432 _views.push_back (DraggingView (*i, this));
435 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
438 void
439 RegionDrag::region_going_away (RegionView* v)
441 list<DraggingView>::iterator i = _views.begin ();
442 while (i != _views.end() && i->view != v) {
443 ++i;
446 if (i != _views.end()) {
447 _views.erase (i);
451 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
453 RegionDrag::find_time_axis_view (TimeAxisView* t) const
455 int i = 0;
456 int const N = _time_axis_views.size ();
457 while (i < N && _time_axis_views[i] != t) {
458 ++i;
461 if (i == N) {
462 return -1;
465 return i;
468 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
469 : RegionDrag (e, i, p, v),
470 _brushing (b),
471 _total_x_delta (0)
477 void
478 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
480 Drag::start_grab (event, cursor);
482 show_verbose_cursor_time (_last_frame_position);
484 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
485 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
486 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
489 double
490 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
492 /* compute the amount of pointer motion in frames, and where
493 the region would be if we moved it by that much.
495 *pending_region_position = adjusted_current_frame (event);
497 framepos_t sync_frame;
498 framecnt_t sync_offset;
499 int32_t sync_dir;
501 sync_offset = _primary->region()->sync_offset (sync_dir);
503 /* we don't handle a sync point that lies before zero.
505 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
507 sync_frame = *pending_region_position + (sync_dir*sync_offset);
509 _editor->snap_to_with_modifier (sync_frame, event);
511 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
513 } else {
514 *pending_region_position = _last_frame_position;
517 if (*pending_region_position > max_framepos - _primary->region()->length()) {
518 *pending_region_position = _last_frame_position;
521 double dx = 0;
523 /* in locked edit mode, reverse the usual meaning of _x_constrained */
524 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
526 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
528 /* x movement since last time (in pixels) */
529 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
531 /* total x movement */
532 framecnt_t total_dx = *pending_region_position;
533 if (regions_came_from_canvas()) {
534 total_dx = total_dx - grab_frame ();
537 /* check that no regions have gone off the start of the session */
538 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
539 if ((i->view->region()->position() + total_dx) < 0) {
540 dx = 0;
541 *pending_region_position = _last_frame_position;
542 break;
546 _last_frame_position = *pending_region_position;
549 return dx;
552 bool
553 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
555 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
556 int const n = i->time_axis_view + delta_track;
557 if (n < 0 || n >= int (_time_axis_views.size())) {
558 /* off the top or bottom track */
559 return false;
562 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
563 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
564 /* not a track, or the wrong type */
565 return false;
568 int const l = i->layer + delta_layer;
569 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
570 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
571 If it has, the layers will be munged later anyway, so it's ok.
573 return false;
577 /* all regions being dragged are ok with this change */
578 return true;
581 void
582 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
584 assert (!_views.empty ());
586 /* Find the TimeAxisView that the pointer is now over */
587 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
589 /* Bail early if we're not over a track */
590 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
591 if (!rtv || !rtv->is_track()) {
592 _editor->verbose_cursor()->hide ();
593 return;
596 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
598 /* Here's the current pointer position in terms of time axis view and layer */
599 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
600 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
602 /* Work out the change in x */
603 framepos_t pending_region_position;
604 double const x_delta = compute_x_delta (event, &pending_region_position);
606 /* Work out the change in y */
607 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
608 int delta_layer = current_pointer_layer - _last_pointer_layer;
610 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
611 /* this y movement is not allowed, so do no y movement this time */
612 delta_time_axis_view = 0;
613 delta_layer = 0;
616 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
617 /* haven't reached next snap point, and we're not switching
618 trackviews nor layers. nothing to do.
620 return;
623 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
625 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
627 RegionView* rv = i->view;
629 if (rv->region()->locked()) {
630 continue;
633 if (first_move) {
635 /* here we are calculating the y distance from the
636 top of the first track view to the top of the region
637 area of the track view that we're working on */
639 /* this x value is just a dummy value so that we have something
640 to pass to i2w () */
642 double ix1 = 0;
644 /* distance from the top of this track view to the region area
645 of our track view is always 1 */
647 double iy1 = 1;
649 /* convert to world coordinates, ie distance from the top of
650 the ruler section */
652 rv->get_canvas_frame()->i2w (ix1, iy1);
654 /* compensate for the ruler section and the vertical scrollbar position */
655 iy1 += _editor->get_trackview_group_vertical_offset ();
657 // hide any dependent views
659 rv->get_time_axis_view().hide_dependent_views (*rv);
662 reparent to a non scrolling group so that we can keep the
663 region selection above all time axis views.
664 reparenting means we have to move the rv as the two
665 parent groups have different coordinates.
668 rv->get_canvas_group()->property_y() = iy1 - 1;
669 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
671 rv->fake_set_opaque (true);
674 /* Work out the change in y position of this region view */
676 double y_delta = 0;
678 /* If we have moved tracks, we'll fudge the layer delta so that the
679 region gets moved back onto layer 0 on its new track; this avoids
680 confusion when dragging regions from non-zero layers onto different
681 tracks.
683 int this_delta_layer = delta_layer;
684 if (delta_time_axis_view != 0) {
685 this_delta_layer = - i->layer;
688 /* Move this region to layer 0 on its old track */
689 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
690 if (lv->layer_display() == Stacked) {
691 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
694 /* Now move it to its right layer on the current track */
695 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
696 if (cv->layer_display() == Stacked) {
697 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
700 /* Move tracks */
701 if (delta_time_axis_view > 0) {
702 for (int j = 0; j < delta_time_axis_view; ++j) {
703 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
705 } else {
706 /* start by subtracting the height of the track above where we are now */
707 for (int j = 1; j <= -delta_time_axis_view; ++j) {
708 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
712 /* Set height */
713 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
715 /* Update the DraggingView */
716 i->time_axis_view += delta_time_axis_view;
717 i->layer += this_delta_layer;
719 if (_brushing) {
720 _editor->mouse_brush_insert_region (rv, pending_region_position);
721 } else {
722 rv->move (x_delta, y_delta);
725 } /* foreach region */
727 _total_x_delta += x_delta;
729 if (first_move) {
730 _editor->cursor_group->raise_to_top();
733 if (x_delta != 0 && !_brushing) {
734 show_verbose_cursor_time (_last_frame_position);
737 _last_pointer_time_axis_view += delta_time_axis_view;
738 _last_pointer_layer += delta_layer;
741 void
742 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
744 if (_copy && first_move) {
746 /* duplicate the regionview(s) and region(s) */
748 list<DraggingView> new_regionviews;
750 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
752 RegionView* rv = i->view;
753 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
754 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
756 const boost::shared_ptr<const Region> original = rv->region();
757 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
758 region_copy->set_position (original->position(), this);
760 RegionView* nrv;
761 if (arv) {
762 boost::shared_ptr<AudioRegion> audioregion_copy
763 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
765 nrv = new AudioRegionView (*arv, audioregion_copy);
766 } else if (mrv) {
767 boost::shared_ptr<MidiRegion> midiregion_copy
768 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
769 nrv = new MidiRegionView (*mrv, midiregion_copy);
770 } else {
771 continue;
774 nrv->get_canvas_group()->show ();
775 new_regionviews.push_back (DraggingView (nrv, this));
777 /* swap _primary to the copy */
779 if (rv == _primary) {
780 _primary = nrv;
783 /* ..and deselect the one we copied */
785 rv->set_selected (false);
788 if (!new_regionviews.empty()) {
790 /* reflect the fact that we are dragging the copies */
792 _views = new_regionviews;
794 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
797 sync the canvas to what we think is its current state
798 without it, the canvas seems to
799 "forget" to update properly after the upcoming reparent()
800 ..only if the mouse is in rapid motion at the time of the grab.
801 something to do with regionview creation taking so long?
803 _editor->update_canvas_now();
807 RegionMotionDrag::motion (event, first_move);
810 void
811 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
813 if (!movement_occurred) {
814 /* just a click */
815 return;
818 /* reverse this here so that we have the correct logic to finalize
819 the drag.
822 if (Config->get_edit_mode() == Lock) {
823 _x_constrained = !_x_constrained;
826 assert (!_views.empty ());
828 bool const changed_position = (_last_frame_position != _primary->region()->position());
829 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
830 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
832 _editor->update_canvas_now ();
834 if (_copy) {
836 finished_copy (
837 changed_position,
838 changed_tracks,
839 drag_delta
842 } else {
844 finished_no_copy (
845 changed_position,
846 changed_tracks,
847 drag_delta
853 void
854 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
856 RegionSelection new_views;
857 PlaylistSet modified_playlists;
858 list<RegionView*> views_to_delete;
860 if (_brushing) {
861 /* all changes were made during motion event handlers */
863 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
864 delete i->view;
867 _editor->commit_reversible_command ();
868 return;
871 if (_x_constrained) {
872 _editor->begin_reversible_command (_("fixed time region copy"));
873 } else {
874 _editor->begin_reversible_command (_("region copy"));
877 /* insert the regions into their new playlists */
878 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
880 if (i->view->region()->locked()) {
881 continue;
884 framepos_t where;
886 if (changed_position && !_x_constrained) {
887 where = i->view->region()->position() - drag_delta;
888 } else {
889 where = i->view->region()->position();
892 RegionView* new_view = insert_region_into_playlist (
893 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
896 if (new_view == 0) {
897 continue;
900 new_views.push_back (new_view);
902 /* we don't need the copied RegionView any more */
903 views_to_delete.push_back (i->view);
906 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
907 because when views are deleted they are automagically removed from _views, which messes
908 up the iteration.
910 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
911 delete *i;
914 /* If we've created new regions either by copying or moving
915 to a new track, we want to replace the old selection with the new ones
918 if (new_views.size() > 0) {
919 _editor->selection->set (new_views);
922 /* write commands for the accumulated diffs for all our modified playlists */
923 add_stateful_diff_commands_for_playlists (modified_playlists);
925 _editor->commit_reversible_command ();
928 void
929 RegionMoveDrag::finished_no_copy (
930 bool const changed_position,
931 bool const changed_tracks,
932 framecnt_t const drag_delta
935 RegionSelection new_views;
936 PlaylistSet modified_playlists;
937 PlaylistSet frozen_playlists;
939 if (_brushing) {
940 /* all changes were made during motion event handlers */
941 _editor->commit_reversible_command ();
942 return;
945 if (_x_constrained) {
946 _editor->begin_reversible_command (_("fixed time region drag"));
947 } else {
948 _editor->begin_reversible_command (Operations::region_drag);
951 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
953 RegionView* rv = i->view;
955 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
956 layer_t const dest_layer = i->layer;
958 if (rv->region()->locked()) {
959 ++i;
960 continue;
963 framepos_t where;
965 if (changed_position && !_x_constrained) {
966 where = rv->region()->position() - drag_delta;
967 } else {
968 where = rv->region()->position();
971 if (changed_tracks) {
973 /* insert into new playlist */
975 RegionView* new_view = insert_region_into_playlist (
976 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
979 if (new_view == 0) {
980 ++i;
981 continue;
984 new_views.push_back (new_view);
986 /* remove from old playlist */
988 /* the region that used to be in the old playlist is not
989 moved to the new one - we use a copy of it. as a result,
990 any existing editor for the region should no longer be
991 visible.
993 rv->hide_region_editor();
994 rv->fake_set_opaque (false);
996 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
998 } else {
1000 rv->region()->clear_changes ();
1003 motion on the same track. plonk the previously reparented region
1004 back to its original canvas group (its streamview).
1005 No need to do anything for copies as they are fake regions which will be deleted.
1008 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1009 rv->get_canvas_group()->property_y() = i->initial_y;
1010 rv->get_time_axis_view().reveal_dependent_views (*rv);
1012 /* just change the model */
1014 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1016 if (dest_rtv->view()->layer_display() == Stacked) {
1017 rv->region()->set_layer (dest_layer);
1018 rv->region()->set_pending_explicit_relayer (true);
1021 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1023 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1025 if (r.second) {
1026 playlist->freeze ();
1029 /* this movement may result in a crossfade being modified, so we need to get undo
1030 data from the playlist as well as the region.
1033 r = modified_playlists.insert (playlist);
1034 if (r.second) {
1035 playlist->clear_changes ();
1038 rv->region()->set_position (where, (void*) this);
1040 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1043 if (changed_tracks) {
1045 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1046 was selected in all of them, then removing it from a playlist will have removed all
1047 trace of it from _views (i.e. there were N regions selected, we removed 1,
1048 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1049 corresponding regionview, and _views is now empty).
1051 This could have invalidated any and all iterators into _views.
1053 The heuristic we use here is: if the region selection is empty, break out of the loop
1054 here. if the region selection is not empty, then restart the loop because we know that
1055 we must have removed at least the region(view) we've just been working on as well as any
1056 that we processed on previous iterations.
1058 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1059 we can just iterate.
1063 if (_views.empty()) {
1064 break;
1065 } else {
1066 i = _views.begin();
1069 } else {
1070 ++i;
1074 /* If we've created new regions either by copying or moving
1075 to a new track, we want to replace the old selection with the new ones
1078 if (new_views.size() > 0) {
1079 _editor->selection->set (new_views);
1082 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1083 (*p)->thaw();
1086 /* write commands for the accumulated diffs for all our modified playlists */
1087 add_stateful_diff_commands_for_playlists (modified_playlists);
1089 _editor->commit_reversible_command ();
1092 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1093 * @param region Region to remove.
1094 * @param playlist playlist To remove from.
1095 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1096 * that clear_changes () is only called once per playlist.
1098 void
1099 RegionMoveDrag::remove_region_from_playlist (
1100 boost::shared_ptr<Region> region,
1101 boost::shared_ptr<Playlist> playlist,
1102 PlaylistSet& modified_playlists
1105 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1107 if (r.second) {
1108 playlist->clear_changes ();
1111 playlist->remove_region (region);
1115 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1116 * clearing the playlist's diff history first if necessary.
1117 * @param region Region to insert.
1118 * @param dest_rtv Destination RouteTimeAxisView.
1119 * @param dest_layer Destination layer.
1120 * @param where Destination position.
1121 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1122 * that clear_changes () is only called once per playlist.
1123 * @return New RegionView, or 0 if no insert was performed.
1125 RegionView *
1126 RegionMoveDrag::insert_region_into_playlist (
1127 boost::shared_ptr<Region> region,
1128 RouteTimeAxisView* dest_rtv,
1129 layer_t dest_layer,
1130 framecnt_t where,
1131 PlaylistSet& modified_playlists
1134 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1135 if (!dest_playlist) {
1136 return 0;
1139 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1140 _new_region_view = 0;
1141 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1143 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1144 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1145 if (r.second) {
1146 dest_playlist->clear_changes ();
1149 dest_playlist->add_region (region, where);
1151 if (dest_rtv->view()->layer_display() == Stacked) {
1152 region->set_layer (dest_layer);
1153 region->set_pending_explicit_relayer (true);
1156 c.disconnect ();
1158 assert (_new_region_view);
1160 return _new_region_view;
1163 void
1164 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1166 _new_region_view = rv;
1169 void
1170 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1172 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1173 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1174 if (!c->empty()) {
1175 _editor->session()->add_command (c);
1176 } else {
1177 delete c;
1183 void
1184 RegionMoveDrag::aborted (bool movement_occurred)
1186 if (_copy) {
1188 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1189 delete i->view;
1192 _views.clear ();
1194 } else {
1195 RegionMotionDrag::aborted (movement_occurred);
1199 void
1200 RegionMotionDrag::aborted (bool)
1202 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1203 RegionView* rv = i->view;
1204 TimeAxisView* tv = &(rv->get_time_axis_view ());
1205 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1206 assert (rtv);
1207 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1208 rv->get_canvas_group()->property_y() = 0;
1209 rv->get_time_axis_view().reveal_dependent_views (*rv);
1210 rv->fake_set_opaque (false);
1211 rv->move (-_total_x_delta, 0);
1212 rv->set_height (rtv->view()->child_height ());
1215 _editor->update_canvas_now ();
1218 /** @param b true to brush, otherwise false.
1219 * @param c true to make copies of the regions being moved, otherwise false.
1221 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1222 : RegionMotionDrag (e, i, p, v, b),
1223 _copy (c)
1225 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1227 double speed = 1;
1228 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1229 if (rtv && rtv->is_track()) {
1230 speed = rtv->track()->speed ();
1233 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1236 void
1237 RegionMoveDrag::setup_pointer_frame_offset ()
1239 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1242 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1243 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1245 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1247 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1248 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1250 _primary = v->view()->create_region_view (r, false, false);
1252 _primary->get_canvas_group()->show ();
1253 _primary->set_position (pos, 0);
1254 _views.push_back (DraggingView (_primary, this));
1256 _last_frame_position = pos;
1258 _item = _primary->get_canvas_group ();
1261 void
1262 RegionInsertDrag::finished (GdkEvent *, bool)
1264 _editor->update_canvas_now ();
1266 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1268 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1269 _primary->get_canvas_group()->property_y() = 0;
1271 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1273 _editor->begin_reversible_command (Operations::insert_region);
1274 playlist->clear_changes ();
1275 playlist->add_region (_primary->region (), _last_frame_position);
1276 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1277 _editor->commit_reversible_command ();
1279 delete _primary;
1280 _primary = 0;
1281 _views.clear ();
1284 void
1285 RegionInsertDrag::aborted (bool)
1287 delete _primary;
1288 _primary = 0;
1289 _views.clear ();
1292 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1293 : RegionMoveDrag (e, i, p, v, false, false)
1295 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1298 struct RegionSelectionByPosition {
1299 bool operator() (RegionView*a, RegionView* b) {
1300 return a->region()->position () < b->region()->position();
1304 void
1305 RegionSpliceDrag::motion (GdkEvent* event, bool)
1307 /* Which trackview is this ? */
1309 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1310 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1311 layer_t layer = tvp.second;
1313 if (tv && tv->layer_display() == Overlaid) {
1314 layer = 0;
1317 /* The region motion is only processed if the pointer is over
1318 an audio track.
1321 if (!tv || !tv->is_track()) {
1322 /* To make sure we hide the verbose canvas cursor when the mouse is
1323 not held over and audiotrack.
1325 _editor->verbose_cursor()->hide ();
1326 return;
1329 int dir;
1331 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1332 dir = 1;
1333 } else {
1334 dir = -1;
1337 RegionSelection copy (_editor->selection->regions);
1339 RegionSelectionByPosition cmp;
1340 copy.sort (cmp);
1342 framepos_t const pf = adjusted_current_frame (event);
1344 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1346 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1348 if (!atv) {
1349 continue;
1352 boost::shared_ptr<Playlist> playlist;
1354 if ((playlist = atv->playlist()) == 0) {
1355 continue;
1358 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1359 continue;
1362 if (dir > 0) {
1363 if (pf < (*i)->region()->last_frame() + 1) {
1364 continue;
1366 } else {
1367 if (pf > (*i)->region()->first_frame()) {
1368 continue;
1373 playlist->shuffle ((*i)->region(), dir);
1377 void
1378 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1380 RegionMoveDrag::finished (event, movement_occurred);
1383 void
1384 RegionSpliceDrag::aborted (bool)
1386 /* XXX: TODO */
1389 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1390 : Drag (e, i),
1391 _view (dynamic_cast<MidiTimeAxisView*> (v))
1393 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1395 assert (_view);
1398 void
1399 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1401 if (first_move) {
1402 add_region();
1403 _view->playlist()->freeze ();
1404 } else {
1405 if (_region) {
1406 framepos_t const f = adjusted_current_frame (event);
1407 if (f < grab_frame()) {
1408 _region->set_position (f, this);
1411 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1412 so that if this region is duplicated, its duplicate starts on
1413 a snap point rather than 1 frame after a snap point. Otherwise things get
1414 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1415 place snapped notes at the start of the region.
1418 framecnt_t const len = abs (f - grab_frame () - 1);
1419 _region->set_length (len < 1 ? 1 : len, this);
1424 void
1425 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1427 if (!movement_occurred) {
1428 add_region ();
1429 } else {
1430 _view->playlist()->thaw ();
1433 if (_region) {
1434 _editor->commit_reversible_command ();
1438 void
1439 RegionCreateDrag::add_region ()
1441 if (_editor->session()) {
1442 const TempoMap& map (_editor->session()->tempo_map());
1443 framecnt_t pos = grab_frame();
1444 const Meter& m = map.meter_at (pos);
1445 /* not that the frame rate used here can be affected by pull up/down which
1446 might be wrong.
1448 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1449 _region = _view->add_region (grab_frame(), len, false);
1453 void
1454 RegionCreateDrag::aborted (bool)
1456 if (_region) {
1457 _view->playlist()->thaw ();
1460 /* XXX */
1463 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1464 : Drag (e, i)
1465 , region (0)
1467 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1470 void
1471 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1473 Gdk::Cursor* cursor;
1474 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1475 float x_fraction = cnote->mouse_x_fraction ();
1477 if (x_fraction > 0.0 && x_fraction < 0.25) {
1478 cursor = _editor->cursors()->left_side_trim;
1479 } else {
1480 cursor = _editor->cursors()->right_side_trim;
1483 Drag::start_grab (event, cursor);
1485 region = &cnote->region_view();
1487 double const region_start = region->get_position_pixels();
1488 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1490 if (grab_x() <= middle_point) {
1491 cursor = _editor->cursors()->left_side_trim;
1492 at_front = true;
1493 } else {
1494 cursor = _editor->cursors()->right_side_trim;
1495 at_front = false;
1498 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1500 if (event->motion.state & Keyboard::PrimaryModifier) {
1501 relative = false;
1502 } else {
1503 relative = true;
1506 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1508 if (ms.size() > 1) {
1509 /* has to be relative, may make no sense otherwise */
1510 relative = true;
1513 /* select this note; if it is already selected, preserve the existing selection,
1514 otherwise make this note the only one selected.
1516 region->note_selected (cnote, cnote->selected ());
1518 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1519 MidiRegionSelection::iterator next;
1520 next = r;
1521 ++next;
1522 (*r)->begin_resizing (at_front);
1523 r = next;
1527 void
1528 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1530 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1531 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1532 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1536 void
1537 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1539 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1540 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1541 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1545 void
1546 NoteResizeDrag::aborted (bool)
1548 /* XXX: TODO */
1551 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1552 : Drag (e, i)
1554 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1557 void
1558 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1563 void
1564 RegionGainDrag::finished (GdkEvent *, bool)
1569 void
1570 RegionGainDrag::aborted (bool)
1572 /* XXX: TODO */
1575 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1576 : RegionDrag (e, i, p, v)
1578 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1581 void
1582 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1584 double speed = 1.0;
1585 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1586 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1588 if (tv && tv->is_track()) {
1589 speed = tv->track()->speed();
1592 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1593 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1594 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1596 framepos_t const pf = adjusted_current_frame (event);
1598 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1599 /* Move the contents of the region around without changing the region bounds */
1600 _operation = ContentsTrim;
1601 Drag::start_grab (event, _editor->cursors()->trimmer);
1602 } else {
1603 /* These will get overridden for a point trim.*/
1604 if (pf < (region_start + region_length/2)) {
1605 /* closer to front */
1606 _operation = StartTrim;
1607 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1608 } else {
1609 /* closer to end */
1610 _operation = EndTrim;
1611 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1615 switch (_operation) {
1616 case StartTrim:
1617 show_verbose_cursor_time (region_start);
1618 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1619 i->view->trim_front_starting ();
1621 break;
1622 case EndTrim:
1623 show_verbose_cursor_time (region_end);
1624 break;
1625 case ContentsTrim:
1626 show_verbose_cursor_time (pf);
1627 break;
1630 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1631 i->view->region()->suspend_property_changes ();
1635 void
1636 TrimDrag::motion (GdkEvent* event, bool first_move)
1638 RegionView* rv = _primary;
1640 double speed = 1.0;
1641 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1642 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1643 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1645 if (tv && tv->is_track()) {
1646 speed = tv->track()->speed();
1649 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1651 if (first_move) {
1653 string trim_type;
1655 switch (_operation) {
1656 case StartTrim:
1657 trim_type = "Region start trim";
1658 break;
1659 case EndTrim:
1660 trim_type = "Region end trim";
1661 break;
1662 case ContentsTrim:
1663 trim_type = "Region content trim";
1664 break;
1667 _editor->begin_reversible_command (trim_type);
1669 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1670 RegionView* rv = i->view;
1671 rv->fake_set_opaque (false);
1672 rv->enable_display (false);
1673 rv->region()->playlist()->clear_owned_changes ();
1675 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1677 if (arv) {
1678 arv->temporarily_hide_envelope ();
1681 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1682 insert_result = _editor->motion_frozen_playlists.insert (pl);
1684 if (insert_result.second) {
1685 pl->freeze();
1690 bool non_overlap_trim = false;
1692 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1693 non_overlap_trim = true;
1696 switch (_operation) {
1697 case StartTrim:
1698 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1699 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1701 break;
1703 case EndTrim:
1704 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1705 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1707 break;
1709 case ContentsTrim:
1711 bool swap_direction = false;
1713 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1714 swap_direction = true;
1717 framecnt_t frame_delta = 0;
1719 bool left_direction = false;
1720 if (last_pointer_frame() > adjusted_current_frame(event)) {
1721 left_direction = true;
1724 if (left_direction) {
1725 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1726 } else {
1727 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1730 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1731 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1734 break;
1737 switch (_operation) {
1738 case StartTrim:
1739 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1740 break;
1741 case EndTrim:
1742 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1743 break;
1744 case ContentsTrim:
1745 show_verbose_cursor_time (adjusted_current_frame (event));
1746 break;
1751 void
1752 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1754 if (movement_occurred) {
1755 motion (event, false);
1757 /* This must happen before the region's StatefulDiffCommand is created, as it may
1758 `correct' (ahem) the region's _start from being negative to being zero. It
1759 needs to be zero in the undo record.
1761 if (_operation == StartTrim) {
1762 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1763 i->view->trim_front_ending ();
1767 if (!_editor->selection->selected (_primary)) {
1768 _primary->thaw_after_trim ();
1769 } else {
1771 set<boost::shared_ptr<Playlist> > diffed_playlists;
1773 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1774 i->view->thaw_after_trim ();
1775 i->view->enable_display (true);
1776 i->view->fake_set_opaque (true);
1778 /* Trimming one region may affect others on the playlist, so we need
1779 to get undo Commands from the whole playlist rather than just the
1780 region. Use diffed_playlists to make sure we don't diff a given
1781 playlist more than once.
1783 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1784 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1785 vector<Command*> cmds;
1786 p->rdiff (cmds);
1787 _editor->session()->add_commands (cmds);
1788 diffed_playlists.insert (p);
1792 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1793 (*p)->thaw ();
1796 _editor->motion_frozen_playlists.clear ();
1797 _editor->commit_reversible_command();
1799 } else {
1800 /* no mouse movement */
1801 _editor->point_trim (event, adjusted_current_frame (event));
1804 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1805 if (_operation == StartTrim) {
1806 i->view->trim_front_ending ();
1809 i->view->region()->resume_property_changes ();
1813 void
1814 TrimDrag::aborted (bool movement_occurred)
1816 /* Our motion method is changing model state, so use the Undo system
1817 to cancel. Perhaps not ideal, as this will leave an Undo point
1818 behind which may be slightly odd from the user's point of view.
1821 finished (0, true);
1823 if (movement_occurred) {
1824 _editor->undo ();
1827 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1828 i->view->region()->resume_property_changes ();
1832 void
1833 TrimDrag::setup_pointer_frame_offset ()
1835 list<DraggingView>::iterator i = _views.begin ();
1836 while (i != _views.end() && i->view != _primary) {
1837 ++i;
1840 if (i == _views.end()) {
1841 return;
1844 switch (_operation) {
1845 case StartTrim:
1846 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1847 break;
1848 case EndTrim:
1849 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1850 break;
1851 case ContentsTrim:
1852 break;
1856 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1857 : Drag (e, i),
1858 _copy (c)
1860 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1862 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1863 assert (_marker);
1866 void
1867 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1869 if (_copy) {
1870 // create a dummy marker for visual representation of moving the copy.
1871 // The actual copying is not done before we reach the finish callback.
1872 char name[64];
1873 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1875 MeterMarker* new_marker = new MeterMarker (
1876 *_editor,
1877 *_editor->meter_group,
1878 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1879 name,
1880 *new MeterSection (_marker->meter())
1883 _item = &new_marker->the_item ();
1884 _marker = new_marker;
1886 } else {
1888 MetricSection& section (_marker->meter());
1890 if (!section.movable()) {
1891 return;
1896 Drag::start_grab (event, cursor);
1898 show_verbose_cursor_time (adjusted_current_frame(event));
1901 void
1902 MeterMarkerDrag::setup_pointer_frame_offset ()
1904 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1907 void
1908 MeterMarkerDrag::motion (GdkEvent* event, bool)
1910 framepos_t const pf = adjusted_current_frame (event);
1912 _marker->set_position (pf);
1914 show_verbose_cursor_time (pf);
1917 void
1918 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1920 if (!movement_occurred) {
1921 return;
1924 motion (event, false);
1926 Timecode::BBT_Time when;
1928 TempoMap& map (_editor->session()->tempo_map());
1929 map.bbt_time (last_pointer_frame(), when);
1931 if (_copy == true) {
1932 _editor->begin_reversible_command (_("copy meter mark"));
1933 XMLNode &before = map.get_state();
1934 map.add_meter (_marker->meter(), when);
1935 XMLNode &after = map.get_state();
1936 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1937 _editor->commit_reversible_command ();
1939 // delete the dummy marker we used for visual representation of copying.
1940 // a new visual marker will show up automatically.
1941 delete _marker;
1942 } else {
1943 _editor->begin_reversible_command (_("move meter mark"));
1944 XMLNode &before = map.get_state();
1945 map.move_meter (_marker->meter(), when);
1946 XMLNode &after = map.get_state();
1947 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1948 _editor->commit_reversible_command ();
1952 void
1953 MeterMarkerDrag::aborted (bool)
1955 _marker->set_position (_marker->meter().frame ());
1958 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1959 : Drag (e, i),
1960 _copy (c)
1962 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1964 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1965 assert (_marker);
1968 void
1969 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1971 if (_copy) {
1973 // create a dummy marker for visual representation of moving the copy.
1974 // The actual copying is not done before we reach the finish callback.
1975 char name[64];
1976 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1978 TempoMarker* new_marker = new TempoMarker (
1979 *_editor,
1980 *_editor->tempo_group,
1981 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1982 name,
1983 *new TempoSection (_marker->tempo())
1986 _item = &new_marker->the_item ();
1987 _marker = new_marker;
1991 Drag::start_grab (event, cursor);
1993 show_verbose_cursor_time (adjusted_current_frame (event));
1996 void
1997 TempoMarkerDrag::setup_pointer_frame_offset ()
1999 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2002 void
2003 TempoMarkerDrag::motion (GdkEvent* event, bool)
2005 framepos_t const pf = adjusted_current_frame (event);
2006 _marker->set_position (pf);
2007 show_verbose_cursor_time (pf);
2010 void
2011 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2013 if (!movement_occurred) {
2014 return;
2017 motion (event, false);
2019 Timecode::BBT_Time when;
2021 TempoMap& map (_editor->session()->tempo_map());
2022 map.bbt_time (last_pointer_frame(), when);
2024 if (_copy == true) {
2025 _editor->begin_reversible_command (_("copy tempo mark"));
2026 XMLNode &before = map.get_state();
2027 map.add_tempo (_marker->tempo(), when);
2028 XMLNode &after = map.get_state();
2029 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2030 _editor->commit_reversible_command ();
2032 // delete the dummy marker we used for visual representation of copying.
2033 // a new visual marker will show up automatically.
2034 delete _marker;
2035 } else {
2036 _editor->begin_reversible_command (_("move tempo mark"));
2037 XMLNode &before = map.get_state();
2038 map.move_tempo (_marker->tempo(), when);
2039 XMLNode &after = map.get_state();
2040 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2041 _editor->commit_reversible_command ();
2045 void
2046 TempoMarkerDrag::aborted (bool)
2048 _marker->set_position (_marker->tempo().frame());
2051 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2052 : Drag (e, i),
2053 _stop (s)
2055 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2058 /** Do all the things we do when dragging the playhead to make it look as though
2059 * we have located, without actually doing the locate (because that would cause
2060 * the diskstream buffers to be refilled, which is too slow).
2062 void
2063 CursorDrag::fake_locate (framepos_t t)
2065 _editor->playhead_cursor->set_position (t);
2067 Session* s = _editor->session ();
2068 if (s->timecode_transmission_suspended ()) {
2069 framepos_t const f = _editor->playhead_cursor->current_frame;
2070 s->send_mmc_locate (f);
2071 s->send_full_time_code (f);
2074 show_verbose_cursor_time (t);
2075 _editor->UpdateAllTransportClocks (t);
2078 void
2079 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2081 Drag::start_grab (event, c);
2083 framepos_t where = _editor->event_frame (event, 0, 0);
2084 _editor->snap_to_with_modifier (where, event);
2086 _editor->_dragging_playhead = true;
2088 Session* s = _editor->session ();
2090 if (s) {
2091 if (_was_rolling && _stop) {
2092 s->request_stop ();
2095 if (s->is_auditioning()) {
2096 s->cancel_audition ();
2099 s->request_suspend_timecode_transmission ();
2100 while (!s->timecode_transmission_suspended ()) {
2101 /* twiddle our thumbs */
2105 fake_locate (where);
2108 void
2109 CursorDrag::motion (GdkEvent* event, bool)
2111 framepos_t const adjusted_frame = adjusted_current_frame (event);
2113 if (adjusted_frame == last_pointer_frame()) {
2114 return;
2117 fake_locate (adjusted_frame);
2119 #ifdef GTKOSX
2120 _editor->update_canvas_now ();
2121 #endif
2124 void
2125 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2127 _editor->_dragging_playhead = false;
2129 if (!movement_occurred && _stop) {
2130 return;
2133 motion (event, false);
2135 Session* s = _editor->session ();
2136 if (s) {
2137 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2138 _editor->_pending_locate_request = true;
2139 s->request_resume_timecode_transmission ();
2143 void
2144 CursorDrag::aborted (bool)
2146 if (_editor->_dragging_playhead) {
2147 _editor->session()->request_resume_timecode_transmission ();
2148 _editor->_dragging_playhead = false;
2151 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2154 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2155 : RegionDrag (e, i, p, v)
2157 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2160 void
2161 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2163 Drag::start_grab (event, cursor);
2165 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2166 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2168 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when);
2170 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2173 void
2174 FadeInDrag::setup_pointer_frame_offset ()
2176 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2177 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2178 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2181 void
2182 FadeInDrag::motion (GdkEvent* event, bool)
2184 framecnt_t fade_length;
2186 framepos_t const pos = adjusted_current_frame (event);
2188 boost::shared_ptr<Region> region = _primary->region ();
2190 if (pos < (region->position() + 64)) {
2191 fade_length = 64; // this should be a minimum defined somewhere
2192 } else if (pos > region->last_frame()) {
2193 fade_length = region->length();
2194 } else {
2195 fade_length = pos - region->position();
2198 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2200 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2202 if (!tmp) {
2203 continue;
2206 tmp->reset_fade_in_shape_width (fade_length);
2207 tmp->show_fade_line((framecnt_t) fade_length);
2210 show_verbose_cursor_duration (region->position(), region->position() + fade_length);
2213 void
2214 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2216 if (!movement_occurred) {
2217 return;
2220 framecnt_t fade_length;
2222 framepos_t const pos = adjusted_current_frame (event);
2224 boost::shared_ptr<Region> region = _primary->region ();
2226 if (pos < (region->position() + 64)) {
2227 fade_length = 64; // this should be a minimum defined somewhere
2228 } else if (pos > region->last_frame()) {
2229 fade_length = region->length();
2230 } else {
2231 fade_length = pos - region->position();
2234 _editor->begin_reversible_command (_("change fade in length"));
2236 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2238 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2240 if (!tmp) {
2241 continue;
2244 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2245 XMLNode &before = alist->get_state();
2247 tmp->audio_region()->set_fade_in_length (fade_length);
2248 tmp->audio_region()->set_fade_in_active (true);
2249 tmp->hide_fade_line();
2251 XMLNode &after = alist->get_state();
2252 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2255 _editor->commit_reversible_command ();
2258 void
2259 FadeInDrag::aborted (bool)
2261 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2262 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2264 if (!tmp) {
2265 continue;
2268 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2269 tmp->hide_fade_line();
2273 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2274 : RegionDrag (e, i, p, v)
2276 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2279 void
2280 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2282 Drag::start_grab (event, cursor);
2284 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2285 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2287 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2289 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2292 void
2293 FadeOutDrag::setup_pointer_frame_offset ()
2295 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2296 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2297 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2300 void
2301 FadeOutDrag::motion (GdkEvent* event, bool)
2303 framecnt_t fade_length;
2305 framepos_t const pos = adjusted_current_frame (event);
2307 boost::shared_ptr<Region> region = _primary->region ();
2309 if (pos > (region->last_frame() - 64)) {
2310 fade_length = 64; // this should really be a minimum fade defined somewhere
2312 else if (pos < region->position()) {
2313 fade_length = region->length();
2315 else {
2316 fade_length = region->last_frame() - pos;
2319 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2321 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2323 if (!tmp) {
2324 continue;
2327 tmp->reset_fade_out_shape_width (fade_length);
2328 tmp->show_fade_line(region->length() - fade_length);
2331 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2334 void
2335 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2337 if (!movement_occurred) {
2338 return;
2341 framecnt_t fade_length;
2343 framepos_t const pos = adjusted_current_frame (event);
2345 boost::shared_ptr<Region> region = _primary->region ();
2347 if (pos > (region->last_frame() - 64)) {
2348 fade_length = 64; // this should really be a minimum fade defined somewhere
2350 else if (pos < region->position()) {
2351 fade_length = region->length();
2353 else {
2354 fade_length = region->last_frame() - pos;
2357 _editor->begin_reversible_command (_("change fade out length"));
2359 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2361 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2363 if (!tmp) {
2364 continue;
2367 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2368 XMLNode &before = alist->get_state();
2370 tmp->audio_region()->set_fade_out_length (fade_length);
2371 tmp->audio_region()->set_fade_out_active (true);
2372 tmp->hide_fade_line();
2374 XMLNode &after = alist->get_state();
2375 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2378 _editor->commit_reversible_command ();
2381 void
2382 FadeOutDrag::aborted (bool)
2384 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2385 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2387 if (!tmp) {
2388 continue;
2391 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2392 tmp->hide_fade_line();
2396 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2397 : Drag (e, i)
2399 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2401 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2402 assert (_marker);
2404 _points.push_back (Gnome::Art::Point (0, 0));
2405 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2408 MarkerDrag::~MarkerDrag ()
2410 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2411 delete *i;
2415 void
2416 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2418 Drag::start_grab (event, cursor);
2420 bool is_start;
2422 Location *location = _editor->find_location_from_marker (_marker, is_start);
2423 _editor->_dragging_edit_point = true;
2425 update_item (location);
2427 // _drag_line->show();
2428 // _line->raise_to_top();
2430 if (is_start) {
2431 show_verbose_cursor_time (location->start());
2432 } else {
2433 show_verbose_cursor_time (location->end());
2436 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2438 switch (op) {
2439 case Selection::Toggle:
2440 _editor->selection->toggle (_marker);
2441 break;
2442 case Selection::Set:
2443 if (!_editor->selection->selected (_marker)) {
2444 _editor->selection->set (_marker);
2446 break;
2447 case Selection::Extend:
2449 Locations::LocationList ll;
2450 list<Marker*> to_add;
2451 framepos_t s, e;
2452 _editor->selection->markers.range (s, e);
2453 s = min (_marker->position(), s);
2454 e = max (_marker->position(), e);
2455 s = min (s, e);
2456 e = max (s, e);
2457 if (e < max_framepos) {
2458 ++e;
2460 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2461 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2462 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2463 if (lm) {
2464 if (lm->start) {
2465 to_add.push_back (lm->start);
2467 if (lm->end) {
2468 to_add.push_back (lm->end);
2472 if (!to_add.empty()) {
2473 _editor->selection->add (to_add);
2475 break;
2477 case Selection::Add:
2478 _editor->selection->add (_marker);
2479 break;
2482 /* Set up copies for us to manipulate during the drag */
2484 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2485 Location* l = _editor->find_location_from_marker (*i, is_start);
2486 _copied_locations.push_back (new Location (*l));
2490 void
2491 MarkerDrag::setup_pointer_frame_offset ()
2493 bool is_start;
2494 Location *location = _editor->find_location_from_marker (_marker, is_start);
2495 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2498 void
2499 MarkerDrag::motion (GdkEvent* event, bool)
2501 framecnt_t f_delta = 0;
2502 bool is_start;
2503 bool move_both = false;
2504 Marker* marker;
2505 Location *real_location;
2506 Location *copy_location = 0;
2508 framepos_t const newframe = adjusted_current_frame (event);
2510 framepos_t next = newframe;
2512 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2513 move_both = true;
2516 MarkerSelection::iterator i;
2517 list<Location*>::iterator x;
2519 /* find the marker we're dragging, and compute the delta */
2521 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2522 x != _copied_locations.end() && i != _editor->selection->markers.end();
2523 ++i, ++x) {
2525 copy_location = *x;
2526 marker = *i;
2528 if (marker == _marker) {
2530 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2531 /* que pasa ?? */
2532 return;
2535 if (real_location->is_mark()) {
2536 f_delta = newframe - copy_location->start();
2537 } else {
2540 switch (marker->type()) {
2541 case Marker::SessionStart:
2542 case Marker::RangeStart:
2543 case Marker::LoopStart:
2544 case Marker::PunchIn:
2545 f_delta = newframe - copy_location->start();
2546 break;
2548 case Marker::SessionEnd:
2549 case Marker::RangeEnd:
2550 case Marker::LoopEnd:
2551 case Marker::PunchOut:
2552 f_delta = newframe - copy_location->end();
2553 break;
2554 default:
2555 /* what kind of marker is this ? */
2556 return;
2559 break;
2563 if (i == _editor->selection->markers.end()) {
2564 /* hmm, impossible - we didn't find the dragged marker */
2565 return;
2568 /* now move them all */
2570 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2571 x != _copied_locations.end() && i != _editor->selection->markers.end();
2572 ++i, ++x) {
2574 copy_location = *x;
2575 marker = *i;
2577 /* call this to find out if its the start or end */
2579 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2580 continue;
2583 if (real_location->locked()) {
2584 continue;
2587 if (copy_location->is_mark()) {
2589 /* now move it */
2591 copy_location->set_start (copy_location->start() + f_delta);
2593 } else {
2595 framepos_t new_start = copy_location->start() + f_delta;
2596 framepos_t new_end = copy_location->end() + f_delta;
2598 if (is_start) { // start-of-range marker
2600 if (move_both) {
2601 copy_location->set_start (new_start);
2602 copy_location->set_end (new_end);
2603 } else if (new_start < copy_location->end()) {
2604 copy_location->set_start (new_start);
2605 } else if (newframe > 0) {
2606 _editor->snap_to (next, 1, true);
2607 copy_location->set_end (next);
2608 copy_location->set_start (newframe);
2611 } else { // end marker
2613 if (move_both) {
2614 copy_location->set_end (new_end);
2615 copy_location->set_start (new_start);
2616 } else if (new_end > copy_location->start()) {
2617 copy_location->set_end (new_end);
2618 } else if (newframe > 0) {
2619 _editor->snap_to (next, -1, true);
2620 copy_location->set_start (next);
2621 copy_location->set_end (newframe);
2626 update_item (copy_location);
2628 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2630 if (lm) {
2631 lm->set_position (copy_location->start(), copy_location->end());
2635 assert (!_copied_locations.empty());
2637 show_verbose_cursor_time (newframe);
2639 #ifdef GTKOSX
2640 _editor->update_canvas_now ();
2641 #endif
2644 void
2645 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2647 if (!movement_occurred) {
2649 /* just a click, do nothing but finish
2650 off the selection process
2653 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2655 switch (op) {
2656 case Selection::Set:
2657 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2658 _editor->selection->set (_marker);
2660 break;
2662 case Selection::Toggle:
2663 case Selection::Extend:
2664 case Selection::Add:
2665 break;
2668 return;
2671 _editor->_dragging_edit_point = false;
2673 _editor->begin_reversible_command ( _("move marker") );
2674 XMLNode &before = _editor->session()->locations()->get_state();
2676 MarkerSelection::iterator i;
2677 list<Location*>::iterator x;
2678 bool is_start;
2680 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2681 x != _copied_locations.end() && i != _editor->selection->markers.end();
2682 ++i, ++x) {
2684 Location * location = _editor->find_location_from_marker (*i, is_start);
2686 if (location) {
2688 if (location->locked()) {
2689 return;
2692 if (location->is_mark()) {
2693 location->set_start ((*x)->start());
2694 } else {
2695 location->set ((*x)->start(), (*x)->end());
2700 XMLNode &after = _editor->session()->locations()->get_state();
2701 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2702 _editor->commit_reversible_command ();
2705 void
2706 MarkerDrag::aborted (bool)
2708 /* XXX: TODO */
2711 void
2712 MarkerDrag::update_item (Location* location)
2714 /* noop */
2717 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2718 : Drag (e, i),
2719 _cumulative_x_drag (0),
2720 _cumulative_y_drag (0)
2722 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2724 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2725 assert (_point);
2729 void
2730 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2732 Drag::start_grab (event, _editor->cursors()->fader);
2734 // start the grab at the center of the control point so
2735 // the point doesn't 'jump' to the mouse after the first drag
2736 _fixed_grab_x = _point->get_x();
2737 _fixed_grab_y = _point->get_y();
2739 float const fraction = 1 - (_point->get_y() / _point->line().height());
2741 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2743 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2744 event->button.x + 10, event->button.y + 10);
2746 _editor->verbose_cursor()->show ();
2749 void
2750 ControlPointDrag::motion (GdkEvent* event, bool)
2752 double dx = _drags->current_pointer_x() - last_pointer_x();
2753 double dy = _drags->current_pointer_y() - last_pointer_y();
2755 if (event->button.state & Keyboard::SecondaryModifier) {
2756 dx *= 0.1;
2757 dy *= 0.1;
2760 /* coordinate in pixels relative to the start of the region (for region-based automation)
2761 or track (for track-based automation) */
2762 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2763 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2765 // calculate zero crossing point. back off by .01 to stay on the
2766 // positive side of zero
2767 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2769 // make sure we hit zero when passing through
2770 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2771 cy = zero_gain_y;
2774 if (_x_constrained) {
2775 cx = _fixed_grab_x;
2777 if (_y_constrained) {
2778 cy = _fixed_grab_y;
2781 _cumulative_x_drag = cx - _fixed_grab_x;
2782 _cumulative_y_drag = cy - _fixed_grab_y;
2784 cx = max (0.0, cx);
2785 cy = max (0.0, cy);
2786 cy = min ((double) _point->line().height(), cy);
2788 framepos_t cx_frames = _editor->unit_to_frame (cx);
2790 if (!_x_constrained) {
2791 _editor->snap_to_with_modifier (cx_frames, event);
2794 cx_frames = min (cx_frames, _point->line().maximum_time());
2796 float const fraction = 1.0 - (cy / _point->line().height());
2798 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2800 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2802 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2805 void
2806 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2808 if (!movement_occurred) {
2810 /* just a click */
2812 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2813 _editor->reset_point_selection ();
2816 } else {
2817 motion (event, false);
2820 _point->line().end_drag ();
2821 _editor->session()->commit_reversible_command ();
2824 void
2825 ControlPointDrag::aborted (bool)
2827 _point->line().reset ();
2830 bool
2831 ControlPointDrag::active (Editing::MouseMode m)
2833 if (m == Editing::MouseGain) {
2834 /* always active in mouse gain */
2835 return true;
2838 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2839 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2842 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2843 : Drag (e, i),
2844 _line (0),
2845 _cumulative_y_drag (0)
2847 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2850 void
2851 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2853 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2854 assert (_line);
2856 _item = &_line->grab_item ();
2858 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2859 origin, and ditto for y.
2862 double cx = event->button.x;
2863 double cy = event->button.y;
2865 _line->parent_group().w2i (cx, cy);
2867 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2869 uint32_t before;
2870 uint32_t after;
2872 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2873 /* no adjacent points */
2874 return;
2877 Drag::start_grab (event, _editor->cursors()->fader);
2879 /* store grab start in parent frame */
2881 _fixed_grab_x = cx;
2882 _fixed_grab_y = cy;
2884 double fraction = 1.0 - (cy / _line->height());
2886 _line->start_drag_line (before, after, fraction);
2888 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2889 event->button.x + 10, event->button.y + 10);
2891 _editor->verbose_cursor()->show ();
2894 void
2895 LineDrag::motion (GdkEvent* event, bool)
2897 double dy = _drags->current_pointer_y() - last_pointer_y();
2899 if (event->button.state & Keyboard::SecondaryModifier) {
2900 dy *= 0.1;
2903 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2905 _cumulative_y_drag = cy - _fixed_grab_y;
2907 cy = max (0.0, cy);
2908 cy = min ((double) _line->height(), cy);
2910 double const fraction = 1.0 - (cy / _line->height());
2912 bool push;
2914 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2915 push = false;
2916 } else {
2917 push = true;
2920 /* we are ignoring x position for this drag, so we can just pass in anything */
2921 _line->drag_motion (0, fraction, true, push);
2923 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2926 void
2927 LineDrag::finished (GdkEvent* event, bool)
2929 motion (event, false);
2930 _line->end_drag ();
2931 _editor->session()->commit_reversible_command ();
2934 void
2935 LineDrag::aborted (bool)
2937 _line->reset ();
2940 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2941 : Drag (e, i),
2942 _line (0),
2943 _cumulative_x_drag (0)
2945 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2948 void
2949 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2951 Drag::start_grab (event);
2953 _line = reinterpret_cast<Line*> (_item);
2954 assert (_line);
2956 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2958 double cx = event->button.x;
2959 double cy = event->button.y;
2961 _item->property_parent().get_value()->w2i(cx, cy);
2963 /* store grab start in parent frame */
2964 _region_view_grab_x = cx;
2966 _before = *(float*) _item->get_data ("position");
2968 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2970 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2973 void
2974 FeatureLineDrag::motion (GdkEvent*, bool)
2976 double dx = _drags->current_pointer_x() - last_pointer_x();
2978 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2980 _cumulative_x_drag += dx;
2982 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2984 if (cx > _max_x){
2985 cx = _max_x;
2987 else if(cx < 0){
2988 cx = 0;
2991 ArdourCanvas::Points points;
2993 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
2995 _line->get_bounds(x1, y2, x2, y2);
2997 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
2998 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3000 _line->property_points() = points;
3002 float *pos = new float;
3003 *pos = cx;
3005 _line->set_data ("position", pos);
3007 _before = cx;
3010 void
3011 FeatureLineDrag::finished (GdkEvent*, bool)
3013 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3014 _arv->update_transient(_before, _before);
3017 void
3018 FeatureLineDrag::aborted (bool)
3020 //_line->reset ();
3023 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3024 : Drag (e, i)
3026 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3029 void
3030 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3032 Drag::start_grab (event);
3033 show_verbose_cursor_time (adjusted_current_frame (event));
3036 void
3037 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3039 framepos_t start;
3040 framepos_t end;
3041 double y1;
3042 double y2;
3044 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3046 framepos_t grab = grab_frame ();
3047 if (Config->get_rubberbanding_snaps_to_grid ()) {
3048 _editor->snap_to_with_modifier (grab, event);
3051 /* base start and end on initial click position */
3053 if (pf < grab) {
3054 start = pf;
3055 end = grab;
3056 } else {
3057 end = pf;
3058 start = grab;
3061 if (_drags->current_pointer_y() < grab_y()) {
3062 y1 = _drags->current_pointer_y();
3063 y2 = grab_y();
3064 } else {
3065 y2 = _drags->current_pointer_y();
3066 y1 = grab_y();
3070 if (start != end || y1 != y2) {
3072 double x1 = _editor->frame_to_pixel (start);
3073 double x2 = _editor->frame_to_pixel (end);
3075 _editor->rubberband_rect->property_x1() = x1;
3076 _editor->rubberband_rect->property_y1() = y1;
3077 _editor->rubberband_rect->property_x2() = x2;
3078 _editor->rubberband_rect->property_y2() = y2;
3080 _editor->rubberband_rect->show();
3081 _editor->rubberband_rect->raise_to_top();
3083 show_verbose_cursor_time (pf);
3087 void
3088 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3090 if (movement_occurred) {
3092 motion (event, false);
3094 double y1,y2;
3095 if (_drags->current_pointer_y() < grab_y()) {
3096 y1 = _drags->current_pointer_y();
3097 y2 = grab_y();
3098 } else {
3099 y2 = _drags->current_pointer_y();
3100 y1 = grab_y();
3104 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3106 _editor->begin_reversible_command (_("rubberband selection"));
3108 if (grab_frame() < last_pointer_frame()) {
3109 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3110 } else {
3111 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3114 _editor->commit_reversible_command ();
3116 } else {
3117 if (!getenv("ARDOUR_SAE")) {
3118 _editor->selection->clear_tracks();
3120 _editor->selection->clear_regions();
3121 _editor->selection->clear_points ();
3122 _editor->selection->clear_lines ();
3125 _editor->rubberband_rect->hide();
3128 void
3129 RubberbandSelectDrag::aborted (bool)
3131 _editor->rubberband_rect->hide ();
3134 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3135 : RegionDrag (e, i, p, v)
3137 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3140 void
3141 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3143 Drag::start_grab (event, cursor);
3145 show_verbose_cursor_time (adjusted_current_frame (event));
3148 void
3149 TimeFXDrag::motion (GdkEvent* event, bool)
3151 RegionView* rv = _primary;
3153 framepos_t const pf = adjusted_current_frame (event);
3155 if (pf > rv->region()->position()) {
3156 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3159 show_verbose_cursor_time (pf);
3162 void
3163 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3165 _primary->get_time_axis_view().hide_timestretch ();
3167 if (!movement_occurred) {
3168 return;
3171 if (last_pointer_frame() < _primary->region()->position()) {
3172 /* backwards drag of the left edge - not usable */
3173 return;
3176 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3178 float percentage = (double) newlen / (double) _primary->region()->length();
3180 #ifndef USE_RUBBERBAND
3181 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3182 if (_primary->region()->data_type() == DataType::AUDIO) {
3183 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3185 #endif
3187 _editor->begin_reversible_command (_("timestretch"));
3189 // XXX how do timeFX on multiple regions ?
3191 RegionSelection rs;
3192 rs.add (_primary);
3194 if (_editor->time_stretch (rs, percentage) == -1) {
3195 error << _("An error occurred while executing time stretch operation") << endmsg;
3199 void
3200 TimeFXDrag::aborted (bool)
3202 _primary->get_time_axis_view().hide_timestretch ();
3205 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3206 : Drag (e, i)
3208 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3211 void
3212 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3214 Drag::start_grab (event);
3217 void
3218 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3220 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3223 void
3224 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3226 if (movement_occurred && _editor->session()) {
3227 /* make sure we stop */
3228 _editor->session()->request_transport_speed (0.0);
3232 void
3233 ScrubDrag::aborted (bool)
3235 /* XXX: TODO */
3238 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3239 : Drag (e, i)
3240 , _operation (o)
3241 , _copy (false)
3242 , _original_pointer_time_axis (-1)
3243 , _last_pointer_time_axis (-1)
3245 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3248 void
3249 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3251 if (_editor->session() == 0) {
3252 return;
3255 Gdk::Cursor* cursor = 0;
3257 switch (_operation) {
3258 case CreateSelection:
3259 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3260 _copy = true;
3261 } else {
3262 _copy = false;
3264 cursor = _editor->cursors()->selector;
3265 Drag::start_grab (event, cursor);
3266 break;
3268 case SelectionStartTrim:
3269 if (_editor->clicked_axisview) {
3270 _editor->clicked_axisview->order_selection_trims (_item, true);
3272 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3273 break;
3275 case SelectionEndTrim:
3276 if (_editor->clicked_axisview) {
3277 _editor->clicked_axisview->order_selection_trims (_item, false);
3279 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3280 break;
3282 case SelectionMove:
3283 Drag::start_grab (event, cursor);
3284 break;
3287 if (_operation == SelectionMove) {
3288 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3289 } else {
3290 show_verbose_cursor_time (adjusted_current_frame (event));
3293 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3296 void
3297 SelectionDrag::setup_pointer_frame_offset ()
3299 switch (_operation) {
3300 case CreateSelection:
3301 _pointer_frame_offset = 0;
3302 break;
3304 case SelectionStartTrim:
3305 case SelectionMove:
3306 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3307 break;
3309 case SelectionEndTrim:
3310 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3311 break;
3315 void
3316 SelectionDrag::motion (GdkEvent* event, bool first_move)
3318 framepos_t start = 0;
3319 framepos_t end = 0;
3320 framecnt_t length;
3322 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3323 if (pending_time_axis.first == 0) {
3324 return;
3327 framepos_t const pending_position = adjusted_current_frame (event);
3329 /* only alter selection if things have changed */
3331 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3332 return;
3335 switch (_operation) {
3336 case CreateSelection:
3338 framepos_t grab = grab_frame ();
3340 if (first_move) {
3341 _editor->snap_to (grab);
3344 if (pending_position < grab_frame()) {
3345 start = pending_position;
3346 end = grab;
3347 } else {
3348 end = pending_position;
3349 start = grab;
3352 /* first drag: Either add to the selection
3353 or create a new selection
3356 if (first_move) {
3358 if (_copy) {
3359 /* adding to the selection */
3360 _editor->set_selected_track_as_side_effect (Selection::Add);
3361 //_editor->selection->add (_editor->clicked_axisview);
3362 _editor->clicked_selection = _editor->selection->add (start, end);
3363 _copy = false;
3364 } else {
3365 /* new selection */
3367 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3368 //_editor->selection->set (_editor->clicked_axisview);
3369 _editor->set_selected_track_as_side_effect (Selection::Set);
3372 _editor->clicked_selection = _editor->selection->set (start, end);
3376 /* select the track that we're in */
3377 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3378 // _editor->set_selected_track_as_side_effect (Selection::Add);
3379 _editor->selection->add (pending_time_axis.first);
3380 _added_time_axes.push_back (pending_time_axis.first);
3383 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3384 tracks that we selected in the first place.
3387 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3388 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3390 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3391 while (i != _added_time_axes.end()) {
3393 list<TimeAxisView*>::iterator tmp = i;
3394 ++tmp;
3396 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3397 _editor->selection->remove (*i);
3398 _added_time_axes.remove (*i);
3401 i = tmp;
3405 break;
3407 case SelectionStartTrim:
3409 start = _editor->selection->time[_editor->clicked_selection].start;
3410 end = _editor->selection->time[_editor->clicked_selection].end;
3412 if (pending_position > end) {
3413 start = end;
3414 } else {
3415 start = pending_position;
3417 break;
3419 case SelectionEndTrim:
3421 start = _editor->selection->time[_editor->clicked_selection].start;
3422 end = _editor->selection->time[_editor->clicked_selection].end;
3424 if (pending_position < start) {
3425 end = start;
3426 } else {
3427 end = pending_position;
3430 break;
3432 case SelectionMove:
3434 start = _editor->selection->time[_editor->clicked_selection].start;
3435 end = _editor->selection->time[_editor->clicked_selection].end;
3437 length = end - start;
3439 start = pending_position;
3440 _editor->snap_to (start);
3442 end = start + length;
3444 break;
3447 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3448 _editor->start_canvas_autoscroll (1, 0);
3451 if (start != end) {
3452 _editor->selection->replace (_editor->clicked_selection, start, end);
3455 if (_operation == SelectionMove) {
3456 show_verbose_cursor_time(start);
3457 } else {
3458 show_verbose_cursor_time(pending_position);
3462 void
3463 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3465 Session* s = _editor->session();
3467 if (movement_occurred) {
3468 motion (event, false);
3469 /* XXX this is not object-oriented programming at all. ick */
3470 if (_editor->selection->time.consolidate()) {
3471 _editor->selection->TimeChanged ();
3474 /* XXX what if its a music time selection? */
3475 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3476 s->request_play_range (&_editor->selection->time, true);
3480 } else {
3481 /* just a click, no pointer movement.*/
3483 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3484 _editor->selection->clear_time();
3487 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3488 _editor->selection->set (_editor->clicked_axisview);
3491 if (s && s->get_play_range () && s->transport_rolling()) {
3492 s->request_stop (false, false);
3497 _editor->stop_canvas_autoscroll ();
3500 void
3501 SelectionDrag::aborted (bool)
3503 /* XXX: TODO */
3506 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3507 : Drag (e, i),
3508 _operation (o),
3509 _copy (false)
3511 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3513 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3514 physical_screen_height (_editor->get_window()));
3515 _drag_rect->hide ();
3517 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3518 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3521 void
3522 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3524 if (_editor->session() == 0) {
3525 return;
3528 Gdk::Cursor* cursor = 0;
3530 if (!_editor->temp_location) {
3531 _editor->temp_location = new Location (*_editor->session());
3534 switch (_operation) {
3535 case CreateRangeMarker:
3536 case CreateTransportMarker:
3537 case CreateCDMarker:
3539 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3540 _copy = true;
3541 } else {
3542 _copy = false;
3544 cursor = _editor->cursors()->selector;
3545 break;
3548 Drag::start_grab (event, cursor);
3550 show_verbose_cursor_time (adjusted_current_frame (event));
3553 void
3554 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3556 framepos_t start = 0;
3557 framepos_t end = 0;
3558 ArdourCanvas::SimpleRect *crect;
3560 switch (_operation) {
3561 case CreateRangeMarker:
3562 crect = _editor->range_bar_drag_rect;
3563 break;
3564 case CreateTransportMarker:
3565 crect = _editor->transport_bar_drag_rect;
3566 break;
3567 case CreateCDMarker:
3568 crect = _editor->cd_marker_bar_drag_rect;
3569 break;
3570 default:
3571 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3572 return;
3573 break;
3576 framepos_t const pf = adjusted_current_frame (event);
3578 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3579 framepos_t grab = grab_frame ();
3580 _editor->snap_to (grab);
3582 if (pf < grab_frame()) {
3583 start = pf;
3584 end = grab;
3585 } else {
3586 end = pf;
3587 start = grab;
3590 /* first drag: Either add to the selection
3591 or create a new selection.
3594 if (first_move) {
3596 _editor->temp_location->set (start, end);
3598 crect->show ();
3600 update_item (_editor->temp_location);
3601 _drag_rect->show();
3602 //_drag_rect->raise_to_top();
3607 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3608 _editor->start_canvas_autoscroll (1, 0);
3611 if (start != end) {
3612 _editor->temp_location->set (start, end);
3614 double x1 = _editor->frame_to_pixel (start);
3615 double x2 = _editor->frame_to_pixel (end);
3616 crect->property_x1() = x1;
3617 crect->property_x2() = x2;
3619 update_item (_editor->temp_location);
3622 show_verbose_cursor_time (pf);
3626 void
3627 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3629 Location * newloc = 0;
3630 string rangename;
3631 int flags;
3633 if (movement_occurred) {
3634 motion (event, false);
3635 _drag_rect->hide();
3637 switch (_operation) {
3638 case CreateRangeMarker:
3639 case CreateCDMarker:
3641 _editor->begin_reversible_command (_("new range marker"));
3642 XMLNode &before = _editor->session()->locations()->get_state();
3643 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3644 if (_operation == CreateCDMarker) {
3645 flags = Location::IsRangeMarker | Location::IsCDMarker;
3646 _editor->cd_marker_bar_drag_rect->hide();
3648 else {
3649 flags = Location::IsRangeMarker;
3650 _editor->range_bar_drag_rect->hide();
3652 newloc = new Location (
3653 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3656 _editor->session()->locations()->add (newloc, true);
3657 XMLNode &after = _editor->session()->locations()->get_state();
3658 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3659 _editor->commit_reversible_command ();
3660 break;
3663 case CreateTransportMarker:
3664 // popup menu to pick loop or punch
3665 _editor->new_transport_marker_context_menu (&event->button, _item);
3666 break;
3668 } else {
3669 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3671 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3673 framepos_t start;
3674 framepos_t end;
3676 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3678 if (end == max_framepos) {
3679 end = _editor->session()->current_end_frame ();
3682 if (start == max_framepos) {
3683 start = _editor->session()->current_start_frame ();
3686 switch (_editor->mouse_mode) {
3687 case MouseObject:
3688 /* find the two markers on either side and then make the selection from it */
3689 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3690 break;
3692 case MouseRange:
3693 /* find the two markers on either side of the click and make the range out of it */
3694 _editor->selection->set (start, end);
3695 break;
3697 default:
3698 break;
3703 _editor->stop_canvas_autoscroll ();
3706 void
3707 RangeMarkerBarDrag::aborted (bool)
3709 /* XXX: TODO */
3712 void
3713 RangeMarkerBarDrag::update_item (Location* location)
3715 double const x1 = _editor->frame_to_pixel (location->start());
3716 double const x2 = _editor->frame_to_pixel (location->end());
3718 _drag_rect->property_x1() = x1;
3719 _drag_rect->property_x2() = x2;
3722 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3723 : Drag (e, i)
3724 , _zoom_out (false)
3726 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3729 void
3730 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3732 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3733 Drag::start_grab (event, _editor->cursors()->zoom_out);
3734 _zoom_out = true;
3735 } else {
3736 Drag::start_grab (event, _editor->cursors()->zoom_in);
3737 _zoom_out = false;
3740 show_verbose_cursor_time (adjusted_current_frame (event));
3743 void
3744 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3746 framepos_t start;
3747 framepos_t end;
3749 framepos_t const pf = adjusted_current_frame (event);
3751 framepos_t grab = grab_frame ();
3752 _editor->snap_to_with_modifier (grab, event);
3754 /* base start and end on initial click position */
3755 if (pf < grab) {
3756 start = pf;
3757 end = grab;
3758 } else {
3759 end = pf;
3760 start = grab;
3763 if (start != end) {
3765 if (first_move) {
3766 _editor->zoom_rect->show();
3767 _editor->zoom_rect->raise_to_top();
3770 _editor->reposition_zoom_rect(start, end);
3772 show_verbose_cursor_time (pf);
3776 void
3777 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3779 if (movement_occurred) {
3780 motion (event, false);
3782 if (grab_frame() < last_pointer_frame()) {
3783 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3784 } else {
3785 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3787 } else {
3788 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3789 _editor->tav_zoom_step (_zoom_out);
3790 } else {
3791 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3795 _editor->zoom_rect->hide();
3798 void
3799 MouseZoomDrag::aborted (bool)
3801 _editor->zoom_rect->hide ();
3804 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3805 : Drag (e, i)
3806 , _cumulative_dx (0)
3807 , _cumulative_dy (0)
3809 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3811 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3812 _region = &_primary->region_view ();
3813 _note_height = _region->midi_stream_view()->note_height ();
3816 void
3817 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3819 Drag::start_grab (event);
3821 if (!(_was_selected = _primary->selected())) {
3823 /* tertiary-click means extend selection - we'll do that on button release,
3824 so don't add it here, because otherwise we make it hard to figure
3825 out the "extend-to" range.
3828 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3830 if (!extend) {
3831 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3833 if (add) {
3834 _region->note_selected (_primary, true);
3835 } else {
3836 _region->unique_select (_primary);
3842 /** @return Current total drag x change in frames */
3843 frameoffset_t
3844 NoteDrag::total_dx () const
3846 /* dx in frames */
3847 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3849 /* primary note time */
3850 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3852 /* new time of the primary note relative to the region position */
3853 frameoffset_t st = n + dx;
3855 /* prevent the note being dragged earlier than the region's position */
3856 if (st < 0) {
3857 st = 0;
3860 /* snap and return corresponding delta */
3861 return _region->snap_frame_to_frame (st) - n;
3864 /** @return Current total drag y change in notes */
3865 int8_t
3866 NoteDrag::total_dy () const
3868 /* this is `backwards' to make increasing note number go in the right direction */
3869 double const dy = _drags->current_pointer_y() - grab_y();
3871 /* dy in notes */
3872 int8_t ndy = 0;
3874 if (abs (dy) >= _note_height) {
3875 if (dy > 0) {
3876 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3877 } else {
3878 ndy = (int8_t) floor (dy / _note_height / 2.0);
3882 /* more positive value = higher pitch and higher y-axis position on track,
3883 which is the inverse of the X-centric geometric universe
3886 return -ndy;
3889 void
3890 NoteDrag::motion (GdkEvent *, bool)
3892 /* Total change in x and y since the start of the drag */
3893 frameoffset_t const dx = total_dx ();
3894 int8_t const dy = -total_dy ();
3896 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3897 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3898 double const tdy = dy * _note_height - _cumulative_dy;
3900 if (tdx || tdy) {
3901 _cumulative_dx += tdx;
3902 _cumulative_dy += tdy;
3904 int8_t note_delta = total_dy();
3906 _region->move_selection (tdx, tdy, note_delta);
3908 char buf[12];
3909 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3910 (int) floor (_primary->note()->note() + note_delta));
3912 show_verbose_cursor_text (buf);
3916 void
3917 NoteDrag::finished (GdkEvent* ev, bool moved)
3919 if (!moved) {
3920 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3922 if (_was_selected) {
3923 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3924 if (add) {
3925 _region->note_deselected (_primary);
3927 } else {
3928 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3929 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3931 if (!extend && !add && _region->selection_size() > 1) {
3932 _region->unique_select (_primary);
3933 } else if (extend) {
3934 _region->note_selected (_primary, true, true);
3935 } else {
3936 /* it was added during button press */
3940 } else {
3941 _region->note_dropped (_primary, total_dx(), total_dy());
3945 void
3946 NoteDrag::aborted (bool)
3948 /* XXX: TODO */
3951 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3952 : Drag (editor, item)
3953 , _ranges (r)
3954 , _nothing_to_drag (false)
3956 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3958 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3959 assert (_atav);
3961 /* get all lines in the automation view */
3962 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3964 /* find those that overlap the ranges being dragged */
3965 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3966 while (i != lines.end ()) {
3967 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3968 ++j;
3970 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3972 /* check this range against all the AudioRanges that we are using */
3973 list<AudioRange>::const_iterator k = _ranges.begin ();
3974 while (k != _ranges.end()) {
3975 if (k->coverage (r.first, r.second) != OverlapNone) {
3976 break;
3978 ++k;
3981 /* add it to our list if it overlaps at all */
3982 if (k != _ranges.end()) {
3983 Line n;
3984 n.line = *i;
3985 n.state = 0;
3986 n.range = r;
3987 _lines.push_back (n);
3990 i = j;
3993 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3996 void
3997 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3999 Drag::start_grab (event, cursor);
4001 /* Get line states before we start changing things */
4002 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4003 i->state = &i->line->get_state ();
4006 if (_ranges.empty()) {
4008 /* No selected time ranges: drag all points */
4009 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4010 uint32_t const N = i->line->npoints ();
4011 for (uint32_t j = 0; j < N; ++j) {
4012 i->points.push_back (i->line->nth (j));
4016 } else {
4018 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4020 framecnt_t const half = (i->start + i->end) / 2;
4022 /* find the line that this audio range starts in */
4023 list<Line>::iterator j = _lines.begin();
4024 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4025 ++j;
4028 if (j != _lines.end()) {
4029 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4031 /* j is the line that this audio range starts in; fade into it;
4032 64 samples length plucked out of thin air.
4035 framepos_t a = i->start + 64;
4036 if (a > half) {
4037 a = half;
4040 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4041 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4043 the_list->add (p, the_list->eval (p));
4044 j->line->add_always_in_view (p);
4045 the_list->add (q, the_list->eval (q));
4046 j->line->add_always_in_view (q);
4049 /* same thing for the end */
4051 j = _lines.begin();
4052 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4053 ++j;
4056 if (j != _lines.end()) {
4057 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4059 /* j is the line that this audio range starts in; fade out of it;
4060 64 samples length plucked out of thin air.
4063 framepos_t b = i->end - 64;
4064 if (b < half) {
4065 b = half;
4068 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4069 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4071 the_list->add (p, the_list->eval (p));
4072 j->line->add_always_in_view (p);
4073 the_list->add (q, the_list->eval (q));
4074 j->line->add_always_in_view (q);
4078 _nothing_to_drag = true;
4080 /* Find all the points that should be dragged and put them in the relevant
4081 points lists in the Line structs.
4084 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4086 uint32_t const N = i->line->npoints ();
4087 for (uint32_t j = 0; j < N; ++j) {
4089 /* here's a control point on this line */
4090 ControlPoint* p = i->line->nth (j);
4091 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4093 /* see if it's inside a range */
4094 list<AudioRange>::const_iterator k = _ranges.begin ();
4095 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4096 ++k;
4099 if (k != _ranges.end()) {
4100 /* dragging this point */
4101 _nothing_to_drag = false;
4102 i->points.push_back (p);
4108 if (_nothing_to_drag) {
4109 return;
4112 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4113 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4117 void
4118 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4120 if (_nothing_to_drag) {
4121 return;
4124 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4125 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4127 /* we are ignoring x position for this drag, so we can just pass in anything */
4128 i->line->drag_motion (0, f, true, false);
4132 void
4133 AutomationRangeDrag::finished (GdkEvent* event, bool)
4135 if (_nothing_to_drag) {
4136 return;
4139 motion (event, false);
4140 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4141 i->line->end_drag ();
4142 i->line->clear_always_in_view ();
4145 _editor->session()->commit_reversible_command ();
4148 void
4149 AutomationRangeDrag::aborted (bool)
4151 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4152 i->line->clear_always_in_view ();
4153 i->line->reset ();
4157 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4158 : view (v)
4160 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4161 layer = v->region()->layer ();
4162 initial_y = v->get_canvas_group()->property_y ();
4163 initial_playlist = v->region()->playlist ();
4164 initial_position = v->region()->position ();
4165 initial_end = v->region()->position () + v->region()->length ();
4168 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4169 : Drag (e, i)
4170 , _region_view (r)
4171 , _patch_change (i)
4172 , _cumulative_dx (0)
4174 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4177 void
4178 PatchChangeDrag::motion (GdkEvent* ev, bool)
4180 framepos_t f = adjusted_current_frame (ev);
4181 boost::shared_ptr<Region> r = _region_view->region ();
4182 f = max (f, r->position ());
4183 f = min (f, r->last_frame ());
4185 framecnt_t const dxf = f - grab_frame();
4186 double const dxu = _editor->frame_to_unit (dxf);
4187 _patch_change->move (dxu - _cumulative_dx, 0);
4188 _cumulative_dx = dxu;
4191 void
4192 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4194 if (!movement_occurred) {
4195 return;
4198 boost::shared_ptr<Region> r (_region_view->region ());
4200 framepos_t f = adjusted_current_frame (ev);
4201 f = max (f, r->position ());
4202 f = min (f, r->last_frame ());
4204 _region_view->move_patch_change (
4205 *_patch_change,
4206 _region_view->frames_to_beats (f - r->position() - r->start())
4210 void
4211 PatchChangeDrag::aborted (bool)
4213 _patch_change->move (-_cumulative_dx, 0);
4216 void
4217 PatchChangeDrag::setup_pointer_frame_offset ()
4219 boost::shared_ptr<Region> region = _region_view->region ();
4220 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();