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;
877 rv->get_time_axis_view().reveal_dependent_views (*rv);
879 /* just change the model */
881 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
883 if (dest_rtv->view()->layer_display() == Stacked) {
884 rv->region()->set_layer (dest_layer);
885 rv->region()->set_pending_explicit_relayer (true);
888 insert_result = modified_playlists.insert (playlist);
890 if (insert_result.second) {
891 _editor->session()->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
893 /* freeze to avoid lots of relayering in the case of a multi-region drag */
894 frozen_insert_result = frozen_playlists.insert(playlist);
896 if (frozen_insert_result.second) {
900 rv->region()->set_position (where, (void*) this);
903 if (changed_tracks && !_copy) {
905 /* get the playlist where this drag started. we can't use rv->region()->playlist()
906 because we may have copied the region and it has not been attached to a playlist.
909 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
910 ds = source_tv->get_diskstream();
911 from_playlist = ds->playlist();
915 assert (from_playlist);
917 /* moved to a different audio track, without copying */
919 /* the region that used to be in the old playlist is not
920 moved to the new one - we use a copy of it. as a result,
921 any existing editor for the region should no longer be
925 rv->hide_region_editor();
926 rv->fake_set_opaque (false);
928 /* remove the region from the old playlist */
930 insert_result = modified_playlists.insert (from_playlist);
932 if (insert_result.second) {
933 _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
936 from_playlist->remove_region (rv->region());
938 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
939 was selected in all of them, then removing it from a playlist will have removed all
940 trace of it from the selection (i.e. there were N regions selected, we removed 1,
941 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
942 corresponding regionview, and the selection is now empty).
944 this could have invalidated any and all iterators into the region selection.
946 the heuristic we use here is: if the region selection is empty, break out of the loop
947 here. if the region selection is not empty, then restart the loop because we know that
948 we must have removed at least the region(view) we've just been working on as well as any
949 that we processed on previous iterations.
951 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
955 if (_views.empty()) {
966 copies.push_back (rv);
970 if we've created new regions either by copying or moving
971 to a new track, we want to replace the old selection with the new ones
973 if (new_views.size() > 0) {
974 _editor->selection->set (new_views);
977 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
982 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
983 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
986 _editor->commit_reversible_command ();
988 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
995 RegionMotionDrag::x_move_allowed () const
997 if (Config->get_edit_mode() == Lock) {
998 /* in locked edit mode, reverse the usual meaning of _x_constrained */
999 return _x_constrained;
1002 return !_x_constrained;
1006 RegionMotionDrag::copy_regions (GdkEvent* event)
1008 /* duplicate the regionview(s) and region(s) */
1010 list<RegionView*> new_regionviews;
1012 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1014 RegionView* rv = (*i);
1015 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1016 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1018 const boost::shared_ptr<const Region> original = rv->region();
1019 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1023 boost::shared_ptr<AudioRegion> audioregion_copy
1024 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1025 nrv = new AudioRegionView (*arv, audioregion_copy);
1027 boost::shared_ptr<MidiRegion> midiregion_copy
1028 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1029 nrv = new MidiRegionView (*mrv, midiregion_copy);
1034 nrv->get_canvas_group()->show ();
1035 new_regionviews.push_back (nrv);
1037 /* swap _primary to the copy */
1039 if (rv == _primary) {
1043 /* ..and deselect the one we copied */
1045 rv->set_selected (false);
1048 if (new_regionviews.empty()) {
1052 /* reflect the fact that we are dragging the copies */
1054 _views = new_regionviews;
1056 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1059 sync the canvas to what we think is its current state
1060 without it, the canvas seems to
1061 "forget" to update properly after the upcoming reparent()
1062 ..only if the mouse is in rapid motion at the time of the grab.
1063 something to do with regionview creation raking so long?
1065 _editor->update_canvas_now();
1069 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1071 /* Which trackview is this ? */
1073 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1074 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1075 (*layer) = tvp.second;
1077 if (*tv && (*tv)->layer_display() == Overlaid) {
1081 /* The region motion is only processed if the pointer is over
1085 if (!(*tv) || !(*tv)->is_track()) {
1086 /* To make sure we hide the verbose canvas cursor when the mouse is
1087 not held over and audiotrack.
1089 _editor->hide_verbose_canvas_cursor ();
1096 /** @param new_order New track order.
1097 * @param old_order Old track order.
1098 * @param visible_y_low Lowest visible order.
1099 * @return true if y movement should not happen, otherwise false.
1102 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1104 if (new_order != old_order) {
1106 /* this isn't the pointer track */
1110 /* moving up the canvas */
1111 if ( (new_order - y_span) >= tavs.visible_y_low) {
1115 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1116 int32_t visible_tracks = 0;
1117 while (visible_tracks < y_span ) {
1119 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1120 /* passing through a hidden track */
1125 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1126 /* moving to a non-track; disallow */
1132 /* moving beyond the lowest visible track; disallow */
1136 } else if (y_span < 0) {
1138 /* moving down the canvas */
1139 if ((new_order - y_span) <= tavs.visible_y_high) {
1141 int32_t visible_tracks = 0;
1143 while (visible_tracks > y_span ) {
1146 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1147 /* passing through a hidden track */
1152 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1153 /* moving to a non-track; disallow */
1160 /* moving beyond the highest visible track; disallow */
1167 /* this is the pointer's track */
1169 if ((new_order - y_span) > tavs.visible_y_high) {
1170 /* we will overflow */
1172 } else if ((new_order - y_span) < tavs.visible_y_low) {
1173 /* we will overflow */
1182 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1183 : RegionMotionDrag (e, i, p, v, b),
1186 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1188 _dest_trackview = tv;
1189 if (tv->layer_display() == Overlaid) {
1192 _dest_layer = _primary->region()->layer ();
1196 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1197 if (rtv && rtv->is_track()) {
1198 speed = rtv->get_diskstream()->speed ();
1201 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1205 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1207 RegionMotionDrag::start_grab (event, c);
1209 _pointer_frame_offset = grab_frame() - _last_frame_position;
1212 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1213 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1215 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1216 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1218 _primary = v->view()->create_region_view (r, false, false);
1220 _primary->get_canvas_group()->show ();
1221 _primary->set_position (pos, 0);
1222 _views.push_back (_primary);
1224 _last_frame_position = pos;
1226 _item = _primary->get_canvas_group ();
1227 _dest_trackview = v;
1228 _dest_layer = _primary->region()->layer ();
1231 map<RegionView*, pair<RouteTimeAxisView*, int> >
1232 RegionMotionDrag::find_time_axis_views_and_layers ()
1234 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1236 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1238 double ix1, ix2, iy1, iy2;
1239 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1240 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1241 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1243 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1244 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1252 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1254 _editor->update_canvas_now ();
1256 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1258 RouteTimeAxisView* dest_rtv = final[_primary].first;
1260 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1261 _primary->get_canvas_group()->property_y() = 0;
1263 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1265 _editor->begin_reversible_command (_("insert region"));
1266 XMLNode& before = playlist->get_state ();
1267 playlist->add_region (_primary->region (), _last_frame_position);
1268 _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1269 _editor->commit_reversible_command ();
1276 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1277 : RegionMoveDrag (e, i, p, v, false, false)
1282 struct RegionSelectionByPosition {
1283 bool operator() (RegionView*a, RegionView* b) {
1284 return a->region()->position () < b->region()->position();
1289 RegionSpliceDrag::motion (GdkEvent* event, bool)
1291 RouteTimeAxisView* tv;
1294 if (!check_possible (&tv, &layer)) {
1300 if ((current_pointer_x() - last_pointer_x()) > 0) {
1306 RegionSelection copy (_editor->selection->regions);
1308 RegionSelectionByPosition cmp;
1311 nframes64_t const pf = adjusted_current_frame (event);
1313 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1315 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1321 boost::shared_ptr<Playlist> playlist;
1323 if ((playlist = atv->playlist()) == 0) {
1327 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1332 if (pf < (*i)->region()->last_frame() + 1) {
1336 if (pf > (*i)->region()->first_frame()) {
1342 playlist->shuffle ((*i)->region(), dir);
1347 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1353 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1361 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1363 _dest_trackview = _view;
1365 Drag::start_grab (event);
1370 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1373 // TODO: create region-create-drag region view here
1376 // TODO: resize region-create-drag region view here
1380 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1382 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1388 if (!movement_occurred) {
1389 mtv->add_region (grab_frame ());
1391 motion (event, false);
1392 // TODO: create region-create-drag region here
1396 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1404 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1407 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1409 Drag::start_grab (event);
1411 region = &cnote->region_view();
1413 double region_start = region->get_position_pixels();
1414 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1416 if (grab_x() <= middle_point) {
1417 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1420 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1424 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1426 if (event->motion.state & Keyboard::PrimaryModifier) {
1432 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1434 if (ms.size() > 1) {
1435 /* has to be relative, may make no sense otherwise */
1439 region->note_selected (cnote, true);
1441 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1442 MidiRegionSelection::iterator next;
1445 (*r)->begin_resizing (at_front);
1451 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1453 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1454 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1455 (*r)->update_resizing (at_front, current_pointer_x() - grab_x(), relative);
1460 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1462 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1463 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1464 (*r)->commit_resizing (at_front, current_pointer_x() - grab_x(), relative);
1469 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1475 RegionGainDrag::finished (GdkEvent *, bool)
1480 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1481 : RegionDrag (e, i, p, v)
1487 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1490 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1491 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1493 if (tv && tv->is_track()) {
1494 speed = tv->get_diskstream()->speed();
1497 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1498 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1499 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1501 Drag::start_grab (event, _editor->trimmer_cursor);
1503 nframes64_t const pf = adjusted_current_frame (event);
1505 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1506 _operation = ContentsTrim;
1508 /* These will get overridden for a point trim.*/
1509 if (pf < (region_start + region_length/2)) {
1510 /* closer to start */
1511 _operation = StartTrim;
1512 } else if (pf > (region_end - region_length/2)) {
1514 _operation = EndTrim;
1518 switch (_operation) {
1520 _editor->show_verbose_time_cursor (region_start, 10);
1523 _editor->show_verbose_time_cursor (region_end, 10);
1526 _editor->show_verbose_time_cursor (pf, 10);
1532 TrimDrag::motion (GdkEvent* event, bool first_move)
1534 RegionView* rv = _primary;
1535 nframes64_t frame_delta = 0;
1537 bool left_direction;
1538 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1540 /* snap modifier works differently here..
1541 its current state has to be passed to the
1542 various trim functions in order to work properly
1546 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1547 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1548 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1550 if (tv && tv->is_track()) {
1551 speed = tv->get_diskstream()->speed();
1554 nframes64_t const pf = adjusted_current_frame (event);
1556 if (last_pointer_frame() > pf) {
1557 left_direction = true;
1559 left_direction = false;
1566 switch (_operation) {
1568 trim_type = "Region start trim";
1571 trim_type = "Region end trim";
1574 trim_type = "Region content trim";
1578 _editor->begin_reversible_command (trim_type);
1579 _have_transaction = true;
1581 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1582 (*i)->fake_set_opaque(false);
1583 (*i)->region()->freeze ();
1585 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1588 arv->temporarily_hide_envelope ();
1591 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1592 insert_result = _editor->motion_frozen_playlists.insert (pl);
1594 if (insert_result.second) {
1595 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1601 if (pf == last_pointer_frame()) {
1605 /* XXX i hope to god that we can really conclude this ... */
1606 _have_transaction = true;
1608 if (left_direction) {
1609 frame_delta = (last_pointer_frame() - pf);
1611 frame_delta = (pf - last_pointer_frame());
1614 bool non_overlap_trim = false;
1616 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1617 non_overlap_trim = true;
1620 switch (_operation) {
1622 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1626 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1627 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1633 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1637 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1638 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1645 bool swap_direction = false;
1647 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1648 swap_direction = true;
1651 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1652 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1658 switch (_operation) {
1660 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1663 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1666 _editor->show_verbose_time_cursor (pf, 10);
1673 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1675 if (movement_occurred) {
1676 motion (event, false);
1678 if (!_editor->selection->selected (_primary)) {
1679 _editor->thaw_region_after_trim (*_primary);
1682 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1683 _editor->thaw_region_after_trim (**i);
1684 (*i)->fake_set_opaque (true);
1687 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1689 if (_have_transaction) {
1690 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1694 _editor->motion_frozen_playlists.clear ();
1696 if (_have_transaction) {
1697 _editor->commit_reversible_command();
1701 /* no mouse movement */
1702 _editor->point_trim (event);
1706 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1710 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1715 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1718 // create a dummy marker for visual representation of moving the copy.
1719 // The actual copying is not done before we reach the finish callback.
1721 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1722 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1723 *new MeterSection (_marker->meter()));
1725 _item = &new_marker->the_item ();
1726 _marker = new_marker;
1730 MetricSection& section (_marker->meter());
1732 if (!section.movable()) {
1738 Drag::start_grab (event, cursor);
1740 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1742 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1746 MeterMarkerDrag::motion (GdkEvent* event, bool)
1748 nframes64_t const pf = adjusted_current_frame (event);
1750 if (pf == last_pointer_frame()) {
1754 _marker->set_position (pf);
1756 _editor->show_verbose_time_cursor (pf, 10);
1760 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1762 if (!movement_occurred) {
1766 motion (event, false);
1770 TempoMap& map (_editor->session()->tempo_map());
1771 map.bbt_time (last_pointer_frame(), when);
1773 if (_copy == true) {
1774 _editor->begin_reversible_command (_("copy meter mark"));
1775 XMLNode &before = map.get_state();
1776 map.add_meter (_marker->meter(), when);
1777 XMLNode &after = map.get_state();
1778 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1779 _editor->commit_reversible_command ();
1781 // delete the dummy marker we used for visual representation of copying.
1782 // a new visual marker will show up automatically.
1785 _editor->begin_reversible_command (_("move meter mark"));
1786 XMLNode &before = map.get_state();
1787 map.move_meter (_marker->meter(), when);
1788 XMLNode &after = map.get_state();
1789 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1790 _editor->commit_reversible_command ();
1794 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1798 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1803 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1808 // create a dummy marker for visual representation of moving the copy.
1809 // The actual copying is not done before we reach the finish callback.
1811 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1812 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1813 *new TempoSection (_marker->tempo()));
1815 _item = &new_marker->the_item ();
1816 _marker = new_marker;
1820 MetricSection& section (_marker->tempo());
1822 if (!section.movable()) {
1827 Drag::start_grab (event, cursor);
1829 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
1830 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1834 TempoMarkerDrag::motion (GdkEvent* event, bool)
1836 nframes64_t const pf = adjusted_current_frame (event);
1838 if (pf == last_pointer_frame()) {
1842 /* OK, we've moved far enough to make it worth actually move the thing. */
1844 _marker->set_position (pf);
1846 _editor->show_verbose_time_cursor (pf, 10);
1850 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1852 if (!movement_occurred) {
1856 motion (event, false);
1860 TempoMap& map (_editor->session()->tempo_map());
1861 map.bbt_time (last_pointer_frame(), when);
1863 if (_copy == true) {
1864 _editor->begin_reversible_command (_("copy tempo mark"));
1865 XMLNode &before = map.get_state();
1866 map.add_tempo (_marker->tempo(), when);
1867 XMLNode &after = map.get_state();
1868 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1869 _editor->commit_reversible_command ();
1871 // delete the dummy marker we used for visual representation of copying.
1872 // a new visual marker will show up automatically.
1875 _editor->begin_reversible_command (_("move tempo mark"));
1876 XMLNode &before = map.get_state();
1877 map.move_tempo (_marker->tempo(), when);
1878 XMLNode &after = map.get_state();
1879 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1880 _editor->commit_reversible_command ();
1885 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1889 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1894 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1896 Drag::start_grab (event, c);
1900 nframes64_t where = _editor->event_frame (event, 0, 0);
1902 _editor->snap_to_with_modifier (where, event);
1903 _editor->playhead_cursor->set_position (where);
1907 if (_cursor == _editor->playhead_cursor) {
1908 _editor->_dragging_playhead = true;
1910 if (_editor->session() && _was_rolling && _stop) {
1911 _editor->session()->request_stop ();
1914 if (_editor->session() && _editor->session()->is_auditioning()) {
1915 _editor->session()->cancel_audition ();
1919 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1923 CursorDrag::motion (GdkEvent* event, bool)
1925 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1927 if (adjusted_frame == last_pointer_frame()) {
1931 _cursor->set_position (adjusted_frame);
1933 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1936 _editor->update_canvas_now ();
1938 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1942 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1944 _editor->_dragging_playhead = false;
1946 if (!movement_occurred && _stop) {
1950 motion (event, false);
1952 if (_item == &_editor->playhead_cursor->canvas_item) {
1953 if (_editor->session()) {
1954 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1955 _editor->_pending_locate_request = true;
1960 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1961 : RegionDrag (e, i, p, v)
1967 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1969 Drag::start_grab (event, cursor);
1971 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1972 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1974 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
1975 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1979 FadeInDrag::motion (GdkEvent* event, bool)
1981 nframes64_t fade_length;
1983 nframes64_t const pos = adjusted_current_frame (event);
1985 boost::shared_ptr<Region> region = _primary->region ();
1987 if (pos < (region->position() + 64)) {
1988 fade_length = 64; // this should be a minimum defined somewhere
1989 } else if (pos > region->last_frame()) {
1990 fade_length = region->length();
1992 fade_length = pos - region->position();
1995 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1997 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2003 tmp->reset_fade_in_shape_width (fade_length);
2006 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2010 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2012 if (!movement_occurred) {
2016 nframes64_t fade_length;
2018 nframes64_t const pos = adjusted_current_frame (event);
2020 boost::shared_ptr<Region> region = _primary->region ();
2022 if (pos < (region->position() + 64)) {
2023 fade_length = 64; // this should be a minimum defined somewhere
2024 } else if (pos > region->last_frame()) {
2025 fade_length = region->length();
2027 fade_length = pos - region->position();
2030 _editor->begin_reversible_command (_("change fade in length"));
2032 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2034 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2040 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2041 XMLNode &before = alist->get_state();
2043 tmp->audio_region()->set_fade_in_length (fade_length);
2044 tmp->audio_region()->set_fade_in_active (true);
2046 XMLNode &after = alist->get_state();
2047 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2050 _editor->commit_reversible_command ();
2053 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2054 : RegionDrag (e, i, p, v)
2060 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2062 Drag::start_grab (event, cursor);
2064 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2065 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2067 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2068 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2072 FadeOutDrag::motion (GdkEvent* event, bool)
2074 nframes64_t fade_length;
2076 nframes64_t const pos = adjusted_current_frame (event);
2078 boost::shared_ptr<Region> region = _primary->region ();
2080 if (pos > (region->last_frame() - 64)) {
2081 fade_length = 64; // this should really be a minimum fade defined somewhere
2083 else if (pos < region->position()) {
2084 fade_length = region->length();
2087 fade_length = region->last_frame() - pos;
2090 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2092 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2098 tmp->reset_fade_out_shape_width (fade_length);
2101 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2105 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2107 if (!movement_occurred) {
2111 nframes64_t fade_length;
2113 nframes64_t const pos = adjusted_current_frame (event);
2115 boost::shared_ptr<Region> region = _primary->region ();
2117 if (pos > (region->last_frame() - 64)) {
2118 fade_length = 64; // this should really be a minimum fade defined somewhere
2120 else if (pos < region->position()) {
2121 fade_length = region->length();
2124 fade_length = region->last_frame() - pos;
2127 _editor->begin_reversible_command (_("change fade out 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_out();
2138 XMLNode &before = alist->get_state();
2140 tmp->audio_region()->set_fade_out_length (fade_length);
2141 tmp->audio_region()->set_fade_out_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 ();
2150 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2153 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2156 _points.push_back (Gnome::Art::Point (0, 0));
2157 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2159 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2160 _line->property_width_pixels() = 1;
2161 _line->property_points () = _points;
2164 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2167 MarkerDrag::~MarkerDrag ()
2169 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2175 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2177 Drag::start_grab (event, cursor);
2181 Location *location = _editor->find_location_from_marker (_marker, is_start);
2182 _editor->_dragging_edit_point = true;
2184 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2186 update_item (location);
2188 // _drag_line->show();
2189 // _line->raise_to_top();
2192 _editor->show_verbose_time_cursor (location->start(), 10);
2194 _editor->show_verbose_time_cursor (location->end(), 10);
2197 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2200 case Selection::Toggle:
2201 _editor->selection->toggle (_marker);
2203 case Selection::Set:
2204 if (!_editor->selection->selected (_marker)) {
2205 _editor->selection->set (_marker);
2208 case Selection::Extend:
2210 Locations::LocationList ll;
2211 list<Marker*> to_add;
2213 _editor->selection->markers.range (s, e);
2214 s = min (_marker->position(), s);
2215 e = max (_marker->position(), e);
2218 if (e < max_frames) {
2221 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2222 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2223 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2226 to_add.push_back (lm->start);
2229 to_add.push_back (lm->end);
2233 if (!to_add.empty()) {
2234 _editor->selection->add (to_add);
2238 case Selection::Add:
2239 _editor->selection->add (_marker);
2243 /* set up copies for us to manipulate during the drag */
2245 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2246 Location *l = _editor->find_location_from_marker (*i, is_start);
2247 _copied_locations.push_back (new Location (*l));
2252 MarkerDrag::motion (GdkEvent* event, bool)
2254 nframes64_t f_delta = 0;
2256 bool move_both = false;
2258 Location *real_location;
2259 Location *copy_location = 0;
2261 nframes64_t const newframe = adjusted_current_frame (event);
2263 nframes64_t next = newframe;
2265 if (newframe == last_pointer_frame()) {
2269 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2273 MarkerSelection::iterator i;
2274 list<Location*>::iterator x;
2276 /* find the marker we're dragging, and compute the delta */
2278 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2279 x != _copied_locations.end() && i != _editor->selection->markers.end();
2285 if (marker == _marker) {
2287 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2292 if (real_location->is_mark()) {
2293 f_delta = newframe - copy_location->start();
2297 switch (marker->type()) {
2299 case Marker::LoopStart:
2300 case Marker::PunchIn:
2301 f_delta = newframe - copy_location->start();
2305 case Marker::LoopEnd:
2306 case Marker::PunchOut:
2307 f_delta = newframe - copy_location->end();
2310 /* what kind of marker is this ? */
2318 if (i == _editor->selection->markers.end()) {
2319 /* hmm, impossible - we didn't find the dragged marker */
2323 /* now move them all */
2325 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2326 x != _copied_locations.end() && i != _editor->selection->markers.end();
2332 /* call this to find out if its the start or end */
2334 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2338 if (real_location->locked()) {
2342 if (copy_location->is_mark()) {
2346 copy_location->set_start (copy_location->start() + f_delta);
2350 nframes64_t new_start = copy_location->start() + f_delta;
2351 nframes64_t new_end = copy_location->end() + f_delta;
2353 if (is_start) { // start-of-range marker
2356 copy_location->set_start (new_start);
2357 copy_location->set_end (new_end);
2358 } else if (new_start < copy_location->end()) {
2359 copy_location->set_start (new_start);
2361 _editor->snap_to (next, 1, true);
2362 copy_location->set_end (next);
2363 copy_location->set_start (newframe);
2366 } else { // end marker
2369 copy_location->set_end (new_end);
2370 copy_location->set_start (new_start);
2371 } else if (new_end > copy_location->start()) {
2372 copy_location->set_end (new_end);
2373 } else if (newframe > 0) {
2374 _editor->snap_to (next, -1, true);
2375 copy_location->set_start (next);
2376 copy_location->set_end (newframe);
2381 update_item (copy_location);
2383 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2386 lm->set_position (copy_location->start(), copy_location->end());
2390 assert (!_copied_locations.empty());
2392 _editor->show_verbose_time_cursor (newframe, 10);
2395 _editor->update_canvas_now ();
2400 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2402 if (!movement_occurred) {
2404 /* just a click, do nothing but finish
2405 off the selection process
2408 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2411 case Selection::Set:
2412 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2413 _editor->selection->set (_marker);
2417 case Selection::Toggle:
2418 case Selection::Extend:
2419 case Selection::Add:
2426 _editor->_dragging_edit_point = false;
2428 _editor->begin_reversible_command ( _("move marker") );
2429 XMLNode &before = _editor->session()->locations()->get_state();
2431 MarkerSelection::iterator i;
2432 list<Location*>::iterator x;
2435 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2436 x != _copied_locations.end() && i != _editor->selection->markers.end();
2439 Location * location = _editor->find_location_from_marker (*i, is_start);
2443 if (location->locked()) {
2447 if (location->is_mark()) {
2448 location->set_start ((*x)->start());
2450 location->set ((*x)->start(), (*x)->end());
2455 XMLNode &after = _editor->session()->locations()->get_state();
2456 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2457 _editor->commit_reversible_command ();
2463 MarkerDrag::update_item (Location* location)
2465 double const x1 = _editor->frame_to_pixel (location->start());
2467 _points.front().set_x(x1);
2468 _points.back().set_x(x1);
2469 _line->property_points() = _points;
2472 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2474 _cumulative_x_drag (0),
2475 _cumulative_y_drag (0)
2477 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2483 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2485 Drag::start_grab (event, _editor->fader_cursor);
2487 // start the grab at the center of the control point so
2488 // the point doesn't 'jump' to the mouse after the first drag
2489 _time_axis_view_grab_x = _point->get_x();
2490 _time_axis_view_grab_y = _point->get_y();
2491 nframes64_t grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x);
2493 float const fraction = 1 - (_point->get_y() / _point->line().height());
2495 _point->line().start_drag_single (_point, grab_frame, fraction);
2497 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2498 event->button.x + 10, event->button.y + 10);
2500 _editor->show_verbose_canvas_cursor ();
2504 ControlPointDrag::motion (GdkEvent* event, bool)
2506 double dx = current_pointer_x() - last_pointer_x();
2507 double dy = current_pointer_y() - last_pointer_y();
2509 if (event->button.state & Keyboard::SecondaryModifier) {
2514 /* coordinate in TimeAxisView's space */
2515 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2516 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2518 // calculate zero crossing point. back off by .01 to stay on the
2519 // positive side of zero
2520 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2522 // make sure we hit zero when passing through
2523 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2527 if (_x_constrained) {
2528 cx = _time_axis_view_grab_x;
2530 if (_y_constrained) {
2531 cy = _time_axis_view_grab_y;
2534 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2535 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2539 cy = min ((double) _point->line().height(), cy);
2541 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2543 if (!_x_constrained) {
2544 _editor->snap_to_with_modifier (cx_frames, event);
2547 float const fraction = 1.0 - (cy / _point->line().height());
2549 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2551 _point->line().drag_motion (cx_frames, fraction, push);
2553 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2557 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2559 if (!movement_occurred) {
2563 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2564 _editor->reset_point_selection ();
2568 motion (event, false);
2570 _point->line().end_drag ();
2574 ControlPointDrag::active (Editing::MouseMode m)
2576 if (m == Editing::MouseGain) {
2577 /* always active in mouse gain */
2581 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2582 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2585 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2588 _cumulative_y_drag (0)
2593 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2595 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2598 _item = &_line->grab_item ();
2600 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2601 origin, and ditto for y.
2604 double cx = event->button.x;
2605 double cy = event->button.y;
2607 _line->parent_group().w2i (cx, cy);
2609 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2614 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2615 /* no adjacent points */
2619 Drag::start_grab (event, _editor->fader_cursor);
2621 /* store grab start in parent frame */
2623 _time_axis_view_grab_x = cx;
2624 _time_axis_view_grab_y = cy;
2626 double fraction = 1.0 - (cy / _line->height());
2628 _line->start_drag_line (before, after, fraction);
2630 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2631 event->button.x + 10, event->button.y + 10);
2633 _editor->show_verbose_canvas_cursor ();
2637 LineDrag::motion (GdkEvent* event, bool)
2639 double dy = current_pointer_y() - last_pointer_y();
2641 if (event->button.state & Keyboard::SecondaryModifier) {
2645 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2647 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2650 cy = min ((double) _line->height(), cy);
2652 double const fraction = 1.0 - (cy / _line->height());
2656 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2662 /* we are ignoring x position for this drag, so we can just pass in 0 */
2663 _line->drag_motion (0, fraction, push);
2665 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2669 LineDrag::finished (GdkEvent* event, bool)
2671 motion (event, false);
2676 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2678 Drag::start_grab (event);
2679 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2683 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2690 /* use a bigger drag threshold than the default */
2692 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2694 if (abs ((int) (pf - grab_frame())) < 8) {
2698 nframes64_t grab = grab_frame ();
2699 if (Config->get_rubberbanding_snaps_to_grid ()) {
2700 _editor->snap_to_with_modifier (grab, event);
2703 /* base start and end on initial click position */
2713 if (current_pointer_y() < grab_y()) {
2714 y1 = current_pointer_y();
2717 y2 = current_pointer_y();
2722 if (start != end || y1 != y2) {
2724 double x1 = _editor->frame_to_pixel (start);
2725 double x2 = _editor->frame_to_pixel (end);
2727 _editor->rubberband_rect->property_x1() = x1;
2728 _editor->rubberband_rect->property_y1() = y1;
2729 _editor->rubberband_rect->property_x2() = x2;
2730 _editor->rubberband_rect->property_y2() = y2;
2732 _editor->rubberband_rect->show();
2733 _editor->rubberband_rect->raise_to_top();
2735 _editor->show_verbose_time_cursor (pf, 10);
2740 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2742 if (movement_occurred) {
2744 motion (event, false);
2747 if (current_pointer_y() < grab_y()) {
2748 y1 = current_pointer_y();
2751 y2 = current_pointer_y();
2756 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2759 _editor->begin_reversible_command (_("rubberband selection"));
2761 if (grab_frame() < last_pointer_frame()) {
2762 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
2764 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
2768 _editor->commit_reversible_command ();
2772 if (!getenv("ARDOUR_SAE")) {
2773 _editor->selection->clear_tracks();
2775 _editor->selection->clear_regions();
2776 _editor->selection->clear_points ();
2777 _editor->selection->clear_lines ();
2780 _editor->rubberband_rect->hide();
2784 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2786 Drag::start_grab (event);
2788 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2792 TimeFXDrag::motion (GdkEvent* event, bool)
2794 RegionView* rv = _primary;
2796 nframes64_t const pf = adjusted_current_frame (event);
2798 if (pf == last_pointer_frame()) {
2802 if (pf > rv->region()->position()) {
2803 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2806 _editor->show_verbose_time_cursor (pf, 10);
2810 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2812 _primary->get_time_axis_view().hide_timestretch ();
2814 if (!movement_occurred) {
2818 if (last_pointer_frame() < _primary->region()->position()) {
2819 /* backwards drag of the left edge - not usable */
2823 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2825 float percentage = (double) newlen / (double) _primary->region()->length();
2827 #ifndef USE_RUBBERBAND
2828 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2829 if (_primary->region()->data_type() == DataType::AUDIO) {
2830 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2834 _editor->begin_reversible_command (_("timestretch"));
2836 // XXX how do timeFX on multiple regions ?
2841 if (!_editor->time_stretch (rs, percentage) == 0) {
2842 error << _("An error occurred while executing time stretch operation") << endmsg;
2847 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2849 Drag::start_grab (event);
2853 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2859 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2861 if (movement_occurred && _editor->session()) {
2862 /* make sure we stop */
2863 _editor->session()->request_transport_speed (0.0);
2867 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2871 , _original_pointer_time_axis (-1)
2872 , _last_pointer_time_axis (-1)
2878 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2880 nframes64_t start = 0;
2881 nframes64_t end = 0;
2883 if (_editor->session() == 0) {
2887 Gdk::Cursor* cursor = 0;
2889 switch (_operation) {
2890 case CreateSelection:
2891 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2896 cursor = _editor->selector_cursor;
2897 Drag::start_grab (event, cursor);
2900 case SelectionStartTrim:
2901 if (_editor->clicked_axisview) {
2902 _editor->clicked_axisview->order_selection_trims (_item, true);
2904 Drag::start_grab (event, _editor->trimmer_cursor);
2905 start = _editor->selection->time[_editor->clicked_selection].start;
2906 _pointer_frame_offset = grab_frame() - start;
2909 case SelectionEndTrim:
2910 if (_editor->clicked_axisview) {
2911 _editor->clicked_axisview->order_selection_trims (_item, false);
2913 Drag::start_grab (event, _editor->trimmer_cursor);
2914 end = _editor->selection->time[_editor->clicked_selection].end;
2915 _pointer_frame_offset = grab_frame() - end;
2919 start = _editor->selection->time[_editor->clicked_selection].start;
2920 Drag::start_grab (event, cursor);
2921 _pointer_frame_offset = grab_frame() - start;
2925 if (_operation == SelectionMove) {
2926 _editor->show_verbose_time_cursor (start, 10);
2928 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2931 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
2935 SelectionDrag::motion (GdkEvent* event, bool first_move)
2937 nframes64_t start = 0;
2938 nframes64_t end = 0;
2941 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (current_pointer_y ());
2942 if (pending_time_axis.first == 0) {
2946 nframes64_t const pending_position = adjusted_current_frame (event);
2948 /* only alter selection if things have changed */
2950 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
2954 switch (_operation) {
2955 case CreateSelection:
2957 nframes64_t grab = grab_frame ();
2960 _editor->snap_to (grab);
2963 if (pending_position < grab_frame()) {
2964 start = pending_position;
2967 end = pending_position;
2971 /* first drag: Either add to the selection
2972 or create a new selection
2977 _editor->begin_reversible_command (_("range selection"));
2978 _have_transaction = true;
2981 /* adding to the selection */
2982 _editor->selection->add (_editor->clicked_axisview);
2983 _editor->clicked_selection = _editor->selection->add (start, end);
2988 if (!_editor->selection->selected (_editor->clicked_axisview)) {
2989 _editor->selection->set (_editor->clicked_axisview);
2992 _editor->clicked_selection = _editor->selection->set (start, end);
2996 /* select the track that we're in */
2997 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
2998 _editor->selection->add (pending_time_axis.first);
2999 _added_time_axes.push_back (pending_time_axis.first);
3002 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3003 tracks that we selected in the first place.
3006 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3007 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3009 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3010 while (i != _added_time_axes.end()) {
3012 list<TimeAxisView*>::iterator tmp = i;
3015 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3016 _editor->selection->remove (*i);
3017 _added_time_axes.remove (*i);
3026 case SelectionStartTrim:
3029 _editor->begin_reversible_command (_("trim selection start"));
3030 _have_transaction = true;
3033 start = _editor->selection->time[_editor->clicked_selection].start;
3034 end = _editor->selection->time[_editor->clicked_selection].end;
3036 if (pending_position > end) {
3039 start = pending_position;
3043 case SelectionEndTrim:
3046 _editor->begin_reversible_command (_("trim selection end"));
3047 _have_transaction = true;
3050 start = _editor->selection->time[_editor->clicked_selection].start;
3051 end = _editor->selection->time[_editor->clicked_selection].end;
3053 if (pending_position < start) {
3056 end = pending_position;
3064 _editor->begin_reversible_command (_("move selection"));
3065 _have_transaction = true;
3068 start = _editor->selection->time[_editor->clicked_selection].start;
3069 end = _editor->selection->time[_editor->clicked_selection].end;
3071 length = end - start;
3073 start = pending_position;
3074 _editor->snap_to (start);
3076 end = start + length;
3081 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3082 _editor->start_canvas_autoscroll (1, 0);
3086 _editor->selection->replace (_editor->clicked_selection, start, end);
3089 if (_operation == SelectionMove) {
3090 _editor->show_verbose_time_cursor(start, 10);
3092 _editor->show_verbose_time_cursor(pending_position, 10);
3097 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3099 Session* s = _editor->session();
3101 if (movement_occurred) {
3102 motion (event, false);
3103 /* XXX this is not object-oriented programming at all. ick */
3104 if (_editor->selection->time.consolidate()) {
3105 _editor->selection->TimeChanged ();
3108 if (_have_transaction) {
3109 _editor->commit_reversible_command ();
3112 /* XXX what if its a music time selection? */
3113 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3114 s->request_play_range (&_editor->selection->time, true);
3119 /* just a click, no pointer movement.*/
3121 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3122 _editor->selection->clear_time();
3125 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3126 _editor->selection->set (_editor->clicked_axisview);
3129 if (s && s->get_play_range () && s->transport_rolling()) {
3130 s->request_stop (false, false);
3135 _editor->stop_canvas_autoscroll ();
3138 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3143 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3144 _drag_rect->hide ();
3146 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3147 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3151 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3153 if (_editor->session() == 0) {
3157 Gdk::Cursor* cursor = 0;
3159 if (!_editor->temp_location) {
3160 _editor->temp_location = new Location;
3163 switch (_operation) {
3164 case CreateRangeMarker:
3165 case CreateTransportMarker:
3166 case CreateCDMarker:
3168 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3173 cursor = _editor->selector_cursor;
3177 Drag::start_grab (event, cursor);
3179 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3183 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3185 nframes64_t start = 0;
3186 nframes64_t end = 0;
3187 ArdourCanvas::SimpleRect *crect;
3189 switch (_operation) {
3190 case CreateRangeMarker:
3191 crect = _editor->range_bar_drag_rect;
3193 case CreateTransportMarker:
3194 crect = _editor->transport_bar_drag_rect;
3196 case CreateCDMarker:
3197 crect = _editor->cd_marker_bar_drag_rect;
3200 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3205 nframes64_t const pf = adjusted_current_frame (event);
3207 /* only alter selection if the current frame is
3208 different from the last frame position.
3211 if (pf == last_pointer_frame()) {
3215 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3216 nframes64_t grab = grab_frame ();
3217 _editor->snap_to (grab);
3219 if (pf < grab_frame()) {
3227 /* first drag: Either add to the selection
3228 or create a new selection.
3233 _editor->temp_location->set (start, end);
3237 update_item (_editor->temp_location);
3239 //_drag_rect->raise_to_top();
3244 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3245 _editor->start_canvas_autoscroll (1, 0);
3249 _editor->temp_location->set (start, end);
3251 double x1 = _editor->frame_to_pixel (start);
3252 double x2 = _editor->frame_to_pixel (end);
3253 crect->property_x1() = x1;
3254 crect->property_x2() = x2;
3256 update_item (_editor->temp_location);
3259 _editor->show_verbose_time_cursor (pf, 10);
3264 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3266 Location * newloc = 0;
3270 if (movement_occurred) {
3271 motion (event, false);
3274 switch (_operation) {
3275 case CreateRangeMarker:
3276 case CreateCDMarker:
3278 _editor->begin_reversible_command (_("new range marker"));
3279 XMLNode &before = _editor->session()->locations()->get_state();
3280 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3281 if (_operation == CreateCDMarker) {
3282 flags = Location::IsRangeMarker | Location::IsCDMarker;
3283 _editor->cd_marker_bar_drag_rect->hide();
3286 flags = Location::IsRangeMarker;
3287 _editor->range_bar_drag_rect->hide();
3289 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3290 _editor->session()->locations()->add (newloc, true);
3291 XMLNode &after = _editor->session()->locations()->get_state();
3292 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3293 _editor->commit_reversible_command ();
3297 case CreateTransportMarker:
3298 // popup menu to pick loop or punch
3299 _editor->new_transport_marker_context_menu (&event->button, _item);
3303 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3305 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3310 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3312 if (end == max_frames) {
3313 end = _editor->session()->current_end_frame ();
3316 if (start == max_frames) {
3317 start = _editor->session()->current_start_frame ();
3320 switch (_editor->mouse_mode) {
3322 /* find the two markers on either side and then make the selection from it */
3323 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3327 /* find the two markers on either side of the click and make the range out of it */
3328 _editor->selection->set (start, end);
3337 _editor->stop_canvas_autoscroll ();
3343 RangeMarkerBarDrag::update_item (Location* location)
3345 double const x1 = _editor->frame_to_pixel (location->start());
3346 double const x2 = _editor->frame_to_pixel (location->end());
3348 _drag_rect->property_x1() = x1;
3349 _drag_rect->property_x2() = x2;
3353 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3355 Drag::start_grab (event, _editor->zoom_cursor);
3356 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3360 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3365 nframes64_t const pf = adjusted_current_frame (event);
3367 if (pf == last_pointer_frame()) {
3371 nframes64_t grab = grab_frame ();
3372 _editor->snap_to_with_modifier (grab, event);
3374 /* base start and end on initial click position */
3386 _editor->zoom_rect->show();
3387 _editor->zoom_rect->raise_to_top();
3390 _editor->reposition_zoom_rect(start, end);
3392 _editor->show_verbose_time_cursor (pf, 10);
3397 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3399 if (movement_occurred) {
3400 motion (event, false);
3402 if (grab_frame() < last_pointer_frame()) {
3403 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3405 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3408 _editor->temporal_zoom_to_frame (false, grab_frame());
3410 temporal_zoom_step (false);
3411 center_screen (grab_frame());
3415 _editor->zoom_rect->hide();
3418 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3421 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3422 region = &cnote->region_view();
3426 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3428 Drag::start_grab (event);
3431 drag_delta_note = 0;
3436 event_x = current_pointer_x();
3437 event_y = current_pointer_y();
3439 _item->property_parent().get_value()->w2i(event_x, event_y);
3441 last_x = region->snap_to_pixel(event_x);
3444 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3446 if (!(was_selected = cnote->selected())) {
3448 /* tertiary-click means extend selection - we'll do that on button release,
3449 so don't add it here, because otherwise we make it hard to figure
3450 out the "extend-to" range.
3453 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3456 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3459 region->note_selected (cnote, true);
3461 region->unique_select (cnote);
3468 NoteDrag::motion (GdkEvent*, bool)
3470 MidiStreamView* streamview = region->midi_stream_view();
3474 event_x = current_pointer_x();
3475 event_y = current_pointer_y();
3477 _item->property_parent().get_value()->w2i(event_x, event_y);
3479 event_x = region->snap_to_pixel(event_x);
3481 double dx = event_x - last_x;
3482 double dy = event_y - last_y;
3487 // Snap to note rows
3489 if (abs (dy) < streamview->note_height()) {
3492 int8_t this_delta_note;
3494 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3496 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3498 drag_delta_note -= this_delta_note;
3499 dy = streamview->note_height() * this_delta_note;
3500 last_y = last_y + dy;
3504 region->move_selection (dx, dy);
3506 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3508 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3509 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3510 _editor->show_verbose_canvas_cursor_with (buf);
3515 NoteDrag::finished (GdkEvent* ev, bool moved)
3517 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3520 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3523 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3525 region->note_deselected (cnote);
3528 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3529 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3531 if (!extend && !add && region->selection_size() > 1) {
3532 region->unique_select(cnote);
3533 } else if (extend) {
3534 region->note_selected (cnote, true, true);
3536 /* it was added during button press */
3541 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3545 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3548 , _nothing_to_drag (false)
3550 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3553 _line = _atav->line ();
3557 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3559 Drag::start_grab (event, cursor);
3561 list<ControlPoint*> points;
3563 XMLNode* state = &_line->get_state ();
3565 if (_ranges.empty()) {
3567 uint32_t const N = _line->npoints ();
3568 for (uint32_t i = 0; i < N; ++i) {
3569 points.push_back (_line->nth (i));
3574 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3575 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3577 /* fade into and out of the region that we're dragging;
3578 64 samples length plucked out of thin air.
3580 nframes64_t const h = (j->start + j->end) / 2;
3581 nframes64_t a = j->start + 64;
3585 nframes64_t b = j->end - 64;
3590 the_list->add (j->start, the_list->eval (j->start));
3591 _line->add_always_in_view (j->start);
3592 the_list->add (a, the_list->eval (a));
3593 _line->add_always_in_view (a);
3594 the_list->add (b, the_list->eval (b));
3595 _line->add_always_in_view (b);
3596 the_list->add (j->end, the_list->eval (j->end));
3597 _line->add_always_in_view (j->end);
3600 uint32_t const N = _line->npoints ();
3601 for (uint32_t i = 0; i < N; ++i) {
3603 ControlPoint* p = _line->nth (i);
3605 list<AudioRange>::const_iterator j = _ranges.begin ();
3606 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3610 if (j != _ranges.end()) {
3611 points.push_back (p);
3616 if (points.empty()) {
3617 _nothing_to_drag = true;
3621 _line->start_drag_multiple (points, 1 - (current_pointer_y() / _line->height ()), state);
3625 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3627 if (_nothing_to_drag) {
3631 float const f = 1 - (current_pointer_y() / _line->height());
3633 /* we are ignoring x position for this drag, so we can just pass in 0 */
3634 _line->drag_motion (0, f, false);
3638 AutomationRangeDrag::finished (GdkEvent* event, bool)
3640 if (_nothing_to_drag) {
3644 motion (event, false);
3646 _line->clear_always_in_view ();