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 ControlPointDrag::_zero_gain_fraction
= -1.0;
71 DragManager::DragManager (Editor
* e
)
74 , _current_pointer_frame (0)
78 DragManager::~DragManager ()
83 /** Call abort for each active drag */
89 for (list
<Drag
*>::const_iterator i
= _drags
.begin(); i
!= _drags
.end(); ++i
) {
94 if (!_drags
.empty ()) {
95 _editor
->set_follow_playhead (_old_follow_playhead
, false);
104 DragManager::add (Drag
* d
)
106 d
->set_manager (this);
107 _drags
.push_back (d
);
111 DragManager::set (Drag
* d
, GdkEvent
* e
, Gdk::Cursor
* c
)
113 d
->set_manager (this);
114 _drags
.push_back (d
);
119 DragManager::start_grab (GdkEvent
* e
, Gdk::Cursor
* c
)
121 /* Prevent follow playhead during the drag to be nice to the user */
122 _old_follow_playhead
= _editor
->follow_playhead ();
123 _editor
->set_follow_playhead (false);
125 _current_pointer_frame
= _editor
->event_frame (e
, &_current_pointer_x
, &_current_pointer_y
);
127 for (list
<Drag
*>::const_iterator i
= _drags
.begin(); i
!= _drags
.end(); ++i
) {
128 (*i
)->start_grab (e
, c
);
132 /** Call end_grab for each active drag.
133 * @return true if any drag reported movement having occurred.
136 DragManager::end_grab (GdkEvent
* e
)
141 for (list
<Drag
*>::iterator i
= _drags
.begin(); i
!= _drags
.end(); ++i
) {
142 bool const t
= (*i
)->end_grab (e
);
153 _editor
->set_follow_playhead (_old_follow_playhead
, false);
159 DragManager::motion_handler (GdkEvent
* e
, bool from_autoscroll
)
163 _current_pointer_frame
= _editor
->event_frame (e
, &_current_pointer_x
, &_current_pointer_y
);
165 for (list
<Drag
*>::iterator i
= _drags
.begin(); i
!= _drags
.end(); ++i
) {
166 bool const t
= (*i
)->motion_handler (e
, from_autoscroll
);
177 DragManager::have_item (ArdourCanvas::Item
* i
) const
179 list
<Drag
*>::const_iterator j
= _drags
.begin ();
180 while (j
!= _drags
.end() && (*j
)->item () != i
) {
184 return j
!= _drags
.end ();
187 Drag::Drag (Editor
* e
, ArdourCanvas::Item
* i
)
190 , _pointer_frame_offset (0)
191 , _move_threshold_passed (false)
192 , _raw_grab_frame (0)
194 , _last_pointer_frame (0)
200 Drag::swap_grab (ArdourCanvas::Item
* new_item
, Gdk::Cursor
* cursor
, uint32_t time
)
206 cursor
= _editor
->which_grabber_cursor ();
209 _item
->grab (Gdk::POINTER_MOTION_MASK
| Gdk::BUTTON_PRESS_MASK
| Gdk::BUTTON_RELEASE_MASK
, *cursor
, time
);
213 Drag::start_grab (GdkEvent
* event
, Gdk::Cursor
*cursor
)
216 cursor
= _editor
->which_grabber_cursor ();
219 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
221 if (Keyboard::is_button2_event (&event
->button
)) {
222 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::SecondaryModifier
)) {
223 _y_constrained
= true;
224 _x_constrained
= false;
226 _y_constrained
= false;
227 _x_constrained
= true;
230 _x_constrained
= false;
231 _y_constrained
= false;
234 _raw_grab_frame
= _editor
->event_frame (event
, &_grab_x
, &_grab_y
);
235 setup_pointer_frame_offset ();
236 _grab_frame
= adjusted_frame (_raw_grab_frame
, event
);
237 _last_pointer_frame
= _grab_frame
;
238 _last_pointer_x
= _grab_x
;
239 _last_pointer_y
= _grab_y
;
241 _item
->grab (Gdk::POINTER_MOTION_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
,
245 if (_editor
->session() && _editor
->session()->transport_rolling()) {
248 _was_rolling
= false;
251 switch (_editor
->snap_type()) {
252 case SnapToRegionStart
:
253 case SnapToRegionEnd
:
254 case SnapToRegionSync
:
255 case SnapToRegionBoundary
:
256 _editor
->build_region_boundary_cache ();
263 /** Call to end a drag `successfully'. Ungrabs item and calls
264 * subclass' finished() method.
266 * @param event GDK event, or 0.
267 * @return true if some movement occurred, otherwise false.
270 Drag::end_grab (GdkEvent
* event
)
272 _editor
->stop_canvas_autoscroll ();
274 _item
->ungrab (event
? event
->button
.time
: 0);
276 finished (event
, _move_threshold_passed
);
278 _editor
->verbose_cursor()->hide ();
280 return _move_threshold_passed
;
284 Drag::adjusted_frame (framepos_t f
, GdkEvent
const * event
, bool snap
) const
288 if (f
> _pointer_frame_offset
) {
289 pos
= f
- _pointer_frame_offset
;
293 _editor
->snap_to_with_modifier (pos
, event
);
300 Drag::adjusted_current_frame (GdkEvent
const * event
, bool snap
) const
302 return adjusted_frame (_drags
->current_pointer_frame (), event
, snap
);
306 Drag::motion_handler (GdkEvent
* event
, bool from_autoscroll
)
308 /* check to see if we have moved in any way that matters since the last motion event */
309 if (_move_threshold_passed
&&
310 (!x_movement_matters() || _last_pointer_frame
== adjusted_current_frame (event
)) &&
311 (!y_movement_matters() || _last_pointer_y
== _drags
->current_pointer_y ()) ) {
315 pair
<framecnt_t
, int> const threshold
= move_threshold ();
317 bool const old_move_threshold_passed
= _move_threshold_passed
;
319 if (!from_autoscroll
&& !_move_threshold_passed
) {
321 bool const xp
= (::llabs (_drags
->current_pointer_frame () - _raw_grab_frame
) >= threshold
.first
);
322 bool const yp
= (::fabs ((_drags
->current_pointer_y () - _grab_y
)) >= threshold
.second
);
324 _move_threshold_passed
= ((xp
&& x_movement_matters()) || (yp
&& y_movement_matters()));
327 if (active (_editor
->mouse_mode
) && _move_threshold_passed
) {
329 if (event
->motion
.state
& Gdk::BUTTON1_MASK
|| event
->motion
.state
& Gdk::BUTTON2_MASK
) {
330 if (!from_autoscroll
) {
331 _editor
->maybe_autoscroll (true, allow_vertical_autoscroll ());
334 motion (event
, _move_threshold_passed
!= old_move_threshold_passed
);
336 _last_pointer_x
= _drags
->current_pointer_x ();
337 _last_pointer_y
= _drags
->current_pointer_y ();
338 _last_pointer_frame
= adjusted_current_frame (event
);
346 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
354 aborted (_move_threshold_passed
);
356 _editor
->stop_canvas_autoscroll ();
357 _editor
->verbose_cursor()->hide ();
361 Drag::show_verbose_cursor_time (framepos_t frame
)
363 _editor
->verbose_cursor()->set_time (
365 _drags
->current_pointer_x() + 10 - _editor
->horizontal_position(),
366 _drags
->current_pointer_y() + 10 - _editor
->vertical_adjustment
.get_value() + _editor
->canvas_timebars_vsize
369 _editor
->verbose_cursor()->show ();
373 Drag::show_verbose_cursor_duration (framepos_t start
, framepos_t end
, double xoffset
)
375 _editor
->verbose_cursor()->show (xoffset
);
377 _editor
->verbose_cursor()->set_duration (
379 _drags
->current_pointer_x() + 10 - _editor
->horizontal_position(),
380 _drags
->current_pointer_y() + 10 - _editor
->vertical_adjustment
.get_value() + _editor
->canvas_timebars_vsize
385 Drag::show_verbose_cursor_text (string
const & text
)
387 _editor
->verbose_cursor()->show ();
389 _editor
->verbose_cursor()->set (
391 _drags
->current_pointer_x() + 10 - _editor
->horizontal_position(),
392 _drags
->current_pointer_y() + 10 - _editor
->vertical_adjustment
.get_value() + _editor
->canvas_timebars_vsize
397 struct EditorOrderTimeAxisViewSorter
{
398 bool operator() (TimeAxisView
* a
, TimeAxisView
* b
) {
399 RouteTimeAxisView
* ra
= dynamic_cast<RouteTimeAxisView
*> (a
);
400 RouteTimeAxisView
* rb
= dynamic_cast<RouteTimeAxisView
*> (b
);
402 return ra
->route()->order_key (N_ ("editor")) < rb
->route()->order_key (N_ ("editor"));
406 RegionDrag::RegionDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
410 _editor
->visible_order_range (&_visible_y_low
, &_visible_y_high
);
412 /* Make a list of non-hidden tracks to refer to during the drag */
414 TrackViewList track_views
= _editor
->track_views
;
415 track_views
.sort (EditorOrderTimeAxisViewSorter ());
417 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
418 if (!(*i
)->hidden()) {
420 _time_axis_views
.push_back (*i
);
422 TimeAxisView::Children children_list
= (*i
)->get_child_list ();
423 for (TimeAxisView::Children::iterator j
= children_list
.begin(); j
!= children_list
.end(); ++j
) {
424 _time_axis_views
.push_back (j
->get());
429 /* the list of views can be empty at this point if this is a region list-insert drag
432 for (list
<RegionView
*>::const_iterator i
= v
.begin(); i
!= v
.end(); ++i
) {
433 _views
.push_back (DraggingView (*i
, this));
436 RegionView::RegionViewGoingAway
.connect (death_connection
, invalidator (*this), ui_bind (&RegionDrag::region_going_away
, this, _1
), gui_context());
440 RegionDrag::region_going_away (RegionView
* v
)
442 list
<DraggingView
>::iterator i
= _views
.begin ();
443 while (i
!= _views
.end() && i
->view
!= v
) {
447 if (i
!= _views
.end()) {
452 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
454 RegionDrag::find_time_axis_view (TimeAxisView
* t
) const
457 int const N
= _time_axis_views
.size ();
458 while (i
< N
&& _time_axis_views
[i
] != t
) {
469 RegionMotionDrag::RegionMotionDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
, bool b
)
470 : RegionDrag (e
, i
, p
, v
),
479 RegionMotionDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
481 Drag::start_grab (event
, cursor
);
483 show_verbose_cursor_time (_last_frame_position
);
485 pair
<TimeAxisView
*, int> const tv
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
486 _last_pointer_time_axis_view
= find_time_axis_view (tv
.first
);
487 _last_pointer_layer
= tv
.first
->layer_display() == Overlaid
? 0 : tv
.second
;
491 RegionMotionDrag::compute_x_delta (GdkEvent
const * event
, framepos_t
* pending_region_position
)
493 /* compute the amount of pointer motion in frames, and where
494 the region would be if we moved it by that much.
496 *pending_region_position
= adjusted_current_frame (event
);
498 framepos_t sync_frame
;
499 framecnt_t sync_offset
;
502 sync_offset
= _primary
->region()->sync_offset (sync_dir
);
504 /* we don't handle a sync point that lies before zero.
506 if (sync_dir
>= 0 || (sync_dir
< 0 && *pending_region_position
>= sync_offset
)) {
508 sync_frame
= *pending_region_position
+ (sync_dir
*sync_offset
);
510 _editor
->snap_to_with_modifier (sync_frame
, event
);
512 *pending_region_position
= _primary
->region()->adjust_to_sync (sync_frame
);
515 *pending_region_position
= _last_frame_position
;
518 if (*pending_region_position
> max_framepos
- _primary
->region()->length()) {
519 *pending_region_position
= _last_frame_position
;
524 /* in locked edit mode, reverse the usual meaning of _x_constrained */
525 bool const x_move_allowed
= Config
->get_edit_mode() == Lock
? _x_constrained
: !_x_constrained
;
527 if ((*pending_region_position
!= _last_frame_position
) && x_move_allowed
) {
529 /* x movement since last time (in pixels) */
530 dx
= (static_cast<double> (*pending_region_position
) - _last_frame_position
) / _editor
->frames_per_unit
;
532 /* total x movement */
533 framecnt_t total_dx
= *pending_region_position
;
534 if (regions_came_from_canvas()) {
535 total_dx
= total_dx
- grab_frame ();
538 /* check that no regions have gone off the start of the session */
539 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
540 if ((i
->view
->region()->position() + total_dx
) < 0) {
542 *pending_region_position
= _last_frame_position
;
547 _last_frame_position
= *pending_region_position
;
554 RegionMotionDrag::y_movement_allowed (int delta_track
, layer_t delta_layer
) const
556 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
557 int const n
= i
->time_axis_view
+ delta_track
;
558 if (n
< 0 || n
>= int (_time_axis_views
.size())) {
559 /* off the top or bottom track */
563 RouteTimeAxisView
const * to
= dynamic_cast<RouteTimeAxisView
const *> (_time_axis_views
[n
]);
564 if (to
== 0 || !to
->is_track() || to
->track()->data_type() != i
->view
->region()->data_type()) {
565 /* not a track, or the wrong type */
569 int const l
= i
->layer
+ delta_layer
;
570 if (delta_track
== 0 && (l
< 0 || l
>= int (to
->view()->layers()))) {
571 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
572 If it has, the layers will be munged later anyway, so it's ok.
578 /* all regions being dragged are ok with this change */
583 RegionMotionDrag::motion (GdkEvent
* event
, bool first_move
)
585 assert (!_views
.empty ());
587 /* Find the TimeAxisView that the pointer is now over */
588 pair
<TimeAxisView
*, int> const tv
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
590 /* Bail early if we're not over a track */
591 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tv
.first
);
592 if (!rtv
|| !rtv
->is_track()) {
593 _editor
->verbose_cursor()->hide ();
597 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
599 /* Here's the current pointer position in terms of time axis view and layer */
600 int const current_pointer_time_axis_view
= find_time_axis_view (tv
.first
);
601 layer_t
const current_pointer_layer
= tv
.first
->layer_display() == Overlaid
? 0 : tv
.second
;
603 /* Work out the change in x */
604 framepos_t pending_region_position
;
605 double const x_delta
= compute_x_delta (event
, &pending_region_position
);
607 /* Work out the change in y */
608 int delta_time_axis_view
= current_pointer_time_axis_view
- _last_pointer_time_axis_view
;
609 int delta_layer
= current_pointer_layer
- _last_pointer_layer
;
611 if (!y_movement_allowed (delta_time_axis_view
, delta_layer
)) {
612 /* this y movement is not allowed, so do no y movement this time */
613 delta_time_axis_view
= 0;
617 if (x_delta
== 0 && delta_time_axis_view
== 0 && delta_layer
== 0 && !first_move
) {
618 /* haven't reached next snap point, and we're not switching
619 trackviews nor layers. nothing to do.
624 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
626 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
628 RegionView
* rv
= i
->view
;
630 if (rv
->region()->locked()) {
636 /* here we are calculating the y distance from the
637 top of the first track view to the top of the region
638 area of the track view that we're working on */
640 /* this x value is just a dummy value so that we have something
645 /* distance from the top of this track view to the region area
646 of our track view is always 1 */
650 /* convert to world coordinates, ie distance from the top of
653 rv
->get_canvas_frame()->i2w (ix1
, iy1
);
655 /* compensate for the ruler section and the vertical scrollbar position */
656 iy1
+= _editor
->get_trackview_group_vertical_offset ();
658 // hide any dependent views
660 rv
->get_time_axis_view().hide_dependent_views (*rv
);
663 reparent to a non scrolling group so that we can keep the
664 region selection above all time axis views.
665 reparenting means we have to move the rv as the two
666 parent groups have different coordinates.
669 rv
->get_canvas_group()->property_y() = iy1
- 1;
670 rv
->get_canvas_group()->reparent (*(_editor
->_region_motion_group
));
672 rv
->fake_set_opaque (true);
675 /* Work out the change in y position of this region view */
679 /* If we have moved tracks, we'll fudge the layer delta so that the
680 region gets moved back onto layer 0 on its new track; this avoids
681 confusion when dragging regions from non-zero layers onto different
684 int this_delta_layer
= delta_layer
;
685 if (delta_time_axis_view
!= 0) {
686 this_delta_layer
= - i
->layer
;
689 /* Move this region to layer 0 on its old track */
690 StreamView
* lv
= _time_axis_views
[i
->time_axis_view
]->view ();
691 if (lv
->layer_display() == Stacked
) {
692 y_delta
-= (lv
->layers() - i
->layer
- 1) * lv
->child_height ();
695 /* Now move it to its right layer on the current track */
696 StreamView
* cv
= _time_axis_views
[i
->time_axis_view
+ delta_time_axis_view
]->view ();
697 if (cv
->layer_display() == Stacked
) {
698 y_delta
+= (cv
->layers() - (i
->layer
+ this_delta_layer
) - 1) * cv
->child_height ();
702 if (delta_time_axis_view
> 0) {
703 for (int j
= 0; j
< delta_time_axis_view
; ++j
) {
704 y_delta
+= _time_axis_views
[i
->time_axis_view
+ j
]->current_height ();
707 /* start by subtracting the height of the track above where we are now */
708 for (int j
= 1; j
<= -delta_time_axis_view
; ++j
) {
709 y_delta
-= _time_axis_views
[i
->time_axis_view
- j
]->current_height ();
714 rv
->set_height (_time_axis_views
[i
->time_axis_view
+ delta_time_axis_view
]->view()->child_height ());
716 /* Update the DraggingView */
717 i
->time_axis_view
+= delta_time_axis_view
;
718 i
->layer
+= this_delta_layer
;
721 _editor
->mouse_brush_insert_region (rv
, pending_region_position
);
723 rv
->move (x_delta
, y_delta
);
726 } /* foreach region */
728 _total_x_delta
+= x_delta
;
731 _editor
->cursor_group
->raise_to_top();
734 if (x_delta
!= 0 && !_brushing
) {
735 show_verbose_cursor_time (_last_frame_position
);
738 _last_pointer_time_axis_view
+= delta_time_axis_view
;
739 _last_pointer_layer
+= delta_layer
;
743 RegionMoveDrag::motion (GdkEvent
* event
, bool first_move
)
745 if (_copy
&& first_move
) {
747 /* duplicate the regionview(s) and region(s) */
749 list
<DraggingView
> new_regionviews
;
751 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
753 RegionView
* rv
= i
->view
;
754 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(rv
);
755 MidiRegionView
* mrv
= dynamic_cast<MidiRegionView
*>(rv
);
757 const boost::shared_ptr
<const Region
> original
= rv
->region();
758 boost::shared_ptr
<Region
> region_copy
= RegionFactory::create (original
, true);
759 region_copy
->set_position (original
->position());
763 boost::shared_ptr
<AudioRegion
> audioregion_copy
764 = boost::dynamic_pointer_cast
<AudioRegion
>(region_copy
);
766 nrv
= new AudioRegionView (*arv
, audioregion_copy
);
768 boost::shared_ptr
<MidiRegion
> midiregion_copy
769 = boost::dynamic_pointer_cast
<MidiRegion
>(region_copy
);
770 nrv
= new MidiRegionView (*mrv
, midiregion_copy
);
775 nrv
->get_canvas_group()->show ();
776 new_regionviews
.push_back (DraggingView (nrv
, this));
778 /* swap _primary to the copy */
780 if (rv
== _primary
) {
784 /* ..and deselect the one we copied */
786 rv
->set_selected (false);
789 if (!new_regionviews
.empty()) {
791 /* reflect the fact that we are dragging the copies */
793 _views
= new_regionviews
;
795 swap_grab (new_regionviews
.front().view
->get_canvas_group (), 0, event
? event
->motion
.time
: 0);
798 sync the canvas to what we think is its current state
799 without it, the canvas seems to
800 "forget" to update properly after the upcoming reparent()
801 ..only if the mouse is in rapid motion at the time of the grab.
802 something to do with regionview creation taking so long?
804 _editor
->update_canvas_now();
808 RegionMotionDrag::motion (event
, first_move
);
812 RegionMoveDrag::finished (GdkEvent
*, bool movement_occurred
)
814 if (!movement_occurred
) {
819 /* reverse this here so that we have the correct logic to finalize
823 if (Config
->get_edit_mode() == Lock
) {
824 _x_constrained
= !_x_constrained
;
827 assert (!_views
.empty ());
829 bool const changed_position
= (_last_frame_position
!= _primary
->region()->position());
830 bool const changed_tracks
= (_time_axis_views
[_views
.front().time_axis_view
] != &_views
.front().view
->get_time_axis_view());
831 framecnt_t
const drag_delta
= _primary
->region()->position() - _last_frame_position
;
833 _editor
->update_canvas_now ();
855 RegionMoveDrag::finished_copy (bool const changed_position
, bool const /*changed_tracks*/, framecnt_t
const drag_delta
)
857 RegionSelection new_views
;
858 PlaylistSet modified_playlists
;
859 list
<RegionView
*> views_to_delete
;
862 /* all changes were made during motion event handlers */
864 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
868 _editor
->commit_reversible_command ();
872 if (_x_constrained
) {
873 _editor
->begin_reversible_command (Operations::fixed_time_region_copy
);
875 _editor
->begin_reversible_command (Operations::region_copy
);
878 /* insert the regions into their new playlists */
879 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
881 if (i
->view
->region()->locked()) {
887 if (changed_position
&& !_x_constrained
) {
888 where
= i
->view
->region()->position() - drag_delta
;
890 where
= i
->view
->region()->position();
893 RegionView
* new_view
= insert_region_into_playlist (
894 i
->view
->region(), dynamic_cast<RouteTimeAxisView
*> (_time_axis_views
[i
->time_axis_view
]), i
->layer
, where
, modified_playlists
901 new_views
.push_back (new_view
);
903 /* we don't need the copied RegionView any more */
904 views_to_delete
.push_back (i
->view
);
907 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
908 because when views are deleted they are automagically removed from _views, which messes
911 for (list
<RegionView
*>::iterator i
= views_to_delete
.begin(); i
!= views_to_delete
.end(); ++i
) {
915 /* If we've created new regions either by copying or moving
916 to a new track, we want to replace the old selection with the new ones
919 if (new_views
.size() > 0) {
920 _editor
->selection
->set (new_views
);
923 /* write commands for the accumulated diffs for all our modified playlists */
924 add_stateful_diff_commands_for_playlists (modified_playlists
);
926 _editor
->commit_reversible_command ();
930 RegionMoveDrag::finished_no_copy (
931 bool const changed_position
,
932 bool const changed_tracks
,
933 framecnt_t
const drag_delta
936 RegionSelection new_views
;
937 PlaylistSet modified_playlists
;
938 PlaylistSet frozen_playlists
;
941 /* all changes were made during motion event handlers */
942 _editor
->commit_reversible_command ();
946 if (_x_constrained
) {
947 _editor
->begin_reversible_command (_("fixed time region drag"));
949 _editor
->begin_reversible_command (Operations::region_drag
);
952 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ) {
954 RegionView
* rv
= i
->view
;
956 RouteTimeAxisView
* const dest_rtv
= dynamic_cast<RouteTimeAxisView
*> (_time_axis_views
[i
->time_axis_view
]);
957 layer_t
const dest_layer
= i
->layer
;
959 if (rv
->region()->locked()) {
966 if (changed_position
&& !_x_constrained
) {
967 where
= rv
->region()->position() - drag_delta
;
969 where
= rv
->region()->position();
972 if (changed_tracks
) {
974 /* insert into new playlist */
976 RegionView
* new_view
= insert_region_into_playlist (
977 RegionFactory::create (rv
->region (), true), dest_rtv
, dest_layer
, where
, modified_playlists
985 new_views
.push_back (new_view
);
987 /* remove from old playlist */
989 /* the region that used to be in the old playlist is not
990 moved to the new one - we use a copy of it. as a result,
991 any existing editor for the region should no longer be
994 rv
->hide_region_editor();
995 rv
->fake_set_opaque (false);
997 remove_region_from_playlist (rv
->region(), i
->initial_playlist
, modified_playlists
);
1001 rv
->region()->clear_changes ();
1004 motion on the same track. plonk the previously reparented region
1005 back to its original canvas group (its streamview).
1006 No need to do anything for copies as they are fake regions which will be deleted.
1009 rv
->get_canvas_group()->reparent (*dest_rtv
->view()->canvas_item());
1010 rv
->get_canvas_group()->property_y() = i
->initial_y
;
1011 rv
->get_time_axis_view().reveal_dependent_views (*rv
);
1013 /* just change the model */
1015 boost::shared_ptr
<Playlist
> playlist
= dest_rtv
->playlist();
1017 if (dest_rtv
->view()->layer_display() == Stacked
) {
1018 rv
->region()->set_layer (dest_layer
);
1019 rv
->region()->set_pending_explicit_relayer (true);
1022 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1024 pair
<PlaylistSet::iterator
, bool> r
= frozen_playlists
.insert (playlist
);
1027 playlist
->freeze ();
1030 /* this movement may result in a crossfade being modified, so we need to get undo
1031 data from the playlist as well as the region.
1034 r
= modified_playlists
.insert (playlist
);
1036 playlist
->clear_changes ();
1039 rv
->region()->set_position (where
);
1041 _editor
->session()->add_command (new StatefulDiffCommand (rv
->region()));
1044 if (changed_tracks
) {
1046 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1047 was selected in all of them, then removing it from a playlist will have removed all
1048 trace of it from _views (i.e. there were N regions selected, we removed 1,
1049 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1050 corresponding regionview, and _views is now empty).
1052 This could have invalidated any and all iterators into _views.
1054 The heuristic we use here is: if the region selection is empty, break out of the loop
1055 here. if the region selection is not empty, then restart the loop because we know that
1056 we must have removed at least the region(view) we've just been working on as well as any
1057 that we processed on previous iterations.
1059 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1060 we can just iterate.
1064 if (_views
.empty()) {
1075 /* If we've created new regions either by copying or moving
1076 to a new track, we want to replace the old selection with the new ones
1079 if (new_views
.size() > 0) {
1080 _editor
->selection
->set (new_views
);
1083 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= frozen_playlists
.begin(); p
!= frozen_playlists
.end(); ++p
) {
1087 /* write commands for the accumulated diffs for all our modified playlists */
1088 add_stateful_diff_commands_for_playlists (modified_playlists
);
1090 _editor
->commit_reversible_command ();
1093 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1094 * @param region Region to remove.
1095 * @param playlist playlist To remove from.
1096 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1097 * that clear_changes () is only called once per playlist.
1100 RegionMoveDrag::remove_region_from_playlist (
1101 boost::shared_ptr
<Region
> region
,
1102 boost::shared_ptr
<Playlist
> playlist
,
1103 PlaylistSet
& modified_playlists
1106 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
, bool> r
= modified_playlists
.insert (playlist
);
1109 playlist
->clear_changes ();
1112 playlist
->remove_region (region
);
1116 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1117 * clearing the playlist's diff history first if necessary.
1118 * @param region Region to insert.
1119 * @param dest_rtv Destination RouteTimeAxisView.
1120 * @param dest_layer Destination layer.
1121 * @param where Destination position.
1122 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1123 * that clear_changes () is only called once per playlist.
1124 * @return New RegionView, or 0 if no insert was performed.
1127 RegionMoveDrag::insert_region_into_playlist (
1128 boost::shared_ptr
<Region
> region
,
1129 RouteTimeAxisView
* dest_rtv
,
1132 PlaylistSet
& modified_playlists
1135 boost::shared_ptr
<Playlist
> dest_playlist
= dest_rtv
->playlist ();
1136 if (!dest_playlist
) {
1140 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1141 _new_region_view
= 0;
1142 sigc::connection c
= dest_rtv
->view()->RegionViewAdded
.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view
));
1144 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1145 pair
<PlaylistSet::iterator
, bool> r
= modified_playlists
.insert (dest_playlist
);
1147 dest_playlist
->clear_changes ();
1150 dest_playlist
->add_region (region
, where
);
1152 if (dest_rtv
->view()->layer_display() == Stacked
) {
1153 region
->set_layer (dest_layer
);
1154 region
->set_pending_explicit_relayer (true);
1159 assert (_new_region_view
);
1161 return _new_region_view
;
1165 RegionMoveDrag::collect_new_region_view (RegionView
* rv
)
1167 _new_region_view
= rv
;
1171 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet
const & playlists
)
1173 for (PlaylistSet::const_iterator i
= playlists
.begin(); i
!= playlists
.end(); ++i
) {
1174 StatefulDiffCommand
* c
= new StatefulDiffCommand (*i
);
1176 _editor
->session()->add_command (c
);
1185 RegionMoveDrag::aborted (bool movement_occurred
)
1189 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1196 RegionMotionDrag::aborted (movement_occurred
);
1201 RegionMotionDrag::aborted (bool)
1203 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1204 RegionView
* rv
= i
->view
;
1205 TimeAxisView
* tv
= &(rv
->get_time_axis_view ());
1206 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (tv
);
1208 rv
->get_canvas_group()->reparent (*rtv
->view()->canvas_item());
1209 rv
->get_canvas_group()->property_y() = 0;
1210 rv
->get_time_axis_view().reveal_dependent_views (*rv
);
1211 rv
->fake_set_opaque (false);
1212 rv
->move (-_total_x_delta
, 0);
1213 rv
->set_height (rtv
->view()->child_height ());
1216 _editor
->update_canvas_now ();
1219 /** @param b true to brush, otherwise false.
1220 * @param c true to make copies of the regions being moved, otherwise false.
1222 RegionMoveDrag::RegionMoveDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
, bool b
, bool c
)
1223 : RegionMotionDrag (e
, i
, p
, v
, b
),
1226 DEBUG_TRACE (DEBUG::Drags
, "New RegionMoveDrag\n");
1229 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (&_primary
->get_time_axis_view ());
1230 if (rtv
&& rtv
->is_track()) {
1231 speed
= rtv
->track()->speed ();
1234 _last_frame_position
= static_cast<framepos_t
> (_primary
->region()->position() / speed
);
1238 RegionMoveDrag::setup_pointer_frame_offset ()
1240 _pointer_frame_offset
= raw_grab_frame() - _last_frame_position
;
1243 RegionInsertDrag::RegionInsertDrag (Editor
* e
, boost::shared_ptr
<Region
> r
, RouteTimeAxisView
* v
, framepos_t pos
)
1244 : RegionMotionDrag (e
, 0, 0, list
<RegionView
*> (), false)
1246 DEBUG_TRACE (DEBUG::Drags
, "New RegionInsertDrag\n");
1248 assert ((boost::dynamic_pointer_cast
<AudioRegion
> (r
) && dynamic_cast<AudioTimeAxisView
*> (v
)) ||
1249 (boost::dynamic_pointer_cast
<MidiRegion
> (r
) && dynamic_cast<MidiTimeAxisView
*> (v
)));
1251 _primary
= v
->view()->create_region_view (r
, false, false);
1253 _primary
->get_canvas_group()->show ();
1254 _primary
->set_position (pos
, 0);
1255 _views
.push_back (DraggingView (_primary
, this));
1257 _last_frame_position
= pos
;
1259 _item
= _primary
->get_canvas_group ();
1263 RegionInsertDrag::finished (GdkEvent
*, bool)
1265 _editor
->update_canvas_now ();
1267 RouteTimeAxisView
* dest_rtv
= dynamic_cast<RouteTimeAxisView
*> (_time_axis_views
[_views
.front().time_axis_view
]);
1269 _primary
->get_canvas_group()->reparent (*dest_rtv
->view()->canvas_item());
1270 _primary
->get_canvas_group()->property_y() = 0;
1272 boost::shared_ptr
<Playlist
> playlist
= dest_rtv
->playlist();
1274 _editor
->begin_reversible_command (Operations::insert_region
);
1275 playlist
->clear_changes ();
1276 playlist
->add_region (_primary
->region (), _last_frame_position
);
1277 _editor
->session()->add_command (new StatefulDiffCommand (playlist
));
1278 _editor
->commit_reversible_command ();
1286 RegionInsertDrag::aborted (bool)
1293 RegionSpliceDrag::RegionSpliceDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
1294 : RegionMoveDrag (e
, i
, p
, v
, false, false)
1296 DEBUG_TRACE (DEBUG::Drags
, "New RegionSpliceDrag\n");
1299 struct RegionSelectionByPosition
{
1300 bool operator() (RegionView
*a
, RegionView
* b
) {
1301 return a
->region()->position () < b
->region()->position();
1306 RegionSpliceDrag::motion (GdkEvent
* event
, bool)
1308 /* Which trackview is this ? */
1310 pair
<TimeAxisView
*, int> const tvp
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
1311 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*> (tvp
.first
);
1313 /* The region motion is only processed if the pointer is over
1317 if (!tv
|| !tv
->is_track()) {
1318 /* To make sure we hide the verbose canvas cursor when the mouse is
1319 not held over and audiotrack.
1321 _editor
->verbose_cursor()->hide ();
1327 if ((_drags
->current_pointer_x() - last_pointer_x()) > 0) {
1333 RegionSelection
copy (_editor
->selection
->regions
);
1335 RegionSelectionByPosition cmp
;
1338 framepos_t
const pf
= adjusted_current_frame (event
);
1340 for (RegionSelection::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
1342 RouteTimeAxisView
* atv
= dynamic_cast<RouteTimeAxisView
*> (&(*i
)->get_time_axis_view());
1348 boost::shared_ptr
<Playlist
> playlist
;
1350 if ((playlist
= atv
->playlist()) == 0) {
1354 if (!playlist
->region_is_shuffle_constrained ((*i
)->region())) {
1359 if (pf
< (*i
)->region()->last_frame() + 1) {
1363 if (pf
> (*i
)->region()->first_frame()) {
1369 playlist
->shuffle ((*i
)->region(), dir
);
1374 RegionSpliceDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1376 RegionMoveDrag::finished (event
, movement_occurred
);
1380 RegionSpliceDrag::aborted (bool)
1385 RegionCreateDrag::RegionCreateDrag (Editor
* e
, ArdourCanvas::Item
* i
, TimeAxisView
* v
)
1387 _view (dynamic_cast<MidiTimeAxisView
*> (v
))
1389 DEBUG_TRACE (DEBUG::Drags
, "New RegionCreateDrag\n");
1395 RegionCreateDrag::motion (GdkEvent
* event
, bool first_move
)
1399 _view
->playlist()->freeze ();
1402 framepos_t
const f
= adjusted_current_frame (event
);
1403 if (f
< grab_frame()) {
1404 _region
->set_position (f
);
1407 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1408 so that if this region is duplicated, its duplicate starts on
1409 a snap point rather than 1 frame after a snap point. Otherwise things get
1410 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1411 place snapped notes at the start of the region.
1414 framecnt_t
const len
= abs (f
- grab_frame () - 1);
1415 _region
->set_length (len
< 1 ? 1 : len
);
1421 RegionCreateDrag::finished (GdkEvent
*, bool movement_occurred
)
1423 if (!movement_occurred
) {
1426 _view
->playlist()->thaw ();
1430 _editor
->commit_reversible_command ();
1435 RegionCreateDrag::add_region ()
1437 if (_editor
->session()) {
1438 const TempoMap
& map (_editor
->session()->tempo_map());
1439 framecnt_t pos
= grab_frame();
1440 const Meter
& m
= map
.meter_at (pos
);
1441 /* not that the frame rate used here can be affected by pull up/down which
1444 framecnt_t len
= m
.frames_per_bar (map
.tempo_at (pos
), _editor
->session()->frame_rate());
1445 _region
= _view
->add_region (grab_frame(), len
, false);
1450 RegionCreateDrag::aborted (bool)
1453 _view
->playlist()->thaw ();
1459 NoteResizeDrag::NoteResizeDrag (Editor
* e
, ArdourCanvas::Item
* i
)
1463 DEBUG_TRACE (DEBUG::Drags
, "New NoteResizeDrag\n");
1467 NoteResizeDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*ignored*/)
1469 Gdk::Cursor
* cursor
;
1470 ArdourCanvas::CanvasNoteEvent
* cnote
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
);
1471 float x_fraction
= cnote
->mouse_x_fraction ();
1473 if (x_fraction
> 0.0 && x_fraction
< 0.25) {
1474 cursor
= _editor
->cursors()->left_side_trim
;
1476 cursor
= _editor
->cursors()->right_side_trim
;
1479 Drag::start_grab (event
, cursor
);
1481 region
= &cnote
->region_view();
1483 double const region_start
= region
->get_position_pixels();
1484 double const middle_point
= region_start
+ cnote
->x1() + (cnote
->x2() - cnote
->x1()) / 2.0L;
1486 if (grab_x() <= middle_point
) {
1487 cursor
= _editor
->cursors()->left_side_trim
;
1490 cursor
= _editor
->cursors()->right_side_trim
;
1494 _item
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
, *cursor
, event
->motion
.time
);
1496 if (event
->motion
.state
& Keyboard::PrimaryModifier
) {
1502 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1504 if (ms
.size() > 1) {
1505 /* has to be relative, may make no sense otherwise */
1509 /* select this note; if it is already selected, preserve the existing selection,
1510 otherwise make this note the only one selected.
1512 region
->note_selected (cnote
, cnote
->selected ());
1514 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ) {
1515 MidiRegionSelection::iterator next
;
1518 (*r
)->begin_resizing (at_front
);
1524 NoteResizeDrag::motion (GdkEvent
* /*event*/, bool /*first_move*/)
1526 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1527 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ++r
) {
1528 (*r
)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
), at_front
, _drags
->current_pointer_x() - grab_x(), relative
);
1533 NoteResizeDrag::finished (GdkEvent
*, bool /*movement_occurred*/)
1535 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1536 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ++r
) {
1537 (*r
)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
), at_front
, _drags
->current_pointer_x() - grab_x(), relative
);
1542 NoteResizeDrag::aborted (bool)
1547 RegionGainDrag::RegionGainDrag (Editor
* e
, ArdourCanvas::Item
* i
)
1550 DEBUG_TRACE (DEBUG::Drags
, "New RegionGainDrag\n");
1554 RegionGainDrag::motion (GdkEvent
* /*event*/, bool)
1560 RegionGainDrag::finished (GdkEvent
*, bool)
1566 RegionGainDrag::aborted (bool)
1571 TrimDrag::TrimDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
1572 : RegionDrag (e
, i
, p
, v
)
1574 DEBUG_TRACE (DEBUG::Drags
, "New TrimDrag\n");
1578 TrimDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
1581 TimeAxisView
* tvp
= &_primary
->get_time_axis_view ();
1582 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
1584 if (tv
&& tv
->is_track()) {
1585 speed
= tv
->track()->speed();
1588 framepos_t
const region_start
= (framepos_t
) (_primary
->region()->position() / speed
);
1589 framepos_t
const region_end
= (framepos_t
) (_primary
->region()->last_frame() / speed
);
1590 framecnt_t
const region_length
= (framecnt_t
) (_primary
->region()->length() / speed
);
1592 framepos_t
const pf
= adjusted_current_frame (event
);
1594 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1595 /* Move the contents of the region around without changing the region bounds */
1596 _operation
= ContentsTrim
;
1597 Drag::start_grab (event
, _editor
->cursors()->trimmer
);
1599 /* These will get overridden for a point trim.*/
1600 if (pf
< (region_start
+ region_length
/2)) {
1601 /* closer to front */
1602 _operation
= StartTrim
;
1603 Drag::start_grab (event
, _editor
->cursors()->left_side_trim
);
1606 _operation
= EndTrim
;
1607 Drag::start_grab (event
, _editor
->cursors()->right_side_trim
);
1611 switch (_operation
) {
1613 show_verbose_cursor_time (region_start
);
1614 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1615 i
->view
->trim_front_starting ();
1619 show_verbose_cursor_time (region_end
);
1622 show_verbose_cursor_time (pf
);
1626 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1627 i
->view
->region()->suspend_property_changes ();
1632 TrimDrag::motion (GdkEvent
* event
, bool first_move
)
1634 RegionView
* rv
= _primary
;
1637 TimeAxisView
* tvp
= &_primary
->get_time_axis_view ();
1638 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
1639 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
1641 if (tv
&& tv
->is_track()) {
1642 speed
= tv
->track()->speed();
1645 framecnt_t
const dt
= adjusted_current_frame (event
) - raw_grab_frame () + _pointer_frame_offset
;
1651 switch (_operation
) {
1653 trim_type
= "Region start trim";
1656 trim_type
= "Region end trim";
1659 trim_type
= "Region content trim";
1663 _editor
->begin_reversible_command (trim_type
);
1665 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1666 RegionView
* rv
= i
->view
;
1667 rv
->fake_set_opaque (false);
1668 rv
->enable_display (false);
1669 rv
->region()->playlist()->clear_owned_changes ();
1671 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*> (rv
);
1674 arv
->temporarily_hide_envelope ();
1677 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
1678 insert_result
= _editor
->motion_frozen_playlists
.insert (pl
);
1680 if (insert_result
.second
) {
1686 bool non_overlap_trim
= false;
1688 if (event
&& Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
1689 non_overlap_trim
= true;
1692 switch (_operation
) {
1694 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1695 i
->view
->trim_front (i
->initial_position
+ dt
, non_overlap_trim
);
1700 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1701 i
->view
->trim_end (i
->initial_end
+ dt
, non_overlap_trim
);
1707 bool swap_direction
= false;
1709 if (event
&& Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1710 swap_direction
= true;
1713 framecnt_t frame_delta
= 0;
1715 bool left_direction
= false;
1716 if (last_pointer_frame() > adjusted_current_frame(event
)) {
1717 left_direction
= true;
1720 if (left_direction
) {
1721 frame_delta
= (last_pointer_frame() - adjusted_current_frame(event
));
1723 frame_delta
= (adjusted_current_frame(event
) - last_pointer_frame());
1726 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1727 i
->view
->trim_contents (frame_delta
, left_direction
, swap_direction
);
1733 switch (_operation
) {
1735 show_verbose_cursor_time ((framepos_t
) (rv
->region()->position() / speed
));
1738 show_verbose_cursor_time ((framepos_t
) (rv
->region()->last_frame() / speed
));
1741 show_verbose_cursor_time (adjusted_current_frame (event
));
1748 TrimDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1750 if (movement_occurred
) {
1751 motion (event
, false);
1753 /* This must happen before the region's StatefulDiffCommand is created, as it may
1754 `correct' (ahem) the region's _start from being negative to being zero. It
1755 needs to be zero in the undo record.
1757 if (_operation
== StartTrim
) {
1758 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1759 i
->view
->trim_front_ending ();
1763 if (!_editor
->selection
->selected (_primary
)) {
1764 _primary
->thaw_after_trim ();
1767 set
<boost::shared_ptr
<Playlist
> > diffed_playlists
;
1769 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1770 i
->view
->thaw_after_trim ();
1771 i
->view
->enable_display (true);
1772 i
->view
->fake_set_opaque (true);
1774 /* Trimming one region may affect others on the playlist, so we need
1775 to get undo Commands from the whole playlist rather than just the
1776 region. Use diffed_playlists to make sure we don't diff a given
1777 playlist more than once.
1779 boost::shared_ptr
<Playlist
> p
= i
->view
->region()->playlist ();
1780 if (diffed_playlists
.find (p
) == diffed_playlists
.end()) {
1781 vector
<Command
*> cmds
;
1783 _editor
->session()->add_commands (cmds
);
1784 diffed_playlists
.insert (p
);
1788 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= _editor
->motion_frozen_playlists
.begin(); p
!= _editor
->motion_frozen_playlists
.end(); ++p
) {
1792 _editor
->motion_frozen_playlists
.clear ();
1793 _editor
->commit_reversible_command();
1796 /* no mouse movement */
1797 _editor
->point_trim (event
, adjusted_current_frame (event
));
1800 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1801 if (_operation
== StartTrim
) {
1802 i
->view
->trim_front_ending ();
1805 i
->view
->region()->resume_property_changes ();
1810 TrimDrag::aborted (bool movement_occurred
)
1812 /* Our motion method is changing model state, so use the Undo system
1813 to cancel. Perhaps not ideal, as this will leave an Undo point
1814 behind which may be slightly odd from the user's point of view.
1819 if (movement_occurred
) {
1823 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1824 i
->view
->region()->resume_property_changes ();
1829 TrimDrag::setup_pointer_frame_offset ()
1831 list
<DraggingView
>::iterator i
= _views
.begin ();
1832 while (i
!= _views
.end() && i
->view
!= _primary
) {
1836 if (i
== _views
.end()) {
1840 switch (_operation
) {
1842 _pointer_frame_offset
= raw_grab_frame() - i
->initial_position
;
1845 _pointer_frame_offset
= raw_grab_frame() - i
->initial_end
;
1852 MeterMarkerDrag::MeterMarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool c
)
1856 DEBUG_TRACE (DEBUG::Drags
, "New MeterMarkerDrag\n");
1858 _marker
= reinterpret_cast<MeterMarker
*> (_item
->get_data ("marker"));
1863 MeterMarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
1866 // create a dummy marker for visual representation of moving the copy.
1867 // The actual copying is not done before we reach the finish callback.
1869 snprintf (name
, sizeof(name
), "%g/%g", _marker
->meter().beats_per_bar(), _marker
->meter().note_divisor ());
1871 MeterMarker
* new_marker
= new MeterMarker (
1873 *_editor
->meter_group
,
1874 ARDOUR_UI::config()->canvasvar_MeterMarker
.get(),
1876 *new MeterSection (_marker
->meter())
1879 _item
= &new_marker
->the_item ();
1880 _marker
= new_marker
;
1884 MetricSection
& section (_marker
->meter());
1886 if (!section
.movable()) {
1892 Drag::start_grab (event
, cursor
);
1894 show_verbose_cursor_time (adjusted_current_frame(event
));
1898 MeterMarkerDrag::setup_pointer_frame_offset ()
1900 _pointer_frame_offset
= raw_grab_frame() - _marker
->meter().frame();
1904 MeterMarkerDrag::motion (GdkEvent
* event
, bool)
1906 framepos_t
const pf
= adjusted_current_frame (event
);
1908 _marker
->set_position (pf
);
1910 show_verbose_cursor_time (pf
);
1914 MeterMarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1916 if (!movement_occurred
) {
1920 motion (event
, false);
1922 Timecode::BBT_Time when
;
1924 TempoMap
& map (_editor
->session()->tempo_map());
1925 map
.bbt_time (last_pointer_frame(), when
);
1927 if (_copy
== true) {
1928 _editor
->begin_reversible_command (_("copy meter mark"));
1929 XMLNode
&before
= map
.get_state();
1930 map
.add_meter (_marker
->meter(), when
);
1931 XMLNode
&after
= map
.get_state();
1932 _editor
->session()->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
1933 _editor
->commit_reversible_command ();
1935 // delete the dummy marker we used for visual representation of copying.
1936 // a new visual marker will show up automatically.
1939 _editor
->begin_reversible_command (_("move meter mark"));
1940 XMLNode
&before
= map
.get_state();
1941 map
.move_meter (_marker
->meter(), when
);
1942 XMLNode
&after
= map
.get_state();
1943 _editor
->session()->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
1944 _editor
->commit_reversible_command ();
1949 MeterMarkerDrag::aborted (bool)
1951 _marker
->set_position (_marker
->meter().frame ());
1954 TempoMarkerDrag::TempoMarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool c
)
1958 DEBUG_TRACE (DEBUG::Drags
, "New TempoMarkerDrag\n");
1960 _marker
= reinterpret_cast<TempoMarker
*> (_item
->get_data ("marker"));
1965 TempoMarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
1969 // create a dummy marker for visual representation of moving the copy.
1970 // The actual copying is not done before we reach the finish callback.
1972 snprintf (name
, sizeof (name
), "%.2f", _marker
->tempo().beats_per_minute());
1974 TempoMarker
* new_marker
= new TempoMarker (
1976 *_editor
->tempo_group
,
1977 ARDOUR_UI::config()->canvasvar_TempoMarker
.get(),
1979 *new TempoSection (_marker
->tempo())
1982 _item
= &new_marker
->the_item ();
1983 _marker
= new_marker
;
1987 Drag::start_grab (event
, cursor
);
1989 show_verbose_cursor_time (adjusted_current_frame (event
));
1993 TempoMarkerDrag::setup_pointer_frame_offset ()
1995 _pointer_frame_offset
= raw_grab_frame() - _marker
->tempo().frame();
1999 TempoMarkerDrag::motion (GdkEvent
* event
, bool)
2001 framepos_t
const pf
= adjusted_current_frame (event
);
2002 _marker
->set_position (pf
);
2003 show_verbose_cursor_time (pf
);
2007 TempoMarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2009 if (!movement_occurred
) {
2013 motion (event
, false);
2015 Timecode::BBT_Time when
;
2017 TempoMap
& map (_editor
->session()->tempo_map());
2018 map
.bbt_time (last_pointer_frame(), when
);
2020 if (_copy
== true) {
2021 _editor
->begin_reversible_command (_("copy tempo mark"));
2022 XMLNode
&before
= map
.get_state();
2023 map
.add_tempo (_marker
->tempo(), when
);
2024 XMLNode
&after
= map
.get_state();
2025 _editor
->session()->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2026 _editor
->commit_reversible_command ();
2028 // delete the dummy marker we used for visual representation of copying.
2029 // a new visual marker will show up automatically.
2032 _editor
->begin_reversible_command (_("move tempo mark"));
2033 XMLNode
&before
= map
.get_state();
2034 map
.move_tempo (_marker
->tempo(), when
);
2035 XMLNode
&after
= map
.get_state();
2036 _editor
->session()->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2037 _editor
->commit_reversible_command ();
2042 TempoMarkerDrag::aborted (bool)
2044 _marker
->set_position (_marker
->tempo().frame());
2047 CursorDrag::CursorDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool s
)
2051 DEBUG_TRACE (DEBUG::Drags
, "New CursorDrag\n");
2054 /** Do all the things we do when dragging the playhead to make it look as though
2055 * we have located, without actually doing the locate (because that would cause
2056 * the diskstream buffers to be refilled, which is too slow).
2059 CursorDrag::fake_locate (framepos_t t
)
2061 _editor
->playhead_cursor
->set_position (t
);
2063 Session
* s
= _editor
->session ();
2064 if (s
->timecode_transmission_suspended ()) {
2065 framepos_t
const f
= _editor
->playhead_cursor
->current_frame
;
2066 s
->send_mmc_locate (f
);
2067 s
->send_full_time_code (f
);
2070 show_verbose_cursor_time (t
);
2071 _editor
->UpdateAllTransportClocks (t
);
2075 CursorDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* c
)
2077 Drag::start_grab (event
, c
);
2079 _grab_zoom
= _editor
->frames_per_unit
;
2081 framepos_t where
= _editor
->event_frame (event
, 0, 0);
2082 _editor
->snap_to_with_modifier (where
, event
);
2084 _editor
->_dragging_playhead
= true;
2086 Session
* s
= _editor
->session ();
2089 if (_was_rolling
&& _stop
) {
2093 if (s
->is_auditioning()) {
2094 s
->cancel_audition ();
2097 s
->request_suspend_timecode_transmission ();
2098 while (!s
->timecode_transmission_suspended ()) {
2099 /* twiddle our thumbs */
2103 fake_locate (where
);
2107 CursorDrag::motion (GdkEvent
* event
, bool)
2109 if (_drags
->current_pointer_y() != last_pointer_y()) {
2111 /* zoom when we move the pointer up and down */
2113 /* y range to operate over (pixels) */
2114 double const y_range
= 512;
2115 /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
2116 double const scale_range
= 4;
2117 /* dead zone around the grab point in which to do no zooming (pixels) */
2118 double const dead_zone
= 100;
2121 double dy
= _drags
->current_pointer_y() - grab_y();
2123 if (dy
< -dead_zone
|| dy
> dead_zone
) {
2124 /* we are outside the dead zone; remove it from our calculation */
2131 /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
2132 double udy
= max (min (dy
/ y_range
, 1.0), -1.0);
2134 /* and zoom, using playhead focus temporarily */
2135 Editing::ZoomFocus
const zf
= _editor
->get_zoom_focus ();
2136 _editor
->set_zoom_focus (Editing::ZoomFocusPlayhead
);
2137 _editor
->temporal_zoom (_grab_zoom
* pow (scale_range
, -udy
));
2138 _editor
->set_zoom_focus (zf
);
2142 framepos_t
const adjusted_frame
= adjusted_current_frame (event
);
2143 if (adjusted_frame
!= last_pointer_frame()) {
2144 fake_locate (adjusted_frame
);
2146 _editor
->update_canvas_now ();
2152 CursorDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2154 _editor
->_dragging_playhead
= false;
2156 if (!movement_occurred
&& _stop
) {
2160 motion (event
, false);
2162 Session
* s
= _editor
->session ();
2164 s
->request_locate (_editor
->playhead_cursor
->current_frame
, _was_rolling
);
2165 _editor
->_pending_locate_request
= true;
2166 s
->request_resume_timecode_transmission ();
2171 CursorDrag::aborted (bool)
2173 if (_editor
->_dragging_playhead
) {
2174 _editor
->session()->request_resume_timecode_transmission ();
2175 _editor
->_dragging_playhead
= false;
2178 _editor
->playhead_cursor
->set_position (adjusted_frame (grab_frame (), 0, false));
2181 FadeInDrag::FadeInDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
2182 : RegionDrag (e
, i
, p
, v
)
2184 DEBUG_TRACE (DEBUG::Drags
, "New FadeInDrag\n");
2188 FadeInDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2190 Drag::start_grab (event
, cursor
);
2192 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2193 boost::shared_ptr
<AudioRegion
> const r
= arv
->audio_region ();
2195 show_verbose_cursor_duration (r
->position(), r
->position() + r
->fade_in()->back()->when
, 32);
2197 arv
->show_fade_line((framepos_t
) r
->fade_in()->back()->when
);
2201 FadeInDrag::setup_pointer_frame_offset ()
2203 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2204 boost::shared_ptr
<AudioRegion
> const r
= arv
->audio_region ();
2205 _pointer_frame_offset
= raw_grab_frame() - ((framecnt_t
) r
->fade_in()->back()->when
+ r
->position());
2209 FadeInDrag::motion (GdkEvent
* event
, bool)
2211 framecnt_t fade_length
;
2213 framepos_t
const pos
= adjusted_current_frame (event
);
2215 boost::shared_ptr
<Region
> region
= _primary
->region ();
2217 if (pos
< (region
->position() + 64)) {
2218 fade_length
= 64; // this should be a minimum defined somewhere
2219 } else if (pos
> region
->last_frame()) {
2220 fade_length
= region
->length();
2222 fade_length
= pos
- region
->position();
2225 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2227 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2233 tmp
->reset_fade_in_shape_width (fade_length
);
2234 tmp
->show_fade_line((framecnt_t
) fade_length
);
2237 show_verbose_cursor_duration (region
->position(), region
->position() + fade_length
, 32);
2241 FadeInDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2243 if (!movement_occurred
) {
2247 framecnt_t fade_length
;
2249 framepos_t
const pos
= adjusted_current_frame (event
);
2251 boost::shared_ptr
<Region
> region
= _primary
->region ();
2253 if (pos
< (region
->position() + 64)) {
2254 fade_length
= 64; // this should be a minimum defined somewhere
2255 } else if (pos
> region
->last_frame()) {
2256 fade_length
= region
->length();
2258 fade_length
= pos
- region
->position();
2261 _editor
->begin_reversible_command (_("change fade in length"));
2263 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2265 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2271 boost::shared_ptr
<AutomationList
> alist
= tmp
->audio_region()->fade_in();
2272 XMLNode
&before
= alist
->get_state();
2274 tmp
->audio_region()->set_fade_in_length (fade_length
);
2275 tmp
->audio_region()->set_fade_in_active (true);
2276 tmp
->hide_fade_line();
2278 XMLNode
&after
= alist
->get_state();
2279 _editor
->session()->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &after
));
2282 _editor
->commit_reversible_command ();
2286 FadeInDrag::aborted (bool)
2288 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2289 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2295 tmp
->reset_fade_in_shape_width (tmp
->audio_region()->fade_in()->back()->when
);
2296 tmp
->hide_fade_line();
2300 FadeOutDrag::FadeOutDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
2301 : RegionDrag (e
, i
, p
, v
)
2303 DEBUG_TRACE (DEBUG::Drags
, "New FadeOutDrag\n");
2307 FadeOutDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2309 Drag::start_grab (event
, cursor
);
2311 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2312 boost::shared_ptr
<AudioRegion
> r
= arv
->audio_region ();
2314 show_verbose_cursor_duration (r
->last_frame() - r
->fade_out()->back()->when
, r
->last_frame());
2316 arv
->show_fade_line(r
->length() - r
->fade_out()->back()->when
);
2320 FadeOutDrag::setup_pointer_frame_offset ()
2322 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2323 boost::shared_ptr
<AudioRegion
> r
= arv
->audio_region ();
2324 _pointer_frame_offset
= raw_grab_frame() - (r
->length() - (framecnt_t
) r
->fade_out()->back()->when
+ r
->position());
2328 FadeOutDrag::motion (GdkEvent
* event
, bool)
2330 framecnt_t fade_length
;
2332 framepos_t
const pos
= adjusted_current_frame (event
);
2334 boost::shared_ptr
<Region
> region
= _primary
->region ();
2336 if (pos
> (region
->last_frame() - 64)) {
2337 fade_length
= 64; // this should really be a minimum fade defined somewhere
2339 else if (pos
< region
->position()) {
2340 fade_length
= region
->length();
2343 fade_length
= region
->last_frame() - pos
;
2346 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2348 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2354 tmp
->reset_fade_out_shape_width (fade_length
);
2355 tmp
->show_fade_line(region
->length() - fade_length
);
2358 show_verbose_cursor_duration (region
->last_frame() - fade_length
, region
->last_frame());
2362 FadeOutDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2364 if (!movement_occurred
) {
2368 framecnt_t fade_length
;
2370 framepos_t
const pos
= adjusted_current_frame (event
);
2372 boost::shared_ptr
<Region
> region
= _primary
->region ();
2374 if (pos
> (region
->last_frame() - 64)) {
2375 fade_length
= 64; // this should really be a minimum fade defined somewhere
2377 else if (pos
< region
->position()) {
2378 fade_length
= region
->length();
2381 fade_length
= region
->last_frame() - pos
;
2384 _editor
->begin_reversible_command (_("change fade out length"));
2386 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2388 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2394 boost::shared_ptr
<AutomationList
> alist
= tmp
->audio_region()->fade_out();
2395 XMLNode
&before
= alist
->get_state();
2397 tmp
->audio_region()->set_fade_out_length (fade_length
);
2398 tmp
->audio_region()->set_fade_out_active (true);
2399 tmp
->hide_fade_line();
2401 XMLNode
&after
= alist
->get_state();
2402 _editor
->session()->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &after
));
2405 _editor
->commit_reversible_command ();
2409 FadeOutDrag::aborted (bool)
2411 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2412 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2418 tmp
->reset_fade_out_shape_width (tmp
->audio_region()->fade_out()->back()->when
);
2419 tmp
->hide_fade_line();
2423 MarkerDrag::MarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2426 DEBUG_TRACE (DEBUG::Drags
, "New MarkerDrag\n");
2428 _marker
= reinterpret_cast<Marker
*> (_item
->get_data ("marker"));
2431 _points
.push_back (Gnome::Art::Point (0, 0));
2432 _points
.push_back (Gnome::Art::Point (0, physical_screen_height (_editor
->get_window())));
2435 MarkerDrag::~MarkerDrag ()
2437 for (list
<Location
*>::iterator i
= _copied_locations
.begin(); i
!= _copied_locations
.end(); ++i
) {
2443 MarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2445 Drag::start_grab (event
, cursor
);
2449 Location
*location
= _editor
->find_location_from_marker (_marker
, is_start
);
2450 _editor
->_dragging_edit_point
= true;
2452 update_item (location
);
2454 // _drag_line->show();
2455 // _line->raise_to_top();
2458 show_verbose_cursor_time (location
->start());
2460 show_verbose_cursor_time (location
->end());
2463 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
2466 case Selection::Toggle
:
2467 _editor
->selection
->toggle (_marker
);
2469 case Selection::Set
:
2470 if (!_editor
->selection
->selected (_marker
)) {
2471 _editor
->selection
->set (_marker
);
2474 case Selection::Extend
:
2476 Locations::LocationList ll
;
2477 list
<Marker
*> to_add
;
2479 _editor
->selection
->markers
.range (s
, e
);
2480 s
= min (_marker
->position(), s
);
2481 e
= max (_marker
->position(), e
);
2484 if (e
< max_framepos
) {
2487 _editor
->session()->locations()->find_all_between (s
, e
, ll
, Location::Flags (0));
2488 for (Locations::LocationList::iterator i
= ll
.begin(); i
!= ll
.end(); ++i
) {
2489 Editor::LocationMarkers
* lm
= _editor
->find_location_markers (*i
);
2492 to_add
.push_back (lm
->start
);
2495 to_add
.push_back (lm
->end
);
2499 if (!to_add
.empty()) {
2500 _editor
->selection
->add (to_add
);
2504 case Selection::Add
:
2505 _editor
->selection
->add (_marker
);
2509 /* Set up copies for us to manipulate during the drag */
2511 for (MarkerSelection::iterator i
= _editor
->selection
->markers
.begin(); i
!= _editor
->selection
->markers
.end(); ++i
) {
2512 Location
* l
= _editor
->find_location_from_marker (*i
, is_start
);
2513 _copied_locations
.push_back (new Location (*l
));
2518 MarkerDrag::setup_pointer_frame_offset ()
2521 Location
*location
= _editor
->find_location_from_marker (_marker
, is_start
);
2522 _pointer_frame_offset
= raw_grab_frame() - (is_start
? location
->start() : location
->end());
2526 MarkerDrag::motion (GdkEvent
* event
, bool)
2528 framecnt_t f_delta
= 0;
2530 bool move_both
= false;
2532 Location
*real_location
;
2533 Location
*copy_location
= 0;
2535 framepos_t
const newframe
= adjusted_current_frame (event
);
2537 framepos_t next
= newframe
;
2539 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
2543 MarkerSelection::iterator i
;
2544 list
<Location
*>::iterator x
;
2546 /* find the marker we're dragging, and compute the delta */
2548 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2549 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2555 if (marker
== _marker
) {
2557 if ((real_location
= _editor
->find_location_from_marker (marker
, is_start
)) == 0) {
2562 if (real_location
->is_mark()) {
2563 f_delta
= newframe
- copy_location
->start();
2567 switch (marker
->type()) {
2568 case Marker::SessionStart
:
2569 case Marker::RangeStart
:
2570 case Marker::LoopStart
:
2571 case Marker::PunchIn
:
2572 f_delta
= newframe
- copy_location
->start();
2575 case Marker::SessionEnd
:
2576 case Marker::RangeEnd
:
2577 case Marker::LoopEnd
:
2578 case Marker::PunchOut
:
2579 f_delta
= newframe
- copy_location
->end();
2582 /* what kind of marker is this ? */
2590 if (i
== _editor
->selection
->markers
.end()) {
2591 /* hmm, impossible - we didn't find the dragged marker */
2595 /* now move them all */
2597 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2598 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2604 /* call this to find out if its the start or end */
2606 if ((real_location
= _editor
->find_location_from_marker (marker
, is_start
)) == 0) {
2610 if (real_location
->locked()) {
2614 if (copy_location
->is_mark()) {
2618 copy_location
->set_start (copy_location
->start() + f_delta
);
2622 framepos_t new_start
= copy_location
->start() + f_delta
;
2623 framepos_t new_end
= copy_location
->end() + f_delta
;
2625 if (is_start
) { // start-of-range marker
2628 copy_location
->set_start (new_start
);
2629 copy_location
->set_end (new_end
);
2630 } else if (new_start
< copy_location
->end()) {
2631 copy_location
->set_start (new_start
);
2632 } else if (newframe
> 0) {
2633 _editor
->snap_to (next
, 1, true);
2634 copy_location
->set_end (next
);
2635 copy_location
->set_start (newframe
);
2638 } else { // end marker
2641 copy_location
->set_end (new_end
);
2642 copy_location
->set_start (new_start
);
2643 } else if (new_end
> copy_location
->start()) {
2644 copy_location
->set_end (new_end
);
2645 } else if (newframe
> 0) {
2646 _editor
->snap_to (next
, -1, true);
2647 copy_location
->set_start (next
);
2648 copy_location
->set_end (newframe
);
2653 update_item (copy_location
);
2655 Editor::LocationMarkers
* lm
= _editor
->find_location_markers (real_location
);
2658 lm
->set_position (copy_location
->start(), copy_location
->end());
2662 assert (!_copied_locations
.empty());
2664 show_verbose_cursor_time (newframe
);
2667 _editor
->update_canvas_now ();
2672 MarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2674 if (!movement_occurred
) {
2676 /* just a click, do nothing but finish
2677 off the selection process
2680 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
2683 case Selection::Set
:
2684 if (_editor
->selection
->selected (_marker
) && _editor
->selection
->markers
.size() > 1) {
2685 _editor
->selection
->set (_marker
);
2689 case Selection::Toggle
:
2690 case Selection::Extend
:
2691 case Selection::Add
:
2698 _editor
->_dragging_edit_point
= false;
2700 _editor
->begin_reversible_command ( _("move marker") );
2701 XMLNode
&before
= _editor
->session()->locations()->get_state();
2703 MarkerSelection::iterator i
;
2704 list
<Location
*>::iterator x
;
2707 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2708 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2711 Location
* location
= _editor
->find_location_from_marker (*i
, is_start
);
2715 if (location
->locked()) {
2719 if (location
->is_mark()) {
2720 location
->set_start ((*x
)->start());
2722 location
->set ((*x
)->start(), (*x
)->end());
2727 XMLNode
&after
= _editor
->session()->locations()->get_state();
2728 _editor
->session()->add_command(new MementoCommand
<Locations
>(*(_editor
->session()->locations()), &before
, &after
));
2729 _editor
->commit_reversible_command ();
2733 MarkerDrag::aborted (bool)
2739 MarkerDrag::update_item (Location
* location
)
2744 ControlPointDrag::ControlPointDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2746 _cumulative_x_drag (0),
2747 _cumulative_y_drag (0)
2749 if (_zero_gain_fraction
< 0.0) {
2750 _zero_gain_fraction
= gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config
->get_max_gain());
2753 DEBUG_TRACE (DEBUG::Drags
, "New ControlPointDrag\n");
2755 _point
= reinterpret_cast<ControlPoint
*> (_item
->get_data ("control_point"));
2761 ControlPointDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2763 Drag::start_grab (event
, _editor
->cursors()->fader
);
2765 // start the grab at the center of the control point so
2766 // the point doesn't 'jump' to the mouse after the first drag
2767 _fixed_grab_x
= _point
->get_x();
2768 _fixed_grab_y
= _point
->get_y();
2770 float const fraction
= 1 - (_point
->get_y() / _point
->line().height());
2772 _point
->line().start_drag_single (_point
, _fixed_grab_x
, fraction
);
2774 _editor
->verbose_cursor()->set (_point
->line().get_verbose_cursor_string (fraction
),
2775 event
->button
.x
+ 10, event
->button
.y
+ 10);
2777 _editor
->verbose_cursor()->show ();
2781 ControlPointDrag::motion (GdkEvent
* event
, bool)
2783 double dx
= _drags
->current_pointer_x() - last_pointer_x();
2784 double dy
= _drags
->current_pointer_y() - last_pointer_y();
2786 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
2791 /* coordinate in pixels relative to the start of the region (for region-based automation)
2792 or track (for track-based automation) */
2793 double cx
= _fixed_grab_x
+ _cumulative_x_drag
+ dx
;
2794 double cy
= _fixed_grab_y
+ _cumulative_y_drag
+ dy
;
2796 // calculate zero crossing point. back off by .01 to stay on the
2797 // positive side of zero
2798 double const zero_gain_y
= (1.0 - _zero_gain_fraction
) * _point
->line().height() - .01;
2800 // make sure we hit zero when passing through
2801 if ((cy
< zero_gain_y
&& (cy
- dy
) > zero_gain_y
) || (cy
> zero_gain_y
&& (cy
- dy
) < zero_gain_y
)) {
2805 if (_x_constrained
) {
2808 if (_y_constrained
) {
2812 _cumulative_x_drag
= cx
- _fixed_grab_x
;
2813 _cumulative_y_drag
= cy
- _fixed_grab_y
;
2817 cy
= min ((double) _point
->line().height(), cy
);
2819 framepos_t cx_frames
= _editor
->unit_to_frame (cx
);
2821 if (!_x_constrained
) {
2822 _editor
->snap_to_with_modifier (cx_frames
, event
);
2825 cx_frames
= min (cx_frames
, _point
->line().maximum_time());
2827 float const fraction
= 1.0 - (cy
/ _point
->line().height());
2829 bool const push
= Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
);
2831 _point
->line().drag_motion (_editor
->frame_to_unit (cx_frames
), fraction
, false, push
);
2833 _editor
->verbose_cursor()->set_text (_point
->line().get_verbose_cursor_string (fraction
));
2837 ControlPointDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2839 if (!movement_occurred
) {
2843 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
2844 _editor
->reset_point_selection ();
2848 motion (event
, false);
2851 _point
->line().end_drag ();
2852 _editor
->session()->commit_reversible_command ();
2856 ControlPointDrag::aborted (bool)
2858 _point
->line().reset ();
2862 ControlPointDrag::active (Editing::MouseMode m
)
2864 if (m
== Editing::MouseGain
) {
2865 /* always active in mouse gain */
2869 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2870 return dynamic_cast<AutomationLine
*> (&(_point
->line())) != 0;
2873 LineDrag::LineDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2876 _cumulative_y_drag (0)
2878 DEBUG_TRACE (DEBUG::Drags
, "New LineDrag\n");
2882 LineDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2884 _line
= reinterpret_cast<AutomationLine
*> (_item
->get_data ("line"));
2887 _item
= &_line
->grab_item ();
2889 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2890 origin, and ditto for y.
2893 double cx
= event
->button
.x
;
2894 double cy
= event
->button
.y
;
2896 _line
->parent_group().w2i (cx
, cy
);
2898 framecnt_t
const frame_within_region
= (framecnt_t
) floor (cx
* _editor
->frames_per_unit
);
2903 if (!_line
->control_points_adjacent (frame_within_region
, before
, after
)) {
2904 /* no adjacent points */
2908 Drag::start_grab (event
, _editor
->cursors()->fader
);
2910 /* store grab start in parent frame */
2915 double fraction
= 1.0 - (cy
/ _line
->height());
2917 _line
->start_drag_line (before
, after
, fraction
);
2919 _editor
->verbose_cursor()->set (_line
->get_verbose_cursor_string (fraction
),
2920 event
->button
.x
+ 10, event
->button
.y
+ 10);
2922 _editor
->verbose_cursor()->show ();
2926 LineDrag::motion (GdkEvent
* event
, bool)
2928 double dy
= _drags
->current_pointer_y() - last_pointer_y();
2930 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
2934 double cy
= _fixed_grab_y
+ _cumulative_y_drag
+ dy
;
2936 _cumulative_y_drag
= cy
- _fixed_grab_y
;
2939 cy
= min ((double) _line
->height(), cy
);
2941 double const fraction
= 1.0 - (cy
/ _line
->height());
2945 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
)) {
2951 /* we are ignoring x position for this drag, so we can just pass in anything */
2952 _line
->drag_motion (0, fraction
, true, push
);
2954 _editor
->verbose_cursor()->set_text (_line
->get_verbose_cursor_string (fraction
));
2958 LineDrag::finished (GdkEvent
* event
, bool)
2960 motion (event
, false);
2962 _editor
->session()->commit_reversible_command ();
2966 LineDrag::aborted (bool)
2971 FeatureLineDrag::FeatureLineDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2974 _cumulative_x_drag (0)
2976 DEBUG_TRACE (DEBUG::Drags
, "New FeatureLineDrag\n");
2980 FeatureLineDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2982 Drag::start_grab (event
);
2984 _line
= reinterpret_cast<Line
*> (_item
);
2987 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2989 double cx
= event
->button
.x
;
2990 double cy
= event
->button
.y
;
2992 _item
->property_parent().get_value()->w2i(cx
, cy
);
2994 /* store grab start in parent frame */
2995 _region_view_grab_x
= cx
;
2997 _before
= *(float*) _item
->get_data ("position");
2999 _arv
= reinterpret_cast<AudioRegionView
*> (_item
->get_data ("regionview"));
3001 _max_x
= _editor
->frame_to_pixel(_arv
->get_duration());
3005 FeatureLineDrag::motion (GdkEvent
*, bool)
3007 double dx
= _drags
->current_pointer_x() - last_pointer_x();
3009 double cx
= _region_view_grab_x
+ _cumulative_x_drag
+ dx
;
3011 _cumulative_x_drag
+= dx
;
3013 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3022 ArdourCanvas::Points points
;
3024 double x1
= 0, x2
= 0, y1
= 0, y2
= 0;
3026 _line
->get_bounds(x1
, y2
, x2
, y2
);
3028 points
.push_back(Gnome::Art::Point(cx
, 2.0)); // first x-coord needs to be a non-normal value
3029 points
.push_back(Gnome::Art::Point(cx
, y2
- y1
));
3031 _line
->property_points() = points
;
3033 float *pos
= new float;
3036 _line
->set_data ("position", pos
);
3042 FeatureLineDrag::finished (GdkEvent
*, bool)
3044 _arv
= reinterpret_cast<AudioRegionView
*> (_item
->get_data ("regionview"));
3045 _arv
->update_transient(_before
, _before
);
3049 FeatureLineDrag::aborted (bool)
3054 RubberbandSelectDrag::RubberbandSelectDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3057 DEBUG_TRACE (DEBUG::Drags
, "New RubberbandSelectDrag\n");
3061 RubberbandSelectDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3063 Drag::start_grab (event
);
3064 show_verbose_cursor_time (adjusted_current_frame (event
));
3068 RubberbandSelectDrag::motion (GdkEvent
* event
, bool)
3075 framepos_t
const pf
= adjusted_current_frame (event
, Config
->get_rubberbanding_snaps_to_grid ());
3077 framepos_t grab
= grab_frame ();
3078 if (Config
->get_rubberbanding_snaps_to_grid ()) {
3079 _editor
->snap_to_with_modifier (grab
, event
);
3082 /* base start and end on initial click position */
3092 if (_drags
->current_pointer_y() < grab_y()) {
3093 y1
= _drags
->current_pointer_y();
3096 y2
= _drags
->current_pointer_y();
3101 if (start
!= end
|| y1
!= y2
) {
3103 double x1
= _editor
->frame_to_pixel (start
);
3104 double x2
= _editor
->frame_to_pixel (end
);
3106 _editor
->rubberband_rect
->property_x1() = x1
;
3107 _editor
->rubberband_rect
->property_y1() = y1
;
3108 _editor
->rubberband_rect
->property_x2() = x2
;
3109 _editor
->rubberband_rect
->property_y2() = y2
;
3111 _editor
->rubberband_rect
->show();
3112 _editor
->rubberband_rect
->raise_to_top();
3114 show_verbose_cursor_time (pf
);
3119 RubberbandSelectDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3121 if (movement_occurred
) {
3123 motion (event
, false);
3126 if (_drags
->current_pointer_y() < grab_y()) {
3127 y1
= _drags
->current_pointer_y();
3130 y2
= _drags
->current_pointer_y();
3135 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
3137 _editor
->begin_reversible_command (_("rubberband selection"));
3139 if (grab_frame() < last_pointer_frame()) {
3140 _editor
->select_all_within (grab_frame(), last_pointer_frame() - 1, y1
, y2
, _editor
->track_views
, op
, false);
3142 _editor
->select_all_within (last_pointer_frame(), grab_frame() - 1, y1
, y2
, _editor
->track_views
, op
, false);
3145 _editor
->commit_reversible_command ();
3148 if (!getenv("ARDOUR_SAE")) {
3149 _editor
->selection
->clear_tracks();
3151 _editor
->selection
->clear_regions();
3152 _editor
->selection
->clear_points ();
3153 _editor
->selection
->clear_lines ();
3156 _editor
->rubberband_rect
->hide();
3160 RubberbandSelectDrag::aborted (bool)
3162 _editor
->rubberband_rect
->hide ();
3165 TimeFXDrag::TimeFXDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, std::list
<RegionView
*> const & v
)
3166 : RegionDrag (e
, i
, p
, v
)
3168 DEBUG_TRACE (DEBUG::Drags
, "New TimeFXDrag\n");
3172 TimeFXDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
3174 Drag::start_grab (event
, cursor
);
3176 show_verbose_cursor_time (adjusted_current_frame (event
));
3180 TimeFXDrag::motion (GdkEvent
* event
, bool)
3182 RegionView
* rv
= _primary
;
3184 framepos_t
const pf
= adjusted_current_frame (event
);
3186 if (pf
> rv
->region()->position()) {
3187 rv
->get_time_axis_view().show_timestretch (rv
->region()->position(), pf
);
3190 show_verbose_cursor_time (pf
);
3194 TimeFXDrag::finished (GdkEvent
* /*event*/, bool movement_occurred
)
3196 _primary
->get_time_axis_view().hide_timestretch ();
3198 if (!movement_occurred
) {
3202 if (last_pointer_frame() < _primary
->region()->position()) {
3203 /* backwards drag of the left edge - not usable */
3207 framecnt_t newlen
= last_pointer_frame() - _primary
->region()->position();
3209 float percentage
= (double) newlen
/ (double) _primary
->region()->length();
3211 #ifndef USE_RUBBERBAND
3212 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3213 if (_primary
->region()->data_type() == DataType::AUDIO
) {
3214 percentage
= (float) ((double) newlen
- (double) _primary
->region()->length()) / ((double) newlen
) * 100.0f
;
3218 // XXX how do timeFX on multiple regions ?
3223 if (_editor
->time_stretch (rs
, percentage
) == -1) {
3224 error
<< _("An error occurred while executing time stretch operation") << endmsg
;
3229 TimeFXDrag::aborted (bool)
3231 _primary
->get_time_axis_view().hide_timestretch ();
3234 ScrubDrag::ScrubDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3237 DEBUG_TRACE (DEBUG::Drags
, "New ScrubDrag\n");
3241 ScrubDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3243 Drag::start_grab (event
);
3247 ScrubDrag::motion (GdkEvent
* /*event*/, bool)
3249 _editor
->scrub (adjusted_current_frame (0, false), _drags
->current_pointer_x ());
3253 ScrubDrag::finished (GdkEvent
* /*event*/, bool movement_occurred
)
3255 if (movement_occurred
&& _editor
->session()) {
3256 /* make sure we stop */
3257 _editor
->session()->request_transport_speed (0.0);
3262 ScrubDrag::aborted (bool)
3267 SelectionDrag::SelectionDrag (Editor
* e
, ArdourCanvas::Item
* i
, Operation o
)
3271 , _original_pointer_time_axis (-1)
3272 , _last_pointer_time_axis (-1)
3274 DEBUG_TRACE (DEBUG::Drags
, "New SelectionDrag\n");
3278 SelectionDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3280 if (_editor
->session() == 0) {
3284 Gdk::Cursor
* cursor
= 0;
3286 switch (_operation
) {
3287 case CreateSelection
:
3288 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
3293 cursor
= _editor
->cursors()->selector
;
3294 Drag::start_grab (event
, cursor
);
3297 case SelectionStartTrim
:
3298 if (_editor
->clicked_axisview
) {
3299 _editor
->clicked_axisview
->order_selection_trims (_item
, true);
3301 Drag::start_grab (event
, _editor
->cursors()->left_side_trim
);
3304 case SelectionEndTrim
:
3305 if (_editor
->clicked_axisview
) {
3306 _editor
->clicked_axisview
->order_selection_trims (_item
, false);
3308 Drag::start_grab (event
, _editor
->cursors()->right_side_trim
);
3312 Drag::start_grab (event
, cursor
);
3316 if (_operation
== SelectionMove
) {
3317 show_verbose_cursor_time (_editor
->selection
->time
[_editor
->clicked_selection
].start
);
3319 show_verbose_cursor_time (adjusted_current_frame (event
));
3322 _original_pointer_time_axis
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ()).first
->order ();
3326 SelectionDrag::setup_pointer_frame_offset ()
3328 switch (_operation
) {
3329 case CreateSelection
:
3330 _pointer_frame_offset
= 0;
3333 case SelectionStartTrim
:
3335 _pointer_frame_offset
= raw_grab_frame() - _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3338 case SelectionEndTrim
:
3339 _pointer_frame_offset
= raw_grab_frame() - _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3345 SelectionDrag::motion (GdkEvent
* event
, bool first_move
)
3347 framepos_t start
= 0;
3351 pair
<TimeAxisView
*, int> const pending_time_axis
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
3352 if (pending_time_axis
.first
== 0) {
3356 framepos_t
const pending_position
= adjusted_current_frame (event
);
3358 /* only alter selection if things have changed */
3360 if (pending_time_axis
.first
->order() == _last_pointer_time_axis
&& pending_position
== last_pointer_frame()) {
3364 switch (_operation
) {
3365 case CreateSelection
:
3367 framepos_t grab
= grab_frame ();
3370 _editor
->snap_to (grab
);
3373 if (pending_position
< grab_frame()) {
3374 start
= pending_position
;
3377 end
= pending_position
;
3381 /* first drag: Either add to the selection
3382 or create a new selection
3388 /* adding to the selection */
3389 _editor
->set_selected_track_as_side_effect (Selection::Add
);
3390 //_editor->selection->add (_editor->clicked_axisview);
3391 _editor
->clicked_selection
= _editor
->selection
->add (start
, end
);
3396 if (_editor
->clicked_axisview
&& !_editor
->selection
->selected (_editor
->clicked_axisview
)) {
3397 //_editor->selection->set (_editor->clicked_axisview);
3398 _editor
->set_selected_track_as_side_effect (Selection::Set
);
3401 _editor
->clicked_selection
= _editor
->selection
->set (start
, end
);
3405 /* select the track that we're in */
3406 if (find (_added_time_axes
.begin(), _added_time_axes
.end(), pending_time_axis
.first
) == _added_time_axes
.end()) {
3407 // _editor->set_selected_track_as_side_effect (Selection::Add);
3408 _editor
->selection
->add (pending_time_axis
.first
);
3409 _added_time_axes
.push_back (pending_time_axis
.first
);
3412 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3413 tracks that we selected in the first place.
3416 int min_order
= min (_original_pointer_time_axis
, pending_time_axis
.first
->order());
3417 int max_order
= max (_original_pointer_time_axis
, pending_time_axis
.first
->order());
3419 list
<TimeAxisView
*>::iterator i
= _added_time_axes
.begin();
3420 while (i
!= _added_time_axes
.end()) {
3422 list
<TimeAxisView
*>::iterator tmp
= i
;
3425 if ((*i
)->order() < min_order
|| (*i
)->order() > max_order
) {
3426 _editor
->selection
->remove (*i
);
3427 _added_time_axes
.remove (*i
);
3436 case SelectionStartTrim
:
3438 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3439 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3441 if (pending_position
> end
) {
3444 start
= pending_position
;
3448 case SelectionEndTrim
:
3450 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3451 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3453 if (pending_position
< start
) {
3456 end
= pending_position
;
3463 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3464 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3466 length
= end
- start
;
3468 start
= pending_position
;
3469 _editor
->snap_to (start
);
3471 end
= start
+ length
;
3476 if (event
->button
.x
>= _editor
->horizontal_position() + _editor
->_canvas_width
) {
3477 _editor
->start_canvas_autoscroll (1, 0);
3481 _editor
->selection
->replace (_editor
->clicked_selection
, start
, end
);
3484 if (_operation
== SelectionMove
) {
3485 show_verbose_cursor_time(start
);
3487 show_verbose_cursor_time(pending_position
);
3492 SelectionDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3494 Session
* s
= _editor
->session();
3496 if (movement_occurred
) {
3497 motion (event
, false);
3498 /* XXX this is not object-oriented programming at all. ick */
3499 if (_editor
->selection
->time
.consolidate()) {
3500 _editor
->selection
->TimeChanged ();
3503 /* XXX what if its a music time selection? */
3504 if (s
&& (s
->config
.get_auto_play() || (s
->get_play_range() && s
->transport_rolling()))) {
3505 s
->request_play_range (&_editor
->selection
->time
, true);
3510 /* just a click, no pointer movement.*/
3512 if (Keyboard::no_modifier_keys_pressed (&event
->button
)) {
3513 _editor
->selection
->clear_time();
3516 if (_editor
->clicked_axisview
&& !_editor
->selection
->selected (_editor
->clicked_axisview
)) {
3517 _editor
->selection
->set (_editor
->clicked_axisview
);
3520 if (s
&& s
->get_play_range () && s
->transport_rolling()) {
3521 s
->request_stop (false, false);
3526 _editor
->stop_canvas_autoscroll ();
3530 SelectionDrag::aborted (bool)
3535 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor
* e
, ArdourCanvas::Item
* i
, Operation o
)
3540 DEBUG_TRACE (DEBUG::Drags
, "New RangeMarkerBarDrag\n");
3542 _drag_rect
= new ArdourCanvas::SimpleRect (*_editor
->time_line_group
, 0.0, 0.0, 0.0,
3543 physical_screen_height (_editor
->get_window()));
3544 _drag_rect
->hide ();
3546 _drag_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect
.get();
3547 _drag_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect
.get();
3551 RangeMarkerBarDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3553 if (_editor
->session() == 0) {
3557 Gdk::Cursor
* cursor
= 0;
3559 if (!_editor
->temp_location
) {
3560 _editor
->temp_location
= new Location (*_editor
->session());
3563 switch (_operation
) {
3564 case CreateRangeMarker
:
3565 case CreateTransportMarker
:
3566 case CreateCDMarker
:
3568 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
3573 cursor
= _editor
->cursors()->selector
;
3577 Drag::start_grab (event
, cursor
);
3579 show_verbose_cursor_time (adjusted_current_frame (event
));
3583 RangeMarkerBarDrag::motion (GdkEvent
* event
, bool first_move
)
3585 framepos_t start
= 0;
3587 ArdourCanvas::SimpleRect
*crect
;
3589 switch (_operation
) {
3590 case CreateRangeMarker
:
3591 crect
= _editor
->range_bar_drag_rect
;
3593 case CreateTransportMarker
:
3594 crect
= _editor
->transport_bar_drag_rect
;
3596 case CreateCDMarker
:
3597 crect
= _editor
->cd_marker_bar_drag_rect
;
3600 cerr
<< "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl
;
3605 framepos_t
const pf
= adjusted_current_frame (event
);
3607 if (_operation
== CreateRangeMarker
|| _operation
== CreateTransportMarker
|| _operation
== CreateCDMarker
) {
3608 framepos_t grab
= grab_frame ();
3609 _editor
->snap_to (grab
);
3611 if (pf
< grab_frame()) {
3619 /* first drag: Either add to the selection
3620 or create a new selection.
3625 _editor
->temp_location
->set (start
, end
);
3629 update_item (_editor
->temp_location
);
3631 //_drag_rect->raise_to_top();
3636 if (event
->button
.x
>= _editor
->horizontal_position() + _editor
->_canvas_width
) {
3637 _editor
->start_canvas_autoscroll (1, 0);
3641 _editor
->temp_location
->set (start
, end
);
3643 double x1
= _editor
->frame_to_pixel (start
);
3644 double x2
= _editor
->frame_to_pixel (end
);
3645 crect
->property_x1() = x1
;
3646 crect
->property_x2() = x2
;
3648 update_item (_editor
->temp_location
);
3651 show_verbose_cursor_time (pf
);
3656 RangeMarkerBarDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3658 Location
* newloc
= 0;
3662 if (movement_occurred
) {
3663 motion (event
, false);
3666 switch (_operation
) {
3667 case CreateRangeMarker
:
3668 case CreateCDMarker
:
3670 _editor
->begin_reversible_command (_("new range marker"));
3671 XMLNode
&before
= _editor
->session()->locations()->get_state();
3672 _editor
->session()->locations()->next_available_name(rangename
,"unnamed");
3673 if (_operation
== CreateCDMarker
) {
3674 flags
= Location::IsRangeMarker
| Location::IsCDMarker
;
3675 _editor
->cd_marker_bar_drag_rect
->hide();
3678 flags
= Location::IsRangeMarker
;
3679 _editor
->range_bar_drag_rect
->hide();
3681 newloc
= new Location (
3682 *_editor
->session(), _editor
->temp_location
->start(), _editor
->temp_location
->end(), rangename
, (Location::Flags
) flags
3685 _editor
->session()->locations()->add (newloc
, true);
3686 XMLNode
&after
= _editor
->session()->locations()->get_state();
3687 _editor
->session()->add_command(new MementoCommand
<Locations
>(*(_editor
->session()->locations()), &before
, &after
));
3688 _editor
->commit_reversible_command ();
3692 case CreateTransportMarker
:
3693 // popup menu to pick loop or punch
3694 _editor
->new_transport_marker_context_menu (&event
->button
, _item
);
3698 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3700 if (Keyboard::no_modifier_keys_pressed (&event
->button
) && _operation
!= CreateCDMarker
) {
3705 _editor
->session()->locations()->marks_either_side (grab_frame(), start
, end
);
3707 if (end
== max_framepos
) {
3708 end
= _editor
->session()->current_end_frame ();
3711 if (start
== max_framepos
) {
3712 start
= _editor
->session()->current_start_frame ();
3715 switch (_editor
->mouse_mode
) {
3717 /* find the two markers on either side and then make the selection from it */
3718 _editor
->select_all_within (start
, end
, 0.0f
, FLT_MAX
, _editor
->track_views
, Selection::Set
, false);
3722 /* find the two markers on either side of the click and make the range out of it */
3723 _editor
->selection
->set (start
, end
);
3732 _editor
->stop_canvas_autoscroll ();
3736 RangeMarkerBarDrag::aborted (bool)
3742 RangeMarkerBarDrag::update_item (Location
* location
)
3744 double const x1
= _editor
->frame_to_pixel (location
->start());
3745 double const x2
= _editor
->frame_to_pixel (location
->end());
3747 _drag_rect
->property_x1() = x1
;
3748 _drag_rect
->property_x2() = x2
;
3751 MouseZoomDrag::MouseZoomDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3755 DEBUG_TRACE (DEBUG::Drags
, "New MouseZoomDrag\n");
3759 MouseZoomDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3761 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L
)) {
3762 Drag::start_grab (event
, _editor
->cursors()->zoom_out
);
3765 Drag::start_grab (event
, _editor
->cursors()->zoom_in
);
3769 show_verbose_cursor_time (adjusted_current_frame (event
));
3773 MouseZoomDrag::motion (GdkEvent
* event
, bool first_move
)
3778 framepos_t
const pf
= adjusted_current_frame (event
);
3780 framepos_t grab
= grab_frame ();
3781 _editor
->snap_to_with_modifier (grab
, event
);
3783 /* base start and end on initial click position */
3795 _editor
->zoom_rect
->show();
3796 _editor
->zoom_rect
->raise_to_top();
3799 _editor
->reposition_zoom_rect(start
, end
);
3801 show_verbose_cursor_time (pf
);
3806 MouseZoomDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3808 if (movement_occurred
) {
3809 motion (event
, false);
3811 if (grab_frame() < last_pointer_frame()) {
3812 _editor
->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3814 _editor
->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3817 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L
)) {
3818 _editor
->tav_zoom_step (_zoom_out
);
3820 _editor
->temporal_zoom_to_frame (_zoom_out
, grab_frame());
3824 _editor
->zoom_rect
->hide();
3828 MouseZoomDrag::aborted (bool)
3830 _editor
->zoom_rect
->hide ();
3833 NoteDrag::NoteDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3835 , _cumulative_dx (0)
3836 , _cumulative_dy (0)
3838 DEBUG_TRACE (DEBUG::Drags
, "New NoteDrag\n");
3840 _primary
= dynamic_cast<CanvasNoteEvent
*> (_item
);
3841 _region
= &_primary
->region_view ();
3842 _note_height
= _region
->midi_stream_view()->note_height ();
3846 NoteDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3848 Drag::start_grab (event
);
3850 if (!(_was_selected
= _primary
->selected())) {
3852 /* tertiary-click means extend selection - we'll do that on button release,
3853 so don't add it here, because otherwise we make it hard to figure
3854 out the "extend-to" range.
3857 bool extend
= Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
);
3860 bool add
= Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
);
3863 _region
->note_selected (_primary
, true);
3865 _region
->unique_select (_primary
);
3871 /** @return Current total drag x change in frames */
3873 NoteDrag::total_dx () const
3876 frameoffset_t
const dx
= _editor
->unit_to_frame (_drags
->current_pointer_x() - grab_x());
3878 /* primary note time */
3879 frameoffset_t
const n
= _region
->beats_to_frames (_primary
->note()->time ());
3881 /* new time of the primary note relative to the region position */
3882 frameoffset_t st
= n
+ dx
;
3884 /* prevent the note being dragged earlier than the region's position */
3889 /* snap and return corresponding delta */
3890 return _region
->snap_frame_to_frame (st
) - n
;
3893 /** @return Current total drag y change in notes */
3895 NoteDrag::total_dy () const
3897 /* this is `backwards' to make increasing note number go in the right direction */
3898 double const dy
= _drags
->current_pointer_y() - grab_y();
3903 if (abs (dy
) >= _note_height
) {
3905 ndy
= (int8_t) ceil (dy
/ _note_height
/ 2.0);
3907 ndy
= (int8_t) floor (dy
/ _note_height
/ 2.0);
3911 /* more positive value = higher pitch and higher y-axis position on track,
3912 which is the inverse of the X-centric geometric universe
3919 NoteDrag::motion (GdkEvent
*, bool)
3921 /* Total change in x and y since the start of the drag */
3922 frameoffset_t
const dx
= total_dx ();
3923 int8_t const dy
= -total_dy ();
3925 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3926 double const tdx
= _editor
->frame_to_unit (dx
) - _cumulative_dx
;
3927 double const tdy
= dy
* _note_height
- _cumulative_dy
;
3930 _cumulative_dx
+= tdx
;
3931 _cumulative_dy
+= tdy
;
3933 int8_t note_delta
= total_dy();
3935 _region
->move_selection (tdx
, tdy
, note_delta
);
3938 snprintf (buf
, sizeof (buf
), "%s (%d)", Evoral::midi_note_name (_primary
->note()->note() + note_delta
).c_str(),
3939 (int) floor (_primary
->note()->note() + note_delta
));
3941 show_verbose_cursor_text (buf
);
3946 NoteDrag::finished (GdkEvent
* ev
, bool moved
)
3949 if (_editor
->current_mouse_mode() == Editing::MouseObject
) {
3951 if (_was_selected
) {
3952 bool add
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::PrimaryModifier
);
3954 _region
->note_deselected (_primary
);
3957 bool extend
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::TertiaryModifier
);
3958 bool add
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::PrimaryModifier
);
3960 if (!extend
&& !add
&& _region
->selection_size() > 1) {
3961 _region
->unique_select (_primary
);
3962 } else if (extend
) {
3963 _region
->note_selected (_primary
, true, true);
3965 /* it was added during button press */
3970 _region
->note_dropped (_primary
, total_dx(), total_dy());
3975 NoteDrag::aborted (bool)
3980 AutomationRangeDrag::AutomationRangeDrag (Editor
* editor
, ArdourCanvas::Item
* item
, list
<AudioRange
> const & r
)
3981 : Drag (editor
, item
)
3983 , _nothing_to_drag (false)
3985 DEBUG_TRACE (DEBUG::Drags
, "New AutomationRangeDrag\n");
3987 _atav
= reinterpret_cast<AutomationTimeAxisView
*> (_item
->get_data ("trackview"));
3990 /* get all lines in the automation view */
3991 list
<boost::shared_ptr
<AutomationLine
> > lines
= _atav
->lines ();
3993 /* find those that overlap the ranges being dragged */
3994 list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin ();
3995 while (i
!= lines
.end ()) {
3996 list
<boost::shared_ptr
<AutomationLine
> >::iterator j
= i
;
3999 pair
<framepos_t
, framepos_t
> const r
= (*i
)->get_point_x_range ();
4001 /* check this range against all the AudioRanges that we are using */
4002 list
<AudioRange
>::const_iterator k
= _ranges
.begin ();
4003 while (k
!= _ranges
.end()) {
4004 if (k
->coverage (r
.first
, r
.second
) != OverlapNone
) {
4010 /* add it to our list if it overlaps at all */
4011 if (k
!= _ranges
.end()) {
4016 _lines
.push_back (n
);
4022 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4026 AutomationRangeDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
4028 Drag::start_grab (event
, cursor
);
4030 /* Get line states before we start changing things */
4031 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4032 i
->state
= &i
->line
->get_state ();
4035 if (_ranges
.empty()) {
4037 /* No selected time ranges: drag all points */
4038 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4039 uint32_t const N
= i
->line
->npoints ();
4040 for (uint32_t j
= 0; j
< N
; ++j
) {
4041 i
->points
.push_back (i
->line
->nth (j
));
4047 for (list
<AudioRange
>::const_iterator i
= _ranges
.begin(); i
!= _ranges
.end(); ++i
) {
4049 framecnt_t
const half
= (i
->start
+ i
->end
) / 2;
4051 /* find the line that this audio range starts in */
4052 list
<Line
>::iterator j
= _lines
.begin();
4053 while (j
!= _lines
.end() && (j
->range
.first
> i
->start
|| j
->range
.second
< i
->start
)) {
4057 if (j
!= _lines
.end()) {
4058 boost::shared_ptr
<AutomationList
> the_list
= j
->line
->the_list ();
4060 /* j is the line that this audio range starts in; fade into it;
4061 64 samples length plucked out of thin air.
4064 framepos_t a
= i
->start
+ 64;
4069 double const p
= j
->line
->time_converter().from (i
->start
- j
->line
->time_converter().origin_b ());
4070 double const q
= j
->line
->time_converter().from (a
- j
->line
->time_converter().origin_b ());
4072 the_list
->add (p
, the_list
->eval (p
));
4073 j
->line
->add_always_in_view (p
);
4074 the_list
->add (q
, the_list
->eval (q
));
4075 j
->line
->add_always_in_view (q
);
4078 /* same thing for the end */
4081 while (j
!= _lines
.end() && (j
->range
.first
> i
->end
|| j
->range
.second
< i
->end
)) {
4085 if (j
!= _lines
.end()) {
4086 boost::shared_ptr
<AutomationList
> the_list
= j
->line
->the_list ();
4088 /* j is the line that this audio range starts in; fade out of it;
4089 64 samples length plucked out of thin air.
4092 framepos_t b
= i
->end
- 64;
4097 double const p
= j
->line
->time_converter().from (b
- j
->line
->time_converter().origin_b ());
4098 double const q
= j
->line
->time_converter().from (i
->end
- j
->line
->time_converter().origin_b ());
4100 the_list
->add (p
, the_list
->eval (p
));
4101 j
->line
->add_always_in_view (p
);
4102 the_list
->add (q
, the_list
->eval (q
));
4103 j
->line
->add_always_in_view (q
);
4107 _nothing_to_drag
= true;
4109 /* Find all the points that should be dragged and put them in the relevant
4110 points lists in the Line structs.
4113 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4115 uint32_t const N
= i
->line
->npoints ();
4116 for (uint32_t j
= 0; j
< N
; ++j
) {
4118 /* here's a control point on this line */
4119 ControlPoint
* p
= i
->line
->nth (j
);
4120 double const w
= i
->line
->time_converter().to ((*p
->model())->when
) + i
->line
->time_converter().origin_b ();
4122 /* see if it's inside a range */
4123 list
<AudioRange
>::const_iterator k
= _ranges
.begin ();
4124 while (k
!= _ranges
.end() && (k
->start
>= w
|| k
->end
<= w
)) {
4128 if (k
!= _ranges
.end()) {
4129 /* dragging this point */
4130 _nothing_to_drag
= false;
4131 i
->points
.push_back (p
);
4137 if (_nothing_to_drag
) {
4141 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4142 i
->line
->start_drag_multiple (i
->points
, 1 - (_drags
->current_pointer_y() / i
->line
->height ()), i
->state
);
4147 AutomationRangeDrag::motion (GdkEvent
*, bool /*first_move*/)
4149 if (_nothing_to_drag
) {
4153 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4154 float const f
= 1 - (_drags
->current_pointer_y() / i
->line
->height());
4156 /* we are ignoring x position for this drag, so we can just pass in anything */
4157 i
->line
->drag_motion (0, f
, true, false);
4162 AutomationRangeDrag::finished (GdkEvent
* event
, bool)
4164 if (_nothing_to_drag
) {
4168 motion (event
, false);
4169 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4170 i
->line
->end_drag ();
4171 i
->line
->clear_always_in_view ();
4174 _editor
->session()->commit_reversible_command ();
4178 AutomationRangeDrag::aborted (bool)
4180 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4181 i
->line
->clear_always_in_view ();
4186 DraggingView::DraggingView (RegionView
* v
, RegionDrag
* parent
)
4189 time_axis_view
= parent
->find_time_axis_view (&v
->get_time_axis_view ());
4190 layer
= v
->region()->layer ();
4191 initial_y
= v
->get_canvas_group()->property_y ();
4192 initial_playlist
= v
->region()->playlist ();
4193 initial_position
= v
->region()->position ();
4194 initial_end
= v
->region()->position () + v
->region()->length ();
4197 PatchChangeDrag::PatchChangeDrag (Editor
* e
, CanvasPatchChange
* i
, MidiRegionView
* r
)
4201 , _cumulative_dx (0)
4203 DEBUG_TRACE (DEBUG::Drags
, "New PatchChangeDrag\n");
4207 PatchChangeDrag::motion (GdkEvent
* ev
, bool)
4209 framepos_t f
= adjusted_current_frame (ev
);
4210 boost::shared_ptr
<Region
> r
= _region_view
->region ();
4211 f
= max (f
, r
->position ());
4212 f
= min (f
, r
->last_frame ());
4214 framecnt_t
const dxf
= f
- grab_frame();
4215 double const dxu
= _editor
->frame_to_unit (dxf
);
4216 _patch_change
->move (dxu
- _cumulative_dx
, 0);
4217 _cumulative_dx
= dxu
;
4221 PatchChangeDrag::finished (GdkEvent
* ev
, bool movement_occurred
)
4223 if (!movement_occurred
) {
4227 boost::shared_ptr
<Region
> r (_region_view
->region ());
4229 framepos_t f
= adjusted_current_frame (ev
);
4230 f
= max (f
, r
->position ());
4231 f
= min (f
, r
->last_frame ());
4233 _region_view
->move_patch_change (
4235 _region_view
->frames_to_beats (f
- r
->position() - r
->start())
4240 PatchChangeDrag::aborted (bool)
4242 _patch_change
->move (-_cumulative_dx
, 0);
4246 PatchChangeDrag::setup_pointer_frame_offset ()
4248 boost::shared_ptr
<Region
> region
= _region_view
->region ();
4249 _pointer_frame_offset
= raw_grab_frame() - _region_view
->beats_to_frames (_patch_change
->patch()->time()) - region
->position() + region
->start();