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.
21 #include "gtk2ardour-config.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/session.h"
33 #include "ardour/dB.h"
34 #include "ardour/region_factory.h"
35 #include "ardour/operations.h"
40 #include "audio_region_view.h"
41 #include "midi_region_view.h"
42 #include "ardour_ui.h"
43 #include "gui_thread.h"
44 #include "control_point.h"
46 #include "region_gain_line.h"
47 #include "editor_drag.h"
48 #include "audio_time_axis.h"
49 #include "midi_time_axis.h"
50 #include "canvas-note.h"
51 #include "selection.h"
52 #include "midi_selection.h"
53 #include "automation_time_axis.h"
55 #include "editor_cursors.h"
56 #include "mouse_cursors.h"
57 #include "verbose_cursor.h"
60 using namespace ARDOUR
;
63 using namespace Gtkmm2ext
;
64 using namespace Editing
;
65 using namespace ArdourCanvas
;
67 using Gtkmm2ext::Keyboard
;
69 double const ControlPointDrag::_zero_gain_fraction
= gain_to_slider_position (dB_to_coefficient (0.0));
71 DragManager::DragManager (Editor
* e
)
74 , _current_pointer_frame (0)
79 DragManager::~DragManager ()
84 /** Call abort for each active drag */
90 for (list
<Drag
*>::const_iterator i
= _drags
.begin(); i
!= _drags
.end(); ++i
) {
95 if (!_drags
.empty ()) {
96 _editor
->set_follow_playhead (_old_follow_playhead
, false);
105 DragManager::add (Drag
* d
)
107 d
->set_manager (this);
108 _drags
.push_back (d
);
112 DragManager::set (Drag
* d
, GdkEvent
* e
, Gdk::Cursor
* c
)
114 d
->set_manager (this);
115 _drags
.push_back (d
);
120 DragManager::start_grab (GdkEvent
* e
, Gdk::Cursor
* c
)
122 /* Prevent follow playhead during the drag to be nice to the user */
123 _old_follow_playhead
= _editor
->follow_playhead ();
124 _editor
->set_follow_playhead (false);
126 _current_pointer_frame
= _editor
->event_frame (e
, &_current_pointer_x
, &_current_pointer_y
);
128 for (list
<Drag
*>::const_iterator i
= _drags
.begin(); i
!= _drags
.end(); ++i
) {
129 (*i
)->start_grab (e
, c
);
133 /** Call end_grab for each active drag.
134 * @return true if any drag reported movement having occurred.
137 DragManager::end_grab (GdkEvent
* e
)
142 for (list
<Drag
*>::iterator i
= _drags
.begin(); i
!= _drags
.end(); ++i
) {
143 bool const t
= (*i
)->end_grab (e
);
154 _editor
->set_follow_playhead (_old_follow_playhead
, false);
160 DragManager::motion_handler (GdkEvent
* e
, bool from_autoscroll
)
164 _current_pointer_frame
= _editor
->event_frame (e
, &_current_pointer_x
, &_current_pointer_y
);
166 for (list
<Drag
*>::iterator i
= _drags
.begin(); i
!= _drags
.end(); ++i
) {
167 bool const t
= (*i
)->motion_handler (e
, from_autoscroll
);
178 DragManager::have_item (ArdourCanvas::Item
* i
) const
180 list
<Drag
*>::const_iterator j
= _drags
.begin ();
181 while (j
!= _drags
.end() && (*j
)->item () != i
) {
185 return j
!= _drags
.end ();
188 Drag::Drag (Editor
* e
, ArdourCanvas::Item
* i
)
191 , _pointer_frame_offset (0)
192 , _move_threshold_passed (false)
193 , _raw_grab_frame (0)
195 , _last_pointer_frame (0)
201 Drag::swap_grab (ArdourCanvas::Item
* new_item
, Gdk::Cursor
* cursor
, uint32_t time
)
207 cursor
= _editor
->which_grabber_cursor ();
210 _item
->grab (Gdk::POINTER_MOTION_MASK
| Gdk::BUTTON_PRESS_MASK
| Gdk::BUTTON_RELEASE_MASK
, *cursor
, time
);
214 Drag::start_grab (GdkEvent
* event
, Gdk::Cursor
*cursor
)
217 cursor
= _editor
->which_grabber_cursor ();
220 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
222 if (Keyboard::is_button2_event (&event
->button
)) {
223 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::SecondaryModifier
)) {
224 _y_constrained
= true;
225 _x_constrained
= false;
227 _y_constrained
= false;
228 _x_constrained
= true;
231 _x_constrained
= false;
232 _y_constrained
= false;
235 _raw_grab_frame
= _editor
->event_frame (event
, &_grab_x
, &_grab_y
);
236 setup_pointer_frame_offset ();
237 _grab_frame
= adjusted_frame (_raw_grab_frame
, event
);
238 _last_pointer_frame
= _grab_frame
;
239 _last_pointer_x
= _grab_x
;
240 _last_pointer_y
= _grab_y
;
242 _item
->grab (Gdk::POINTER_MOTION_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
,
246 if (_editor
->session() && _editor
->session()->transport_rolling()) {
249 _was_rolling
= false;
252 switch (_editor
->snap_type()) {
253 case SnapToRegionStart
:
254 case SnapToRegionEnd
:
255 case SnapToRegionSync
:
256 case SnapToRegionBoundary
:
257 _editor
->build_region_boundary_cache ();
264 /** Call to end a drag `successfully'. Ungrabs item and calls
265 * subclass' finished() method.
267 * @param event GDK event, or 0.
268 * @return true if some movement occurred, otherwise false.
271 Drag::end_grab (GdkEvent
* event
)
273 _editor
->stop_canvas_autoscroll ();
275 _item
->ungrab (event
? event
->button
.time
: 0);
277 finished (event
, _move_threshold_passed
);
279 _editor
->verbose_cursor()->hide ();
281 return _move_threshold_passed
;
285 Drag::adjusted_frame (framepos_t f
, GdkEvent
const * event
, bool snap
) const
289 if (f
> _pointer_frame_offset
) {
290 pos
= f
- _pointer_frame_offset
;
294 _editor
->snap_to_with_modifier (pos
, event
);
301 Drag::adjusted_current_frame (GdkEvent
const * event
, bool snap
) const
303 return adjusted_frame (_drags
->current_pointer_frame (), event
, snap
);
307 Drag::motion_handler (GdkEvent
* event
, bool from_autoscroll
)
309 /* check to see if we have moved in any way that matters since the last motion event */
310 if (_move_threshold_passed
&&
311 (!x_movement_matters() || _last_pointer_frame
== adjusted_current_frame (event
)) &&
312 (!y_movement_matters() || _last_pointer_y
== _drags
->current_pointer_y ()) ) {
316 pair
<framecnt_t
, int> const threshold
= move_threshold ();
318 bool const old_move_threshold_passed
= _move_threshold_passed
;
320 if (!from_autoscroll
&& !_move_threshold_passed
) {
322 bool const xp
= (::llabs (_drags
->current_pointer_frame () - _raw_grab_frame
) >= threshold
.first
);
323 bool const yp
= (::fabs ((_drags
->current_pointer_y () - _grab_y
)) >= threshold
.second
);
325 _move_threshold_passed
= ((xp
&& x_movement_matters()) || (yp
&& y_movement_matters()));
328 if (active (_editor
->mouse_mode
) && _move_threshold_passed
) {
330 if (event
->motion
.state
& Gdk::BUTTON1_MASK
|| event
->motion
.state
& Gdk::BUTTON2_MASK
) {
331 if (!from_autoscroll
) {
332 _editor
->maybe_autoscroll (true, allow_vertical_autoscroll ());
335 motion (event
, _move_threshold_passed
!= old_move_threshold_passed
);
337 _last_pointer_x
= _drags
->current_pointer_x ();
338 _last_pointer_y
= _drags
->current_pointer_y ();
339 _last_pointer_frame
= adjusted_current_frame (event
);
347 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
355 aborted (_move_threshold_passed
);
357 _editor
->stop_canvas_autoscroll ();
358 _editor
->verbose_cursor()->hide ();
362 Drag::show_verbose_cursor_time (framepos_t frame
)
364 _editor
->verbose_cursor()->set_time (
366 _drags
->current_pointer_x() + 10 - _editor
->horizontal_position(),
367 _drags
->current_pointer_y() + 10 - _editor
->vertical_adjustment
.get_value() + _editor
->canvas_timebars_vsize
370 _editor
->verbose_cursor()->show ();
374 Drag::show_verbose_cursor_duration (framepos_t start
, framepos_t end
, double xoffset
)
376 _editor
->verbose_cursor()->show (xoffset
);
378 _editor
->verbose_cursor()->set_duration (
380 _drags
->current_pointer_x() + 10 - _editor
->horizontal_position(),
381 _drags
->current_pointer_y() + 10 - _editor
->vertical_adjustment
.get_value() + _editor
->canvas_timebars_vsize
386 Drag::show_verbose_cursor_text (string
const & text
)
388 _editor
->verbose_cursor()->show ();
390 _editor
->verbose_cursor()->set (
392 _drags
->current_pointer_x() + 10 - _editor
->horizontal_position(),
393 _drags
->current_pointer_y() + 10 - _editor
->vertical_adjustment
.get_value() + _editor
->canvas_timebars_vsize
398 struct EditorOrderTimeAxisViewSorter
{
399 bool operator() (TimeAxisView
* a
, TimeAxisView
* b
) {
400 RouteTimeAxisView
* ra
= dynamic_cast<RouteTimeAxisView
*> (a
);
401 RouteTimeAxisView
* rb
= dynamic_cast<RouteTimeAxisView
*> (b
);
403 return ra
->route()->order_key (N_ ("editor")) < rb
->route()->order_key (N_ ("editor"));
407 RegionDrag::RegionDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
411 _editor
->visible_order_range (&_visible_y_low
, &_visible_y_high
);
413 /* Make a list of non-hidden tracks to refer to during the drag */
415 TrackViewList track_views
= _editor
->track_views
;
416 track_views
.sort (EditorOrderTimeAxisViewSorter ());
418 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
419 if (!(*i
)->hidden()) {
421 _time_axis_views
.push_back (*i
);
423 TimeAxisView::Children children_list
= (*i
)->get_child_list ();
424 for (TimeAxisView::Children::iterator j
= children_list
.begin(); j
!= children_list
.end(); ++j
) {
425 _time_axis_views
.push_back (j
->get());
430 /* the list of views can be empty at this point if this is a region list-insert drag
433 for (list
<RegionView
*>::const_iterator i
= v
.begin(); i
!= v
.end(); ++i
) {
434 _views
.push_back (DraggingView (*i
, this));
437 RegionView::RegionViewGoingAway
.connect (death_connection
, invalidator (*this), ui_bind (&RegionDrag::region_going_away
, this, _1
), gui_context());
441 RegionDrag::region_going_away (RegionView
* v
)
443 list
<DraggingView
>::iterator i
= _views
.begin ();
444 while (i
!= _views
.end() && i
->view
!= v
) {
448 if (i
!= _views
.end()) {
453 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
455 RegionDrag::find_time_axis_view (TimeAxisView
* t
) const
458 int const N
= _time_axis_views
.size ();
459 while (i
< N
&& _time_axis_views
[i
] != t
) {
470 RegionMotionDrag::RegionMotionDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
, bool b
)
471 : RegionDrag (e
, i
, p
, v
),
480 RegionMotionDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
482 Drag::start_grab (event
, cursor
);
484 show_verbose_cursor_time (_last_frame_position
);
486 pair
<TimeAxisView
*, int> const tv
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
487 _last_pointer_time_axis_view
= find_time_axis_view (tv
.first
);
488 _last_pointer_layer
= tv
.first
->layer_display() == Overlaid
? 0 : tv
.second
;
492 RegionMotionDrag::compute_x_delta (GdkEvent
const * event
, framepos_t
* pending_region_position
)
494 /* compute the amount of pointer motion in frames, and where
495 the region would be if we moved it by that much.
497 *pending_region_position
= adjusted_current_frame (event
);
499 framepos_t sync_frame
;
500 framecnt_t sync_offset
;
503 sync_offset
= _primary
->region()->sync_offset (sync_dir
);
505 /* we don't handle a sync point that lies before zero.
507 if (sync_dir
>= 0 || (sync_dir
< 0 && *pending_region_position
>= sync_offset
)) {
509 sync_frame
= *pending_region_position
+ (sync_dir
*sync_offset
);
511 _editor
->snap_to_with_modifier (sync_frame
, event
);
513 *pending_region_position
= _primary
->region()->adjust_to_sync (sync_frame
);
516 *pending_region_position
= _last_frame_position
;
519 if (*pending_region_position
> max_framepos
- _primary
->region()->length()) {
520 *pending_region_position
= _last_frame_position
;
525 /* in locked edit mode, reverse the usual meaning of _x_constrained */
526 bool const x_move_allowed
= Config
->get_edit_mode() == Lock
? _x_constrained
: !_x_constrained
;
528 if ((*pending_region_position
!= _last_frame_position
) && x_move_allowed
) {
530 /* x movement since last time (in pixels) */
531 dx
= (static_cast<double> (*pending_region_position
) - _last_frame_position
) / _editor
->frames_per_unit
;
533 /* total x movement */
534 framecnt_t total_dx
= *pending_region_position
;
535 if (regions_came_from_canvas()) {
536 total_dx
= total_dx
- grab_frame ();
539 /* check that no regions have gone off the start of the session */
540 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
541 if ((i
->view
->region()->position() + total_dx
) < 0) {
543 *pending_region_position
= _last_frame_position
;
548 _last_frame_position
= *pending_region_position
;
555 RegionMotionDrag::y_movement_allowed (int delta_track
, layer_t delta_layer
) const
557 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
558 int const n
= i
->time_axis_view
+ delta_track
;
559 if (n
< 0 || n
>= int (_time_axis_views
.size())) {
560 /* off the top or bottom track */
564 RouteTimeAxisView
const * to
= dynamic_cast<RouteTimeAxisView
const *> (_time_axis_views
[n
]);
565 if (to
== 0 || !to
->is_track() || to
->track()->data_type() != i
->view
->region()->data_type()) {
566 /* not a track, or the wrong type */
570 int const l
= i
->layer
+ delta_layer
;
571 if (delta_track
== 0 && (l
< 0 || l
>= int (to
->view()->layers()))) {
572 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
573 If it has, the layers will be munged later anyway, so it's ok.
579 /* all regions being dragged are ok with this change */
584 RegionMotionDrag::motion (GdkEvent
* event
, bool first_move
)
586 assert (!_views
.empty ());
588 /* Find the TimeAxisView that the pointer is now over */
589 pair
<TimeAxisView
*, int> const tv
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
591 /* Bail early if we're not over a track */
592 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tv
.first
);
593 if (!rtv
|| !rtv
->is_track()) {
594 _editor
->verbose_cursor()->hide ();
598 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
600 /* Here's the current pointer position in terms of time axis view and layer */
601 int const current_pointer_time_axis_view
= find_time_axis_view (tv
.first
);
602 layer_t
const current_pointer_layer
= tv
.first
->layer_display() == Overlaid
? 0 : tv
.second
;
604 /* Work out the change in x */
605 framepos_t pending_region_position
;
606 double const x_delta
= compute_x_delta (event
, &pending_region_position
);
608 /* Work out the change in y */
609 int delta_time_axis_view
= current_pointer_time_axis_view
- _last_pointer_time_axis_view
;
610 int delta_layer
= current_pointer_layer
- _last_pointer_layer
;
612 if (!y_movement_allowed (delta_time_axis_view
, delta_layer
)) {
613 /* this y movement is not allowed, so do no y movement this time */
614 delta_time_axis_view
= 0;
618 if (x_delta
== 0 && delta_time_axis_view
== 0 && delta_layer
== 0 && !first_move
) {
619 /* haven't reached next snap point, and we're not switching
620 trackviews nor layers. nothing to do.
625 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
627 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
629 RegionView
* rv
= i
->view
;
631 if (rv
->region()->locked()) {
637 /* here we are calculating the y distance from the
638 top of the first track view to the top of the region
639 area of the track view that we're working on */
641 /* this x value is just a dummy value so that we have something
646 /* distance from the top of this track view to the region area
647 of our track view is always 1 */
651 /* convert to world coordinates, ie distance from the top of
654 rv
->get_canvas_frame()->i2w (ix1
, iy1
);
656 /* compensate for the ruler section and the vertical scrollbar position */
657 iy1
+= _editor
->get_trackview_group_vertical_offset ();
659 // hide any dependent views
661 rv
->get_time_axis_view().hide_dependent_views (*rv
);
664 reparent to a non scrolling group so that we can keep the
665 region selection above all time axis views.
666 reparenting means we have to move the rv as the two
667 parent groups have different coordinates.
670 rv
->get_canvas_group()->property_y() = iy1
- 1;
671 rv
->get_canvas_group()->reparent (*(_editor
->_region_motion_group
));
673 rv
->fake_set_opaque (true);
676 /* Work out the change in y position of this region view */
680 /* If we have moved tracks, we'll fudge the layer delta so that the
681 region gets moved back onto layer 0 on its new track; this avoids
682 confusion when dragging regions from non-zero layers onto different
685 int this_delta_layer
= delta_layer
;
686 if (delta_time_axis_view
!= 0) {
687 this_delta_layer
= - i
->layer
;
690 /* Move this region to layer 0 on its old track */
691 StreamView
* lv
= _time_axis_views
[i
->time_axis_view
]->view ();
692 if (lv
->layer_display() == Stacked
) {
693 y_delta
-= (lv
->layers() - i
->layer
- 1) * lv
->child_height ();
696 /* Now move it to its right layer on the current track */
697 StreamView
* cv
= _time_axis_views
[i
->time_axis_view
+ delta_time_axis_view
]->view ();
698 if (cv
->layer_display() == Stacked
) {
699 y_delta
+= (cv
->layers() - (i
->layer
+ this_delta_layer
) - 1) * cv
->child_height ();
703 if (delta_time_axis_view
> 0) {
704 for (int j
= 0; j
< delta_time_axis_view
; ++j
) {
705 y_delta
+= _time_axis_views
[i
->time_axis_view
+ j
]->current_height ();
708 /* start by subtracting the height of the track above where we are now */
709 for (int j
= 1; j
<= -delta_time_axis_view
; ++j
) {
710 y_delta
-= _time_axis_views
[i
->time_axis_view
- j
]->current_height ();
715 rv
->set_height (_time_axis_views
[i
->time_axis_view
+ delta_time_axis_view
]->view()->child_height ());
717 /* Update the DraggingView */
718 i
->time_axis_view
+= delta_time_axis_view
;
719 i
->layer
+= this_delta_layer
;
722 _editor
->mouse_brush_insert_region (rv
, pending_region_position
);
724 rv
->move (x_delta
, y_delta
);
727 } /* foreach region */
729 _total_x_delta
+= x_delta
;
732 _editor
->cursor_group
->raise_to_top();
735 if (x_delta
!= 0 && !_brushing
) {
736 show_verbose_cursor_time (_last_frame_position
);
739 _last_pointer_time_axis_view
+= delta_time_axis_view
;
740 _last_pointer_layer
+= delta_layer
;
744 RegionMoveDrag::motion (GdkEvent
* event
, bool first_move
)
746 if (_copy
&& first_move
) {
748 /* duplicate the regionview(s) and region(s) */
750 list
<DraggingView
> new_regionviews
;
752 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
754 RegionView
* rv
= i
->view
;
755 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(rv
);
756 MidiRegionView
* mrv
= dynamic_cast<MidiRegionView
*>(rv
);
758 const boost::shared_ptr
<const Region
> original
= rv
->region();
759 boost::shared_ptr
<Region
> region_copy
= RegionFactory::create (original
, true);
760 region_copy
->set_position (original
->position(), this);
764 boost::shared_ptr
<AudioRegion
> audioregion_copy
765 = boost::dynamic_pointer_cast
<AudioRegion
>(region_copy
);
767 nrv
= new AudioRegionView (*arv
, audioregion_copy
);
769 boost::shared_ptr
<MidiRegion
> midiregion_copy
770 = boost::dynamic_pointer_cast
<MidiRegion
>(region_copy
);
771 nrv
= new MidiRegionView (*mrv
, midiregion_copy
);
776 nrv
->get_canvas_group()->show ();
777 new_regionviews
.push_back (DraggingView (nrv
, this));
779 /* swap _primary to the copy */
781 if (rv
== _primary
) {
785 /* ..and deselect the one we copied */
787 rv
->set_selected (false);
790 if (!new_regionviews
.empty()) {
792 /* reflect the fact that we are dragging the copies */
794 _views
= new_regionviews
;
796 swap_grab (new_regionviews
.front().view
->get_canvas_group (), 0, event
? event
->motion
.time
: 0);
799 sync the canvas to what we think is its current state
800 without it, the canvas seems to
801 "forget" to update properly after the upcoming reparent()
802 ..only if the mouse is in rapid motion at the time of the grab.
803 something to do with regionview creation taking so long?
805 _editor
->update_canvas_now();
809 RegionMotionDrag::motion (event
, first_move
);
813 RegionMoveDrag::finished (GdkEvent
*, bool movement_occurred
)
815 if (!movement_occurred
) {
820 /* reverse this here so that we have the correct logic to finalize
824 if (Config
->get_edit_mode() == Lock
) {
825 _x_constrained
= !_x_constrained
;
828 assert (!_views
.empty ());
830 bool const changed_position
= (_last_frame_position
!= _primary
->region()->position());
831 bool const changed_tracks
= (_time_axis_views
[_views
.front().time_axis_view
] != &_views
.front().view
->get_time_axis_view());
832 framecnt_t
const drag_delta
= _primary
->region()->position() - _last_frame_position
;
834 _editor
->update_canvas_now ();
856 RegionMoveDrag::finished_copy (bool const changed_position
, bool const /*changed_tracks*/, framecnt_t
const drag_delta
)
858 RegionSelection new_views
;
859 PlaylistSet modified_playlists
;
860 list
<RegionView
*> views_to_delete
;
863 /* all changes were made during motion event handlers */
865 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
869 _editor
->commit_reversible_command ();
873 if (_x_constrained
) {
874 _editor
->begin_reversible_command (Operations::fixed_time_region_copy
);
876 _editor
->begin_reversible_command (Operations::region_copy
);
879 /* insert the regions into their new playlists */
880 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
882 if (i
->view
->region()->locked()) {
888 if (changed_position
&& !_x_constrained
) {
889 where
= i
->view
->region()->position() - drag_delta
;
891 where
= i
->view
->region()->position();
894 RegionView
* new_view
= insert_region_into_playlist (
895 i
->view
->region(), dynamic_cast<RouteTimeAxisView
*> (_time_axis_views
[i
->time_axis_view
]), i
->layer
, where
, modified_playlists
902 new_views
.push_back (new_view
);
904 /* we don't need the copied RegionView any more */
905 views_to_delete
.push_back (i
->view
);
908 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
909 because when views are deleted they are automagically removed from _views, which messes
912 for (list
<RegionView
*>::iterator i
= views_to_delete
.begin(); i
!= views_to_delete
.end(); ++i
) {
916 /* If we've created new regions either by copying or moving
917 to a new track, we want to replace the old selection with the new ones
920 if (new_views
.size() > 0) {
921 _editor
->selection
->set (new_views
);
924 /* write commands for the accumulated diffs for all our modified playlists */
925 add_stateful_diff_commands_for_playlists (modified_playlists
);
927 _editor
->commit_reversible_command ();
931 RegionMoveDrag::finished_no_copy (
932 bool const changed_position
,
933 bool const changed_tracks
,
934 framecnt_t
const drag_delta
937 RegionSelection new_views
;
938 PlaylistSet modified_playlists
;
939 PlaylistSet frozen_playlists
;
942 /* all changes were made during motion event handlers */
943 _editor
->commit_reversible_command ();
947 if (_x_constrained
) {
948 _editor
->begin_reversible_command (_("fixed time region drag"));
950 _editor
->begin_reversible_command (Operations::region_drag
);
953 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ) {
955 RegionView
* rv
= i
->view
;
957 RouteTimeAxisView
* const dest_rtv
= dynamic_cast<RouteTimeAxisView
*> (_time_axis_views
[i
->time_axis_view
]);
958 layer_t
const dest_layer
= i
->layer
;
960 if (rv
->region()->locked()) {
967 if (changed_position
&& !_x_constrained
) {
968 where
= rv
->region()->position() - drag_delta
;
970 where
= rv
->region()->position();
973 if (changed_tracks
) {
975 /* insert into new playlist */
977 RegionView
* new_view
= insert_region_into_playlist (
978 RegionFactory::create (rv
->region (), true), dest_rtv
, dest_layer
, where
, modified_playlists
986 new_views
.push_back (new_view
);
988 /* remove from old playlist */
990 /* the region that used to be in the old playlist is not
991 moved to the new one - we use a copy of it. as a result,
992 any existing editor for the region should no longer be
995 rv
->hide_region_editor();
996 rv
->fake_set_opaque (false);
998 remove_region_from_playlist (rv
->region(), i
->initial_playlist
, modified_playlists
);
1002 rv
->region()->clear_changes ();
1005 motion on the same track. plonk the previously reparented region
1006 back to its original canvas group (its streamview).
1007 No need to do anything for copies as they are fake regions which will be deleted.
1010 rv
->get_canvas_group()->reparent (*dest_rtv
->view()->canvas_item());
1011 rv
->get_canvas_group()->property_y() = i
->initial_y
;
1012 rv
->get_time_axis_view().reveal_dependent_views (*rv
);
1014 /* just change the model */
1016 boost::shared_ptr
<Playlist
> playlist
= dest_rtv
->playlist();
1018 if (dest_rtv
->view()->layer_display() == Stacked
) {
1019 rv
->region()->set_layer (dest_layer
);
1020 rv
->region()->set_pending_explicit_relayer (true);
1023 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1025 pair
<PlaylistSet::iterator
, bool> r
= frozen_playlists
.insert (playlist
);
1028 playlist
->freeze ();
1031 /* this movement may result in a crossfade being modified, so we need to get undo
1032 data from the playlist as well as the region.
1035 r
= modified_playlists
.insert (playlist
);
1037 playlist
->clear_changes ();
1040 rv
->region()->set_position (where
, (void*) this);
1042 _editor
->session()->add_command (new StatefulDiffCommand (rv
->region()));
1045 if (changed_tracks
) {
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 _views (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 _views is now empty).
1053 This could have invalidated any and all iterators into _views.
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 _views hasn't been modified and
1061 we can just iterate.
1065 if (_views
.empty()) {
1076 /* If we've created new regions either by copying or moving
1077 to a new track, we want to replace the old selection with the new ones
1080 if (new_views
.size() > 0) {
1081 _editor
->selection
->set (new_views
);
1084 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= frozen_playlists
.begin(); p
!= frozen_playlists
.end(); ++p
) {
1088 /* write commands for the accumulated diffs for all our modified playlists */
1089 add_stateful_diff_commands_for_playlists (modified_playlists
);
1091 _editor
->commit_reversible_command ();
1094 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1095 * @param region Region to remove.
1096 * @param playlist playlist To remove from.
1097 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1098 * that clear_changes () is only called once per playlist.
1101 RegionMoveDrag::remove_region_from_playlist (
1102 boost::shared_ptr
<Region
> region
,
1103 boost::shared_ptr
<Playlist
> playlist
,
1104 PlaylistSet
& modified_playlists
1107 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
, bool> r
= modified_playlists
.insert (playlist
);
1110 playlist
->clear_changes ();
1113 playlist
->remove_region (region
);
1117 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1118 * clearing the playlist's diff history first if necessary.
1119 * @param region Region to insert.
1120 * @param dest_rtv Destination RouteTimeAxisView.
1121 * @param dest_layer Destination layer.
1122 * @param where Destination position.
1123 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1124 * that clear_changes () is only called once per playlist.
1125 * @return New RegionView, or 0 if no insert was performed.
1128 RegionMoveDrag::insert_region_into_playlist (
1129 boost::shared_ptr
<Region
> region
,
1130 RouteTimeAxisView
* dest_rtv
,
1133 PlaylistSet
& modified_playlists
1136 boost::shared_ptr
<Playlist
> dest_playlist
= dest_rtv
->playlist ();
1137 if (!dest_playlist
) {
1141 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1142 _new_region_view
= 0;
1143 sigc::connection c
= dest_rtv
->view()->RegionViewAdded
.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view
));
1145 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1146 pair
<PlaylistSet::iterator
, bool> r
= modified_playlists
.insert (dest_playlist
);
1148 dest_playlist
->clear_changes ();
1151 dest_playlist
->add_region (region
, where
);
1153 if (dest_rtv
->view()->layer_display() == Stacked
) {
1154 region
->set_layer (dest_layer
);
1155 region
->set_pending_explicit_relayer (true);
1160 assert (_new_region_view
);
1162 return _new_region_view
;
1166 RegionMoveDrag::collect_new_region_view (RegionView
* rv
)
1168 _new_region_view
= rv
;
1172 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet
const & playlists
)
1174 for (PlaylistSet::const_iterator i
= playlists
.begin(); i
!= playlists
.end(); ++i
) {
1175 StatefulDiffCommand
* c
= new StatefulDiffCommand (*i
);
1177 _editor
->session()->add_command (c
);
1186 RegionMoveDrag::aborted (bool movement_occurred
)
1190 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1197 RegionMotionDrag::aborted (movement_occurred
);
1202 RegionMotionDrag::aborted (bool)
1204 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1205 RegionView
* rv
= i
->view
;
1206 TimeAxisView
* tv
= &(rv
->get_time_axis_view ());
1207 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tv
);
1209 rv
->get_canvas_group()->reparent (*rtv
->view()->canvas_item());
1210 rv
->get_canvas_group()->property_y() = 0;
1211 rv
->get_time_axis_view().reveal_dependent_views (*rv
);
1212 rv
->fake_set_opaque (false);
1213 rv
->move (-_total_x_delta
, 0);
1214 rv
->set_height (rtv
->view()->child_height ());
1217 _editor
->update_canvas_now ();
1220 /** @param b true to brush, otherwise false.
1221 * @param c true to make copies of the regions being moved, otherwise false.
1223 RegionMoveDrag::RegionMoveDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
, bool b
, bool c
)
1224 : RegionMotionDrag (e
, i
, p
, v
, b
),
1227 DEBUG_TRACE (DEBUG::Drags
, "New RegionMoveDrag\n");
1230 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (&_primary
->get_time_axis_view ());
1231 if (rtv
&& rtv
->is_track()) {
1232 speed
= rtv
->track()->speed ();
1235 _last_frame_position
= static_cast<framepos_t
> (_primary
->region()->position() / speed
);
1239 RegionMoveDrag::setup_pointer_frame_offset ()
1241 _pointer_frame_offset
= raw_grab_frame() - _last_frame_position
;
1244 RegionInsertDrag::RegionInsertDrag (Editor
* e
, boost::shared_ptr
<Region
> r
, RouteTimeAxisView
* v
, framepos_t pos
)
1245 : RegionMotionDrag (e
, 0, 0, list
<RegionView
*> (), false)
1247 DEBUG_TRACE (DEBUG::Drags
, "New RegionInsertDrag\n");
1249 assert ((boost::dynamic_pointer_cast
<AudioRegion
> (r
) && dynamic_cast<AudioTimeAxisView
*> (v
)) ||
1250 (boost::dynamic_pointer_cast
<MidiRegion
> (r
) && dynamic_cast<MidiTimeAxisView
*> (v
)));
1252 _primary
= v
->view()->create_region_view (r
, false, false);
1254 _primary
->get_canvas_group()->show ();
1255 _primary
->set_position (pos
, 0);
1256 _views
.push_back (DraggingView (_primary
, this));
1258 _last_frame_position
= pos
;
1260 _item
= _primary
->get_canvas_group ();
1264 RegionInsertDrag::finished (GdkEvent
*, bool)
1266 _editor
->update_canvas_now ();
1268 RouteTimeAxisView
* dest_rtv
= dynamic_cast<RouteTimeAxisView
*> (_time_axis_views
[_views
.front().time_axis_view
]);
1270 _primary
->get_canvas_group()->reparent (*dest_rtv
->view()->canvas_item());
1271 _primary
->get_canvas_group()->property_y() = 0;
1273 boost::shared_ptr
<Playlist
> playlist
= dest_rtv
->playlist();
1275 _editor
->begin_reversible_command (Operations::insert_region
);
1276 playlist
->clear_changes ();
1277 playlist
->add_region (_primary
->region (), _last_frame_position
);
1278 _editor
->session()->add_command (new StatefulDiffCommand (playlist
));
1279 _editor
->commit_reversible_command ();
1287 RegionInsertDrag::aborted (bool)
1294 RegionSpliceDrag::RegionSpliceDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
1295 : RegionMoveDrag (e
, i
, p
, v
, false, false)
1297 DEBUG_TRACE (DEBUG::Drags
, "New RegionSpliceDrag\n");
1300 struct RegionSelectionByPosition
{
1301 bool operator() (RegionView
*a
, RegionView
* b
) {
1302 return a
->region()->position () < b
->region()->position();
1307 RegionSpliceDrag::motion (GdkEvent
* event
, bool)
1309 /* Which trackview is this ? */
1311 pair
<TimeAxisView
*, int> const tvp
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
1312 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*> (tvp
.first
);
1313 layer_t layer
= tvp
.second
;
1315 if (tv
&& tv
->layer_display() == Overlaid
) {
1319 /* The region motion is only processed if the pointer is over
1323 if (!tv
|| !tv
->is_track()) {
1324 /* To make sure we hide the verbose canvas cursor when the mouse is
1325 not held over and audiotrack.
1327 _editor
->verbose_cursor()->hide ();
1333 if ((_drags
->current_pointer_x() - last_pointer_x()) > 0) {
1339 RegionSelection
copy (_editor
->selection
->regions
);
1341 RegionSelectionByPosition cmp
;
1344 framepos_t
const pf
= adjusted_current_frame (event
);
1346 for (RegionSelection::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
1348 RouteTimeAxisView
* atv
= dynamic_cast<RouteTimeAxisView
*> (&(*i
)->get_time_axis_view());
1354 boost::shared_ptr
<Playlist
> playlist
;
1356 if ((playlist
= atv
->playlist()) == 0) {
1360 if (!playlist
->region_is_shuffle_constrained ((*i
)->region())) {
1365 if (pf
< (*i
)->region()->last_frame() + 1) {
1369 if (pf
> (*i
)->region()->first_frame()) {
1375 playlist
->shuffle ((*i
)->region(), dir
);
1380 RegionSpliceDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1382 RegionMoveDrag::finished (event
, movement_occurred
);
1386 RegionSpliceDrag::aborted (bool)
1391 RegionCreateDrag::RegionCreateDrag (Editor
* e
, ArdourCanvas::Item
* i
, TimeAxisView
* v
)
1393 _view (dynamic_cast<MidiTimeAxisView
*> (v
))
1395 DEBUG_TRACE (DEBUG::Drags
, "New RegionCreateDrag\n");
1401 RegionCreateDrag::motion (GdkEvent
* event
, bool first_move
)
1405 _view
->playlist()->freeze ();
1408 framepos_t
const f
= adjusted_current_frame (event
);
1409 if (f
< grab_frame()) {
1410 _region
->set_position (f
, this);
1413 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1414 so that if this region is duplicated, its duplicate starts on
1415 a snap point rather than 1 frame after a snap point. Otherwise things get
1416 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1417 place snapped notes at the start of the region.
1420 framecnt_t
const len
= abs (f
- grab_frame () - 1);
1421 _region
->set_length (len
< 1 ? 1 : len
, this);
1427 RegionCreateDrag::finished (GdkEvent
*, bool movement_occurred
)
1429 if (!movement_occurred
) {
1432 _view
->playlist()->thaw ();
1436 _editor
->commit_reversible_command ();
1441 RegionCreateDrag::add_region ()
1443 if (_editor
->session()) {
1444 const TempoMap
& map (_editor
->session()->tempo_map());
1445 framecnt_t pos
= grab_frame();
1446 const Meter
& m
= map
.meter_at (pos
);
1447 /* not that the frame rate used here can be affected by pull up/down which
1450 framecnt_t len
= m
.frames_per_bar (map
.tempo_at (pos
), _editor
->session()->frame_rate());
1451 _region
= _view
->add_region (grab_frame(), len
, false);
1456 RegionCreateDrag::aborted (bool)
1459 _view
->playlist()->thaw ();
1465 NoteResizeDrag::NoteResizeDrag (Editor
* e
, ArdourCanvas::Item
* i
)
1469 DEBUG_TRACE (DEBUG::Drags
, "New NoteResizeDrag\n");
1473 NoteResizeDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*ignored*/)
1475 Gdk::Cursor
* cursor
;
1476 ArdourCanvas::CanvasNoteEvent
* cnote
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
);
1477 float x_fraction
= cnote
->mouse_x_fraction ();
1479 if (x_fraction
> 0.0 && x_fraction
< 0.25) {
1480 cursor
= _editor
->cursors()->left_side_trim
;
1482 cursor
= _editor
->cursors()->right_side_trim
;
1485 Drag::start_grab (event
, cursor
);
1487 region
= &cnote
->region_view();
1489 double const region_start
= region
->get_position_pixels();
1490 double const middle_point
= region_start
+ cnote
->x1() + (cnote
->x2() - cnote
->x1()) / 2.0L;
1492 if (grab_x() <= middle_point
) {
1493 cursor
= _editor
->cursors()->left_side_trim
;
1496 cursor
= _editor
->cursors()->right_side_trim
;
1500 _item
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
, *cursor
, event
->motion
.time
);
1502 if (event
->motion
.state
& Keyboard::PrimaryModifier
) {
1508 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1510 if (ms
.size() > 1) {
1511 /* has to be relative, may make no sense otherwise */
1515 /* select this note; if it is already selected, preserve the existing selection,
1516 otherwise make this note the only one selected.
1518 region
->note_selected (cnote
, cnote
->selected ());
1520 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ) {
1521 MidiRegionSelection::iterator next
;
1524 (*r
)->begin_resizing (at_front
);
1530 NoteResizeDrag::motion (GdkEvent
* /*event*/, bool /*first_move*/)
1532 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1533 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ++r
) {
1534 (*r
)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
), at_front
, _drags
->current_pointer_x() - grab_x(), relative
);
1539 NoteResizeDrag::finished (GdkEvent
*, bool /*movement_occurred*/)
1541 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1542 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ++r
) {
1543 (*r
)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
), at_front
, _drags
->current_pointer_x() - grab_x(), relative
);
1548 NoteResizeDrag::aborted (bool)
1553 RegionGainDrag::RegionGainDrag (Editor
* e
, ArdourCanvas::Item
* i
)
1556 DEBUG_TRACE (DEBUG::Drags
, "New RegionGainDrag\n");
1560 RegionGainDrag::motion (GdkEvent
* /*event*/, bool)
1566 RegionGainDrag::finished (GdkEvent
*, bool)
1572 RegionGainDrag::aborted (bool)
1577 TrimDrag::TrimDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
1578 : RegionDrag (e
, i
, p
, v
)
1580 DEBUG_TRACE (DEBUG::Drags
, "New TrimDrag\n");
1584 TrimDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
1587 TimeAxisView
* tvp
= &_primary
->get_time_axis_view ();
1588 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
1590 if (tv
&& tv
->is_track()) {
1591 speed
= tv
->track()->speed();
1594 framepos_t
const region_start
= (framepos_t
) (_primary
->region()->position() / speed
);
1595 framepos_t
const region_end
= (framepos_t
) (_primary
->region()->last_frame() / speed
);
1596 framecnt_t
const region_length
= (framecnt_t
) (_primary
->region()->length() / speed
);
1598 framepos_t
const pf
= adjusted_current_frame (event
);
1600 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1601 /* Move the contents of the region around without changing the region bounds */
1602 _operation
= ContentsTrim
;
1603 Drag::start_grab (event
, _editor
->cursors()->trimmer
);
1605 /* These will get overridden for a point trim.*/
1606 if (pf
< (region_start
+ region_length
/2)) {
1607 /* closer to front */
1608 _operation
= StartTrim
;
1609 Drag::start_grab (event
, _editor
->cursors()->left_side_trim
);
1612 _operation
= EndTrim
;
1613 Drag::start_grab (event
, _editor
->cursors()->right_side_trim
);
1617 switch (_operation
) {
1619 show_verbose_cursor_time (region_start
);
1620 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1621 i
->view
->trim_front_starting ();
1625 show_verbose_cursor_time (region_end
);
1628 show_verbose_cursor_time (pf
);
1632 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1633 i
->view
->region()->suspend_property_changes ();
1638 TrimDrag::motion (GdkEvent
* event
, bool first_move
)
1640 RegionView
* rv
= _primary
;
1643 TimeAxisView
* tvp
= &_primary
->get_time_axis_view ();
1644 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
1645 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
1647 if (tv
&& tv
->is_track()) {
1648 speed
= tv
->track()->speed();
1651 framecnt_t
const dt
= adjusted_current_frame (event
) - raw_grab_frame () + _pointer_frame_offset
;
1657 switch (_operation
) {
1659 trim_type
= "Region start trim";
1662 trim_type
= "Region end trim";
1665 trim_type
= "Region content trim";
1669 _editor
->begin_reversible_command (trim_type
);
1671 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1672 RegionView
* rv
= i
->view
;
1673 rv
->fake_set_opaque (false);
1674 rv
->enable_display (false);
1675 rv
->region()->playlist()->clear_owned_changes ();
1677 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*> (rv
);
1680 arv
->temporarily_hide_envelope ();
1683 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
1684 insert_result
= _editor
->motion_frozen_playlists
.insert (pl
);
1686 if (insert_result
.second
) {
1692 bool non_overlap_trim
= false;
1694 if (event
&& Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
1695 non_overlap_trim
= true;
1698 switch (_operation
) {
1700 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1701 i
->view
->trim_front (i
->initial_position
+ dt
, non_overlap_trim
);
1706 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1707 i
->view
->trim_end (i
->initial_end
+ dt
, non_overlap_trim
);
1713 bool swap_direction
= false;
1715 if (event
&& Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1716 swap_direction
= true;
1719 framecnt_t frame_delta
= 0;
1721 bool left_direction
= false;
1722 if (last_pointer_frame() > adjusted_current_frame(event
)) {
1723 left_direction
= true;
1726 if (left_direction
) {
1727 frame_delta
= (last_pointer_frame() - adjusted_current_frame(event
));
1729 frame_delta
= (adjusted_current_frame(event
) - last_pointer_frame());
1732 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1733 i
->view
->trim_contents (frame_delta
, left_direction
, swap_direction
);
1739 switch (_operation
) {
1741 show_verbose_cursor_time ((framepos_t
) (rv
->region()->position() / speed
));
1744 show_verbose_cursor_time ((framepos_t
) (rv
->region()->last_frame() / speed
));
1747 show_verbose_cursor_time (adjusted_current_frame (event
));
1754 TrimDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1756 if (movement_occurred
) {
1757 motion (event
, false);
1759 /* This must happen before the region's StatefulDiffCommand is created, as it may
1760 `correct' (ahem) the region's _start from being negative to being zero. It
1761 needs to be zero in the undo record.
1763 if (_operation
== StartTrim
) {
1764 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1765 i
->view
->trim_front_ending ();
1769 if (!_editor
->selection
->selected (_primary
)) {
1770 _primary
->thaw_after_trim ();
1773 set
<boost::shared_ptr
<Playlist
> > diffed_playlists
;
1775 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1776 i
->view
->thaw_after_trim ();
1777 i
->view
->enable_display (true);
1778 i
->view
->fake_set_opaque (true);
1780 /* Trimming one region may affect others on the playlist, so we need
1781 to get undo Commands from the whole playlist rather than just the
1782 region. Use diffed_playlists to make sure we don't diff a given
1783 playlist more than once.
1785 boost::shared_ptr
<Playlist
> p
= i
->view
->region()->playlist ();
1786 if (diffed_playlists
.find (p
) == diffed_playlists
.end()) {
1787 vector
<Command
*> cmds
;
1789 _editor
->session()->add_commands (cmds
);
1790 diffed_playlists
.insert (p
);
1794 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= _editor
->motion_frozen_playlists
.begin(); p
!= _editor
->motion_frozen_playlists
.end(); ++p
) {
1798 _editor
->motion_frozen_playlists
.clear ();
1799 _editor
->commit_reversible_command();
1802 /* no mouse movement */
1803 _editor
->point_trim (event
, adjusted_current_frame (event
));
1806 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1807 if (_operation
== StartTrim
) {
1808 i
->view
->trim_front_ending ();
1811 i
->view
->region()->resume_property_changes ();
1816 TrimDrag::aborted (bool movement_occurred
)
1818 /* Our motion method is changing model state, so use the Undo system
1819 to cancel. Perhaps not ideal, as this will leave an Undo point
1820 behind which may be slightly odd from the user's point of view.
1825 if (movement_occurred
) {
1829 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1830 i
->view
->region()->resume_property_changes ();
1835 TrimDrag::setup_pointer_frame_offset ()
1837 list
<DraggingView
>::iterator i
= _views
.begin ();
1838 while (i
!= _views
.end() && i
->view
!= _primary
) {
1842 if (i
== _views
.end()) {
1846 switch (_operation
) {
1848 _pointer_frame_offset
= raw_grab_frame() - i
->initial_position
;
1851 _pointer_frame_offset
= raw_grab_frame() - i
->initial_end
;
1858 MeterMarkerDrag::MeterMarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool c
)
1862 DEBUG_TRACE (DEBUG::Drags
, "New MeterMarkerDrag\n");
1864 _marker
= reinterpret_cast<MeterMarker
*> (_item
->get_data ("marker"));
1869 MeterMarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
1872 // create a dummy marker for visual representation of moving the copy.
1873 // The actual copying is not done before we reach the finish callback.
1875 snprintf (name
, sizeof(name
), "%g/%g", _marker
->meter().beats_per_bar(), _marker
->meter().note_divisor ());
1877 MeterMarker
* new_marker
= new MeterMarker (
1879 *_editor
->meter_group
,
1880 ARDOUR_UI::config()->canvasvar_MeterMarker
.get(),
1882 *new MeterSection (_marker
->meter())
1885 _item
= &new_marker
->the_item ();
1886 _marker
= new_marker
;
1890 MetricSection
& section (_marker
->meter());
1892 if (!section
.movable()) {
1898 Drag::start_grab (event
, cursor
);
1900 show_verbose_cursor_time (adjusted_current_frame(event
));
1904 MeterMarkerDrag::setup_pointer_frame_offset ()
1906 _pointer_frame_offset
= raw_grab_frame() - _marker
->meter().frame();
1910 MeterMarkerDrag::motion (GdkEvent
* event
, bool)
1912 framepos_t
const pf
= adjusted_current_frame (event
);
1914 _marker
->set_position (pf
);
1916 show_verbose_cursor_time (pf
);
1920 MeterMarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1922 if (!movement_occurred
) {
1926 motion (event
, false);
1928 Timecode::BBT_Time when
;
1930 TempoMap
& map (_editor
->session()->tempo_map());
1931 map
.bbt_time (last_pointer_frame(), when
);
1933 if (_copy
== true) {
1934 _editor
->begin_reversible_command (_("copy meter mark"));
1935 XMLNode
&before
= map
.get_state();
1936 map
.add_meter (_marker
->meter(), when
);
1937 XMLNode
&after
= map
.get_state();
1938 _editor
->session()->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
1939 _editor
->commit_reversible_command ();
1941 // delete the dummy marker we used for visual representation of copying.
1942 // a new visual marker will show up automatically.
1945 _editor
->begin_reversible_command (_("move meter mark"));
1946 XMLNode
&before
= map
.get_state();
1947 map
.move_meter (_marker
->meter(), when
);
1948 XMLNode
&after
= map
.get_state();
1949 _editor
->session()->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
1950 _editor
->commit_reversible_command ();
1955 MeterMarkerDrag::aborted (bool)
1957 _marker
->set_position (_marker
->meter().frame ());
1960 TempoMarkerDrag::TempoMarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool c
)
1964 DEBUG_TRACE (DEBUG::Drags
, "New TempoMarkerDrag\n");
1966 _marker
= reinterpret_cast<TempoMarker
*> (_item
->get_data ("marker"));
1971 TempoMarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
1975 // create a dummy marker for visual representation of moving the copy.
1976 // The actual copying is not done before we reach the finish callback.
1978 snprintf (name
, sizeof (name
), "%.2f", _marker
->tempo().beats_per_minute());
1980 TempoMarker
* new_marker
= new TempoMarker (
1982 *_editor
->tempo_group
,
1983 ARDOUR_UI::config()->canvasvar_TempoMarker
.get(),
1985 *new TempoSection (_marker
->tempo())
1988 _item
= &new_marker
->the_item ();
1989 _marker
= new_marker
;
1993 Drag::start_grab (event
, cursor
);
1995 show_verbose_cursor_time (adjusted_current_frame (event
));
1999 TempoMarkerDrag::setup_pointer_frame_offset ()
2001 _pointer_frame_offset
= raw_grab_frame() - _marker
->tempo().frame();
2005 TempoMarkerDrag::motion (GdkEvent
* event
, bool)
2007 framepos_t
const pf
= adjusted_current_frame (event
);
2008 _marker
->set_position (pf
);
2009 show_verbose_cursor_time (pf
);
2013 TempoMarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2015 if (!movement_occurred
) {
2019 motion (event
, false);
2021 Timecode::BBT_Time when
;
2023 TempoMap
& map (_editor
->session()->tempo_map());
2024 map
.bbt_time (last_pointer_frame(), when
);
2026 if (_copy
== true) {
2027 _editor
->begin_reversible_command (_("copy tempo mark"));
2028 XMLNode
&before
= map
.get_state();
2029 map
.add_tempo (_marker
->tempo(), when
);
2030 XMLNode
&after
= map
.get_state();
2031 _editor
->session()->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2032 _editor
->commit_reversible_command ();
2034 // delete the dummy marker we used for visual representation of copying.
2035 // a new visual marker will show up automatically.
2038 _editor
->begin_reversible_command (_("move tempo mark"));
2039 XMLNode
&before
= map
.get_state();
2040 map
.move_tempo (_marker
->tempo(), when
);
2041 XMLNode
&after
= map
.get_state();
2042 _editor
->session()->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2043 _editor
->commit_reversible_command ();
2048 TempoMarkerDrag::aborted (bool)
2050 _marker
->set_position (_marker
->tempo().frame());
2053 CursorDrag::CursorDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool s
)
2057 DEBUG_TRACE (DEBUG::Drags
, "New CursorDrag\n");
2060 /** Do all the things we do when dragging the playhead to make it look as though
2061 * we have located, without actually doing the locate (because that would cause
2062 * the diskstream buffers to be refilled, which is too slow).
2065 CursorDrag::fake_locate (framepos_t t
)
2067 _editor
->playhead_cursor
->set_position (t
);
2069 Session
* s
= _editor
->session ();
2070 if (s
->timecode_transmission_suspended ()) {
2071 framepos_t
const f
= _editor
->playhead_cursor
->current_frame
;
2072 s
->send_mmc_locate (f
);
2073 s
->send_full_time_code (f
);
2076 show_verbose_cursor_time (t
);
2077 _editor
->UpdateAllTransportClocks (t
);
2081 CursorDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* c
)
2083 Drag::start_grab (event
, c
);
2085 framepos_t where
= _editor
->event_frame (event
, 0, 0);
2086 _editor
->snap_to_with_modifier (where
, event
);
2088 _editor
->_dragging_playhead
= true;
2090 Session
* s
= _editor
->session ();
2093 if (_was_rolling
&& _stop
) {
2097 if (s
->is_auditioning()) {
2098 s
->cancel_audition ();
2101 s
->request_suspend_timecode_transmission ();
2102 while (!s
->timecode_transmission_suspended ()) {
2103 /* twiddle our thumbs */
2107 fake_locate (where
);
2111 CursorDrag::motion (GdkEvent
* event
, bool)
2113 framepos_t
const adjusted_frame
= adjusted_current_frame (event
);
2115 if (adjusted_frame
== last_pointer_frame()) {
2119 fake_locate (adjusted_frame
);
2122 _editor
->update_canvas_now ();
2127 CursorDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2129 _editor
->_dragging_playhead
= false;
2131 if (!movement_occurred
&& _stop
) {
2135 motion (event
, false);
2137 Session
* s
= _editor
->session ();
2139 s
->request_locate (_editor
->playhead_cursor
->current_frame
, _was_rolling
);
2140 _editor
->_pending_locate_request
= true;
2141 s
->request_resume_timecode_transmission ();
2146 CursorDrag::aborted (bool)
2148 if (_editor
->_dragging_playhead
) {
2149 _editor
->session()->request_resume_timecode_transmission ();
2150 _editor
->_dragging_playhead
= false;
2153 _editor
->playhead_cursor
->set_position (adjusted_frame (grab_frame (), 0, false));
2156 FadeInDrag::FadeInDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
2157 : RegionDrag (e
, i
, p
, v
)
2159 DEBUG_TRACE (DEBUG::Drags
, "New FadeInDrag\n");
2163 FadeInDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2165 Drag::start_grab (event
, cursor
);
2167 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2168 boost::shared_ptr
<AudioRegion
> const r
= arv
->audio_region ();
2170 show_verbose_cursor_duration (r
->position(), r
->position() + r
->fade_in()->back()->when
, 32);
2172 arv
->show_fade_line((framepos_t
) r
->fade_in()->back()->when
);
2176 FadeInDrag::setup_pointer_frame_offset ()
2178 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2179 boost::shared_ptr
<AudioRegion
> const r
= arv
->audio_region ();
2180 _pointer_frame_offset
= raw_grab_frame() - ((framecnt_t
) r
->fade_in()->back()->when
+ r
->position());
2184 FadeInDrag::motion (GdkEvent
* event
, bool)
2186 framecnt_t fade_length
;
2188 framepos_t
const pos
= adjusted_current_frame (event
);
2190 boost::shared_ptr
<Region
> region
= _primary
->region ();
2192 if (pos
< (region
->position() + 64)) {
2193 fade_length
= 64; // this should be a minimum defined somewhere
2194 } else if (pos
> region
->last_frame()) {
2195 fade_length
= region
->length();
2197 fade_length
= pos
- region
->position();
2200 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2202 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2208 tmp
->reset_fade_in_shape_width (fade_length
);
2209 tmp
->show_fade_line((framecnt_t
) fade_length
);
2212 show_verbose_cursor_duration (region
->position(), region
->position() + fade_length
, 32);
2216 FadeInDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2218 if (!movement_occurred
) {
2222 framecnt_t fade_length
;
2224 framepos_t
const pos
= adjusted_current_frame (event
);
2226 boost::shared_ptr
<Region
> region
= _primary
->region ();
2228 if (pos
< (region
->position() + 64)) {
2229 fade_length
= 64; // this should be a minimum defined somewhere
2230 } else if (pos
> region
->last_frame()) {
2231 fade_length
= region
->length();
2233 fade_length
= pos
- region
->position();
2236 _editor
->begin_reversible_command (_("change fade in length"));
2238 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2240 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2246 boost::shared_ptr
<AutomationList
> alist
= tmp
->audio_region()->fade_in();
2247 XMLNode
&before
= alist
->get_state();
2249 tmp
->audio_region()->set_fade_in_length (fade_length
);
2250 tmp
->audio_region()->set_fade_in_active (true);
2251 tmp
->hide_fade_line();
2253 XMLNode
&after
= alist
->get_state();
2254 _editor
->session()->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &after
));
2257 _editor
->commit_reversible_command ();
2261 FadeInDrag::aborted (bool)
2263 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2264 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2270 tmp
->reset_fade_in_shape_width (tmp
->audio_region()->fade_in()->back()->when
);
2271 tmp
->hide_fade_line();
2275 FadeOutDrag::FadeOutDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
2276 : RegionDrag (e
, i
, p
, v
)
2278 DEBUG_TRACE (DEBUG::Drags
, "New FadeOutDrag\n");
2282 FadeOutDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2284 Drag::start_grab (event
, cursor
);
2286 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2287 boost::shared_ptr
<AudioRegion
> r
= arv
->audio_region ();
2289 show_verbose_cursor_duration (r
->last_frame() - r
->fade_out()->back()->when
, r
->last_frame());
2291 arv
->show_fade_line(r
->length() - r
->fade_out()->back()->when
);
2295 FadeOutDrag::setup_pointer_frame_offset ()
2297 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2298 boost::shared_ptr
<AudioRegion
> r
= arv
->audio_region ();
2299 _pointer_frame_offset
= raw_grab_frame() - (r
->length() - (framecnt_t
) r
->fade_out()->back()->when
+ r
->position());
2303 FadeOutDrag::motion (GdkEvent
* event
, bool)
2305 framecnt_t fade_length
;
2307 framepos_t
const pos
= adjusted_current_frame (event
);
2309 boost::shared_ptr
<Region
> region
= _primary
->region ();
2311 if (pos
> (region
->last_frame() - 64)) {
2312 fade_length
= 64; // this should really be a minimum fade defined somewhere
2314 else if (pos
< region
->position()) {
2315 fade_length
= region
->length();
2318 fade_length
= region
->last_frame() - pos
;
2321 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2323 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2329 tmp
->reset_fade_out_shape_width (fade_length
);
2330 tmp
->show_fade_line(region
->length() - fade_length
);
2333 show_verbose_cursor_duration (region
->last_frame() - fade_length
, region
->last_frame());
2337 FadeOutDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2339 if (!movement_occurred
) {
2343 framecnt_t fade_length
;
2345 framepos_t
const pos
= adjusted_current_frame (event
);
2347 boost::shared_ptr
<Region
> region
= _primary
->region ();
2349 if (pos
> (region
->last_frame() - 64)) {
2350 fade_length
= 64; // this should really be a minimum fade defined somewhere
2352 else if (pos
< region
->position()) {
2353 fade_length
= region
->length();
2356 fade_length
= region
->last_frame() - pos
;
2359 _editor
->begin_reversible_command (_("change fade out length"));
2361 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2363 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2369 boost::shared_ptr
<AutomationList
> alist
= tmp
->audio_region()->fade_out();
2370 XMLNode
&before
= alist
->get_state();
2372 tmp
->audio_region()->set_fade_out_length (fade_length
);
2373 tmp
->audio_region()->set_fade_out_active (true);
2374 tmp
->hide_fade_line();
2376 XMLNode
&after
= alist
->get_state();
2377 _editor
->session()->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &after
));
2380 _editor
->commit_reversible_command ();
2384 FadeOutDrag::aborted (bool)
2386 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2387 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2393 tmp
->reset_fade_out_shape_width (tmp
->audio_region()->fade_out()->back()->when
);
2394 tmp
->hide_fade_line();
2398 MarkerDrag::MarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2401 DEBUG_TRACE (DEBUG::Drags
, "New MarkerDrag\n");
2403 _marker
= reinterpret_cast<Marker
*> (_item
->get_data ("marker"));
2406 _points
.push_back (Gnome::Art::Point (0, 0));
2407 _points
.push_back (Gnome::Art::Point (0, physical_screen_height (_editor
->get_window())));
2410 MarkerDrag::~MarkerDrag ()
2412 for (list
<Location
*>::iterator i
= _copied_locations
.begin(); i
!= _copied_locations
.end(); ++i
) {
2418 MarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2420 Drag::start_grab (event
, cursor
);
2424 Location
*location
= _editor
->find_location_from_marker (_marker
, is_start
);
2425 _editor
->_dragging_edit_point
= true;
2427 update_item (location
);
2429 // _drag_line->show();
2430 // _line->raise_to_top();
2433 show_verbose_cursor_time (location
->start());
2435 show_verbose_cursor_time (location
->end());
2438 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
2441 case Selection::Toggle
:
2442 _editor
->selection
->toggle (_marker
);
2444 case Selection::Set
:
2445 if (!_editor
->selection
->selected (_marker
)) {
2446 _editor
->selection
->set (_marker
);
2449 case Selection::Extend
:
2451 Locations::LocationList ll
;
2452 list
<Marker
*> to_add
;
2454 _editor
->selection
->markers
.range (s
, e
);
2455 s
= min (_marker
->position(), s
);
2456 e
= max (_marker
->position(), e
);
2459 if (e
< max_framepos
) {
2462 _editor
->session()->locations()->find_all_between (s
, e
, ll
, Location::Flags (0));
2463 for (Locations::LocationList::iterator i
= ll
.begin(); i
!= ll
.end(); ++i
) {
2464 Editor::LocationMarkers
* lm
= _editor
->find_location_markers (*i
);
2467 to_add
.push_back (lm
->start
);
2470 to_add
.push_back (lm
->end
);
2474 if (!to_add
.empty()) {
2475 _editor
->selection
->add (to_add
);
2479 case Selection::Add
:
2480 _editor
->selection
->add (_marker
);
2484 /* Set up copies for us to manipulate during the drag */
2486 for (MarkerSelection::iterator i
= _editor
->selection
->markers
.begin(); i
!= _editor
->selection
->markers
.end(); ++i
) {
2487 Location
* l
= _editor
->find_location_from_marker (*i
, is_start
);
2488 _copied_locations
.push_back (new Location (*l
));
2493 MarkerDrag::setup_pointer_frame_offset ()
2496 Location
*location
= _editor
->find_location_from_marker (_marker
, is_start
);
2497 _pointer_frame_offset
= raw_grab_frame() - (is_start
? location
->start() : location
->end());
2501 MarkerDrag::motion (GdkEvent
* event
, bool)
2503 framecnt_t f_delta
= 0;
2505 bool move_both
= false;
2507 Location
*real_location
;
2508 Location
*copy_location
= 0;
2510 framepos_t
const newframe
= adjusted_current_frame (event
);
2512 framepos_t next
= newframe
;
2514 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
2518 MarkerSelection::iterator i
;
2519 list
<Location
*>::iterator x
;
2521 /* find the marker we're dragging, and compute the delta */
2523 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2524 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2530 if (marker
== _marker
) {
2532 if ((real_location
= _editor
->find_location_from_marker (marker
, is_start
)) == 0) {
2537 if (real_location
->is_mark()) {
2538 f_delta
= newframe
- copy_location
->start();
2542 switch (marker
->type()) {
2543 case Marker::SessionStart
:
2544 case Marker::RangeStart
:
2545 case Marker::LoopStart
:
2546 case Marker::PunchIn
:
2547 f_delta
= newframe
- copy_location
->start();
2550 case Marker::SessionEnd
:
2551 case Marker::RangeEnd
:
2552 case Marker::LoopEnd
:
2553 case Marker::PunchOut
:
2554 f_delta
= newframe
- copy_location
->end();
2557 /* what kind of marker is this ? */
2565 if (i
== _editor
->selection
->markers
.end()) {
2566 /* hmm, impossible - we didn't find the dragged marker */
2570 /* now move them all */
2572 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2573 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2579 /* call this to find out if its the start or end */
2581 if ((real_location
= _editor
->find_location_from_marker (marker
, is_start
)) == 0) {
2585 if (real_location
->locked()) {
2589 if (copy_location
->is_mark()) {
2593 copy_location
->set_start (copy_location
->start() + f_delta
);
2597 framepos_t new_start
= copy_location
->start() + f_delta
;
2598 framepos_t new_end
= copy_location
->end() + f_delta
;
2600 if (is_start
) { // start-of-range marker
2603 copy_location
->set_start (new_start
);
2604 copy_location
->set_end (new_end
);
2605 } else if (new_start
< copy_location
->end()) {
2606 copy_location
->set_start (new_start
);
2607 } else if (newframe
> 0) {
2608 _editor
->snap_to (next
, 1, true);
2609 copy_location
->set_end (next
);
2610 copy_location
->set_start (newframe
);
2613 } else { // end marker
2616 copy_location
->set_end (new_end
);
2617 copy_location
->set_start (new_start
);
2618 } else if (new_end
> copy_location
->start()) {
2619 copy_location
->set_end (new_end
);
2620 } else if (newframe
> 0) {
2621 _editor
->snap_to (next
, -1, true);
2622 copy_location
->set_start (next
);
2623 copy_location
->set_end (newframe
);
2628 update_item (copy_location
);
2630 Editor::LocationMarkers
* lm
= _editor
->find_location_markers (real_location
);
2633 lm
->set_position (copy_location
->start(), copy_location
->end());
2637 assert (!_copied_locations
.empty());
2639 show_verbose_cursor_time (newframe
);
2642 _editor
->update_canvas_now ();
2647 MarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2649 if (!movement_occurred
) {
2651 /* just a click, do nothing but finish
2652 off the selection process
2655 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
2658 case Selection::Set
:
2659 if (_editor
->selection
->selected (_marker
) && _editor
->selection
->markers
.size() > 1) {
2660 _editor
->selection
->set (_marker
);
2664 case Selection::Toggle
:
2665 case Selection::Extend
:
2666 case Selection::Add
:
2673 _editor
->_dragging_edit_point
= false;
2675 _editor
->begin_reversible_command ( _("move marker") );
2676 XMLNode
&before
= _editor
->session()->locations()->get_state();
2678 MarkerSelection::iterator i
;
2679 list
<Location
*>::iterator x
;
2682 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2683 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2686 Location
* location
= _editor
->find_location_from_marker (*i
, is_start
);
2690 if (location
->locked()) {
2694 if (location
->is_mark()) {
2695 location
->set_start ((*x
)->start());
2697 location
->set ((*x
)->start(), (*x
)->end());
2702 XMLNode
&after
= _editor
->session()->locations()->get_state();
2703 _editor
->session()->add_command(new MementoCommand
<Locations
>(*(_editor
->session()->locations()), &before
, &after
));
2704 _editor
->commit_reversible_command ();
2708 MarkerDrag::aborted (bool)
2714 MarkerDrag::update_item (Location
* location
)
2719 ControlPointDrag::ControlPointDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2721 _cumulative_x_drag (0),
2722 _cumulative_y_drag (0)
2724 DEBUG_TRACE (DEBUG::Drags
, "New ControlPointDrag\n");
2726 _point
= reinterpret_cast<ControlPoint
*> (_item
->get_data ("control_point"));
2732 ControlPointDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2734 Drag::start_grab (event
, _editor
->cursors()->fader
);
2736 // start the grab at the center of the control point so
2737 // the point doesn't 'jump' to the mouse after the first drag
2738 _fixed_grab_x
= _point
->get_x();
2739 _fixed_grab_y
= _point
->get_y();
2741 float const fraction
= 1 - (_point
->get_y() / _point
->line().height());
2743 _point
->line().start_drag_single (_point
, _fixed_grab_x
, fraction
);
2745 _editor
->verbose_cursor()->set (_point
->line().get_verbose_cursor_string (fraction
),
2746 event
->button
.x
+ 10, event
->button
.y
+ 10);
2748 _editor
->verbose_cursor()->show ();
2752 ControlPointDrag::motion (GdkEvent
* event
, bool)
2754 double dx
= _drags
->current_pointer_x() - last_pointer_x();
2755 double dy
= _drags
->current_pointer_y() - last_pointer_y();
2757 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
2762 /* coordinate in pixels relative to the start of the region (for region-based automation)
2763 or track (for track-based automation) */
2764 double cx
= _fixed_grab_x
+ _cumulative_x_drag
+ dx
;
2765 double cy
= _fixed_grab_y
+ _cumulative_y_drag
+ dy
;
2767 // calculate zero crossing point. back off by .01 to stay on the
2768 // positive side of zero
2769 double const zero_gain_y
= (1.0 - _zero_gain_fraction
) * _point
->line().height() - .01;
2771 // make sure we hit zero when passing through
2772 if ((cy
< zero_gain_y
&& (cy
- dy
) > zero_gain_y
) || (cy
> zero_gain_y
&& (cy
- dy
) < zero_gain_y
)) {
2776 if (_x_constrained
) {
2779 if (_y_constrained
) {
2783 _cumulative_x_drag
= cx
- _fixed_grab_x
;
2784 _cumulative_y_drag
= cy
- _fixed_grab_y
;
2788 cy
= min ((double) _point
->line().height(), cy
);
2790 framepos_t cx_frames
= _editor
->unit_to_frame (cx
);
2792 if (!_x_constrained
) {
2793 _editor
->snap_to_with_modifier (cx_frames
, event
);
2796 cx_frames
= min (cx_frames
, _point
->line().maximum_time());
2798 float const fraction
= 1.0 - (cy
/ _point
->line().height());
2800 bool const push
= Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
);
2802 _point
->line().drag_motion (_editor
->frame_to_unit (cx_frames
), fraction
, false, push
);
2804 _editor
->verbose_cursor()->set_text (_point
->line().get_verbose_cursor_string (fraction
));
2808 ControlPointDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2810 if (!movement_occurred
) {
2814 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
2815 _editor
->reset_point_selection ();
2819 motion (event
, false);
2822 _point
->line().end_drag ();
2823 _editor
->session()->commit_reversible_command ();
2827 ControlPointDrag::aborted (bool)
2829 _point
->line().reset ();
2833 ControlPointDrag::active (Editing::MouseMode m
)
2835 if (m
== Editing::MouseGain
) {
2836 /* always active in mouse gain */
2840 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2841 return dynamic_cast<AutomationLine
*> (&(_point
->line())) != 0;
2844 LineDrag::LineDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2847 _cumulative_y_drag (0)
2849 DEBUG_TRACE (DEBUG::Drags
, "New LineDrag\n");
2853 LineDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2855 _line
= reinterpret_cast<AutomationLine
*> (_item
->get_data ("line"));
2858 _item
= &_line
->grab_item ();
2860 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2861 origin, and ditto for y.
2864 double cx
= event
->button
.x
;
2865 double cy
= event
->button
.y
;
2867 _line
->parent_group().w2i (cx
, cy
);
2869 framecnt_t
const frame_within_region
= (framecnt_t
) floor (cx
* _editor
->frames_per_unit
);
2874 if (!_line
->control_points_adjacent (frame_within_region
, before
, after
)) {
2875 /* no adjacent points */
2879 Drag::start_grab (event
, _editor
->cursors()->fader
);
2881 /* store grab start in parent frame */
2886 double fraction
= 1.0 - (cy
/ _line
->height());
2888 _line
->start_drag_line (before
, after
, fraction
);
2890 _editor
->verbose_cursor()->set (_line
->get_verbose_cursor_string (fraction
),
2891 event
->button
.x
+ 10, event
->button
.y
+ 10);
2893 _editor
->verbose_cursor()->show ();
2897 LineDrag::motion (GdkEvent
* event
, bool)
2899 double dy
= _drags
->current_pointer_y() - last_pointer_y();
2901 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
2905 double cy
= _fixed_grab_y
+ _cumulative_y_drag
+ dy
;
2907 _cumulative_y_drag
= cy
- _fixed_grab_y
;
2910 cy
= min ((double) _line
->height(), cy
);
2912 double const fraction
= 1.0 - (cy
/ _line
->height());
2916 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
)) {
2922 /* we are ignoring x position for this drag, so we can just pass in anything */
2923 _line
->drag_motion (0, fraction
, true, push
);
2925 _editor
->verbose_cursor()->set_text (_line
->get_verbose_cursor_string (fraction
));
2929 LineDrag::finished (GdkEvent
* event
, bool)
2931 motion (event
, false);
2933 _editor
->session()->commit_reversible_command ();
2937 LineDrag::aborted (bool)
2942 FeatureLineDrag::FeatureLineDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2945 _cumulative_x_drag (0)
2947 DEBUG_TRACE (DEBUG::Drags
, "New FeatureLineDrag\n");
2951 FeatureLineDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2953 Drag::start_grab (event
);
2955 _line
= reinterpret_cast<Line
*> (_item
);
2958 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2960 double cx
= event
->button
.x
;
2961 double cy
= event
->button
.y
;
2963 _item
->property_parent().get_value()->w2i(cx
, cy
);
2965 /* store grab start in parent frame */
2966 _region_view_grab_x
= cx
;
2968 _before
= *(float*) _item
->get_data ("position");
2970 _arv
= reinterpret_cast<AudioRegionView
*> (_item
->get_data ("regionview"));
2972 _max_x
= _editor
->frame_to_pixel(_arv
->get_duration());
2976 FeatureLineDrag::motion (GdkEvent
*, bool)
2978 double dx
= _drags
->current_pointer_x() - last_pointer_x();
2980 double cx
= _region_view_grab_x
+ _cumulative_x_drag
+ dx
;
2982 _cumulative_x_drag
+= dx
;
2984 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2993 ArdourCanvas::Points points
;
2995 double x1
= 0, x2
= 0, y1
= 0, y2
= 0;
2997 _line
->get_bounds(x1
, y2
, x2
, y2
);
2999 points
.push_back(Gnome::Art::Point(cx
, 2.0)); // first x-coord needs to be a non-normal value
3000 points
.push_back(Gnome::Art::Point(cx
, y2
- y1
));
3002 _line
->property_points() = points
;
3004 float *pos
= new float;
3007 _line
->set_data ("position", pos
);
3013 FeatureLineDrag::finished (GdkEvent
*, bool)
3015 _arv
= reinterpret_cast<AudioRegionView
*> (_item
->get_data ("regionview"));
3016 _arv
->update_transient(_before
, _before
);
3020 FeatureLineDrag::aborted (bool)
3025 RubberbandSelectDrag::RubberbandSelectDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3028 DEBUG_TRACE (DEBUG::Drags
, "New RubberbandSelectDrag\n");
3032 RubberbandSelectDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3034 Drag::start_grab (event
);
3035 show_verbose_cursor_time (adjusted_current_frame (event
));
3039 RubberbandSelectDrag::motion (GdkEvent
* event
, bool)
3046 framepos_t
const pf
= adjusted_current_frame (event
, Config
->get_rubberbanding_snaps_to_grid ());
3048 framepos_t grab
= grab_frame ();
3049 if (Config
->get_rubberbanding_snaps_to_grid ()) {
3050 _editor
->snap_to_with_modifier (grab
, event
);
3053 /* base start and end on initial click position */
3063 if (_drags
->current_pointer_y() < grab_y()) {
3064 y1
= _drags
->current_pointer_y();
3067 y2
= _drags
->current_pointer_y();
3072 if (start
!= end
|| y1
!= y2
) {
3074 double x1
= _editor
->frame_to_pixel (start
);
3075 double x2
= _editor
->frame_to_pixel (end
);
3077 _editor
->rubberband_rect
->property_x1() = x1
;
3078 _editor
->rubberband_rect
->property_y1() = y1
;
3079 _editor
->rubberband_rect
->property_x2() = x2
;
3080 _editor
->rubberband_rect
->property_y2() = y2
;
3082 _editor
->rubberband_rect
->show();
3083 _editor
->rubberband_rect
->raise_to_top();
3085 show_verbose_cursor_time (pf
);
3090 RubberbandSelectDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3092 if (movement_occurred
) {
3094 motion (event
, false);
3097 if (_drags
->current_pointer_y() < grab_y()) {
3098 y1
= _drags
->current_pointer_y();
3101 y2
= _drags
->current_pointer_y();
3106 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
3108 _editor
->begin_reversible_command (_("rubberband selection"));
3110 if (grab_frame() < last_pointer_frame()) {
3111 _editor
->select_all_within (grab_frame(), last_pointer_frame() - 1, y1
, y2
, _editor
->track_views
, op
, false);
3113 _editor
->select_all_within (last_pointer_frame(), grab_frame() - 1, y1
, y2
, _editor
->track_views
, op
, false);
3116 _editor
->commit_reversible_command ();
3119 if (!getenv("ARDOUR_SAE")) {
3120 _editor
->selection
->clear_tracks();
3122 _editor
->selection
->clear_regions();
3123 _editor
->selection
->clear_points ();
3124 _editor
->selection
->clear_lines ();
3127 _editor
->rubberband_rect
->hide();
3131 RubberbandSelectDrag::aborted (bool)
3133 _editor
->rubberband_rect
->hide ();
3136 TimeFXDrag::TimeFXDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, std::list
<RegionView
*> const & v
)
3137 : RegionDrag (e
, i
, p
, v
)
3139 DEBUG_TRACE (DEBUG::Drags
, "New TimeFXDrag\n");
3143 TimeFXDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
3145 Drag::start_grab (event
, cursor
);
3147 show_verbose_cursor_time (adjusted_current_frame (event
));
3151 TimeFXDrag::motion (GdkEvent
* event
, bool)
3153 RegionView
* rv
= _primary
;
3155 framepos_t
const pf
= adjusted_current_frame (event
);
3157 if (pf
> rv
->region()->position()) {
3158 rv
->get_time_axis_view().show_timestretch (rv
->region()->position(), pf
);
3161 show_verbose_cursor_time (pf
);
3165 TimeFXDrag::finished (GdkEvent
* /*event*/, bool movement_occurred
)
3167 _primary
->get_time_axis_view().hide_timestretch ();
3169 if (!movement_occurred
) {
3173 if (last_pointer_frame() < _primary
->region()->position()) {
3174 /* backwards drag of the left edge - not usable */
3178 framecnt_t newlen
= last_pointer_frame() - _primary
->region()->position();
3180 float percentage
= (double) newlen
/ (double) _primary
->region()->length();
3182 #ifndef USE_RUBBERBAND
3183 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3184 if (_primary
->region()->data_type() == DataType::AUDIO
) {
3185 percentage
= (float) ((double) newlen
- (double) _primary
->region()->length()) / ((double) newlen
) * 100.0f
;
3189 // XXX how do timeFX on multiple regions ?
3194 if (_editor
->time_stretch (rs
, percentage
) == -1) {
3195 error
<< _("An error occurred while executing time stretch operation") << endmsg
;
3200 TimeFXDrag::aborted (bool)
3202 _primary
->get_time_axis_view().hide_timestretch ();
3205 ScrubDrag::ScrubDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3208 DEBUG_TRACE (DEBUG::Drags
, "New ScrubDrag\n");
3212 ScrubDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3214 Drag::start_grab (event
);
3218 ScrubDrag::motion (GdkEvent
* /*event*/, bool)
3220 _editor
->scrub (adjusted_current_frame (0, false), _drags
->current_pointer_x ());
3224 ScrubDrag::finished (GdkEvent
* /*event*/, bool movement_occurred
)
3226 if (movement_occurred
&& _editor
->session()) {
3227 /* make sure we stop */
3228 _editor
->session()->request_transport_speed (0.0);
3233 ScrubDrag::aborted (bool)
3238 SelectionDrag::SelectionDrag (Editor
* e
, ArdourCanvas::Item
* i
, Operation o
)
3242 , _original_pointer_time_axis (-1)
3243 , _last_pointer_time_axis (-1)
3245 DEBUG_TRACE (DEBUG::Drags
, "New SelectionDrag\n");
3249 SelectionDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3251 if (_editor
->session() == 0) {
3255 Gdk::Cursor
* cursor
= 0;
3257 switch (_operation
) {
3258 case CreateSelection
:
3259 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
3264 cursor
= _editor
->cursors()->selector
;
3265 Drag::start_grab (event
, cursor
);
3268 case SelectionStartTrim
:
3269 if (_editor
->clicked_axisview
) {
3270 _editor
->clicked_axisview
->order_selection_trims (_item
, true);
3272 Drag::start_grab (event
, _editor
->cursors()->left_side_trim
);
3275 case SelectionEndTrim
:
3276 if (_editor
->clicked_axisview
) {
3277 _editor
->clicked_axisview
->order_selection_trims (_item
, false);
3279 Drag::start_grab (event
, _editor
->cursors()->right_side_trim
);
3283 Drag::start_grab (event
, cursor
);
3287 if (_operation
== SelectionMove
) {
3288 show_verbose_cursor_time (_editor
->selection
->time
[_editor
->clicked_selection
].start
);
3290 show_verbose_cursor_time (adjusted_current_frame (event
));
3293 _original_pointer_time_axis
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ()).first
->order ();
3297 SelectionDrag::setup_pointer_frame_offset ()
3299 switch (_operation
) {
3300 case CreateSelection
:
3301 _pointer_frame_offset
= 0;
3304 case SelectionStartTrim
:
3306 _pointer_frame_offset
= raw_grab_frame() - _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3309 case SelectionEndTrim
:
3310 _pointer_frame_offset
= raw_grab_frame() - _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3316 SelectionDrag::motion (GdkEvent
* event
, bool first_move
)
3318 framepos_t start
= 0;
3322 pair
<TimeAxisView
*, int> const pending_time_axis
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
3323 if (pending_time_axis
.first
== 0) {
3327 framepos_t
const pending_position
= adjusted_current_frame (event
);
3329 /* only alter selection if things have changed */
3331 if (pending_time_axis
.first
->order() == _last_pointer_time_axis
&& pending_position
== last_pointer_frame()) {
3335 switch (_operation
) {
3336 case CreateSelection
:
3338 framepos_t grab
= grab_frame ();
3341 _editor
->snap_to (grab
);
3344 if (pending_position
< grab_frame()) {
3345 start
= pending_position
;
3348 end
= pending_position
;
3352 /* first drag: Either add to the selection
3353 or create a new selection
3359 /* adding to the selection */
3360 _editor
->set_selected_track_as_side_effect (Selection::Add
);
3361 //_editor->selection->add (_editor->clicked_axisview);
3362 _editor
->clicked_selection
= _editor
->selection
->add (start
, end
);
3367 if (_editor
->clicked_axisview
&& !_editor
->selection
->selected (_editor
->clicked_axisview
)) {
3368 //_editor->selection->set (_editor->clicked_axisview);
3369 _editor
->set_selected_track_as_side_effect (Selection::Set
);
3372 _editor
->clicked_selection
= _editor
->selection
->set (start
, end
);
3376 /* select the track that we're in */
3377 if (find (_added_time_axes
.begin(), _added_time_axes
.end(), pending_time_axis
.first
) == _added_time_axes
.end()) {
3378 // _editor->set_selected_track_as_side_effect (Selection::Add);
3379 _editor
->selection
->add (pending_time_axis
.first
);
3380 _added_time_axes
.push_back (pending_time_axis
.first
);
3383 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3384 tracks that we selected in the first place.
3387 int min_order
= min (_original_pointer_time_axis
, pending_time_axis
.first
->order());
3388 int max_order
= max (_original_pointer_time_axis
, pending_time_axis
.first
->order());
3390 list
<TimeAxisView
*>::iterator i
= _added_time_axes
.begin();
3391 while (i
!= _added_time_axes
.end()) {
3393 list
<TimeAxisView
*>::iterator tmp
= i
;
3396 if ((*i
)->order() < min_order
|| (*i
)->order() > max_order
) {
3397 _editor
->selection
->remove (*i
);
3398 _added_time_axes
.remove (*i
);
3407 case SelectionStartTrim
:
3409 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3410 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3412 if (pending_position
> end
) {
3415 start
= pending_position
;
3419 case SelectionEndTrim
:
3421 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3422 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3424 if (pending_position
< start
) {
3427 end
= pending_position
;
3434 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3435 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3437 length
= end
- start
;
3439 start
= pending_position
;
3440 _editor
->snap_to (start
);
3442 end
= start
+ length
;
3447 if (event
->button
.x
>= _editor
->horizontal_position() + _editor
->_canvas_width
) {
3448 _editor
->start_canvas_autoscroll (1, 0);
3452 _editor
->selection
->replace (_editor
->clicked_selection
, start
, end
);
3455 if (_operation
== SelectionMove
) {
3456 show_verbose_cursor_time(start
);
3458 show_verbose_cursor_time(pending_position
);
3463 SelectionDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3465 Session
* s
= _editor
->session();
3467 if (movement_occurred
) {
3468 motion (event
, false);
3469 /* XXX this is not object-oriented programming at all. ick */
3470 if (_editor
->selection
->time
.consolidate()) {
3471 _editor
->selection
->TimeChanged ();
3474 /* XXX what if its a music time selection? */
3475 if (s
&& (s
->config
.get_auto_play() || (s
->get_play_range() && s
->transport_rolling()))) {
3476 s
->request_play_range (&_editor
->selection
->time
, true);
3481 /* just a click, no pointer movement.*/
3483 if (Keyboard::no_modifier_keys_pressed (&event
->button
)) {
3484 _editor
->selection
->clear_time();
3487 if (_editor
->clicked_axisview
&& !_editor
->selection
->selected (_editor
->clicked_axisview
)) {
3488 _editor
->selection
->set (_editor
->clicked_axisview
);
3491 if (s
&& s
->get_play_range () && s
->transport_rolling()) {
3492 s
->request_stop (false, false);
3497 _editor
->stop_canvas_autoscroll ();
3501 SelectionDrag::aborted (bool)
3506 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor
* e
, ArdourCanvas::Item
* i
, Operation o
)
3511 DEBUG_TRACE (DEBUG::Drags
, "New RangeMarkerBarDrag\n");
3513 _drag_rect
= new ArdourCanvas::SimpleRect (*_editor
->time_line_group
, 0.0, 0.0, 0.0,
3514 physical_screen_height (_editor
->get_window()));
3515 _drag_rect
->hide ();
3517 _drag_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect
.get();
3518 _drag_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect
.get();
3522 RangeMarkerBarDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3524 if (_editor
->session() == 0) {
3528 Gdk::Cursor
* cursor
= 0;
3530 if (!_editor
->temp_location
) {
3531 _editor
->temp_location
= new Location (*_editor
->session());
3534 switch (_operation
) {
3535 case CreateRangeMarker
:
3536 case CreateTransportMarker
:
3537 case CreateCDMarker
:
3539 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
3544 cursor
= _editor
->cursors()->selector
;
3548 Drag::start_grab (event
, cursor
);
3550 show_verbose_cursor_time (adjusted_current_frame (event
));
3554 RangeMarkerBarDrag::motion (GdkEvent
* event
, bool first_move
)
3556 framepos_t start
= 0;
3558 ArdourCanvas::SimpleRect
*crect
;
3560 switch (_operation
) {
3561 case CreateRangeMarker
:
3562 crect
= _editor
->range_bar_drag_rect
;
3564 case CreateTransportMarker
:
3565 crect
= _editor
->transport_bar_drag_rect
;
3567 case CreateCDMarker
:
3568 crect
= _editor
->cd_marker_bar_drag_rect
;
3571 cerr
<< "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl
;
3576 framepos_t
const pf
= adjusted_current_frame (event
);
3578 if (_operation
== CreateRangeMarker
|| _operation
== CreateTransportMarker
|| _operation
== CreateCDMarker
) {
3579 framepos_t grab
= grab_frame ();
3580 _editor
->snap_to (grab
);
3582 if (pf
< grab_frame()) {
3590 /* first drag: Either add to the selection
3591 or create a new selection.
3596 _editor
->temp_location
->set (start
, end
);
3600 update_item (_editor
->temp_location
);
3602 //_drag_rect->raise_to_top();
3607 if (event
->button
.x
>= _editor
->horizontal_position() + _editor
->_canvas_width
) {
3608 _editor
->start_canvas_autoscroll (1, 0);
3612 _editor
->temp_location
->set (start
, end
);
3614 double x1
= _editor
->frame_to_pixel (start
);
3615 double x2
= _editor
->frame_to_pixel (end
);
3616 crect
->property_x1() = x1
;
3617 crect
->property_x2() = x2
;
3619 update_item (_editor
->temp_location
);
3622 show_verbose_cursor_time (pf
);
3627 RangeMarkerBarDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3629 Location
* newloc
= 0;
3633 if (movement_occurred
) {
3634 motion (event
, false);
3637 switch (_operation
) {
3638 case CreateRangeMarker
:
3639 case CreateCDMarker
:
3641 _editor
->begin_reversible_command (_("new range marker"));
3642 XMLNode
&before
= _editor
->session()->locations()->get_state();
3643 _editor
->session()->locations()->next_available_name(rangename
,"unnamed");
3644 if (_operation
== CreateCDMarker
) {
3645 flags
= Location::IsRangeMarker
| Location::IsCDMarker
;
3646 _editor
->cd_marker_bar_drag_rect
->hide();
3649 flags
= Location::IsRangeMarker
;
3650 _editor
->range_bar_drag_rect
->hide();
3652 newloc
= new Location (
3653 *_editor
->session(), _editor
->temp_location
->start(), _editor
->temp_location
->end(), rangename
, (Location::Flags
) flags
3656 _editor
->session()->locations()->add (newloc
, true);
3657 XMLNode
&after
= _editor
->session()->locations()->get_state();
3658 _editor
->session()->add_command(new MementoCommand
<Locations
>(*(_editor
->session()->locations()), &before
, &after
));
3659 _editor
->commit_reversible_command ();
3663 case CreateTransportMarker
:
3664 // popup menu to pick loop or punch
3665 _editor
->new_transport_marker_context_menu (&event
->button
, _item
);
3669 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3671 if (Keyboard::no_modifier_keys_pressed (&event
->button
) && _operation
!= CreateCDMarker
) {
3676 _editor
->session()->locations()->marks_either_side (grab_frame(), start
, end
);
3678 if (end
== max_framepos
) {
3679 end
= _editor
->session()->current_end_frame ();
3682 if (start
== max_framepos
) {
3683 start
= _editor
->session()->current_start_frame ();
3686 switch (_editor
->mouse_mode
) {
3688 /* find the two markers on either side and then make the selection from it */
3689 _editor
->select_all_within (start
, end
, 0.0f
, FLT_MAX
, _editor
->track_views
, Selection::Set
, false);
3693 /* find the two markers on either side of the click and make the range out of it */
3694 _editor
->selection
->set (start
, end
);
3703 _editor
->stop_canvas_autoscroll ();
3707 RangeMarkerBarDrag::aborted (bool)
3713 RangeMarkerBarDrag::update_item (Location
* location
)
3715 double const x1
= _editor
->frame_to_pixel (location
->start());
3716 double const x2
= _editor
->frame_to_pixel (location
->end());
3718 _drag_rect
->property_x1() = x1
;
3719 _drag_rect
->property_x2() = x2
;
3722 MouseZoomDrag::MouseZoomDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3726 DEBUG_TRACE (DEBUG::Drags
, "New MouseZoomDrag\n");
3730 MouseZoomDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3732 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L
)) {
3733 Drag::start_grab (event
, _editor
->cursors()->zoom_out
);
3736 Drag::start_grab (event
, _editor
->cursors()->zoom_in
);
3740 show_verbose_cursor_time (adjusted_current_frame (event
));
3744 MouseZoomDrag::motion (GdkEvent
* event
, bool first_move
)
3749 framepos_t
const pf
= adjusted_current_frame (event
);
3751 framepos_t grab
= grab_frame ();
3752 _editor
->snap_to_with_modifier (grab
, event
);
3754 /* base start and end on initial click position */
3766 _editor
->zoom_rect
->show();
3767 _editor
->zoom_rect
->raise_to_top();
3770 _editor
->reposition_zoom_rect(start
, end
);
3772 show_verbose_cursor_time (pf
);
3777 MouseZoomDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3779 if (movement_occurred
) {
3780 motion (event
, false);
3782 if (grab_frame() < last_pointer_frame()) {
3783 _editor
->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3785 _editor
->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3788 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L
)) {
3789 _editor
->tav_zoom_step (_zoom_out
);
3791 _editor
->temporal_zoom_to_frame (_zoom_out
, grab_frame());
3795 _editor
->zoom_rect
->hide();
3799 MouseZoomDrag::aborted (bool)
3801 _editor
->zoom_rect
->hide ();
3804 NoteDrag::NoteDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3806 , _cumulative_dx (0)
3807 , _cumulative_dy (0)
3809 DEBUG_TRACE (DEBUG::Drags
, "New NoteDrag\n");
3811 _primary
= dynamic_cast<CanvasNoteEvent
*> (_item
);
3812 _region
= &_primary
->region_view ();
3813 _note_height
= _region
->midi_stream_view()->note_height ();
3817 NoteDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3819 Drag::start_grab (event
);
3821 if (!(_was_selected
= _primary
->selected())) {
3823 /* tertiary-click means extend selection - we'll do that on button release,
3824 so don't add it here, because otherwise we make it hard to figure
3825 out the "extend-to" range.
3828 bool extend
= Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
);
3831 bool add
= Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
);
3834 _region
->note_selected (_primary
, true);
3836 _region
->unique_select (_primary
);
3842 /** @return Current total drag x change in frames */
3844 NoteDrag::total_dx () const
3847 frameoffset_t
const dx
= _editor
->unit_to_frame (_drags
->current_pointer_x() - grab_x());
3849 /* primary note time */
3850 frameoffset_t
const n
= _region
->beats_to_frames (_primary
->note()->time ());
3852 /* new time of the primary note relative to the region position */
3853 frameoffset_t st
= n
+ dx
;
3855 /* prevent the note being dragged earlier than the region's position */
3860 /* snap and return corresponding delta */
3861 return _region
->snap_frame_to_frame (st
) - n
;
3864 /** @return Current total drag y change in notes */
3866 NoteDrag::total_dy () const
3868 /* this is `backwards' to make increasing note number go in the right direction */
3869 double const dy
= _drags
->current_pointer_y() - grab_y();
3874 if (abs (dy
) >= _note_height
) {
3876 ndy
= (int8_t) ceil (dy
/ _note_height
/ 2.0);
3878 ndy
= (int8_t) floor (dy
/ _note_height
/ 2.0);
3882 /* more positive value = higher pitch and higher y-axis position on track,
3883 which is the inverse of the X-centric geometric universe
3890 NoteDrag::motion (GdkEvent
*, bool)
3892 /* Total change in x and y since the start of the drag */
3893 frameoffset_t
const dx
= total_dx ();
3894 int8_t const dy
= -total_dy ();
3896 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3897 double const tdx
= _editor
->frame_to_unit (dx
) - _cumulative_dx
;
3898 double const tdy
= dy
* _note_height
- _cumulative_dy
;
3901 _cumulative_dx
+= tdx
;
3902 _cumulative_dy
+= tdy
;
3904 int8_t note_delta
= total_dy();
3906 _region
->move_selection (tdx
, tdy
, note_delta
);
3909 snprintf (buf
, sizeof (buf
), "%s (%d)", Evoral::midi_note_name (_primary
->note()->note() + note_delta
).c_str(),
3910 (int) floor (_primary
->note()->note() + note_delta
));
3912 show_verbose_cursor_text (buf
);
3917 NoteDrag::finished (GdkEvent
* ev
, bool moved
)
3920 if (_editor
->current_mouse_mode() == Editing::MouseObject
) {
3922 if (_was_selected
) {
3923 bool add
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::PrimaryModifier
);
3925 _region
->note_deselected (_primary
);
3928 bool extend
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::TertiaryModifier
);
3929 bool add
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::PrimaryModifier
);
3931 if (!extend
&& !add
&& _region
->selection_size() > 1) {
3932 _region
->unique_select (_primary
);
3933 } else if (extend
) {
3934 _region
->note_selected (_primary
, true, true);
3936 /* it was added during button press */
3941 _region
->note_dropped (_primary
, total_dx(), total_dy());
3946 NoteDrag::aborted (bool)
3951 AutomationRangeDrag::AutomationRangeDrag (Editor
* editor
, ArdourCanvas::Item
* item
, list
<AudioRange
> const & r
)
3952 : Drag (editor
, item
)
3954 , _nothing_to_drag (false)
3956 DEBUG_TRACE (DEBUG::Drags
, "New AutomationRangeDrag\n");
3958 _atav
= reinterpret_cast<AutomationTimeAxisView
*> (_item
->get_data ("trackview"));
3961 /* get all lines in the automation view */
3962 list
<boost::shared_ptr
<AutomationLine
> > lines
= _atav
->lines ();
3964 /* find those that overlap the ranges being dragged */
3965 list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin ();
3966 while (i
!= lines
.end ()) {
3967 list
<boost::shared_ptr
<AutomationLine
> >::iterator j
= i
;
3970 pair
<framepos_t
, framepos_t
> const r
= (*i
)->get_point_x_range ();
3972 /* check this range against all the AudioRanges that we are using */
3973 list
<AudioRange
>::const_iterator k
= _ranges
.begin ();
3974 while (k
!= _ranges
.end()) {
3975 if (k
->coverage (r
.first
, r
.second
) != OverlapNone
) {
3981 /* add it to our list if it overlaps at all */
3982 if (k
!= _ranges
.end()) {
3987 _lines
.push_back (n
);
3993 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3997 AutomationRangeDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
3999 Drag::start_grab (event
, cursor
);
4001 /* Get line states before we start changing things */
4002 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4003 i
->state
= &i
->line
->get_state ();
4006 if (_ranges
.empty()) {
4008 /* No selected time ranges: drag all points */
4009 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4010 uint32_t const N
= i
->line
->npoints ();
4011 for (uint32_t j
= 0; j
< N
; ++j
) {
4012 i
->points
.push_back (i
->line
->nth (j
));
4018 for (list
<AudioRange
>::const_iterator i
= _ranges
.begin(); i
!= _ranges
.end(); ++i
) {
4020 framecnt_t
const half
= (i
->start
+ i
->end
) / 2;
4022 /* find the line that this audio range starts in */
4023 list
<Line
>::iterator j
= _lines
.begin();
4024 while (j
!= _lines
.end() && (j
->range
.first
> i
->start
|| j
->range
.second
< i
->start
)) {
4028 if (j
!= _lines
.end()) {
4029 boost::shared_ptr
<AutomationList
> the_list
= j
->line
->the_list ();
4031 /* j is the line that this audio range starts in; fade into it;
4032 64 samples length plucked out of thin air.
4035 framepos_t a
= i
->start
+ 64;
4040 double const p
= j
->line
->time_converter().from (i
->start
- j
->line
->time_converter().origin_b ());
4041 double const q
= j
->line
->time_converter().from (a
- j
->line
->time_converter().origin_b ());
4043 the_list
->add (p
, the_list
->eval (p
));
4044 j
->line
->add_always_in_view (p
);
4045 the_list
->add (q
, the_list
->eval (q
));
4046 j
->line
->add_always_in_view (q
);
4049 /* same thing for the end */
4052 while (j
!= _lines
.end() && (j
->range
.first
> i
->end
|| j
->range
.second
< i
->end
)) {
4056 if (j
!= _lines
.end()) {
4057 boost::shared_ptr
<AutomationList
> the_list
= j
->line
->the_list ();
4059 /* j is the line that this audio range starts in; fade out of it;
4060 64 samples length plucked out of thin air.
4063 framepos_t b
= i
->end
- 64;
4068 double const p
= j
->line
->time_converter().from (b
- j
->line
->time_converter().origin_b ());
4069 double const q
= j
->line
->time_converter().from (i
->end
- j
->line
->time_converter().origin_b ());
4071 the_list
->add (p
, the_list
->eval (p
));
4072 j
->line
->add_always_in_view (p
);
4073 the_list
->add (q
, the_list
->eval (q
));
4074 j
->line
->add_always_in_view (q
);
4078 _nothing_to_drag
= true;
4080 /* Find all the points that should be dragged and put them in the relevant
4081 points lists in the Line structs.
4084 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4086 uint32_t const N
= i
->line
->npoints ();
4087 for (uint32_t j
= 0; j
< N
; ++j
) {
4089 /* here's a control point on this line */
4090 ControlPoint
* p
= i
->line
->nth (j
);
4091 double const w
= i
->line
->time_converter().to ((*p
->model())->when
) + i
->line
->time_converter().origin_b ();
4093 /* see if it's inside a range */
4094 list
<AudioRange
>::const_iterator k
= _ranges
.begin ();
4095 while (k
!= _ranges
.end() && (k
->start
>= w
|| k
->end
<= w
)) {
4099 if (k
!= _ranges
.end()) {
4100 /* dragging this point */
4101 _nothing_to_drag
= false;
4102 i
->points
.push_back (p
);
4108 if (_nothing_to_drag
) {
4112 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4113 i
->line
->start_drag_multiple (i
->points
, 1 - (_drags
->current_pointer_y() / i
->line
->height ()), i
->state
);
4118 AutomationRangeDrag::motion (GdkEvent
*, bool /*first_move*/)
4120 if (_nothing_to_drag
) {
4124 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4125 float const f
= 1 - (_drags
->current_pointer_y() / i
->line
->height());
4127 /* we are ignoring x position for this drag, so we can just pass in anything */
4128 i
->line
->drag_motion (0, f
, true, false);
4133 AutomationRangeDrag::finished (GdkEvent
* event
, bool)
4135 if (_nothing_to_drag
) {
4139 motion (event
, false);
4140 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4141 i
->line
->end_drag ();
4142 i
->line
->clear_always_in_view ();
4145 _editor
->session()->commit_reversible_command ();
4149 AutomationRangeDrag::aborted (bool)
4151 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4152 i
->line
->clear_always_in_view ();
4157 DraggingView::DraggingView (RegionView
* v
, RegionDrag
* parent
)
4160 time_axis_view
= parent
->find_time_axis_view (&v
->get_time_axis_view ());
4161 layer
= v
->region()->layer ();
4162 initial_y
= v
->get_canvas_group()->property_y ();
4163 initial_playlist
= v
->region()->playlist ();
4164 initial_position
= v
->region()->position ();
4165 initial_end
= v
->region()->position () + v
->region()->length ();
4168 PatchChangeDrag::PatchChangeDrag (Editor
* e
, CanvasPatchChange
* i
, MidiRegionView
* r
)
4172 , _cumulative_dx (0)
4174 DEBUG_TRACE (DEBUG::Drags
, "New PatchChangeDrag\n");
4178 PatchChangeDrag::motion (GdkEvent
* ev
, bool)
4180 framepos_t f
= adjusted_current_frame (ev
);
4181 boost::shared_ptr
<Region
> r
= _region_view
->region ();
4182 f
= max (f
, r
->position ());
4183 f
= min (f
, r
->last_frame ());
4185 framecnt_t
const dxf
= f
- grab_frame();
4186 double const dxu
= _editor
->frame_to_unit (dxf
);
4187 _patch_change
->move (dxu
- _cumulative_dx
, 0);
4188 _cumulative_dx
= dxu
;
4192 PatchChangeDrag::finished (GdkEvent
* ev
, bool movement_occurred
)
4194 if (!movement_occurred
) {
4198 boost::shared_ptr
<Region
> r (_region_view
->region ());
4200 framepos_t f
= adjusted_current_frame (ev
);
4201 f
= max (f
, r
->position ());
4202 f
= min (f
, r
->last_frame ());
4204 _region_view
->move_patch_change (
4206 _region_view
->frames_to_beats (f
- r
->position() - r
->start())
4211 PatchChangeDrag::aborted (bool)
4213 _patch_change
->move (-_cumulative_dx
, 0);
4217 PatchChangeDrag::setup_pointer_frame_offset ()
4219 boost::shared_ptr
<Region
> region
= _region_view
->region ();
4220 _pointer_frame_offset
= raw_grab_frame() - _region_view
->beats_to_frames (_patch_change
->patch()->time()) - region
->position() + region
->start();