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)
61 , _move_threshold_passed (false)
63 , _last_pointer_frame (0)
64 , _current_pointer_frame (0)
70 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
76 cursor = _editor->which_grabber_cursor ();
79 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
83 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
86 cursor = _editor->which_grabber_cursor ();
89 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
91 if (Keyboard::is_button2_event (&event->button)) {
92 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
93 _y_constrained = true;
94 _x_constrained = false;
96 _y_constrained = false;
97 _x_constrained = true;
100 _x_constrained = false;
101 _y_constrained = false;
104 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
105 _grab_frame = adjusted_frame (_grab_frame, event);
106 _last_pointer_frame = _grab_frame;
107 _current_pointer_frame = _grab_frame;
108 _current_pointer_x = _grab_x;
109 _current_pointer_y = _grab_y;
110 _last_pointer_x = _current_pointer_x;
111 _last_pointer_y = _current_pointer_y;
115 _item->i2w (_original_x, _original_y);
117 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
121 if (_editor->session() && _editor->session()->transport_rolling()) {
124 _was_rolling = false;
127 switch (_editor->snap_type()) {
128 case SnapToRegionStart:
129 case SnapToRegionEnd:
130 case SnapToRegionSync:
131 case SnapToRegionBoundary:
132 _editor->build_region_boundary_cache ();
139 /** @param event GDK event, or 0.
140 * @return true if some movement occurred, otherwise false.
143 Drag::end_grab (GdkEvent* event)
147 _editor->stop_canvas_autoscroll ();
149 _item->ungrab (event ? event->button.time : 0);
151 _last_pointer_x = _current_pointer_x;
152 _last_pointer_y = _current_pointer_y;
153 finished (event, _move_threshold_passed);
155 _editor->hide_verbose_canvas_cursor();
159 return _move_threshold_passed;
163 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
167 if (f > _pointer_frame_offset) {
168 pos = f - _pointer_frame_offset;
172 _editor->snap_to_with_modifier (pos, event);
179 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
181 return adjusted_frame (_current_pointer_frame, event, snap);
185 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
187 _last_pointer_x = _current_pointer_x;
188 _last_pointer_y = _current_pointer_y;
189 _last_pointer_frame = adjusted_current_frame (event);
190 _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
192 /* check to see if we have moved in any way that matters since the last motion event */
193 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
194 (!y_movement_matters() || _last_pointer_y == _current_pointer_y) ) {
198 pair<nframes64_t, int> const threshold = move_threshold ();
200 bool const old_move_threshold_passed = _move_threshold_passed;
202 if (!from_autoscroll && !_move_threshold_passed) {
204 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
205 bool const yp = (::fabs ((_current_pointer_y - _grab_y)) >= threshold.second);
207 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
210 if (active (_editor->mouse_mode) && _move_threshold_passed) {
212 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
213 if (!from_autoscroll) {
214 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
217 motion (event, _move_threshold_passed != old_move_threshold_passed);
229 _editor->stop_canvas_autoscroll ();
230 _editor->hide_verbose_canvas_cursor ();
235 /* put it back where it came from */
240 _item->i2w (cxw, cyw);
241 _item->move (_original_x - cxw, _original_y - cyw);
245 pair<nframes64_t, nframes64_t>
246 Drag::extent () const
248 nframes64_t const f = adjusted_current_frame (0);
249 return make_pair (f, f);
252 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
257 RegionView::RegionViewGoingAway.connect (death_connection, ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
261 RegionDrag::region_going_away (RegionView* v)
266 pair<nframes64_t, nframes64_t>
267 RegionDrag::extent () const
269 nframes64_t const f = adjusted_current_frame (0);
270 return make_pair (f, f + _primary->region()->length ());
274 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
275 : RegionDrag (e, i, p, v),
285 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
287 Drag::start_grab (event);
289 _editor->show_verbose_time_cursor (_last_frame_position, 10);
292 RegionMotionDrag::TimeAxisViewSummary
293 RegionMotionDrag::get_time_axis_view_summary ()
295 int32_t children = 0;
296 TimeAxisViewSummary sum;
298 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
300 /* get a bitmask representing the visible tracks */
302 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
303 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
304 TimeAxisView::Children children_list;
306 /* zeroes are audio/MIDI tracks. ones are other types. */
308 if (!rtv->hidden()) {
310 if (!rtv->is_track()) {
311 /* not an audio nor MIDI track */
312 sum.tracks = sum.tracks |= (0x01 << rtv->order());
315 sum.height_list[rtv->order()] = (*i)->current_height();
318 if ((children_list = rtv->get_child_list()).size() > 0) {
319 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
320 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
321 sum.height_list[rtv->order() + children] = (*j)->current_height();
332 RegionMotionDrag::compute_y_delta (
333 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
334 int32_t last_pointer_layer, int32_t current_pointer_layer,
335 TimeAxisViewSummary const & tavs,
336 int32_t* pointer_order_span, int32_t* pointer_layer_span,
337 int32_t* canvas_pointer_order_span
341 *pointer_order_span = 0;
342 *pointer_layer_span = 0;
346 bool clamp_y_axis = false;
348 /* the change in track order between this callback and the last */
349 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
350 /* the change in layer between this callback and the last;
351 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
352 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
354 if (*pointer_order_span != 0) {
356 /* find the actual pointer span, in terms of the number of visible tracks;
357 to do this, we reduce |pointer_order_span| by the number of hidden tracks
360 *canvas_pointer_order_span = *pointer_order_span;
361 if (last_pointer_view->order() >= current_pointer_view->order()) {
362 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
363 if (tavs.height_list[y] == 0) {
364 *canvas_pointer_order_span--;
368 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
369 if (tavs.height_list[y] == 0) {
370 *canvas_pointer_order_span++;
375 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
377 RegionView* rv = (*i);
379 if (rv->region()->locked()) {
383 double ix1, ix2, iy1, iy2;
384 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
385 rv->get_canvas_frame()->i2w (ix1, iy1);
386 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
388 /* get the new trackview for this particular region */
389 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
391 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
393 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
394 as surely this is a per-region thing... */
396 clamp_y_axis = y_movement_disallowed (
397 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
405 } else if (_dest_trackview == current_pointer_view) {
407 if (current_pointer_layer == last_pointer_layer) {
408 /* No movement; clamp */
414 _dest_trackview = current_pointer_view;
415 _dest_layer = current_pointer_layer;
423 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
425 /* compute the amount of pointer motion in frames, and where
426 the region would be if we moved it by that much.
428 *pending_region_position = adjusted_current_frame (event);
430 nframes64_t sync_frame;
431 nframes64_t sync_offset;
434 sync_offset = _primary->region()->sync_offset (sync_dir);
436 /* we don't handle a sync point that lies before zero.
438 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
440 sync_frame = *pending_region_position + (sync_dir*sync_offset);
442 _editor->snap_to_with_modifier (sync_frame, event);
444 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
447 *pending_region_position = _last_frame_position;
450 if (*pending_region_position > max_frames - _primary->region()->length()) {
451 *pending_region_position = _last_frame_position;
456 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
458 /* now compute the canvas unit distance we need to move the regionview
459 to make it appear at the new location.
462 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
464 if (*pending_region_position <= _last_frame_position) {
466 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
468 RegionView* rv = (*i);
470 // If any regionview is at zero, we need to know so we can stop further leftward motion.
472 double ix1, ix2, iy1, iy2;
473 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
474 rv->get_canvas_frame()->i2w (ix1, iy1);
476 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
478 *pending_region_position = _last_frame_position;
485 _last_frame_position = *pending_region_position;
492 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
496 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
498 vector<int32_t>::iterator j;
500 /* *pointer* variables reflect things about the pointer; as we may be moving
501 multiple regions, much detail must be computed per-region */
503 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
504 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
505 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
506 is always 0 regardless of what the region's "real" layer is */
507 RouteTimeAxisView* current_pointer_view;
508 layer_t current_pointer_layer;
509 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
513 /* TimeAxisView that we were pointing at last time we entered this method */
514 TimeAxisView const * const last_pointer_view = _dest_trackview;
515 /* the order of the track that we were pointing at last time we entered this method */
516 int32_t const last_pointer_order = last_pointer_view->order ();
517 /* the layer that we were pointing at last time we entered this method */
518 layer_t const last_pointer_layer = _dest_layer;
520 int32_t pointer_order_span;
521 int32_t pointer_layer_span;
522 int32_t canvas_pointer_order_span;
524 bool const clamp_y_axis = compute_y_delta (
525 last_pointer_view, current_pointer_view,
526 last_pointer_layer, current_pointer_layer, tavs,
527 &pointer_order_span, &pointer_layer_span,
528 &canvas_pointer_order_span
531 nframes64_t pending_region_position;
532 double const x_delta = compute_x_delta (event, &pending_region_position);
534 /*************************************************************
536 ************************************************************/
538 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
539 /* haven't reached next snap point, and we're not switching
540 trackviews nor layers. nothing to do.
545 /*************************************************************
547 ************************************************************/
549 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
551 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
553 RegionView* rv = (*i);
555 if (rv->region()->locked()) {
559 /* here we are calculating the y distance from the
560 top of the first track view to the top of the region
561 area of the track view that we're working on */
563 /* this x value is just a dummy value so that we have something
568 /* distance from the top of this track view to the region area
569 of our track view is always 1 */
573 /* convert to world coordinates, ie distance from the top of
576 rv->get_canvas_frame()->i2w (ix1, iy1);
578 /* compensate for the ruler section and the vertical scrollbar position */
579 iy1 += _editor->get_trackview_group_vertical_offset ();
583 // hide any dependent views
585 rv->get_time_axis_view().hide_dependent_views (*rv);
588 reparent to a non scrolling group so that we can keep the
589 region selection above all time axis views.
590 reparenting means we have to move the rv as the two
591 parent groups have different coordinates.
594 rv->get_canvas_group()->property_y() = iy1 - 1;
595 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
597 rv->fake_set_opaque (true);
600 /* current view for this particular region */
601 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
602 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
604 if (pointer_order_span != 0 && !clamp_y_axis) {
606 /* INTER-TRACK MOVEMENT */
608 /* move through the height list to the track that the region is currently on */
609 vector<int32_t>::iterator j = tavs.height_list.begin ();
611 while (j != tavs.height_list.end () && x != rtv->order ()) {
617 int32_t temp_pointer_order_span = canvas_pointer_order_span;
619 if (j != tavs.height_list.end ()) {
621 /* Account for layers in the original and
622 destination tracks. If we're moving around in layers we assume
623 that only one track is involved, so it's ok to use *pointer*
626 StreamView* lv = last_pointer_view->view ();
629 /* move to the top of the last trackview */
630 if (lv->layer_display () == Stacked) {
631 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
634 StreamView* cv = current_pointer_view->view ();
637 /* move to the right layer on the current trackview */
638 if (cv->layer_display () == Stacked) {
639 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
642 /* And for being on a non-topmost layer on the new
645 while (temp_pointer_order_span > 0) {
646 /* we're moving up canvas-wise,
647 so we need to find the next track height
649 if (j != tavs.height_list.begin()) {
653 if (x != last_pointer_order) {
655 ++temp_pointer_order_span;
660 temp_pointer_order_span--;
663 while (temp_pointer_order_span < 0) {
667 if (x != last_pointer_order) {
669 --temp_pointer_order_span;
673 if (j != tavs.height_list.end()) {
677 temp_pointer_order_span++;
681 /* find out where we'll be when we move and set height accordingly */
683 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
684 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
685 rv->set_height (temp_rtv->view()->child_height());
687 /* if you un-comment the following, the region colours will follow
688 the track colours whilst dragging; personally
689 i think this can confuse things, but never mind.
692 //const GdkColor& col (temp_rtv->view->get_region_color());
693 //rv->set_color (const_cast<GdkColor&>(col));
697 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
699 /* INTER-LAYER MOVEMENT in the same track */
700 y_delta = rtv->view()->child_height () * pointer_layer_span;
705 _editor->mouse_brush_insert_region (rv, pending_region_position);
707 rv->move (x_delta, y_delta);
710 } /* foreach region */
713 _editor->cursor_group->raise_to_top();
716 if (x_delta != 0 && !_brushing) {
717 _editor->show_verbose_time_cursor (_last_frame_position, 10);
722 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
724 if (_copy && first_move) {
725 copy_regions (event);
728 RegionMotionDrag::motion (event, first_move);
732 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
734 vector<RegionView*> copies;
735 boost::shared_ptr<Diskstream> ds;
736 boost::shared_ptr<Playlist> from_playlist;
737 RegionSelection new_views;
738 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
739 PlaylistSet modified_playlists;
740 PlaylistSet frozen_playlists;
741 list <sigc::connection> modified_playlist_connections;
742 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
743 nframes64_t drag_delta;
744 bool changed_tracks, changed_position;
745 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
746 RouteTimeAxisView* source_tv;
748 if (!movement_occurred) {
754 /* all changes were made during motion event handlers */
757 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
758 copies.push_back (*i);
765 /* reverse this here so that we have the correct logic to finalize
769 if (Config->get_edit_mode() == Lock) {
770 _x_constrained = !_x_constrained;
774 if (_x_constrained) {
775 _editor->begin_reversible_command (_("fixed time region copy"));
777 _editor->begin_reversible_command (_("region copy"));
780 if (_x_constrained) {
781 _editor->begin_reversible_command (_("fixed time region drag"));
783 _editor->begin_reversible_command (_("region drag"));
787 _have_transaction = true;
789 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
790 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
792 drag_delta = _primary->region()->position() - _last_frame_position;
794 _editor->update_canvas_now ();
796 /* make a list of where each region ended up */
797 final = find_time_axis_views_and_layers ();
799 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
801 RegionView* rv = (*i);
802 RouteTimeAxisView* dest_rtv = final[*i].first;
803 layer_t dest_layer = final[*i].second;
807 if (rv->region()->locked()) {
812 if (changed_position && !_x_constrained) {
813 where = rv->region()->position() - drag_delta;
815 where = rv->region()->position();
818 boost::shared_ptr<Region> new_region;
821 /* we already made a copy */
822 new_region = rv->region();
824 /* undo the previous hide_dependent_views so that xfades don't
825 disappear on copying regions
828 //rv->get_time_axis_view().reveal_dependent_views (*rv);
830 } else if (changed_tracks && dest_rtv->playlist()) {
831 new_region = RegionFactory::create (rv->region());
834 if (changed_tracks || _copy) {
836 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
843 _editor->latest_regionviews.clear ();
845 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
847 insert_result = modified_playlists.insert (to_playlist);
849 if (insert_result.second) {
850 _editor->session()->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
853 to_playlist->add_region (new_region, where);
854 if (dest_rtv->view()->layer_display() == Stacked) {
855 new_region->set_layer (dest_layer);
856 new_region->set_pending_explicit_relayer (true);
861 if (!_editor->latest_regionviews.empty()) {
862 // XXX why just the first one ? we only expect one
863 // commented out in nick_m's canvas reworking. is that intended?
864 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
865 new_views.push_back (_editor->latest_regionviews.front());
870 motion on the same track. plonk the previously reparented region
871 back to its original canvas group (its streamview).
872 No need to do anything for copies as they are fake regions which will be deleted.
875 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
876 rv->get_canvas_group()->property_y() = 0;
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) {
994 RegionMotionDrag::x_move_allowed () const
996 if (Config->get_edit_mode() == Lock) {
997 /* in locked edit mode, reverse the usual meaning of _x_constrained */
998 return _x_constrained;
1001 return !_x_constrained;
1005 RegionMotionDrag::copy_regions (GdkEvent* event)
1007 /* duplicate the regionview(s) and region(s) */
1009 list<RegionView*> new_regionviews;
1011 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1013 RegionView* rv = (*i);
1014 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1015 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1017 const boost::shared_ptr<const Region> original = rv->region();
1018 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1022 boost::shared_ptr<AudioRegion> audioregion_copy
1023 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1024 nrv = new AudioRegionView (*arv, audioregion_copy);
1026 boost::shared_ptr<MidiRegion> midiregion_copy
1027 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1028 nrv = new MidiRegionView (*mrv, midiregion_copy);
1033 nrv->get_canvas_group()->show ();
1034 new_regionviews.push_back (nrv);
1036 /* swap _primary to the copy */
1038 if (rv == _primary) {
1042 /* ..and deselect the one we copied */
1044 rv->set_selected (false);
1047 if (new_regionviews.empty()) {
1051 /* reflect the fact that we are dragging the copies */
1053 _views = new_regionviews;
1055 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1058 sync the canvas to what we think is its current state
1059 without it, the canvas seems to
1060 "forget" to update properly after the upcoming reparent()
1061 ..only if the mouse is in rapid motion at the time of the grab.
1062 something to do with regionview creation raking so long?
1064 _editor->update_canvas_now();
1068 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1070 /* Which trackview is this ? */
1072 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1073 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1074 (*layer) = tvp.second;
1076 if (*tv && (*tv)->layer_display() == Overlaid) {
1080 /* The region motion is only processed if the pointer is over
1084 if (!(*tv) || !(*tv)->is_track()) {
1085 /* To make sure we hide the verbose canvas cursor when the mouse is
1086 not held over and audiotrack.
1088 _editor->hide_verbose_canvas_cursor ();
1095 /** @param new_order New track order.
1096 * @param old_order Old track order.
1097 * @param visible_y_low Lowest visible order.
1098 * @return true if y movement should not happen, otherwise false.
1101 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1103 if (new_order != old_order) {
1105 /* this isn't the pointer track */
1109 /* moving up the canvas */
1110 if ( (new_order - y_span) >= tavs.visible_y_low) {
1114 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1115 int32_t visible_tracks = 0;
1116 while (visible_tracks < y_span ) {
1118 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1119 /* passing through a hidden track */
1124 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1125 /* moving to a non-track; disallow */
1131 /* moving beyond the lowest visible track; disallow */
1135 } else if (y_span < 0) {
1137 /* moving down the canvas */
1138 if ((new_order - y_span) <= tavs.visible_y_high) {
1140 int32_t visible_tracks = 0;
1142 while (visible_tracks > y_span ) {
1145 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1146 /* passing through a hidden track */
1151 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1152 /* moving to a non-track; disallow */
1159 /* moving beyond the highest visible track; disallow */
1166 /* this is the pointer's track */
1168 if ((new_order - y_span) > tavs.visible_y_high) {
1169 /* we will overflow */
1171 } else if ((new_order - y_span) < tavs.visible_y_low) {
1172 /* we will overflow */
1181 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1182 : RegionMotionDrag (e, i, p, v, b),
1185 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1187 _dest_trackview = tv;
1188 if (tv->layer_display() == Overlaid) {
1191 _dest_layer = _primary->region()->layer ();
1195 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1196 if (rtv && rtv->is_track()) {
1197 speed = rtv->get_diskstream()->speed ();
1200 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1204 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1206 RegionMotionDrag::start_grab (event, c);
1208 _pointer_frame_offset = grab_frame() - _last_frame_position;
1211 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1212 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1214 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1215 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1217 _primary = v->view()->create_region_view (r, false, false);
1219 _primary->get_canvas_group()->show ();
1220 _primary->set_position (pos, 0);
1221 _views.push_back (_primary);
1223 _last_frame_position = pos;
1225 _item = _primary->get_canvas_group ();
1226 _dest_trackview = v;
1227 _dest_layer = _primary->region()->layer ();
1230 map<RegionView*, pair<RouteTimeAxisView*, int> >
1231 RegionMotionDrag::find_time_axis_views_and_layers ()
1233 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1235 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1237 double ix1, ix2, iy1, iy2;
1238 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1239 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1240 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1242 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1243 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1251 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1253 _editor->update_canvas_now ();
1255 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1257 RouteTimeAxisView* dest_rtv = final[_primary].first;
1259 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1260 _primary->get_canvas_group()->property_y() = 0;
1262 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1264 _editor->begin_reversible_command (_("insert region"));
1265 XMLNode& before = playlist->get_state ();
1266 playlist->add_region (_primary->region (), _last_frame_position);
1267 _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1268 _editor->commit_reversible_command ();
1275 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1276 : RegionMoveDrag (e, i, p, v, false, false)
1281 struct RegionSelectionByPosition {
1282 bool operator() (RegionView*a, RegionView* b) {
1283 return a->region()->position () < b->region()->position();
1288 RegionSpliceDrag::motion (GdkEvent* event, bool)
1290 RouteTimeAxisView* tv;
1293 if (!check_possible (&tv, &layer)) {
1299 if ((current_pointer_x() - last_pointer_x()) > 0) {
1305 RegionSelection copy (_editor->selection->regions);
1307 RegionSelectionByPosition cmp;
1310 nframes64_t const pf = adjusted_current_frame (event);
1312 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1314 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1320 boost::shared_ptr<Playlist> playlist;
1322 if ((playlist = atv->playlist()) == 0) {
1326 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1331 if (pf < (*i)->region()->last_frame() + 1) {
1335 if (pf > (*i)->region()->first_frame()) {
1341 playlist->shuffle ((*i)->region(), dir);
1346 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1352 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1360 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1362 _dest_trackview = _view;
1364 Drag::start_grab (event);
1369 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1372 // TODO: create region-create-drag region view here
1375 // TODO: resize region-create-drag region view here
1379 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1381 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1387 if (!movement_occurred) {
1388 mtv->add_region (grab_frame ());
1390 motion (event, false);
1391 // TODO: create region-create-drag region here
1395 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1403 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1406 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1408 Drag::start_grab (event);
1410 region = &cnote->region_view();
1412 double region_start = region->get_position_pixels();
1413 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1415 if (grab_x() <= middle_point) {
1416 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1419 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1423 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1425 if (event->motion.state & Keyboard::PrimaryModifier) {
1431 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1433 if (ms.size() > 1) {
1434 /* has to be relative, may make no sense otherwise */
1438 region->note_selected (cnote, true);
1440 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1441 MidiRegionSelection::iterator next;
1444 (*r)->begin_resizing (at_front);
1450 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1452 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1453 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1454 (*r)->update_resizing (at_front, current_pointer_x() - grab_x(), relative);
1459 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1461 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1462 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1463 (*r)->commit_resizing (at_front, current_pointer_x() - grab_x(), relative);
1468 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1474 RegionGainDrag::finished (GdkEvent *, bool)
1479 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1480 : RegionDrag (e, i, p, v)
1486 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1489 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1490 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1492 if (tv && tv->is_track()) {
1493 speed = tv->get_diskstream()->speed();
1496 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1497 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1498 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1500 Drag::start_grab (event, _editor->trimmer_cursor);
1502 nframes64_t const pf = adjusted_current_frame (event);
1504 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1505 _operation = ContentsTrim;
1507 /* These will get overridden for a point trim.*/
1508 if (pf < (region_start + region_length/2)) {
1509 /* closer to start */
1510 _operation = StartTrim;
1511 } else if (pf > (region_end - region_length/2)) {
1513 _operation = EndTrim;
1517 switch (_operation) {
1519 _editor->show_verbose_time_cursor (region_start, 10);
1522 _editor->show_verbose_time_cursor (region_end, 10);
1525 _editor->show_verbose_time_cursor (pf, 10);
1531 TrimDrag::motion (GdkEvent* event, bool first_move)
1533 RegionView* rv = _primary;
1534 nframes64_t frame_delta = 0;
1536 bool left_direction;
1537 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1539 /* snap modifier works differently here..
1540 its current state has to be passed to the
1541 various trim functions in order to work properly
1545 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1546 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1547 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1549 if (tv && tv->is_track()) {
1550 speed = tv->get_diskstream()->speed();
1553 nframes64_t const pf = adjusted_current_frame (event);
1555 if (last_pointer_frame() > pf) {
1556 left_direction = true;
1558 left_direction = false;
1565 switch (_operation) {
1567 trim_type = "Region start trim";
1570 trim_type = "Region end trim";
1573 trim_type = "Region content trim";
1577 _editor->begin_reversible_command (trim_type);
1578 _have_transaction = true;
1580 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1581 (*i)->fake_set_opaque(false);
1582 (*i)->region()->freeze ();
1584 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1587 arv->temporarily_hide_envelope ();
1590 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1591 insert_result = _editor->motion_frozen_playlists.insert (pl);
1593 if (insert_result.second) {
1594 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1600 if (pf == last_pointer_frame()) {
1604 /* XXX i hope to god that we can really conclude this ... */
1605 _have_transaction = true;
1607 if (left_direction) {
1608 frame_delta = (last_pointer_frame() - pf);
1610 frame_delta = (pf - last_pointer_frame());
1613 bool non_overlap_trim = false;
1615 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1616 non_overlap_trim = true;
1619 switch (_operation) {
1621 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1625 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1626 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1632 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1636 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1637 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1644 bool swap_direction = false;
1646 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1647 swap_direction = true;
1650 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1651 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1657 switch (_operation) {
1659 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1662 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1665 _editor->show_verbose_time_cursor (pf, 10);
1672 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1674 if (movement_occurred) {
1675 motion (event, false);
1677 if (!_editor->selection->selected (_primary)) {
1678 _editor->thaw_region_after_trim (*_primary);
1681 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1682 _editor->thaw_region_after_trim (**i);
1683 (*i)->fake_set_opaque (true);
1686 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1688 if (_have_transaction) {
1689 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1693 _editor->motion_frozen_playlists.clear ();
1695 if (_have_transaction) {
1696 _editor->commit_reversible_command();
1700 /* no mouse movement */
1701 _editor->point_trim (event);
1705 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1709 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1714 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1717 // create a dummy marker for visual representation of moving the copy.
1718 // The actual copying is not done before we reach the finish callback.
1720 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1721 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1722 *new MeterSection (_marker->meter()));
1724 _item = &new_marker->the_item ();
1725 _marker = new_marker;
1729 MetricSection& section (_marker->meter());
1731 if (!section.movable()) {
1737 Drag::start_grab (event, cursor);
1739 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1741 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1745 MeterMarkerDrag::motion (GdkEvent* event, bool)
1747 nframes64_t const pf = adjusted_current_frame (event);
1749 if (pf == last_pointer_frame()) {
1753 _marker->set_position (pf);
1755 _editor->show_verbose_time_cursor (pf, 10);
1759 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1761 if (!movement_occurred) {
1765 motion (event, false);
1769 TempoMap& map (_editor->session()->tempo_map());
1770 map.bbt_time (last_pointer_frame(), when);
1772 if (_copy == true) {
1773 _editor->begin_reversible_command (_("copy meter mark"));
1774 XMLNode &before = map.get_state();
1775 map.add_meter (_marker->meter(), when);
1776 XMLNode &after = map.get_state();
1777 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1778 _editor->commit_reversible_command ();
1780 // delete the dummy marker we used for visual representation of copying.
1781 // a new visual marker will show up automatically.
1784 _editor->begin_reversible_command (_("move meter mark"));
1785 XMLNode &before = map.get_state();
1786 map.move_meter (_marker->meter(), when);
1787 XMLNode &after = map.get_state();
1788 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1789 _editor->commit_reversible_command ();
1793 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1797 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1802 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1807 // create a dummy marker for visual representation of moving the copy.
1808 // The actual copying is not done before we reach the finish callback.
1810 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1811 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1812 *new TempoSection (_marker->tempo()));
1814 _item = &new_marker->the_item ();
1815 _marker = new_marker;
1819 MetricSection& section (_marker->tempo());
1821 if (!section.movable()) {
1826 Drag::start_grab (event, cursor);
1828 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
1829 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1833 TempoMarkerDrag::motion (GdkEvent* event, bool)
1835 nframes64_t const pf = adjusted_current_frame (event);
1837 if (pf == last_pointer_frame()) {
1841 /* OK, we've moved far enough to make it worth actually move the thing. */
1843 _marker->set_position (pf);
1845 _editor->show_verbose_time_cursor (pf, 10);
1849 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1851 if (!movement_occurred) {
1855 motion (event, false);
1859 TempoMap& map (_editor->session()->tempo_map());
1860 map.bbt_time (last_pointer_frame(), when);
1862 if (_copy == true) {
1863 _editor->begin_reversible_command (_("copy tempo mark"));
1864 XMLNode &before = map.get_state();
1865 map.add_tempo (_marker->tempo(), when);
1866 XMLNode &after = map.get_state();
1867 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1868 _editor->commit_reversible_command ();
1870 // delete the dummy marker we used for visual representation of copying.
1871 // a new visual marker will show up automatically.
1874 _editor->begin_reversible_command (_("move tempo mark"));
1875 XMLNode &before = map.get_state();
1876 map.move_tempo (_marker->tempo(), when);
1877 XMLNode &after = map.get_state();
1878 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1879 _editor->commit_reversible_command ();
1884 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1888 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1893 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1895 Drag::start_grab (event, c);
1899 nframes64_t where = _editor->event_frame (event, 0, 0);
1901 _editor->snap_to_with_modifier (where, event);
1902 _editor->playhead_cursor->set_position (where);
1906 if (_cursor == _editor->playhead_cursor) {
1907 _editor->_dragging_playhead = true;
1909 if (_editor->session() && _was_rolling && _stop) {
1910 _editor->session()->request_stop ();
1913 if (_editor->session() && _editor->session()->is_auditioning()) {
1914 _editor->session()->cancel_audition ();
1918 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1922 CursorDrag::motion (GdkEvent* event, bool)
1924 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1926 if (adjusted_frame == last_pointer_frame()) {
1930 _cursor->set_position (adjusted_frame);
1932 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1935 _editor->update_canvas_now ();
1937 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1941 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1943 _editor->_dragging_playhead = false;
1945 if (!movement_occurred && _stop) {
1949 motion (event, false);
1951 if (_item == &_editor->playhead_cursor->canvas_item) {
1952 if (_editor->session()) {
1953 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1954 _editor->_pending_locate_request = true;
1959 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1960 : RegionDrag (e, i, p, v)
1966 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1968 Drag::start_grab (event, cursor);
1970 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1971 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1973 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
1974 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1978 FadeInDrag::motion (GdkEvent* event, bool)
1980 nframes64_t fade_length;
1982 nframes64_t const pos = adjusted_current_frame (event);
1984 boost::shared_ptr<Region> region = _primary->region ();
1986 if (pos < (region->position() + 64)) {
1987 fade_length = 64; // this should be a minimum defined somewhere
1988 } else if (pos > region->last_frame()) {
1989 fade_length = region->length();
1991 fade_length = pos - region->position();
1994 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1996 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2002 tmp->reset_fade_in_shape_width (fade_length);
2005 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2009 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2011 if (!movement_occurred) {
2015 nframes64_t fade_length;
2017 nframes64_t const pos = adjusted_current_frame (event);
2019 boost::shared_ptr<Region> region = _primary->region ();
2021 if (pos < (region->position() + 64)) {
2022 fade_length = 64; // this should be a minimum defined somewhere
2023 } else if (pos > region->last_frame()) {
2024 fade_length = region->length();
2026 fade_length = pos - region->position();
2029 _editor->begin_reversible_command (_("change fade in length"));
2031 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2033 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2039 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2040 XMLNode &before = alist->get_state();
2042 tmp->audio_region()->set_fade_in_length (fade_length);
2043 tmp->audio_region()->set_fade_in_active (true);
2045 XMLNode &after = alist->get_state();
2046 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2049 _editor->commit_reversible_command ();
2052 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2053 : RegionDrag (e, i, p, v)
2059 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2061 Drag::start_grab (event, cursor);
2063 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2064 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2066 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2067 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2071 FadeOutDrag::motion (GdkEvent* event, bool)
2073 nframes64_t fade_length;
2075 nframes64_t const pos = adjusted_current_frame (event);
2077 boost::shared_ptr<Region> region = _primary->region ();
2079 if (pos > (region->last_frame() - 64)) {
2080 fade_length = 64; // this should really be a minimum fade defined somewhere
2082 else if (pos < region->position()) {
2083 fade_length = region->length();
2086 fade_length = region->last_frame() - pos;
2089 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2091 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2097 tmp->reset_fade_out_shape_width (fade_length);
2100 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2104 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2106 if (!movement_occurred) {
2110 nframes64_t fade_length;
2112 nframes64_t const pos = adjusted_current_frame (event);
2114 boost::shared_ptr<Region> region = _primary->region ();
2116 if (pos > (region->last_frame() - 64)) {
2117 fade_length = 64; // this should really be a minimum fade defined somewhere
2119 else if (pos < region->position()) {
2120 fade_length = region->length();
2123 fade_length = region->last_frame() - pos;
2126 _editor->begin_reversible_command (_("change fade out length"));
2128 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2130 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2136 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2137 XMLNode &before = alist->get_state();
2139 tmp->audio_region()->set_fade_out_length (fade_length);
2140 tmp->audio_region()->set_fade_out_active (true);
2142 XMLNode &after = alist->get_state();
2143 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2146 _editor->commit_reversible_command ();
2149 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2152 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2155 _points.push_back (Gnome::Art::Point (0, 0));
2156 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2158 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2159 _line->property_width_pixels() = 1;
2160 _line->property_points () = _points;
2163 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2166 MarkerDrag::~MarkerDrag ()
2168 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2174 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2176 Drag::start_grab (event, cursor);
2180 Location *location = _editor->find_location_from_marker (_marker, is_start);
2181 _editor->_dragging_edit_point = true;
2183 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2185 update_item (location);
2187 // _drag_line->show();
2188 // _line->raise_to_top();
2191 _editor->show_verbose_time_cursor (location->start(), 10);
2193 _editor->show_verbose_time_cursor (location->end(), 10);
2196 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2199 case Selection::Toggle:
2200 _editor->selection->toggle (_marker);
2202 case Selection::Set:
2203 if (!_editor->selection->selected (_marker)) {
2204 _editor->selection->set (_marker);
2207 case Selection::Extend:
2209 Locations::LocationList ll;
2210 list<Marker*> to_add;
2212 _editor->selection->markers.range (s, e);
2213 s = min (_marker->position(), s);
2214 e = max (_marker->position(), e);
2217 if (e < max_frames) {
2220 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2221 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2222 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2225 to_add.push_back (lm->start);
2228 to_add.push_back (lm->end);
2232 if (!to_add.empty()) {
2233 _editor->selection->add (to_add);
2237 case Selection::Add:
2238 _editor->selection->add (_marker);
2242 /* set up copies for us to manipulate during the drag */
2244 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2245 Location *l = _editor->find_location_from_marker (*i, is_start);
2246 _copied_locations.push_back (new Location (*l));
2251 MarkerDrag::motion (GdkEvent* event, bool)
2253 nframes64_t f_delta = 0;
2255 bool move_both = false;
2257 Location *real_location;
2258 Location *copy_location = 0;
2260 nframes64_t const newframe = adjusted_current_frame (event);
2262 nframes64_t next = newframe;
2264 if (newframe == last_pointer_frame()) {
2268 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2272 MarkerSelection::iterator i;
2273 list<Location*>::iterator x;
2275 /* find the marker we're dragging, and compute the delta */
2277 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2278 x != _copied_locations.end() && i != _editor->selection->markers.end();
2284 if (marker == _marker) {
2286 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2291 if (real_location->is_mark()) {
2292 f_delta = newframe - copy_location->start();
2296 switch (marker->type()) {
2298 case Marker::LoopStart:
2299 case Marker::PunchIn:
2300 f_delta = newframe - copy_location->start();
2304 case Marker::LoopEnd:
2305 case Marker::PunchOut:
2306 f_delta = newframe - copy_location->end();
2309 /* what kind of marker is this ? */
2317 if (i == _editor->selection->markers.end()) {
2318 /* hmm, impossible - we didn't find the dragged marker */
2322 /* now move them all */
2324 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2325 x != _copied_locations.end() && i != _editor->selection->markers.end();
2331 /* call this to find out if its the start or end */
2333 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2337 if (real_location->locked()) {
2341 if (copy_location->is_mark()) {
2345 copy_location->set_start (copy_location->start() + f_delta);
2349 nframes64_t new_start = copy_location->start() + f_delta;
2350 nframes64_t new_end = copy_location->end() + f_delta;
2352 if (is_start) { // start-of-range marker
2355 copy_location->set_start (new_start);
2356 copy_location->set_end (new_end);
2357 } else if (new_start < copy_location->end()) {
2358 copy_location->set_start (new_start);
2360 _editor->snap_to (next, 1, true);
2361 copy_location->set_end (next);
2362 copy_location->set_start (newframe);
2365 } else { // end marker
2368 copy_location->set_end (new_end);
2369 copy_location->set_start (new_start);
2370 } else if (new_end > copy_location->start()) {
2371 copy_location->set_end (new_end);
2372 } else if (newframe > 0) {
2373 _editor->snap_to (next, -1, true);
2374 copy_location->set_start (next);
2375 copy_location->set_end (newframe);
2380 update_item (copy_location);
2382 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2385 lm->set_position (copy_location->start(), copy_location->end());
2389 assert (!_copied_locations.empty());
2391 _editor->show_verbose_time_cursor (newframe, 10);
2394 _editor->update_canvas_now ();
2399 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2401 if (!movement_occurred) {
2403 /* just a click, do nothing but finish
2404 off the selection process
2407 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2410 case Selection::Set:
2411 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2412 _editor->selection->set (_marker);
2416 case Selection::Toggle:
2417 case Selection::Extend:
2418 case Selection::Add:
2425 _editor->_dragging_edit_point = false;
2427 _editor->begin_reversible_command ( _("move marker") );
2428 XMLNode &before = _editor->session()->locations()->get_state();
2430 MarkerSelection::iterator i;
2431 list<Location*>::iterator x;
2434 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2435 x != _copied_locations.end() && i != _editor->selection->markers.end();
2438 Location * location = _editor->find_location_from_marker (*i, is_start);
2442 if (location->locked()) {
2446 if (location->is_mark()) {
2447 location->set_start ((*x)->start());
2449 location->set ((*x)->start(), (*x)->end());
2454 XMLNode &after = _editor->session()->locations()->get_state();
2455 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2456 _editor->commit_reversible_command ();
2462 MarkerDrag::update_item (Location* location)
2464 double const x1 = _editor->frame_to_pixel (location->start());
2466 _points.front().set_x(x1);
2467 _points.back().set_x(x1);
2468 _line->property_points() = _points;
2471 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2473 _cumulative_x_drag (0),
2474 _cumulative_y_drag (0)
2476 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2482 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2484 Drag::start_grab (event, _editor->fader_cursor);
2486 // start the grab at the center of the control point so
2487 // the point doesn't 'jump' to the mouse after the first drag
2488 _time_axis_view_grab_x = _point->get_x();
2489 _time_axis_view_grab_y = _point->get_y();
2490 nframes64_t grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x);
2492 float const fraction = 1 - (_point->get_y() / _point->line().height());
2494 _point->line().start_drag_single (_point, grab_frame, fraction);
2496 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2497 event->button.x + 10, event->button.y + 10);
2499 _editor->show_verbose_canvas_cursor ();
2503 ControlPointDrag::motion (GdkEvent* event, bool)
2505 double dx = current_pointer_x() - last_pointer_x();
2506 double dy = current_pointer_y() - last_pointer_y();
2508 if (event->button.state & Keyboard::SecondaryModifier) {
2513 /* coordinate in TimeAxisView's space */
2514 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2515 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2517 // calculate zero crossing point. back off by .01 to stay on the
2518 // positive side of zero
2519 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2521 // make sure we hit zero when passing through
2522 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2526 if (_x_constrained) {
2527 cx = _time_axis_view_grab_x;
2529 if (_y_constrained) {
2530 cy = _time_axis_view_grab_y;
2533 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2534 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2538 cy = min ((double) _point->line().height(), cy);
2540 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2542 if (!_x_constrained) {
2543 _editor->snap_to_with_modifier (cx_frames, event);
2546 float const fraction = 1.0 - (cy / _point->line().height());
2548 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2550 _point->line().drag_motion (cx_frames, fraction, push);
2552 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2556 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2558 if (!movement_occurred) {
2562 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2563 _editor->reset_point_selection ();
2567 motion (event, false);
2569 _point->line().end_drag ();
2573 ControlPointDrag::active (Editing::MouseMode m)
2575 if (m == Editing::MouseGain) {
2576 /* always active in mouse gain */
2580 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2581 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2584 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2587 _cumulative_y_drag (0)
2592 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2594 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2597 _item = &_line->grab_item ();
2599 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2600 origin, and ditto for y.
2603 double cx = event->button.x;
2604 double cy = event->button.y;
2606 _line->parent_group().w2i (cx, cy);
2608 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2613 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2614 /* no adjacent points */
2618 Drag::start_grab (event, _editor->fader_cursor);
2620 /* store grab start in parent frame */
2622 _time_axis_view_grab_x = cx;
2623 _time_axis_view_grab_y = cy;
2625 double fraction = 1.0 - (cy / _line->height());
2627 _line->start_drag_line (before, after, fraction);
2629 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2630 event->button.x + 10, event->button.y + 10);
2632 _editor->show_verbose_canvas_cursor ();
2636 LineDrag::motion (GdkEvent* event, bool)
2638 double dy = current_pointer_y() - last_pointer_y();
2640 if (event->button.state & Keyboard::SecondaryModifier) {
2644 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2646 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2649 cy = min ((double) _line->height(), cy);
2651 double const fraction = 1.0 - (cy / _line->height());
2655 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2661 /* we are ignoring x position for this drag, so we can just pass in 0 */
2662 _line->drag_motion (0, fraction, push);
2664 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2668 LineDrag::finished (GdkEvent* event, bool)
2670 motion (event, false);
2675 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2677 Drag::start_grab (event);
2678 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2682 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2689 /* use a bigger drag threshold than the default */
2691 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2693 if (abs ((int) (pf - grab_frame())) < 8) {
2697 nframes64_t grab = grab_frame ();
2698 if (Config->get_rubberbanding_snaps_to_grid ()) {
2699 _editor->snap_to_with_modifier (grab, event);
2702 /* base start and end on initial click position */
2712 if (current_pointer_y() < grab_y()) {
2713 y1 = current_pointer_y();
2716 y2 = current_pointer_y();
2721 if (start != end || y1 != y2) {
2723 double x1 = _editor->frame_to_pixel (start);
2724 double x2 = _editor->frame_to_pixel (end);
2726 _editor->rubberband_rect->property_x1() = x1;
2727 _editor->rubberband_rect->property_y1() = y1;
2728 _editor->rubberband_rect->property_x2() = x2;
2729 _editor->rubberband_rect->property_y2() = y2;
2731 _editor->rubberband_rect->show();
2732 _editor->rubberband_rect->raise_to_top();
2734 _editor->show_verbose_time_cursor (pf, 10);
2739 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2741 if (movement_occurred) {
2743 motion (event, false);
2746 if (current_pointer_y() < grab_y()) {
2747 y1 = current_pointer_y();
2750 y2 = current_pointer_y();
2755 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2758 _editor->begin_reversible_command (_("rubberband selection"));
2760 if (grab_frame() < last_pointer_frame()) {
2761 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
2763 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
2767 _editor->commit_reversible_command ();
2771 if (!getenv("ARDOUR_SAE")) {
2772 _editor->selection->clear_tracks();
2774 _editor->selection->clear_regions();
2775 _editor->selection->clear_points ();
2776 _editor->selection->clear_lines ();
2779 _editor->rubberband_rect->hide();
2783 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2785 Drag::start_grab (event);
2787 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2791 TimeFXDrag::motion (GdkEvent* event, bool)
2793 RegionView* rv = _primary;
2795 nframes64_t const pf = adjusted_current_frame (event);
2797 if (pf == last_pointer_frame()) {
2801 if (pf > rv->region()->position()) {
2802 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2805 _editor->show_verbose_time_cursor (pf, 10);
2809 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2811 _primary->get_time_axis_view().hide_timestretch ();
2813 if (!movement_occurred) {
2817 if (last_pointer_frame() < _primary->region()->position()) {
2818 /* backwards drag of the left edge - not usable */
2822 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2824 float percentage = (double) newlen / (double) _primary->region()->length();
2826 #ifndef USE_RUBBERBAND
2827 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2828 if (_primary->region()->data_type() == DataType::AUDIO) {
2829 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2833 _editor->begin_reversible_command (_("timestretch"));
2835 // XXX how do timeFX on multiple regions ?
2840 if (!_editor->time_stretch (rs, percentage) == 0) {
2841 error << _("An error occurred while executing time stretch operation") << endmsg;
2846 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2848 Drag::start_grab (event);
2852 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2858 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2860 if (movement_occurred && _editor->session()) {
2861 /* make sure we stop */
2862 _editor->session()->request_transport_speed (0.0);
2866 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2870 , _original_pointer_time_axis (-1)
2871 , _last_pointer_time_axis (-1)
2877 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2879 nframes64_t start = 0;
2880 nframes64_t end = 0;
2882 if (_editor->session() == 0) {
2886 Gdk::Cursor* cursor = 0;
2888 switch (_operation) {
2889 case CreateSelection:
2890 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2895 cursor = _editor->selector_cursor;
2896 Drag::start_grab (event, cursor);
2899 case SelectionStartTrim:
2900 if (_editor->clicked_axisview) {
2901 _editor->clicked_axisview->order_selection_trims (_item, true);
2903 Drag::start_grab (event, _editor->trimmer_cursor);
2904 start = _editor->selection->time[_editor->clicked_selection].start;
2905 _pointer_frame_offset = grab_frame() - start;
2908 case SelectionEndTrim:
2909 if (_editor->clicked_axisview) {
2910 _editor->clicked_axisview->order_selection_trims (_item, false);
2912 Drag::start_grab (event, _editor->trimmer_cursor);
2913 end = _editor->selection->time[_editor->clicked_selection].end;
2914 _pointer_frame_offset = grab_frame() - end;
2918 start = _editor->selection->time[_editor->clicked_selection].start;
2919 Drag::start_grab (event, cursor);
2920 _pointer_frame_offset = grab_frame() - start;
2924 if (_operation == SelectionMove) {
2925 _editor->show_verbose_time_cursor (start, 10);
2927 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2930 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
2934 SelectionDrag::motion (GdkEvent* event, bool first_move)
2936 nframes64_t start = 0;
2937 nframes64_t end = 0;
2940 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (current_pointer_y ());
2941 if (pending_time_axis.first == 0) {
2945 nframes64_t const pending_position = adjusted_current_frame (event);
2947 /* only alter selection if things have changed */
2949 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
2953 switch (_operation) {
2954 case CreateSelection:
2956 nframes64_t grab = grab_frame ();
2959 _editor->snap_to (grab);
2962 if (pending_position < grab_frame()) {
2963 start = pending_position;
2966 end = pending_position;
2970 /* first drag: Either add to the selection
2971 or create a new selection
2976 _editor->begin_reversible_command (_("range selection"));
2977 _have_transaction = true;
2980 /* adding to the selection */
2981 _editor->selection->add (_editor->clicked_axisview);
2982 _editor->clicked_selection = _editor->selection->add (start, end);
2987 if (!_editor->selection->selected (_editor->clicked_axisview)) {
2988 _editor->selection->set (_editor->clicked_axisview);
2991 _editor->clicked_selection = _editor->selection->set (start, end);
2995 /* select the track that we're in */
2996 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
2997 _editor->selection->add (pending_time_axis.first);
2998 _added_time_axes.push_back (pending_time_axis.first);
3001 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3002 tracks that we selected in the first place.
3005 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3006 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3008 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3009 while (i != _added_time_axes.end()) {
3011 list<TimeAxisView*>::iterator tmp = i;
3014 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3015 _editor->selection->remove (*i);
3016 _added_time_axes.remove (*i);
3025 case SelectionStartTrim:
3028 _editor->begin_reversible_command (_("trim selection start"));
3029 _have_transaction = true;
3032 start = _editor->selection->time[_editor->clicked_selection].start;
3033 end = _editor->selection->time[_editor->clicked_selection].end;
3035 if (pending_position > end) {
3038 start = pending_position;
3042 case SelectionEndTrim:
3045 _editor->begin_reversible_command (_("trim selection end"));
3046 _have_transaction = true;
3049 start = _editor->selection->time[_editor->clicked_selection].start;
3050 end = _editor->selection->time[_editor->clicked_selection].end;
3052 if (pending_position < start) {
3055 end = pending_position;
3063 _editor->begin_reversible_command (_("move selection"));
3064 _have_transaction = true;
3067 start = _editor->selection->time[_editor->clicked_selection].start;
3068 end = _editor->selection->time[_editor->clicked_selection].end;
3070 length = end - start;
3072 start = pending_position;
3073 _editor->snap_to (start);
3075 end = start + length;
3080 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3081 _editor->start_canvas_autoscroll (1, 0);
3085 _editor->selection->replace (_editor->clicked_selection, start, end);
3088 if (_operation == SelectionMove) {
3089 _editor->show_verbose_time_cursor(start, 10);
3091 _editor->show_verbose_time_cursor(pending_position, 10);
3096 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3098 Session* s = _editor->session();
3100 if (movement_occurred) {
3101 motion (event, false);
3102 /* XXX this is not object-oriented programming at all. ick */
3103 if (_editor->selection->time.consolidate()) {
3104 _editor->selection->TimeChanged ();
3107 if (_have_transaction) {
3108 _editor->commit_reversible_command ();
3111 /* XXX what if its a music time selection? */
3112 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3113 s->request_play_range (&_editor->selection->time, true);
3118 /* just a click, no pointer movement.*/
3120 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3121 _editor->selection->clear_time();
3124 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3125 _editor->selection->set (_editor->clicked_axisview);
3128 if (s && s->get_play_range () && s->transport_rolling()) {
3129 s->request_stop (false, false);
3134 _editor->stop_canvas_autoscroll ();
3137 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3142 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3143 _drag_rect->hide ();
3145 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3146 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3150 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3152 if (_editor->session() == 0) {
3156 Gdk::Cursor* cursor = 0;
3158 if (!_editor->temp_location) {
3159 _editor->temp_location = new Location;
3162 switch (_operation) {
3163 case CreateRangeMarker:
3164 case CreateTransportMarker:
3165 case CreateCDMarker:
3167 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3172 cursor = _editor->selector_cursor;
3176 Drag::start_grab (event, cursor);
3178 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3182 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3184 nframes64_t start = 0;
3185 nframes64_t end = 0;
3186 ArdourCanvas::SimpleRect *crect;
3188 switch (_operation) {
3189 case CreateRangeMarker:
3190 crect = _editor->range_bar_drag_rect;
3192 case CreateTransportMarker:
3193 crect = _editor->transport_bar_drag_rect;
3195 case CreateCDMarker:
3196 crect = _editor->cd_marker_bar_drag_rect;
3199 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3204 nframes64_t const pf = adjusted_current_frame (event);
3206 /* only alter selection if the current frame is
3207 different from the last frame position.
3210 if (pf == last_pointer_frame()) {
3214 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3215 nframes64_t grab = grab_frame ();
3216 _editor->snap_to (grab);
3218 if (pf < grab_frame()) {
3226 /* first drag: Either add to the selection
3227 or create a new selection.
3232 _editor->temp_location->set (start, end);
3236 update_item (_editor->temp_location);
3238 //_drag_rect->raise_to_top();
3243 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3244 _editor->start_canvas_autoscroll (1, 0);
3248 _editor->temp_location->set (start, end);
3250 double x1 = _editor->frame_to_pixel (start);
3251 double x2 = _editor->frame_to_pixel (end);
3252 crect->property_x1() = x1;
3253 crect->property_x2() = x2;
3255 update_item (_editor->temp_location);
3258 _editor->show_verbose_time_cursor (pf, 10);
3263 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3265 Location * newloc = 0;
3269 if (movement_occurred) {
3270 motion (event, false);
3273 switch (_operation) {
3274 case CreateRangeMarker:
3275 case CreateCDMarker:
3277 _editor->begin_reversible_command (_("new range marker"));
3278 XMLNode &before = _editor->session()->locations()->get_state();
3279 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3280 if (_operation == CreateCDMarker) {
3281 flags = Location::IsRangeMarker | Location::IsCDMarker;
3282 _editor->cd_marker_bar_drag_rect->hide();
3285 flags = Location::IsRangeMarker;
3286 _editor->range_bar_drag_rect->hide();
3288 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3289 _editor->session()->locations()->add (newloc, true);
3290 XMLNode &after = _editor->session()->locations()->get_state();
3291 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3292 _editor->commit_reversible_command ();
3296 case CreateTransportMarker:
3297 // popup menu to pick loop or punch
3298 _editor->new_transport_marker_context_menu (&event->button, _item);
3302 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3304 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3309 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3311 if (end == max_frames) {
3312 end = _editor->session()->current_end_frame ();
3315 if (start == max_frames) {
3316 start = _editor->session()->current_start_frame ();
3319 switch (_editor->mouse_mode) {
3321 /* find the two markers on either side and then make the selection from it */
3322 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3326 /* find the two markers on either side of the click and make the range out of it */
3327 _editor->selection->set (start, end);
3336 _editor->stop_canvas_autoscroll ();
3342 RangeMarkerBarDrag::update_item (Location* location)
3344 double const x1 = _editor->frame_to_pixel (location->start());
3345 double const x2 = _editor->frame_to_pixel (location->end());
3347 _drag_rect->property_x1() = x1;
3348 _drag_rect->property_x2() = x2;
3352 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3354 Drag::start_grab (event, _editor->zoom_cursor);
3355 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3359 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3364 nframes64_t const pf = adjusted_current_frame (event);
3366 if (pf == last_pointer_frame()) {
3370 nframes64_t grab = grab_frame ();
3371 _editor->snap_to_with_modifier (grab, event);
3373 /* base start and end on initial click position */
3385 _editor->zoom_rect->show();
3386 _editor->zoom_rect->raise_to_top();
3389 _editor->reposition_zoom_rect(start, end);
3391 _editor->show_verbose_time_cursor (pf, 10);
3396 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3398 if (movement_occurred) {
3399 motion (event, false);
3401 if (grab_frame() < last_pointer_frame()) {
3402 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3404 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3407 _editor->temporal_zoom_to_frame (false, grab_frame());
3409 temporal_zoom_step (false);
3410 center_screen (grab_frame());
3414 _editor->zoom_rect->hide();
3417 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3420 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3421 region = &cnote->region_view();
3425 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3427 Drag::start_grab (event);
3430 drag_delta_note = 0;
3435 event_x = current_pointer_x();
3436 event_y = current_pointer_y();
3438 _item->property_parent().get_value()->w2i(event_x, event_y);
3440 last_x = region->snap_to_pixel(event_x);
3443 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3445 if (!(was_selected = cnote->selected())) {
3447 /* tertiary-click means extend selection - we'll do that on button release,
3448 so don't add it here, because otherwise we make it hard to figure
3449 out the "extend-to" range.
3452 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3455 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3458 region->note_selected (cnote, true);
3460 region->unique_select (cnote);
3467 NoteDrag::motion (GdkEvent*, bool)
3469 MidiStreamView* streamview = region->midi_stream_view();
3473 event_x = current_pointer_x();
3474 event_y = current_pointer_y();
3476 _item->property_parent().get_value()->w2i(event_x, event_y);
3478 event_x = region->snap_to_pixel(event_x);
3480 double dx = event_x - last_x;
3481 double dy = event_y - last_y;
3486 // Snap to note rows
3488 if (abs (dy) < streamview->note_height()) {
3491 int8_t this_delta_note;
3493 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3495 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3497 drag_delta_note -= this_delta_note;
3498 dy = streamview->note_height() * this_delta_note;
3499 last_y = last_y + dy;
3503 region->move_selection (dx, dy);
3505 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3507 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3508 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3509 _editor->show_verbose_canvas_cursor_with (buf);
3514 NoteDrag::finished (GdkEvent* ev, bool moved)
3516 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3519 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3522 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3524 region->note_deselected (cnote);
3527 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3528 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3530 if (!extend && !add && region->selection_size() > 1) {
3531 region->unique_select(cnote);
3532 } else if (extend) {
3533 region->note_selected (cnote, true, true);
3535 /* it was added during button press */
3540 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3544 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3547 , _nothing_to_drag (false)
3549 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3552 _line = _atav->line ();
3556 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3558 Drag::start_grab (event, cursor);
3560 list<ControlPoint*> points;
3562 XMLNode* state = &_line->get_state ();
3564 if (_ranges.empty()) {
3566 uint32_t const N = _line->npoints ();
3567 for (uint32_t i = 0; i < N; ++i) {
3568 points.push_back (_line->nth (i));
3573 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3574 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3576 /* fade into and out of the region that we're dragging;
3577 64 samples length plucked out of thin air.
3579 nframes64_t const h = (j->start + j->end) / 2;
3580 nframes64_t a = j->start + 64;
3584 nframes64_t b = j->end - 64;
3589 the_list->add (j->start, the_list->eval (j->start));
3590 _line->add_always_in_view (j->start);
3591 the_list->add (a, the_list->eval (a));
3592 _line->add_always_in_view (a);
3593 the_list->add (b, the_list->eval (b));
3594 _line->add_always_in_view (b);
3595 the_list->add (j->end, the_list->eval (j->end));
3596 _line->add_always_in_view (j->end);
3599 uint32_t const N = _line->npoints ();
3600 for (uint32_t i = 0; i < N; ++i) {
3602 ControlPoint* p = _line->nth (i);
3604 list<AudioRange>::const_iterator j = _ranges.begin ();
3605 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3609 if (j != _ranges.end()) {
3610 points.push_back (p);
3615 if (points.empty()) {
3616 _nothing_to_drag = true;
3620 _line->start_drag_multiple (points, 1 - (current_pointer_y() / _line->height ()), state);
3624 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3626 if (_nothing_to_drag) {
3630 float const f = 1 - (current_pointer_y() / _line->height());
3632 /* we are ignoring x position for this drag, so we can just pass in 0 */
3633 _line->drag_motion (0, f, false);
3637 AutomationRangeDrag::finished (GdkEvent* event, bool)
3639 if (_nothing_to_drag) {
3643 motion (event, false);
3645 _line->clear_always_in_view ();