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
);
1312 layer_t layer
= tvp
.second
;
1314 if (tv
&& tv
->layer_display() == Overlaid
) {
1318 /* The region motion is only processed if the pointer is over
1322 if (!tv
|| !tv
->is_track()) {
1323 /* To make sure we hide the verbose canvas cursor when the mouse is
1324 not held over and audiotrack.
1326 _editor
->verbose_cursor()->hide ();
1332 if ((_drags
->current_pointer_x() - last_pointer_x()) > 0) {
1338 RegionSelection
copy (_editor
->selection
->regions
);
1340 RegionSelectionByPosition cmp
;
1343 framepos_t
const pf
= adjusted_current_frame (event
);
1345 for (RegionSelection::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
1347 RouteTimeAxisView
* atv
= dynamic_cast<RouteTimeAxisView
*> (&(*i
)->get_time_axis_view());
1353 boost::shared_ptr
<Playlist
> playlist
;
1355 if ((playlist
= atv
->playlist()) == 0) {
1359 if (!playlist
->region_is_shuffle_constrained ((*i
)->region())) {
1364 if (pf
< (*i
)->region()->last_frame() + 1) {
1368 if (pf
> (*i
)->region()->first_frame()) {
1374 playlist
->shuffle ((*i
)->region(), dir
);
1379 RegionSpliceDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1381 RegionMoveDrag::finished (event
, movement_occurred
);
1385 RegionSpliceDrag::aborted (bool)
1390 RegionCreateDrag::RegionCreateDrag (Editor
* e
, ArdourCanvas::Item
* i
, TimeAxisView
* v
)
1392 _view (dynamic_cast<MidiTimeAxisView
*> (v
))
1394 DEBUG_TRACE (DEBUG::Drags
, "New RegionCreateDrag\n");
1400 RegionCreateDrag::motion (GdkEvent
* event
, bool first_move
)
1404 _view
->playlist()->freeze ();
1407 framepos_t
const f
= adjusted_current_frame (event
);
1408 if (f
< grab_frame()) {
1409 _region
->set_position (f
);
1412 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1413 so that if this region is duplicated, its duplicate starts on
1414 a snap point rather than 1 frame after a snap point. Otherwise things get
1415 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1416 place snapped notes at the start of the region.
1419 framecnt_t
const len
= abs (f
- grab_frame () - 1);
1420 _region
->set_length (len
< 1 ? 1 : len
);
1426 RegionCreateDrag::finished (GdkEvent
*, bool movement_occurred
)
1428 if (!movement_occurred
) {
1431 _view
->playlist()->thaw ();
1435 _editor
->commit_reversible_command ();
1440 RegionCreateDrag::add_region ()
1442 if (_editor
->session()) {
1443 const TempoMap
& map (_editor
->session()->tempo_map());
1444 framecnt_t pos
= grab_frame();
1445 const Meter
& m
= map
.meter_at (pos
);
1446 /* not that the frame rate used here can be affected by pull up/down which
1449 framecnt_t len
= m
.frames_per_bar (map
.tempo_at (pos
), _editor
->session()->frame_rate());
1450 _region
= _view
->add_region (grab_frame(), len
, false);
1455 RegionCreateDrag::aborted (bool)
1458 _view
->playlist()->thaw ();
1464 NoteResizeDrag::NoteResizeDrag (Editor
* e
, ArdourCanvas::Item
* i
)
1468 DEBUG_TRACE (DEBUG::Drags
, "New NoteResizeDrag\n");
1472 NoteResizeDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*ignored*/)
1474 Gdk::Cursor
* cursor
;
1475 ArdourCanvas::CanvasNoteEvent
* cnote
= dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
);
1476 float x_fraction
= cnote
->mouse_x_fraction ();
1478 if (x_fraction
> 0.0 && x_fraction
< 0.25) {
1479 cursor
= _editor
->cursors()->left_side_trim
;
1481 cursor
= _editor
->cursors()->right_side_trim
;
1484 Drag::start_grab (event
, cursor
);
1486 region
= &cnote
->region_view();
1488 double const region_start
= region
->get_position_pixels();
1489 double const middle_point
= region_start
+ cnote
->x1() + (cnote
->x2() - cnote
->x1()) / 2.0L;
1491 if (grab_x() <= middle_point
) {
1492 cursor
= _editor
->cursors()->left_side_trim
;
1495 cursor
= _editor
->cursors()->right_side_trim
;
1499 _item
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
, *cursor
, event
->motion
.time
);
1501 if (event
->motion
.state
& Keyboard::PrimaryModifier
) {
1507 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1509 if (ms
.size() > 1) {
1510 /* has to be relative, may make no sense otherwise */
1514 /* select this note; if it is already selected, preserve the existing selection,
1515 otherwise make this note the only one selected.
1517 region
->note_selected (cnote
, cnote
->selected ());
1519 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ) {
1520 MidiRegionSelection::iterator next
;
1523 (*r
)->begin_resizing (at_front
);
1529 NoteResizeDrag::motion (GdkEvent
* /*event*/, bool /*first_move*/)
1531 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1532 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ++r
) {
1533 (*r
)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
), at_front
, _drags
->current_pointer_x() - grab_x(), relative
);
1538 NoteResizeDrag::finished (GdkEvent
*, bool /*movement_occurred*/)
1540 MidiRegionSelection
& ms (_editor
->get_selection().midi_regions
);
1541 for (MidiRegionSelection::iterator r
= ms
.begin(); r
!= ms
.end(); ++r
) {
1542 (*r
)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent
*>(_item
), at_front
, _drags
->current_pointer_x() - grab_x(), relative
);
1547 NoteResizeDrag::aborted (bool)
1552 RegionGainDrag::RegionGainDrag (Editor
* e
, ArdourCanvas::Item
* i
)
1555 DEBUG_TRACE (DEBUG::Drags
, "New RegionGainDrag\n");
1559 RegionGainDrag::motion (GdkEvent
* /*event*/, bool)
1565 RegionGainDrag::finished (GdkEvent
*, bool)
1571 RegionGainDrag::aborted (bool)
1576 TrimDrag::TrimDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
1577 : RegionDrag (e
, i
, p
, v
)
1579 DEBUG_TRACE (DEBUG::Drags
, "New TrimDrag\n");
1583 TrimDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
1586 TimeAxisView
* tvp
= &_primary
->get_time_axis_view ();
1587 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
1589 if (tv
&& tv
->is_track()) {
1590 speed
= tv
->track()->speed();
1593 framepos_t
const region_start
= (framepos_t
) (_primary
->region()->position() / speed
);
1594 framepos_t
const region_end
= (framepos_t
) (_primary
->region()->last_frame() / speed
);
1595 framecnt_t
const region_length
= (framecnt_t
) (_primary
->region()->length() / speed
);
1597 framepos_t
const pf
= adjusted_current_frame (event
);
1599 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1600 /* Move the contents of the region around without changing the region bounds */
1601 _operation
= ContentsTrim
;
1602 Drag::start_grab (event
, _editor
->cursors()->trimmer
);
1604 /* These will get overridden for a point trim.*/
1605 if (pf
< (region_start
+ region_length
/2)) {
1606 /* closer to front */
1607 _operation
= StartTrim
;
1608 Drag::start_grab (event
, _editor
->cursors()->left_side_trim
);
1611 _operation
= EndTrim
;
1612 Drag::start_grab (event
, _editor
->cursors()->right_side_trim
);
1616 switch (_operation
) {
1618 show_verbose_cursor_time (region_start
);
1619 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1620 i
->view
->trim_front_starting ();
1624 show_verbose_cursor_time (region_end
);
1627 show_verbose_cursor_time (pf
);
1631 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1632 i
->view
->region()->suspend_property_changes ();
1637 TrimDrag::motion (GdkEvent
* event
, bool first_move
)
1639 RegionView
* rv
= _primary
;
1642 TimeAxisView
* tvp
= &_primary
->get_time_axis_view ();
1643 RouteTimeAxisView
* tv
= dynamic_cast<RouteTimeAxisView
*>(tvp
);
1644 pair
<set
<boost::shared_ptr
<Playlist
> >::iterator
,bool> insert_result
;
1646 if (tv
&& tv
->is_track()) {
1647 speed
= tv
->track()->speed();
1650 framecnt_t
const dt
= adjusted_current_frame (event
) - raw_grab_frame () + _pointer_frame_offset
;
1656 switch (_operation
) {
1658 trim_type
= "Region start trim";
1661 trim_type
= "Region end trim";
1664 trim_type
= "Region content trim";
1668 _editor
->begin_reversible_command (trim_type
);
1670 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1671 RegionView
* rv
= i
->view
;
1672 rv
->fake_set_opaque (false);
1673 rv
->enable_display (false);
1674 rv
->region()->playlist()->clear_owned_changes ();
1676 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*> (rv
);
1679 arv
->temporarily_hide_envelope ();
1682 boost::shared_ptr
<Playlist
> pl
= rv
->region()->playlist();
1683 insert_result
= _editor
->motion_frozen_playlists
.insert (pl
);
1685 if (insert_result
.second
) {
1691 bool non_overlap_trim
= false;
1693 if (event
&& Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
1694 non_overlap_trim
= true;
1697 switch (_operation
) {
1699 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1700 i
->view
->trim_front (i
->initial_position
+ dt
, non_overlap_trim
);
1705 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1706 i
->view
->trim_end (i
->initial_end
+ dt
, non_overlap_trim
);
1712 bool swap_direction
= false;
1714 if (event
&& Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
1715 swap_direction
= true;
1718 framecnt_t frame_delta
= 0;
1720 bool left_direction
= false;
1721 if (last_pointer_frame() > adjusted_current_frame(event
)) {
1722 left_direction
= true;
1725 if (left_direction
) {
1726 frame_delta
= (last_pointer_frame() - adjusted_current_frame(event
));
1728 frame_delta
= (adjusted_current_frame(event
) - last_pointer_frame());
1731 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1732 i
->view
->trim_contents (frame_delta
, left_direction
, swap_direction
);
1738 switch (_operation
) {
1740 show_verbose_cursor_time ((framepos_t
) (rv
->region()->position() / speed
));
1743 show_verbose_cursor_time ((framepos_t
) (rv
->region()->last_frame() / speed
));
1746 show_verbose_cursor_time (adjusted_current_frame (event
));
1753 TrimDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1755 if (movement_occurred
) {
1756 motion (event
, false);
1758 /* This must happen before the region's StatefulDiffCommand is created, as it may
1759 `correct' (ahem) the region's _start from being negative to being zero. It
1760 needs to be zero in the undo record.
1762 if (_operation
== StartTrim
) {
1763 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1764 i
->view
->trim_front_ending ();
1768 if (!_editor
->selection
->selected (_primary
)) {
1769 _primary
->thaw_after_trim ();
1772 set
<boost::shared_ptr
<Playlist
> > diffed_playlists
;
1774 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1775 i
->view
->thaw_after_trim ();
1776 i
->view
->enable_display (true);
1777 i
->view
->fake_set_opaque (true);
1779 /* Trimming one region may affect others on the playlist, so we need
1780 to get undo Commands from the whole playlist rather than just the
1781 region. Use diffed_playlists to make sure we don't diff a given
1782 playlist more than once.
1784 boost::shared_ptr
<Playlist
> p
= i
->view
->region()->playlist ();
1785 if (diffed_playlists
.find (p
) == diffed_playlists
.end()) {
1786 vector
<Command
*> cmds
;
1788 _editor
->session()->add_commands (cmds
);
1789 diffed_playlists
.insert (p
);
1793 for (set
<boost::shared_ptr
<Playlist
> >::iterator p
= _editor
->motion_frozen_playlists
.begin(); p
!= _editor
->motion_frozen_playlists
.end(); ++p
) {
1797 _editor
->motion_frozen_playlists
.clear ();
1798 _editor
->commit_reversible_command();
1801 /* no mouse movement */
1802 _editor
->point_trim (event
, adjusted_current_frame (event
));
1805 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1806 if (_operation
== StartTrim
) {
1807 i
->view
->trim_front_ending ();
1810 i
->view
->region()->resume_property_changes ();
1815 TrimDrag::aborted (bool movement_occurred
)
1817 /* Our motion method is changing model state, so use the Undo system
1818 to cancel. Perhaps not ideal, as this will leave an Undo point
1819 behind which may be slightly odd from the user's point of view.
1824 if (movement_occurred
) {
1828 for (list
<DraggingView
>::const_iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
1829 i
->view
->region()->resume_property_changes ();
1834 TrimDrag::setup_pointer_frame_offset ()
1836 list
<DraggingView
>::iterator i
= _views
.begin ();
1837 while (i
!= _views
.end() && i
->view
!= _primary
) {
1841 if (i
== _views
.end()) {
1845 switch (_operation
) {
1847 _pointer_frame_offset
= raw_grab_frame() - i
->initial_position
;
1850 _pointer_frame_offset
= raw_grab_frame() - i
->initial_end
;
1857 MeterMarkerDrag::MeterMarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool c
)
1861 DEBUG_TRACE (DEBUG::Drags
, "New MeterMarkerDrag\n");
1863 _marker
= reinterpret_cast<MeterMarker
*> (_item
->get_data ("marker"));
1868 MeterMarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
1871 // create a dummy marker for visual representation of moving the copy.
1872 // The actual copying is not done before we reach the finish callback.
1874 snprintf (name
, sizeof(name
), "%g/%g", _marker
->meter().beats_per_bar(), _marker
->meter().note_divisor ());
1876 MeterMarker
* new_marker
= new MeterMarker (
1878 *_editor
->meter_group
,
1879 ARDOUR_UI::config()->canvasvar_MeterMarker
.get(),
1881 *new MeterSection (_marker
->meter())
1884 _item
= &new_marker
->the_item ();
1885 _marker
= new_marker
;
1889 MetricSection
& section (_marker
->meter());
1891 if (!section
.movable()) {
1897 Drag::start_grab (event
, cursor
);
1899 show_verbose_cursor_time (adjusted_current_frame(event
));
1903 MeterMarkerDrag::setup_pointer_frame_offset ()
1905 _pointer_frame_offset
= raw_grab_frame() - _marker
->meter().frame();
1909 MeterMarkerDrag::motion (GdkEvent
* event
, bool)
1911 framepos_t
const pf
= adjusted_current_frame (event
);
1913 _marker
->set_position (pf
);
1915 show_verbose_cursor_time (pf
);
1919 MeterMarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
1921 if (!movement_occurred
) {
1925 motion (event
, false);
1927 Timecode::BBT_Time when
;
1929 TempoMap
& map (_editor
->session()->tempo_map());
1930 map
.bbt_time (last_pointer_frame(), when
);
1932 if (_copy
== true) {
1933 _editor
->begin_reversible_command (_("copy meter mark"));
1934 XMLNode
&before
= map
.get_state();
1935 map
.add_meter (_marker
->meter(), when
);
1936 XMLNode
&after
= map
.get_state();
1937 _editor
->session()->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
1938 _editor
->commit_reversible_command ();
1940 // delete the dummy marker we used for visual representation of copying.
1941 // a new visual marker will show up automatically.
1944 _editor
->begin_reversible_command (_("move meter mark"));
1945 XMLNode
&before
= map
.get_state();
1946 map
.move_meter (_marker
->meter(), when
);
1947 XMLNode
&after
= map
.get_state();
1948 _editor
->session()->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
1949 _editor
->commit_reversible_command ();
1954 MeterMarkerDrag::aborted (bool)
1956 _marker
->set_position (_marker
->meter().frame ());
1959 TempoMarkerDrag::TempoMarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool c
)
1963 DEBUG_TRACE (DEBUG::Drags
, "New TempoMarkerDrag\n");
1965 _marker
= reinterpret_cast<TempoMarker
*> (_item
->get_data ("marker"));
1970 TempoMarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
1974 // create a dummy marker for visual representation of moving the copy.
1975 // The actual copying is not done before we reach the finish callback.
1977 snprintf (name
, sizeof (name
), "%.2f", _marker
->tempo().beats_per_minute());
1979 TempoMarker
* new_marker
= new TempoMarker (
1981 *_editor
->tempo_group
,
1982 ARDOUR_UI::config()->canvasvar_TempoMarker
.get(),
1984 *new TempoSection (_marker
->tempo())
1987 _item
= &new_marker
->the_item ();
1988 _marker
= new_marker
;
1992 Drag::start_grab (event
, cursor
);
1994 show_verbose_cursor_time (adjusted_current_frame (event
));
1998 TempoMarkerDrag::setup_pointer_frame_offset ()
2000 _pointer_frame_offset
= raw_grab_frame() - _marker
->tempo().frame();
2004 TempoMarkerDrag::motion (GdkEvent
* event
, bool)
2006 framepos_t
const pf
= adjusted_current_frame (event
);
2007 _marker
->set_position (pf
);
2008 show_verbose_cursor_time (pf
);
2012 TempoMarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2014 if (!movement_occurred
) {
2018 motion (event
, false);
2020 Timecode::BBT_Time when
;
2022 TempoMap
& map (_editor
->session()->tempo_map());
2023 map
.bbt_time (last_pointer_frame(), when
);
2025 if (_copy
== true) {
2026 _editor
->begin_reversible_command (_("copy tempo mark"));
2027 XMLNode
&before
= map
.get_state();
2028 map
.add_tempo (_marker
->tempo(), when
);
2029 XMLNode
&after
= map
.get_state();
2030 _editor
->session()->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2031 _editor
->commit_reversible_command ();
2033 // delete the dummy marker we used for visual representation of copying.
2034 // a new visual marker will show up automatically.
2037 _editor
->begin_reversible_command (_("move tempo mark"));
2038 XMLNode
&before
= map
.get_state();
2039 map
.move_tempo (_marker
->tempo(), when
);
2040 XMLNode
&after
= map
.get_state();
2041 _editor
->session()->add_command (new MementoCommand
<TempoMap
>(map
, &before
, &after
));
2042 _editor
->commit_reversible_command ();
2047 TempoMarkerDrag::aborted (bool)
2049 _marker
->set_position (_marker
->tempo().frame());
2052 CursorDrag::CursorDrag (Editor
* e
, ArdourCanvas::Item
* i
, bool s
)
2056 DEBUG_TRACE (DEBUG::Drags
, "New CursorDrag\n");
2059 /** Do all the things we do when dragging the playhead to make it look as though
2060 * we have located, without actually doing the locate (because that would cause
2061 * the diskstream buffers to be refilled, which is too slow).
2064 CursorDrag::fake_locate (framepos_t t
)
2066 _editor
->playhead_cursor
->set_position (t
);
2068 Session
* s
= _editor
->session ();
2069 if (s
->timecode_transmission_suspended ()) {
2070 framepos_t
const f
= _editor
->playhead_cursor
->current_frame
;
2071 s
->send_mmc_locate (f
);
2072 s
->send_full_time_code (f
);
2075 show_verbose_cursor_time (t
);
2076 _editor
->UpdateAllTransportClocks (t
);
2080 CursorDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* c
)
2082 Drag::start_grab (event
, c
);
2084 _grab_zoom
= _editor
->frames_per_unit
;
2086 framepos_t where
= _editor
->event_frame (event
, 0, 0);
2087 _editor
->snap_to_with_modifier (where
, event
);
2089 _editor
->_dragging_playhead
= true;
2091 Session
* s
= _editor
->session ();
2094 if (_was_rolling
&& _stop
) {
2098 if (s
->is_auditioning()) {
2099 s
->cancel_audition ();
2102 s
->request_suspend_timecode_transmission ();
2103 while (!s
->timecode_transmission_suspended ()) {
2104 /* twiddle our thumbs */
2108 fake_locate (where
);
2112 CursorDrag::motion (GdkEvent
* event
, bool)
2114 if (_drags
->current_pointer_y() != last_pointer_y()) {
2116 /* zoom when we move the pointer up and down */
2118 /* y range to operate over (pixels) */
2119 double const y_range
= 512;
2120 /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
2121 double const scale_range
= 4;
2122 /* dead zone around the grab point in which to do no zooming (pixels) */
2123 double const dead_zone
= 100;
2126 double dy
= _drags
->current_pointer_y() - grab_y();
2128 if (dy
< -dead_zone
|| dy
> dead_zone
) {
2129 /* we are outside the dead zone; remove it from our calculation */
2136 /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
2137 double udy
= max (min (dy
/ y_range
, 1.0), -1.0);
2139 /* and zoom, using playhead focus temporarily */
2140 Editing::ZoomFocus
const zf
= _editor
->get_zoom_focus ();
2141 _editor
->set_zoom_focus (Editing::ZoomFocusPlayhead
);
2142 _editor
->temporal_zoom (_grab_zoom
* pow (scale_range
, -udy
));
2143 _editor
->set_zoom_focus (zf
);
2147 framepos_t
const adjusted_frame
= adjusted_current_frame (event
);
2148 if (adjusted_frame
!= last_pointer_frame()) {
2149 fake_locate (adjusted_frame
);
2151 _editor
->update_canvas_now ();
2157 CursorDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2159 _editor
->_dragging_playhead
= false;
2161 if (!movement_occurred
&& _stop
) {
2165 motion (event
, false);
2167 Session
* s
= _editor
->session ();
2169 s
->request_locate (_editor
->playhead_cursor
->current_frame
, _was_rolling
);
2170 _editor
->_pending_locate_request
= true;
2171 s
->request_resume_timecode_transmission ();
2176 CursorDrag::aborted (bool)
2178 if (_editor
->_dragging_playhead
) {
2179 _editor
->session()->request_resume_timecode_transmission ();
2180 _editor
->_dragging_playhead
= false;
2183 _editor
->playhead_cursor
->set_position (adjusted_frame (grab_frame (), 0, false));
2186 FadeInDrag::FadeInDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
2187 : RegionDrag (e
, i
, p
, v
)
2189 DEBUG_TRACE (DEBUG::Drags
, "New FadeInDrag\n");
2193 FadeInDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2195 Drag::start_grab (event
, cursor
);
2197 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2198 boost::shared_ptr
<AudioRegion
> const r
= arv
->audio_region ();
2200 show_verbose_cursor_duration (r
->position(), r
->position() + r
->fade_in()->back()->when
, 32);
2202 arv
->show_fade_line((framepos_t
) r
->fade_in()->back()->when
);
2206 FadeInDrag::setup_pointer_frame_offset ()
2208 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2209 boost::shared_ptr
<AudioRegion
> const r
= arv
->audio_region ();
2210 _pointer_frame_offset
= raw_grab_frame() - ((framecnt_t
) r
->fade_in()->back()->when
+ r
->position());
2214 FadeInDrag::motion (GdkEvent
* event
, bool)
2216 framecnt_t fade_length
;
2218 framepos_t
const pos
= adjusted_current_frame (event
);
2220 boost::shared_ptr
<Region
> region
= _primary
->region ();
2222 if (pos
< (region
->position() + 64)) {
2223 fade_length
= 64; // this should be a minimum defined somewhere
2224 } else if (pos
> region
->last_frame()) {
2225 fade_length
= region
->length();
2227 fade_length
= pos
- region
->position();
2230 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2232 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2238 tmp
->reset_fade_in_shape_width (fade_length
);
2239 tmp
->show_fade_line((framecnt_t
) fade_length
);
2242 show_verbose_cursor_duration (region
->position(), region
->position() + fade_length
, 32);
2246 FadeInDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2248 if (!movement_occurred
) {
2252 framecnt_t fade_length
;
2254 framepos_t
const pos
= adjusted_current_frame (event
);
2256 boost::shared_ptr
<Region
> region
= _primary
->region ();
2258 if (pos
< (region
->position() + 64)) {
2259 fade_length
= 64; // this should be a minimum defined somewhere
2260 } else if (pos
> region
->last_frame()) {
2261 fade_length
= region
->length();
2263 fade_length
= pos
- region
->position();
2266 _editor
->begin_reversible_command (_("change fade in length"));
2268 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2270 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2276 boost::shared_ptr
<AutomationList
> alist
= tmp
->audio_region()->fade_in();
2277 XMLNode
&before
= alist
->get_state();
2279 tmp
->audio_region()->set_fade_in_length (fade_length
);
2280 tmp
->audio_region()->set_fade_in_active (true);
2281 tmp
->hide_fade_line();
2283 XMLNode
&after
= alist
->get_state();
2284 _editor
->session()->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &after
));
2287 _editor
->commit_reversible_command ();
2291 FadeInDrag::aborted (bool)
2293 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2294 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2300 tmp
->reset_fade_in_shape_width (tmp
->audio_region()->fade_in()->back()->when
);
2301 tmp
->hide_fade_line();
2305 FadeOutDrag::FadeOutDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, list
<RegionView
*> const & v
)
2306 : RegionDrag (e
, i
, p
, v
)
2308 DEBUG_TRACE (DEBUG::Drags
, "New FadeOutDrag\n");
2312 FadeOutDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2314 Drag::start_grab (event
, cursor
);
2316 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2317 boost::shared_ptr
<AudioRegion
> r
= arv
->audio_region ();
2319 show_verbose_cursor_duration (r
->last_frame() - r
->fade_out()->back()->when
, r
->last_frame());
2321 arv
->show_fade_line(r
->length() - r
->fade_out()->back()->when
);
2325 FadeOutDrag::setup_pointer_frame_offset ()
2327 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (_primary
);
2328 boost::shared_ptr
<AudioRegion
> r
= arv
->audio_region ();
2329 _pointer_frame_offset
= raw_grab_frame() - (r
->length() - (framecnt_t
) r
->fade_out()->back()->when
+ r
->position());
2333 FadeOutDrag::motion (GdkEvent
* event
, bool)
2335 framecnt_t fade_length
;
2337 framepos_t
const pos
= adjusted_current_frame (event
);
2339 boost::shared_ptr
<Region
> region
= _primary
->region ();
2341 if (pos
> (region
->last_frame() - 64)) {
2342 fade_length
= 64; // this should really be a minimum fade defined somewhere
2344 else if (pos
< region
->position()) {
2345 fade_length
= region
->length();
2348 fade_length
= region
->last_frame() - pos
;
2351 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2353 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2359 tmp
->reset_fade_out_shape_width (fade_length
);
2360 tmp
->show_fade_line(region
->length() - fade_length
);
2363 show_verbose_cursor_duration (region
->last_frame() - fade_length
, region
->last_frame());
2367 FadeOutDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2369 if (!movement_occurred
) {
2373 framecnt_t fade_length
;
2375 framepos_t
const pos
= adjusted_current_frame (event
);
2377 boost::shared_ptr
<Region
> region
= _primary
->region ();
2379 if (pos
> (region
->last_frame() - 64)) {
2380 fade_length
= 64; // this should really be a minimum fade defined somewhere
2382 else if (pos
< region
->position()) {
2383 fade_length
= region
->length();
2386 fade_length
= region
->last_frame() - pos
;
2389 _editor
->begin_reversible_command (_("change fade out length"));
2391 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2393 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2399 boost::shared_ptr
<AutomationList
> alist
= tmp
->audio_region()->fade_out();
2400 XMLNode
&before
= alist
->get_state();
2402 tmp
->audio_region()->set_fade_out_length (fade_length
);
2403 tmp
->audio_region()->set_fade_out_active (true);
2404 tmp
->hide_fade_line();
2406 XMLNode
&after
= alist
->get_state();
2407 _editor
->session()->add_command(new MementoCommand
<AutomationList
>(*alist
.get(), &before
, &after
));
2410 _editor
->commit_reversible_command ();
2414 FadeOutDrag::aborted (bool)
2416 for (list
<DraggingView
>::iterator i
= _views
.begin(); i
!= _views
.end(); ++i
) {
2417 AudioRegionView
* tmp
= dynamic_cast<AudioRegionView
*> (i
->view
);
2423 tmp
->reset_fade_out_shape_width (tmp
->audio_region()->fade_out()->back()->when
);
2424 tmp
->hide_fade_line();
2428 MarkerDrag::MarkerDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2431 DEBUG_TRACE (DEBUG::Drags
, "New MarkerDrag\n");
2433 _marker
= reinterpret_cast<Marker
*> (_item
->get_data ("marker"));
2436 _points
.push_back (Gnome::Art::Point (0, 0));
2437 _points
.push_back (Gnome::Art::Point (0, physical_screen_height (_editor
->get_window())));
2440 MarkerDrag::~MarkerDrag ()
2442 for (list
<Location
*>::iterator i
= _copied_locations
.begin(); i
!= _copied_locations
.end(); ++i
) {
2448 MarkerDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
2450 Drag::start_grab (event
, cursor
);
2454 Location
*location
= _editor
->find_location_from_marker (_marker
, is_start
);
2455 _editor
->_dragging_edit_point
= true;
2457 update_item (location
);
2459 // _drag_line->show();
2460 // _line->raise_to_top();
2463 show_verbose_cursor_time (location
->start());
2465 show_verbose_cursor_time (location
->end());
2468 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
2471 case Selection::Toggle
:
2472 _editor
->selection
->toggle (_marker
);
2474 case Selection::Set
:
2475 if (!_editor
->selection
->selected (_marker
)) {
2476 _editor
->selection
->set (_marker
);
2479 case Selection::Extend
:
2481 Locations::LocationList ll
;
2482 list
<Marker
*> to_add
;
2484 _editor
->selection
->markers
.range (s
, e
);
2485 s
= min (_marker
->position(), s
);
2486 e
= max (_marker
->position(), e
);
2489 if (e
< max_framepos
) {
2492 _editor
->session()->locations()->find_all_between (s
, e
, ll
, Location::Flags (0));
2493 for (Locations::LocationList::iterator i
= ll
.begin(); i
!= ll
.end(); ++i
) {
2494 Editor::LocationMarkers
* lm
= _editor
->find_location_markers (*i
);
2497 to_add
.push_back (lm
->start
);
2500 to_add
.push_back (lm
->end
);
2504 if (!to_add
.empty()) {
2505 _editor
->selection
->add (to_add
);
2509 case Selection::Add
:
2510 _editor
->selection
->add (_marker
);
2514 /* Set up copies for us to manipulate during the drag */
2516 for (MarkerSelection::iterator i
= _editor
->selection
->markers
.begin(); i
!= _editor
->selection
->markers
.end(); ++i
) {
2517 Location
* l
= _editor
->find_location_from_marker (*i
, is_start
);
2518 _copied_locations
.push_back (new Location (*l
));
2523 MarkerDrag::setup_pointer_frame_offset ()
2526 Location
*location
= _editor
->find_location_from_marker (_marker
, is_start
);
2527 _pointer_frame_offset
= raw_grab_frame() - (is_start
? location
->start() : location
->end());
2531 MarkerDrag::motion (GdkEvent
* event
, bool)
2533 framecnt_t f_delta
= 0;
2535 bool move_both
= false;
2537 Location
*real_location
;
2538 Location
*copy_location
= 0;
2540 framepos_t
const newframe
= adjusted_current_frame (event
);
2542 framepos_t next
= newframe
;
2544 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
)) {
2548 MarkerSelection::iterator i
;
2549 list
<Location
*>::iterator x
;
2551 /* find the marker we're dragging, and compute the delta */
2553 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2554 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2560 if (marker
== _marker
) {
2562 if ((real_location
= _editor
->find_location_from_marker (marker
, is_start
)) == 0) {
2567 if (real_location
->is_mark()) {
2568 f_delta
= newframe
- copy_location
->start();
2572 switch (marker
->type()) {
2573 case Marker::SessionStart
:
2574 case Marker::RangeStart
:
2575 case Marker::LoopStart
:
2576 case Marker::PunchIn
:
2577 f_delta
= newframe
- copy_location
->start();
2580 case Marker::SessionEnd
:
2581 case Marker::RangeEnd
:
2582 case Marker::LoopEnd
:
2583 case Marker::PunchOut
:
2584 f_delta
= newframe
- copy_location
->end();
2587 /* what kind of marker is this ? */
2595 if (i
== _editor
->selection
->markers
.end()) {
2596 /* hmm, impossible - we didn't find the dragged marker */
2600 /* now move them all */
2602 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2603 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2609 /* call this to find out if its the start or end */
2611 if ((real_location
= _editor
->find_location_from_marker (marker
, is_start
)) == 0) {
2615 if (real_location
->locked()) {
2619 if (copy_location
->is_mark()) {
2623 copy_location
->set_start (copy_location
->start() + f_delta
);
2627 framepos_t new_start
= copy_location
->start() + f_delta
;
2628 framepos_t new_end
= copy_location
->end() + f_delta
;
2630 if (is_start
) { // start-of-range marker
2633 copy_location
->set_start (new_start
);
2634 copy_location
->set_end (new_end
);
2635 } else if (new_start
< copy_location
->end()) {
2636 copy_location
->set_start (new_start
);
2637 } else if (newframe
> 0) {
2638 _editor
->snap_to (next
, 1, true);
2639 copy_location
->set_end (next
);
2640 copy_location
->set_start (newframe
);
2643 } else { // end marker
2646 copy_location
->set_end (new_end
);
2647 copy_location
->set_start (new_start
);
2648 } else if (new_end
> copy_location
->start()) {
2649 copy_location
->set_end (new_end
);
2650 } else if (newframe
> 0) {
2651 _editor
->snap_to (next
, -1, true);
2652 copy_location
->set_start (next
);
2653 copy_location
->set_end (newframe
);
2658 update_item (copy_location
);
2660 Editor::LocationMarkers
* lm
= _editor
->find_location_markers (real_location
);
2663 lm
->set_position (copy_location
->start(), copy_location
->end());
2667 assert (!_copied_locations
.empty());
2669 show_verbose_cursor_time (newframe
);
2672 _editor
->update_canvas_now ();
2677 MarkerDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2679 if (!movement_occurred
) {
2681 /* just a click, do nothing but finish
2682 off the selection process
2685 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
2688 case Selection::Set
:
2689 if (_editor
->selection
->selected (_marker
) && _editor
->selection
->markers
.size() > 1) {
2690 _editor
->selection
->set (_marker
);
2694 case Selection::Toggle
:
2695 case Selection::Extend
:
2696 case Selection::Add
:
2703 _editor
->_dragging_edit_point
= false;
2705 _editor
->begin_reversible_command ( _("move marker") );
2706 XMLNode
&before
= _editor
->session()->locations()->get_state();
2708 MarkerSelection::iterator i
;
2709 list
<Location
*>::iterator x
;
2712 for (i
= _editor
->selection
->markers
.begin(), x
= _copied_locations
.begin();
2713 x
!= _copied_locations
.end() && i
!= _editor
->selection
->markers
.end();
2716 Location
* location
= _editor
->find_location_from_marker (*i
, is_start
);
2720 if (location
->locked()) {
2724 if (location
->is_mark()) {
2725 location
->set_start ((*x
)->start());
2727 location
->set ((*x
)->start(), (*x
)->end());
2732 XMLNode
&after
= _editor
->session()->locations()->get_state();
2733 _editor
->session()->add_command(new MementoCommand
<Locations
>(*(_editor
->session()->locations()), &before
, &after
));
2734 _editor
->commit_reversible_command ();
2738 MarkerDrag::aborted (bool)
2744 MarkerDrag::update_item (Location
* location
)
2749 ControlPointDrag::ControlPointDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2751 _cumulative_x_drag (0),
2752 _cumulative_y_drag (0)
2754 if (_zero_gain_fraction
< 0.0) {
2755 _zero_gain_fraction
= gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config
->get_max_gain());
2758 DEBUG_TRACE (DEBUG::Drags
, "New ControlPointDrag\n");
2760 _point
= reinterpret_cast<ControlPoint
*> (_item
->get_data ("control_point"));
2766 ControlPointDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2768 Drag::start_grab (event
, _editor
->cursors()->fader
);
2770 // start the grab at the center of the control point so
2771 // the point doesn't 'jump' to the mouse after the first drag
2772 _fixed_grab_x
= _point
->get_x();
2773 _fixed_grab_y
= _point
->get_y();
2775 float const fraction
= 1 - (_point
->get_y() / _point
->line().height());
2777 _point
->line().start_drag_single (_point
, _fixed_grab_x
, fraction
);
2779 _editor
->verbose_cursor()->set (_point
->line().get_verbose_cursor_string (fraction
),
2780 event
->button
.x
+ 10, event
->button
.y
+ 10);
2782 _editor
->verbose_cursor()->show ();
2786 ControlPointDrag::motion (GdkEvent
* event
, bool)
2788 double dx
= _drags
->current_pointer_x() - last_pointer_x();
2789 double dy
= _drags
->current_pointer_y() - last_pointer_y();
2791 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
2796 /* coordinate in pixels relative to the start of the region (for region-based automation)
2797 or track (for track-based automation) */
2798 double cx
= _fixed_grab_x
+ _cumulative_x_drag
+ dx
;
2799 double cy
= _fixed_grab_y
+ _cumulative_y_drag
+ dy
;
2801 // calculate zero crossing point. back off by .01 to stay on the
2802 // positive side of zero
2803 double const zero_gain_y
= (1.0 - _zero_gain_fraction
) * _point
->line().height() - .01;
2805 // make sure we hit zero when passing through
2806 if ((cy
< zero_gain_y
&& (cy
- dy
) > zero_gain_y
) || (cy
> zero_gain_y
&& (cy
- dy
) < zero_gain_y
)) {
2810 if (_x_constrained
) {
2813 if (_y_constrained
) {
2817 _cumulative_x_drag
= cx
- _fixed_grab_x
;
2818 _cumulative_y_drag
= cy
- _fixed_grab_y
;
2822 cy
= min ((double) _point
->line().height(), cy
);
2824 framepos_t cx_frames
= _editor
->unit_to_frame (cx
);
2826 if (!_x_constrained
) {
2827 _editor
->snap_to_with_modifier (cx_frames
, event
);
2830 cx_frames
= min (cx_frames
, _point
->line().maximum_time());
2832 float const fraction
= 1.0 - (cy
/ _point
->line().height());
2834 bool const push
= Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
);
2836 _point
->line().drag_motion (_editor
->frame_to_unit (cx_frames
), fraction
, false, push
);
2838 _editor
->verbose_cursor()->set_text (_point
->line().get_verbose_cursor_string (fraction
));
2842 ControlPointDrag::finished (GdkEvent
* event
, bool movement_occurred
)
2844 if (!movement_occurred
) {
2848 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
2849 _editor
->reset_point_selection ();
2853 motion (event
, false);
2856 _point
->line().end_drag ();
2857 _editor
->session()->commit_reversible_command ();
2861 ControlPointDrag::aborted (bool)
2863 _point
->line().reset ();
2867 ControlPointDrag::active (Editing::MouseMode m
)
2869 if (m
== Editing::MouseGain
) {
2870 /* always active in mouse gain */
2874 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2875 return dynamic_cast<AutomationLine
*> (&(_point
->line())) != 0;
2878 LineDrag::LineDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2881 _cumulative_y_drag (0)
2883 DEBUG_TRACE (DEBUG::Drags
, "New LineDrag\n");
2887 LineDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2889 _line
= reinterpret_cast<AutomationLine
*> (_item
->get_data ("line"));
2892 _item
= &_line
->grab_item ();
2894 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2895 origin, and ditto for y.
2898 double cx
= event
->button
.x
;
2899 double cy
= event
->button
.y
;
2901 _line
->parent_group().w2i (cx
, cy
);
2903 framecnt_t
const frame_within_region
= (framecnt_t
) floor (cx
* _editor
->frames_per_unit
);
2908 if (!_line
->control_points_adjacent (frame_within_region
, before
, after
)) {
2909 /* no adjacent points */
2913 Drag::start_grab (event
, _editor
->cursors()->fader
);
2915 /* store grab start in parent frame */
2920 double fraction
= 1.0 - (cy
/ _line
->height());
2922 _line
->start_drag_line (before
, after
, fraction
);
2924 _editor
->verbose_cursor()->set (_line
->get_verbose_cursor_string (fraction
),
2925 event
->button
.x
+ 10, event
->button
.y
+ 10);
2927 _editor
->verbose_cursor()->show ();
2931 LineDrag::motion (GdkEvent
* event
, bool)
2933 double dy
= _drags
->current_pointer_y() - last_pointer_y();
2935 if (event
->button
.state
& Keyboard::SecondaryModifier
) {
2939 double cy
= _fixed_grab_y
+ _cumulative_y_drag
+ dy
;
2941 _cumulative_y_drag
= cy
- _fixed_grab_y
;
2944 cy
= min ((double) _line
->height(), cy
);
2946 double const fraction
= 1.0 - (cy
/ _line
->height());
2950 if (Keyboard::modifier_state_contains (event
->button
.state
, Keyboard::PrimaryModifier
)) {
2956 /* we are ignoring x position for this drag, so we can just pass in anything */
2957 _line
->drag_motion (0, fraction
, true, push
);
2959 _editor
->verbose_cursor()->set_text (_line
->get_verbose_cursor_string (fraction
));
2963 LineDrag::finished (GdkEvent
* event
, bool)
2965 motion (event
, false);
2967 _editor
->session()->commit_reversible_command ();
2971 LineDrag::aborted (bool)
2976 FeatureLineDrag::FeatureLineDrag (Editor
* e
, ArdourCanvas::Item
* i
)
2979 _cumulative_x_drag (0)
2981 DEBUG_TRACE (DEBUG::Drags
, "New FeatureLineDrag\n");
2985 FeatureLineDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* /*cursor*/)
2987 Drag::start_grab (event
);
2989 _line
= reinterpret_cast<Line
*> (_item
);
2992 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2994 double cx
= event
->button
.x
;
2995 double cy
= event
->button
.y
;
2997 _item
->property_parent().get_value()->w2i(cx
, cy
);
2999 /* store grab start in parent frame */
3000 _region_view_grab_x
= cx
;
3002 _before
= *(float*) _item
->get_data ("position");
3004 _arv
= reinterpret_cast<AudioRegionView
*> (_item
->get_data ("regionview"));
3006 _max_x
= _editor
->frame_to_pixel(_arv
->get_duration());
3010 FeatureLineDrag::motion (GdkEvent
*, bool)
3012 double dx
= _drags
->current_pointer_x() - last_pointer_x();
3014 double cx
= _region_view_grab_x
+ _cumulative_x_drag
+ dx
;
3016 _cumulative_x_drag
+= dx
;
3018 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3027 ArdourCanvas::Points points
;
3029 double x1
= 0, x2
= 0, y1
= 0, y2
= 0;
3031 _line
->get_bounds(x1
, y2
, x2
, y2
);
3033 points
.push_back(Gnome::Art::Point(cx
, 2.0)); // first x-coord needs to be a non-normal value
3034 points
.push_back(Gnome::Art::Point(cx
, y2
- y1
));
3036 _line
->property_points() = points
;
3038 float *pos
= new float;
3041 _line
->set_data ("position", pos
);
3047 FeatureLineDrag::finished (GdkEvent
*, bool)
3049 _arv
= reinterpret_cast<AudioRegionView
*> (_item
->get_data ("regionview"));
3050 _arv
->update_transient(_before
, _before
);
3054 FeatureLineDrag::aborted (bool)
3059 RubberbandSelectDrag::RubberbandSelectDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3062 DEBUG_TRACE (DEBUG::Drags
, "New RubberbandSelectDrag\n");
3066 RubberbandSelectDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3068 Drag::start_grab (event
);
3069 show_verbose_cursor_time (adjusted_current_frame (event
));
3073 RubberbandSelectDrag::motion (GdkEvent
* event
, bool)
3080 framepos_t
const pf
= adjusted_current_frame (event
, Config
->get_rubberbanding_snaps_to_grid ());
3082 framepos_t grab
= grab_frame ();
3083 if (Config
->get_rubberbanding_snaps_to_grid ()) {
3084 _editor
->snap_to_with_modifier (grab
, event
);
3087 /* base start and end on initial click position */
3097 if (_drags
->current_pointer_y() < grab_y()) {
3098 y1
= _drags
->current_pointer_y();
3101 y2
= _drags
->current_pointer_y();
3106 if (start
!= end
|| y1
!= y2
) {
3108 double x1
= _editor
->frame_to_pixel (start
);
3109 double x2
= _editor
->frame_to_pixel (end
);
3111 _editor
->rubberband_rect
->property_x1() = x1
;
3112 _editor
->rubberband_rect
->property_y1() = y1
;
3113 _editor
->rubberband_rect
->property_x2() = x2
;
3114 _editor
->rubberband_rect
->property_y2() = y2
;
3116 _editor
->rubberband_rect
->show();
3117 _editor
->rubberband_rect
->raise_to_top();
3119 show_verbose_cursor_time (pf
);
3124 RubberbandSelectDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3126 if (movement_occurred
) {
3128 motion (event
, false);
3131 if (_drags
->current_pointer_y() < grab_y()) {
3132 y1
= _drags
->current_pointer_y();
3135 y2
= _drags
->current_pointer_y();
3140 Selection::Operation op
= ArdourKeyboard::selection_type (event
->button
.state
);
3142 _editor
->begin_reversible_command (_("rubberband selection"));
3144 if (grab_frame() < last_pointer_frame()) {
3145 _editor
->select_all_within (grab_frame(), last_pointer_frame() - 1, y1
, y2
, _editor
->track_views
, op
, false);
3147 _editor
->select_all_within (last_pointer_frame(), grab_frame() - 1, y1
, y2
, _editor
->track_views
, op
, false);
3150 _editor
->commit_reversible_command ();
3153 if (!getenv("ARDOUR_SAE")) {
3154 _editor
->selection
->clear_tracks();
3156 _editor
->selection
->clear_regions();
3157 _editor
->selection
->clear_points ();
3158 _editor
->selection
->clear_lines ();
3161 _editor
->rubberband_rect
->hide();
3165 RubberbandSelectDrag::aborted (bool)
3167 _editor
->rubberband_rect
->hide ();
3170 TimeFXDrag::TimeFXDrag (Editor
* e
, ArdourCanvas::Item
* i
, RegionView
* p
, std::list
<RegionView
*> const & v
)
3171 : RegionDrag (e
, i
, p
, v
)
3173 DEBUG_TRACE (DEBUG::Drags
, "New TimeFXDrag\n");
3177 TimeFXDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
3179 Drag::start_grab (event
, cursor
);
3181 show_verbose_cursor_time (adjusted_current_frame (event
));
3185 TimeFXDrag::motion (GdkEvent
* event
, bool)
3187 RegionView
* rv
= _primary
;
3189 framepos_t
const pf
= adjusted_current_frame (event
);
3191 if (pf
> rv
->region()->position()) {
3192 rv
->get_time_axis_view().show_timestretch (rv
->region()->position(), pf
);
3195 show_verbose_cursor_time (pf
);
3199 TimeFXDrag::finished (GdkEvent
* /*event*/, bool movement_occurred
)
3201 _primary
->get_time_axis_view().hide_timestretch ();
3203 if (!movement_occurred
) {
3207 if (last_pointer_frame() < _primary
->region()->position()) {
3208 /* backwards drag of the left edge - not usable */
3212 framecnt_t newlen
= last_pointer_frame() - _primary
->region()->position();
3214 float percentage
= (double) newlen
/ (double) _primary
->region()->length();
3216 #ifndef USE_RUBBERBAND
3217 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3218 if (_primary
->region()->data_type() == DataType::AUDIO
) {
3219 percentage
= (float) ((double) newlen
- (double) _primary
->region()->length()) / ((double) newlen
) * 100.0f
;
3223 // XXX how do timeFX on multiple regions ?
3228 if (_editor
->time_stretch (rs
, percentage
) == -1) {
3229 error
<< _("An error occurred while executing time stretch operation") << endmsg
;
3234 TimeFXDrag::aborted (bool)
3236 _primary
->get_time_axis_view().hide_timestretch ();
3239 ScrubDrag::ScrubDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3242 DEBUG_TRACE (DEBUG::Drags
, "New ScrubDrag\n");
3246 ScrubDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3248 Drag::start_grab (event
);
3252 ScrubDrag::motion (GdkEvent
* /*event*/, bool)
3254 _editor
->scrub (adjusted_current_frame (0, false), _drags
->current_pointer_x ());
3258 ScrubDrag::finished (GdkEvent
* /*event*/, bool movement_occurred
)
3260 if (movement_occurred
&& _editor
->session()) {
3261 /* make sure we stop */
3262 _editor
->session()->request_transport_speed (0.0);
3267 ScrubDrag::aborted (bool)
3272 SelectionDrag::SelectionDrag (Editor
* e
, ArdourCanvas::Item
* i
, Operation o
)
3276 , _original_pointer_time_axis (-1)
3277 , _last_pointer_time_axis (-1)
3279 DEBUG_TRACE (DEBUG::Drags
, "New SelectionDrag\n");
3283 SelectionDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3285 if (_editor
->session() == 0) {
3289 Gdk::Cursor
* cursor
= 0;
3291 switch (_operation
) {
3292 case CreateSelection
:
3293 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
3298 cursor
= _editor
->cursors()->selector
;
3299 Drag::start_grab (event
, cursor
);
3302 case SelectionStartTrim
:
3303 if (_editor
->clicked_axisview
) {
3304 _editor
->clicked_axisview
->order_selection_trims (_item
, true);
3306 Drag::start_grab (event
, _editor
->cursors()->left_side_trim
);
3309 case SelectionEndTrim
:
3310 if (_editor
->clicked_axisview
) {
3311 _editor
->clicked_axisview
->order_selection_trims (_item
, false);
3313 Drag::start_grab (event
, _editor
->cursors()->right_side_trim
);
3317 Drag::start_grab (event
, cursor
);
3321 if (_operation
== SelectionMove
) {
3322 show_verbose_cursor_time (_editor
->selection
->time
[_editor
->clicked_selection
].start
);
3324 show_verbose_cursor_time (adjusted_current_frame (event
));
3327 _original_pointer_time_axis
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ()).first
->order ();
3331 SelectionDrag::setup_pointer_frame_offset ()
3333 switch (_operation
) {
3334 case CreateSelection
:
3335 _pointer_frame_offset
= 0;
3338 case SelectionStartTrim
:
3340 _pointer_frame_offset
= raw_grab_frame() - _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3343 case SelectionEndTrim
:
3344 _pointer_frame_offset
= raw_grab_frame() - _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3350 SelectionDrag::motion (GdkEvent
* event
, bool first_move
)
3352 framepos_t start
= 0;
3356 pair
<TimeAxisView
*, int> const pending_time_axis
= _editor
->trackview_by_y_position (_drags
->current_pointer_y ());
3357 if (pending_time_axis
.first
== 0) {
3361 framepos_t
const pending_position
= adjusted_current_frame (event
);
3363 /* only alter selection if things have changed */
3365 if (pending_time_axis
.first
->order() == _last_pointer_time_axis
&& pending_position
== last_pointer_frame()) {
3369 switch (_operation
) {
3370 case CreateSelection
:
3372 framepos_t grab
= grab_frame ();
3375 _editor
->snap_to (grab
);
3378 if (pending_position
< grab_frame()) {
3379 start
= pending_position
;
3382 end
= pending_position
;
3386 /* first drag: Either add to the selection
3387 or create a new selection
3393 /* adding to the selection */
3394 _editor
->set_selected_track_as_side_effect (Selection::Add
);
3395 //_editor->selection->add (_editor->clicked_axisview);
3396 _editor
->clicked_selection
= _editor
->selection
->add (start
, end
);
3401 if (_editor
->clicked_axisview
&& !_editor
->selection
->selected (_editor
->clicked_axisview
)) {
3402 //_editor->selection->set (_editor->clicked_axisview);
3403 _editor
->set_selected_track_as_side_effect (Selection::Set
);
3406 _editor
->clicked_selection
= _editor
->selection
->set (start
, end
);
3410 /* select the track that we're in */
3411 if (find (_added_time_axes
.begin(), _added_time_axes
.end(), pending_time_axis
.first
) == _added_time_axes
.end()) {
3412 // _editor->set_selected_track_as_side_effect (Selection::Add);
3413 _editor
->selection
->add (pending_time_axis
.first
);
3414 _added_time_axes
.push_back (pending_time_axis
.first
);
3417 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3418 tracks that we selected in the first place.
3421 int min_order
= min (_original_pointer_time_axis
, pending_time_axis
.first
->order());
3422 int max_order
= max (_original_pointer_time_axis
, pending_time_axis
.first
->order());
3424 list
<TimeAxisView
*>::iterator i
= _added_time_axes
.begin();
3425 while (i
!= _added_time_axes
.end()) {
3427 list
<TimeAxisView
*>::iterator tmp
= i
;
3430 if ((*i
)->order() < min_order
|| (*i
)->order() > max_order
) {
3431 _editor
->selection
->remove (*i
);
3432 _added_time_axes
.remove (*i
);
3441 case SelectionStartTrim
:
3443 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3444 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3446 if (pending_position
> end
) {
3449 start
= pending_position
;
3453 case SelectionEndTrim
:
3455 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3456 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3458 if (pending_position
< start
) {
3461 end
= pending_position
;
3468 start
= _editor
->selection
->time
[_editor
->clicked_selection
].start
;
3469 end
= _editor
->selection
->time
[_editor
->clicked_selection
].end
;
3471 length
= end
- start
;
3473 start
= pending_position
;
3474 _editor
->snap_to (start
);
3476 end
= start
+ length
;
3481 if (event
->button
.x
>= _editor
->horizontal_position() + _editor
->_canvas_width
) {
3482 _editor
->start_canvas_autoscroll (1, 0);
3486 _editor
->selection
->replace (_editor
->clicked_selection
, start
, end
);
3489 if (_operation
== SelectionMove
) {
3490 show_verbose_cursor_time(start
);
3492 show_verbose_cursor_time(pending_position
);
3497 SelectionDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3499 Session
* s
= _editor
->session();
3501 if (movement_occurred
) {
3502 motion (event
, false);
3503 /* XXX this is not object-oriented programming at all. ick */
3504 if (_editor
->selection
->time
.consolidate()) {
3505 _editor
->selection
->TimeChanged ();
3508 /* XXX what if its a music time selection? */
3509 if (s
&& (s
->config
.get_auto_play() || (s
->get_play_range() && s
->transport_rolling()))) {
3510 s
->request_play_range (&_editor
->selection
->time
, true);
3515 /* just a click, no pointer movement.*/
3517 if (Keyboard::no_modifier_keys_pressed (&event
->button
)) {
3518 _editor
->selection
->clear_time();
3521 if (_editor
->clicked_axisview
&& !_editor
->selection
->selected (_editor
->clicked_axisview
)) {
3522 _editor
->selection
->set (_editor
->clicked_axisview
);
3525 if (s
&& s
->get_play_range () && s
->transport_rolling()) {
3526 s
->request_stop (false, false);
3531 _editor
->stop_canvas_autoscroll ();
3535 SelectionDrag::aborted (bool)
3540 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor
* e
, ArdourCanvas::Item
* i
, Operation o
)
3545 DEBUG_TRACE (DEBUG::Drags
, "New RangeMarkerBarDrag\n");
3547 _drag_rect
= new ArdourCanvas::SimpleRect (*_editor
->time_line_group
, 0.0, 0.0, 0.0,
3548 physical_screen_height (_editor
->get_window()));
3549 _drag_rect
->hide ();
3551 _drag_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect
.get();
3552 _drag_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect
.get();
3556 RangeMarkerBarDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3558 if (_editor
->session() == 0) {
3562 Gdk::Cursor
* cursor
= 0;
3564 if (!_editor
->temp_location
) {
3565 _editor
->temp_location
= new Location (*_editor
->session());
3568 switch (_operation
) {
3569 case CreateRangeMarker
:
3570 case CreateTransportMarker
:
3571 case CreateCDMarker
:
3573 if (Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
)) {
3578 cursor
= _editor
->cursors()->selector
;
3582 Drag::start_grab (event
, cursor
);
3584 show_verbose_cursor_time (adjusted_current_frame (event
));
3588 RangeMarkerBarDrag::motion (GdkEvent
* event
, bool first_move
)
3590 framepos_t start
= 0;
3592 ArdourCanvas::SimpleRect
*crect
;
3594 switch (_operation
) {
3595 case CreateRangeMarker
:
3596 crect
= _editor
->range_bar_drag_rect
;
3598 case CreateTransportMarker
:
3599 crect
= _editor
->transport_bar_drag_rect
;
3601 case CreateCDMarker
:
3602 crect
= _editor
->cd_marker_bar_drag_rect
;
3605 cerr
<< "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl
;
3610 framepos_t
const pf
= adjusted_current_frame (event
);
3612 if (_operation
== CreateRangeMarker
|| _operation
== CreateTransportMarker
|| _operation
== CreateCDMarker
) {
3613 framepos_t grab
= grab_frame ();
3614 _editor
->snap_to (grab
);
3616 if (pf
< grab_frame()) {
3624 /* first drag: Either add to the selection
3625 or create a new selection.
3630 _editor
->temp_location
->set (start
, end
);
3634 update_item (_editor
->temp_location
);
3636 //_drag_rect->raise_to_top();
3641 if (event
->button
.x
>= _editor
->horizontal_position() + _editor
->_canvas_width
) {
3642 _editor
->start_canvas_autoscroll (1, 0);
3646 _editor
->temp_location
->set (start
, end
);
3648 double x1
= _editor
->frame_to_pixel (start
);
3649 double x2
= _editor
->frame_to_pixel (end
);
3650 crect
->property_x1() = x1
;
3651 crect
->property_x2() = x2
;
3653 update_item (_editor
->temp_location
);
3656 show_verbose_cursor_time (pf
);
3661 RangeMarkerBarDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3663 Location
* newloc
= 0;
3667 if (movement_occurred
) {
3668 motion (event
, false);
3671 switch (_operation
) {
3672 case CreateRangeMarker
:
3673 case CreateCDMarker
:
3675 _editor
->begin_reversible_command (_("new range marker"));
3676 XMLNode
&before
= _editor
->session()->locations()->get_state();
3677 _editor
->session()->locations()->next_available_name(rangename
,"unnamed");
3678 if (_operation
== CreateCDMarker
) {
3679 flags
= Location::IsRangeMarker
| Location::IsCDMarker
;
3680 _editor
->cd_marker_bar_drag_rect
->hide();
3683 flags
= Location::IsRangeMarker
;
3684 _editor
->range_bar_drag_rect
->hide();
3686 newloc
= new Location (
3687 *_editor
->session(), _editor
->temp_location
->start(), _editor
->temp_location
->end(), rangename
, (Location::Flags
) flags
3690 _editor
->session()->locations()->add (newloc
, true);
3691 XMLNode
&after
= _editor
->session()->locations()->get_state();
3692 _editor
->session()->add_command(new MementoCommand
<Locations
>(*(_editor
->session()->locations()), &before
, &after
));
3693 _editor
->commit_reversible_command ();
3697 case CreateTransportMarker
:
3698 // popup menu to pick loop or punch
3699 _editor
->new_transport_marker_context_menu (&event
->button
, _item
);
3703 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3705 if (Keyboard::no_modifier_keys_pressed (&event
->button
) && _operation
!= CreateCDMarker
) {
3710 _editor
->session()->locations()->marks_either_side (grab_frame(), start
, end
);
3712 if (end
== max_framepos
) {
3713 end
= _editor
->session()->current_end_frame ();
3716 if (start
== max_framepos
) {
3717 start
= _editor
->session()->current_start_frame ();
3720 switch (_editor
->mouse_mode
) {
3722 /* find the two markers on either side and then make the selection from it */
3723 _editor
->select_all_within (start
, end
, 0.0f
, FLT_MAX
, _editor
->track_views
, Selection::Set
, false);
3727 /* find the two markers on either side of the click and make the range out of it */
3728 _editor
->selection
->set (start
, end
);
3737 _editor
->stop_canvas_autoscroll ();
3741 RangeMarkerBarDrag::aborted (bool)
3747 RangeMarkerBarDrag::update_item (Location
* location
)
3749 double const x1
= _editor
->frame_to_pixel (location
->start());
3750 double const x2
= _editor
->frame_to_pixel (location
->end());
3752 _drag_rect
->property_x1() = x1
;
3753 _drag_rect
->property_x2() = x2
;
3756 MouseZoomDrag::MouseZoomDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3760 DEBUG_TRACE (DEBUG::Drags
, "New MouseZoomDrag\n");
3764 MouseZoomDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3766 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L
)) {
3767 Drag::start_grab (event
, _editor
->cursors()->zoom_out
);
3770 Drag::start_grab (event
, _editor
->cursors()->zoom_in
);
3774 show_verbose_cursor_time (adjusted_current_frame (event
));
3778 MouseZoomDrag::motion (GdkEvent
* event
, bool first_move
)
3783 framepos_t
const pf
= adjusted_current_frame (event
);
3785 framepos_t grab
= grab_frame ();
3786 _editor
->snap_to_with_modifier (grab
, event
);
3788 /* base start and end on initial click position */
3800 _editor
->zoom_rect
->show();
3801 _editor
->zoom_rect
->raise_to_top();
3804 _editor
->reposition_zoom_rect(start
, end
);
3806 show_verbose_cursor_time (pf
);
3811 MouseZoomDrag::finished (GdkEvent
* event
, bool movement_occurred
)
3813 if (movement_occurred
) {
3814 motion (event
, false);
3816 if (grab_frame() < last_pointer_frame()) {
3817 _editor
->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3819 _editor
->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3822 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L
)) {
3823 _editor
->tav_zoom_step (_zoom_out
);
3825 _editor
->temporal_zoom_to_frame (_zoom_out
, grab_frame());
3829 _editor
->zoom_rect
->hide();
3833 MouseZoomDrag::aborted (bool)
3835 _editor
->zoom_rect
->hide ();
3838 NoteDrag::NoteDrag (Editor
* e
, ArdourCanvas::Item
* i
)
3840 , _cumulative_dx (0)
3841 , _cumulative_dy (0)
3843 DEBUG_TRACE (DEBUG::Drags
, "New NoteDrag\n");
3845 _primary
= dynamic_cast<CanvasNoteEvent
*> (_item
);
3846 _region
= &_primary
->region_view ();
3847 _note_height
= _region
->midi_stream_view()->note_height ();
3851 NoteDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
*)
3853 Drag::start_grab (event
);
3855 if (!(_was_selected
= _primary
->selected())) {
3857 /* tertiary-click means extend selection - we'll do that on button release,
3858 so don't add it here, because otherwise we make it hard to figure
3859 out the "extend-to" range.
3862 bool extend
= Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::TertiaryModifier
);
3865 bool add
= Keyboard::modifier_state_equals (event
->button
.state
, Keyboard::PrimaryModifier
);
3868 _region
->note_selected (_primary
, true);
3870 _region
->unique_select (_primary
);
3876 /** @return Current total drag x change in frames */
3878 NoteDrag::total_dx () const
3881 frameoffset_t
const dx
= _editor
->unit_to_frame (_drags
->current_pointer_x() - grab_x());
3883 /* primary note time */
3884 frameoffset_t
const n
= _region
->beats_to_frames (_primary
->note()->time ());
3886 /* new time of the primary note relative to the region position */
3887 frameoffset_t st
= n
+ dx
;
3889 /* prevent the note being dragged earlier than the region's position */
3894 /* snap and return corresponding delta */
3895 return _region
->snap_frame_to_frame (st
) - n
;
3898 /** @return Current total drag y change in notes */
3900 NoteDrag::total_dy () const
3902 /* this is `backwards' to make increasing note number go in the right direction */
3903 double const dy
= _drags
->current_pointer_y() - grab_y();
3908 if (abs (dy
) >= _note_height
) {
3910 ndy
= (int8_t) ceil (dy
/ _note_height
/ 2.0);
3912 ndy
= (int8_t) floor (dy
/ _note_height
/ 2.0);
3916 /* more positive value = higher pitch and higher y-axis position on track,
3917 which is the inverse of the X-centric geometric universe
3924 NoteDrag::motion (GdkEvent
*, bool)
3926 /* Total change in x and y since the start of the drag */
3927 frameoffset_t
const dx
= total_dx ();
3928 int8_t const dy
= -total_dy ();
3930 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3931 double const tdx
= _editor
->frame_to_unit (dx
) - _cumulative_dx
;
3932 double const tdy
= dy
* _note_height
- _cumulative_dy
;
3935 _cumulative_dx
+= tdx
;
3936 _cumulative_dy
+= tdy
;
3938 int8_t note_delta
= total_dy();
3940 _region
->move_selection (tdx
, tdy
, note_delta
);
3943 snprintf (buf
, sizeof (buf
), "%s (%d)", Evoral::midi_note_name (_primary
->note()->note() + note_delta
).c_str(),
3944 (int) floor (_primary
->note()->note() + note_delta
));
3946 show_verbose_cursor_text (buf
);
3951 NoteDrag::finished (GdkEvent
* ev
, bool moved
)
3954 if (_editor
->current_mouse_mode() == Editing::MouseObject
) {
3956 if (_was_selected
) {
3957 bool add
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::PrimaryModifier
);
3959 _region
->note_deselected (_primary
);
3962 bool extend
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::TertiaryModifier
);
3963 bool add
= Keyboard::modifier_state_equals (ev
->button
.state
, Keyboard::PrimaryModifier
);
3965 if (!extend
&& !add
&& _region
->selection_size() > 1) {
3966 _region
->unique_select (_primary
);
3967 } else if (extend
) {
3968 _region
->note_selected (_primary
, true, true);
3970 /* it was added during button press */
3975 _region
->note_dropped (_primary
, total_dx(), total_dy());
3980 NoteDrag::aborted (bool)
3985 AutomationRangeDrag::AutomationRangeDrag (Editor
* editor
, ArdourCanvas::Item
* item
, list
<AudioRange
> const & r
)
3986 : Drag (editor
, item
)
3988 , _nothing_to_drag (false)
3990 DEBUG_TRACE (DEBUG::Drags
, "New AutomationRangeDrag\n");
3992 _atav
= reinterpret_cast<AutomationTimeAxisView
*> (_item
->get_data ("trackview"));
3995 /* get all lines in the automation view */
3996 list
<boost::shared_ptr
<AutomationLine
> > lines
= _atav
->lines ();
3998 /* find those that overlap the ranges being dragged */
3999 list
<boost::shared_ptr
<AutomationLine
> >::iterator i
= lines
.begin ();
4000 while (i
!= lines
.end ()) {
4001 list
<boost::shared_ptr
<AutomationLine
> >::iterator j
= i
;
4004 pair
<framepos_t
, framepos_t
> const r
= (*i
)->get_point_x_range ();
4006 /* check this range against all the AudioRanges that we are using */
4007 list
<AudioRange
>::const_iterator k
= _ranges
.begin ();
4008 while (k
!= _ranges
.end()) {
4009 if (k
->coverage (r
.first
, r
.second
) != OverlapNone
) {
4015 /* add it to our list if it overlaps at all */
4016 if (k
!= _ranges
.end()) {
4021 _lines
.push_back (n
);
4027 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4031 AutomationRangeDrag::start_grab (GdkEvent
* event
, Gdk::Cursor
* cursor
)
4033 Drag::start_grab (event
, cursor
);
4035 /* Get line states before we start changing things */
4036 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4037 i
->state
= &i
->line
->get_state ();
4040 if (_ranges
.empty()) {
4042 /* No selected time ranges: drag all points */
4043 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4044 uint32_t const N
= i
->line
->npoints ();
4045 for (uint32_t j
= 0; j
< N
; ++j
) {
4046 i
->points
.push_back (i
->line
->nth (j
));
4052 for (list
<AudioRange
>::const_iterator i
= _ranges
.begin(); i
!= _ranges
.end(); ++i
) {
4054 framecnt_t
const half
= (i
->start
+ i
->end
) / 2;
4056 /* find the line that this audio range starts in */
4057 list
<Line
>::iterator j
= _lines
.begin();
4058 while (j
!= _lines
.end() && (j
->range
.first
> i
->start
|| j
->range
.second
< i
->start
)) {
4062 if (j
!= _lines
.end()) {
4063 boost::shared_ptr
<AutomationList
> the_list
= j
->line
->the_list ();
4065 /* j is the line that this audio range starts in; fade into it;
4066 64 samples length plucked out of thin air.
4069 framepos_t a
= i
->start
+ 64;
4074 double const p
= j
->line
->time_converter().from (i
->start
- j
->line
->time_converter().origin_b ());
4075 double const q
= j
->line
->time_converter().from (a
- j
->line
->time_converter().origin_b ());
4077 the_list
->add (p
, the_list
->eval (p
));
4078 j
->line
->add_always_in_view (p
);
4079 the_list
->add (q
, the_list
->eval (q
));
4080 j
->line
->add_always_in_view (q
);
4083 /* same thing for the end */
4086 while (j
!= _lines
.end() && (j
->range
.first
> i
->end
|| j
->range
.second
< i
->end
)) {
4090 if (j
!= _lines
.end()) {
4091 boost::shared_ptr
<AutomationList
> the_list
= j
->line
->the_list ();
4093 /* j is the line that this audio range starts in; fade out of it;
4094 64 samples length plucked out of thin air.
4097 framepos_t b
= i
->end
- 64;
4102 double const p
= j
->line
->time_converter().from (b
- j
->line
->time_converter().origin_b ());
4103 double const q
= j
->line
->time_converter().from (i
->end
- j
->line
->time_converter().origin_b ());
4105 the_list
->add (p
, the_list
->eval (p
));
4106 j
->line
->add_always_in_view (p
);
4107 the_list
->add (q
, the_list
->eval (q
));
4108 j
->line
->add_always_in_view (q
);
4112 _nothing_to_drag
= true;
4114 /* Find all the points that should be dragged and put them in the relevant
4115 points lists in the Line structs.
4118 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4120 uint32_t const N
= i
->line
->npoints ();
4121 for (uint32_t j
= 0; j
< N
; ++j
) {
4123 /* here's a control point on this line */
4124 ControlPoint
* p
= i
->line
->nth (j
);
4125 double const w
= i
->line
->time_converter().to ((*p
->model())->when
) + i
->line
->time_converter().origin_b ();
4127 /* see if it's inside a range */
4128 list
<AudioRange
>::const_iterator k
= _ranges
.begin ();
4129 while (k
!= _ranges
.end() && (k
->start
>= w
|| k
->end
<= w
)) {
4133 if (k
!= _ranges
.end()) {
4134 /* dragging this point */
4135 _nothing_to_drag
= false;
4136 i
->points
.push_back (p
);
4142 if (_nothing_to_drag
) {
4146 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4147 i
->line
->start_drag_multiple (i
->points
, 1 - (_drags
->current_pointer_y() / i
->line
->height ()), i
->state
);
4152 AutomationRangeDrag::motion (GdkEvent
*, bool /*first_move*/)
4154 if (_nothing_to_drag
) {
4158 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4159 float const f
= 1 - (_drags
->current_pointer_y() / i
->line
->height());
4161 /* we are ignoring x position for this drag, so we can just pass in anything */
4162 i
->line
->drag_motion (0, f
, true, false);
4167 AutomationRangeDrag::finished (GdkEvent
* event
, bool)
4169 if (_nothing_to_drag
) {
4173 motion (event
, false);
4174 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4175 i
->line
->end_drag ();
4176 i
->line
->clear_always_in_view ();
4179 _editor
->session()->commit_reversible_command ();
4183 AutomationRangeDrag::aborted (bool)
4185 for (list
<Line
>::iterator i
= _lines
.begin(); i
!= _lines
.end(); ++i
) {
4186 i
->line
->clear_always_in_view ();
4191 DraggingView::DraggingView (RegionView
* v
, RegionDrag
* parent
)
4194 time_axis_view
= parent
->find_time_axis_view (&v
->get_time_axis_view ());
4195 layer
= v
->region()->layer ();
4196 initial_y
= v
->get_canvas_group()->property_y ();
4197 initial_playlist
= v
->region()->playlist ();
4198 initial_position
= v
->region()->position ();
4199 initial_end
= v
->region()->position () + v
->region()->length ();
4202 PatchChangeDrag::PatchChangeDrag (Editor
* e
, CanvasPatchChange
* i
, MidiRegionView
* r
)
4206 , _cumulative_dx (0)
4208 DEBUG_TRACE (DEBUG::Drags
, "New PatchChangeDrag\n");
4212 PatchChangeDrag::motion (GdkEvent
* ev
, bool)
4214 framepos_t f
= adjusted_current_frame (ev
);
4215 boost::shared_ptr
<Region
> r
= _region_view
->region ();
4216 f
= max (f
, r
->position ());
4217 f
= min (f
, r
->last_frame ());
4219 framecnt_t
const dxf
= f
- grab_frame();
4220 double const dxu
= _editor
->frame_to_unit (dxf
);
4221 _patch_change
->move (dxu
- _cumulative_dx
, 0);
4222 _cumulative_dx
= dxu
;
4226 PatchChangeDrag::finished (GdkEvent
* ev
, bool movement_occurred
)
4228 if (!movement_occurred
) {
4232 boost::shared_ptr
<Region
> r (_region_view
->region ());
4234 framepos_t f
= adjusted_current_frame (ev
);
4235 f
= max (f
, r
->position ());
4236 f
= min (f
, r
->last_frame ());
4238 _region_view
->move_patch_change (
4240 _region_view
->frames_to_beats (f
- r
->position() - r
->start())
4245 PatchChangeDrag::aborted (bool)
4247 _patch_change
->move (-_cumulative_dx
, 0);
4251 PatchChangeDrag::setup_pointer_frame_offset ()
4253 boost::shared_ptr
<Region
> region
= _region_view
->region ();
4254 _pointer_frame_offset
= raw_grab_frame() - _region_view
->beats_to_frames (_patch_change
->patch()->time()) - region
->position() + region
->start();