2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "pbd/memento_command.h"
21 #include "pbd/basename.h"
22 #include "ardour/diskstream.h"
23 #include "ardour/session.h"
24 #include "ardour/dB.h"
25 #include "ardour/region_factory.h"
26 #include "ardour/midi_diskstream.h"
30 #include "audio_region_view.h"
31 #include "midi_region_view.h"
32 #include "ardour_ui.h"
33 #include "gui_thread.h"
34 #include "control_point.h"
36 #include "region_gain_line.h"
37 #include "editor_drag.h"
38 #include "audio_time_axis.h"
39 #include "midi_time_axis.h"
40 #include "canvas-note.h"
41 #include "selection.h"
42 #include "midi_selection.h"
43 #include "automation_time_axis.h"
46 using namespace ARDOUR;
49 using namespace Editing;
50 using namespace ArdourCanvas;
52 using Gtkmm2ext::Keyboard;
54 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
56 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
59 , _pointer_frame_offset (0)
60 , _have_transaction (false)
62 , _move_threshold_passed (false)
64 , _last_pointer_frame (0)
65 , _current_pointer_frame (0)
71 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
77 cursor = _editor->which_grabber_cursor ();
80 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
84 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
87 cursor = _editor->which_grabber_cursor ();
90 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
92 if (Keyboard::is_button2_event (&event->button)) {
93 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
94 _y_constrained = true;
95 _x_constrained = false;
97 _y_constrained = false;
98 _x_constrained = true;
101 _x_constrained = false;
102 _y_constrained = false;
105 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
106 _grab_frame = adjusted_frame (_grab_frame, event);
107 _last_pointer_frame = _grab_frame;
108 _current_pointer_frame = _grab_frame;
109 _current_pointer_x = _grab_x;
110 _current_pointer_y = _grab_y;
111 _last_pointer_x = _current_pointer_x;
112 _last_pointer_y = _current_pointer_y;
114 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
118 if (_editor->session() && _editor->session()->transport_rolling()) {
121 _was_rolling = false;
124 switch (_editor->snap_type()) {
125 case SnapToRegionStart:
126 case SnapToRegionEnd:
127 case SnapToRegionSync:
128 case SnapToRegionBoundary:
129 _editor->build_region_boundary_cache ();
136 /** @param event GDK event, or 0.
137 * @return true if some movement occurred, otherwise false.
140 Drag::end_grab (GdkEvent* event)
144 _editor->stop_canvas_autoscroll ();
146 _item->ungrab (event ? event->button.time : 0);
148 _last_pointer_x = _current_pointer_x;
149 _last_pointer_y = _current_pointer_y;
150 finished (event, _move_threshold_passed);
152 _editor->hide_verbose_canvas_cursor();
156 return _move_threshold_passed;
160 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
164 if (f > _pointer_frame_offset) {
165 pos = f - _pointer_frame_offset;
169 _editor->snap_to_with_modifier (pos, event);
176 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
178 return adjusted_frame (_current_pointer_frame, event, snap);
182 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
184 _last_pointer_x = _current_pointer_x;
185 _last_pointer_y = _current_pointer_y;
186 _last_pointer_frame = adjusted_current_frame (event);
187 _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
189 /* check to see if we have moved in any way that matters since the last motion event */
190 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
191 (!y_movement_matters() || _last_pointer_y == _current_pointer_y) ) {
195 pair<nframes64_t, int> const threshold = move_threshold ();
197 bool const old_move_threshold_passed = _move_threshold_passed;
199 if (!from_autoscroll && !_move_threshold_passed) {
201 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
202 bool const yp = (::fabs ((_current_pointer_y - _grab_y)) >= threshold.second);
204 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
207 if (active (_editor->mouse_mode) && _move_threshold_passed) {
209 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
210 if (!from_autoscroll) {
211 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
214 motion (event, _move_threshold_passed != old_move_threshold_passed);
233 _editor->stop_canvas_autoscroll ();
234 _editor->hide_verbose_canvas_cursor ();
239 pair<nframes64_t, nframes64_t>
240 Drag::extent () const
242 nframes64_t const f = adjusted_current_frame (0);
243 return make_pair (f, f);
246 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
251 RegionView::RegionViewGoingAway.connect (death_connection, ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
255 RegionDrag::region_going_away (RegionView* v)
262 pair<nframes64_t, nframes64_t>
263 RegionDrag::extent () const
265 nframes64_t const f = adjusted_current_frame (0);
266 return make_pair (f, f + _primary->region()->length ());
270 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
271 : RegionDrag (e, i, p, v),
282 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
284 Drag::start_grab (event);
286 _editor->show_verbose_time_cursor (_last_frame_position, 10);
289 RegionMotionDrag::TimeAxisViewSummary
290 RegionMotionDrag::get_time_axis_view_summary ()
292 int32_t children = 0;
293 TimeAxisViewSummary sum;
295 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
297 /* get a bitmask representing the visible tracks */
299 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
300 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
301 TimeAxisView::Children children_list;
303 /* zeroes are audio/MIDI tracks. ones are other types. */
305 if (!rtv->hidden()) {
307 if (!rtv->is_track()) {
308 /* not an audio nor MIDI track */
309 sum.tracks = sum.tracks |= (0x01 << rtv->order());
312 sum.height_list[rtv->order()] = (*i)->current_height();
315 if ((children_list = rtv->get_child_list()).size() > 0) {
316 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
317 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
318 sum.height_list[rtv->order() + children] = (*j)->current_height();
329 RegionMotionDrag::compute_y_delta (
330 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
331 int32_t last_pointer_layer, int32_t current_pointer_layer,
332 TimeAxisViewSummary const & tavs,
333 int32_t* pointer_order_span, int32_t* pointer_layer_span,
334 int32_t* canvas_pointer_order_span
338 *pointer_order_span = 0;
339 *pointer_layer_span = 0;
343 bool clamp_y_axis = false;
345 /* the change in track order between this callback and the last */
346 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
347 /* the change in layer between this callback and the last;
348 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
349 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
351 if (*pointer_order_span != 0) {
353 /* find the actual pointer span, in terms of the number of visible tracks;
354 to do this, we reduce |pointer_order_span| by the number of hidden tracks
357 *canvas_pointer_order_span = *pointer_order_span;
358 if (last_pointer_view->order() >= current_pointer_view->order()) {
359 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
360 if (tavs.height_list[y] == 0) {
361 *canvas_pointer_order_span--;
365 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
366 if (tavs.height_list[y] == 0) {
367 *canvas_pointer_order_span++;
372 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
374 RegionView* rv = (*i);
376 if (rv->region()->locked()) {
380 double ix1, ix2, iy1, iy2;
381 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
382 rv->get_canvas_frame()->i2w (ix1, iy1);
383 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
385 /* get the new trackview for this particular region */
386 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
390 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
391 as surely this is a per-region thing... */
393 clamp_y_axis = y_movement_disallowed (
394 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
402 } else if (_dest_trackview == current_pointer_view) {
404 if (current_pointer_layer == last_pointer_layer) {
405 /* No movement; clamp */
411 _dest_trackview = current_pointer_view;
412 _dest_layer = current_pointer_layer;
420 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
422 /* compute the amount of pointer motion in frames, and where
423 the region would be if we moved it by that much.
425 *pending_region_position = adjusted_current_frame (event);
427 nframes64_t sync_frame;
428 nframes64_t sync_offset;
431 sync_offset = _primary->region()->sync_offset (sync_dir);
433 /* we don't handle a sync point that lies before zero.
435 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
437 sync_frame = *pending_region_position + (sync_dir*sync_offset);
439 _editor->snap_to_with_modifier (sync_frame, event);
441 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
444 *pending_region_position = _last_frame_position;
447 if (*pending_region_position > max_frames - _primary->region()->length()) {
448 *pending_region_position = _last_frame_position;
453 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
455 /* now compute the canvas unit distance we need to move the regionview
456 to make it appear at the new location.
459 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
461 if (*pending_region_position <= _last_frame_position) {
463 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
465 RegionView* rv = (*i);
467 // If any regionview is at zero, we need to know so we can stop further leftward motion.
469 double ix1, ix2, iy1, iy2;
470 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
471 rv->get_canvas_frame()->i2w (ix1, iy1);
473 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
475 *pending_region_position = _last_frame_position;
482 _last_frame_position = *pending_region_position;
489 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
493 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
495 vector<int32_t>::iterator j;
497 /* *pointer* variables reflect things about the pointer; as we may be moving
498 multiple regions, much detail must be computed per-region */
500 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
501 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
502 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
503 is always 0 regardless of what the region's "real" layer is */
504 RouteTimeAxisView* current_pointer_view;
505 layer_t current_pointer_layer;
506 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
510 /* TimeAxisView that we were pointing at last time we entered this method */
511 TimeAxisView const * const last_pointer_view = _dest_trackview;
512 /* the order of the track that we were pointing at last time we entered this method */
513 int32_t const last_pointer_order = last_pointer_view->order ();
514 /* the layer that we were pointing at last time we entered this method */
515 layer_t const last_pointer_layer = _dest_layer;
517 int32_t pointer_order_span;
518 int32_t pointer_layer_span;
519 int32_t canvas_pointer_order_span;
521 bool const clamp_y_axis = compute_y_delta (
522 last_pointer_view, current_pointer_view,
523 last_pointer_layer, current_pointer_layer, tavs,
524 &pointer_order_span, &pointer_layer_span,
525 &canvas_pointer_order_span
528 nframes64_t pending_region_position;
529 double const x_delta = compute_x_delta (event, &pending_region_position);
531 /*************************************************************
533 ************************************************************/
535 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
536 /* haven't reached next snap point, and we're not switching
537 trackviews nor layers. nothing to do.
542 /*************************************************************
544 ************************************************************/
546 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
548 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
550 RegionView* rv = (*i);
552 if (rv->region()->locked()) {
556 /* here we are calculating the y distance from the
557 top of the first track view to the top of the region
558 area of the track view that we're working on */
560 /* this x value is just a dummy value so that we have something
565 /* distance from the top of this track view to the region area
566 of our track view is always 1 */
570 /* convert to world coordinates, ie distance from the top of
573 rv->get_canvas_frame()->i2w (ix1, iy1);
575 /* compensate for the ruler section and the vertical scrollbar position */
576 iy1 += _editor->get_trackview_group_vertical_offset ();
580 // hide any dependent views
582 rv->get_time_axis_view().hide_dependent_views (*rv);
585 reparent to a non scrolling group so that we can keep the
586 region selection above all time axis views.
587 reparenting means we have to move the rv as the two
588 parent groups have different coordinates.
591 rv->get_canvas_group()->property_y() = iy1 - 1;
592 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
594 rv->fake_set_opaque (true);
597 /* current view for this particular region */
598 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
599 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
601 if (pointer_order_span != 0 && !clamp_y_axis) {
603 /* INTER-TRACK MOVEMENT */
605 /* move through the height list to the track that the region is currently on */
606 vector<int32_t>::iterator j = tavs.height_list.begin ();
608 while (j != tavs.height_list.end () && x != rtv->order ()) {
614 int32_t temp_pointer_order_span = canvas_pointer_order_span;
616 if (j != tavs.height_list.end ()) {
618 /* Account for layers in the original and
619 destination tracks. If we're moving around in layers we assume
620 that only one track is involved, so it's ok to use *pointer*
623 StreamView* lv = last_pointer_view->view ();
626 /* move to the top of the last trackview */
627 if (lv->layer_display () == Stacked) {
628 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
631 StreamView* cv = current_pointer_view->view ();
634 /* move to the right layer on the current trackview */
635 if (cv->layer_display () == Stacked) {
636 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
639 /* And for being on a non-topmost layer on the new
642 while (temp_pointer_order_span > 0) {
643 /* we're moving up canvas-wise,
644 so we need to find the next track height
646 if (j != tavs.height_list.begin()) {
650 if (x != last_pointer_order) {
652 ++temp_pointer_order_span;
657 temp_pointer_order_span--;
660 while (temp_pointer_order_span < 0) {
664 if (x != last_pointer_order) {
666 --temp_pointer_order_span;
670 if (j != tavs.height_list.end()) {
674 temp_pointer_order_span++;
678 /* find out where we'll be when we move and set height accordingly */
680 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
681 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
682 rv->set_height (temp_rtv->view()->child_height());
684 /* if you un-comment the following, the region colours will follow
685 the track colours whilst dragging; personally
686 i think this can confuse things, but never mind.
689 //const GdkColor& col (temp_rtv->view->get_region_color());
690 //rv->set_color (const_cast<GdkColor&>(col));
694 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
696 /* INTER-LAYER MOVEMENT in the same track */
697 y_delta = rtv->view()->child_height () * pointer_layer_span;
702 _editor->mouse_brush_insert_region (rv, pending_region_position);
704 rv->move (x_delta, y_delta);
707 } /* foreach region */
709 _total_x_delta += x_delta;
712 _editor->cursor_group->raise_to_top();
715 if (x_delta != 0 && !_brushing) {
716 _editor->show_verbose_time_cursor (_last_frame_position, 10);
721 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
723 if (_copy && first_move) {
724 copy_regions (event);
727 RegionMotionDrag::motion (event, first_move);
731 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
733 vector<RegionView*> copies;
734 boost::shared_ptr<Diskstream> ds;
735 boost::shared_ptr<Playlist> from_playlist;
736 RegionSelection new_views;
737 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
738 PlaylistSet modified_playlists;
739 PlaylistSet frozen_playlists;
740 list <sigc::connection> modified_playlist_connections;
741 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
742 nframes64_t drag_delta;
743 bool changed_tracks, changed_position;
744 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
745 RouteTimeAxisView* source_tv;
747 if (!movement_occurred) {
753 /* all changes were made during motion event handlers */
756 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
757 copies.push_back (*i);
764 /* reverse this here so that we have the correct logic to finalize
768 if (Config->get_edit_mode() == Lock) {
769 _x_constrained = !_x_constrained;
773 if (_x_constrained) {
774 _editor->begin_reversible_command (_("fixed time region copy"));
776 _editor->begin_reversible_command (_("region copy"));
779 if (_x_constrained) {
780 _editor->begin_reversible_command (_("fixed time region drag"));
782 _editor->begin_reversible_command (_("region drag"));
786 _have_transaction = true;
788 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
789 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
791 drag_delta = _primary->region()->position() - _last_frame_position;
793 _editor->update_canvas_now ();
795 /* make a list of where each region ended up */
796 final = find_time_axis_views_and_layers ();
798 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
800 RegionView* rv = (*i);
801 RouteTimeAxisView* dest_rtv = final[*i].first;
802 layer_t dest_layer = final[*i].second;
806 if (rv->region()->locked()) {
811 if (changed_position && !_x_constrained) {
812 where = rv->region()->position() - drag_delta;
814 where = rv->region()->position();
817 boost::shared_ptr<Region> new_region;
820 /* we already made a copy */
821 new_region = rv->region();
823 /* undo the previous hide_dependent_views so that xfades don't
824 disappear on copying regions
827 //rv->get_time_axis_view().reveal_dependent_views (*rv);
829 } else if (changed_tracks && dest_rtv->playlist()) {
830 new_region = RegionFactory::create (rv->region());
833 if (changed_tracks || _copy) {
835 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
842 _editor->latest_regionviews.clear ();
844 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
846 insert_result = modified_playlists.insert (to_playlist);
848 if (insert_result.second) {
849 _editor->session()->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
852 to_playlist->add_region (new_region, where);
853 if (dest_rtv->view()->layer_display() == Stacked) {
854 new_region->set_layer (dest_layer);
855 new_region->set_pending_explicit_relayer (true);
860 if (!_editor->latest_regionviews.empty()) {
861 // XXX why just the first one ? we only expect one
862 // commented out in nick_m's canvas reworking. is that intended?
863 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
864 new_views.push_back (_editor->latest_regionviews.front());
869 motion on the same track. plonk the previously reparented region
870 back to its original canvas group (its streamview).
871 No need to do anything for copies as they are fake regions which will be deleted.
874 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
875 rv->get_canvas_group()->property_y() = 0;
876 rv->get_time_axis_view().reveal_dependent_views (*rv);
878 /* just change the model */
880 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
882 if (dest_rtv->view()->layer_display() == Stacked) {
883 rv->region()->set_layer (dest_layer);
884 rv->region()->set_pending_explicit_relayer (true);
887 insert_result = modified_playlists.insert (playlist);
889 if (insert_result.second) {
890 _editor->session()->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
892 /* freeze to avoid lots of relayering in the case of a multi-region drag */
893 frozen_insert_result = frozen_playlists.insert(playlist);
895 if (frozen_insert_result.second) {
899 rv->region()->set_position (where, (void*) this);
902 if (changed_tracks && !_copy) {
904 /* get the playlist where this drag started. we can't use rv->region()->playlist()
905 because we may have copied the region and it has not been attached to a playlist.
908 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
909 ds = source_tv->get_diskstream();
910 from_playlist = ds->playlist();
914 assert (from_playlist);
916 /* moved to a different audio track, without copying */
918 /* the region that used to be in the old playlist is not
919 moved to the new one - we use a copy of it. as a result,
920 any existing editor for the region should no longer be
924 rv->hide_region_editor();
925 rv->fake_set_opaque (false);
927 /* remove the region from the old playlist */
929 insert_result = modified_playlists.insert (from_playlist);
931 if (insert_result.second) {
932 _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
935 from_playlist->remove_region (rv->region());
937 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
938 was selected in all of them, then removing it from a playlist will have removed all
939 trace of it from the selection (i.e. there were N regions selected, we removed 1,
940 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
941 corresponding regionview, and the selection is now empty).
943 this could have invalidated any and all iterators into the region selection.
945 the heuristic we use here is: if the region selection is empty, break out of the loop
946 here. if the region selection is not empty, then restart the loop because we know that
947 we must have removed at least the region(view) we've just been working on as well as any
948 that we processed on previous iterations.
950 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
954 if (_views.empty()) {
965 copies.push_back (rv);
969 if we've created new regions either by copying or moving
970 to a new track, we want to replace the old selection with the new ones
972 if (new_views.size() > 0) {
973 _editor->selection->set (new_views);
976 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
981 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
982 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
985 _editor->commit_reversible_command ();
987 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
993 RegionMoveDrag::aborted ()
997 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1004 RegionMotionDrag::aborted ();
1009 RegionMotionDrag::aborted ()
1011 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1012 TimeAxisView* tv = &(*i)->get_time_axis_view ();
1013 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1015 (*i)->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1016 (*i)->get_canvas_group()->property_y() = 0;
1017 (*i)->get_time_axis_view().reveal_dependent_views (**i);
1018 (*i)->fake_set_opaque (false);
1019 (*i)->move (-_total_x_delta, 0);
1020 (*i)->set_height (rtv->view()->child_height ());
1023 _editor->update_canvas_now ();
1028 RegionMotionDrag::x_move_allowed () const
1030 if (Config->get_edit_mode() == Lock) {
1031 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1032 return _x_constrained;
1035 return !_x_constrained;
1039 RegionMotionDrag::copy_regions (GdkEvent* event)
1041 /* duplicate the regionview(s) and region(s) */
1043 list<RegionView*> new_regionviews;
1045 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1047 RegionView* rv = (*i);
1048 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1049 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1051 const boost::shared_ptr<const Region> original = rv->region();
1052 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1056 boost::shared_ptr<AudioRegion> audioregion_copy
1057 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1058 nrv = new AudioRegionView (*arv, audioregion_copy);
1060 boost::shared_ptr<MidiRegion> midiregion_copy
1061 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1062 nrv = new MidiRegionView (*mrv, midiregion_copy);
1067 nrv->get_canvas_group()->show ();
1068 new_regionviews.push_back (nrv);
1070 /* swap _primary to the copy */
1072 if (rv == _primary) {
1076 /* ..and deselect the one we copied */
1078 rv->set_selected (false);
1081 if (new_regionviews.empty()) {
1085 /* reflect the fact that we are dragging the copies */
1087 _views = new_regionviews;
1089 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1092 sync the canvas to what we think is its current state
1093 without it, the canvas seems to
1094 "forget" to update properly after the upcoming reparent()
1095 ..only if the mouse is in rapid motion at the time of the grab.
1096 something to do with regionview creation taking so long?
1098 _editor->update_canvas_now();
1102 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1104 /* Which trackview is this ? */
1106 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1107 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1108 (*layer) = tvp.second;
1110 if (*tv && (*tv)->layer_display() == Overlaid) {
1114 /* The region motion is only processed if the pointer is over
1118 if (!(*tv) || !(*tv)->is_track()) {
1119 /* To make sure we hide the verbose canvas cursor when the mouse is
1120 not held over and audiotrack.
1122 _editor->hide_verbose_canvas_cursor ();
1129 /** @param new_order New track order.
1130 * @param old_order Old track order.
1131 * @param visible_y_low Lowest visible order.
1132 * @return true if y movement should not happen, otherwise false.
1135 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1137 if (new_order != old_order) {
1139 /* this isn't the pointer track */
1143 /* moving up the canvas */
1144 if ( (new_order - y_span) >= tavs.visible_y_low) {
1148 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1149 int32_t visible_tracks = 0;
1150 while (visible_tracks < y_span ) {
1152 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1153 /* passing through a hidden track */
1158 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1159 /* moving to a non-track; disallow */
1165 /* moving beyond the lowest visible track; disallow */
1169 } else if (y_span < 0) {
1171 /* moving down the canvas */
1172 if ((new_order - y_span) <= tavs.visible_y_high) {
1174 int32_t visible_tracks = 0;
1176 while (visible_tracks > y_span ) {
1179 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1180 /* passing through a hidden track */
1185 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1186 /* moving to a non-track; disallow */
1193 /* moving beyond the highest visible track; disallow */
1200 /* this is the pointer's track */
1202 if ((new_order - y_span) > tavs.visible_y_high) {
1203 /* we will overflow */
1205 } else if ((new_order - y_span) < tavs.visible_y_low) {
1206 /* we will overflow */
1215 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1216 : RegionMotionDrag (e, i, p, v, b),
1219 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1221 _dest_trackview = tv;
1222 if (tv->layer_display() == Overlaid) {
1225 _dest_layer = _primary->region()->layer ();
1229 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1230 if (rtv && rtv->is_track()) {
1231 speed = rtv->get_diskstream()->speed ();
1234 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1238 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1240 RegionMotionDrag::start_grab (event, c);
1242 _pointer_frame_offset = grab_frame() - _last_frame_position;
1245 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1246 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
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 (_primary);
1257 _last_frame_position = pos;
1259 _item = _primary->get_canvas_group ();
1260 _dest_trackview = v;
1261 _dest_layer = _primary->region()->layer ();
1264 map<RegionView*, pair<RouteTimeAxisView*, int> >
1265 RegionMotionDrag::find_time_axis_views_and_layers ()
1267 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1269 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1271 double ix1, ix2, iy1, iy2;
1272 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1273 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1274 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1276 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1277 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1285 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1287 _editor->update_canvas_now ();
1289 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1291 RouteTimeAxisView* dest_rtv = final[_primary].first;
1293 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1294 _primary->get_canvas_group()->property_y() = 0;
1296 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1298 _editor->begin_reversible_command (_("insert region"));
1299 XMLNode& before = playlist->get_state ();
1300 playlist->add_region (_primary->region (), _last_frame_position);
1301 _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1302 _editor->commit_reversible_command ();
1310 RegionInsertDrag::aborted ()
1315 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1316 : RegionMoveDrag (e, i, p, v, false, false)
1321 struct RegionSelectionByPosition {
1322 bool operator() (RegionView*a, RegionView* b) {
1323 return a->region()->position () < b->region()->position();
1328 RegionSpliceDrag::motion (GdkEvent* event, bool)
1330 RouteTimeAxisView* tv;
1333 if (!check_possible (&tv, &layer)) {
1339 if ((current_pointer_x() - last_pointer_x()) > 0) {
1345 RegionSelection copy (_editor->selection->regions);
1347 RegionSelectionByPosition cmp;
1350 nframes64_t const pf = adjusted_current_frame (event);
1352 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1354 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1360 boost::shared_ptr<Playlist> playlist;
1362 if ((playlist = atv->playlist()) == 0) {
1366 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1371 if (pf < (*i)->region()->last_frame() + 1) {
1375 if (pf > (*i)->region()->first_frame()) {
1381 playlist->shuffle ((*i)->region(), dir);
1386 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1392 RegionSpliceDrag::aborted ()
1397 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1405 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1407 _dest_trackview = _view;
1409 Drag::start_grab (event);
1414 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1417 // TODO: create region-create-drag region view here
1420 // TODO: resize region-create-drag region view here
1424 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1426 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1432 if (!movement_occurred) {
1433 mtv->add_region (grab_frame ());
1435 motion (event, false);
1436 // TODO: create region-create-drag region here
1441 RegionCreateDrag::aborted ()
1446 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1454 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1457 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1459 Drag::start_grab (event);
1461 region = &cnote->region_view();
1463 double region_start = region->get_position_pixels();
1464 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1466 if (grab_x() <= middle_point) {
1467 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1470 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1474 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1476 if (event->motion.state & Keyboard::PrimaryModifier) {
1482 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1484 if (ms.size() > 1) {
1485 /* has to be relative, may make no sense otherwise */
1489 region->note_selected (cnote, true);
1491 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1492 MidiRegionSelection::iterator next;
1495 (*r)->begin_resizing (at_front);
1501 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1503 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1504 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1505 (*r)->update_resizing (at_front, current_pointer_x() - grab_x(), relative);
1510 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1512 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1513 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1514 (*r)->commit_resizing (at_front, current_pointer_x() - grab_x(), relative);
1519 NoteResizeDrag::aborted ()
1525 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1531 RegionGainDrag::finished (GdkEvent *, bool)
1537 RegionGainDrag::aborted ()
1542 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1543 : RegionDrag (e, i, p, v)
1549 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1552 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1553 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1555 if (tv && tv->is_track()) {
1556 speed = tv->get_diskstream()->speed();
1559 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1560 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1561 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1563 Drag::start_grab (event, _editor->trimmer_cursor);
1565 nframes64_t const pf = adjusted_current_frame (event);
1567 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1568 _operation = ContentsTrim;
1570 /* These will get overridden for a point trim.*/
1571 if (pf < (region_start + region_length/2)) {
1572 /* closer to start */
1573 _operation = StartTrim;
1574 } else if (pf > (region_end - region_length/2)) {
1576 _operation = EndTrim;
1580 switch (_operation) {
1582 _editor->show_verbose_time_cursor (region_start, 10);
1585 _editor->show_verbose_time_cursor (region_end, 10);
1588 _editor->show_verbose_time_cursor (pf, 10);
1594 TrimDrag::motion (GdkEvent* event, bool first_move)
1596 RegionView* rv = _primary;
1597 nframes64_t frame_delta = 0;
1599 bool left_direction;
1600 bool obey_snap = event ? !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) : false;
1602 /* snap modifier works differently here..
1603 its current state has to be passed to the
1604 various trim functions in order to work properly
1608 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1609 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1610 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1612 if (tv && tv->is_track()) {
1613 speed = tv->get_diskstream()->speed();
1616 nframes64_t const pf = adjusted_current_frame (event);
1618 if (last_pointer_frame() > pf) {
1619 left_direction = true;
1621 left_direction = false;
1628 switch (_operation) {
1630 trim_type = "Region start trim";
1633 trim_type = "Region end trim";
1636 trim_type = "Region content trim";
1640 _editor->begin_reversible_command (trim_type);
1641 _have_transaction = true;
1643 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1644 (*i)->fake_set_opaque(false);
1645 (*i)->region()->freeze ();
1647 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1650 arv->temporarily_hide_envelope ();
1653 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1654 insert_result = _editor->motion_frozen_playlists.insert (pl);
1656 if (insert_result.second) {
1657 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1663 if (pf == last_pointer_frame()) {
1667 /* XXX i hope to god that we can really conclude this ... */
1668 _have_transaction = true;
1670 if (left_direction) {
1671 frame_delta = (last_pointer_frame() - pf);
1673 frame_delta = (pf - last_pointer_frame());
1676 bool non_overlap_trim = false;
1678 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1679 non_overlap_trim = true;
1682 switch (_operation) {
1684 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1688 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1689 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1695 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1699 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1700 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1707 bool swap_direction = false;
1709 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1710 swap_direction = true;
1713 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1714 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1720 switch (_operation) {
1722 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1725 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1728 _editor->show_verbose_time_cursor (pf, 10);
1735 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1737 if (movement_occurred) {
1738 motion (event, false);
1740 if (!_editor->selection->selected (_primary)) {
1741 _editor->thaw_region_after_trim (*_primary);
1744 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1745 _editor->thaw_region_after_trim (**i);
1746 (*i)->fake_set_opaque (true);
1749 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1751 if (_have_transaction) {
1752 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1756 _editor->motion_frozen_playlists.clear ();
1758 if (_have_transaction) {
1759 _editor->commit_reversible_command();
1763 /* no mouse movement */
1764 _editor->point_trim (event);
1769 TrimDrag::aborted ()
1771 /* Our motion method is changing model state, so use the Undo system
1772 to cancel. Perhaps not ideal, as this will leave an Undo point
1773 behind which may be slightly odd from the user's point of view.
1778 if (_have_transaction) {
1783 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1787 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1792 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1795 // create a dummy marker for visual representation of moving the copy.
1796 // The actual copying is not done before we reach the finish callback.
1798 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1799 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1800 *new MeterSection (_marker->meter()));
1802 _item = &new_marker->the_item ();
1803 _marker = new_marker;
1807 MetricSection& section (_marker->meter());
1809 if (!section.movable()) {
1815 Drag::start_grab (event, cursor);
1817 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1819 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1823 MeterMarkerDrag::motion (GdkEvent* event, bool)
1825 nframes64_t const pf = adjusted_current_frame (event);
1827 if (pf == last_pointer_frame()) {
1831 _marker->set_position (pf);
1833 _editor->show_verbose_time_cursor (pf, 10);
1837 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1839 if (!movement_occurred) {
1843 motion (event, false);
1847 TempoMap& map (_editor->session()->tempo_map());
1848 map.bbt_time (last_pointer_frame(), when);
1850 if (_copy == true) {
1851 _editor->begin_reversible_command (_("copy meter mark"));
1852 XMLNode &before = map.get_state();
1853 map.add_meter (_marker->meter(), when);
1854 XMLNode &after = map.get_state();
1855 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1856 _editor->commit_reversible_command ();
1858 // delete the dummy marker we used for visual representation of copying.
1859 // a new visual marker will show up automatically.
1862 _editor->begin_reversible_command (_("move meter mark"));
1863 XMLNode &before = map.get_state();
1864 map.move_meter (_marker->meter(), when);
1865 XMLNode &after = map.get_state();
1866 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1867 _editor->commit_reversible_command ();
1872 MeterMarkerDrag::aborted ()
1874 _marker->set_position (_marker->meter().frame ());
1877 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1881 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1886 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1891 // create a dummy marker for visual representation of moving the copy.
1892 // The actual copying is not done before we reach the finish callback.
1894 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1895 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1896 *new TempoSection (_marker->tempo()));
1898 _item = &new_marker->the_item ();
1899 _marker = new_marker;
1903 MetricSection& section (_marker->tempo());
1905 if (!section.movable()) {
1910 Drag::start_grab (event, cursor);
1912 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
1913 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1917 TempoMarkerDrag::motion (GdkEvent* event, bool)
1919 nframes64_t const pf = adjusted_current_frame (event);
1921 if (pf == last_pointer_frame()) {
1925 /* OK, we've moved far enough to make it worth actually move the thing. */
1927 _marker->set_position (pf);
1929 _editor->show_verbose_time_cursor (pf, 10);
1933 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1935 if (!movement_occurred) {
1939 motion (event, false);
1943 TempoMap& map (_editor->session()->tempo_map());
1944 map.bbt_time (last_pointer_frame(), when);
1946 if (_copy == true) {
1947 _editor->begin_reversible_command (_("copy tempo mark"));
1948 XMLNode &before = map.get_state();
1949 map.add_tempo (_marker->tempo(), when);
1950 XMLNode &after = map.get_state();
1951 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1952 _editor->commit_reversible_command ();
1954 // delete the dummy marker we used for visual representation of copying.
1955 // a new visual marker will show up automatically.
1958 _editor->begin_reversible_command (_("move tempo mark"));
1959 XMLNode &before = map.get_state();
1960 map.move_tempo (_marker->tempo(), when);
1961 XMLNode &after = map.get_state();
1962 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1963 _editor->commit_reversible_command ();
1968 TempoMarkerDrag::aborted ()
1970 _marker->set_position (_marker->tempo().frame());
1973 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1977 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1982 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1984 Drag::start_grab (event, c);
1988 nframes64_t where = _editor->event_frame (event, 0, 0);
1990 _editor->snap_to_with_modifier (where, event);
1991 _editor->playhead_cursor->set_position (where);
1995 if (_cursor == _editor->playhead_cursor) {
1996 _editor->_dragging_playhead = true;
1998 if (_editor->session() && _was_rolling && _stop) {
1999 _editor->session()->request_stop ();
2002 if (_editor->session() && _editor->session()->is_auditioning()) {
2003 _editor->session()->cancel_audition ();
2007 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2009 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2013 CursorDrag::motion (GdkEvent* event, bool)
2015 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2017 if (adjusted_frame == last_pointer_frame()) {
2021 _cursor->set_position (adjusted_frame);
2023 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2026 _editor->update_canvas_now ();
2028 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2032 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2034 _editor->_dragging_playhead = false;
2036 if (!movement_occurred && _stop) {
2040 motion (event, false);
2042 if (_item == &_editor->playhead_cursor->canvas_item) {
2043 if (_editor->session()) {
2044 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2045 _editor->_pending_locate_request = true;
2051 CursorDrag::aborted ()
2053 _editor->_dragging_playhead = false;
2054 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2057 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2058 : RegionDrag (e, i, p, v)
2064 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2066 Drag::start_grab (event, cursor);
2068 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2069 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2071 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2072 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2076 FadeInDrag::motion (GdkEvent* event, bool)
2078 nframes64_t fade_length;
2080 nframes64_t const pos = adjusted_current_frame (event);
2082 boost::shared_ptr<Region> region = _primary->region ();
2084 if (pos < (region->position() + 64)) {
2085 fade_length = 64; // this should be a minimum defined somewhere
2086 } else if (pos > region->last_frame()) {
2087 fade_length = region->length();
2089 fade_length = pos - region->position();
2092 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2094 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2100 tmp->reset_fade_in_shape_width (fade_length);
2103 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2107 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2109 if (!movement_occurred) {
2113 nframes64_t fade_length;
2115 nframes64_t const pos = adjusted_current_frame (event);
2117 boost::shared_ptr<Region> region = _primary->region ();
2119 if (pos < (region->position() + 64)) {
2120 fade_length = 64; // this should be a minimum defined somewhere
2121 } else if (pos > region->last_frame()) {
2122 fade_length = region->length();
2124 fade_length = pos - region->position();
2127 _editor->begin_reversible_command (_("change fade in length"));
2129 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2131 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2137 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2138 XMLNode &before = alist->get_state();
2140 tmp->audio_region()->set_fade_in_length (fade_length);
2141 tmp->audio_region()->set_fade_in_active (true);
2143 XMLNode &after = alist->get_state();
2144 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2147 _editor->commit_reversible_command ();
2151 FadeInDrag::aborted ()
2153 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2154 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2160 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2164 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2165 : RegionDrag (e, i, p, v)
2171 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2173 Drag::start_grab (event, cursor);
2175 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2176 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2178 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2179 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2183 FadeOutDrag::motion (GdkEvent* event, bool)
2185 nframes64_t fade_length;
2187 nframes64_t const pos = adjusted_current_frame (event);
2189 boost::shared_ptr<Region> region = _primary->region ();
2191 if (pos > (region->last_frame() - 64)) {
2192 fade_length = 64; // this should really be a minimum fade defined somewhere
2194 else if (pos < region->position()) {
2195 fade_length = region->length();
2198 fade_length = region->last_frame() - pos;
2201 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2203 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2209 tmp->reset_fade_out_shape_width (fade_length);
2212 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2216 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2218 if (!movement_occurred) {
2222 nframes64_t fade_length;
2224 nframes64_t const pos = adjusted_current_frame (event);
2226 boost::shared_ptr<Region> region = _primary->region ();
2228 if (pos > (region->last_frame() - 64)) {
2229 fade_length = 64; // this should really be a minimum fade defined somewhere
2231 else if (pos < region->position()) {
2232 fade_length = region->length();
2235 fade_length = region->last_frame() - pos;
2238 _editor->begin_reversible_command (_("change fade out length"));
2240 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2242 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2248 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2249 XMLNode &before = alist->get_state();
2251 tmp->audio_region()->set_fade_out_length (fade_length);
2252 tmp->audio_region()->set_fade_out_active (true);
2254 XMLNode &after = alist->get_state();
2255 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2258 _editor->commit_reversible_command ();
2262 FadeOutDrag::aborted ()
2264 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2265 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2271 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2275 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2278 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2281 _points.push_back (Gnome::Art::Point (0, 0));
2282 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2284 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2285 _line->property_width_pixels() = 1;
2286 _line->property_points () = _points;
2289 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2292 MarkerDrag::~MarkerDrag ()
2294 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2300 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2302 Drag::start_grab (event, cursor);
2306 Location *location = _editor->find_location_from_marker (_marker, is_start);
2307 _editor->_dragging_edit_point = true;
2309 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2311 update_item (location);
2313 // _drag_line->show();
2314 // _line->raise_to_top();
2317 _editor->show_verbose_time_cursor (location->start(), 10);
2319 _editor->show_verbose_time_cursor (location->end(), 10);
2322 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2325 case Selection::Toggle:
2326 _editor->selection->toggle (_marker);
2328 case Selection::Set:
2329 if (!_editor->selection->selected (_marker)) {
2330 _editor->selection->set (_marker);
2333 case Selection::Extend:
2335 Locations::LocationList ll;
2336 list<Marker*> to_add;
2338 _editor->selection->markers.range (s, e);
2339 s = min (_marker->position(), s);
2340 e = max (_marker->position(), e);
2343 if (e < max_frames) {
2346 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2347 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2348 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2351 to_add.push_back (lm->start);
2354 to_add.push_back (lm->end);
2358 if (!to_add.empty()) {
2359 _editor->selection->add (to_add);
2363 case Selection::Add:
2364 _editor->selection->add (_marker);
2368 /* set up copies for us to manipulate during the drag */
2370 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2371 Location *l = _editor->find_location_from_marker (*i, is_start);
2372 _copied_locations.push_back (new Location (*l));
2377 MarkerDrag::motion (GdkEvent* event, bool)
2379 nframes64_t f_delta = 0;
2381 bool move_both = false;
2383 Location *real_location;
2384 Location *copy_location = 0;
2386 nframes64_t const newframe = adjusted_current_frame (event);
2388 nframes64_t next = newframe;
2390 if (newframe == last_pointer_frame()) {
2394 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2398 MarkerSelection::iterator i;
2399 list<Location*>::iterator x;
2401 /* find the marker we're dragging, and compute the delta */
2403 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2404 x != _copied_locations.end() && i != _editor->selection->markers.end();
2410 if (marker == _marker) {
2412 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2417 if (real_location->is_mark()) {
2418 f_delta = newframe - copy_location->start();
2422 switch (marker->type()) {
2424 case Marker::LoopStart:
2425 case Marker::PunchIn:
2426 f_delta = newframe - copy_location->start();
2430 case Marker::LoopEnd:
2431 case Marker::PunchOut:
2432 f_delta = newframe - copy_location->end();
2435 /* what kind of marker is this ? */
2443 if (i == _editor->selection->markers.end()) {
2444 /* hmm, impossible - we didn't find the dragged marker */
2448 /* now move them all */
2450 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2451 x != _copied_locations.end() && i != _editor->selection->markers.end();
2457 /* call this to find out if its the start or end */
2459 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2463 if (real_location->locked()) {
2467 if (copy_location->is_mark()) {
2471 copy_location->set_start (copy_location->start() + f_delta);
2475 nframes64_t new_start = copy_location->start() + f_delta;
2476 nframes64_t new_end = copy_location->end() + f_delta;
2478 if (is_start) { // start-of-range marker
2481 copy_location->set_start (new_start);
2482 copy_location->set_end (new_end);
2483 } else if (new_start < copy_location->end()) {
2484 copy_location->set_start (new_start);
2486 _editor->snap_to (next, 1, true);
2487 copy_location->set_end (next);
2488 copy_location->set_start (newframe);
2491 } else { // end marker
2494 copy_location->set_end (new_end);
2495 copy_location->set_start (new_start);
2496 } else if (new_end > copy_location->start()) {
2497 copy_location->set_end (new_end);
2498 } else if (newframe > 0) {
2499 _editor->snap_to (next, -1, true);
2500 copy_location->set_start (next);
2501 copy_location->set_end (newframe);
2506 update_item (copy_location);
2508 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2511 lm->set_position (copy_location->start(), copy_location->end());
2515 assert (!_copied_locations.empty());
2517 _editor->show_verbose_time_cursor (newframe, 10);
2520 _editor->update_canvas_now ();
2525 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2527 if (!movement_occurred) {
2529 /* just a click, do nothing but finish
2530 off the selection process
2533 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2536 case Selection::Set:
2537 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2538 _editor->selection->set (_marker);
2542 case Selection::Toggle:
2543 case Selection::Extend:
2544 case Selection::Add:
2551 _editor->_dragging_edit_point = false;
2553 _editor->begin_reversible_command ( _("move marker") );
2554 XMLNode &before = _editor->session()->locations()->get_state();
2556 MarkerSelection::iterator i;
2557 list<Location*>::iterator x;
2560 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2561 x != _copied_locations.end() && i != _editor->selection->markers.end();
2564 Location * location = _editor->find_location_from_marker (*i, is_start);
2568 if (location->locked()) {
2572 if (location->is_mark()) {
2573 location->set_start ((*x)->start());
2575 location->set ((*x)->start(), (*x)->end());
2580 XMLNode &after = _editor->session()->locations()->get_state();
2581 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2582 _editor->commit_reversible_command ();
2588 MarkerDrag::aborted ()
2594 MarkerDrag::update_item (Location* location)
2596 double const x1 = _editor->frame_to_pixel (location->start());
2598 _points.front().set_x(x1);
2599 _points.back().set_x(x1);
2600 _line->property_points() = _points;
2603 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2605 _cumulative_x_drag (0),
2606 _cumulative_y_drag (0)
2608 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2614 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2616 Drag::start_grab (event, _editor->fader_cursor);
2618 // start the grab at the center of the control point so
2619 // the point doesn't 'jump' to the mouse after the first drag
2620 _time_axis_view_grab_x = _point->get_x();
2621 _time_axis_view_grab_y = _point->get_y();
2622 nframes64_t grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x);
2624 float const fraction = 1 - (_point->get_y() / _point->line().height());
2626 _point->line().start_drag_single (_point, grab_frame, fraction);
2628 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2629 event->button.x + 10, event->button.y + 10);
2631 _editor->show_verbose_canvas_cursor ();
2635 ControlPointDrag::motion (GdkEvent* event, bool)
2637 double dx = current_pointer_x() - last_pointer_x();
2638 double dy = current_pointer_y() - last_pointer_y();
2640 if (event->button.state & Keyboard::SecondaryModifier) {
2645 /* coordinate in TimeAxisView's space */
2646 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2647 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2649 // calculate zero crossing point. back off by .01 to stay on the
2650 // positive side of zero
2651 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2653 // make sure we hit zero when passing through
2654 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2658 if (_x_constrained) {
2659 cx = _time_axis_view_grab_x;
2661 if (_y_constrained) {
2662 cy = _time_axis_view_grab_y;
2665 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2666 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2670 cy = min ((double) _point->line().height(), cy);
2672 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2674 if (!_x_constrained) {
2675 _editor->snap_to_with_modifier (cx_frames, event);
2678 float const fraction = 1.0 - (cy / _point->line().height());
2680 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2682 _point->line().drag_motion (cx_frames, fraction, push);
2684 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2688 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2690 if (!movement_occurred) {
2694 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2695 _editor->reset_point_selection ();
2699 motion (event, false);
2701 _point->line().end_drag ();
2705 ControlPointDrag::aborted ()
2707 _point->line().reset ();
2711 ControlPointDrag::active (Editing::MouseMode m)
2713 if (m == Editing::MouseGain) {
2714 /* always active in mouse gain */
2718 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2719 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2722 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2725 _cumulative_y_drag (0)
2730 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2732 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2735 _item = &_line->grab_item ();
2737 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2738 origin, and ditto for y.
2741 double cx = event->button.x;
2742 double cy = event->button.y;
2744 _line->parent_group().w2i (cx, cy);
2746 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2751 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2752 /* no adjacent points */
2756 Drag::start_grab (event, _editor->fader_cursor);
2758 /* store grab start in parent frame */
2760 _time_axis_view_grab_x = cx;
2761 _time_axis_view_grab_y = cy;
2763 double fraction = 1.0 - (cy / _line->height());
2765 _line->start_drag_line (before, after, fraction);
2767 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2768 event->button.x + 10, event->button.y + 10);
2770 _editor->show_verbose_canvas_cursor ();
2774 LineDrag::motion (GdkEvent* event, bool)
2776 double dy = current_pointer_y() - last_pointer_y();
2778 if (event->button.state & Keyboard::SecondaryModifier) {
2782 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2784 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2787 cy = min ((double) _line->height(), cy);
2789 double const fraction = 1.0 - (cy / _line->height());
2793 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2799 /* we are ignoring x position for this drag, so we can just pass in 0 */
2800 _line->drag_motion (0, fraction, push);
2802 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2806 LineDrag::finished (GdkEvent* event, bool)
2808 motion (event, false);
2813 LineDrag::aborted ()
2819 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2821 Drag::start_grab (event);
2822 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2826 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2833 /* use a bigger drag threshold than the default */
2835 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2837 if (abs ((int) (pf - grab_frame())) < 8) {
2841 nframes64_t grab = grab_frame ();
2842 if (Config->get_rubberbanding_snaps_to_grid ()) {
2843 _editor->snap_to_with_modifier (grab, event);
2846 /* base start and end on initial click position */
2856 if (current_pointer_y() < grab_y()) {
2857 y1 = current_pointer_y();
2860 y2 = current_pointer_y();
2865 if (start != end || y1 != y2) {
2867 double x1 = _editor->frame_to_pixel (start);
2868 double x2 = _editor->frame_to_pixel (end);
2870 _editor->rubberband_rect->property_x1() = x1;
2871 _editor->rubberband_rect->property_y1() = y1;
2872 _editor->rubberband_rect->property_x2() = x2;
2873 _editor->rubberband_rect->property_y2() = y2;
2875 _editor->rubberband_rect->show();
2876 _editor->rubberband_rect->raise_to_top();
2878 _editor->show_verbose_time_cursor (pf, 10);
2883 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2885 if (movement_occurred) {
2887 motion (event, false);
2890 if (current_pointer_y() < grab_y()) {
2891 y1 = current_pointer_y();
2894 y2 = current_pointer_y();
2899 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2902 _editor->begin_reversible_command (_("rubberband selection"));
2904 if (grab_frame() < last_pointer_frame()) {
2905 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
2907 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
2911 _editor->commit_reversible_command ();
2915 if (!getenv("ARDOUR_SAE")) {
2916 _editor->selection->clear_tracks();
2918 _editor->selection->clear_regions();
2919 _editor->selection->clear_points ();
2920 _editor->selection->clear_lines ();
2923 _editor->rubberband_rect->hide();
2927 RubberbandSelectDrag::aborted ()
2929 _editor->rubberband_rect->hide ();
2933 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2935 Drag::start_grab (event);
2937 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2941 TimeFXDrag::motion (GdkEvent* event, bool)
2943 RegionView* rv = _primary;
2945 nframes64_t const pf = adjusted_current_frame (event);
2947 if (pf == last_pointer_frame()) {
2951 if (pf > rv->region()->position()) {
2952 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2955 _editor->show_verbose_time_cursor (pf, 10);
2959 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2961 _primary->get_time_axis_view().hide_timestretch ();
2963 if (!movement_occurred) {
2967 if (last_pointer_frame() < _primary->region()->position()) {
2968 /* backwards drag of the left edge - not usable */
2972 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2974 float percentage = (double) newlen / (double) _primary->region()->length();
2976 #ifndef USE_RUBBERBAND
2977 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2978 if (_primary->region()->data_type() == DataType::AUDIO) {
2979 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2983 _editor->begin_reversible_command (_("timestretch"));
2985 // XXX how do timeFX on multiple regions ?
2990 if (!_editor->time_stretch (rs, percentage) == 0) {
2991 error << _("An error occurred while executing time stretch operation") << endmsg;
2996 TimeFXDrag::aborted ()
2998 _primary->get_time_axis_view().hide_timestretch ();
3003 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3005 Drag::start_grab (event);
3009 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3015 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3017 if (movement_occurred && _editor->session()) {
3018 /* make sure we stop */
3019 _editor->session()->request_transport_speed (0.0);
3024 ScrubDrag::aborted ()
3029 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3033 , _original_pointer_time_axis (-1)
3034 , _last_pointer_time_axis (-1)
3040 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3042 nframes64_t start = 0;
3043 nframes64_t end = 0;
3045 if (_editor->session() == 0) {
3049 Gdk::Cursor* cursor = 0;
3051 switch (_operation) {
3052 case CreateSelection:
3053 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3058 cursor = _editor->selector_cursor;
3059 Drag::start_grab (event, cursor);
3062 case SelectionStartTrim:
3063 if (_editor->clicked_axisview) {
3064 _editor->clicked_axisview->order_selection_trims (_item, true);
3066 Drag::start_grab (event, _editor->trimmer_cursor);
3067 start = _editor->selection->time[_editor->clicked_selection].start;
3068 _pointer_frame_offset = grab_frame() - start;
3071 case SelectionEndTrim:
3072 if (_editor->clicked_axisview) {
3073 _editor->clicked_axisview->order_selection_trims (_item, false);
3075 Drag::start_grab (event, _editor->trimmer_cursor);
3076 end = _editor->selection->time[_editor->clicked_selection].end;
3077 _pointer_frame_offset = grab_frame() - end;
3081 start = _editor->selection->time[_editor->clicked_selection].start;
3082 Drag::start_grab (event, cursor);
3083 _pointer_frame_offset = grab_frame() - start;
3087 if (_operation == SelectionMove) {
3088 _editor->show_verbose_time_cursor (start, 10);
3090 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3093 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
3097 SelectionDrag::motion (GdkEvent* event, bool first_move)
3099 nframes64_t start = 0;
3100 nframes64_t end = 0;
3103 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (current_pointer_y ());
3104 if (pending_time_axis.first == 0) {
3108 nframes64_t const pending_position = adjusted_current_frame (event);
3110 /* only alter selection if things have changed */
3112 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3116 switch (_operation) {
3117 case CreateSelection:
3119 nframes64_t grab = grab_frame ();
3122 _editor->snap_to (grab);
3125 if (pending_position < grab_frame()) {
3126 start = pending_position;
3129 end = pending_position;
3133 /* first drag: Either add to the selection
3134 or create a new selection
3139 _editor->begin_reversible_command (_("range selection"));
3140 _have_transaction = true;
3143 /* adding to the selection */
3144 _editor->selection->add (_editor->clicked_axisview);
3145 _editor->clicked_selection = _editor->selection->add (start, end);
3150 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3151 _editor->selection->set (_editor->clicked_axisview);
3154 _editor->clicked_selection = _editor->selection->set (start, end);
3158 /* select the track that we're in */
3159 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3160 _editor->selection->add (pending_time_axis.first);
3161 _added_time_axes.push_back (pending_time_axis.first);
3164 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3165 tracks that we selected in the first place.
3168 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3169 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3171 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3172 while (i != _added_time_axes.end()) {
3174 list<TimeAxisView*>::iterator tmp = i;
3177 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3178 _editor->selection->remove (*i);
3179 _added_time_axes.remove (*i);
3188 case SelectionStartTrim:
3191 _editor->begin_reversible_command (_("trim selection start"));
3192 _have_transaction = true;
3195 start = _editor->selection->time[_editor->clicked_selection].start;
3196 end = _editor->selection->time[_editor->clicked_selection].end;
3198 if (pending_position > end) {
3201 start = pending_position;
3205 case SelectionEndTrim:
3208 _editor->begin_reversible_command (_("trim selection end"));
3209 _have_transaction = true;
3212 start = _editor->selection->time[_editor->clicked_selection].start;
3213 end = _editor->selection->time[_editor->clicked_selection].end;
3215 if (pending_position < start) {
3218 end = pending_position;
3226 _editor->begin_reversible_command (_("move selection"));
3227 _have_transaction = true;
3230 start = _editor->selection->time[_editor->clicked_selection].start;
3231 end = _editor->selection->time[_editor->clicked_selection].end;
3233 length = end - start;
3235 start = pending_position;
3236 _editor->snap_to (start);
3238 end = start + length;
3243 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3244 _editor->start_canvas_autoscroll (1, 0);
3248 _editor->selection->replace (_editor->clicked_selection, start, end);
3251 if (_operation == SelectionMove) {
3252 _editor->show_verbose_time_cursor(start, 10);
3254 _editor->show_verbose_time_cursor(pending_position, 10);
3259 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3261 Session* s = _editor->session();
3263 if (movement_occurred) {
3264 motion (event, false);
3265 /* XXX this is not object-oriented programming at all. ick */
3266 if (_editor->selection->time.consolidate()) {
3267 _editor->selection->TimeChanged ();
3270 if (_have_transaction) {
3271 _editor->commit_reversible_command ();
3274 /* XXX what if its a music time selection? */
3275 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3276 s->request_play_range (&_editor->selection->time, true);
3281 /* just a click, no pointer movement.*/
3283 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3284 _editor->selection->clear_time();
3287 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3288 _editor->selection->set (_editor->clicked_axisview);
3291 if (s && s->get_play_range () && s->transport_rolling()) {
3292 s->request_stop (false, false);
3297 _editor->stop_canvas_autoscroll ();
3301 SelectionDrag::aborted ()
3306 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3311 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3312 _drag_rect->hide ();
3314 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3315 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3319 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3321 if (_editor->session() == 0) {
3325 Gdk::Cursor* cursor = 0;
3327 if (!_editor->temp_location) {
3328 _editor->temp_location = new Location;
3331 switch (_operation) {
3332 case CreateRangeMarker:
3333 case CreateTransportMarker:
3334 case CreateCDMarker:
3336 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3341 cursor = _editor->selector_cursor;
3345 Drag::start_grab (event, cursor);
3347 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3351 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3353 nframes64_t start = 0;
3354 nframes64_t end = 0;
3355 ArdourCanvas::SimpleRect *crect;
3357 switch (_operation) {
3358 case CreateRangeMarker:
3359 crect = _editor->range_bar_drag_rect;
3361 case CreateTransportMarker:
3362 crect = _editor->transport_bar_drag_rect;
3364 case CreateCDMarker:
3365 crect = _editor->cd_marker_bar_drag_rect;
3368 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3373 nframes64_t const pf = adjusted_current_frame (event);
3375 /* only alter selection if the current frame is
3376 different from the last frame position.
3379 if (pf == last_pointer_frame()) {
3383 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3384 nframes64_t grab = grab_frame ();
3385 _editor->snap_to (grab);
3387 if (pf < grab_frame()) {
3395 /* first drag: Either add to the selection
3396 or create a new selection.
3401 _editor->temp_location->set (start, end);
3405 update_item (_editor->temp_location);
3407 //_drag_rect->raise_to_top();
3412 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3413 _editor->start_canvas_autoscroll (1, 0);
3417 _editor->temp_location->set (start, end);
3419 double x1 = _editor->frame_to_pixel (start);
3420 double x2 = _editor->frame_to_pixel (end);
3421 crect->property_x1() = x1;
3422 crect->property_x2() = x2;
3424 update_item (_editor->temp_location);
3427 _editor->show_verbose_time_cursor (pf, 10);
3432 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3434 Location * newloc = 0;
3438 if (movement_occurred) {
3439 motion (event, false);
3442 switch (_operation) {
3443 case CreateRangeMarker:
3444 case CreateCDMarker:
3446 _editor->begin_reversible_command (_("new range marker"));
3447 XMLNode &before = _editor->session()->locations()->get_state();
3448 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3449 if (_operation == CreateCDMarker) {
3450 flags = Location::IsRangeMarker | Location::IsCDMarker;
3451 _editor->cd_marker_bar_drag_rect->hide();
3454 flags = Location::IsRangeMarker;
3455 _editor->range_bar_drag_rect->hide();
3457 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3458 _editor->session()->locations()->add (newloc, true);
3459 XMLNode &after = _editor->session()->locations()->get_state();
3460 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3461 _editor->commit_reversible_command ();
3465 case CreateTransportMarker:
3466 // popup menu to pick loop or punch
3467 _editor->new_transport_marker_context_menu (&event->button, _item);
3471 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3473 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3478 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3480 if (end == max_frames) {
3481 end = _editor->session()->current_end_frame ();
3484 if (start == max_frames) {
3485 start = _editor->session()->current_start_frame ();
3488 switch (_editor->mouse_mode) {
3490 /* find the two markers on either side and then make the selection from it */
3491 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3495 /* find the two markers on either side of the click and make the range out of it */
3496 _editor->selection->set (start, end);
3505 _editor->stop_canvas_autoscroll ();
3509 RangeMarkerBarDrag::aborted ()
3515 RangeMarkerBarDrag::update_item (Location* location)
3517 double const x1 = _editor->frame_to_pixel (location->start());
3518 double const x2 = _editor->frame_to_pixel (location->end());
3520 _drag_rect->property_x1() = x1;
3521 _drag_rect->property_x2() = x2;
3525 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3527 Drag::start_grab (event, _editor->zoom_cursor);
3528 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3532 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3537 nframes64_t const pf = adjusted_current_frame (event);
3539 if (pf == last_pointer_frame()) {
3543 nframes64_t grab = grab_frame ();
3544 _editor->snap_to_with_modifier (grab, event);
3546 /* base start and end on initial click position */
3558 _editor->zoom_rect->show();
3559 _editor->zoom_rect->raise_to_top();
3562 _editor->reposition_zoom_rect(start, end);
3564 _editor->show_verbose_time_cursor (pf, 10);
3569 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3571 if (movement_occurred) {
3572 motion (event, false);
3574 if (grab_frame() < last_pointer_frame()) {
3575 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3577 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3580 _editor->temporal_zoom_to_frame (false, grab_frame());
3582 temporal_zoom_step (false);
3583 center_screen (grab_frame());
3587 _editor->zoom_rect->hide();
3591 MouseZoomDrag::aborted ()
3593 _editor->zoom_rect->hide ();
3596 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3599 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3600 region = &cnote->region_view();
3604 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3606 Drag::start_grab (event);
3609 drag_delta_note = 0;
3614 event_x = current_pointer_x();
3615 event_y = current_pointer_y();
3617 _item->property_parent().get_value()->w2i(event_x, event_y);
3619 last_x = region->snap_to_pixel(event_x);
3622 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3624 if (!(was_selected = cnote->selected())) {
3626 /* tertiary-click means extend selection - we'll do that on button release,
3627 so don't add it here, because otherwise we make it hard to figure
3628 out the "extend-to" range.
3631 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3634 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3637 region->note_selected (cnote, true);
3639 region->unique_select (cnote);
3646 NoteDrag::motion (GdkEvent*, bool)
3648 MidiStreamView* streamview = region->midi_stream_view();
3652 event_x = current_pointer_x();
3653 event_y = current_pointer_y();
3655 _item->property_parent().get_value()->w2i(event_x, event_y);
3657 event_x = region->snap_to_pixel(event_x);
3659 double dx = event_x - last_x;
3660 double dy = event_y - last_y;
3665 // Snap to note rows
3667 if (abs (dy) < streamview->note_height()) {
3670 int8_t this_delta_note;
3672 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3674 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3676 drag_delta_note -= this_delta_note;
3677 dy = streamview->note_height() * this_delta_note;
3678 last_y = last_y + dy;
3682 region->move_selection (dx, dy);
3684 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3686 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3687 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3688 _editor->show_verbose_canvas_cursor_with (buf);
3693 NoteDrag::finished (GdkEvent* ev, bool moved)
3695 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3698 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3701 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3703 region->note_deselected (cnote);
3706 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3707 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3709 if (!extend && !add && region->selection_size() > 1) {
3710 region->unique_select(cnote);
3711 } else if (extend) {
3712 region->note_selected (cnote, true, true);
3714 /* it was added during button press */
3719 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3724 NoteDrag::aborted ()
3729 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3732 , _nothing_to_drag (false)
3734 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3737 _line = _atav->line ();
3741 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3743 Drag::start_grab (event, cursor);
3745 list<ControlPoint*> points;
3747 XMLNode* state = &_line->get_state ();
3749 if (_ranges.empty()) {
3751 uint32_t const N = _line->npoints ();
3752 for (uint32_t i = 0; i < N; ++i) {
3753 points.push_back (_line->nth (i));
3758 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3759 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3761 /* fade into and out of the region that we're dragging;
3762 64 samples length plucked out of thin air.
3764 nframes64_t const h = (j->start + j->end) / 2;
3765 nframes64_t a = j->start + 64;
3769 nframes64_t b = j->end - 64;
3774 the_list->add (j->start, the_list->eval (j->start));
3775 _line->add_always_in_view (j->start);
3776 the_list->add (a, the_list->eval (a));
3777 _line->add_always_in_view (a);
3778 the_list->add (b, the_list->eval (b));
3779 _line->add_always_in_view (b);
3780 the_list->add (j->end, the_list->eval (j->end));
3781 _line->add_always_in_view (j->end);
3784 uint32_t const N = _line->npoints ();
3785 for (uint32_t i = 0; i < N; ++i) {
3787 ControlPoint* p = _line->nth (i);
3789 list<AudioRange>::const_iterator j = _ranges.begin ();
3790 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3794 if (j != _ranges.end()) {
3795 points.push_back (p);
3800 if (points.empty()) {
3801 _nothing_to_drag = true;
3805 _line->start_drag_multiple (points, 1 - (current_pointer_y() / _line->height ()), state);
3809 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3811 if (_nothing_to_drag) {
3815 float const f = 1 - (current_pointer_y() / _line->height());
3817 /* we are ignoring x position for this drag, so we can just pass in 0 */
3818 _line->drag_motion (0, f, false);
3822 AutomationRangeDrag::finished (GdkEvent* event, bool)
3824 if (_nothing_to_drag) {
3828 motion (event, false);
3830 _line->clear_always_in_view ();
3834 AutomationRangeDrag::aborted ()
3836 _line->clear_always_in_view ();