add left/right side trim cursors and use them for region trimming, as appropriate
[ardour2.git] / gtk2_ardour / editor_drag.cc
blobd978029d2d9f7ce36cfe85406afbc80c1c167e9a
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 #define __STDC_LIMIT_MACROS 1
21 #include <stdint.h>
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
28 #include "editor.h"
29 #include "i18n.h"
30 #include "keyboard.h"
31 #include "audio_region_view.h"
32 #include "midi_region_view.h"
33 #include "ardour_ui.h"
34 #include "gui_thread.h"
35 #include "control_point.h"
36 #include "utils.h"
37 #include "region_gain_line.h"
38 #include "editor_drag.h"
39 #include "audio_time_axis.h"
40 #include "midi_time_axis.h"
41 #include "canvas-note.h"
42 #include "selection.h"
43 #include "midi_selection.h"
44 #include "automation_time_axis.h"
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
49 using namespace Gtk;
50 using namespace Editing;
51 using namespace ArdourCanvas;
53 using Gtkmm2ext::Keyboard;
55 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
57 DragManager::DragManager (Editor* e)
58 : _editor (e)
59 , _ending (false)
60 , _current_pointer_frame (0)
65 DragManager::~DragManager ()
67 abort ();
70 /** Call abort for each active drag */
71 void
72 DragManager::abort ()
74 _ending = true;
76 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
77 (*i)->abort ();
78 delete *i;
81 _drags.clear ();
83 _ending = false;
86 void
87 DragManager::add (Drag* d)
89 d->set_manager (this);
90 _drags.push_back (d);
93 void
94 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
96 assert (_drags.empty ());
97 d->set_manager (this);
98 _drags.push_back (d);
99 start_grab (e);
102 void
103 DragManager::start_grab (GdkEvent* e)
105 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
107 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 (*i)->start_grab (e);
112 /** Call end_grab for each active drag.
113 * @return true if any drag reported movement having occurred.
115 bool
116 DragManager::end_grab (GdkEvent* e)
118 _ending = true;
120 bool r = false;
121 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
122 bool const t = (*i)->end_grab (e);
123 if (t) {
124 r = true;
126 delete *i;
129 _drags.clear ();
131 _ending = false;
133 return r;
136 bool
137 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
139 bool r = false;
141 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->motion_handler (e, from_autoscroll);
145 if (t) {
146 r = true;
151 return r;
154 bool
155 DragManager::have_item (ArdourCanvas::Item* i) const
157 list<Drag*>::const_iterator j = _drags.begin ();
158 while (j != _drags.end() && (*j)->item () != i) {
159 ++j;
162 return j != _drags.end ();
165 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
166 : _editor (e)
167 , _item (i)
168 , _pointer_frame_offset (0)
169 , _move_threshold_passed (false)
170 , _grab_frame (0)
171 , _last_pointer_frame (0)
176 void
177 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
179 _item->ungrab (0);
180 _item = new_item;
182 if (cursor == 0) {
183 cursor = _editor->which_grabber_cursor ();
186 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
189 void
190 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
192 if (cursor == 0) {
193 cursor = _editor->which_grabber_cursor ();
196 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
198 if (Keyboard::is_button2_event (&event->button)) {
199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
200 _y_constrained = true;
201 _x_constrained = false;
202 } else {
203 _y_constrained = false;
204 _x_constrained = true;
206 } else {
207 _x_constrained = false;
208 _y_constrained = false;
211 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
212 _grab_frame = adjusted_frame (_grab_frame, event);
213 _last_pointer_frame = _grab_frame;
214 _last_pointer_x = _grab_x;
215 _last_pointer_y = _grab_y;
217 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
218 *cursor,
219 event->button.time);
221 if (_editor->session() && _editor->session()->transport_rolling()) {
222 _was_rolling = true;
223 } else {
224 _was_rolling = false;
227 switch (_editor->snap_type()) {
228 case SnapToRegionStart:
229 case SnapToRegionEnd:
230 case SnapToRegionSync:
231 case SnapToRegionBoundary:
232 _editor->build_region_boundary_cache ();
233 break;
234 default:
235 break;
239 /** Call to end a drag `successfully'. Ungrabs item and calls
240 * subclass' finished() method.
242 * @param event GDK event, or 0.
243 * @return true if some movement occurred, otherwise false.
245 bool
246 Drag::end_grab (GdkEvent* event)
248 _editor->stop_canvas_autoscroll ();
250 _item->ungrab (event ? event->button.time : 0);
252 finished (event, _move_threshold_passed);
254 _editor->hide_verbose_canvas_cursor();
256 return _move_threshold_passed;
259 nframes64_t
260 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
262 nframes64_t pos = 0;
264 if (f > _pointer_frame_offset) {
265 pos = f - _pointer_frame_offset;
268 if (snap) {
269 _editor->snap_to_with_modifier (pos, event);
272 return pos;
275 nframes64_t
276 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
278 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
281 bool
282 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
284 /* check to see if we have moved in any way that matters since the last motion event */
285 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
286 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
287 return false;
290 pair<nframes64_t, int> const threshold = move_threshold ();
292 bool const old_move_threshold_passed = _move_threshold_passed;
294 if (!from_autoscroll && !_move_threshold_passed) {
296 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
297 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
299 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
302 if (active (_editor->mouse_mode) && _move_threshold_passed) {
304 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
305 if (!from_autoscroll) {
306 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
309 motion (event, _move_threshold_passed != old_move_threshold_passed);
311 _last_pointer_x = _drags->current_pointer_x ();
312 _last_pointer_y = _drags->current_pointer_y ();
313 _last_pointer_frame = adjusted_current_frame (event);
315 return true;
318 return false;
321 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
322 void
323 Drag::abort ()
325 if (_item) {
326 _item->ungrab (0);
329 aborted ();
331 _editor->stop_canvas_autoscroll ();
332 _editor->hide_verbose_canvas_cursor ();
335 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
336 : Drag (e, i),
337 _primary (p)
339 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
340 _views.push_back (DraggingView (*i));
343 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
346 void
347 RegionDrag::region_going_away (RegionView* v)
349 list<DraggingView>::iterator i = _views.begin ();
350 while (i != _views.end() && i->view != v) {
351 ++i;
354 if (i != _views.end()) {
355 _views.erase (i);
359 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
360 : RegionDrag (e, i, p, v),
361 _dest_trackview (0),
362 _dest_layer (0),
363 _brushing (b),
364 _total_x_delta (0)
370 void
371 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
373 Drag::start_grab (event);
375 _editor->show_verbose_time_cursor (_last_frame_position, 10);
378 RegionMotionDrag::TimeAxisViewSummary
379 RegionMotionDrag::get_time_axis_view_summary ()
381 int32_t children = 0;
382 TimeAxisViewSummary sum;
384 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
386 /* get a bitmask representing the visible tracks */
388 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
389 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
390 TimeAxisView::Children children_list;
392 /* zeroes are audio/MIDI tracks. ones are other types. */
394 if (!rtv->hidden()) {
396 if (!rtv->is_track()) {
397 /* not an audio nor MIDI track */
398 sum.tracks = sum.tracks |= (0x01 << rtv->order());
401 sum.height_list[rtv->order()] = (*i)->current_height();
402 children = 1;
404 if ((children_list = rtv->get_child_list()).size() > 0) {
405 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
406 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
407 sum.height_list[rtv->order() + children] = (*j)->current_height();
408 children++;
414 return sum;
417 bool
418 RegionMotionDrag::compute_y_delta (
419 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
420 int32_t last_pointer_layer, int32_t current_pointer_layer,
421 TimeAxisViewSummary const & tavs,
422 int32_t* pointer_order_span, int32_t* pointer_layer_span,
423 int32_t* canvas_pointer_order_span
426 if (_brushing) {
427 *pointer_order_span = 0;
428 *pointer_layer_span = 0;
429 return true;
432 bool clamp_y_axis = false;
434 /* the change in track order between this callback and the last */
435 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
436 /* the change in layer between this callback and the last;
437 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
438 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
440 if (*pointer_order_span != 0) {
442 /* find the actual pointer span, in terms of the number of visible tracks;
443 to do this, we reduce |pointer_order_span| by the number of hidden tracks
444 over the span */
446 *canvas_pointer_order_span = *pointer_order_span;
447 if (last_pointer_view->order() >= current_pointer_view->order()) {
448 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
449 if (tavs.height_list[y] == 0) {
450 *canvas_pointer_order_span--;
453 } else {
454 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
455 if (tavs.height_list[y] == 0) {
456 *canvas_pointer_order_span++;
461 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
463 RegionView* rv = i->view;
465 if (rv->region()->locked()) {
466 continue;
469 double ix1, ix2, iy1, iy2;
470 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
471 rv->get_canvas_frame()->i2w (ix1, iy1);
472 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
474 /* get the new trackview for this particular region */
475 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
476 assert (tvp.first);
477 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
479 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
480 as surely this is a per-region thing... */
482 clamp_y_axis = y_movement_disallowed (
483 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
486 if (clamp_y_axis) {
487 break;
491 } else if (_dest_trackview == current_pointer_view) {
493 if (current_pointer_layer == last_pointer_layer) {
494 /* No movement; clamp */
495 clamp_y_axis = true;
499 if (!clamp_y_axis) {
500 _dest_trackview = current_pointer_view;
501 _dest_layer = current_pointer_layer;
504 return clamp_y_axis;
508 double
509 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
511 /* compute the amount of pointer motion in frames, and where
512 the region would be if we moved it by that much.
514 *pending_region_position = adjusted_current_frame (event);
516 nframes64_t sync_frame;
517 nframes64_t sync_offset;
518 int32_t sync_dir;
520 sync_offset = _primary->region()->sync_offset (sync_dir);
522 /* we don't handle a sync point that lies before zero.
524 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
526 sync_frame = *pending_region_position + (sync_dir*sync_offset);
528 _editor->snap_to_with_modifier (sync_frame, event);
530 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
532 } else {
533 *pending_region_position = _last_frame_position;
536 if (*pending_region_position > max_frames - _primary->region()->length()) {
537 *pending_region_position = _last_frame_position;
540 double x_delta = 0;
542 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
544 /* now compute the canvas unit distance we need to move the regionview
545 to make it appear at the new location.
548 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
550 if (*pending_region_position <= _last_frame_position) {
552 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
554 RegionView* rv = i->view;
556 // If any regionview is at zero, we need to know so we can stop further leftward motion.
558 double ix1, ix2, iy1, iy2;
559 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
560 rv->get_canvas_frame()->i2w (ix1, iy1);
562 if (-x_delta > ix1 + _editor->horizontal_position()) {
563 x_delta = 0;
564 *pending_region_position = _last_frame_position;
565 break;
571 _last_frame_position = *pending_region_position;
574 return x_delta;
577 void
578 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
580 double y_delta = 0;
582 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
584 vector<int32_t>::iterator j;
586 /* *pointer* variables reflect things about the pointer; as we may be moving
587 multiple regions, much detail must be computed per-region */
589 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
590 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
591 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
592 is always 0 regardless of what the region's "real" layer is */
593 RouteTimeAxisView* current_pointer_view;
594 layer_t current_pointer_layer;
595 if (!check_possible (&current_pointer_view, &current_pointer_layer)) {
596 return;
599 /* TimeAxisView that we were pointing at last time we entered this method */
600 TimeAxisView const * const last_pointer_view = _dest_trackview;
601 /* the order of the track that we were pointing at last time we entered this method */
602 int32_t const last_pointer_order = last_pointer_view->order ();
603 /* the layer that we were pointing at last time we entered this method */
604 layer_t const last_pointer_layer = _dest_layer;
606 int32_t pointer_order_span;
607 int32_t pointer_layer_span;
608 int32_t canvas_pointer_order_span;
610 bool const clamp_y_axis = compute_y_delta (
611 last_pointer_view, current_pointer_view,
612 last_pointer_layer, current_pointer_layer, tavs,
613 &pointer_order_span, &pointer_layer_span,
614 &canvas_pointer_order_span
617 nframes64_t pending_region_position;
618 double const x_delta = compute_x_delta (event, &pending_region_position);
620 /*************************************************************
621 PREPARE TO MOVE
622 ************************************************************/
624 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
625 /* haven't reached next snap point, and we're not switching
626 trackviews nor layers. nothing to do.
628 return;
631 /*************************************************************
632 MOTION
633 ************************************************************/
635 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
637 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
639 RegionView* rv = i->view;
641 if (rv->region()->locked()) {
642 continue;
645 /* here we are calculating the y distance from the
646 top of the first track view to the top of the region
647 area of the track view that we're working on */
649 /* this x value is just a dummy value so that we have something
650 to pass to i2w () */
652 double ix1 = 0;
654 /* distance from the top of this track view to the region area
655 of our track view is always 1 */
657 double iy1 = 1;
659 /* convert to world coordinates, ie distance from the top of
660 the ruler section */
662 rv->get_canvas_frame()->i2w (ix1, iy1);
664 /* compensate for the ruler section and the vertical scrollbar position */
665 iy1 += _editor->get_trackview_group_vertical_offset ();
667 if (first_move) {
669 // hide any dependent views
671 rv->get_time_axis_view().hide_dependent_views (*rv);
674 reparent to a non scrolling group so that we can keep the
675 region selection above all time axis views.
676 reparenting means we have to move the rv as the two
677 parent groups have different coordinates.
680 rv->get_canvas_group()->property_y() = iy1 - 1;
681 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
683 rv->fake_set_opaque (true);
686 /* current view for this particular region */
687 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
688 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
690 if (pointer_order_span != 0 && !clamp_y_axis) {
692 /* INTER-TRACK MOVEMENT */
694 /* move through the height list to the track that the region is currently on */
695 vector<int32_t>::iterator j = tavs.height_list.begin ();
696 int32_t x = 0;
697 while (j != tavs.height_list.end () && x != rtv->order ()) {
698 ++x;
699 ++j;
702 y_delta = 0;
703 int32_t temp_pointer_order_span = canvas_pointer_order_span;
705 if (j != tavs.height_list.end ()) {
707 /* Account for layers in the original and
708 destination tracks. If we're moving around in layers we assume
709 that only one track is involved, so it's ok to use *pointer*
710 variables here. */
712 StreamView* lv = last_pointer_view->view ();
713 assert (lv);
715 /* move to the top of the last trackview */
716 if (lv->layer_display () == Stacked) {
717 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
720 StreamView* cv = current_pointer_view->view ();
721 assert (cv);
723 /* move to the right layer on the current trackview */
724 if (cv->layer_display () == Stacked) {
725 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
728 /* And for being on a non-topmost layer on the new
729 track */
731 while (temp_pointer_order_span > 0) {
732 /* we're moving up canvas-wise,
733 so we need to find the next track height
735 if (j != tavs.height_list.begin()) {
736 j--;
739 if (x != last_pointer_order) {
740 if ((*j) == 0) {
741 ++temp_pointer_order_span;
745 y_delta -= (*j);
746 temp_pointer_order_span--;
749 while (temp_pointer_order_span < 0) {
751 y_delta += (*j);
753 if (x != last_pointer_order) {
754 if ((*j) == 0) {
755 --temp_pointer_order_span;
759 if (j != tavs.height_list.end()) {
760 j++;
763 temp_pointer_order_span++;
767 /* find out where we'll be when we move and set height accordingly */
769 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
770 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
771 rv->set_height (temp_rtv->view()->child_height());
773 /* if you un-comment the following, the region colours will follow
774 the track colours whilst dragging; personally
775 i think this can confuse things, but never mind.
778 //const GdkColor& col (temp_rtv->view->get_region_color());
779 //rv->set_color (const_cast<GdkColor&>(col));
783 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
785 /* INTER-LAYER MOVEMENT in the same track */
786 y_delta = rtv->view()->child_height () * pointer_layer_span;
790 if (_brushing) {
791 _editor->mouse_brush_insert_region (rv, pending_region_position);
792 } else {
793 rv->move (x_delta, y_delta);
796 } /* foreach region */
798 _total_x_delta += x_delta;
800 if (first_move) {
801 _editor->cursor_group->raise_to_top();
804 if (x_delta != 0 && !_brushing) {
805 _editor->show_verbose_time_cursor (_last_frame_position, 10);
809 void
810 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
812 if (_copy && first_move) {
813 copy_regions (event);
816 RegionMotionDrag::motion (event, first_move);
819 void
820 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
822 vector<RegionView*> copies;
823 boost::shared_ptr<Track> tr;
824 boost::shared_ptr<Playlist> from_playlist;
825 boost::shared_ptr<Playlist> to_playlist;
826 RegionSelection new_views;
827 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
828 PlaylistSet modified_playlists;
829 PlaylistSet frozen_playlists;
830 list <sigc::connection> modified_playlist_connections;
831 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
832 nframes64_t drag_delta;
833 bool changed_tracks, changed_position;
834 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
835 RouteTimeAxisView* source_tv;
836 vector<StatefulDiffCommand*> sdc;
838 if (!movement_occurred) {
839 /* just a click */
840 return;
843 if (_brushing) {
844 /* all changes were made during motion event handlers */
846 if (_copy) {
847 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
848 copies.push_back (i->view);
852 goto out;
855 /* reverse this here so that we have the correct logic to finalize
856 the drag.
859 if (Config->get_edit_mode() == Lock) {
860 _x_constrained = !_x_constrained;
863 if (_copy) {
864 if (_x_constrained) {
865 _editor->begin_reversible_command (_("fixed time region copy"));
866 } else {
867 _editor->begin_reversible_command (_("region copy"));
869 } else {
870 if (_x_constrained) {
871 _editor->begin_reversible_command (_("fixed time region drag"));
872 } else {
873 _editor->begin_reversible_command (_("region drag"));
877 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
878 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
880 drag_delta = _primary->region()->position() - _last_frame_position;
882 _editor->update_canvas_now ();
884 /* make a list of where each region ended up */
885 final = find_time_axis_views_and_layers ();
887 cerr << "Iterate over " << _views.size() << " views\n";
889 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
891 RegionView* rv = i->view;
892 RouteTimeAxisView* dest_rtv = final[rv].first;
893 layer_t dest_layer = final[rv].second;
895 nframes64_t where;
897 from_playlist.reset ();
898 to_playlist.reset ();
900 if (rv->region()->locked()) {
901 ++i;
902 continue;
905 if (changed_position && !_x_constrained) {
906 where = rv->region()->position() - drag_delta;
907 } else {
908 where = rv->region()->position();
911 boost::shared_ptr<Region> new_region;
913 if (_copy) {
914 /* we already made a copy */
915 new_region = rv->region();
917 /* undo the previous hide_dependent_views so that xfades don't
918 disappear on copying regions
921 //rv->get_time_axis_view().reveal_dependent_views (*rv);
923 } else if (changed_tracks && dest_rtv->playlist()) {
924 new_region = RegionFactory::create (rv->region());
927 if (changed_tracks || _copy) {
929 to_playlist = dest_rtv->playlist();
931 if (!to_playlist) {
932 ++i;
933 continue;
936 _editor->latest_regionviews.clear ();
938 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
940 insert_result = modified_playlists.insert (to_playlist);
942 if (insert_result.second) {
943 to_playlist->clear_history ();
946 cerr << "To playlist " << to_playlist->name() << " region history contains "
947 << to_playlist->region_list().change().added.size() << " adds and "
948 << to_playlist->region_list().change().removed.size() << " removes\n";
950 cerr << "Adding new region " << new_region->id() << " based on "
951 << rv->region()->id() << endl;
953 to_playlist->add_region (new_region, where);
955 if (dest_rtv->view()->layer_display() == Stacked) {
956 new_region->set_layer (dest_layer);
957 new_region->set_pending_explicit_relayer (true);
960 c.disconnect ();
962 if (!_editor->latest_regionviews.empty()) {
963 // XXX why just the first one ? we only expect one
964 // commented out in nick_m's canvas reworking. is that intended?
965 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
966 new_views.push_back (_editor->latest_regionviews.front());
969 } else {
970 rv->region()->clear_history ();
973 motion on the same track. plonk the previously reparented region
974 back to its original canvas group (its streamview).
975 No need to do anything for copies as they are fake regions which will be deleted.
978 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
979 rv->get_canvas_group()->property_y() = i->initial_y;
980 rv->get_time_axis_view().reveal_dependent_views (*rv);
982 /* just change the model */
984 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
986 if (dest_rtv->view()->layer_display() == Stacked) {
987 rv->region()->set_layer (dest_layer);
988 rv->region()->set_pending_explicit_relayer (true);
991 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
993 frozen_insert_result = frozen_playlists.insert(playlist);
995 if (frozen_insert_result.second) {
996 playlist->freeze();
999 cerr << "Moving region " << rv->region()->id() << endl;
1001 rv->region()->set_position (where, (void*) this);
1003 sdc.push_back (new StatefulDiffCommand (rv->region()));
1006 if (changed_tracks && !_copy) {
1008 /* get the playlist where this drag started. we can't use rv->region()->playlist()
1009 because we may have copied the region and it has not been attached to a playlist.
1012 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
1013 tr = source_tv->track();
1014 from_playlist = tr->playlist();
1016 assert (source_tv);
1017 assert (tr);
1018 assert (from_playlist);
1020 /* moved to a different audio track, without copying */
1022 /* the region that used to be in the old playlist is not
1023 moved to the new one - we use a copy of it. as a result,
1024 any existing editor for the region should no longer be
1025 visible.
1028 rv->hide_region_editor();
1029 rv->fake_set_opaque (false);
1031 /* remove the region from the old playlist */
1033 insert_result = modified_playlists.insert (from_playlist);
1035 if (insert_result.second) {
1036 from_playlist->clear_history ();
1039 cerr << "From playlist " << from_playlist->name() << " region history contains "
1040 << from_playlist->region_list().change().added.size() << " adds and "
1041 << from_playlist->region_list().change().removed.size() << " removes\n";
1043 cerr << "removing region " << rv->region() << endl;
1045 from_playlist->remove_region (rv->region());
1047 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1048 was selected in all of them, then removing it from a playlist will have removed all
1049 trace of it from the selection (i.e. there were N regions selected, we removed 1,
1050 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1051 corresponding regionview, and the selection is now empty).
1053 this could have invalidated any and all iterators into the region selection.
1055 the heuristic we use here is: if the region selection is empty, break out of the loop
1056 here. if the region selection is not empty, then restart the loop because we know that
1057 we must have removed at least the region(view) we've just been working on as well as any
1058 that we processed on previous iterations.
1060 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
1061 we can just iterate.
1064 if (_views.empty()) {
1065 if (to_playlist) {
1066 sdc.push_back (new StatefulDiffCommand (to_playlist));
1067 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1070 if (from_playlist && (from_playlist != to_playlist)) {
1071 sdc.push_back (new StatefulDiffCommand (from_playlist));
1072 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1074 break;
1075 } else {
1076 i = _views.begin();
1079 } else {
1080 ++i;
1083 if (_copy) {
1084 copies.push_back (rv);
1087 cerr << "Done with TV, top = " << to_playlist << " from = " << from_playlist << endl;
1089 if (to_playlist) {
1090 sdc.push_back (new StatefulDiffCommand (to_playlist));
1091 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1094 if (from_playlist && (from_playlist != to_playlist)) {
1095 sdc.push_back (new StatefulDiffCommand (from_playlist));
1096 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1101 if we've created new regions either by copying or moving
1102 to a new track, we want to replace the old selection with the new ones
1105 if (new_views.size() > 0) {
1106 _editor->selection->set (new_views);
1109 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1110 (*p)->thaw();
1113 out:
1114 for (vector<StatefulDiffCommand*>::iterator i = sdc.begin(); i != sdc.end(); ++i) {
1115 _editor->session()->add_command (*i);
1118 _editor->commit_reversible_command ();
1120 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1121 delete *x;
1125 void
1126 RegionMoveDrag::aborted ()
1128 if (_copy) {
1130 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1131 delete i->view;
1134 _views.clear ();
1136 } else {
1137 RegionMotionDrag::aborted ();
1141 void
1142 RegionMotionDrag::aborted ()
1144 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1145 RegionView* rv = i->view;
1146 TimeAxisView* tv = &(rv->get_time_axis_view ());
1147 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1148 assert (rtv);
1149 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1150 rv->get_canvas_group()->property_y() = 0;
1151 rv->get_time_axis_view().reveal_dependent_views (*rv);
1152 rv->fake_set_opaque (false);
1153 rv->move (-_total_x_delta, 0);
1154 rv->set_height (rtv->view()->child_height ());
1157 _editor->update_canvas_now ();
1161 bool
1162 RegionMotionDrag::x_move_allowed () const
1164 if (Config->get_edit_mode() == Lock) {
1165 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1166 return _x_constrained;
1169 return !_x_constrained;
1172 void
1173 RegionMotionDrag::copy_regions (GdkEvent* event)
1175 /* duplicate the regionview(s) and region(s) */
1177 list<DraggingView> new_regionviews;
1179 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1181 RegionView* rv = i->view;
1182 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1183 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1185 const boost::shared_ptr<const Region> original = rv->region();
1186 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1187 region_copy->set_position (original->position(), this);
1189 RegionView* nrv;
1190 if (arv) {
1191 boost::shared_ptr<AudioRegion> audioregion_copy
1192 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1194 nrv = new AudioRegionView (*arv, audioregion_copy);
1195 } else if (mrv) {
1196 boost::shared_ptr<MidiRegion> midiregion_copy
1197 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1198 nrv = new MidiRegionView (*mrv, midiregion_copy);
1199 } else {
1200 continue;
1203 nrv->get_canvas_group()->show ();
1204 new_regionviews.push_back (DraggingView (nrv));
1206 /* swap _primary to the copy */
1208 if (rv == _primary) {
1209 _primary = nrv;
1212 /* ..and deselect the one we copied */
1214 rv->set_selected (false);
1217 if (new_regionviews.empty()) {
1218 return;
1221 /* reflect the fact that we are dragging the copies */
1223 _views = new_regionviews;
1225 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1228 sync the canvas to what we think is its current state
1229 without it, the canvas seems to
1230 "forget" to update properly after the upcoming reparent()
1231 ..only if the mouse is in rapid motion at the time of the grab.
1232 something to do with regionview creation taking so long?
1234 _editor->update_canvas_now();
1237 bool
1238 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1240 /* Which trackview is this ? */
1242 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1243 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1244 (*layer) = tvp.second;
1246 if (*tv && (*tv)->layer_display() == Overlaid) {
1247 *layer = 0;
1250 /* The region motion is only processed if the pointer is over
1251 an audio track.
1254 if (!(*tv) || !(*tv)->is_track()) {
1255 /* To make sure we hide the verbose canvas cursor when the mouse is
1256 not held over and audiotrack.
1258 _editor->hide_verbose_canvas_cursor ();
1259 return false;
1262 return true;
1265 /** @param new_order New track order.
1266 * @param old_order Old track order.
1267 * @param visible_y_low Lowest visible order.
1268 * @return true if y movement should not happen, otherwise false.
1270 bool
1271 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1273 if (new_order != old_order) {
1275 /* this isn't the pointer track */
1277 if (y_span > 0) {
1279 /* moving up the canvas */
1280 if ( (new_order - y_span) >= tavs.visible_y_low) {
1282 int32_t n = 0;
1284 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1285 int32_t visible_tracks = 0;
1286 while (visible_tracks < y_span ) {
1287 visible_tracks++;
1288 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1289 /* passing through a hidden track */
1290 n--;
1294 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1295 /* moving to a non-track; disallow */
1296 return true;
1300 } else {
1301 /* moving beyond the lowest visible track; disallow */
1302 return true;
1305 } else if (y_span < 0) {
1307 /* moving down the canvas */
1308 if ((new_order - y_span) <= tavs.visible_y_high) {
1310 int32_t visible_tracks = 0;
1311 int32_t n = 0;
1312 while (visible_tracks > y_span ) {
1313 visible_tracks--;
1315 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1316 /* passing through a hidden track */
1317 n++;
1321 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1322 /* moving to a non-track; disallow */
1323 return true;
1327 } else {
1329 /* moving beyond the highest visible track; disallow */
1330 return true;
1334 } else {
1336 /* this is the pointer's track */
1338 if ((new_order - y_span) > tavs.visible_y_high) {
1339 /* we will overflow */
1340 return true;
1341 } else if ((new_order - y_span) < tavs.visible_y_low) {
1342 /* we will overflow */
1343 return true;
1347 return false;
1351 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1352 : RegionMotionDrag (e, i, p, v, b),
1353 _copy (c)
1355 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1357 _dest_trackview = tv;
1358 if (tv->layer_display() == Overlaid) {
1359 _dest_layer = 0;
1360 } else {
1361 _dest_layer = _primary->region()->layer ();
1364 double speed = 1;
1365 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1366 if (rtv && rtv->is_track()) {
1367 speed = rtv->track()->speed ();
1370 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1373 void
1374 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1376 RegionMotionDrag::start_grab (event, c);
1378 _pointer_frame_offset = grab_frame() - _last_frame_position;
1381 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1382 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1384 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1385 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1387 _primary = v->view()->create_region_view (r, false, false);
1389 _primary->get_canvas_group()->show ();
1390 _primary->set_position (pos, 0);
1391 _views.push_back (DraggingView (_primary));
1393 _last_frame_position = pos;
1395 _item = _primary->get_canvas_group ();
1396 _dest_trackview = v;
1397 _dest_layer = _primary->region()->layer ();
1400 map<RegionView*, pair<RouteTimeAxisView*, int> >
1401 RegionMotionDrag::find_time_axis_views_and_layers ()
1403 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1405 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1407 double ix1, ix2, iy1, iy2;
1408 RegionView* rv = i->view;
1409 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1410 rv->get_canvas_frame()->i2w (ix1, iy1);
1411 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1413 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1414 tav[rv] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1417 return tav;
1421 void
1422 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1424 _editor->update_canvas_now ();
1426 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1428 RouteTimeAxisView* dest_rtv = final[_primary].first;
1430 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1431 _primary->get_canvas_group()->property_y() = 0;
1433 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1435 _editor->begin_reversible_command (_("insert region"));
1436 playlist->clear_history ();
1437 playlist->add_region (_primary->region (), _last_frame_position);
1438 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1439 _editor->commit_reversible_command ();
1441 delete _primary;
1442 _primary = 0;
1443 _views.clear ();
1446 void
1447 RegionInsertDrag::aborted ()
1449 delete _primary;
1450 _primary = 0;
1451 _views.clear ();
1454 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1455 : RegionMoveDrag (e, i, p, v, false, false)
1460 struct RegionSelectionByPosition {
1461 bool operator() (RegionView*a, RegionView* b) {
1462 return a->region()->position () < b->region()->position();
1466 void
1467 RegionSpliceDrag::motion (GdkEvent* event, bool)
1469 RouteTimeAxisView* tv;
1470 layer_t layer;
1472 if (!check_possible (&tv, &layer)) {
1473 return;
1476 int dir;
1478 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1479 dir = 1;
1480 } else {
1481 dir = -1;
1484 RegionSelection copy (_editor->selection->regions);
1486 RegionSelectionByPosition cmp;
1487 copy.sort (cmp);
1489 nframes64_t const pf = adjusted_current_frame (event);
1491 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1493 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1495 if (!atv) {
1496 continue;
1499 boost::shared_ptr<Playlist> playlist;
1501 if ((playlist = atv->playlist()) == 0) {
1502 continue;
1505 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1506 continue;
1509 if (dir > 0) {
1510 if (pf < (*i)->region()->last_frame() + 1) {
1511 continue;
1513 } else {
1514 if (pf > (*i)->region()->first_frame()) {
1515 continue;
1520 playlist->shuffle ((*i)->region(), dir);
1524 void
1525 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1530 void
1531 RegionSpliceDrag::aborted ()
1533 /* XXX: TODO */
1536 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1537 : Drag (e, i),
1538 _view (v)
1543 void
1544 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1546 _dest_trackview = _view;
1548 Drag::start_grab (event);
1552 void
1553 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1555 if (first_move) {
1556 // TODO: create region-create-drag region view here
1559 // TODO: resize region-create-drag region view here
1562 void
1563 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1565 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1567 if (!mtv) {
1568 return;
1571 if (!movement_occurred) {
1572 mtv->add_region (grab_frame ());
1573 } else {
1574 motion (event, false);
1575 // TODO: create region-create-drag region here
1579 void
1580 RegionCreateDrag::aborted ()
1582 /* XXX: TODO */
1585 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1586 : Drag (e, i)
1587 , region (0)
1592 void
1593 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1595 Gdk::Cursor cursor;
1596 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1598 Drag::start_grab (event);
1600 region = &cnote->region_view();
1602 double const region_start = region->get_position_pixels();
1603 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1605 if (grab_x() <= middle_point) {
1606 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1607 at_front = true;
1608 } else {
1609 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1610 at_front = false;
1613 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1615 if (event->motion.state & Keyboard::PrimaryModifier) {
1616 relative = false;
1617 } else {
1618 relative = true;
1621 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1623 if (ms.size() > 1) {
1624 /* has to be relative, may make no sense otherwise */
1625 relative = true;
1628 /* select this note; if it is already selected, preserve the existing selection,
1629 otherwise make this note the only one selected.
1631 region->note_selected (cnote, cnote->selected ());
1633 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1634 MidiRegionSelection::iterator next;
1635 next = r;
1636 ++next;
1637 (*r)->begin_resizing (at_front);
1638 r = next;
1642 void
1643 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1645 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1646 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1647 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1651 void
1652 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1654 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1655 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1656 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1660 void
1661 NoteResizeDrag::aborted ()
1663 /* XXX: TODO */
1666 void
1667 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1672 void
1673 RegionGainDrag::finished (GdkEvent *, bool)
1678 void
1679 RegionGainDrag::aborted ()
1681 /* XXX: TODO */
1684 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1685 : RegionDrag (e, i, p, v)
1686 , _have_transaction (false)
1691 void
1692 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1694 double speed = 1.0;
1695 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1696 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1698 if (tv && tv->is_track()) {
1699 speed = tv->track()->speed();
1702 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1703 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1704 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1707 nframes64_t const pf = adjusted_current_frame (event);
1709 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1710 _operation = ContentsTrim;
1711 Drag::start_grab (event, _editor->trimmer_cursor);
1712 } else {
1713 /* These will get overridden for a point trim.*/
1714 if (pf < (region_start + region_length/2)) {
1715 /* closer to start */
1716 _operation = StartTrim;
1717 Drag::start_grab (event, _editor->left_side_trim_cursor);
1718 } else {
1719 /* closer to end */
1720 _operation = EndTrim;
1721 Drag::start_grab (event, _editor->right_side_trim_cursor);
1725 switch (_operation) {
1726 case StartTrim:
1727 _editor->show_verbose_time_cursor (region_start, 10);
1728 break;
1729 case EndTrim:
1730 _editor->show_verbose_time_cursor (region_end, 10);
1731 break;
1732 case ContentsTrim:
1733 _editor->show_verbose_time_cursor (pf, 10);
1734 break;
1738 void
1739 TrimDrag::motion (GdkEvent* event, bool first_move)
1741 RegionView* rv = _primary;
1743 /* snap modifier works differently here..
1744 its current state has to be passed to the
1745 various trim functions in order to work properly
1748 double speed = 1.0;
1749 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1750 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1751 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1753 if (tv && tv->is_track()) {
1754 speed = tv->track()->speed();
1757 nframes64_t const pf = adjusted_current_frame (event);
1759 if (first_move) {
1761 string trim_type;
1763 switch (_operation) {
1764 case StartTrim:
1765 trim_type = "Region start trim";
1766 break;
1767 case EndTrim:
1768 trim_type = "Region end trim";
1769 break;
1770 case ContentsTrim:
1771 trim_type = "Region content trim";
1772 break;
1775 _editor->begin_reversible_command (trim_type);
1776 _have_transaction = true;
1778 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1779 RegionView* rv = i->view;
1780 rv->fake_set_opaque(false);
1781 rv->region()->clear_history ();
1782 rv->region()->suspend_property_changes ();
1784 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1786 if (arv){
1787 arv->temporarily_hide_envelope ();
1790 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1791 insert_result = _editor->motion_frozen_playlists.insert (pl);
1793 if (insert_result.second) {
1794 pl->freeze();
1799 bool non_overlap_trim = false;
1801 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1802 non_overlap_trim = true;
1805 switch (_operation) {
1806 case StartTrim:
1807 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1808 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1810 break;
1812 case EndTrim:
1813 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1814 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1816 break;
1818 case ContentsTrim:
1820 bool swap_direction = false;
1822 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1823 swap_direction = true;
1826 nframes64_t frame_delta = 0;
1828 bool left_direction = false;
1829 if (last_pointer_frame() > pf) {
1830 left_direction = true;
1833 if (left_direction) {
1834 frame_delta = (last_pointer_frame() - pf);
1835 } else {
1836 frame_delta = (pf - last_pointer_frame());
1839 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1840 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1843 break;
1846 switch (_operation) {
1847 case StartTrim:
1848 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1849 break;
1850 case EndTrim:
1851 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1852 break;
1853 case ContentsTrim:
1854 _editor->show_verbose_time_cursor (pf, 10);
1855 break;
1860 void
1861 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1863 if (movement_occurred) {
1864 motion (event, false);
1866 if (!_editor->selection->selected (_primary)) {
1867 _editor->thaw_region_after_trim (*_primary);
1868 } else {
1870 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1871 _editor->thaw_region_after_trim (*i->view);
1872 i->view->fake_set_opaque (true);
1873 if (_have_transaction) {
1874 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1878 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1879 (*p)->thaw ();
1882 _editor->motion_frozen_playlists.clear ();
1884 if (_have_transaction) {
1885 _editor->commit_reversible_command();
1888 } else {
1889 /* no mouse movement */
1890 _editor->point_trim (event, adjusted_current_frame (event));
1894 void
1895 TrimDrag::aborted ()
1897 /* Our motion method is changing model state, so use the Undo system
1898 to cancel. Perhaps not ideal, as this will leave an Undo point
1899 behind which may be slightly odd from the user's point of view.
1902 finished (0, true);
1904 if (_have_transaction) {
1905 _editor->undo ();
1909 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1910 : Drag (e, i),
1911 _copy (c)
1913 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1914 assert (_marker);
1917 void
1918 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1920 if (_copy) {
1921 // create a dummy marker for visual representation of moving the copy.
1922 // The actual copying is not done before we reach the finish callback.
1923 char name[64];
1924 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1925 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1926 *new MeterSection (_marker->meter()));
1928 _item = &new_marker->the_item ();
1929 _marker = new_marker;
1931 } else {
1933 MetricSection& section (_marker->meter());
1935 if (!section.movable()) {
1936 return;
1941 Drag::start_grab (event, cursor);
1943 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1945 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1948 void
1949 MeterMarkerDrag::motion (GdkEvent* event, bool)
1951 nframes64_t const pf = adjusted_current_frame (event);
1953 _marker->set_position (pf);
1955 _editor->show_verbose_time_cursor (pf, 10);
1958 void
1959 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1961 if (!movement_occurred) {
1962 return;
1965 motion (event, false);
1967 BBT_Time when;
1969 TempoMap& map (_editor->session()->tempo_map());
1970 map.bbt_time (last_pointer_frame(), when);
1972 if (_copy == true) {
1973 _editor->begin_reversible_command (_("copy meter mark"));
1974 XMLNode &before = map.get_state();
1975 map.add_meter (_marker->meter(), when);
1976 XMLNode &after = map.get_state();
1977 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1978 _editor->commit_reversible_command ();
1980 // delete the dummy marker we used for visual representation of copying.
1981 // a new visual marker will show up automatically.
1982 delete _marker;
1983 } else {
1984 _editor->begin_reversible_command (_("move meter mark"));
1985 XMLNode &before = map.get_state();
1986 map.move_meter (_marker->meter(), when);
1987 XMLNode &after = map.get_state();
1988 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1989 _editor->commit_reversible_command ();
1993 void
1994 MeterMarkerDrag::aborted ()
1996 _marker->set_position (_marker->meter().frame ());
1999 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2000 : Drag (e, i),
2001 _copy (c)
2003 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2004 assert (_marker);
2007 void
2008 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2011 if (_copy) {
2013 // create a dummy marker for visual representation of moving the copy.
2014 // The actual copying is not done before we reach the finish callback.
2015 char name[64];
2016 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2017 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2018 *new TempoSection (_marker->tempo()));
2020 _item = &new_marker->the_item ();
2021 _marker = new_marker;
2023 } else {
2025 MetricSection& section (_marker->tempo());
2027 if (!section.movable()) {
2028 return;
2032 Drag::start_grab (event, cursor);
2034 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2035 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2038 void
2039 TempoMarkerDrag::motion (GdkEvent* event, bool)
2041 nframes64_t const pf = adjusted_current_frame (event);
2042 _marker->set_position (pf);
2043 _editor->show_verbose_time_cursor (pf, 10);
2046 void
2047 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2049 if (!movement_occurred) {
2050 return;
2053 motion (event, false);
2055 BBT_Time when;
2057 TempoMap& map (_editor->session()->tempo_map());
2058 map.bbt_time (last_pointer_frame(), when);
2060 if (_copy == true) {
2061 _editor->begin_reversible_command (_("copy tempo mark"));
2062 XMLNode &before = map.get_state();
2063 map.add_tempo (_marker->tempo(), when);
2064 XMLNode &after = map.get_state();
2065 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2066 _editor->commit_reversible_command ();
2068 // delete the dummy marker we used for visual representation of copying.
2069 // a new visual marker will show up automatically.
2070 delete _marker;
2071 } else {
2072 _editor->begin_reversible_command (_("move tempo mark"));
2073 XMLNode &before = map.get_state();
2074 map.move_tempo (_marker->tempo(), when);
2075 XMLNode &after = map.get_state();
2076 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2077 _editor->commit_reversible_command ();
2081 void
2082 TempoMarkerDrag::aborted ()
2084 _marker->set_position (_marker->tempo().frame());
2087 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2088 : Drag (e, i),
2089 _stop (s)
2091 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2092 assert (_cursor);
2095 void
2096 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2098 Drag::start_grab (event, c);
2100 if (!_stop) {
2102 nframes64_t where = _editor->event_frame (event, 0, 0);
2104 _editor->snap_to_with_modifier (where, event);
2105 _editor->playhead_cursor->set_position (where);
2109 if (_cursor == _editor->playhead_cursor) {
2110 _editor->_dragging_playhead = true;
2112 if (_editor->session() && _was_rolling && _stop) {
2113 _editor->session()->request_stop ();
2116 if (_editor->session() && _editor->session()->is_auditioning()) {
2117 _editor->session()->cancel_audition ();
2121 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2123 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2126 void
2127 CursorDrag::motion (GdkEvent* event, bool)
2129 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2131 if (adjusted_frame == last_pointer_frame()) {
2132 return;
2135 _cursor->set_position (adjusted_frame);
2137 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2139 #ifdef GTKOSX
2140 _editor->update_canvas_now ();
2141 #endif
2142 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2145 void
2146 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2148 _editor->_dragging_playhead = false;
2150 if (!movement_occurred && _stop) {
2151 return;
2154 motion (event, false);
2156 if (_item == &_editor->playhead_cursor->canvas_item) {
2157 if (_editor->session()) {
2158 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2159 _editor->_pending_locate_request = true;
2164 void
2165 CursorDrag::aborted ()
2167 _editor->_dragging_playhead = false;
2168 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2171 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2172 : RegionDrag (e, i, p, v)
2177 void
2178 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2180 Drag::start_grab (event, cursor);
2182 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2183 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2185 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2186 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2189 void
2190 FadeInDrag::motion (GdkEvent* event, bool)
2192 nframes64_t fade_length;
2194 nframes64_t const pos = adjusted_current_frame (event);
2196 boost::shared_ptr<Region> region = _primary->region ();
2198 if (pos < (region->position() + 64)) {
2199 fade_length = 64; // this should be a minimum defined somewhere
2200 } else if (pos > region->last_frame()) {
2201 fade_length = region->length();
2202 } else {
2203 fade_length = pos - region->position();
2206 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2208 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2210 if (!tmp) {
2211 continue;
2214 tmp->reset_fade_in_shape_width (fade_length);
2217 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2220 void
2221 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2223 if (!movement_occurred) {
2224 return;
2227 nframes64_t fade_length;
2229 nframes64_t const pos = adjusted_current_frame (event);
2231 boost::shared_ptr<Region> region = _primary->region ();
2233 if (pos < (region->position() + 64)) {
2234 fade_length = 64; // this should be a minimum defined somewhere
2235 } else if (pos > region->last_frame()) {
2236 fade_length = region->length();
2237 } else {
2238 fade_length = pos - region->position();
2241 _editor->begin_reversible_command (_("change fade in length"));
2243 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2245 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2247 if (!tmp) {
2248 continue;
2251 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2252 XMLNode &before = alist->get_state();
2254 tmp->audio_region()->set_fade_in_length (fade_length);
2255 tmp->audio_region()->set_fade_in_active (true);
2257 XMLNode &after = alist->get_state();
2258 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2261 _editor->commit_reversible_command ();
2264 void
2265 FadeInDrag::aborted ()
2267 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2268 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2270 if (!tmp) {
2271 continue;
2274 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2278 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2279 : RegionDrag (e, i, p, v)
2284 void
2285 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2287 Drag::start_grab (event, cursor);
2289 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2290 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2292 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2293 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2296 void
2297 FadeOutDrag::motion (GdkEvent* event, bool)
2299 nframes64_t fade_length;
2301 nframes64_t const pos = adjusted_current_frame (event);
2303 boost::shared_ptr<Region> region = _primary->region ();
2305 if (pos > (region->last_frame() - 64)) {
2306 fade_length = 64; // this should really be a minimum fade defined somewhere
2308 else if (pos < region->position()) {
2309 fade_length = region->length();
2311 else {
2312 fade_length = region->last_frame() - pos;
2315 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2317 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2319 if (!tmp) {
2320 continue;
2323 tmp->reset_fade_out_shape_width (fade_length);
2326 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2329 void
2330 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2332 if (!movement_occurred) {
2333 return;
2336 nframes64_t fade_length;
2338 nframes64_t const pos = adjusted_current_frame (event);
2340 boost::shared_ptr<Region> region = _primary->region ();
2342 if (pos > (region->last_frame() - 64)) {
2343 fade_length = 64; // this should really be a minimum fade defined somewhere
2345 else if (pos < region->position()) {
2346 fade_length = region->length();
2348 else {
2349 fade_length = region->last_frame() - pos;
2352 _editor->begin_reversible_command (_("change fade out length"));
2354 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2356 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2358 if (!tmp) {
2359 continue;
2362 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2363 XMLNode &before = alist->get_state();
2365 tmp->audio_region()->set_fade_out_length (fade_length);
2366 tmp->audio_region()->set_fade_out_active (true);
2368 XMLNode &after = alist->get_state();
2369 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2372 _editor->commit_reversible_command ();
2375 void
2376 FadeOutDrag::aborted ()
2378 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2379 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2381 if (!tmp) {
2382 continue;
2385 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2389 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2390 : Drag (e, i)
2392 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2393 assert (_marker);
2395 _points.push_back (Gnome::Art::Point (0, 0));
2396 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2398 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2399 _line->property_width_pixels() = 1;
2400 _line->property_points () = _points;
2401 _line->hide ();
2403 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2406 MarkerDrag::~MarkerDrag ()
2408 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2409 delete *i;
2413 void
2414 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2416 Drag::start_grab (event, cursor);
2418 bool is_start;
2420 Location *location = _editor->find_location_from_marker (_marker, is_start);
2421 _editor->_dragging_edit_point = true;
2423 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2425 update_item (location);
2427 // _drag_line->show();
2428 // _line->raise_to_top();
2430 if (is_start) {
2431 _editor->show_verbose_time_cursor (location->start(), 10);
2432 } else {
2433 _editor->show_verbose_time_cursor (location->end(), 10);
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 nframes64_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_frames) {
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::motion (GdkEvent* event, bool)
2493 nframes64_t f_delta = 0;
2494 bool is_start;
2495 bool move_both = false;
2496 Marker* marker;
2497 Location *real_location;
2498 Location *copy_location = 0;
2500 nframes64_t const newframe = adjusted_current_frame (event);
2502 nframes64_t next = newframe;
2504 if (newframe == last_pointer_frame()) {
2505 return;
2508 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2509 move_both = true;
2512 MarkerSelection::iterator i;
2513 list<Location*>::iterator x;
2515 /* find the marker we're dragging, and compute the delta */
2517 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2518 x != _copied_locations.end() && i != _editor->selection->markers.end();
2519 ++i, ++x) {
2521 copy_location = *x;
2522 marker = *i;
2524 if (marker == _marker) {
2526 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2527 /* que pasa ?? */
2528 return;
2531 if (real_location->is_mark()) {
2532 f_delta = newframe - copy_location->start();
2533 } else {
2536 switch (marker->type()) {
2537 case Marker::Start:
2538 case Marker::LoopStart:
2539 case Marker::PunchIn:
2540 f_delta = newframe - copy_location->start();
2541 break;
2543 case Marker::End:
2544 case Marker::LoopEnd:
2545 case Marker::PunchOut:
2546 f_delta = newframe - copy_location->end();
2547 break;
2548 default:
2549 /* what kind of marker is this ? */
2550 return;
2553 break;
2557 if (i == _editor->selection->markers.end()) {
2558 /* hmm, impossible - we didn't find the dragged marker */
2559 return;
2562 /* now move them all */
2564 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2565 x != _copied_locations.end() && i != _editor->selection->markers.end();
2566 ++i, ++x) {
2568 copy_location = *x;
2569 marker = *i;
2571 /* call this to find out if its the start or end */
2573 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2574 continue;
2577 if (real_location->locked()) {
2578 continue;
2581 if (copy_location->is_mark()) {
2583 /* now move it */
2585 copy_location->set_start (copy_location->start() + f_delta);
2587 } else {
2589 nframes64_t new_start = copy_location->start() + f_delta;
2590 nframes64_t new_end = copy_location->end() + f_delta;
2592 if (is_start) { // start-of-range marker
2594 if (move_both) {
2595 copy_location->set_start (new_start);
2596 copy_location->set_end (new_end);
2597 } else if (new_start < copy_location->end()) {
2598 copy_location->set_start (new_start);
2599 } else {
2600 _editor->snap_to (next, 1, true);
2601 copy_location->set_end (next);
2602 copy_location->set_start (newframe);
2605 } else { // end marker
2607 if (move_both) {
2608 copy_location->set_end (new_end);
2609 copy_location->set_start (new_start);
2610 } else if (new_end > copy_location->start()) {
2611 copy_location->set_end (new_end);
2612 } else if (newframe > 0) {
2613 _editor->snap_to (next, -1, true);
2614 copy_location->set_start (next);
2615 copy_location->set_end (newframe);
2620 update_item (copy_location);
2622 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2624 if (lm) {
2625 lm->set_position (copy_location->start(), copy_location->end());
2629 assert (!_copied_locations.empty());
2631 _editor->show_verbose_time_cursor (newframe, 10);
2633 #ifdef GTKOSX
2634 _editor->update_canvas_now ();
2635 #endif
2638 void
2639 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2641 if (!movement_occurred) {
2643 /* just a click, do nothing but finish
2644 off the selection process
2647 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2649 switch (op) {
2650 case Selection::Set:
2651 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2652 _editor->selection->set (_marker);
2654 break;
2656 case Selection::Toggle:
2657 case Selection::Extend:
2658 case Selection::Add:
2659 break;
2662 return;
2665 _editor->_dragging_edit_point = false;
2667 _editor->begin_reversible_command ( _("move marker") );
2668 XMLNode &before = _editor->session()->locations()->get_state();
2670 MarkerSelection::iterator i;
2671 list<Location*>::iterator x;
2672 bool is_start;
2674 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2675 x != _copied_locations.end() && i != _editor->selection->markers.end();
2676 ++i, ++x) {
2678 Location * location = _editor->find_location_from_marker (*i, is_start);
2680 if (location) {
2682 if (location->locked()) {
2683 return;
2686 if (location->is_mark()) {
2687 location->set_start ((*x)->start());
2688 } else {
2689 location->set ((*x)->start(), (*x)->end());
2694 XMLNode &after = _editor->session()->locations()->get_state();
2695 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2696 _editor->commit_reversible_command ();
2698 _line->hide();
2701 void
2702 MarkerDrag::aborted ()
2704 /* XXX: TODO */
2707 void
2708 MarkerDrag::update_item (Location* location)
2710 double const x1 = _editor->frame_to_pixel (location->start());
2712 _points.front().set_x(x1);
2713 _points.back().set_x(x1);
2714 _line->property_points() = _points;
2717 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2718 : Drag (e, i),
2719 _cumulative_x_drag (0),
2720 _cumulative_y_drag (0)
2722 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2723 assert (_point);
2727 void
2728 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2730 Drag::start_grab (event, _editor->fader_cursor);
2732 // start the grab at the center of the control point so
2733 // the point doesn't 'jump' to the mouse after the first drag
2734 _time_axis_view_grab_x = _point->get_x();
2735 _time_axis_view_grab_y = _point->get_y();
2737 float const fraction = 1 - (_point->get_y() / _point->line().height());
2739 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2741 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2742 event->button.x + 10, event->button.y + 10);
2744 _editor->show_verbose_canvas_cursor ();
2747 void
2748 ControlPointDrag::motion (GdkEvent* event, bool)
2750 double dx = _drags->current_pointer_x() - last_pointer_x();
2751 double dy = _drags->current_pointer_y() - last_pointer_y();
2753 if (event->button.state & Keyboard::SecondaryModifier) {
2754 dx *= 0.1;
2755 dy *= 0.1;
2758 /* coordinate in TimeAxisView's space */
2759 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2760 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2762 // calculate zero crossing point. back off by .01 to stay on the
2763 // positive side of zero
2764 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2766 // make sure we hit zero when passing through
2767 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2768 cy = zero_gain_y;
2771 if (_x_constrained) {
2772 cx = _time_axis_view_grab_x;
2774 if (_y_constrained) {
2775 cy = _time_axis_view_grab_y;
2778 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2779 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2781 cx = max (0.0, cx);
2782 cy = max (0.0, cy);
2783 cy = min ((double) _point->line().height(), cy);
2785 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2787 if (!_x_constrained) {
2788 _editor->snap_to_with_modifier (cx_frames, event);
2791 float const fraction = 1.0 - (cy / _point->line().height());
2793 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2795 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2797 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2800 void
2801 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2803 if (!movement_occurred) {
2805 /* just a click */
2807 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2808 _editor->reset_point_selection ();
2811 } else {
2812 motion (event, false);
2814 _point->line().end_drag ();
2817 void
2818 ControlPointDrag::aborted ()
2820 _point->line().reset ();
2823 bool
2824 ControlPointDrag::active (Editing::MouseMode m)
2826 if (m == Editing::MouseGain) {
2827 /* always active in mouse gain */
2828 return true;
2831 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2832 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2835 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2836 : Drag (e, i),
2837 _line (0),
2838 _cumulative_y_drag (0)
2842 void
2843 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2845 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2846 assert (_line);
2848 _item = &_line->grab_item ();
2850 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2851 origin, and ditto for y.
2854 double cx = event->button.x;
2855 double cy = event->button.y;
2857 _line->parent_group().w2i (cx, cy);
2859 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2861 uint32_t before;
2862 uint32_t after;
2864 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2865 /* no adjacent points */
2866 return;
2869 Drag::start_grab (event, _editor->fader_cursor);
2871 /* store grab start in parent frame */
2873 _time_axis_view_grab_x = cx;
2874 _time_axis_view_grab_y = cy;
2876 double fraction = 1.0 - (cy / _line->height());
2878 _line->start_drag_line (before, after, fraction);
2880 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2881 event->button.x + 10, event->button.y + 10);
2883 _editor->show_verbose_canvas_cursor ();
2886 void
2887 LineDrag::motion (GdkEvent* event, bool)
2889 double dy = _drags->current_pointer_y() - last_pointer_y();
2891 if (event->button.state & Keyboard::SecondaryModifier) {
2892 dy *= 0.1;
2895 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2897 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2899 cy = max (0.0, cy);
2900 cy = min ((double) _line->height(), cy);
2902 double const fraction = 1.0 - (cy / _line->height());
2904 bool push;
2906 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2907 push = false;
2908 } else {
2909 push = true;
2912 /* we are ignoring x position for this drag, so we can just pass in anything */
2913 _line->drag_motion (0, fraction, true, push);
2915 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2918 void
2919 LineDrag::finished (GdkEvent* event, bool)
2921 motion (event, false);
2922 _line->end_drag ();
2925 void
2926 LineDrag::aborted ()
2928 _line->reset ();
2931 void
2932 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2934 Drag::start_grab (event);
2935 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2938 void
2939 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2941 nframes64_t start;
2942 nframes64_t end;
2943 double y1;
2944 double y2;
2946 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2948 nframes64_t grab = grab_frame ();
2949 if (Config->get_rubberbanding_snaps_to_grid ()) {
2950 _editor->snap_to_with_modifier (grab, event);
2953 /* base start and end on initial click position */
2955 if (pf < grab) {
2956 start = pf;
2957 end = grab;
2958 } else {
2959 end = pf;
2960 start = grab;
2963 if (_drags->current_pointer_y() < grab_y()) {
2964 y1 = _drags->current_pointer_y();
2965 y2 = grab_y();
2966 } else {
2967 y2 = _drags->current_pointer_y();
2968 y1 = grab_y();
2972 if (start != end || y1 != y2) {
2974 double x1 = _editor->frame_to_pixel (start);
2975 double x2 = _editor->frame_to_pixel (end);
2977 _editor->rubberband_rect->property_x1() = x1;
2978 _editor->rubberband_rect->property_y1() = y1;
2979 _editor->rubberband_rect->property_x2() = x2;
2980 _editor->rubberband_rect->property_y2() = y2;
2982 _editor->rubberband_rect->show();
2983 _editor->rubberband_rect->raise_to_top();
2985 _editor->show_verbose_time_cursor (pf, 10);
2989 void
2990 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2992 if (movement_occurred) {
2994 motion (event, false);
2996 double y1,y2;
2997 if (_drags->current_pointer_y() < grab_y()) {
2998 y1 = _drags->current_pointer_y();
2999 y2 = grab_y();
3000 } else {
3001 y2 = _drags->current_pointer_y();
3002 y1 = grab_y();
3006 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3007 bool committed;
3009 _editor->begin_reversible_command (_("rubberband selection"));
3011 if (grab_frame() < last_pointer_frame()) {
3012 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3013 } else {
3014 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3017 if (!committed) {
3018 _editor->commit_reversible_command ();
3021 } else {
3022 if (!getenv("ARDOUR_SAE")) {
3023 _editor->selection->clear_tracks();
3025 _editor->selection->clear_regions();
3026 _editor->selection->clear_points ();
3027 _editor->selection->clear_lines ();
3030 _editor->rubberband_rect->hide();
3033 void
3034 RubberbandSelectDrag::aborted ()
3036 _editor->rubberband_rect->hide ();
3039 void
3040 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3042 Drag::start_grab (event);
3044 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3047 void
3048 TimeFXDrag::motion (GdkEvent* event, bool)
3050 RegionView* rv = _primary;
3052 nframes64_t const pf = adjusted_current_frame (event);
3054 if (pf > rv->region()->position()) {
3055 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3058 _editor->show_verbose_time_cursor (pf, 10);
3061 void
3062 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3064 _primary->get_time_axis_view().hide_timestretch ();
3066 if (!movement_occurred) {
3067 return;
3070 if (last_pointer_frame() < _primary->region()->position()) {
3071 /* backwards drag of the left edge - not usable */
3072 return;
3075 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3077 float percentage = (double) newlen / (double) _primary->region()->length();
3079 #ifndef USE_RUBBERBAND
3080 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3081 if (_primary->region()->data_type() == DataType::AUDIO) {
3082 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3084 #endif
3086 _editor->begin_reversible_command (_("timestretch"));
3088 // XXX how do timeFX on multiple regions ?
3090 RegionSelection rs;
3091 rs.add (_primary);
3093 if (_editor->time_stretch (rs, percentage) == -1) {
3094 error << _("An error occurred while executing time stretch operation") << endmsg;
3098 void
3099 TimeFXDrag::aborted ()
3101 _primary->get_time_axis_view().hide_timestretch ();
3105 void
3106 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3108 Drag::start_grab (event);
3111 void
3112 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3114 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3117 void
3118 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3120 if (movement_occurred && _editor->session()) {
3121 /* make sure we stop */
3122 _editor->session()->request_transport_speed (0.0);
3126 void
3127 ScrubDrag::aborted ()
3129 /* XXX: TODO */
3132 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3133 : Drag (e, i)
3134 , _operation (o)
3135 , _copy (false)
3136 , _original_pointer_time_axis (-1)
3137 , _last_pointer_time_axis (-1)
3142 void
3143 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3145 nframes64_t start = 0;
3146 nframes64_t end = 0;
3148 if (_editor->session() == 0) {
3149 return;
3152 Gdk::Cursor* cursor = 0;
3154 switch (_operation) {
3155 case CreateSelection:
3156 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3157 _copy = true;
3158 } else {
3159 _copy = false;
3161 cursor = _editor->selector_cursor;
3162 Drag::start_grab (event, cursor);
3163 break;
3165 case SelectionStartTrim:
3166 if (_editor->clicked_axisview) {
3167 _editor->clicked_axisview->order_selection_trims (_item, true);
3169 Drag::start_grab (event, _editor->left_side_trim_cursor);
3170 start = _editor->selection->time[_editor->clicked_selection].start;
3171 _pointer_frame_offset = grab_frame() - start;
3172 break;
3174 case SelectionEndTrim:
3175 if (_editor->clicked_axisview) {
3176 _editor->clicked_axisview->order_selection_trims (_item, false);
3178 Drag::start_grab (event, _editor->right_side_trim_cursor);
3179 end = _editor->selection->time[_editor->clicked_selection].end;
3180 _pointer_frame_offset = grab_frame() - end;
3181 break;
3183 case SelectionMove:
3184 start = _editor->selection->time[_editor->clicked_selection].start;
3185 Drag::start_grab (event, cursor);
3186 _pointer_frame_offset = grab_frame() - start;
3187 break;
3190 if (_operation == SelectionMove) {
3191 _editor->show_verbose_time_cursor (start, 10);
3192 } else {
3193 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3196 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3199 void
3200 SelectionDrag::motion (GdkEvent* event, bool first_move)
3202 nframes64_t start = 0;
3203 nframes64_t end = 0;
3204 nframes64_t length;
3206 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3207 if (pending_time_axis.first == 0) {
3208 return;
3211 nframes64_t const pending_position = adjusted_current_frame (event);
3213 /* only alter selection if things have changed */
3215 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3216 return;
3219 switch (_operation) {
3220 case CreateSelection:
3222 nframes64_t grab = grab_frame ();
3224 if (first_move) {
3225 _editor->snap_to (grab);
3228 if (pending_position < grab_frame()) {
3229 start = pending_position;
3230 end = grab;
3231 } else {
3232 end = pending_position;
3233 start = grab;
3236 /* first drag: Either add to the selection
3237 or create a new selection
3240 if (first_move) {
3242 if (_copy) {
3243 /* adding to the selection */
3244 _editor->selection->add (_editor->clicked_axisview);
3245 _editor->clicked_selection = _editor->selection->add (start, end);
3246 _copy = false;
3247 } else {
3248 /* new selection */
3250 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3251 _editor->selection->set (_editor->clicked_axisview);
3254 _editor->clicked_selection = _editor->selection->set (start, end);
3258 /* select the track that we're in */
3259 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3260 _editor->selection->add (pending_time_axis.first);
3261 _added_time_axes.push_back (pending_time_axis.first);
3264 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3265 tracks that we selected in the first place.
3268 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3269 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3271 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3272 while (i != _added_time_axes.end()) {
3274 list<TimeAxisView*>::iterator tmp = i;
3275 ++tmp;
3277 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3278 _editor->selection->remove (*i);
3279 _added_time_axes.remove (*i);
3282 i = tmp;
3286 break;
3288 case SelectionStartTrim:
3290 start = _editor->selection->time[_editor->clicked_selection].start;
3291 end = _editor->selection->time[_editor->clicked_selection].end;
3293 if (pending_position > end) {
3294 start = end;
3295 } else {
3296 start = pending_position;
3298 break;
3300 case SelectionEndTrim:
3302 start = _editor->selection->time[_editor->clicked_selection].start;
3303 end = _editor->selection->time[_editor->clicked_selection].end;
3305 if (pending_position < start) {
3306 end = start;
3307 } else {
3308 end = pending_position;
3311 break;
3313 case SelectionMove:
3315 start = _editor->selection->time[_editor->clicked_selection].start;
3316 end = _editor->selection->time[_editor->clicked_selection].end;
3318 length = end - start;
3320 start = pending_position;
3321 _editor->snap_to (start);
3323 end = start + length;
3325 break;
3328 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3329 _editor->start_canvas_autoscroll (1, 0);
3332 if (start != end) {
3333 _editor->selection->replace (_editor->clicked_selection, start, end);
3336 if (_operation == SelectionMove) {
3337 _editor->show_verbose_time_cursor(start, 10);
3338 } else {
3339 _editor->show_verbose_time_cursor(pending_position, 10);
3343 void
3344 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3346 Session* s = _editor->session();
3348 if (movement_occurred) {
3349 motion (event, false);
3350 /* XXX this is not object-oriented programming at all. ick */
3351 if (_editor->selection->time.consolidate()) {
3352 _editor->selection->TimeChanged ();
3355 /* XXX what if its a music time selection? */
3356 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3357 s->request_play_range (&_editor->selection->time, true);
3361 } else {
3362 /* just a click, no pointer movement.*/
3364 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3365 _editor->selection->clear_time();
3368 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3369 _editor->selection->set (_editor->clicked_axisview);
3372 if (s && s->get_play_range () && s->transport_rolling()) {
3373 s->request_stop (false, false);
3378 _editor->stop_canvas_autoscroll ();
3381 void
3382 SelectionDrag::aborted ()
3384 /* XXX: TODO */
3387 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3388 : Drag (e, i),
3389 _operation (o),
3390 _copy (false)
3392 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3393 _drag_rect->hide ();
3395 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3396 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3399 void
3400 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3402 if (_editor->session() == 0) {
3403 return;
3406 Gdk::Cursor* cursor = 0;
3408 if (!_editor->temp_location) {
3409 _editor->temp_location = new Location;
3412 switch (_operation) {
3413 case CreateRangeMarker:
3414 case CreateTransportMarker:
3415 case CreateCDMarker:
3417 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3418 _copy = true;
3419 } else {
3420 _copy = false;
3422 cursor = _editor->selector_cursor;
3423 break;
3426 Drag::start_grab (event, cursor);
3428 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3431 void
3432 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3434 nframes64_t start = 0;
3435 nframes64_t end = 0;
3436 ArdourCanvas::SimpleRect *crect;
3438 switch (_operation) {
3439 case CreateRangeMarker:
3440 crect = _editor->range_bar_drag_rect;
3441 break;
3442 case CreateTransportMarker:
3443 crect = _editor->transport_bar_drag_rect;
3444 break;
3445 case CreateCDMarker:
3446 crect = _editor->cd_marker_bar_drag_rect;
3447 break;
3448 default:
3449 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3450 return;
3451 break;
3454 nframes64_t const pf = adjusted_current_frame (event);
3456 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3457 nframes64_t grab = grab_frame ();
3458 _editor->snap_to (grab);
3460 if (pf < grab_frame()) {
3461 start = pf;
3462 end = grab;
3463 } else {
3464 end = pf;
3465 start = grab;
3468 /* first drag: Either add to the selection
3469 or create a new selection.
3472 if (first_move) {
3474 _editor->temp_location->set (start, end);
3476 crect->show ();
3478 update_item (_editor->temp_location);
3479 _drag_rect->show();
3480 //_drag_rect->raise_to_top();
3485 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3486 _editor->start_canvas_autoscroll (1, 0);
3489 if (start != end) {
3490 _editor->temp_location->set (start, end);
3492 double x1 = _editor->frame_to_pixel (start);
3493 double x2 = _editor->frame_to_pixel (end);
3494 crect->property_x1() = x1;
3495 crect->property_x2() = x2;
3497 update_item (_editor->temp_location);
3500 _editor->show_verbose_time_cursor (pf, 10);
3504 void
3505 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3507 Location * newloc = 0;
3508 string rangename;
3509 int flags;
3511 if (movement_occurred) {
3512 motion (event, false);
3513 _drag_rect->hide();
3515 switch (_operation) {
3516 case CreateRangeMarker:
3517 case CreateCDMarker:
3519 _editor->begin_reversible_command (_("new range marker"));
3520 XMLNode &before = _editor->session()->locations()->get_state();
3521 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3522 if (_operation == CreateCDMarker) {
3523 flags = Location::IsRangeMarker | Location::IsCDMarker;
3524 _editor->cd_marker_bar_drag_rect->hide();
3526 else {
3527 flags = Location::IsRangeMarker;
3528 _editor->range_bar_drag_rect->hide();
3530 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3531 _editor->session()->locations()->add (newloc, true);
3532 XMLNode &after = _editor->session()->locations()->get_state();
3533 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3534 _editor->commit_reversible_command ();
3535 break;
3538 case CreateTransportMarker:
3539 // popup menu to pick loop or punch
3540 _editor->new_transport_marker_context_menu (&event->button, _item);
3541 break;
3543 } else {
3544 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3546 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3548 nframes64_t start;
3549 nframes64_t end;
3551 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3553 if (end == max_frames) {
3554 end = _editor->session()->current_end_frame ();
3557 if (start == max_frames) {
3558 start = _editor->session()->current_start_frame ();
3561 switch (_editor->mouse_mode) {
3562 case MouseObject:
3563 /* find the two markers on either side and then make the selection from it */
3564 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3565 break;
3567 case MouseRange:
3568 /* find the two markers on either side of the click and make the range out of it */
3569 _editor->selection->set (start, end);
3570 break;
3572 default:
3573 break;
3578 _editor->stop_canvas_autoscroll ();
3581 void
3582 RangeMarkerBarDrag::aborted ()
3584 /* XXX: TODO */
3587 void
3588 RangeMarkerBarDrag::update_item (Location* location)
3590 double const x1 = _editor->frame_to_pixel (location->start());
3591 double const x2 = _editor->frame_to_pixel (location->end());
3593 _drag_rect->property_x1() = x1;
3594 _drag_rect->property_x2() = x2;
3597 void
3598 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3600 Drag::start_grab (event, _editor->zoom_cursor);
3601 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3604 void
3605 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3607 nframes64_t start;
3608 nframes64_t end;
3610 nframes64_t const pf = adjusted_current_frame (event);
3612 nframes64_t grab = grab_frame ();
3613 _editor->snap_to_with_modifier (grab, event);
3615 /* base start and end on initial click position */
3616 if (pf < grab) {
3617 start = pf;
3618 end = grab;
3619 } else {
3620 end = pf;
3621 start = grab;
3624 if (start != end) {
3626 if (first_move) {
3627 _editor->zoom_rect->show();
3628 _editor->zoom_rect->raise_to_top();
3631 _editor->reposition_zoom_rect(start, end);
3633 _editor->show_verbose_time_cursor (pf, 10);
3637 void
3638 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3640 if (movement_occurred) {
3641 motion (event, false);
3643 if (grab_frame() < last_pointer_frame()) {
3644 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3645 } else {
3646 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3648 } else {
3649 _editor->temporal_zoom_to_frame (false, grab_frame());
3651 temporal_zoom_step (false);
3652 center_screen (grab_frame());
3656 _editor->zoom_rect->hide();
3659 void
3660 MouseZoomDrag::aborted ()
3662 _editor->zoom_rect->hide ();
3665 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3666 : Drag (e, i)
3668 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3669 region = &cnote->region_view();
3672 void
3673 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3675 Drag::start_grab (event);
3677 drag_delta_x = 0;
3678 drag_delta_note = 0;
3680 double event_x;
3681 double event_y;
3683 event_x = _drags->current_pointer_x();
3684 event_y = _drags->current_pointer_y();
3686 _item->property_parent().get_value()->w2i(event_x, event_y);
3688 last_x = region->snap_to_pixel(event_x);
3689 last_y = event_y;
3691 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3693 if (!(was_selected = cnote->selected())) {
3695 /* tertiary-click means extend selection - we'll do that on button release,
3696 so don't add it here, because otherwise we make it hard to figure
3697 out the "extend-to" range.
3700 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3702 if (!extend) {
3703 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3705 if (add) {
3706 region->note_selected (cnote, true);
3707 } else {
3708 region->unique_select (cnote);
3714 void
3715 NoteDrag::motion (GdkEvent*, bool)
3717 MidiStreamView* streamview = region->midi_stream_view();
3718 double event_x;
3719 double event_y;
3721 event_x = _drags->current_pointer_x();
3722 event_y = _drags->current_pointer_y();
3724 _item->property_parent().get_value()->w2i(event_x, event_y);
3726 event_x = region->snap_to_pixel(event_x);
3728 double dx = event_x - last_x;
3729 double dy = event_y - last_y;
3730 last_x = event_x;
3732 drag_delta_x += dx;
3734 // Snap to note rows
3736 if (abs (dy) < streamview->note_height()) {
3737 dy = 0.0;
3738 } else {
3739 int8_t this_delta_note;
3740 if (dy > 0) {
3741 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3742 } else {
3743 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3745 drag_delta_note -= this_delta_note;
3746 dy = streamview->note_height() * this_delta_note;
3747 last_y = last_y + dy;
3750 if (dx || dy) {
3752 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3753 Evoral::MusicalTime new_time;
3755 if (drag_delta_x) {
3756 nframes64_t start_frames = region->beats_to_frames(cnote->note()->time());
3757 if (drag_delta_x >= 0) {
3758 start_frames += region->snap_frame_to_frame(_editor->pixel_to_frame(drag_delta_x));
3759 } else {
3760 start_frames -= region->snap_frame_to_frame(_editor->pixel_to_frame(-drag_delta_x));
3762 new_time = region->frames_to_beats(start_frames);
3763 } else {
3764 new_time = cnote->note()->time();
3767 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > check_note (
3768 new Evoral::Note<Evoral::MusicalTime> (cnote->note()->channel(),
3769 new_time,
3770 cnote->note()->length(),
3771 cnote->note()->note() + drag_delta_note,
3772 cnote->note()->velocity()));
3774 region->move_selection (dx, dy);
3776 char buf[12];
3777 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (cnote->note()->note()).c_str(),
3778 (int) floor ((cnote->note()->note() + drag_delta_note)));
3779 _editor->show_verbose_canvas_cursor_with (buf);
3783 void
3784 NoteDrag::finished (GdkEvent* ev, bool moved)
3786 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3788 if (!moved) {
3789 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3791 if (was_selected) {
3792 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3793 if (add) {
3794 region->note_deselected (cnote);
3796 } else {
3797 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3798 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3800 if (!extend && !add && region->selection_size() > 1) {
3801 region->unique_select(cnote);
3802 } else if (extend) {
3803 region->note_selected (cnote, true, true);
3804 } else {
3805 /* it was added during button press */
3809 } else {
3810 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3814 void
3815 NoteDrag::aborted ()
3817 /* XXX: TODO */
3820 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3821 : Drag (e, i)
3822 , _ranges (r)
3823 , _nothing_to_drag (false)
3825 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3826 assert (_atav);
3828 _line = _atav->line ();
3831 void
3832 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3834 Drag::start_grab (event, cursor);
3836 list<ControlPoint*> points;
3838 XMLNode* state = &_line->get_state ();
3840 if (_ranges.empty()) {
3842 uint32_t const N = _line->npoints ();
3843 for (uint32_t i = 0; i < N; ++i) {
3844 points.push_back (_line->nth (i));
3847 } else {
3849 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3850 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3852 /* fade into and out of the region that we're dragging;
3853 64 samples length plucked out of thin air.
3855 nframes64_t const h = (j->start + j->end) / 2;
3856 nframes64_t a = j->start + 64;
3857 if (a > h) {
3858 a = h;
3860 nframes64_t b = j->end - 64;
3861 if (b < h) {
3862 b = h;
3865 the_list->add (j->start, the_list->eval (j->start));
3866 _line->add_always_in_view (j->start);
3867 the_list->add (a, the_list->eval (a));
3868 _line->add_always_in_view (a);
3869 the_list->add (b, the_list->eval (b));
3870 _line->add_always_in_view (b);
3871 the_list->add (j->end, the_list->eval (j->end));
3872 _line->add_always_in_view (j->end);
3875 uint32_t const N = _line->npoints ();
3876 for (uint32_t i = 0; i < N; ++i) {
3878 ControlPoint* p = _line->nth (i);
3880 list<AudioRange>::const_iterator j = _ranges.begin ();
3881 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3882 ++j;
3885 if (j != _ranges.end()) {
3886 points.push_back (p);
3891 if (points.empty()) {
3892 _nothing_to_drag = true;
3893 return;
3896 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3899 void
3900 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3902 if (_nothing_to_drag) {
3903 return;
3906 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3908 /* we are ignoring x position for this drag, so we can just pass in anything */
3909 _line->drag_motion (0, f, true, false);
3912 void
3913 AutomationRangeDrag::finished (GdkEvent* event, bool)
3915 if (_nothing_to_drag) {
3916 return;
3919 motion (event, false);
3920 _line->end_drag ();
3921 _line->clear_always_in_view ();
3924 void
3925 AutomationRangeDrag::aborted ()
3927 _line->clear_always_in_view ();
3928 _line->reset ();
3931 DraggingView::DraggingView (RegionView* v)
3932 : view (v)
3934 initial_y = v->get_canvas_group()->property_y ();