fix up file renaming code a little bit
[ArdourMidi.git] / gtk2_ardour / editor_drag.cc
blob27ca9b6c78c1c166ed1f237a4e23463d12899275
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 (true, 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* /*ignored*/)
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 = _editor->left_side_trim_cursor;
1607 at_front = true;
1608 } else {
1609 cursor = _editor->right_side_trim_cursor;
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 const region_start = (nframes64_t) (_primary->region()->position() / speed);
1703 nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1704 nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1706 nframes64_t const pf = adjusted_current_frame (event);
1708 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1709 _operation = ContentsTrim;
1710 Drag::start_grab (event, _editor->trimmer_cursor);
1711 } else {
1712 /* These will get overridden for a point trim.*/
1713 if (pf < (region_start + region_length/2)) {
1714 /* closer to start */
1715 _operation = StartTrim;
1716 Drag::start_grab (event, _editor->left_side_trim_cursor);
1717 } else {
1718 /* closer to end */
1719 _operation = EndTrim;
1720 Drag::start_grab (event, _editor->right_side_trim_cursor);
1724 switch (_operation) {
1725 case StartTrim:
1726 _editor->show_verbose_time_cursor (region_start, 10);
1727 break;
1728 case EndTrim:
1729 _editor->show_verbose_time_cursor (region_end, 10);
1730 break;
1731 case ContentsTrim:
1732 _editor->show_verbose_time_cursor (pf, 10);
1733 break;
1737 void
1738 TrimDrag::motion (GdkEvent* event, bool first_move)
1740 RegionView* rv = _primary;
1742 /* snap modifier works differently here..
1743 its current state has to be passed to the
1744 various trim functions in order to work properly
1747 double speed = 1.0;
1748 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1749 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1750 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1752 if (tv && tv->is_track()) {
1753 speed = tv->track()->speed();
1756 nframes64_t const pf = adjusted_current_frame (event);
1758 if (first_move) {
1760 string trim_type;
1762 switch (_operation) {
1763 case StartTrim:
1764 trim_type = "Region start trim";
1765 break;
1766 case EndTrim:
1767 trim_type = "Region end trim";
1768 break;
1769 case ContentsTrim:
1770 trim_type = "Region content trim";
1771 break;
1774 _editor->begin_reversible_command (trim_type);
1775 _have_transaction = true;
1777 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1778 RegionView* rv = i->view;
1779 rv->fake_set_opaque(false);
1780 rv->enable_display (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->enable_display (true);
1873 i->view->fake_set_opaque (true);
1874 if (_have_transaction) {
1875 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1879 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1880 (*p)->thaw ();
1883 _editor->motion_frozen_playlists.clear ();
1885 if (_have_transaction) {
1886 _editor->commit_reversible_command();
1889 } else {
1890 /* no mouse movement */
1891 _editor->point_trim (event, adjusted_current_frame (event));
1895 void
1896 TrimDrag::aborted ()
1898 /* Our motion method is changing model state, so use the Undo system
1899 to cancel. Perhaps not ideal, as this will leave an Undo point
1900 behind which may be slightly odd from the user's point of view.
1903 finished (0, true);
1905 if (_have_transaction) {
1906 _editor->undo ();
1910 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1911 : Drag (e, i),
1912 _copy (c)
1914 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1915 assert (_marker);
1918 void
1919 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1921 if (_copy) {
1922 // create a dummy marker for visual representation of moving the copy.
1923 // The actual copying is not done before we reach the finish callback.
1924 char name[64];
1925 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1926 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1927 *new MeterSection (_marker->meter()));
1929 _item = &new_marker->the_item ();
1930 _marker = new_marker;
1932 } else {
1934 MetricSection& section (_marker->meter());
1936 if (!section.movable()) {
1937 return;
1942 Drag::start_grab (event, cursor);
1944 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1946 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1949 void
1950 MeterMarkerDrag::motion (GdkEvent* event, bool)
1952 nframes64_t const pf = adjusted_current_frame (event);
1954 _marker->set_position (pf);
1956 _editor->show_verbose_time_cursor (pf, 10);
1959 void
1960 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1962 if (!movement_occurred) {
1963 return;
1966 motion (event, false);
1968 BBT_Time when;
1970 TempoMap& map (_editor->session()->tempo_map());
1971 map.bbt_time (last_pointer_frame(), when);
1973 if (_copy == true) {
1974 _editor->begin_reversible_command (_("copy meter mark"));
1975 XMLNode &before = map.get_state();
1976 map.add_meter (_marker->meter(), when);
1977 XMLNode &after = map.get_state();
1978 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1979 _editor->commit_reversible_command ();
1981 // delete the dummy marker we used for visual representation of copying.
1982 // a new visual marker will show up automatically.
1983 delete _marker;
1984 } else {
1985 _editor->begin_reversible_command (_("move meter mark"));
1986 XMLNode &before = map.get_state();
1987 map.move_meter (_marker->meter(), when);
1988 XMLNode &after = map.get_state();
1989 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1990 _editor->commit_reversible_command ();
1994 void
1995 MeterMarkerDrag::aborted ()
1997 _marker->set_position (_marker->meter().frame ());
2000 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2001 : Drag (e, i),
2002 _copy (c)
2004 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2005 assert (_marker);
2008 void
2009 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2012 if (_copy) {
2014 // create a dummy marker for visual representation of moving the copy.
2015 // The actual copying is not done before we reach the finish callback.
2016 char name[64];
2017 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2018 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2019 *new TempoSection (_marker->tempo()));
2021 _item = &new_marker->the_item ();
2022 _marker = new_marker;
2024 } else {
2026 MetricSection& section (_marker->tempo());
2028 if (!section.movable()) {
2029 return;
2033 Drag::start_grab (event, cursor);
2035 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2036 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2039 void
2040 TempoMarkerDrag::motion (GdkEvent* event, bool)
2042 nframes64_t const pf = adjusted_current_frame (event);
2043 _marker->set_position (pf);
2044 _editor->show_verbose_time_cursor (pf, 10);
2047 void
2048 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2050 if (!movement_occurred) {
2051 return;
2054 motion (event, false);
2056 BBT_Time when;
2058 TempoMap& map (_editor->session()->tempo_map());
2059 map.bbt_time (last_pointer_frame(), when);
2061 if (_copy == true) {
2062 _editor->begin_reversible_command (_("copy tempo mark"));
2063 XMLNode &before = map.get_state();
2064 map.add_tempo (_marker->tempo(), when);
2065 XMLNode &after = map.get_state();
2066 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2067 _editor->commit_reversible_command ();
2069 // delete the dummy marker we used for visual representation of copying.
2070 // a new visual marker will show up automatically.
2071 delete _marker;
2072 } else {
2073 _editor->begin_reversible_command (_("move tempo mark"));
2074 XMLNode &before = map.get_state();
2075 map.move_tempo (_marker->tempo(), when);
2076 XMLNode &after = map.get_state();
2077 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2078 _editor->commit_reversible_command ();
2082 void
2083 TempoMarkerDrag::aborted ()
2085 _marker->set_position (_marker->tempo().frame());
2088 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2089 : Drag (e, i),
2090 _stop (s)
2092 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2093 assert (_cursor);
2096 void
2097 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2099 Drag::start_grab (event, c);
2101 if (!_stop) {
2103 nframes64_t where = _editor->event_frame (event, 0, 0);
2105 _editor->snap_to_with_modifier (where, event);
2106 _editor->playhead_cursor->set_position (where);
2110 if (_cursor == _editor->playhead_cursor) {
2111 _editor->_dragging_playhead = true;
2113 if (_editor->session()) {
2114 if (_was_rolling && _stop) {
2115 _editor->session()->request_stop ();
2118 if (_editor->session()->is_auditioning()) {
2119 _editor->session()->cancel_audition ();
2122 nframes64_t const f = _editor->playhead_cursor->current_frame;
2123 _editor->session()->send_mmc_locate (f);
2124 _editor->session()->send_full_time_code (f);
2128 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2130 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2133 void
2134 CursorDrag::motion (GdkEvent* event, bool)
2136 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2138 if (adjusted_frame == last_pointer_frame()) {
2139 return;
2142 _cursor->set_position (adjusted_frame);
2144 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2146 if (_editor->session() && _item == &_editor->playhead_cursor->canvas_item) {
2147 nframes64_t const f = _editor->playhead_cursor->current_frame;
2148 _editor->session()->send_mmc_locate (f);
2149 _editor->session()->send_full_time_code (f);
2153 #ifdef GTKOSX
2154 _editor->update_canvas_now ();
2155 #endif
2156 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2159 void
2160 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2162 _editor->_dragging_playhead = false;
2164 if (!movement_occurred && _stop) {
2165 return;
2168 motion (event, false);
2170 if (_item == &_editor->playhead_cursor->canvas_item) {
2171 if (_editor->session()) {
2172 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2173 _editor->_pending_locate_request = true;
2178 void
2179 CursorDrag::aborted ()
2181 _editor->_dragging_playhead = false;
2182 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2185 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2186 : RegionDrag (e, i, p, v)
2191 void
2192 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2194 Drag::start_grab (event, cursor);
2196 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2197 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2199 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2200 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2204 void
2205 FadeInDrag::motion (GdkEvent* event, bool)
2207 nframes64_t fade_length;
2209 nframes64_t const pos = adjusted_current_frame (event);
2211 boost::shared_ptr<Region> region = _primary->region ();
2213 if (pos < (region->position() + 64)) {
2214 fade_length = 64; // this should be a minimum defined somewhere
2215 } else if (pos > region->last_frame()) {
2216 fade_length = region->length();
2217 } else {
2218 fade_length = pos - region->position();
2221 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2223 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2225 if (!tmp) {
2226 continue;
2229 tmp->reset_fade_in_shape_width (fade_length);
2232 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2235 void
2236 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2238 if (!movement_occurred) {
2239 return;
2242 nframes64_t fade_length;
2244 nframes64_t const pos = adjusted_current_frame (event);
2246 boost::shared_ptr<Region> region = _primary->region ();
2248 if (pos < (region->position() + 64)) {
2249 fade_length = 64; // this should be a minimum defined somewhere
2250 } else if (pos > region->last_frame()) {
2251 fade_length = region->length();
2252 } else {
2253 fade_length = pos - region->position();
2256 _editor->begin_reversible_command (_("change fade in length"));
2258 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2260 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2262 if (!tmp) {
2263 continue;
2266 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2267 XMLNode &before = alist->get_state();
2269 tmp->audio_region()->set_fade_in_length (fade_length);
2270 tmp->audio_region()->set_fade_in_active (true);
2272 XMLNode &after = alist->get_state();
2273 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2276 _editor->commit_reversible_command ();
2279 void
2280 FadeInDrag::aborted ()
2282 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2283 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2285 if (!tmp) {
2286 continue;
2289 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2293 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2294 : RegionDrag (e, i, p, v)
2299 void
2300 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2302 Drag::start_grab (event, cursor);
2304 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2305 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2307 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2308 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2311 void
2312 FadeOutDrag::motion (GdkEvent* event, bool)
2314 nframes64_t fade_length;
2316 nframes64_t const pos = adjusted_current_frame (event);
2318 boost::shared_ptr<Region> region = _primary->region ();
2320 if (pos > (region->last_frame() - 64)) {
2321 fade_length = 64; // this should really be a minimum fade defined somewhere
2323 else if (pos < region->position()) {
2324 fade_length = region->length();
2326 else {
2327 fade_length = region->last_frame() - pos;
2330 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2332 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2334 if (!tmp) {
2335 continue;
2338 tmp->reset_fade_out_shape_width (fade_length);
2341 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2344 void
2345 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2347 if (!movement_occurred) {
2348 return;
2351 nframes64_t fade_length;
2353 nframes64_t const pos = adjusted_current_frame (event);
2355 boost::shared_ptr<Region> region = _primary->region ();
2357 if (pos > (region->last_frame() - 64)) {
2358 fade_length = 64; // this should really be a minimum fade defined somewhere
2360 else if (pos < region->position()) {
2361 fade_length = region->length();
2363 else {
2364 fade_length = region->last_frame() - pos;
2367 _editor->begin_reversible_command (_("change fade out length"));
2369 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2371 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2373 if (!tmp) {
2374 continue;
2377 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2378 XMLNode &before = alist->get_state();
2380 tmp->audio_region()->set_fade_out_length (fade_length);
2381 tmp->audio_region()->set_fade_out_active (true);
2383 XMLNode &after = alist->get_state();
2384 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2387 _editor->commit_reversible_command ();
2390 void
2391 FadeOutDrag::aborted ()
2393 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2394 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2396 if (!tmp) {
2397 continue;
2400 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2404 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2405 : Drag (e, i)
2407 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2408 assert (_marker);
2410 _points.push_back (Gnome::Art::Point (0, 0));
2411 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2413 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2414 _line->property_width_pixels() = 1;
2415 _line->property_points () = _points;
2416 _line->hide ();
2418 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2421 MarkerDrag::~MarkerDrag ()
2423 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2424 delete *i;
2428 void
2429 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2431 Drag::start_grab (event, cursor);
2433 bool is_start;
2435 Location *location = _editor->find_location_from_marker (_marker, is_start);
2436 _editor->_dragging_edit_point = true;
2438 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2440 update_item (location);
2442 // _drag_line->show();
2443 // _line->raise_to_top();
2445 if (is_start) {
2446 _editor->show_verbose_time_cursor (location->start(), 10);
2447 } else {
2448 _editor->show_verbose_time_cursor (location->end(), 10);
2451 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2453 switch (op) {
2454 case Selection::Toggle:
2455 _editor->selection->toggle (_marker);
2456 break;
2457 case Selection::Set:
2458 if (!_editor->selection->selected (_marker)) {
2459 _editor->selection->set (_marker);
2461 break;
2462 case Selection::Extend:
2464 Locations::LocationList ll;
2465 list<Marker*> to_add;
2466 nframes64_t s, e;
2467 _editor->selection->markers.range (s, e);
2468 s = min (_marker->position(), s);
2469 e = max (_marker->position(), e);
2470 s = min (s, e);
2471 e = max (s, e);
2472 if (e < max_frames) {
2473 ++e;
2475 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2476 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2477 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2478 if (lm) {
2479 if (lm->start) {
2480 to_add.push_back (lm->start);
2482 if (lm->end) {
2483 to_add.push_back (lm->end);
2487 if (!to_add.empty()) {
2488 _editor->selection->add (to_add);
2490 break;
2492 case Selection::Add:
2493 _editor->selection->add (_marker);
2494 break;
2497 /* Set up copies for us to manipulate during the drag */
2499 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2500 Location* l = _editor->find_location_from_marker (*i, is_start);
2501 _copied_locations.push_back (new Location (*l));
2505 void
2506 MarkerDrag::motion (GdkEvent* event, bool)
2508 nframes64_t f_delta = 0;
2509 bool is_start;
2510 bool move_both = false;
2511 Marker* marker;
2512 Location *real_location;
2513 Location *copy_location = 0;
2515 nframes64_t const newframe = adjusted_current_frame (event);
2517 nframes64_t next = newframe;
2519 if (newframe == last_pointer_frame()) {
2520 return;
2523 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2524 move_both = true;
2527 MarkerSelection::iterator i;
2528 list<Location*>::iterator x;
2530 /* find the marker we're dragging, and compute the delta */
2532 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2533 x != _copied_locations.end() && i != _editor->selection->markers.end();
2534 ++i, ++x) {
2536 copy_location = *x;
2537 marker = *i;
2539 if (marker == _marker) {
2541 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2542 /* que pasa ?? */
2543 return;
2546 if (real_location->is_mark()) {
2547 f_delta = newframe - copy_location->start();
2548 } else {
2551 switch (marker->type()) {
2552 case Marker::Start:
2553 case Marker::LoopStart:
2554 case Marker::PunchIn:
2555 f_delta = newframe - copy_location->start();
2556 break;
2558 case Marker::End:
2559 case Marker::LoopEnd:
2560 case Marker::PunchOut:
2561 f_delta = newframe - copy_location->end();
2562 break;
2563 default:
2564 /* what kind of marker is this ? */
2565 return;
2568 break;
2572 if (i == _editor->selection->markers.end()) {
2573 /* hmm, impossible - we didn't find the dragged marker */
2574 return;
2577 /* now move them all */
2579 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2580 x != _copied_locations.end() && i != _editor->selection->markers.end();
2581 ++i, ++x) {
2583 copy_location = *x;
2584 marker = *i;
2586 /* call this to find out if its the start or end */
2588 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2589 continue;
2592 if (real_location->locked()) {
2593 continue;
2596 if (copy_location->is_mark()) {
2598 /* now move it */
2600 copy_location->set_start (copy_location->start() + f_delta);
2602 } else {
2604 nframes64_t new_start = copy_location->start() + f_delta;
2605 nframes64_t new_end = copy_location->end() + f_delta;
2607 if (is_start) { // start-of-range marker
2609 if (move_both) {
2610 copy_location->set_start (new_start);
2611 copy_location->set_end (new_end);
2612 } else if (new_start < copy_location->end()) {
2613 copy_location->set_start (new_start);
2614 } else {
2615 _editor->snap_to (next, 1, true);
2616 copy_location->set_end (next);
2617 copy_location->set_start (newframe);
2620 } else { // end marker
2622 if (move_both) {
2623 copy_location->set_end (new_end);
2624 copy_location->set_start (new_start);
2625 } else if (new_end > copy_location->start()) {
2626 copy_location->set_end (new_end);
2627 } else if (newframe > 0) {
2628 _editor->snap_to (next, -1, true);
2629 copy_location->set_start (next);
2630 copy_location->set_end (newframe);
2635 update_item (copy_location);
2637 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2639 if (lm) {
2640 lm->set_position (copy_location->start(), copy_location->end());
2644 assert (!_copied_locations.empty());
2646 _editor->show_verbose_time_cursor (newframe, 10);
2648 #ifdef GTKOSX
2649 _editor->update_canvas_now ();
2650 #endif
2653 void
2654 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2656 if (!movement_occurred) {
2658 /* just a click, do nothing but finish
2659 off the selection process
2662 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2664 switch (op) {
2665 case Selection::Set:
2666 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2667 _editor->selection->set (_marker);
2669 break;
2671 case Selection::Toggle:
2672 case Selection::Extend:
2673 case Selection::Add:
2674 break;
2677 return;
2680 _editor->_dragging_edit_point = false;
2682 _editor->begin_reversible_command ( _("move marker") );
2683 XMLNode &before = _editor->session()->locations()->get_state();
2685 MarkerSelection::iterator i;
2686 list<Location*>::iterator x;
2687 bool is_start;
2689 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2690 x != _copied_locations.end() && i != _editor->selection->markers.end();
2691 ++i, ++x) {
2693 Location * location = _editor->find_location_from_marker (*i, is_start);
2695 if (location) {
2697 if (location->locked()) {
2698 return;
2701 if (location->is_mark()) {
2702 location->set_start ((*x)->start());
2703 } else {
2704 location->set ((*x)->start(), (*x)->end());
2709 XMLNode &after = _editor->session()->locations()->get_state();
2710 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2711 _editor->commit_reversible_command ();
2713 _line->hide();
2716 void
2717 MarkerDrag::aborted ()
2719 /* XXX: TODO */
2722 void
2723 MarkerDrag::update_item (Location* location)
2725 double const x1 = _editor->frame_to_pixel (location->start());
2727 _points.front().set_x(x1);
2728 _points.back().set_x(x1);
2729 _line->property_points() = _points;
2732 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2733 : Drag (e, i),
2734 _cumulative_x_drag (0),
2735 _cumulative_y_drag (0)
2737 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2738 assert (_point);
2742 void
2743 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2745 Drag::start_grab (event, _editor->fader_cursor);
2747 // start the grab at the center of the control point so
2748 // the point doesn't 'jump' to the mouse after the first drag
2749 _time_axis_view_grab_x = _point->get_x();
2750 _time_axis_view_grab_y = _point->get_y();
2752 float const fraction = 1 - (_point->get_y() / _point->line().height());
2754 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2756 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2757 event->button.x + 10, event->button.y + 10);
2759 _editor->show_verbose_canvas_cursor ();
2762 void
2763 ControlPointDrag::motion (GdkEvent* event, bool)
2765 double dx = _drags->current_pointer_x() - last_pointer_x();
2766 double dy = _drags->current_pointer_y() - last_pointer_y();
2768 if (event->button.state & Keyboard::SecondaryModifier) {
2769 dx *= 0.1;
2770 dy *= 0.1;
2773 /* coordinate in TimeAxisView's space */
2774 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2775 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2777 // calculate zero crossing point. back off by .01 to stay on the
2778 // positive side of zero
2779 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2781 // make sure we hit zero when passing through
2782 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2783 cy = zero_gain_y;
2786 if (_x_constrained) {
2787 cx = _time_axis_view_grab_x;
2789 if (_y_constrained) {
2790 cy = _time_axis_view_grab_y;
2793 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2794 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2796 cx = max (0.0, cx);
2797 cy = max (0.0, cy);
2798 cy = min ((double) _point->line().height(), cy);
2800 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2802 if (!_x_constrained) {
2803 _editor->snap_to_with_modifier (cx_frames, event);
2806 float const fraction = 1.0 - (cy / _point->line().height());
2808 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2810 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2812 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2815 void
2816 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2818 if (!movement_occurred) {
2820 /* just a click */
2822 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2823 _editor->reset_point_selection ();
2826 } else {
2827 motion (event, false);
2829 _point->line().end_drag ();
2832 void
2833 ControlPointDrag::aborted ()
2835 _point->line().reset ();
2838 bool
2839 ControlPointDrag::active (Editing::MouseMode m)
2841 if (m == Editing::MouseGain) {
2842 /* always active in mouse gain */
2843 return true;
2846 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2847 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2850 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2851 : Drag (e, i),
2852 _line (0),
2853 _cumulative_y_drag (0)
2857 void
2858 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2860 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2861 assert (_line);
2863 _item = &_line->grab_item ();
2865 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2866 origin, and ditto for y.
2869 double cx = event->button.x;
2870 double cy = event->button.y;
2872 _line->parent_group().w2i (cx, cy);
2874 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2876 uint32_t before;
2877 uint32_t after;
2879 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2880 /* no adjacent points */
2881 return;
2884 Drag::start_grab (event, _editor->fader_cursor);
2886 /* store grab start in parent frame */
2888 _time_axis_view_grab_x = cx;
2889 _time_axis_view_grab_y = cy;
2891 double fraction = 1.0 - (cy / _line->height());
2893 _line->start_drag_line (before, after, fraction);
2895 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2896 event->button.x + 10, event->button.y + 10);
2898 _editor->show_verbose_canvas_cursor ();
2901 void
2902 LineDrag::motion (GdkEvent* event, bool)
2904 double dy = _drags->current_pointer_y() - last_pointer_y();
2906 if (event->button.state & Keyboard::SecondaryModifier) {
2907 dy *= 0.1;
2910 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2912 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2914 cy = max (0.0, cy);
2915 cy = min ((double) _line->height(), cy);
2917 double const fraction = 1.0 - (cy / _line->height());
2919 bool push;
2921 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2922 push = false;
2923 } else {
2924 push = true;
2927 /* we are ignoring x position for this drag, so we can just pass in anything */
2928 _line->drag_motion (0, fraction, true, push);
2930 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2933 void
2934 LineDrag::finished (GdkEvent* event, bool)
2936 motion (event, false);
2937 _line->end_drag ();
2940 void
2941 LineDrag::aborted ()
2943 _line->reset ();
2946 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2947 : Drag (e, i),
2948 _line (0),
2949 _cumulative_x_drag (0)
2953 void
2954 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2957 Drag::start_grab (event);
2959 _line = reinterpret_cast<SimpleLine*> (_item);
2960 assert (_line);
2962 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2964 double cx = event->button.x;
2965 double cy = event->button.y;
2967 _item->property_parent().get_value()->w2i(cx, cy);
2969 /* store grab start in parent frame */
2970 _region_view_grab_x = cx;
2972 _before = _line->property_x1();
2974 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2976 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2979 void
2980 FeatureLineDrag::motion (GdkEvent* event, bool)
2982 double dx = _drags->current_pointer_x() - last_pointer_x();
2984 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2986 _cumulative_x_drag += dx;
2988 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2990 if (cx > _max_x){
2991 cx = _max_x;
2993 else if(cx < 0){
2994 cx = 0;
2997 _line->property_x1() = cx;
2998 _line->property_x2() = cx;
3000 _before = _line->property_x1();
3003 void
3004 FeatureLineDrag::finished (GdkEvent* event, bool)
3006 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3007 _arv->update_transient(_before, _line->property_x1());
3010 void
3011 FeatureLineDrag::aborted ()
3013 //_line->reset ();
3016 void
3017 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3019 Drag::start_grab (event);
3020 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3023 void
3024 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3026 nframes64_t start;
3027 nframes64_t end;
3028 double y1;
3029 double y2;
3031 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3033 nframes64_t grab = grab_frame ();
3034 if (Config->get_rubberbanding_snaps_to_grid ()) {
3035 _editor->snap_to_with_modifier (grab, event);
3038 /* base start and end on initial click position */
3040 if (pf < grab) {
3041 start = pf;
3042 end = grab;
3043 } else {
3044 end = pf;
3045 start = grab;
3048 if (_drags->current_pointer_y() < grab_y()) {
3049 y1 = _drags->current_pointer_y();
3050 y2 = grab_y();
3051 } else {
3052 y2 = _drags->current_pointer_y();
3053 y1 = grab_y();
3057 if (start != end || y1 != y2) {
3059 double x1 = _editor->frame_to_pixel (start);
3060 double x2 = _editor->frame_to_pixel (end);
3062 _editor->rubberband_rect->property_x1() = x1;
3063 _editor->rubberband_rect->property_y1() = y1;
3064 _editor->rubberband_rect->property_x2() = x2;
3065 _editor->rubberband_rect->property_y2() = y2;
3067 _editor->rubberband_rect->show();
3068 _editor->rubberband_rect->raise_to_top();
3070 _editor->show_verbose_time_cursor (pf, 10);
3074 void
3075 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3077 if (movement_occurred) {
3079 motion (event, false);
3081 double y1,y2;
3082 if (_drags->current_pointer_y() < grab_y()) {
3083 y1 = _drags->current_pointer_y();
3084 y2 = grab_y();
3085 } else {
3086 y2 = _drags->current_pointer_y();
3087 y1 = grab_y();
3091 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3092 bool committed;
3094 _editor->begin_reversible_command (_("rubberband selection"));
3096 if (grab_frame() < last_pointer_frame()) {
3097 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3098 } else {
3099 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3102 if (!committed) {
3103 _editor->commit_reversible_command ();
3106 } else {
3107 if (!getenv("ARDOUR_SAE")) {
3108 _editor->selection->clear_tracks();
3110 _editor->selection->clear_regions();
3111 _editor->selection->clear_points ();
3112 _editor->selection->clear_lines ();
3115 _editor->rubberband_rect->hide();
3118 void
3119 RubberbandSelectDrag::aborted ()
3121 _editor->rubberband_rect->hide ();
3124 void
3125 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3127 Drag::start_grab (event);
3129 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3132 void
3133 TimeFXDrag::motion (GdkEvent* event, bool)
3135 RegionView* rv = _primary;
3137 nframes64_t const pf = adjusted_current_frame (event);
3139 if (pf > rv->region()->position()) {
3140 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3143 _editor->show_verbose_time_cursor (pf, 10);
3146 void
3147 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3149 _primary->get_time_axis_view().hide_timestretch ();
3151 if (!movement_occurred) {
3152 return;
3155 if (last_pointer_frame() < _primary->region()->position()) {
3156 /* backwards drag of the left edge - not usable */
3157 return;
3160 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3162 float percentage = (double) newlen / (double) _primary->region()->length();
3164 #ifndef USE_RUBBERBAND
3165 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3166 if (_primary->region()->data_type() == DataType::AUDIO) {
3167 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3169 #endif
3171 _editor->begin_reversible_command (_("timestretch"));
3173 // XXX how do timeFX on multiple regions ?
3175 RegionSelection rs;
3176 rs.add (_primary);
3178 if (_editor->time_stretch (rs, percentage) == -1) {
3179 error << _("An error occurred while executing time stretch operation") << endmsg;
3183 void
3184 TimeFXDrag::aborted ()
3186 _primary->get_time_axis_view().hide_timestretch ();
3190 void
3191 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3193 Drag::start_grab (event);
3196 void
3197 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3199 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3202 void
3203 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3205 if (movement_occurred && _editor->session()) {
3206 /* make sure we stop */
3207 _editor->session()->request_transport_speed (0.0);
3211 void
3212 ScrubDrag::aborted ()
3214 /* XXX: TODO */
3217 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3218 : Drag (e, i)
3219 , _operation (o)
3220 , _copy (false)
3221 , _original_pointer_time_axis (-1)
3222 , _last_pointer_time_axis (-1)
3227 void
3228 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3230 nframes64_t start = 0;
3231 nframes64_t end = 0;
3233 if (_editor->session() == 0) {
3234 return;
3237 Gdk::Cursor* cursor = 0;
3239 switch (_operation) {
3240 case CreateSelection:
3241 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3242 _copy = true;
3243 } else {
3244 _copy = false;
3246 cursor = _editor->selector_cursor;
3247 Drag::start_grab (event, cursor);
3248 break;
3250 case SelectionStartTrim:
3251 if (_editor->clicked_axisview) {
3252 _editor->clicked_axisview->order_selection_trims (_item, true);
3254 Drag::start_grab (event, _editor->left_side_trim_cursor);
3255 start = _editor->selection->time[_editor->clicked_selection].start;
3256 _pointer_frame_offset = grab_frame() - start;
3257 break;
3259 case SelectionEndTrim:
3260 if (_editor->clicked_axisview) {
3261 _editor->clicked_axisview->order_selection_trims (_item, false);
3263 Drag::start_grab (event, _editor->right_side_trim_cursor);
3264 end = _editor->selection->time[_editor->clicked_selection].end;
3265 _pointer_frame_offset = grab_frame() - end;
3266 break;
3268 case SelectionMove:
3269 start = _editor->selection->time[_editor->clicked_selection].start;
3270 Drag::start_grab (event, cursor);
3271 _pointer_frame_offset = grab_frame() - start;
3272 break;
3275 if (_operation == SelectionMove) {
3276 _editor->show_verbose_time_cursor (start, 10);
3277 } else {
3278 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3281 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3284 void
3285 SelectionDrag::motion (GdkEvent* event, bool first_move)
3287 nframes64_t start = 0;
3288 nframes64_t end = 0;
3289 nframes64_t length;
3291 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3292 if (pending_time_axis.first == 0) {
3293 return;
3296 nframes64_t const pending_position = adjusted_current_frame (event);
3298 /* only alter selection if things have changed */
3300 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3301 return;
3304 switch (_operation) {
3305 case CreateSelection:
3307 nframes64_t grab = grab_frame ();
3309 if (first_move) {
3310 _editor->snap_to (grab);
3313 if (pending_position < grab_frame()) {
3314 start = pending_position;
3315 end = grab;
3316 } else {
3317 end = pending_position;
3318 start = grab;
3321 /* first drag: Either add to the selection
3322 or create a new selection
3325 if (first_move) {
3327 if (_copy) {
3328 /* adding to the selection */
3329 _editor->selection->add (_editor->clicked_axisview);
3330 _editor->clicked_selection = _editor->selection->add (start, end);
3331 _copy = false;
3332 } else {
3333 /* new selection */
3335 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3336 _editor->selection->set (_editor->clicked_axisview);
3339 _editor->clicked_selection = _editor->selection->set (start, end);
3343 /* select the track that we're in */
3344 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3345 _editor->selection->add (pending_time_axis.first);
3346 _added_time_axes.push_back (pending_time_axis.first);
3349 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3350 tracks that we selected in the first place.
3353 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3354 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3356 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3357 while (i != _added_time_axes.end()) {
3359 list<TimeAxisView*>::iterator tmp = i;
3360 ++tmp;
3362 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3363 _editor->selection->remove (*i);
3364 _added_time_axes.remove (*i);
3367 i = tmp;
3371 break;
3373 case SelectionStartTrim:
3375 start = _editor->selection->time[_editor->clicked_selection].start;
3376 end = _editor->selection->time[_editor->clicked_selection].end;
3378 if (pending_position > end) {
3379 start = end;
3380 } else {
3381 start = pending_position;
3383 break;
3385 case SelectionEndTrim:
3387 start = _editor->selection->time[_editor->clicked_selection].start;
3388 end = _editor->selection->time[_editor->clicked_selection].end;
3390 if (pending_position < start) {
3391 end = start;
3392 } else {
3393 end = pending_position;
3396 break;
3398 case SelectionMove:
3400 start = _editor->selection->time[_editor->clicked_selection].start;
3401 end = _editor->selection->time[_editor->clicked_selection].end;
3403 length = end - start;
3405 start = pending_position;
3406 _editor->snap_to (start);
3408 end = start + length;
3410 break;
3413 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3414 _editor->start_canvas_autoscroll (1, 0);
3417 if (start != end) {
3418 _editor->selection->replace (_editor->clicked_selection, start, end);
3421 if (_operation == SelectionMove) {
3422 _editor->show_verbose_time_cursor(start, 10);
3423 } else {
3424 _editor->show_verbose_time_cursor(pending_position, 10);
3428 void
3429 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3431 Session* s = _editor->session();
3433 if (movement_occurred) {
3434 motion (event, false);
3435 /* XXX this is not object-oriented programming at all. ick */
3436 if (_editor->selection->time.consolidate()) {
3437 _editor->selection->TimeChanged ();
3440 /* XXX what if its a music time selection? */
3441 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3442 s->request_play_range (&_editor->selection->time, true);
3446 } else {
3447 /* just a click, no pointer movement.*/
3449 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3450 _editor->selection->clear_time();
3453 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3454 _editor->selection->set (_editor->clicked_axisview);
3457 if (s && s->get_play_range () && s->transport_rolling()) {
3458 s->request_stop (false, false);
3463 _editor->stop_canvas_autoscroll ();
3466 void
3467 SelectionDrag::aborted ()
3469 /* XXX: TODO */
3472 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3473 : Drag (e, i),
3474 _operation (o),
3475 _copy (false)
3477 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3478 _drag_rect->hide ();
3480 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3481 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3484 void
3485 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3487 if (_editor->session() == 0) {
3488 return;
3491 Gdk::Cursor* cursor = 0;
3493 if (!_editor->temp_location) {
3494 _editor->temp_location = new Location;
3497 switch (_operation) {
3498 case CreateRangeMarker:
3499 case CreateTransportMarker:
3500 case CreateCDMarker:
3502 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3503 _copy = true;
3504 } else {
3505 _copy = false;
3507 cursor = _editor->selector_cursor;
3508 break;
3511 Drag::start_grab (event, cursor);
3513 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3516 void
3517 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3519 nframes64_t start = 0;
3520 nframes64_t end = 0;
3521 ArdourCanvas::SimpleRect *crect;
3523 switch (_operation) {
3524 case CreateRangeMarker:
3525 crect = _editor->range_bar_drag_rect;
3526 break;
3527 case CreateTransportMarker:
3528 crect = _editor->transport_bar_drag_rect;
3529 break;
3530 case CreateCDMarker:
3531 crect = _editor->cd_marker_bar_drag_rect;
3532 break;
3533 default:
3534 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3535 return;
3536 break;
3539 nframes64_t const pf = adjusted_current_frame (event);
3541 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3542 nframes64_t grab = grab_frame ();
3543 _editor->snap_to (grab);
3545 if (pf < grab_frame()) {
3546 start = pf;
3547 end = grab;
3548 } else {
3549 end = pf;
3550 start = grab;
3553 /* first drag: Either add to the selection
3554 or create a new selection.
3557 if (first_move) {
3559 _editor->temp_location->set (start, end);
3561 crect->show ();
3563 update_item (_editor->temp_location);
3564 _drag_rect->show();
3565 //_drag_rect->raise_to_top();
3570 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3571 _editor->start_canvas_autoscroll (1, 0);
3574 if (start != end) {
3575 _editor->temp_location->set (start, end);
3577 double x1 = _editor->frame_to_pixel (start);
3578 double x2 = _editor->frame_to_pixel (end);
3579 crect->property_x1() = x1;
3580 crect->property_x2() = x2;
3582 update_item (_editor->temp_location);
3585 _editor->show_verbose_time_cursor (pf, 10);
3589 void
3590 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3592 Location * newloc = 0;
3593 string rangename;
3594 int flags;
3596 if (movement_occurred) {
3597 motion (event, false);
3598 _drag_rect->hide();
3600 switch (_operation) {
3601 case CreateRangeMarker:
3602 case CreateCDMarker:
3604 _editor->begin_reversible_command (_("new range marker"));
3605 XMLNode &before = _editor->session()->locations()->get_state();
3606 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3607 if (_operation == CreateCDMarker) {
3608 flags = Location::IsRangeMarker | Location::IsCDMarker;
3609 _editor->cd_marker_bar_drag_rect->hide();
3611 else {
3612 flags = Location::IsRangeMarker;
3613 _editor->range_bar_drag_rect->hide();
3615 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3616 _editor->session()->locations()->add (newloc, true);
3617 XMLNode &after = _editor->session()->locations()->get_state();
3618 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3619 _editor->commit_reversible_command ();
3620 break;
3623 case CreateTransportMarker:
3624 // popup menu to pick loop or punch
3625 _editor->new_transport_marker_context_menu (&event->button, _item);
3626 break;
3628 } else {
3629 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3631 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3633 nframes64_t start;
3634 nframes64_t end;
3636 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3638 if (end == max_frames) {
3639 end = _editor->session()->current_end_frame ();
3642 if (start == max_frames) {
3643 start = _editor->session()->current_start_frame ();
3646 switch (_editor->mouse_mode) {
3647 case MouseObject:
3648 /* find the two markers on either side and then make the selection from it */
3649 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3650 break;
3652 case MouseRange:
3653 /* find the two markers on either side of the click and make the range out of it */
3654 _editor->selection->set (start, end);
3655 break;
3657 default:
3658 break;
3663 _editor->stop_canvas_autoscroll ();
3666 void
3667 RangeMarkerBarDrag::aborted ()
3669 /* XXX: TODO */
3672 void
3673 RangeMarkerBarDrag::update_item (Location* location)
3675 double const x1 = _editor->frame_to_pixel (location->start());
3676 double const x2 = _editor->frame_to_pixel (location->end());
3678 _drag_rect->property_x1() = x1;
3679 _drag_rect->property_x2() = x2;
3682 void
3683 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3685 Drag::start_grab (event, _editor->zoom_cursor);
3686 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3689 void
3690 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3692 nframes64_t start;
3693 nframes64_t end;
3695 nframes64_t const pf = adjusted_current_frame (event);
3697 nframes64_t grab = grab_frame ();
3698 _editor->snap_to_with_modifier (grab, event);
3700 /* base start and end on initial click position */
3701 if (pf < grab) {
3702 start = pf;
3703 end = grab;
3704 } else {
3705 end = pf;
3706 start = grab;
3709 if (start != end) {
3711 if (first_move) {
3712 _editor->zoom_rect->show();
3713 _editor->zoom_rect->raise_to_top();
3716 _editor->reposition_zoom_rect(start, end);
3718 _editor->show_verbose_time_cursor (pf, 10);
3722 void
3723 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3725 if (movement_occurred) {
3726 motion (event, false);
3728 if (grab_frame() < last_pointer_frame()) {
3729 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3730 } else {
3731 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3733 } else {
3734 _editor->temporal_zoom_to_frame (false, grab_frame());
3736 temporal_zoom_step (false);
3737 center_screen (grab_frame());
3741 _editor->zoom_rect->hide();
3744 void
3745 MouseZoomDrag::aborted ()
3747 _editor->zoom_rect->hide ();
3750 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3751 : Drag (e, i)
3753 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3754 region = &cnote->region_view();
3757 void
3758 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3760 Drag::start_grab (event);
3762 drag_delta_x = 0;
3763 drag_delta_note = 0;
3765 double event_x;
3766 double event_y;
3768 event_x = _drags->current_pointer_x();
3769 event_y = _drags->current_pointer_y();
3771 _item->property_parent().get_value()->w2i(event_x, event_y);
3773 last_x = region->snap_to_pixel(event_x);
3774 last_y = event_y;
3776 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3778 if (!(was_selected = cnote->selected())) {
3780 /* tertiary-click means extend selection - we'll do that on button release,
3781 so don't add it here, because otherwise we make it hard to figure
3782 out the "extend-to" range.
3785 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3787 if (!extend) {
3788 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3790 if (add) {
3791 region->note_selected (cnote, true);
3792 } else {
3793 region->unique_select (cnote);
3799 void
3800 NoteDrag::motion (GdkEvent*, bool)
3802 MidiStreamView* streamview = region->midi_stream_view();
3803 double event_x;
3804 double event_y;
3806 event_x = _drags->current_pointer_x();
3807 event_y = _drags->current_pointer_y();
3809 _item->property_parent().get_value()->w2i(event_x, event_y);
3811 event_x = region->snap_to_pixel(event_x);
3813 double dx = event_x - last_x;
3814 double dy = event_y - last_y;
3815 last_x = event_x;
3817 drag_delta_x += dx;
3819 // Snap to note rows
3821 if (abs (dy) < streamview->note_height()) {
3822 dy = 0.0;
3823 } else {
3824 int8_t this_delta_note;
3825 if (dy > 0) {
3826 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3827 } else {
3828 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3830 drag_delta_note -= this_delta_note;
3831 dy = streamview->note_height() * this_delta_note;
3832 last_y = last_y + dy;
3835 if (dx || dy) {
3837 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3838 Evoral::MusicalTime new_time;
3840 if (drag_delta_x) {
3841 nframes64_t start_frames = region->beats_to_frames(cnote->note()->time());
3842 if (drag_delta_x >= 0) {
3843 start_frames += region->snap_frame_to_frame(_editor->pixel_to_frame(drag_delta_x));
3844 } else {
3845 start_frames -= region->snap_frame_to_frame(_editor->pixel_to_frame(-drag_delta_x));
3847 new_time = region->frames_to_beats(start_frames);
3848 } else {
3849 new_time = cnote->note()->time();
3852 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > check_note (
3853 new Evoral::Note<Evoral::MusicalTime> (cnote->note()->channel(),
3854 new_time,
3855 cnote->note()->length(),
3856 cnote->note()->note() + drag_delta_note,
3857 cnote->note()->velocity()));
3859 region->move_selection (dx, dy);
3861 char buf[12];
3862 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (cnote->note()->note()).c_str(),
3863 (int) floor ((cnote->note()->note() + drag_delta_note)));
3864 _editor->show_verbose_canvas_cursor_with (buf);
3868 void
3869 NoteDrag::finished (GdkEvent* ev, bool moved)
3871 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3873 if (!moved) {
3874 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3876 if (was_selected) {
3877 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3878 if (add) {
3879 region->note_deselected (cnote);
3881 } else {
3882 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3883 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3885 if (!extend && !add && region->selection_size() > 1) {
3886 region->unique_select(cnote);
3887 } else if (extend) {
3888 region->note_selected (cnote, true, true);
3889 } else {
3890 /* it was added during button press */
3894 } else {
3895 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3899 void
3900 NoteDrag::aborted ()
3902 /* XXX: TODO */
3905 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3906 : Drag (e, i)
3907 , _ranges (r)
3908 , _nothing_to_drag (false)
3910 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3911 assert (_atav);
3913 _line = _atav->line ();
3916 void
3917 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3919 Drag::start_grab (event, cursor);
3921 list<ControlPoint*> points;
3923 XMLNode* state = &_line->get_state ();
3925 if (_ranges.empty()) {
3927 uint32_t const N = _line->npoints ();
3928 for (uint32_t i = 0; i < N; ++i) {
3929 points.push_back (_line->nth (i));
3932 } else {
3934 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3935 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3937 /* fade into and out of the region that we're dragging;
3938 64 samples length plucked out of thin air.
3940 nframes64_t const h = (j->start + j->end) / 2;
3941 nframes64_t a = j->start + 64;
3942 if (a > h) {
3943 a = h;
3945 nframes64_t b = j->end - 64;
3946 if (b < h) {
3947 b = h;
3950 the_list->add (j->start, the_list->eval (j->start));
3951 _line->add_always_in_view (j->start);
3952 the_list->add (a, the_list->eval (a));
3953 _line->add_always_in_view (a);
3954 the_list->add (b, the_list->eval (b));
3955 _line->add_always_in_view (b);
3956 the_list->add (j->end, the_list->eval (j->end));
3957 _line->add_always_in_view (j->end);
3960 uint32_t const N = _line->npoints ();
3961 for (uint32_t i = 0; i < N; ++i) {
3963 ControlPoint* p = _line->nth (i);
3965 list<AudioRange>::const_iterator j = _ranges.begin ();
3966 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3967 ++j;
3970 if (j != _ranges.end()) {
3971 points.push_back (p);
3976 if (points.empty()) {
3977 _nothing_to_drag = true;
3978 return;
3981 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3984 void
3985 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3987 if (_nothing_to_drag) {
3988 return;
3991 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3993 /* we are ignoring x position for this drag, so we can just pass in anything */
3994 _line->drag_motion (0, f, true, false);
3997 void
3998 AutomationRangeDrag::finished (GdkEvent* event, bool)
4000 if (_nothing_to_drag) {
4001 return;
4004 motion (event, false);
4005 _line->end_drag ();
4006 _line->clear_always_in_view ();
4009 void
4010 AutomationRangeDrag::aborted ()
4012 _line->clear_always_in_view ();
4013 _line->reset ();
4016 DraggingView::DraggingView (RegionView* v)
4017 : view (v)
4019 initial_y = v->get_canvas_group()->property_y ();