enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / editor_drag.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <stdint.h>
25 #include <algorithm>
26
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
30
31 #include "gtkmm2ext/utils.h"
32
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
42
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
45
46 #include "editor.h"
47 #include "pbd/i18n.h"
48 #include "keyboard.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
62 #include "debug.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "ui_config.h"
68 #include "verbose_cursor.h"
69
70 using namespace std;
71 using namespace ARDOUR;
72 using namespace PBD;
73 using namespace Gtk;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
77
78 using Gtkmm2ext::Keyboard;
79
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
81
82 DragManager::DragManager (Editor* e)
83         : _editor (e)
84         , _ending (false)
85         , _current_pointer_x (0.0)
86         , _current_pointer_y (0.0)
87         , _current_pointer_frame (0)
88         , _old_follow_playhead (false)
89 {
90 }
91
92 DragManager::~DragManager ()
93 {
94         abort ();
95 }
96
97 /** Call abort for each active drag */
98 void
99 DragManager::abort ()
100 {
101         _ending = true;
102
103         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
104                 (*i)->abort ();
105                 delete *i;
106         }
107
108         if (!_drags.empty ()) {
109                 _editor->set_follow_playhead (_old_follow_playhead, false);
110         }
111
112         _drags.clear ();
113         _editor->abort_reversible_command();
114
115         _ending = false;
116 }
117
118 void
119 DragManager::add (Drag* d)
120 {
121         d->set_manager (this);
122         _drags.push_back (d);
123 }
124
125 void
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
127 {
128         d->set_manager (this);
129         _drags.push_back (d);
130         start_grab (e, c);
131 }
132
133 void
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
135 {
136         /* Prevent follow playhead during the drag to be nice to the user */
137         _old_follow_playhead = _editor->follow_playhead ();
138         _editor->set_follow_playhead (false);
139
140         _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
141
142         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143                 (*i)->start_grab (e, c);
144         }
145 }
146
147 /** Call end_grab for each active drag.
148  *  @return true if any drag reported movement having occurred.
149  */
150 bool
151 DragManager::end_grab (GdkEvent* e)
152 {
153         _ending = true;
154
155         bool r = false;
156         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157                 bool const t = (*i)->end_grab (e);
158                 if (t) {
159                         r = true;
160                 }
161                 delete *i;
162         }
163
164         _drags.clear ();
165
166         _ending = false;
167
168         _editor->set_follow_playhead (_old_follow_playhead, false);
169
170         return r;
171 }
172
173 void
174 DragManager::mark_double_click ()
175 {
176         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177                 (*i)->set_double_click (true);
178         }
179 }
180
181 bool
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
183 {
184         bool r = false;
185
186         /* calling this implies that we expect the event to have canvas
187          * coordinates
188          *
189          * Can we guarantee that this is true?
190          */
191
192         _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
193
194         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195                 bool const t = (*i)->motion_handler (e, from_autoscroll);
196                 /* run all handlers; return true if at least one of them
197                    returns true (indicating that the event has been handled).
198                 */
199                 if (t) {
200                         r = true;
201                 }
202
203         }
204
205         return r;
206 }
207
208 bool
209 DragManager::have_item (ArdourCanvas::Item* i) const
210 {
211         list<Drag*>::const_iterator j = _drags.begin ();
212         while (j != _drags.end() && (*j)->item () != i) {
213                 ++j;
214         }
215
216         return j != _drags.end ();
217 }
218
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
220         : _editor (e)
221         , _drags (0)
222         , _item (i)
223         , _pointer_frame_offset (0)
224         , _x_constrained (false)
225         , _y_constrained (false)
226         , _was_rolling (false)
227         , _trackview_only (trackview_only)
228         , _move_threshold_passed (false)
229         , _starting_point_passed (false)
230         , _initially_vertical (false)
231         , _was_double_click (false)
232         , _grab_x (0.0)
233         , _grab_y (0.0)
234         , _last_pointer_x (0.0)
235         , _last_pointer_y (0.0)
236         , _raw_grab_frame (0)
237         , _grab_frame (0)
238         , _last_pointer_frame (0)
239         , _snap_delta (0)
240 {
241
242 }
243
244 void
245 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
246 {
247         _item->ungrab ();
248         _item = new_item;
249
250         if (!_cursor_ctx) {
251                 _cursor_ctx = CursorContext::create (*_editor, cursor);
252         } else {
253                 _cursor_ctx->change (cursor);
254         }
255
256         _item->grab ();
257 }
258
259 void
260 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
261 {
262
263         /* we set up x/y dragging constraints on first move */
264
265         _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
266
267         setup_pointer_frame_offset ();
268         _grab_frame = adjusted_frame (_raw_grab_frame, event);
269         _last_pointer_frame = _grab_frame;
270         _last_pointer_x = _grab_x;
271
272         if (_trackview_only) {
273                 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
274         }
275
276         _last_pointer_y = _grab_y;
277
278         _item->grab ();
279
280         if (!_editor->cursors()->is_invalid (cursor)) {
281                 /* CAIROCANVAS need a variant here that passes *cursor */
282                 _cursor_ctx = CursorContext::create (*_editor, cursor);
283         }
284
285         if (_editor->session() && _editor->session()->transport_rolling()) {
286                 _was_rolling = true;
287         } else {
288                 _was_rolling = false;
289         }
290
291         switch (_editor->snap_type()) {
292         case SnapToRegionStart:
293         case SnapToRegionEnd:
294         case SnapToRegionSync:
295         case SnapToRegionBoundary:
296                 _editor->build_region_boundary_cache ();
297                 break;
298         default:
299                 break;
300         }
301 }
302
303 /** Call to end a drag `successfully'.  Ungrabs item and calls
304  *  subclass' finished() method.
305  *
306  *  @param event GDK event, or 0.
307  *  @return true if some movement occurred, otherwise false.
308  */
309 bool
310 Drag::end_grab (GdkEvent* event)
311 {
312         _editor->stop_canvas_autoscroll ();
313
314         _item->ungrab ();
315
316         finished (event, _move_threshold_passed);
317
318         _editor->verbose_cursor()->hide ();
319         _cursor_ctx.reset();
320
321         return _move_threshold_passed;
322 }
323
324 framepos_t
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
326 {
327         framepos_t pos = 0;
328
329         if (f > _pointer_frame_offset) {
330                 pos = f - _pointer_frame_offset;
331         }
332
333         if (snap) {
334                 _editor->snap_to_with_modifier (pos, event);
335         }
336
337         return pos;
338 }
339
340 framepos_t
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
342 {
343         return adjusted_frame (_drags->current_pointer_frame (), event, snap);
344 }
345
346 frameoffset_t
347 Drag::snap_delta (guint state) const
348 {
349         if (ArdourKeyboard::indicates_snap_delta (state)) {
350                 return _snap_delta;
351         }
352
353         return 0;
354 }
355
356 double
357 Drag::current_pointer_x() const
358 {
359         return _drags->current_pointer_x ();
360 }
361
362 double
363 Drag::current_pointer_y () const
364 {
365         if (!_trackview_only) {
366                 return _drags->current_pointer_y ();
367         }
368
369         return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
370 }
371
372 void
373 Drag::setup_snap_delta (framepos_t pos)
374 {
375         framepos_t temp = pos;
376         _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
377         _snap_delta = temp - pos;
378 }
379
380 bool
381 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
382 {
383         /* check to see if we have moved in any way that matters since the last motion event */
384         if (_move_threshold_passed &&
385             (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
386             (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
387                 return false;
388         }
389
390         pair<framecnt_t, int> const threshold = move_threshold ();
391
392         bool const old_move_threshold_passed = _move_threshold_passed;
393
394         if (!_move_threshold_passed) {
395
396                 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
397                 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
398
399                 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
400         }
401
402         if (active (_editor->mouse_mode) && _move_threshold_passed) {
403
404                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
405
406                         if (old_move_threshold_passed != _move_threshold_passed) {
407
408                                 /* just changed */
409
410                                 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
411                                         _initially_vertical = true;
412                                 } else {
413                                         _initially_vertical = false;
414                                 }
415                                 /** check constraints for this drag.
416                                  *  Note that the current convention is to use "contains" for
417                                  *  key modifiers during motion and "equals" when initiating a drag.
418                                  *  In this case we haven't moved yet, so "equals" applies here.
419                                  */
420                                 if (Config->get_edit_mode() != Lock) {
421                                         if (event->motion.state & Gdk::BUTTON2_MASK) {
422                                                 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
423                                                 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
424                                                         _x_constrained = false;
425                                                         _y_constrained = true;
426                                                 } else {
427                                                         _x_constrained = true;
428                                                         _y_constrained = false;
429                                                 }
430                                         } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
431                                                 // if dragging normally, the motion is constrained to the first direction of movement.
432                                                 if (_initially_vertical) {
433                                                         _x_constrained = true;
434                                                         _y_constrained = false;
435                                                 } else {
436                                                         _x_constrained = false;
437                                                         _y_constrained = true;
438                                                 }
439                                         }
440                                 } else {
441                                         if (event->button.state & Gdk::BUTTON2_MASK) {
442                                                 _x_constrained = false;
443                                         } else {
444                                                 _x_constrained = true;
445                                         }
446                                         _y_constrained = false;
447                                 }
448                         }
449
450                         if (!from_autoscroll) {
451                                 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
452                         }
453
454                         if (!_editor->autoscroll_active() || from_autoscroll) {
455
456
457                                 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
458
459                                 motion (event, first_move && !_starting_point_passed);
460
461                                 if (first_move && !_starting_point_passed) {
462                                         _starting_point_passed = true;
463                                 }
464
465                                 _last_pointer_x = _drags->current_pointer_x ();
466                                 _last_pointer_y = current_pointer_y ();
467                                 _last_pointer_frame = adjusted_current_frame (event, false);
468                         }
469
470                         return true;
471                 }
472         }
473
474         return false;
475 }
476
477 /** Call to abort a drag.  Ungrabs item and calls subclass's aborted () */
478 void
479 Drag::abort ()
480 {
481         if (_item) {
482                 _item->ungrab ();
483         }
484
485         aborted (_move_threshold_passed);
486
487         _editor->stop_canvas_autoscroll ();
488         _editor->verbose_cursor()->hide ();
489 }
490
491 void
492 Drag::show_verbose_cursor_time (framepos_t frame)
493 {
494         _editor->verbose_cursor()->set_time (frame);
495         _editor->verbose_cursor()->show ();
496 }
497
498 void
499 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
500 {
501         _editor->verbose_cursor()->set_duration (start, end);
502         _editor->verbose_cursor()->show ();
503 }
504
505 void
506 Drag::show_verbose_cursor_text (string const & text)
507 {
508         _editor->verbose_cursor()->set (text);
509         _editor->verbose_cursor()->show ();
510 }
511
512 boost::shared_ptr<Region>
513 Drag::add_midi_region (MidiTimeAxisView* view, bool commit, const int32_t sub_num)
514 {
515         if (_editor->session()) {
516                 const TempoMap& map (_editor->session()->tempo_map());
517                 framecnt_t pos = grab_frame();
518                 /* not that the frame rate used here can be affected by pull up/down which
519                    might be wrong.
520                 */
521                 framecnt_t len = map.frame_at_beat (map.beat_at_frame (pos) + 1.0) - pos;
522                 return view->add_region (grab_frame(), len, commit, sub_num);
523         }
524
525         return boost::shared_ptr<Region>();
526 }
527
528 struct PresentationInfoTimeAxisViewSorter {
529         bool operator() (TimeAxisView* a, TimeAxisView* b) {
530                 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
531         }
532 };
533
534 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
535         : Drag (e, i)
536         , _primary (p)
537         , _ntracks (0)
538 {
539         _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
540
541         /* Make a list of tracks to refer to during the drag; we include hidden tracks,
542            as some of the regions we are dragging may be on such tracks.
543         */
544
545         TrackViewList track_views = _editor->track_views;
546         track_views.sort (PresentationInfoTimeAxisViewSorter ());
547
548         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
549                 _time_axis_views.push_back (*i);
550
551                 TimeAxisView::Children children_list = (*i)->get_child_list ();
552                 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
553                         _time_axis_views.push_back (j->get());
554                 }
555         }
556
557         /* the list of views can be empty at this point if this is a region list-insert drag
558          */
559
560         for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
561                 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
562         }
563
564         RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
565 }
566
567 void
568 RegionDrag::region_going_away (RegionView* v)
569 {
570         list<DraggingView>::iterator i = _views.begin ();
571         while (i != _views.end() && i->view != v) {
572                 ++i;
573         }
574
575         if (i != _views.end()) {
576                 _views.erase (i);
577         }
578 }
579
580 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
581  *  or -1 if it is not found.
582  */
583 int
584 RegionDrag::find_time_axis_view (TimeAxisView* t) const
585 {
586         int i = 0;
587         int const N = _time_axis_views.size ();
588         while (i < N && _time_axis_views[i] != t) {
589                 ++i;
590         }
591
592         if (_time_axis_views[i] != t) {
593                 return -1;
594         }
595
596         return i;
597 }
598
599 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
600         : RegionDrag (e, i, p, v)
601         , _brushing (b)
602         , _ignore_video_lock (false)
603         , _total_x_delta (0)
604         , _last_pointer_time_axis_view (0)
605         , _last_pointer_layer (0)
606         , _ndropzone (0)
607         , _pdropzone (0)
608         , _ddropzone (0)
609 {
610         DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
611 }
612
613 void
614 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
615 {
616         Drag::start_grab (event, cursor);
617         setup_snap_delta (_last_frame_position);
618
619         show_verbose_cursor_time (_last_frame_position);
620
621         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
622         if (tv.first) {
623                 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
624                 assert(_last_pointer_time_axis_view >= 0);
625                 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
626         }
627
628         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
629                 _ignore_video_lock = true;
630         }
631
632         if (_brushing) {
633                 /* cross track dragging seems broken here. disabled for now. */
634                 _y_constrained = true;
635         }
636 }
637
638 double
639 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
640 {
641         /* compute the amount of pointer motion in frames, and where
642            the region would be if we moved it by that much.
643         */
644         *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
645
646         framepos_t sync_frame;
647         framecnt_t sync_offset;
648         int32_t sync_dir;
649
650         sync_offset = _primary->region()->sync_offset (sync_dir);
651
652         /* we don't handle a sync point that lies before zero.
653          */
654         if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
655
656                 framecnt_t const sd = snap_delta (event->button.state);
657                 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
658
659                 _editor->snap_to_with_modifier (sync_frame, event);
660
661                 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
662
663         } else {
664                 *pending_region_position = _last_frame_position;
665         }
666
667         if (*pending_region_position > max_framepos - _primary->region()->length()) {
668                 *pending_region_position = _last_frame_position;
669         }
670
671         double dx = 0;
672
673         bool const x_move_allowed = !_x_constrained;
674
675         if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
676
677                 /* x movement since last time (in pixels) */
678                 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
679
680                 /* total x movement */
681                 framecnt_t total_dx = *pending_region_position;
682                 if (regions_came_from_canvas()) {
683                         total_dx = total_dx - grab_frame ();
684                 }
685
686                 /* check that no regions have gone off the start of the session */
687                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
688                         if ((i->view->region()->position() + total_dx) < 0) {
689                                 dx = 0;
690                                 *pending_region_position = _last_frame_position;
691                                 break;
692                         }
693                 }
694
695         }
696
697         return dx;
698 }
699
700 int
701 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
702 {
703         if (delta == 0) {
704                 return start;
705         }
706
707         const int tavsize  = _time_axis_views.size();
708         const int dt = delta > 0 ? +1 : -1;
709         int current  = start;
710         int target   = start + delta - skip;
711
712         assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
713         assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
714
715         while (current >= 0 && current != target) {
716                 current += dt;
717                 if (current < 0 && dt < 0) {
718                         break;
719                 }
720                 if (current >= tavsize && dt > 0) {
721                         break;
722                 }
723                 if (current < 0 || current >= tavsize) {
724                         continue;
725                 }
726
727                 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
728                 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
729                         target += dt;
730                 }
731
732                 if (distance_only && current == start + delta) {
733                         break;
734                 }
735         }
736         return target;
737 }
738
739 bool
740 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
741 {
742         if (_y_constrained) {
743                 return false;
744         }
745
746         const int tavsize  = _time_axis_views.size();
747         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
748                 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
749                 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
750
751                 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
752                         /* already in the drop zone */
753                         if (delta_track >= 0) {
754                                 /* downward motion - OK if others are still not in the dropzone */
755                                 continue;
756                         }
757
758                 }
759
760                 if (n < 0) {
761                         /* off the top */
762                         return false;
763                 } else if (n >= tavsize) {
764                         /* downward motion into drop zone. That's fine. */
765                         continue;
766                 }
767
768                 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
769                 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
770                         /* not a track, or the wrong type */
771                         return false;
772                 }
773
774                 double const l = i->layer + delta_layer;
775
776                 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
777                    mode to allow the user to place a region below another on layer 0.
778                 */
779                 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
780                         /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
781                            If it has, the layers will be munged later anyway, so it's ok.
782                         */
783                         return false;
784                 }
785         }
786
787         /* all regions being dragged are ok with this change */
788         return true;
789 }
790
791 struct DraggingViewSorter {
792         bool operator() (const DraggingView& a, const DraggingView& b) {
793                 return a.time_axis_view < b.time_axis_view;
794         }
795 };
796
797 void
798 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
799 {
800         double delta_layer = 0;
801         int delta_time_axis_view = 0;
802         int current_pointer_time_axis_view = -1;
803
804         assert (!_views.empty ());
805
806         /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
807
808         /* Find the TimeAxisView that the pointer is now over */
809         const double cur_y = current_pointer_y ();
810         pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
811         TimeAxisView* tv = r.first;
812
813         if (!tv && cur_y < 0) {
814                 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
815                 return;
816         }
817
818         /* find drop-zone y-position */
819         Coord last_track_bottom_edge;
820         last_track_bottom_edge = 0;
821         for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
822                 if (!(*t)->hidden()) {
823                         last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
824                         break;
825                 }
826         }
827
828         if (tv && tv->view()) {
829                 /* the mouse is over a track */
830                 double layer = r.second;
831
832                 if (first_move && tv->view()->layer_display() == Stacked) {
833                         tv->view()->set_layer_display (Expanded);
834                 }
835
836                 /* Here's the current pointer position in terms of time axis view and layer */
837                 current_pointer_time_axis_view = find_time_axis_view (tv);
838                 assert(current_pointer_time_axis_view >= 0);
839
840                 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
841
842                 /* Work out the change in y */
843
844                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
845                 if (!rtv || !rtv->is_track()) {
846                         /* ignore non-tracks early on. we can't move any regions on them */
847                 } else if (_last_pointer_time_axis_view < 0) {
848                         /* Was in the drop-zone, now over a track.
849                          * Hence it must be an upward move (from the bottom)
850                          *
851                          * track_index is still -1, so delta must be set to
852                          * move up the correct number of tracks from the bottom.
853                          *
854                          * This is necessary because steps may be skipped if
855                          * the bottom-most track is not a valid target and/or
856                          * if there are hidden tracks at the bottom.
857                          * Hence the initial offset (_ddropzone) as well as the
858                          * last valid pointer position (_pdropzone) need to be
859                          * taken into account.
860                          */
861                         delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
862                 } else {
863                         delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
864                 }
865
866                 /* TODO needs adjustment per DraggingView,
867                  *
868                  * e.g. select one region on the top-layer of a track
869                  * and one region which is at the bottom-layer of another track
870                  * drag both.
871                  *
872                  * Indicated drop-zones and layering is wrong.
873                  * and may infer additional layers on the target-track
874                  * (depending how many layers the original track had).
875                  *
876                  * Or select two regions (different layers) on a same track,
877                  * move across a non-layer track.. -> layering info is lost.
878                  * on drop either of the regions may be on top.
879                  *
880                  * Proposed solution: screw it :) well,
881                  *   don't use delta_layer, use an absolute value
882                  *   1) remember DraggingView's layer  as float 0..1  [current layer / all layers of source]
883                  *   2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
884                  *   3) iterate over all DraggingView, find the one that is over the track with most layers
885                  *   4) proportionally scale layer to layers available on target
886                  */
887                 delta_layer = current_pointer_layer - _last_pointer_layer;
888
889         }
890         /* for automation lanes, there is a TimeAxisView but no ->view()
891          * if (!tv) -> dropzone
892          */
893         else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
894                 /* Moving into the drop-zone.. */
895                 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
896                 /* delta_time_axis_view may not be sufficient to move into the DZ
897                  * the mouse may enter it, but it may not be a valid move due to
898                  * constraints.
899                  *
900                  * -> remember the delta needed to move into the dropzone
901                  */
902                 _ddropzone = delta_time_axis_view;
903                 /* ..but subtract hidden tracks (or routes) at the bottom.
904                  * we silently move mover them
905                  */
906                 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
907                               - _time_axis_views.size();
908         }
909         else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
910                 /* move around inside the zone.
911                  * This allows to move further down until all regions are in the zone.
912                  */
913                 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
914                 assert(ptr_y >= last_track_bottom_edge);
915                 assert(_ddropzone > 0);
916
917                 /* calculate mouse position in 'tracks' below last track. */
918                 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
919                 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
920
921                 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
922                         // move further down
923                         delta_time_axis_view =  dzpos - _pdropzone;
924                 } else if (dzpos < _pdropzone && _ndropzone > 0) {
925                         // move up inside the DZ
926                         delta_time_axis_view =  dzpos - _pdropzone;
927                 }
928         }
929
930         /* Work out the change in x */
931         framepos_t pending_region_position;
932         double const x_delta = compute_x_delta (event, &pending_region_position);
933         _last_frame_position = pending_region_position;
934
935         /* calculate hidden tracks in current y-axis delta */
936         int delta_skip = 0;
937         if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
938                 /* The mouse is more than one track below the dropzone.
939                  * distance calculation is not needed (and would not work, either
940                  * because the dropzone is "packed").
941                  *
942                  * Except when [partially] moving regions out of dropzone in a large step.
943                  * (the mouse may or may not remain in the DZ)
944                  * Hidden tracks at the bottom of the TAV need to be skipped.
945                  *
946                  * This also handles the case if the mouse entered the DZ
947                  * in a large step (exessive delta), either due to fast-movement,
948                  * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
949                  */
950                 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
951                         const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
952                         assert(dt <= 0);
953                         delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
954                                 -_time_axis_views.size() - dt;
955                 }
956         }
957         else if (_last_pointer_time_axis_view < 0) {
958                 /* Moving out of the zone. Check for hidden tracks at the bottom. */
959                 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
960                              -_time_axis_views.size() - delta_time_axis_view;
961         } else {
962                 /* calculate hidden tracks that are skipped by the pointer movement */
963                 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
964                              - _last_pointer_time_axis_view
965                              - delta_time_axis_view;
966         }
967
968         /* Verify change in y */
969         if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
970                 /* this y movement is not allowed, so do no y movement this time */
971                 delta_time_axis_view = 0;
972                 delta_layer = 0;
973                 delta_skip = 0;
974         }
975
976         if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
977                 /* haven't reached next snap point, and we're not switching
978                    trackviews nor layers. nothing to do.
979                 */
980                 return;
981         }
982
983         typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
984         PlaylistDropzoneMap playlist_dropzone_map;
985         _ndropzone = 0; // number of elements currently in the dropzone
986
987         if (first_move) {
988                 /* sort views by time_axis.
989                  * This retains track order in the dropzone, regardless
990                  * of actual selection order
991                  */
992                 _views.sort (DraggingViewSorter());
993
994                 /* count number of distinct tracks of all regions
995                  * being dragged, used for dropzone.
996                  */
997                 int prev_track = -1;
998                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
999                         if (i->time_axis_view != prev_track) {
1000                                 prev_track = i->time_axis_view;
1001                                 ++_ntracks;
1002                         }
1003                 }
1004 #ifndef NDEBUG
1005                 int spread =
1006                         _views.back().time_axis_view -
1007                         _views.front().time_axis_view;
1008
1009                 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1010                           -  _views.back().time_axis_view;
1011
1012                 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1013 #endif
1014         }
1015
1016         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1017
1018                 RegionView* rv = i->view;
1019                 double y_delta;
1020
1021                 y_delta = 0;
1022
1023                 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1024                         continue;
1025                 }
1026
1027                 if (first_move) {
1028                         rv->drag_start ();
1029
1030                         /* reparent the regionview into a group above all
1031                          * others
1032                          */
1033
1034                         ArdourCanvas::Item* rvg = rv->get_canvas_group();
1035                         Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1036                         Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1037                         rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1038                         /* move the item so that it continues to appear at the
1039                            same location now that its parent has changed.
1040                            */
1041                         rvg->move (rv_canvas_offset - dmg_canvas_offset);
1042                 }
1043
1044                 /* If we have moved tracks, we'll fudge the layer delta so that the
1045                    region gets moved back onto layer 0 on its new track; this avoids
1046                    confusion when dragging regions from non-zero layers onto different
1047                    tracks.
1048                 */
1049                 double this_delta_layer = delta_layer;
1050                 if (delta_time_axis_view != 0) {
1051                         this_delta_layer = - i->layer;
1052                 }
1053
1054                 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1055
1056                 int track_index = i->time_axis_view + this_delta_time_axis_view;
1057                 assert(track_index >= 0);
1058
1059                 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1060                         /* Track is in the Dropzone */
1061
1062                         i->time_axis_view = track_index;
1063                         assert(i->time_axis_view >= (int) _time_axis_views.size());
1064                         if (cur_y >= 0) {
1065
1066                                 double yposition = 0;
1067                                 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1068                                 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1069                                 ++_ndropzone;
1070
1071                                 /* store index of each new playlist as a negative count, starting at -1 */
1072
1073                                 if (pdz == playlist_dropzone_map.end()) {
1074                                         /* compute where this new track (which doesn't exist yet) will live
1075                                            on the y-axis.
1076                                         */
1077                                         yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1078
1079                                         /* How high is this region view ? */
1080
1081                                         boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1082                                         ArdourCanvas::Rect bbox;
1083
1084                                         if (obbox) {
1085                                                 bbox = obbox.get ();
1086                                         }
1087
1088                                         last_track_bottom_edge += bbox.height();
1089
1090                                         playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1091
1092                                 } else {
1093                                         yposition = pdz->second;
1094                                 }
1095
1096                                 /* values are zero or negative, hence the use of min() */
1097                                 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1098                         }
1099
1100                 } else {
1101
1102                         /* The TimeAxisView that this region is now over */
1103                         TimeAxisView* current_tv = _time_axis_views[track_index];
1104
1105                         /* Ensure it is moved from stacked -> expanded if appropriate */
1106                         if (current_tv->view()->layer_display() == Stacked) {
1107                                 current_tv->view()->set_layer_display (Expanded);
1108                         }
1109
1110                         /* We're only allowed to go -ve in layer on Expanded views */
1111                         if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1112                                 this_delta_layer = - i->layer;
1113                         }
1114
1115                         /* Set height */
1116                         rv->set_height (current_tv->view()->child_height ());
1117
1118                         /* Update show/hidden status as the region view may have come from a hidden track,
1119                            or have moved to one.
1120                         */
1121                         if (current_tv->hidden ()) {
1122                                 rv->get_canvas_group()->hide ();
1123                         } else {
1124                                 rv->get_canvas_group()->show ();
1125                         }
1126
1127                         /* Update the DraggingView */
1128                         i->time_axis_view = track_index;
1129                         i->layer += this_delta_layer;
1130
1131                         if (_brushing) {
1132                                 _editor->mouse_brush_insert_region (rv, pending_region_position);
1133                         } else {
1134                                 Duple track_origin;
1135
1136                                 /* Get the y coordinate of the top of the track that this region is now over */
1137                                 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1138
1139                                 /* And adjust for the layer that it should be on */
1140                                 StreamView* cv = current_tv->view ();
1141                                 switch (cv->layer_display ()) {
1142                                 case Overlaid:
1143                                         break;
1144                                 case Stacked:
1145                                         track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1146                                         break;
1147                                 case Expanded:
1148                                         track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1149                                         break;
1150                                 }
1151
1152                                 /* need to get the parent of the regionview
1153                                  * canvas group and get its position in
1154                                  * equivalent coordinate space as the trackview
1155                                  * we are now dragging over.
1156                                  */
1157
1158                                 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1159
1160                         }
1161                 }
1162
1163                 /* Now move the region view */
1164                 rv->move (x_delta, y_delta);
1165
1166         } /* foreach region */
1167
1168         _total_x_delta += x_delta;
1169
1170         if (x_delta != 0 && !_brushing) {
1171                 show_verbose_cursor_time (_last_frame_position);
1172         }
1173
1174         /* keep track of pointer movement */
1175         if (tv) {
1176                 /* the pointer is currently over a time axis view */
1177
1178                 if (_last_pointer_time_axis_view < 0) {
1179                         /* last motion event was not over a time axis view
1180                          * or last y-movement out of the dropzone was not valid
1181                          */
1182                         int dtz = 0;
1183                         if (delta_time_axis_view < 0) {
1184                                 /* in the drop zone, moving up */
1185
1186                                 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1187                                  * We do not use negative _last_pointer_time_axis_view because
1188                                  * the dropzone is "packed" (the actual track offset is ignored)
1189                                  *
1190                                  * As opposed to the actual number
1191                                  * of elements in the dropzone (_ndropzone)
1192                                  * _pdropzone is not constrained. This is necessary
1193                                  * to allow moving multiple regions with y-distance
1194                                  * into the DZ.
1195                                  *
1196                                  * There can be 0 elements in the dropzone,
1197                                  * even though the drag-pointer is inside the DZ.
1198                                  *
1199                                  * example:
1200                                  * [ Audio-track, Midi-track, Audio-track, DZ ]
1201                                  * move regions from both audio tracks at the same time into the
1202                                  * DZ by grabbing the region in the bottom track.
1203                                  */
1204                                 assert(current_pointer_time_axis_view >= 0);
1205                                 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1206                                 _pdropzone -= dtz;
1207                         }
1208
1209                         /* only move out of the zone if the movement is OK */
1210                         if (_pdropzone == 0 && delta_time_axis_view != 0) {
1211                                 assert(delta_time_axis_view < 0);
1212                                 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1213                                 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1214                                  * the current position can be calculated as follows:
1215                                  */
1216                                 // a well placed oofus attack can still throw this off.
1217                                 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1218                                 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1219                         }
1220                 } else {
1221                         /* last motion event was also over a time axis view */
1222                         _last_pointer_time_axis_view += delta_time_axis_view;
1223                         assert(_last_pointer_time_axis_view >= 0);
1224                 }
1225
1226         } else {
1227
1228                 /* the pointer is not over a time axis view */
1229                 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1230                 _pdropzone += delta_time_axis_view - delta_skip;
1231                 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1232         }
1233
1234         _last_pointer_layer += delta_layer;
1235 }
1236
1237 void
1238 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1239 {
1240         if (_copy && first_move) {
1241                 if (_x_constrained && !_brushing) {
1242                         _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1243                 } else if (!_brushing) {
1244                         _editor->begin_reversible_command (Operations::region_copy);
1245                 } else if (_brushing) {
1246                         _editor->begin_reversible_command (Operations::drag_region_brush);
1247                 }
1248                 /* duplicate the regionview(s) and region(s) */
1249
1250                 list<DraggingView> new_regionviews;
1251
1252                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1253
1254                         RegionView* rv = i->view;
1255                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1256                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1257
1258                         const boost::shared_ptr<const Region> original = rv->region();
1259                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true
1260                                                                                        , _editor->get_grid_music_divisions (event->button.state));
1261                         /* need to set this so that the drop zone code can work. This doesn't
1262                            actually put the region into the playlist, but just sets a weak pointer
1263                            to it.
1264                         */
1265                         region_copy->set_playlist (original->playlist());
1266
1267                         RegionView* nrv;
1268                         if (arv) {
1269                                 boost::shared_ptr<AudioRegion> audioregion_copy
1270                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1271
1272                                 nrv = new AudioRegionView (*arv, audioregion_copy);
1273                         } else if (mrv) {
1274                                 boost::shared_ptr<MidiRegion> midiregion_copy
1275                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1276                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
1277                         } else {
1278                                 continue;
1279                         }
1280
1281                         nrv->get_canvas_group()->show ();
1282                         new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1283
1284                         /* swap _primary to the copy */
1285
1286                         if (rv == _primary) {
1287                                 _primary = nrv;
1288                         }
1289
1290                         /* ..and deselect the one we copied */
1291
1292                         rv->set_selected (false);
1293                 }
1294
1295                 if (!new_regionviews.empty()) {
1296
1297                         /* reflect the fact that we are dragging the copies */
1298
1299                         _views = new_regionviews;
1300
1301                         swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1302                 }
1303
1304         } else if (!_copy && first_move) {
1305                 if (_x_constrained && !_brushing) {
1306                         _editor->begin_reversible_command (_("fixed time region drag"));
1307                 } else if (!_brushing) {
1308                         _editor->begin_reversible_command (Operations::region_drag);
1309                 } else if (_brushing) {
1310                         _editor->begin_reversible_command (Operations::drag_region_brush);
1311                 }
1312         }
1313         RegionMotionDrag::motion (event, first_move);
1314 }
1315
1316 void
1317 RegionMotionDrag::finished (GdkEvent *, bool)
1318 {
1319         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1320                 if (!(*i)->view()) {
1321                         continue;
1322                 }
1323
1324                 if ((*i)->view()->layer_display() == Expanded) {
1325                         (*i)->view()->set_layer_display (Stacked);
1326                 }
1327         }
1328 }
1329
1330 void
1331 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1332 {
1333         RegionMotionDrag::finished (ev, movement_occurred);
1334
1335         if (!movement_occurred) {
1336
1337                 /* just a click */
1338
1339                 if (was_double_click() && !_views.empty()) {
1340                         DraggingView dv = _views.front();
1341                         dv.view->show_region_editor ();
1342
1343                 }
1344
1345                 return;
1346         }
1347
1348         assert (!_views.empty ());
1349
1350         /* We might have hidden region views so that they weren't visible during the drag
1351            (when they have been reparented).  Now everything can be shown again, as region
1352            views are back in their track parent groups.
1353         */
1354         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1355                 i->view->get_canvas_group()->show ();
1356         }
1357
1358         bool const changed_position = (_last_frame_position != _primary->region()->position());
1359         bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1360         framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1361
1362         if (_copy) {
1363
1364                 finished_copy (
1365                         changed_position,
1366                         changed_tracks,
1367                         drag_delta,
1368                         ev->button.state
1369                         );
1370
1371         } else {
1372
1373                 finished_no_copy (
1374                         changed_position,
1375                         changed_tracks,
1376                         drag_delta,
1377                         ev->button.state
1378                         );
1379
1380         }
1381
1382         _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1383 }
1384
1385 RouteTimeAxisView*
1386 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1387 {
1388         /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1389            new track.
1390          */
1391
1392         try {
1393                 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1394                         list<boost::shared_ptr<AudioTrack> > audio_tracks;
1395                         uint32_t output_chan = region->n_channels();
1396                         if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1397                                 output_chan =  _editor->session()->master_out()->n_inputs().n_audio();
1398                         }
1399                         audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1400                         TimeAxisView* tav =_editor->axis_view_from_stripable (audio_tracks.front());
1401                         if (tav) {
1402                                 tav->set_height (original->current_height());
1403                         }
1404                         return dynamic_cast<RouteTimeAxisView*>(tav);
1405                 } else {
1406                         ChanCount one_midi_port (DataType::MIDI, 1);
1407                         list<boost::shared_ptr<MidiTrack> > midi_tracks;
1408                         midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1409                                                                           (ARDOUR::Plugin::PresetRecord*) 0,
1410                                                                           (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1411                         TimeAxisView* tav = _editor->axis_view_from_stripable (midi_tracks.front());
1412                         if (tav) {
1413                                 tav->set_height (original->current_height());
1414                         }
1415                         return dynamic_cast<RouteTimeAxisView*> (tav);
1416                 }
1417         } catch (...) {
1418                 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1419                 return 0;
1420         }
1421 }
1422
1423 void
1424 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state)
1425 {
1426         RegionSelection new_views;
1427         PlaylistSet modified_playlists;
1428         RouteTimeAxisView* new_time_axis_view = 0;
1429
1430         if (_brushing) {
1431                 /* all changes were made during motion event handlers */
1432
1433                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1434                         delete i->view;
1435                 }
1436
1437                 _editor->commit_reversible_command ();
1438                 return;
1439         }
1440
1441         typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1442         PlaylistMapping playlist_mapping;
1443
1444         /* insert the regions into their new playlists */
1445         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1446
1447                 RouteTimeAxisView* dest_rtv = 0;
1448
1449                 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1450                         continue;
1451                 }
1452
1453                 framepos_t where;
1454
1455                 if (changed_position && !_x_constrained) {
1456                         where = i->view->region()->position() - drag_delta;
1457                 } else {
1458                         where = i->view->region()->position();
1459                 }
1460
1461                 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1462                         /* dragged to drop zone */
1463
1464                         PlaylistMapping::iterator pm;
1465
1466                         if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1467                                 /* first region from this original playlist: create a new track */
1468                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1469                                 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1470                                 dest_rtv = new_time_axis_view;
1471                         } else {
1472                                 /* we already created a new track for regions from this playlist, use it */
1473                                 dest_rtv = pm->second;
1474                         }
1475                 } else {
1476                         /* destination time axis view is the one we dragged to */
1477                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1478                 }
1479
1480                 if (dest_rtv != 0) {
1481                         RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1482                                                                             modified_playlists, _editor->get_grid_music_divisions (ev_state));
1483
1484                         if (new_view != 0) {
1485                                 new_views.push_back (new_view);
1486                         }
1487                 }
1488
1489                 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1490                    since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1491                  */
1492
1493                 list<DraggingView>::const_iterator next = i;
1494                 ++next;
1495                 delete i->view;
1496                 i = next;
1497         }
1498
1499         /* If we've created new regions either by copying or moving
1500            to a new track, we want to replace the old selection with the new ones
1501         */
1502
1503         if (new_views.size() > 0) {
1504                 _editor->selection->set (new_views);
1505         }
1506
1507         /* write commands for the accumulated diffs for all our modified playlists */
1508         add_stateful_diff_commands_for_playlists (modified_playlists);
1509
1510         _editor->commit_reversible_command ();
1511 }
1512
1513 void
1514 RegionMoveDrag::finished_no_copy (
1515         bool const changed_position,
1516         bool const changed_tracks,
1517         framecnt_t const drag_delta,
1518         int32_t const ev_state
1519         )
1520 {
1521         RegionSelection new_views;
1522         PlaylistSet modified_playlists;
1523         PlaylistSet frozen_playlists;
1524         set<RouteTimeAxisView*> views_to_update;
1525         RouteTimeAxisView* new_time_axis_view = 0;
1526
1527         typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1528         PlaylistMapping playlist_mapping;
1529
1530         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1531
1532                 RegionView* rv = i->view;
1533                 RouteTimeAxisView* dest_rtv = 0;
1534
1535                 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1536                         ++i;
1537                         continue;
1538                 }
1539
1540                 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1541                         /* dragged to drop zone */
1542
1543                         PlaylistMapping::iterator pm;
1544
1545                         if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1546                                 /* first region from this original playlist: create a new track */
1547                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1548                                 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1549                                 dest_rtv = new_time_axis_view;
1550                         } else {
1551                                 /* we already created a new track for regions from this playlist, use it */
1552                                 dest_rtv = pm->second;
1553                         }
1554
1555                 } else {
1556                         /* destination time axis view is the one we dragged to */
1557                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1558                 }
1559
1560                 assert (dest_rtv);
1561
1562                 double const dest_layer = i->layer;
1563
1564                 views_to_update.insert (dest_rtv);
1565
1566                 framepos_t where;
1567
1568                 if (changed_position && !_x_constrained) {
1569                         where = rv->region()->position() - drag_delta;
1570                 } else {
1571                         where = rv->region()->position();
1572                 }
1573
1574                 if (changed_tracks) {
1575
1576                         /* insert into new playlist */
1577
1578                         RegionView* new_view = insert_region_into_playlist (
1579                                 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1580                                 modified_playlists, _editor->get_grid_music_divisions (ev_state)
1581                                 );
1582
1583                         if (new_view == 0) {
1584                                 ++i;
1585                                 continue;
1586                         }
1587
1588                         new_views.push_back (new_view);
1589
1590                         /* remove from old playlist */
1591
1592                         /* the region that used to be in the old playlist is not
1593                            moved to the new one - we use a copy of it. as a result,
1594                            any existing editor for the region should no longer be
1595                            visible.
1596                         */
1597                         rv->hide_region_editor();
1598
1599
1600                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1601
1602                 } else {
1603
1604                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1605
1606                         /* this movement may result in a crossfade being modified, or a layering change,
1607                            so we need to get undo data from the playlist as well as the region.
1608                         */
1609
1610                         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1611                         if (r.second) {
1612                                 playlist->clear_changes ();
1613                         }
1614
1615                         rv->region()->clear_changes ();
1616
1617                         /*
1618                            motion on the same track. plonk the previously reparented region
1619                            back to its original canvas group (its streamview).
1620                            No need to do anything for copies as they are fake regions which will be deleted.
1621                         */
1622
1623                         rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1624                         rv->get_canvas_group()->set_y_position (i->initial_y);
1625                         rv->drag_end ();
1626
1627                         /* just change the model */
1628                         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1629                                 playlist->set_layer (rv->region(), dest_layer);
1630                         }
1631
1632                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1633
1634                         r = frozen_playlists.insert (playlist);
1635
1636                         if (r.second) {
1637                                 playlist->freeze ();
1638                         }
1639
1640                         rv->region()->set_position (where, _editor->get_grid_music_divisions (ev_state));
1641                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1642                 }
1643
1644                 if (changed_tracks) {
1645
1646                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1647                            was selected in all of them, then removing it from a playlist will have removed all
1648                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1649                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1650                            corresponding regionview, and _views is now empty).
1651
1652                            This could have invalidated any and all iterators into _views.
1653
1654                            The heuristic we use here is: if the region selection is empty, break out of the loop
1655                            here. if the region selection is not empty, then restart the loop because we know that
1656                            we must have removed at least the region(view) we've just been working on as well as any
1657                            that we processed on previous iterations.
1658
1659                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1660                            we can just iterate.
1661                         */
1662
1663
1664                         if (_views.empty()) {
1665                                 break;
1666                         } else {
1667                                 i = _views.begin();
1668                         }
1669
1670                 } else {
1671                         ++i;
1672                 }
1673         }
1674
1675         /* If we've created new regions either by copying or moving
1676            to a new track, we want to replace the old selection with the new ones
1677         */
1678
1679         if (new_views.size() > 0) {
1680                 _editor->selection->set (new_views);
1681         }
1682
1683         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1684                 (*p)->thaw();
1685         }
1686
1687         /* write commands for the accumulated diffs for all our modified playlists */
1688         add_stateful_diff_commands_for_playlists (modified_playlists);
1689         /* applies to _brushing */
1690         _editor->commit_reversible_command ();
1691
1692         /* We have futzed with the layering of canvas items on our streamviews.
1693            If any region changed layer, this will have resulted in the stream
1694            views being asked to set up their region views, and all will be well.
1695            If not, we might now have badly-ordered region views.  Ask the StreamViews
1696            involved to sort themselves out, just in case.
1697         */
1698
1699         for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1700                 (*i)->view()->playlist_layered ((*i)->track ());
1701         }
1702 }
1703
1704 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1705  *  @param region Region to remove.
1706  *  @param playlist playlist To remove from.
1707  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1708  *  that clear_changes () is only called once per playlist.
1709  */
1710 void
1711 RegionMoveDrag::remove_region_from_playlist (
1712         boost::shared_ptr<Region> region,
1713         boost::shared_ptr<Playlist> playlist,
1714         PlaylistSet& modified_playlists
1715         )
1716 {
1717         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1718
1719         if (r.second) {
1720                 playlist->clear_changes ();
1721         }
1722
1723         playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1724 }
1725
1726
1727 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1728  *  clearing the playlist's diff history first if necessary.
1729  *  @param region Region to insert.
1730  *  @param dest_rtv Destination RouteTimeAxisView.
1731  *  @param dest_layer Destination layer.
1732  *  @param where Destination position.
1733  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1734  *  that clear_changes () is only called once per playlist.
1735  *  @return New RegionView, or 0 if no insert was performed.
1736  */
1737 RegionView *
1738 RegionMoveDrag::insert_region_into_playlist (
1739         boost::shared_ptr<Region> region,
1740         RouteTimeAxisView* dest_rtv,
1741         layer_t dest_layer,
1742         framecnt_t where,
1743         PlaylistSet& modified_playlists,
1744         const int32_t sub_num
1745         )
1746 {
1747         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1748         if (!dest_playlist) {
1749                 return 0;
1750         }
1751
1752         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1753         _new_region_view = 0;
1754         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1755
1756         /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1757         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1758         if (r.second) {
1759                 dest_playlist->clear_changes ();
1760         }
1761         dest_playlist->add_region (region, where, 1.0, false, sub_num);
1762
1763         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1764                 dest_playlist->set_layer (region, dest_layer);
1765         }
1766
1767         c.disconnect ();
1768
1769         assert (_new_region_view);
1770
1771         return _new_region_view;
1772 }
1773
1774 void
1775 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1776 {
1777         _new_region_view = rv;
1778 }
1779
1780 void
1781 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1782 {
1783         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1784                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1785                 if (!c->empty()) {
1786                         _editor->session()->add_command (c);
1787                 } else {
1788                         delete c;
1789                 }
1790         }
1791 }
1792
1793
1794 void
1795 RegionMoveDrag::aborted (bool movement_occurred)
1796 {
1797         if (_copy) {
1798
1799                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1800                         list<DraggingView>::const_iterator next = i;
1801                         ++next;
1802                         delete i->view;
1803                         i = next;
1804                 }
1805
1806                 _views.clear ();
1807
1808         } else {
1809                 RegionMotionDrag::aborted (movement_occurred);
1810         }
1811 }
1812
1813 void
1814 RegionMotionDrag::aborted (bool)
1815 {
1816         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1817
1818                 StreamView* sview = (*i)->view();
1819
1820                 if (sview) {
1821                         if (sview->layer_display() == Expanded) {
1822                                 sview->set_layer_display (Stacked);
1823                         }
1824                 }
1825         }
1826
1827         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1828                 RegionView* rv = i->view;
1829                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1830                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1831                 assert (rtv);
1832                 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1833                 rv->get_canvas_group()->set_y_position (0);
1834                 rv->drag_end ();
1835                 rv->move (-_total_x_delta, 0);
1836                 rv->set_height (rtv->view()->child_height ());
1837         }
1838 }
1839
1840 /** @param b true to brush, otherwise false.
1841  *  @param c true to make copies of the regions being moved, otherwise false.
1842  */
1843 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1844         : RegionMotionDrag (e, i, p, v, b)
1845         , _copy (c)
1846         , _new_region_view (0)
1847 {
1848         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1849
1850         double speed = 1;
1851         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1852         if (rtv && rtv->is_track()) {
1853                 speed = rtv->track()->speed ();
1854         }
1855
1856         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1857 }
1858
1859 void
1860 RegionMoveDrag::setup_pointer_frame_offset ()
1861 {
1862         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1863 }
1864
1865 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1866         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1867 {
1868         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1869
1870         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1871                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1872
1873         _primary = v->view()->create_region_view (r, false, false);
1874
1875         _primary->get_canvas_group()->show ();
1876         _primary->set_position (pos, 0);
1877         _views.push_back (DraggingView (_primary, this, v));
1878
1879         _last_frame_position = pos;
1880
1881         _item = _primary->get_canvas_group ();
1882 }
1883
1884 void
1885 RegionInsertDrag::finished (GdkEvent * event, bool)
1886 {
1887         int pos = _views.front().time_axis_view;
1888         assert(pos >= 0 && pos < (int)_time_axis_views.size());
1889
1890         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1891
1892         _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1893         _primary->get_canvas_group()->set_y_position (0);
1894
1895         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1896
1897         _editor->begin_reversible_command (Operations::insert_region);
1898         playlist->clear_changes ();
1899         playlist->add_region (_primary->region (), _last_frame_position);
1900
1901         // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1902         if (Config->get_edit_mode() == Ripple) {
1903                 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1904         }
1905
1906         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1907         _editor->commit_reversible_command ();
1908
1909         delete _primary;
1910         _primary = 0;
1911         _views.clear ();
1912 }
1913
1914 void
1915 RegionInsertDrag::aborted (bool)
1916 {
1917         delete _primary;
1918         _primary = 0;
1919         _views.clear ();
1920 }
1921
1922 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1923         : RegionMoveDrag (e, i, p, v, false, false)
1924 {
1925         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1926 }
1927
1928 struct RegionSelectionByPosition {
1929         bool operator() (RegionView*a, RegionView* b) {
1930                 return a->region()->position () < b->region()->position();
1931         }
1932 };
1933
1934 void
1935 RegionSpliceDrag::motion (GdkEvent* event, bool)
1936 {
1937         /* Which trackview is this ? */
1938
1939         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1940         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1941
1942         /* The region motion is only processed if the pointer is over
1943            an audio track.
1944         */
1945
1946         if (!tv || !tv->is_track()) {
1947                 /* To make sure we hide the verbose canvas cursor when the mouse is
1948                    not held over an audio track.
1949                 */
1950                 _editor->verbose_cursor()->hide ();
1951                 return;
1952         } else {
1953                 _editor->verbose_cursor()->show ();
1954         }
1955
1956         int dir;
1957
1958         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1959                 dir = 1;
1960         } else {
1961                 dir = -1;
1962         }
1963
1964         RegionSelection copy;
1965         _editor->selection->regions.by_position(copy);
1966
1967         framepos_t const pf = adjusted_current_frame (event);
1968
1969         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1970
1971                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1972
1973                 if (!atv) {
1974                         continue;
1975                 }
1976
1977                 boost::shared_ptr<Playlist> playlist;
1978
1979                 if ((playlist = atv->playlist()) == 0) {
1980                         continue;
1981                 }
1982
1983                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1984                         continue;
1985                 }
1986
1987                 if (dir > 0) {
1988                         if (pf < (*i)->region()->last_frame() + 1) {
1989                                 continue;
1990                         }
1991                 } else {
1992                         if (pf > (*i)->region()->first_frame()) {
1993                                 continue;
1994                         }
1995                 }
1996
1997
1998                 playlist->shuffle ((*i)->region(), dir);
1999         }
2000 }
2001
2002 void
2003 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2004 {
2005         RegionMoveDrag::finished (event, movement_occurred);
2006 }
2007
2008 void
2009 RegionSpliceDrag::aborted (bool)
2010 {
2011         /* XXX: TODO */
2012 }
2013
2014 /***
2015  * ripple mode...
2016  */
2017
2018 void
2019 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2020 {
2021
2022         boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2023
2024         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2025         RegionSelection to_ripple;
2026         for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2027                 if ((*i)->position() >= where) {
2028                         to_ripple.push_back (rtv->view()->find_view(*i));
2029                 }
2030         }
2031
2032         for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2033                 if (!exclude.contains (*i)) {
2034                         // the selection has already been added to _views
2035
2036                         if (drag_in_progress) {
2037                                 // do the same things that RegionMotionDrag::motion does when
2038                                 // first_move is true, for the region views that we're adding
2039                                 // to _views this time
2040
2041                                 (*i)->drag_start();
2042                                 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2043                                 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2044                                 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2045                                 rvg->reparent (_editor->_drag_motion_group);
2046
2047                                 // we only need to move in the y direction
2048                                 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2049                                 fudge.x = 0;
2050                                 rvg->move (fudge);
2051
2052                         }
2053                         _views.push_back (DraggingView (*i, this, tav));
2054                 }
2055         }
2056 }
2057
2058 void
2059 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2060 {
2061
2062         for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2063                 // we added all the regions after the selection
2064
2065                 std::list<DraggingView>::iterator to_erase = i++;
2066                 if (!_editor->selection->regions.contains (to_erase->view)) {
2067                         // restore the non-selected regions to their original playlist & positions,
2068                         // and then ripple them back by the length of the regions that were dragged away
2069                         // do the same things as RegionMotionDrag::aborted
2070
2071                         RegionView *rv = to_erase->view;
2072                         TimeAxisView* tv = &(rv->get_time_axis_view ());
2073                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2074                         assert (rtv);
2075
2076                         // plonk them back onto their own track
2077                         rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2078                         rv->get_canvas_group()->set_y_position (0);
2079                         rv->drag_end ();
2080
2081                         if (move_regions) {
2082                                 // move the underlying region to match the view
2083                                 rv->region()->set_position (rv->region()->position() + amount);
2084                         } else {
2085                                 // restore the view to match the underlying region's original position
2086                                 rv->move(-amount, 0);   // second parameter is y delta - seems 0 is OK
2087                         }
2088
2089                         rv->set_height (rtv->view()->child_height ());
2090                         _views.erase (to_erase);
2091                 }
2092         }
2093 }
2094
2095 bool
2096 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2097 {
2098         if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2099                 if (delta_track) {
2100                         return allow_moves_across_tracks;
2101                 } else {
2102                         return true;
2103                 }
2104         }
2105         return false;
2106 }
2107
2108 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2109         : RegionMoveDrag (e, i, p, v, false, false)
2110 {
2111         DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2112         // compute length of selection
2113         RegionSelection selected_regions = _editor->selection->regions;
2114         selection_length = selected_regions.end_frame() - selected_regions.start();
2115
2116         // we'll only allow dragging to another track in ripple mode if all the regions
2117         // being dragged start off on the same track
2118         allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2119         prev_tav = NULL;
2120         prev_amount = 0;
2121         exclude = new RegionList;
2122         for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2123                 exclude->push_back((*i)->region());
2124         }
2125
2126         // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2127         RegionSelection copy;
2128         selected_regions.by_position(copy); // get selected regions sorted by position into copy
2129
2130         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2131         std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2132
2133         for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2134                 // find ripple start point on each applicable playlist
2135                 RegionView *first_selected_on_this_track = NULL;
2136                 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2137                         if ((*i)->region()->playlist() == (*pi)) {
2138                                 // region is on this playlist - it's the first, because they're sorted
2139                                 first_selected_on_this_track = *i;
2140                                 break;
2141                         }
2142                 }
2143                 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2144                 add_all_after_to_views (
2145                                 &first_selected_on_this_track->get_time_axis_view(),
2146                                 first_selected_on_this_track->region()->position(),
2147                                 selected_regions, false);
2148         }
2149
2150         if (allow_moves_across_tracks) {
2151                 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2152         } else {
2153                 orig_tav = NULL;
2154         }
2155
2156 }
2157
2158 void
2159 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2160 {
2161         /* Which trackview is this ? */
2162
2163         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2164         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2165
2166         /* The region motion is only processed if the pointer is over
2167            an audio track.
2168          */
2169
2170         if (!tv || !tv->is_track()) {
2171                 /* To make sure we hide the verbose canvas cursor when the mouse is
2172                    not held over an audiotrack.
2173                  */
2174                 _editor->verbose_cursor()->hide ();
2175                 return;
2176         }
2177
2178         framepos_t where = adjusted_current_frame (event);
2179         assert (where >= 0);
2180         framepos_t after;
2181         double delta = compute_x_delta (event, &after);
2182
2183         framecnt_t amount = _editor->pixel_to_sample (delta);
2184
2185         if (allow_moves_across_tracks) {
2186                 // all the originally selected regions were on the same track
2187
2188                 framecnt_t adjust = 0;
2189                 if (prev_tav && tv != prev_tav) {
2190                         // dragged onto a different track
2191                         // remove the unselected regions from _views, restore them to their original positions
2192                         // and add the regions after the drop point on the new playlist to _views instead.
2193                         // undo the effect of rippling the previous playlist, and include the effect of removing
2194                         // the dragged region(s) from this track
2195
2196                         remove_unselected_from_views (prev_amount, false);
2197                         // ripple previous playlist according to the regions that have been removed onto the new playlist
2198                         prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2199                         prev_amount = 0;
2200
2201                         // move just the selected regions
2202                         RegionMoveDrag::motion(event, first_move);
2203
2204                         // ensure that the ripple operation on the new playlist inserts selection_length time
2205                         adjust = selection_length;
2206                         // ripple the new current playlist
2207                         tv->playlist()->ripple (where, amount+adjust, exclude);
2208
2209                         // add regions after point where drag entered this track to subsequent ripples
2210                         add_all_after_to_views (tv, where, _editor->selection->regions, true);
2211
2212                 } else {
2213                         // motion on same track
2214                         RegionMoveDrag::motion(event, first_move);
2215                 }
2216                 prev_tav = tv;
2217
2218                 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2219                 prev_position = where;
2220         } else {
2221                 // selection encompasses multiple tracks - just drag
2222                 // cross-track drags are forbidden
2223                 RegionMoveDrag::motion(event, first_move);
2224         }
2225
2226         if (!_x_constrained) {
2227                 prev_amount += amount;
2228         }
2229
2230         _last_frame_position = after;
2231 }
2232
2233 void
2234 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2235 {
2236         if (!movement_occurred) {
2237
2238                 /* just a click */
2239
2240                 if (was_double_click() && !_views.empty()) {
2241                         DraggingView dv = _views.front();
2242                         dv.view->show_region_editor ();
2243
2244                 }
2245
2246                 return;
2247         }
2248
2249         _editor->begin_reversible_command(_("Ripple drag"));
2250
2251         // remove the regions being rippled from the dragging view, updating them to
2252         // their new positions
2253         remove_unselected_from_views (prev_amount, true);
2254
2255         if (allow_moves_across_tracks) {
2256                 if (orig_tav) {
2257                         // if regions were dragged across tracks, we've rippled any later
2258                         // regions on the track the regions were dragged off, so we need
2259                         // to add the original track to the undo record
2260                         orig_tav->playlist()->clear_changes();
2261                         vector<Command*> cmds;
2262                         orig_tav->playlist()->rdiff (cmds);
2263                         _editor->session()->add_commands (cmds);
2264                 }
2265                 if (prev_tav && prev_tav != orig_tav) {
2266                         prev_tav->playlist()->clear_changes();
2267                         vector<Command*> cmds;
2268                         prev_tav->playlist()->rdiff (cmds);
2269                         _editor->session()->add_commands (cmds);
2270                 }
2271         } else {
2272                 // selection spanned multiple tracks - all will need adding to undo record
2273
2274                 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2275                 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2276
2277                 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2278                         (*pi)->clear_changes();
2279                         vector<Command*> cmds;
2280                         (*pi)->rdiff (cmds);
2281                         _editor->session()->add_commands (cmds);
2282                 }
2283         }
2284
2285         // other modified playlists are added to undo by RegionMoveDrag::finished()
2286         RegionMoveDrag::finished (event, movement_occurred);
2287         _editor->commit_reversible_command();
2288 }
2289
2290 void
2291 RegionRippleDrag::aborted (bool movement_occurred)
2292 {
2293         RegionMoveDrag::aborted (movement_occurred);
2294         _views.clear ();
2295 }
2296
2297
2298 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2299         : Drag (e, i),
2300           _view (dynamic_cast<MidiTimeAxisView*> (v))
2301 {
2302         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2303
2304         assert (_view);
2305 }
2306
2307 void
2308 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2309 {
2310         if (first_move) {
2311                 _editor->begin_reversible_command (_("create region"));
2312                 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2313                 _view->playlist()->freeze ();
2314         } else {
2315                 if (_region) {
2316                         framepos_t const f = adjusted_current_frame (event);
2317                         if (f < grab_frame()) {
2318                                 _region->set_initial_position (f);
2319                         }
2320
2321                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2322                            so that if this region is duplicated, its duplicate starts on
2323                            a snap point rather than 1 frame after a snap point.  Otherwise things get
2324                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
2325                            place snapped notes at the start of the region.
2326                         */
2327
2328                         framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2329                         _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2330                 }
2331         }
2332 }
2333
2334 void
2335 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2336 {
2337         if (!movement_occurred) {
2338                 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2339         } else {
2340                 _view->playlist()->thaw ();
2341                 _editor->commit_reversible_command();
2342         }
2343 }
2344
2345 void
2346 RegionCreateDrag::aborted (bool)
2347 {
2348         if (_region) {
2349                 _view->playlist()->thaw ();
2350         }
2351
2352         /* XXX */
2353 }
2354
2355 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2356         : Drag (e, i)
2357         , region (0)
2358         , relative (false)
2359         , at_front (true)
2360         , _was_selected (false)
2361         , _snap_delta (0)
2362 {
2363         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2364 }
2365
2366 void
2367 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2368 {
2369         Gdk::Cursor* cursor;
2370         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2371         assert (cnote);
2372         float x_fraction = cnote->mouse_x_fraction ();
2373
2374         if (x_fraction > 0.0 && x_fraction < 0.25) {
2375                 cursor = _editor->cursors()->left_side_trim;
2376                 at_front = true;
2377         } else  {
2378                 cursor = _editor->cursors()->right_side_trim;
2379                 at_front = false;
2380         }
2381
2382         Drag::start_grab (event, cursor);
2383
2384         region = &cnote->region_view();
2385
2386         double temp;
2387         temp = region->snap_to_pixel (cnote->x0 (), true);
2388         _snap_delta = temp - cnote->x0 ();
2389
2390         _item->grab ();
2391
2392         if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2393                 relative = false;
2394         } else {
2395                 relative = true;
2396         }
2397         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2398         if (ms.size() > 1) {
2399                 /* has to be relative, may make no sense otherwise */
2400                 relative = true;
2401         }
2402
2403         if (!(_was_selected = cnote->selected())) {
2404
2405                 /* tertiary-click means extend selection - we'll do that on button release,
2406                    so don't add it here, because otherwise we make it hard to figure
2407                    out the "extend-to" range.
2408                 */
2409
2410                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2411
2412                 if (!extend) {
2413                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2414
2415                         if (add) {
2416                                 region->note_selected (cnote, true);
2417                         } else {
2418                                 _editor->get_selection().clear_points();
2419                                 region->unique_select (cnote);
2420                         }
2421                 }
2422         }
2423 }
2424
2425 void
2426 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2427 {
2428         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2429         if (first_move) {
2430                 _editor->begin_reversible_command (_("resize notes"));
2431
2432                 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2433                         MidiRegionSelection::iterator next;
2434                         next = r;
2435                         ++next;
2436                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2437                         if (mrv) {
2438                                 mrv->begin_resizing (at_front);
2439                         }
2440                         r = next;
2441                 }
2442         }
2443
2444         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2445                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2446                 assert (nb);
2447                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2448                 if (mrv) {
2449                         double sd = 0.0;
2450                         bool snap = true;
2451                         bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2452
2453                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2454                                 if (_editor->snap_mode () != SnapOff) {
2455                                         snap = false;
2456                                 }
2457                         } else {
2458                                 if (_editor->snap_mode () == SnapOff) {
2459                                         snap = false;
2460                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2461                                         if (apply_snap_delta) {
2462                                                 snap = true;
2463                                         }
2464                                 }
2465                         }
2466
2467                         if (apply_snap_delta) {
2468                                 sd = _snap_delta;
2469                         }
2470
2471                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2472                 }
2473         }
2474 }
2475
2476 void
2477 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2478 {
2479         if (!movement_occurred) {
2480                 /* no motion - select note */
2481                 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2482                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2483                     _editor->current_mouse_mode() == Editing::MouseDraw) {
2484
2485                         bool changed = false;
2486
2487                         if (_was_selected) {
2488                                 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2489                                 if (add) {
2490                                         region->note_deselected (cnote);
2491                                         changed = true;
2492                                 } else {
2493                                         _editor->get_selection().clear_points();
2494                                         region->unique_select (cnote);
2495                                         changed = true;
2496                                 }
2497                         } else {
2498                                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2499                                 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2500
2501                                 if (!extend && !add && region->selection_size() > 1) {
2502                                         _editor->get_selection().clear_points();
2503                                         region->unique_select (cnote);
2504                                         changed = true;
2505                                 } else if (extend) {
2506                                         region->note_selected (cnote, true, true);
2507                                         changed = true;
2508                                 } else {
2509                                         /* it was added during button press */
2510                                         changed = true;
2511                                 }
2512                         }
2513
2514                         if (changed) {
2515                                 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2516                                 _editor->commit_reversible_selection_op();
2517                         }
2518                 }
2519
2520                 return;
2521         }
2522
2523         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2524         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2525                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2526                 assert (nb);
2527                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2528                 double sd = 0.0;
2529                 bool snap = true;
2530                 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2531                 if (mrv) {
2532                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2533                                 if (_editor->snap_mode () != SnapOff) {
2534                                         snap = false;
2535                                 }
2536                         } else {
2537                                 if (_editor->snap_mode () == SnapOff) {
2538                                         snap = false;
2539                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2540                                         if (apply_snap_delta) {
2541                                                 snap = true;
2542                                         }
2543                                 }
2544                         }
2545
2546                         if (apply_snap_delta) {
2547                                 sd = _snap_delta;
2548                         }
2549
2550                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2551                 }
2552         }
2553
2554         _editor->commit_reversible_command ();
2555 }
2556
2557 void
2558 NoteResizeDrag::aborted (bool)
2559 {
2560         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2561         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2562                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2563                 if (mrv) {
2564                         mrv->abort_resizing ();
2565                 }
2566         }
2567 }
2568
2569 AVDraggingView::AVDraggingView (RegionView* v)
2570         : view (v)
2571 {
2572         initial_position = v->region()->position ();
2573 }
2574
2575 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2576         : Drag (e, i)
2577 {
2578         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2579
2580         RegionSelection rs;
2581         TrackViewList empty;
2582         empty.clear();
2583         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2584         std::list<RegionView*> views = rs.by_layer();
2585
2586         _stuck = false;
2587         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2588                 RegionView* rv = (*i);
2589                 if (!rv->region()->video_locked()) {
2590                         continue;
2591                 }
2592                 if (rv->region()->locked()) {
2593                         _stuck = true;
2594                 }
2595                 _views.push_back (AVDraggingView (rv));
2596         }
2597 }
2598
2599 void
2600 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2601 {
2602         Drag::start_grab (event);
2603         if (_editor->session() == 0) {
2604                 return;
2605         }
2606
2607         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2608                 _stuck = false;
2609                 _views.clear();
2610         }
2611
2612         if (_stuck) {
2613                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2614                 return;
2615         }
2616
2617         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2618         _max_backwards_drag = (
2619                           ARDOUR_UI::instance()->video_timeline->get_duration()
2620                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2621                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2622                         );
2623
2624         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2625                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2626                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2627                 }
2628         }
2629         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2630
2631         char buf[128];
2632         Timecode::Time timecode;
2633         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2634         snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2635         show_verbose_cursor_text (buf);
2636 }
2637
2638 void
2639 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2640 {
2641         if (_editor->session() == 0) {
2642                 return;
2643         }
2644         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2645                 return;
2646         }
2647         if (_stuck) {
2648                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2649                 return;
2650         }
2651
2652         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2653         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2654
2655         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2656                 dt = - _max_backwards_drag;
2657         }
2658
2659         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2660         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2661
2662         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2663                 RegionView* rv = i->view;
2664                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2665                 if (first_move) {
2666                         rv->drag_start ();
2667                         rv->region()->clear_changes ();
2668                         rv->region()->suspend_property_changes();
2669                 }
2670                 rv->region()->set_position(i->initial_position + dt);
2671                 rv->region_changed(ARDOUR::Properties::position);
2672         }
2673
2674         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2675         Timecode::Time timecode;
2676         Timecode::Time timediff;
2677         char buf[128];
2678         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2679         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2680         snprintf (buf, sizeof (buf),
2681                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2682                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2683                         , _("Video Start:"),
2684                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2685                         , _("Diff:"),
2686                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2687                                 );
2688         show_verbose_cursor_text (buf);
2689 }
2690
2691 void
2692 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2693 {
2694         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2695                 return;
2696         }
2697         if (_stuck) {
2698                 return;
2699         }
2700
2701         if (!movement_occurred || ! _editor->session()) {
2702                 return;
2703         }
2704
2705         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2706
2707         _editor->begin_reversible_command (_("Move Video"));
2708
2709         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2710         ARDOUR_UI::instance()->video_timeline->save_undo();
2711         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2712         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2713
2714         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2715                 i->view->drag_end();
2716                 i->view->region()->resume_property_changes ();
2717
2718                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2719         }
2720
2721         _editor->session()->maybe_update_session_range(
2722                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2723                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2724                         );
2725
2726
2727         _editor->commit_reversible_command ();
2728 }
2729
2730 void
2731 VideoTimeLineDrag::aborted (bool)
2732 {
2733         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2734                 return;
2735         }
2736         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2737         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2738
2739         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2740                 i->view->region()->resume_property_changes ();
2741                 i->view->region()->set_position(i->initial_position);
2742         }
2743 }
2744
2745 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2746         : RegionDrag (e, i, p, v)
2747         , _operation (StartTrim)
2748         , _preserve_fade_anchor (preserve_fade_anchor)
2749         , _jump_position_when_done (false)
2750 {
2751         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2752 }
2753
2754 void
2755 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2756 {
2757         double speed = 1.0;
2758         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2759         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2760
2761         if (tv && tv->is_track()) {
2762                 speed = tv->track()->speed();
2763         }
2764
2765         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2766         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2767         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2768
2769         framepos_t const pf = adjusted_current_frame (event);
2770         setup_snap_delta (region_start);
2771
2772         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2773                 /* Move the contents of the region around without changing the region bounds */
2774                 _operation = ContentsTrim;
2775                 Drag::start_grab (event, _editor->cursors()->trimmer);
2776         } else {
2777                 /* These will get overridden for a point trim.*/
2778                 if (pf < (region_start + region_length/2)) {
2779                         /* closer to front */
2780                         _operation = StartTrim;
2781                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2782                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2783                         } else {
2784                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2785                         }
2786                 } else {
2787                         /* closer to end */
2788                         _operation = EndTrim;
2789                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2790                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2791                         } else {
2792                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2793                         }
2794                 }
2795         }
2796         /* jump trim disabled for now
2797         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2798                 _jump_position_when_done = true;
2799         }
2800         */
2801
2802         switch (_operation) {
2803         case StartTrim:
2804                 show_verbose_cursor_time (region_start);
2805                 break;
2806         case EndTrim:
2807                 show_verbose_cursor_duration (region_start, region_end);
2808                 break;
2809         case ContentsTrim:
2810                 show_verbose_cursor_time (pf);
2811                 break;
2812         }
2813
2814         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2815                 i->view->region()->suspend_property_changes ();
2816         }
2817 }
2818
2819 void
2820 TrimDrag::motion (GdkEvent* event, bool first_move)
2821 {
2822         RegionView* rv = _primary;
2823
2824         double speed = 1.0;
2825         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2826         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2827         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2828         frameoffset_t frame_delta = 0;
2829
2830         if (tv && tv->is_track()) {
2831                 speed = tv->track()->speed();
2832         }
2833         framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2834         framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2835
2836         if (first_move) {
2837
2838                 string trim_type;
2839
2840                 switch (_operation) {
2841                 case StartTrim:
2842                         trim_type = "Region start trim";
2843                         break;
2844                 case EndTrim:
2845                         trim_type = "Region end trim";
2846                         break;
2847                 case ContentsTrim:
2848                         trim_type = "Region content trim";
2849                         break;
2850                 default:
2851                         assert(0);
2852                         break;
2853                 }
2854
2855                 _editor->begin_reversible_command (trim_type);
2856
2857                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2858                         RegionView* rv = i->view;
2859                         rv->enable_display (false);
2860                         rv->region()->playlist()->clear_owned_changes ();
2861
2862                         if (_operation == StartTrim) {
2863                                 rv->trim_front_starting ();
2864                         }
2865
2866                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2867
2868                         if (arv) {
2869                                 arv->temporarily_hide_envelope ();
2870                                 arv->drag_start ();
2871                         }
2872
2873                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2874                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2875
2876                         if (insert_result.second) {
2877                                 pl->freeze();
2878                         }
2879                 }
2880         }
2881
2882         bool non_overlap_trim = false;
2883
2884         if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2885                 non_overlap_trim = true;
2886         }
2887
2888         /* contstrain trim to fade length */
2889         if (_preserve_fade_anchor) {
2890                 switch (_operation) {
2891                         case StartTrim:
2892                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2893                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2894                                         if (!arv) continue;
2895                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2896                                         if (ar->locked()) continue;
2897                                         framecnt_t len = ar->fade_in()->back()->when;
2898                                         if (len < dt) dt = min(dt, len);
2899                                 }
2900                                 break;
2901                         case EndTrim:
2902                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2903                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2904                                         if (!arv) continue;
2905                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2906                                         if (ar->locked()) continue;
2907                                         framecnt_t len = ar->fade_out()->back()->when;
2908                                         if (len < -dt) dt = max(dt, -len);
2909                                 }
2910                                 break;
2911                         case ContentsTrim:
2912                                 break;
2913                 }
2914         }
2915
2916
2917         switch (_operation) {
2918         case StartTrim:
2919                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2920                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2921                                                             , _editor->get_grid_music_divisions (event->button.state));
2922
2923                         if (changed && _preserve_fade_anchor) {
2924                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2925                                 if (arv) {
2926                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2927                                         framecnt_t len = ar->fade_in()->back()->when;
2928                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2929                                         framepos_t new_length = len - diff;
2930                                         i->anchored_fade_length = min (ar->length(), new_length);
2931                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2932                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2933                                 }
2934                         }
2935                 }
2936                 break;
2937
2938         case EndTrim:
2939                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2940                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, _editor->get_grid_music_divisions (event->button.state));
2941                         if (changed && _preserve_fade_anchor) {
2942                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2943                                 if (arv) {
2944                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2945                                         framecnt_t len = ar->fade_out()->back()->when;
2946                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2947                                         framepos_t new_length = len + diff;
2948                                         i->anchored_fade_length = min (ar->length(), new_length);
2949                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2950                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2951                                 }
2952                         }
2953                 }
2954                 break;
2955
2956         case ContentsTrim:
2957                 {
2958                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2959
2960                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2961                                 i->view->move_contents (frame_delta);
2962                         }
2963                 }
2964                 break;
2965         }
2966
2967         switch (_operation) {
2968         case StartTrim:
2969                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2970                 break;
2971         case EndTrim:
2972                 show_verbose_cursor_duration ((framepos_t)  rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2973                 break;
2974         case ContentsTrim:
2975                 // show_verbose_cursor_time (frame_delta);
2976                 break;
2977         }
2978 }
2979
2980 void
2981 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2982 {
2983         if (movement_occurred) {
2984                 motion (event, false);
2985
2986                 if (_operation == StartTrim) {
2987                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2988                                 {
2989                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2990                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2991                                            needs to be zero in the undo record.
2992                                         */
2993                                         i->view->trim_front_ending ();
2994                                 }
2995                                 if (_preserve_fade_anchor) {
2996                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2997                                         if (arv) {
2998                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2999                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3000                                                 ar->set_fade_in_length(i->anchored_fade_length);
3001                                                 ar->set_fade_in_active(true);
3002                                         }
3003                                 }
3004                                 if (_jump_position_when_done) {
3005                                         i->view->region()->set_position (i->initial_position);
3006                                 }
3007                         }
3008                 } else if (_operation == EndTrim) {
3009                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3010                                 if (_preserve_fade_anchor) {
3011                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3012                                         if (arv) {
3013                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3014                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3015                                                 ar->set_fade_out_length(i->anchored_fade_length);
3016                                                 ar->set_fade_out_active(true);
3017                                         }
3018                                 }
3019                                 if (_jump_position_when_done) {
3020                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
3021                                 }
3022                         }
3023                 }
3024
3025                 if (!_views.empty()) {
3026                         if (_operation == StartTrim) {
3027                                 _editor->maybe_locate_with_edit_preroll(
3028                                         _views.begin()->view->region()->position());
3029                         }
3030                         if (_operation == EndTrim) {
3031                                 _editor->maybe_locate_with_edit_preroll(
3032                                         _views.begin()->view->region()->position() +
3033                                         _views.begin()->view->region()->length());
3034                         }
3035                 }
3036
3037                 if (!_editor->selection->selected (_primary)) {
3038                         _primary->thaw_after_trim ();
3039                 } else {
3040
3041                         set<boost::shared_ptr<Playlist> > diffed_playlists;
3042
3043                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3044                                 i->view->thaw_after_trim ();
3045                                 i->view->enable_display (true);
3046
3047                                 /* Trimming one region may affect others on the playlist, so we need
3048                                    to get undo Commands from the whole playlist rather than just the
3049                                    region.  Use diffed_playlists to make sure we don't diff a given
3050                                    playlist more than once.
3051                                 */
3052                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3053                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3054                                         vector<Command*> cmds;
3055                                         p->rdiff (cmds);
3056                                         _editor->session()->add_commands (cmds);
3057                                         diffed_playlists.insert (p);
3058                                 }
3059                         }
3060                 }
3061
3062                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3063                         (*p)->thaw ();
3064                 }
3065
3066                 _editor->motion_frozen_playlists.clear ();
3067                 _editor->commit_reversible_command();
3068
3069         } else {
3070                 /* no mouse movement */
3071                 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3072                         _editor->point_trim (event, adjusted_current_frame (event));
3073                 }
3074         }
3075
3076         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3077                 i->view->region()->resume_property_changes ();
3078         }
3079 }
3080
3081 void
3082 TrimDrag::aborted (bool movement_occurred)
3083 {
3084         /* Our motion method is changing model state, so use the Undo system
3085            to cancel.  Perhaps not ideal, as this will leave an Undo point
3086            behind which may be slightly odd from the user's point of view.
3087         */
3088
3089         finished (0, true);
3090
3091         if (movement_occurred) {
3092                 _editor->undo ();
3093         }
3094
3095         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3096                 i->view->region()->resume_property_changes ();
3097         }
3098 }
3099
3100 void
3101 TrimDrag::setup_pointer_frame_offset ()
3102 {
3103         list<DraggingView>::iterator i = _views.begin ();
3104         while (i != _views.end() && i->view != _primary) {
3105                 ++i;
3106         }
3107
3108         if (i == _views.end()) {
3109                 return;
3110         }
3111
3112         switch (_operation) {
3113         case StartTrim:
3114                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3115                 break;
3116         case EndTrim:
3117                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3118                 break;
3119         case ContentsTrim:
3120                 break;
3121         }
3122 }
3123
3124 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3125         : Drag (e, i)
3126         , _copy (c)
3127         , _old_snap_type (e->snap_type())
3128         , _old_snap_mode (e->snap_mode())
3129         , before_state (0)
3130 {
3131         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3132         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3133         assert (_marker);
3134         _real_section = &_marker->meter();
3135
3136 }
3137
3138 void
3139 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3140 {
3141         Drag::start_grab (event, cursor);
3142         show_verbose_cursor_time (adjusted_current_frame(event));
3143 }
3144
3145 void
3146 MeterMarkerDrag::setup_pointer_frame_offset ()
3147 {
3148         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3149 }
3150
3151 void
3152 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3153 {
3154         if (first_move) {
3155                 // create a dummy marker to catch events, then hide it.
3156
3157                 char name[64];
3158                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3159
3160                 _marker = new MeterMarker (
3161                         *_editor,
3162                         *_editor->meter_group,
3163                         UIConfiguration::instance().color ("meter marker"),
3164                         name,
3165                         *new MeterSection (_marker->meter())
3166                 );
3167
3168                 /* use the new marker for the grab */
3169                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3170                 _marker->hide();
3171
3172                 TempoMap& map (_editor->session()->tempo_map());
3173                 /* get current state */
3174                 before_state = &map.get_state();
3175
3176                 if (!_copy) {
3177                         _editor->begin_reversible_command (_("move meter mark"));
3178                 } else {
3179                         _editor->begin_reversible_command (_("copy meter mark"));
3180
3181                         Timecode::BBT_Time bbt = _real_section->bbt();
3182
3183                         /* we can't add a meter where one currently exists */
3184                         if (_real_section->frame() < adjusted_current_frame (event, false)) {
3185                                 ++bbt.bars;
3186                         } else {
3187                                 --bbt.bars;
3188                         }
3189                         const double beat = map.beat_at_bbt (bbt);
3190                         _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3191                                                        , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3192                         if (!_real_section) {
3193                                 aborted (true);
3194                                 return;
3195                         }
3196
3197                 }
3198                 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3199                 if (_real_section->position_lock_style() != AudioTime) {
3200                         _editor->set_snap_to (SnapToBar);
3201                         _editor->set_snap_mode (SnapNormal);
3202                 }
3203         }
3204
3205         framepos_t pf = adjusted_current_frame (event);
3206
3207         if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3208                 /* never snap to music for audio locked */
3209                 pf = adjusted_current_frame (event, false);
3210         }
3211
3212         _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3213
3214         /* fake marker meeds to stay under the mouse, unlike the real one. */
3215         _marker->set_position (adjusted_current_frame (event, false));
3216
3217         show_verbose_cursor_time (_real_section->frame());
3218 }
3219
3220 void
3221 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3222 {
3223         if (!movement_occurred) {
3224                 if (was_double_click()) {
3225                         _editor->edit_meter_marker (*_marker);
3226                 }
3227                 return;
3228         }
3229
3230         /* reinstate old snap setting */
3231         _editor->set_snap_to (_old_snap_type);
3232         _editor->set_snap_mode (_old_snap_mode);
3233
3234         TempoMap& map (_editor->session()->tempo_map());
3235
3236         XMLNode &after = map.get_state();
3237         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3238         _editor->commit_reversible_command ();
3239
3240         // delete the dummy marker we used for visual representation while moving.
3241         // a new visual marker will show up automatically.
3242         delete _marker;
3243 }
3244
3245 void
3246 MeterMarkerDrag::aborted (bool moved)
3247 {
3248         _marker->set_position (_marker->meter().frame ());
3249         if (moved) {
3250                 /* reinstate old snap setting */
3251                 _editor->set_snap_to (_old_snap_type);
3252                 _editor->set_snap_mode (_old_snap_mode);
3253
3254                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3255                 // delete the dummy marker we used for visual representation while moving.
3256                 // a new visual marker will show up automatically.
3257                 delete _marker;
3258         }
3259 }
3260
3261 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3262         : Drag (e, i)
3263         , _copy (c)
3264         , before_state (0)
3265 {
3266         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3267
3268         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3269         _real_section = &_marker->tempo();
3270         _movable = _real_section->movable();
3271         assert (_marker);
3272 }
3273
3274 void
3275 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3276 {
3277         Drag::start_grab (event, cursor);
3278         if (!_real_section->active()) {
3279                 show_verbose_cursor_text (_("inactive"));
3280         } else {
3281                 show_verbose_cursor_time (adjusted_current_frame (event));
3282         }
3283 }
3284
3285 void
3286 TempoMarkerDrag::setup_pointer_frame_offset ()
3287 {
3288         _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3289 }
3290
3291 void
3292 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3293 {
3294         if (!_real_section->active()) {
3295                 return;
3296         }
3297
3298         if (first_move) {
3299
3300                 // mvc drag - create a dummy marker to catch events, hide it.
3301
3302                 char name[64];
3303                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3304
3305                 TempoSection section (_marker->tempo());
3306
3307                 _marker = new TempoMarker (
3308                         *_editor,
3309                         *_editor->tempo_group,
3310                         UIConfiguration::instance().color ("tempo marker"),
3311                         name,
3312                         *new TempoSection (_marker->tempo())
3313                         );
3314
3315                 /* use the new marker for the grab */
3316                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3317                 _marker->hide();
3318
3319                 TempoMap& map (_editor->session()->tempo_map());
3320                 /* get current state */
3321                 before_state = &map.get_state();
3322
3323                 if (!_copy) {
3324                         _editor->begin_reversible_command (_("move tempo mark"));
3325
3326                 } else {
3327                         const Tempo tempo (_marker->tempo());
3328                         const framepos_t frame = adjusted_current_frame (event) + 1;
3329                         const TempoSection::Type type = _real_section->type();
3330
3331                         _editor->begin_reversible_command (_("copy tempo mark"));
3332
3333                         if (_real_section->position_lock_style() == MusicTime) {
3334                                 _real_section = map.add_tempo (tempo, map.pulse_at_frame (frame), 0, type, MusicTime);
3335                         } else {
3336                                 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3337                         }
3338
3339                         if (!_real_section) {
3340                                 aborted (true);
3341                                 return;
3342                         }
3343                 }
3344
3345         }
3346
3347         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3348                 /* use vertical movement to alter tempo .. should be log */
3349                 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3350                 stringstream strs;
3351
3352                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3353                 strs << new_bpm;
3354                 show_verbose_cursor_text (strs.str());
3355
3356         } else if (_movable && !_real_section->locked_to_meter()) {
3357                 const framepos_t pf = adjusted_current_frame (event);
3358                 TempoMap& map (_editor->session()->tempo_map());
3359
3360                 /* snap to beat is 1, snap to bar is -1 (sorry) */
3361                 int sub_num = _editor->get_grid_music_divisions (event->button.state);
3362
3363                 map.gui_move_tempo (_real_section, pf, sub_num);
3364
3365                 show_verbose_cursor_time (_real_section->frame());
3366         }
3367         _marker->set_position (adjusted_current_frame (event, false));
3368 }
3369
3370 void
3371 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3372 {
3373         if (!_real_section->active()) {
3374                 return;
3375         }
3376         if (!movement_occurred) {
3377                 if (was_double_click()) {
3378                         _editor->edit_tempo_marker (*_marker);
3379                 }
3380                 return;
3381         }
3382
3383         TempoMap& map (_editor->session()->tempo_map());
3384
3385         XMLNode &after = map.get_state();
3386         _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3387         _editor->commit_reversible_command ();
3388
3389         // delete the dummy marker we used for visual representation while moving.
3390         // a new visual marker will show up automatically.
3391         delete _marker;
3392 }
3393
3394 void
3395 TempoMarkerDrag::aborted (bool moved)
3396 {
3397         _marker->set_position (_marker->tempo().frame());
3398         if (moved) {
3399                 TempoMap& map (_editor->session()->tempo_map());
3400                 map.set_state (*before_state, Stateful::current_state_version);
3401                 // delete the dummy (hidden) marker we used for events while moving.
3402                 delete _marker;
3403         }
3404 }
3405
3406 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3407         : Drag (e, i)
3408         , _pulse (0.0)
3409         , _tempo (0)
3410         , before_state (0)
3411 {
3412         DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3413
3414 }
3415
3416 void
3417 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3418 {
3419         Drag::start_grab (event, cursor);
3420         TempoMap& map (_editor->session()->tempo_map());
3421         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3422         ostringstream sstr;
3423
3424         sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3425         sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3426         show_verbose_cursor_text (sstr.str());
3427         finished (event, false);
3428 }
3429
3430 void
3431 BBTRulerDrag::setup_pointer_frame_offset ()
3432 {
3433         TempoMap& map (_editor->session()->tempo_map());
3434         const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3435         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3436         double beat = 0.0;
3437
3438         if (divisions > 0) {
3439                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3440         } else {
3441                 /* while it makes some sense for the user to determine the division to 'grab',
3442                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3443                    and the result over steep tempo curves. Use sixteenths.
3444                 */
3445                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3446         }
3447
3448         _pulse = map.pulse_at_beat (beat);
3449
3450         _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3451
3452 }
3453
3454 void
3455 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3456 {
3457         TempoMap& map (_editor->session()->tempo_map());
3458
3459         if (first_move) {
3460                 /* get current state */
3461                 before_state = &map.get_state();
3462                 _editor->begin_reversible_command (_("dilate tempo"));
3463         }
3464
3465         framepos_t pf;
3466
3467         if (_editor->snap_musical()) {
3468                 pf = adjusted_current_frame (event, false);
3469         } else {
3470                 pf = adjusted_current_frame (event);
3471         }
3472
3473         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3474                 /* adjust previous tempo to match pointer frame */
3475                 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3476         }
3477         ostringstream sstr;
3478         sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3479         sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3480         show_verbose_cursor_text (sstr.str());
3481 }
3482
3483 void
3484 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3485 {
3486         if (!movement_occurred) {
3487                 return;
3488         }
3489
3490         TempoMap& map (_editor->session()->tempo_map());
3491
3492         XMLNode &after = map.get_state();
3493         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3494         _editor->commit_reversible_command ();
3495 }
3496
3497 void
3498 BBTRulerDrag::aborted (bool moved)
3499 {
3500         if (moved) {
3501                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3502         }
3503 }
3504
3505
3506 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3507         : Drag (e, &c.track_canvas_item(), false)
3508         , _cursor (c)
3509         , _stop (s)
3510         , _grab_zoom (0.0)
3511 {
3512         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3513 }
3514
3515 /** Do all the things we do when dragging the playhead to make it look as though
3516  *  we have located, without actually doing the locate (because that would cause
3517  *  the diskstream buffers to be refilled, which is too slow).
3518  */
3519 void
3520 CursorDrag::fake_locate (framepos_t t)
3521 {
3522         if (_editor->session () == 0) {
3523                 return;
3524         }
3525
3526         _editor->playhead_cursor->set_position (t);
3527
3528         Session* s = _editor->session ();
3529         if (s->timecode_transmission_suspended ()) {
3530                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3531                 /* This is asynchronous so it will be sent "now"
3532                  */
3533                 s->send_mmc_locate (f);
3534                 /* These are synchronous and will be sent during the next
3535                    process cycle
3536                 */
3537                 s->queue_full_time_code ();
3538                 s->queue_song_position_pointer ();
3539         }
3540
3541         show_verbose_cursor_time (t);
3542         _editor->UpdateAllTransportClocks (t);
3543 }
3544
3545 void
3546 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3547 {
3548         Drag::start_grab (event, c);
3549         setup_snap_delta (_editor->playhead_cursor->current_frame ());
3550
3551         _grab_zoom = _editor->samples_per_pixel;
3552
3553         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3554
3555         _editor->snap_to_with_modifier (where, event);
3556
3557         _editor->_dragging_playhead = true;
3558
3559         Session* s = _editor->session ();
3560
3561         /* grab the track canvas item as well */
3562
3563         _cursor.track_canvas_item().grab();
3564
3565         if (s) {
3566                 if (_was_rolling && _stop) {
3567                         s->request_stop ();
3568                 }
3569
3570                 if (s->is_auditioning()) {
3571                         s->cancel_audition ();
3572                 }
3573
3574
3575                 if (AudioEngine::instance()->connected()) {
3576
3577                         /* do this only if we're the engine is connected
3578                          * because otherwise this request will never be
3579                          * serviced and we'll busy wait forever. likewise,
3580                          * notice if we are disconnected while waiting for the
3581                          * request to be serviced.
3582                          */
3583
3584                         s->request_suspend_timecode_transmission ();
3585                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3586                                 /* twiddle our thumbs */
3587                         }
3588                 }
3589         }
3590
3591         fake_locate (where - snap_delta (event->button.state));
3592 }
3593
3594 void
3595 CursorDrag::motion (GdkEvent* event, bool)
3596 {
3597         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3598         _editor->snap_to_with_modifier (where, event);
3599         if (where != last_pointer_frame()) {
3600                 fake_locate (where - snap_delta (event->button.state));
3601         }
3602 }
3603
3604 void
3605 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3606 {
3607         _editor->_dragging_playhead = false;
3608
3609         _cursor.track_canvas_item().ungrab();
3610
3611         if (!movement_occurred && _stop) {
3612                 return;
3613         }
3614
3615         motion (event, false);
3616
3617         Session* s = _editor->session ();
3618         if (s) {
3619                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3620                 _editor->_pending_locate_request = true;
3621                 s->request_resume_timecode_transmission ();
3622         }
3623 }
3624
3625 void
3626 CursorDrag::aborted (bool)
3627 {
3628         _cursor.track_canvas_item().ungrab();
3629
3630         if (_editor->_dragging_playhead) {
3631                 _editor->session()->request_resume_timecode_transmission ();
3632                 _editor->_dragging_playhead = false;
3633         }
3634
3635         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3636 }
3637
3638 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3639         : RegionDrag (e, i, p, v)
3640 {
3641         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3642 }
3643
3644 void
3645 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3646 {
3647         Drag::start_grab (event, cursor);
3648
3649         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3650         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3651         setup_snap_delta (r->position ());
3652
3653         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3654 }
3655
3656 void
3657 FadeInDrag::setup_pointer_frame_offset ()
3658 {
3659         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3660         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3661         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3662 }
3663
3664 void
3665 FadeInDrag::motion (GdkEvent* event, bool)
3666 {
3667         framecnt_t fade_length;
3668
3669         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3670         _editor->snap_to_with_modifier (pos, event);
3671         pos -= snap_delta (event->button.state);
3672
3673         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3674
3675         if (pos < (region->position() + 64)) {
3676                 fade_length = 64; // this should be a minimum defined somewhere
3677         } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3678                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3679         } else {
3680                 fade_length = pos - region->position();
3681         }
3682
3683         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3684
3685                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3686
3687                 if (!tmp) {
3688                         continue;
3689                 }
3690
3691                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3692         }
3693
3694         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3695 }
3696
3697 void
3698 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3699 {
3700         if (!movement_occurred) {
3701                 return;
3702         }
3703
3704         framecnt_t fade_length;
3705         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3706         _editor->snap_to_with_modifier (pos, event);
3707         pos -= snap_delta (event->button.state);
3708
3709         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3710
3711         if (pos < (region->position() + 64)) {
3712                 fade_length = 64; // this should be a minimum defined somewhere
3713         } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3714                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3715         } else {
3716                 fade_length = pos - region->position();
3717         }
3718
3719         bool in_command = false;
3720
3721         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3722
3723                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3724
3725                 if (!tmp) {
3726                         continue;
3727                 }
3728
3729                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3730                 XMLNode &before = alist->get_state();
3731
3732                 tmp->audio_region()->set_fade_in_length (fade_length);
3733                 tmp->audio_region()->set_fade_in_active (true);
3734
3735                 if (!in_command) {
3736                         _editor->begin_reversible_command (_("change fade in length"));
3737                         in_command = true;
3738                 }
3739                 XMLNode &after = alist->get_state();
3740                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3741         }
3742
3743         if (in_command) {
3744                 _editor->commit_reversible_command ();
3745         }
3746 }
3747
3748 void
3749 FadeInDrag::aborted (bool)
3750 {
3751         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3752                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3753
3754                 if (!tmp) {
3755                         continue;
3756                 }
3757
3758                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3759         }
3760 }
3761
3762 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3763         : RegionDrag (e, i, p, v)
3764 {
3765         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3766 }
3767
3768 void
3769 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3770 {
3771         Drag::start_grab (event, cursor);
3772
3773         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3774         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3775         setup_snap_delta (r->last_frame ());
3776
3777         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3778 }
3779
3780 void
3781 FadeOutDrag::setup_pointer_frame_offset ()
3782 {
3783         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3784         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3785         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3786 }
3787
3788 void
3789 FadeOutDrag::motion (GdkEvent* event, bool)
3790 {
3791         framecnt_t fade_length;
3792
3793         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3794         _editor->snap_to_with_modifier (pos, event);
3795         pos -= snap_delta (event->button.state);
3796
3797         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3798
3799         if (pos > (region->last_frame() - 64)) {
3800                 fade_length = 64; // this should really be a minimum fade defined somewhere
3801         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3802                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3803         } else {
3804                 fade_length = region->last_frame() - pos;
3805         }
3806
3807         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3808
3809                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3810
3811                 if (!tmp) {
3812                         continue;
3813                 }
3814
3815                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3816         }
3817
3818         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3819 }
3820
3821 void
3822 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3823 {
3824         if (!movement_occurred) {
3825                 return;
3826         }
3827
3828         framecnt_t fade_length;
3829
3830         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3831         _editor->snap_to_with_modifier (pos, event);
3832         pos -= snap_delta (event->button.state);
3833
3834         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3835
3836         if (pos > (region->last_frame() - 64)) {
3837                 fade_length = 64; // this should really be a minimum fade defined somewhere
3838         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3839                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3840         } else {
3841                 fade_length = region->last_frame() - pos;
3842         }
3843
3844         bool in_command = false;
3845
3846         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3847
3848                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3849
3850                 if (!tmp) {
3851                         continue;
3852                 }
3853
3854                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3855                 XMLNode &before = alist->get_state();
3856
3857                 tmp->audio_region()->set_fade_out_length (fade_length);
3858                 tmp->audio_region()->set_fade_out_active (true);
3859
3860                 if (!in_command) {
3861                         _editor->begin_reversible_command (_("change fade out length"));
3862                         in_command = true;
3863                 }
3864                 XMLNode &after = alist->get_state();
3865                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3866         }
3867
3868         if (in_command) {
3869                 _editor->commit_reversible_command ();
3870         }
3871 }
3872
3873 void
3874 FadeOutDrag::aborted (bool)
3875 {
3876         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3877                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3878
3879                 if (!tmp) {
3880                         continue;
3881                 }
3882
3883                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3884         }
3885 }
3886
3887 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3888         : Drag (e, i)
3889         , _selection_changed (false)
3890 {
3891         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3892         Gtk::Window* toplevel = _editor->current_toplevel();
3893         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3894
3895         assert (_marker);
3896
3897         _points.push_back (ArdourCanvas::Duple (0, 0));
3898
3899         _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3900 }
3901
3902 MarkerDrag::~MarkerDrag ()
3903 {
3904         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3905                 delete i->location;
3906         }
3907 }
3908
3909 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3910 {
3911         location = new Location (*l);
3912         markers.push_back (m);
3913         move_both = false;
3914 }
3915
3916 void
3917 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3918 {
3919         Drag::start_grab (event, cursor);
3920
3921         bool is_start;
3922
3923         Location *location = _editor->find_location_from_marker (_marker, is_start);
3924         _editor->_dragging_edit_point = true;
3925
3926         update_item (location);
3927
3928         // _drag_line->show();
3929         // _line->raise_to_top();
3930
3931         if (is_start) {
3932                 show_verbose_cursor_time (location->start());
3933         } else {
3934                 show_verbose_cursor_time (location->end());
3935         }
3936         setup_snap_delta (is_start ? location->start() : location->end());
3937
3938         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3939
3940         switch (op) {
3941         case Selection::Toggle:
3942                 /* we toggle on the button release */
3943                 break;
3944         case Selection::Set:
3945                 if (!_editor->selection->selected (_marker)) {
3946                         _editor->selection->set (_marker);
3947                         _selection_changed = true;
3948                 }
3949                 break;
3950         case Selection::Extend:
3951         {
3952                 Locations::LocationList ll;
3953                 list<ArdourMarker*> to_add;
3954                 framepos_t s, e;
3955                 _editor->selection->markers.range (s, e);
3956                 s = min (_marker->position(), s);
3957                 e = max (_marker->position(), e);
3958                 s = min (s, e);
3959                 e = max (s, e);
3960                 if (e < max_framepos) {
3961                         ++e;
3962                 }
3963                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3964                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3965                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3966                         if (lm) {
3967                                 if (lm->start) {
3968                                         to_add.push_back (lm->start);
3969                                 }
3970                                 if (lm->end) {
3971                                         to_add.push_back (lm->end);
3972                                 }
3973                         }
3974                 }
3975                 if (!to_add.empty()) {
3976                         _editor->selection->add (to_add);
3977                         _selection_changed = true;
3978                 }
3979                 break;
3980         }
3981         case Selection::Add:
3982                 _editor->selection->add (_marker);
3983                 _selection_changed = true;
3984
3985                 break;
3986         }
3987
3988         /* Set up copies for us to manipulate during the drag
3989          */
3990
3991         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3992
3993                 Location* l = _editor->find_location_from_marker (*i, is_start);
3994
3995                 if (!l) {
3996                         continue;
3997                 }
3998
3999                 if (l->is_mark()) {
4000                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4001                 } else {
4002                         /* range: check that the other end of the range isn't
4003                            already there.
4004                         */
4005                         CopiedLocationInfo::iterator x;
4006                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4007                                 if (*(*x).location == *l) {
4008                                         break;
4009                                 }
4010                         }
4011                         if (x == _copied_locations.end()) {
4012                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4013                         } else {
4014                                 (*x).markers.push_back (*i);
4015                                 (*x).move_both = true;
4016                         }
4017                 }
4018
4019         }
4020 }
4021
4022 void
4023 MarkerDrag::setup_pointer_frame_offset ()
4024 {
4025         bool is_start;
4026         Location *location = _editor->find_location_from_marker (_marker, is_start);
4027         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4028 }
4029
4030 void
4031 MarkerDrag::motion (GdkEvent* event, bool)
4032 {
4033         framecnt_t f_delta = 0;
4034         bool is_start;
4035         bool move_both = false;
4036         Location *real_location;
4037         Location *copy_location = 0;
4038         framecnt_t const sd = snap_delta (event->button.state);
4039
4040         framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4041         framepos_t next = newframe;
4042
4043         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4044                 move_both = true;
4045         }
4046
4047         CopiedLocationInfo::iterator x;
4048
4049         /* find the marker we're dragging, and compute the delta */
4050
4051         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4052
4053                 copy_location = (*x).location;
4054
4055                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4056
4057                         /* this marker is represented by this
4058                          * CopiedLocationMarkerInfo
4059                          */
4060
4061                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4062                                 /* que pasa ?? */
4063                                 return;
4064                         }
4065
4066                         if (real_location->is_mark()) {
4067                                 f_delta = newframe - copy_location->start();
4068                         } else {
4069
4070
4071                                 switch (_marker->type()) {
4072                                 case ArdourMarker::SessionStart:
4073                                 case ArdourMarker::RangeStart:
4074                                 case ArdourMarker::LoopStart:
4075                                 case ArdourMarker::PunchIn:
4076                                         f_delta = newframe - copy_location->start();
4077                                         break;
4078
4079                                 case ArdourMarker::SessionEnd:
4080                                 case ArdourMarker::RangeEnd:
4081                                 case ArdourMarker::LoopEnd:
4082                                 case ArdourMarker::PunchOut:
4083                                         f_delta = newframe - copy_location->end();
4084                                         break;
4085                                 default:
4086                                         /* what kind of marker is this ? */
4087                                         return;
4088                                 }
4089                         }
4090
4091                         break;
4092                 }
4093         }
4094
4095         if (x == _copied_locations.end()) {
4096                 /* hmm, impossible - we didn't find the dragged marker */
4097                 return;
4098         }
4099
4100         /* now move them all */
4101
4102         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4103
4104                 copy_location = x->location;
4105
4106                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4107                         continue;
4108                 }
4109
4110                 if (real_location->locked()) {
4111                         continue;
4112                 }
4113
4114                 if (copy_location->is_mark()) {
4115
4116                         /* now move it */
4117
4118                         copy_location->set_start (copy_location->start() + f_delta);
4119
4120                 } else {
4121
4122                         framepos_t new_start = copy_location->start() + f_delta;
4123                         framepos_t new_end = copy_location->end() + f_delta;
4124
4125                         if (is_start) { // start-of-range marker
4126
4127                                 if (move_both || (*x).move_both) {
4128                                         copy_location->set_start (new_start);
4129                                         copy_location->set_end (new_end);
4130                                 } else  if (new_start < copy_location->end()) {
4131                                         copy_location->set_start (new_start);
4132                                 } else if (newframe > 0) {
4133                                         //_editor->snap_to (next, RoundUpAlways, true);
4134                                         copy_location->set_end (next);
4135                                         copy_location->set_start (newframe);
4136                                 }
4137
4138                         } else { // end marker
4139
4140                                 if (move_both || (*x).move_both) {
4141                                         copy_location->set_end (new_end);
4142                                         copy_location->set_start (new_start);
4143                                 } else if (new_end > copy_location->start()) {
4144                                         copy_location->set_end (new_end);
4145                                 } else if (newframe > 0) {
4146                                         //_editor->snap_to (next, RoundDownAlways, true);
4147                                         copy_location->set_start (next);
4148                                         copy_location->set_end (newframe);
4149                                 }
4150                         }
4151                 }
4152
4153                 update_item (copy_location);
4154
4155                 /* now lookup the actual GUI items used to display this
4156                  * location and move them to wherever the copy of the location
4157                  * is now. This means that the logic in ARDOUR::Location is
4158                  * still enforced, even though we are not (yet) modifying
4159                  * the real Location itself.
4160                  */
4161
4162                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4163
4164                 if (lm) {
4165                         lm->set_position (copy_location->start(), copy_location->end());
4166                 }
4167
4168         }
4169
4170         assert (!_copied_locations.empty());
4171
4172         show_verbose_cursor_time (newframe);
4173 }
4174
4175 void
4176 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4177 {
4178         if (!movement_occurred) {
4179
4180                 if (was_double_click()) {
4181                         _editor->rename_marker (_marker);
4182                         return;
4183                 }
4184
4185                 /* just a click, do nothing but finish
4186                    off the selection process
4187                 */
4188
4189                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4190                 switch (op) {
4191                 case Selection::Set:
4192                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4193                                 _editor->selection->set (_marker);
4194                                 _selection_changed = true;
4195                         }
4196                         break;
4197
4198                 case Selection::Toggle:
4199                         /* we toggle on the button release, click only */
4200                         _editor->selection->toggle (_marker);
4201                         _selection_changed = true;
4202
4203                         break;
4204
4205                 case Selection::Extend:
4206                 case Selection::Add:
4207                         break;
4208                 }
4209
4210                 if (_selection_changed) {
4211                         _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4212                         _editor->commit_reversible_selection_op();
4213                 }
4214
4215                 return;
4216         }
4217
4218         _editor->_dragging_edit_point = false;
4219
4220         XMLNode &before = _editor->session()->locations()->get_state();
4221         bool in_command = false;
4222
4223         MarkerSelection::iterator i;
4224         CopiedLocationInfo::iterator x;
4225         bool is_start;
4226
4227         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4228              x != _copied_locations.end() && i != _editor->selection->markers.end();
4229              ++i, ++x) {
4230
4231                 Location * location = _editor->find_location_from_marker (*i, is_start);
4232
4233                 if (location) {
4234
4235                         if (location->locked()) {
4236                                 continue;
4237                         }
4238                         if (!in_command) {
4239                                 _editor->begin_reversible_command ( _("move marker") );
4240                                 in_command = true;
4241                         }
4242                         if (location->is_mark()) {
4243                                 location->set_start (((*x).location)->start());
4244                         } else {
4245                                 location->set (((*x).location)->start(), ((*x).location)->end());
4246                         }
4247
4248                         if (location->is_session_range()) {
4249                                 _editor->session()->set_end_is_free (false);
4250                         }
4251                 }
4252         }
4253
4254         if (in_command) {
4255                 XMLNode &after = _editor->session()->locations()->get_state();
4256                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4257                 _editor->commit_reversible_command ();
4258         }
4259 }
4260
4261 void
4262 MarkerDrag::aborted (bool movement_occurred)
4263 {
4264         if (!movement_occurred) {
4265                 return;
4266         }
4267
4268         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4269
4270                 /* move all markers to their original location */
4271
4272
4273                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4274
4275                         bool is_start;
4276                         Location * location = _editor->find_location_from_marker (*m, is_start);
4277
4278                         if (location) {
4279                                 (*m)->set_position (is_start ? location->start() : location->end());
4280                         }
4281                 }
4282         }
4283 }
4284
4285 void
4286 MarkerDrag::update_item (Location*)
4287 {
4288         /* noop */
4289 }
4290
4291 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4292         : Drag (e, i)
4293         , _fixed_grab_x (0.0)
4294         , _fixed_grab_y (0.0)
4295         , _cumulative_x_drag (0.0)
4296         , _cumulative_y_drag (0.0)
4297         , _pushing (false)
4298         , _final_index (0)
4299 {
4300         if (_zero_gain_fraction < 0.0) {
4301                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4302         }
4303
4304         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4305
4306         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4307         assert (_point);
4308 }
4309
4310
4311 void
4312 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4313 {
4314         Drag::start_grab (event, _editor->cursors()->fader);
4315
4316         // start the grab at the center of the control point so
4317         // the point doesn't 'jump' to the mouse after the first drag
4318         _fixed_grab_x = _point->get_x();
4319         _fixed_grab_y = _point->get_y();
4320
4321         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4322         setup_snap_delta (pos);
4323
4324         float const fraction = 1 - (_point->get_y() / _point->line().height());
4325         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4326
4327         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4328
4329         if (!_point->can_slide ()) {
4330                 _x_constrained = true;
4331         }
4332 }
4333
4334 void
4335 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4336 {
4337         double dx = _drags->current_pointer_x() - last_pointer_x();
4338         double dy = current_pointer_y() - last_pointer_y();
4339         bool need_snap = true;
4340
4341         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4342                 dx *= 0.1;
4343                 dy *= 0.1;
4344                 need_snap = false;
4345         }
4346
4347         /* coordinate in pixels relative to the start of the region (for region-based automation)
4348            or track (for track-based automation) */
4349         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4350         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4351
4352         // calculate zero crossing point. back off by .01 to stay on the
4353         // positive side of zero
4354         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4355
4356         if (_x_constrained) {
4357                 cx = _fixed_grab_x;
4358         }
4359         if (_y_constrained) {
4360                 cy = _fixed_grab_y;
4361         }
4362
4363         _cumulative_x_drag = cx - _fixed_grab_x;
4364         _cumulative_y_drag = cy - _fixed_grab_y;
4365
4366         cx = max (0.0, cx);
4367         cy = max (0.0, cy);
4368         cy = min ((double) _point->line().height(), cy);
4369
4370         // make sure we hit zero when passing through
4371         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4372                 cy = zero_gain_y;
4373         }
4374
4375         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4376
4377         if (!_x_constrained && need_snap) {
4378                 _editor->snap_to_with_modifier (cx_frames, event);
4379         }
4380
4381         cx_frames -= snap_delta (event->button.state);
4382         cx_frames = min (cx_frames, _point->line().maximum_time());
4383
4384         float const fraction = 1.0 - (cy / _point->line().height());
4385
4386         if (first_motion) {
4387                 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4388                 _editor->begin_reversible_command (_("automation event move"));
4389                 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4390         }
4391         pair<double, float> result;
4392         result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4393
4394         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4395 }
4396
4397 void
4398 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4399 {
4400         if (!movement_occurred) {
4401
4402                 /* just a click */
4403                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4404                         _editor->reset_point_selection ();
4405                 }
4406
4407         } else {
4408                 _point->line().end_drag (_pushing, _final_index);
4409                 _editor->commit_reversible_command ();
4410         }
4411 }
4412
4413 void
4414 ControlPointDrag::aborted (bool)
4415 {
4416         _point->line().reset ();
4417 }
4418
4419 bool
4420 ControlPointDrag::active (Editing::MouseMode m)
4421 {
4422         if (m == Editing::MouseDraw) {
4423                 /* always active in mouse draw */
4424                 return true;
4425         }
4426
4427         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4428         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4429 }
4430
4431 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4432         : Drag (e, i)
4433         , _line (0)
4434         , _fixed_grab_x (0.0)
4435         , _fixed_grab_y (0.0)
4436         , _cumulative_y_drag (0)
4437         , _before (0)
4438         , _after (0)
4439 {
4440         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4441 }
4442
4443 void
4444 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4445 {
4446         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4447         assert (_line);
4448
4449         _item = &_line->grab_item ();
4450
4451         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4452            origin, and ditto for y.
4453         */
4454
4455         double mx = event->button.x;
4456         double my = event->button.y;
4457
4458         _line->grab_item().canvas_to_item (mx, my);
4459
4460         framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4461
4462         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4463                 /* no adjacent points */
4464                 return;
4465         }
4466
4467         Drag::start_grab (event, _editor->cursors()->fader);
4468
4469         /* store grab start in item frame */
4470         double const bx = _line->nth (_before)->get_x();
4471         double const ax = _line->nth (_after)->get_x();
4472         double const click_ratio = (ax - mx) / (ax - bx);
4473
4474         double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4475
4476         _fixed_grab_x = mx;
4477         _fixed_grab_y = cy;
4478
4479         double fraction = 1.0 - (cy / _line->height());
4480
4481         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4482 }
4483
4484 void
4485 LineDrag::motion (GdkEvent* event, bool first_move)
4486 {
4487         double dy = current_pointer_y() - last_pointer_y();
4488
4489         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4490                 dy *= 0.1;
4491         }
4492
4493         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4494
4495         _cumulative_y_drag = cy - _fixed_grab_y;
4496
4497         cy = max (0.0, cy);
4498         cy = min ((double) _line->height(), cy);
4499
4500         double const fraction = 1.0 - (cy / _line->height());
4501         uint32_t ignored;
4502
4503         if (first_move) {
4504                 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4505
4506                 _editor->begin_reversible_command (_("automation range move"));
4507                 _line->start_drag_line (_before, _after, initial_fraction);
4508         }
4509
4510         /* we are ignoring x position for this drag, so we can just pass in anything */
4511         pair<double, float> result;
4512
4513         result = _line->drag_motion (0, fraction, true, false, ignored);
4514         show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4515 }
4516
4517 void
4518 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4519 {
4520         if (movement_occurred) {
4521                 motion (event, false);
4522                 _line->end_drag (false, 0);
4523                 _editor->commit_reversible_command ();
4524         } else {
4525                 /* add a new control point on the line */
4526
4527                 AutomationTimeAxisView* atv;
4528
4529                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4530                         framepos_t where = grab_frame ();
4531
4532                         double cx = 0;
4533                         double cy = _fixed_grab_y;
4534
4535                         _line->grab_item().item_to_canvas (cx, cy);
4536
4537                         atv->add_automation_event (event, where, cy, false);
4538                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4539                         AudioRegionView* arv;
4540
4541                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4542                                 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4543                         }
4544                 }
4545         }
4546 }
4547
4548 void
4549 LineDrag::aborted (bool)
4550 {
4551         _line->reset ();
4552 }
4553
4554 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4555         : Drag (e, i),
4556           _line (0),
4557           _arv (0),
4558           _region_view_grab_x (0.0),
4559           _cumulative_x_drag (0),
4560           _before (0.0),
4561           _max_x (0)
4562 {
4563         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4564 }
4565
4566 void
4567 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4568 {
4569         Drag::start_grab (event);
4570
4571         _line = reinterpret_cast<Line*> (_item);
4572         assert (_line);
4573
4574         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4575
4576         double cx = event->button.x;
4577         double cy = event->button.y;
4578
4579         _item->parent()->canvas_to_item (cx, cy);
4580
4581         /* store grab start in parent frame */
4582         _region_view_grab_x = cx;
4583
4584         _before = *(float*) _item->get_data ("position");
4585
4586         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4587
4588         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4589 }
4590
4591 void
4592 FeatureLineDrag::motion (GdkEvent*, bool)
4593 {
4594         double dx = _drags->current_pointer_x() - last_pointer_x();
4595
4596         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4597
4598         _cumulative_x_drag += dx;
4599
4600         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4601
4602         if (cx > _max_x){
4603                 cx = _max_x;
4604         }
4605         else if(cx < 0){
4606                 cx = 0;
4607         }
4608
4609         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4610         assert (bbox);
4611         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4612
4613         float *pos = new float;
4614         *pos = cx;
4615
4616         _line->set_data ("position", pos);
4617
4618         _before = cx;
4619 }
4620
4621 void
4622 FeatureLineDrag::finished (GdkEvent*, bool)
4623 {
4624         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4625         _arv->update_transient(_before, _before);
4626 }
4627
4628 void
4629 FeatureLineDrag::aborted (bool)
4630 {
4631         //_line->reset ();
4632 }
4633
4634 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4635         : Drag (e, i)
4636         , _vertical_only (false)
4637 {
4638         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4639 }
4640
4641 void
4642 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4643 {
4644         Drag::start_grab (event);
4645         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4646 }
4647
4648 void
4649 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4650 {
4651         framepos_t start;
4652         framepos_t end;
4653         double y1;
4654         double y2;
4655
4656         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4657
4658         framepos_t grab = grab_frame ();
4659         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4660                 _editor->snap_to_with_modifier (grab, event);
4661         } else {
4662                 grab = raw_grab_frame ();
4663         }
4664
4665         /* base start and end on initial click position */
4666
4667         if (pf < grab) {
4668                 start = pf;
4669                 end = grab;
4670         } else {
4671                 end = pf;
4672                 start = grab;
4673         }
4674
4675         if (current_pointer_y() < grab_y()) {
4676                 y1 = current_pointer_y();
4677                 y2 = grab_y();
4678         } else {
4679                 y2 = current_pointer_y();
4680                 y1 = grab_y();
4681         }
4682
4683         if (start != end || y1 != y2) {
4684
4685                 double x1 = _editor->sample_to_pixel (start);
4686                 double x2 = _editor->sample_to_pixel (end);
4687                 const double min_dimension = 2.0;
4688
4689                 if (_vertical_only) {
4690                         /* fixed 10 pixel width */
4691                         x2 = x1 + 10;
4692                 } else {
4693                         if (x2 < x1) {
4694                                 x2 = min (x1 - min_dimension, x2);
4695                         } else {
4696                                 x2 = max (x1 + min_dimension, x2);
4697                         }
4698                 }
4699
4700                 if (y2 < y1) {
4701                         y2 = min (y1 - min_dimension, y2);
4702                 } else {
4703                         y2 = max (y1 + min_dimension, y2);
4704                 }
4705
4706                 /* translate rect into item space and set */
4707
4708                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4709
4710                 /* this drag is a _trackview_only == true drag, so the y1 and
4711                  * y2 (computed using current_pointer_y() and grab_y()) will be
4712                  * relative to the top of the trackview group). The
4713                  * rubberband rect has the same parent/scroll offset as the
4714                  * the trackview group, so we can use the "r" rect directly
4715                  * to set the shape of the rubberband.
4716                  */
4717
4718                 _editor->rubberband_rect->set (r);
4719                 _editor->rubberband_rect->show();
4720                 _editor->rubberband_rect->raise_to_top();
4721
4722                 show_verbose_cursor_time (pf);
4723
4724                 do_select_things (event, true);
4725         }
4726 }
4727
4728 void
4729 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4730 {
4731         framepos_t x1;
4732         framepos_t x2;
4733         framepos_t grab = grab_frame ();
4734         framepos_t lpf = last_pointer_frame ();
4735
4736         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4737                 grab = raw_grab_frame ();
4738                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4739         }
4740
4741         if (grab < lpf) {
4742                 x1 = grab;
4743                 x2 = lpf;
4744         } else {
4745                 x2 = grab;
4746                 x1 = lpf;
4747         }
4748
4749         double y1;
4750         double y2;
4751
4752         if (current_pointer_y() < grab_y()) {
4753                 y1 = current_pointer_y();
4754                 y2 = grab_y();
4755         } else {
4756                 y2 = current_pointer_y();
4757                 y1 = grab_y();
4758         }
4759
4760         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4761 }
4762
4763 void
4764 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4765 {
4766         if (movement_occurred) {
4767
4768                 motion (event, false);
4769                 do_select_things (event, false);
4770
4771         } else {
4772
4773                 /* just a click */
4774
4775                 bool do_deselect = true;
4776                 MidiTimeAxisView* mtv;
4777
4778                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4779                         /* MIDI track */
4780                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4781                                 /* nothing selected */
4782                                 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4783                                 do_deselect = false;
4784                         }
4785                 }
4786
4787                 /* do not deselect if Primary or Tertiary (toggle-select or
4788                  * extend-select are pressed.
4789                  */
4790
4791                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4792                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4793                     do_deselect) {
4794                         deselect_things ();
4795                 }
4796
4797         }
4798
4799         _editor->rubberband_rect->hide();
4800 }
4801
4802 void
4803 RubberbandSelectDrag::aborted (bool)
4804 {
4805         _editor->rubberband_rect->hide ();
4806 }
4807
4808 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4809         : RegionDrag (e, i, p, v)
4810 {
4811         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4812 }
4813
4814 void
4815 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4816 {
4817         Drag::start_grab (event, cursor);
4818
4819         _editor->get_selection().add (_primary);
4820
4821         framepos_t where = _primary->region()->position();
4822         setup_snap_delta (where);
4823
4824         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4825 }
4826
4827 void
4828 TimeFXDrag::motion (GdkEvent* event, bool)
4829 {
4830         RegionView* rv = _primary;
4831         StreamView* cv = rv->get_time_axis_view().view ();
4832
4833         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4834         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4835         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4836         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4837         _editor->snap_to_with_modifier (pf, event);
4838         pf -= snap_delta (event->button.state);
4839
4840         if (pf > rv->region()->position()) {
4841                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4842         }
4843
4844         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4845 }
4846
4847 void
4848 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4849 {
4850         /* this may have been a single click, no drag. We still want the dialog
4851            to show up in that case, so that the user can manually edit the
4852            parameters for the timestretch.
4853         */
4854
4855         float fraction = 1.0;
4856
4857         if (movement_occurred) {
4858
4859                 motion (event, false);
4860
4861                 _primary->get_time_axis_view().hide_timestretch ();
4862
4863                 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4864
4865                 if (adjusted_frame_pos < _primary->region()->position()) {
4866                         /* backwards drag of the left edge - not usable */
4867                         return;
4868                 }
4869
4870                 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4871
4872                 fraction = (double) newlen / (double) _primary->region()->length();
4873
4874 #ifndef USE_RUBBERBAND
4875                 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4876                 if (_primary->region()->data_type() == DataType::AUDIO) {
4877                         fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4878                 }
4879 #endif
4880         }
4881
4882         if (!_editor->get_selection().regions.empty()) {
4883                 /* primary will already be included in the selection, and edit
4884                    group shared editing will propagate selection across
4885                    equivalent regions, so just use the current region
4886                    selection.
4887                 */
4888
4889                 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4890                         error << _("An error occurred while executing time stretch operation") << endmsg;
4891                 }
4892         }
4893 }
4894
4895 void
4896 TimeFXDrag::aborted (bool)
4897 {
4898         _primary->get_time_axis_view().hide_timestretch ();
4899 }
4900
4901 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4902         : Drag (e, i)
4903 {
4904         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4905 }
4906
4907 void
4908 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4909 {
4910         Drag::start_grab (event);
4911 }
4912
4913 void
4914 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4915 {
4916         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4917 }
4918
4919 void
4920 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4921 {
4922         if (movement_occurred && _editor->session()) {
4923                 /* make sure we stop */
4924                 _editor->session()->request_transport_speed (0.0);
4925         }
4926 }
4927
4928 void
4929 ScrubDrag::aborted (bool)
4930 {
4931         /* XXX: TODO */
4932 }
4933
4934 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4935         : Drag (e, i)
4936         , _operation (o)
4937         , _add (false)
4938         , _time_selection_at_start (!_editor->get_selection().time.empty())
4939 {
4940         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4941
4942         if (_time_selection_at_start) {
4943                 start_at_start = _editor->get_selection().time.start();
4944                 end_at_start = _editor->get_selection().time.end_frame();
4945         }
4946 }
4947
4948 void
4949 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4950 {
4951         if (_editor->session() == 0) {
4952                 return;
4953         }
4954
4955         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4956
4957         switch (_operation) {
4958         case CreateSelection:
4959                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4960                         _add = true;
4961                 } else {
4962                         _add = false;
4963                 }
4964                 cursor = _editor->cursors()->selector;
4965                 Drag::start_grab (event, cursor);
4966                 break;
4967
4968         case SelectionStartTrim:
4969                 if (_editor->clicked_axisview) {
4970                         _editor->clicked_axisview->order_selection_trims (_item, true);
4971                 }
4972                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4973                 break;
4974
4975         case SelectionEndTrim:
4976                 if (_editor->clicked_axisview) {
4977                         _editor->clicked_axisview->order_selection_trims (_item, false);
4978                 }
4979                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4980                 break;
4981
4982         case SelectionMove:
4983                 Drag::start_grab (event, cursor);
4984                 break;
4985
4986         case SelectionExtend:
4987                 Drag::start_grab (event, cursor);
4988                 break;
4989         }
4990
4991         if (_operation == SelectionMove) {
4992                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4993         } else {
4994                 show_verbose_cursor_time (adjusted_current_frame (event));
4995         }
4996 }
4997
4998 void
4999 SelectionDrag::setup_pointer_frame_offset ()
5000 {
5001         switch (_operation) {
5002         case CreateSelection:
5003                 _pointer_frame_offset = 0;
5004                 break;
5005
5006         case SelectionStartTrim:
5007         case SelectionMove:
5008                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5009                 break;
5010
5011         case SelectionEndTrim:
5012                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5013                 break;
5014
5015         case SelectionExtend:
5016                 break;
5017         }
5018 }
5019
5020 void
5021 SelectionDrag::motion (GdkEvent* event, bool first_move)
5022 {
5023         framepos_t start = 0;
5024         framepos_t end = 0;
5025         framecnt_t length = 0;
5026         framecnt_t distance = 0;
5027
5028         framepos_t const pending_position = adjusted_current_frame (event);
5029
5030         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5031                 return;
5032         }
5033
5034         switch (_operation) {
5035         case CreateSelection:
5036         {
5037                 framepos_t grab = grab_frame ();
5038
5039                 if (first_move) {
5040                         grab = adjusted_current_frame (event, false);
5041                         if (grab < pending_position) {
5042                                 _editor->snap_to (grab, RoundDownMaybe);
5043                         }  else {
5044                                 _editor->snap_to (grab, RoundUpMaybe);
5045                         }
5046                 }
5047
5048                 if (pending_position < grab) {
5049                         start = pending_position;
5050                         end = grab;
5051                 } else {
5052                         end = pending_position;
5053                         start = grab;
5054                 }
5055
5056                 /* first drag: Either add to the selection
5057                    or create a new selection
5058                 */
5059
5060                 if (first_move) {
5061
5062                         if (_add) {
5063
5064                                 /* adding to the selection */
5065                                 _editor->set_selected_track_as_side_effect (Selection::Add);
5066                                 _editor->clicked_selection = _editor->selection->add (start, end);
5067                                 _add = false;
5068
5069                         } else {
5070
5071                                 /* new selection */
5072
5073                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5074                                         _editor->set_selected_track_as_side_effect (Selection::Set);
5075                                 }
5076
5077                                 _editor->clicked_selection = _editor->selection->set (start, end);
5078                         }
5079                 }
5080
5081                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5082                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5083                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5084                 if (atest) {
5085                         _editor->selection->add (atest);
5086                         break;
5087                 }
5088
5089                 /* select all tracks within the rectangle that we've marked out so far */
5090                 TrackViewList new_selection;
5091                 TrackViewList& all_tracks (_editor->track_views);
5092
5093                 ArdourCanvas::Coord const top = grab_y();
5094                 ArdourCanvas::Coord const bottom = current_pointer_y();
5095
5096                 if (top >= 0 && bottom >= 0) {
5097
5098                         //first, find the tracks that are covered in the y range selection
5099                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5100                                 if ((*i)->covered_by_y_range (top, bottom)) {
5101                                         new_selection.push_back (*i);
5102                                 }
5103                         }
5104
5105                         //now find any tracks that are GROUPED with the tracks we selected
5106                         TrackViewList grouped_add = new_selection;
5107                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5108                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5109                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5110                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5111                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5112                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5113                                                         grouped_add.push_back (*j);
5114                                         }
5115                                 }
5116                         }
5117
5118                         //now compare our list with the current selection, and add or remove as necessary
5119                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5120                         TrackViewList tracks_to_add;
5121                         TrackViewList tracks_to_remove;
5122                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5123                                 if ( !_editor->selection->tracks.contains ( *i ) )
5124                                         tracks_to_add.push_back ( *i );
5125                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5126                                 if ( !grouped_add.contains ( *i ) )
5127                                         tracks_to_remove.push_back ( *i );
5128                         _editor->selection->add(tracks_to_add);
5129                         _editor->selection->remove(tracks_to_remove);
5130
5131                 }
5132         }
5133         break;
5134
5135         case SelectionStartTrim:
5136
5137                 end = _editor->selection->time[_editor->clicked_selection].end;
5138
5139                 if (pending_position > end) {
5140                         start = end;
5141                 } else {
5142                         start = pending_position;
5143                 }
5144                 break;
5145
5146         case SelectionEndTrim:
5147
5148                 start = _editor->selection->time[_editor->clicked_selection].start;
5149
5150                 if (pending_position < start) {
5151                         end = start;
5152                 } else {
5153                         end = pending_position;
5154                 }
5155
5156                 break;
5157
5158         case SelectionMove:
5159
5160                 start = _editor->selection->time[_editor->clicked_selection].start;
5161                 end = _editor->selection->time[_editor->clicked_selection].end;
5162
5163                 length = end - start;
5164                 distance = pending_position - start;
5165                 start = pending_position;
5166                 _editor->snap_to (start);
5167
5168                 end = start + length;
5169
5170                 break;
5171
5172         case SelectionExtend:
5173                 break;
5174         }
5175
5176         if (start != end) {
5177                 switch (_operation) {
5178                 case SelectionMove:
5179                         if (_time_selection_at_start) {
5180                                 _editor->selection->move_time (distance);
5181                         }
5182                         break;
5183                 default:
5184                         _editor->selection->replace (_editor->clicked_selection, start, end);
5185                 }
5186         }
5187
5188         if (_operation == SelectionMove) {
5189                 show_verbose_cursor_time(start);
5190         } else {
5191                 show_verbose_cursor_time(pending_position);
5192         }
5193 }
5194
5195 void
5196 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5197 {
5198         Session* s = _editor->session();
5199
5200         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5201         if (movement_occurred) {
5202                 motion (event, false);
5203                 /* XXX this is not object-oriented programming at all. ick */
5204                 if (_editor->selection->time.consolidate()) {
5205                         _editor->selection->TimeChanged ();
5206                 }
5207
5208                 /* XXX what if its a music time selection? */
5209                 if (s) {
5210                         if (s->get_play_range() && s->transport_rolling()) {
5211                                 s->request_play_range (&_editor->selection->time, true);
5212                         } else if (!s->config.get_external_sync()) {
5213                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5214                                         if (_operation == SelectionEndTrim)
5215                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5216                                         else
5217                                                 s->request_locate (_editor->get_selection().time.start());
5218                                 }
5219                         }
5220
5221                         if (_editor->get_selection().time.length() != 0) {
5222                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5223                         } else {
5224                                 s->clear_range_selection ();
5225                         }
5226                 }
5227
5228         } else {
5229                 /* just a click, no pointer movement.
5230                  */
5231
5232                 if (_operation == SelectionExtend) {
5233                         if (_time_selection_at_start) {
5234                                 framepos_t pos = adjusted_current_frame (event, false);
5235                                 framepos_t start = min (pos, start_at_start);
5236                                 framepos_t end = max (pos, end_at_start);
5237                                 _editor->selection->set (start, end);
5238                         }
5239                 } else {
5240                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5241                                 if (_editor->clicked_selection) {
5242                                         _editor->selection->remove (_editor->clicked_selection);
5243                                 }
5244                         } else {
5245                                 if (!_editor->clicked_selection) {
5246                                         _editor->selection->clear_time();
5247                                 }
5248                         }
5249                 }
5250
5251                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5252                         _editor->selection->set (_editor->clicked_axisview);
5253                 }
5254
5255                 if (s && s->get_play_range () && s->transport_rolling()) {
5256                         s->request_stop (false, false);
5257                 }
5258
5259         }
5260
5261         _editor->stop_canvas_autoscroll ();
5262         _editor->clicked_selection = 0;
5263         _editor->commit_reversible_selection_op ();
5264 }
5265
5266 void
5267 SelectionDrag::aborted (bool)
5268 {
5269         /* XXX: TODO */
5270 }
5271
5272 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5273         : Drag (e, i, false),
5274           _operation (o),
5275           _copy (false)
5276 {
5277         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5278
5279         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5280                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5281                                                                       physical_screen_height (_editor->current_toplevel()->get_window())));
5282         _drag_rect->hide ();
5283
5284         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5285         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5286 }
5287
5288 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5289 {
5290         /* normal canvas items will be cleaned up when their parent group is deleted. But
5291            this item is created as the child of a long-lived parent group, and so we
5292            need to explicitly delete it.
5293         */
5294         delete _drag_rect;
5295 }
5296
5297 void
5298 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5299 {
5300         if (_editor->session() == 0) {
5301                 return;
5302         }
5303
5304         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5305
5306         if (!_editor->temp_location) {
5307                 _editor->temp_location = new Location (*_editor->session());
5308         }
5309
5310         switch (_operation) {
5311         case CreateSkipMarker:
5312         case CreateRangeMarker:
5313         case CreateTransportMarker:
5314         case CreateCDMarker:
5315
5316                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5317                         _copy = true;
5318                 } else {
5319                         _copy = false;
5320                 }
5321                 cursor = _editor->cursors()->selector;
5322                 break;
5323         }
5324
5325         Drag::start_grab (event, cursor);
5326
5327         show_verbose_cursor_time (adjusted_current_frame (event));
5328 }
5329
5330 void
5331 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5332 {
5333         framepos_t start = 0;
5334         framepos_t end = 0;
5335         ArdourCanvas::Rectangle *crect;
5336
5337         switch (_operation) {
5338         case CreateSkipMarker:
5339                 crect = _editor->range_bar_drag_rect;
5340                 break;
5341         case CreateRangeMarker:
5342                 crect = _editor->range_bar_drag_rect;
5343                 break;
5344         case CreateTransportMarker:
5345                 crect = _editor->transport_bar_drag_rect;
5346                 break;
5347         case CreateCDMarker:
5348                 crect = _editor->cd_marker_bar_drag_rect;
5349                 break;
5350         default:
5351                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5352                 return;
5353                 break;
5354         }
5355
5356         framepos_t const pf = adjusted_current_frame (event);
5357
5358         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5359                 framepos_t grab = grab_frame ();
5360                 _editor->snap_to (grab);
5361
5362                 if (pf < grab_frame()) {
5363                         start = pf;
5364                         end = grab;
5365                 } else {
5366                         end = pf;
5367                         start = grab;
5368                 }
5369
5370                 /* first drag: Either add to the selection
5371                    or create a new selection.
5372                 */
5373
5374                 if (first_move) {
5375
5376                         _editor->temp_location->set (start, end);
5377
5378                         crect->show ();
5379
5380                         update_item (_editor->temp_location);
5381                         _drag_rect->show();
5382                         //_drag_rect->raise_to_top();
5383
5384                 }
5385         }
5386
5387         if (start != end) {
5388                 _editor->temp_location->set (start, end);
5389
5390                 double x1 = _editor->sample_to_pixel (start);
5391                 double x2 = _editor->sample_to_pixel (end);
5392                 crect->set_x0 (x1);
5393                 crect->set_x1 (x2);
5394
5395                 update_item (_editor->temp_location);
5396         }
5397
5398         show_verbose_cursor_time (pf);
5399
5400 }
5401
5402 void
5403 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5404 {
5405         Location * newloc = 0;
5406         string rangename;
5407         int flags;
5408
5409         if (movement_occurred) {
5410                 motion (event, false);
5411                 _drag_rect->hide();
5412
5413                 switch (_operation) {
5414                 case CreateSkipMarker:
5415                 case CreateRangeMarker:
5416                 case CreateCDMarker:
5417                     {
5418                         XMLNode &before = _editor->session()->locations()->get_state();
5419                         if (_operation == CreateSkipMarker) {
5420                                 _editor->begin_reversible_command (_("new skip marker"));
5421                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5422                                 flags = Location::IsRangeMarker | Location::IsSkip;
5423                                 _editor->range_bar_drag_rect->hide();
5424                         } else if (_operation == CreateCDMarker) {
5425                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5426                                 _editor->begin_reversible_command (_("new CD marker"));
5427                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5428                                 _editor->cd_marker_bar_drag_rect->hide();
5429                         } else {
5430                                 _editor->begin_reversible_command (_("new skip marker"));
5431                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5432                                 flags = Location::IsRangeMarker;
5433                                 _editor->range_bar_drag_rect->hide();
5434                         }
5435                         newloc = new Location (
5436                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5437                                 );
5438
5439                         _editor->session()->locations()->add (newloc, true);
5440                         XMLNode &after = _editor->session()->locations()->get_state();
5441                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5442                         _editor->commit_reversible_command ();
5443                         break;
5444                     }
5445
5446                 case CreateTransportMarker:
5447                         // popup menu to pick loop or punch
5448                         _editor->new_transport_marker_context_menu (&event->button, _item);
5449                         break;
5450                 }
5451
5452         } else {
5453
5454                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5455
5456                 if (_operation == CreateTransportMarker) {
5457
5458                         /* didn't drag, so just locate */
5459
5460                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5461
5462                 } else if (_operation == CreateCDMarker) {
5463
5464                         /* didn't drag, but mark is already created so do
5465                          * nothing */
5466
5467                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5468
5469                         framepos_t start;
5470                         framepos_t end;
5471
5472                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5473
5474                         if (end == max_framepos) {
5475                                 end = _editor->session()->current_end_frame ();
5476                         }
5477
5478                         if (start == max_framepos) {
5479                                 start = _editor->session()->current_start_frame ();
5480                         }
5481
5482                         switch (_editor->mouse_mode) {
5483                         case MouseObject:
5484                                 /* find the two markers on either side and then make the selection from it */
5485                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5486                                 break;
5487
5488                         case MouseRange:
5489                                 /* find the two markers on either side of the click and make the range out of it */
5490                                 _editor->selection->set (start, end);
5491                                 break;
5492
5493                         default:
5494                                 break;
5495                         }
5496                 }
5497         }
5498
5499         _editor->stop_canvas_autoscroll ();
5500 }
5501
5502 void
5503 RangeMarkerBarDrag::aborted (bool movement_occurred)
5504 {
5505         if (movement_occurred) {
5506                 _drag_rect->hide ();
5507         }
5508 }
5509
5510 void
5511 RangeMarkerBarDrag::update_item (Location* location)
5512 {
5513         double const x1 = _editor->sample_to_pixel (location->start());
5514         double const x2 = _editor->sample_to_pixel (location->end());
5515
5516         _drag_rect->set_x0 (x1);
5517         _drag_rect->set_x1 (x2);
5518 }
5519
5520 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5521         : Drag (e, i)
5522         , _cumulative_dx (0)
5523         , _cumulative_dy (0)
5524         , _was_selected (false)
5525 {
5526         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5527
5528         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5529         assert (_primary);
5530         _region = &_primary->region_view ();
5531         _note_height = _region->midi_stream_view()->note_height ();
5532 }
5533
5534 void
5535 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5536 {
5537         Drag::start_grab (event);
5538         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5539
5540         if (!(_was_selected = _primary->selected())) {
5541
5542                 /* tertiary-click means extend selection - we'll do that on button release,
5543                    so don't add it here, because otherwise we make it hard to figure
5544                    out the "extend-to" range.
5545                 */
5546
5547                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5548
5549                 if (!extend) {
5550                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5551
5552                         if (add) {
5553                                 _region->note_selected (_primary, true);
5554                         } else {
5555                                 _editor->get_selection().clear_points();
5556                                 _region->unique_select (_primary);
5557                         }
5558                 }
5559         }
5560 }
5561
5562 /** @return Current total drag x change in frames */
5563 frameoffset_t
5564 NoteDrag::total_dx (const guint state) const
5565 {
5566         /* dx in frames */
5567         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5568
5569         /* primary note time */
5570         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5571
5572         /* new time of the primary note in session frames */
5573         frameoffset_t st = n + dx + snap_delta (state);
5574
5575         framepos_t const rp = _region->region()->position ();
5576
5577         /* prevent the note being dragged earlier than the region's position */
5578         st = max (st, rp);
5579
5580         /* possibly snap and return corresponding delta */
5581
5582         bool snap = true;
5583
5584         if (ArdourKeyboard::indicates_snap (state)) {
5585                 if (_editor->snap_mode () != SnapOff) {
5586                         snap = false;
5587                 }
5588         } else {
5589                 if (_editor->snap_mode () == SnapOff) {
5590                         snap = false;
5591                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5592                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5593                                 snap = true;
5594                         }
5595                 }
5596         }
5597
5598         frameoffset_t ret;
5599         if (snap) {
5600                 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5601                 ret =  _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5602         } else {
5603                 ret = st - n - snap_delta (state);
5604         }
5605         return ret;
5606 }
5607
5608 /** @return Current total drag y change in note number */
5609 int8_t
5610 NoteDrag::total_dy () const
5611 {
5612         MidiStreamView* msv = _region->midi_stream_view ();
5613         double const y = _region->midi_view()->y_position ();
5614         /* new current note */
5615         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5616         /* clamp */
5617         n = max (msv->lowest_note(), n);
5618         n = min (msv->highest_note(), n);
5619         /* and work out delta */
5620         return n - msv->y_to_note (grab_y() - y);
5621 }
5622
5623 void
5624 NoteDrag::motion (GdkEvent * event, bool)
5625 {
5626         /* Total change in x and y since the start of the drag */
5627         frameoffset_t const dx = total_dx (event->button.state);
5628         int8_t const dy = total_dy ();
5629
5630         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5631         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5632         double const tdy = -dy * _note_height - _cumulative_dy;
5633
5634         if (tdx || tdy) {
5635                 _cumulative_dx += tdx;
5636                 _cumulative_dy += tdy;
5637
5638                 int8_t note_delta = total_dy();
5639
5640                 _region->move_selection (tdx, tdy, note_delta);
5641
5642                 /* the new note value may be the same as the old one, but we
5643                  * don't know what that means because the selection may have
5644                  * involved more than one note and we might be doing something
5645                  * odd with them. so show the note value anyway, always.
5646                  */
5647
5648                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5649
5650                 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5651         }
5652 }
5653
5654 void
5655 NoteDrag::finished (GdkEvent* ev, bool moved)
5656 {
5657         if (!moved) {
5658                 /* no motion - select note */
5659
5660                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5661                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5662
5663                         bool changed = false;
5664
5665                         if (_was_selected) {
5666                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5667                                 if (add) {
5668                                         _region->note_deselected (_primary);
5669                                         changed = true;
5670                                 } else {
5671                                         _editor->get_selection().clear_points();
5672                                         _region->unique_select (_primary);
5673                                         changed = true;
5674                                 }
5675                         } else {
5676                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5677                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5678
5679                                 if (!extend && !add && _region->selection_size() > 1) {
5680                                         _editor->get_selection().clear_points();
5681                                         _region->unique_select (_primary);
5682                                         changed = true;
5683                                 } else if (extend) {
5684                                         _region->note_selected (_primary, true, true);
5685                                         changed = true;
5686                                 } else {
5687                                         /* it was added during button press */
5688                                         changed = true;
5689
5690                                 }
5691                         }
5692
5693                         if (changed) {
5694                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5695                                 _editor->commit_reversible_selection_op();
5696                         }
5697                 }
5698         } else {
5699                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5700         }
5701 }
5702
5703 void
5704 NoteDrag::aborted (bool)
5705 {
5706         /* XXX: TODO */
5707 }
5708
5709 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5710 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5711         : Drag (editor, atv->base_item ())
5712         , _ranges (r)
5713         , _y_origin (atv->y_position())
5714         , _nothing_to_drag (false)
5715 {
5716         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5717         setup (atv->lines ());
5718 }
5719
5720 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5721 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5722         : Drag (editor, rv->get_canvas_group ())
5723         , _ranges (r)
5724         , _y_origin (rv->get_time_axis_view().y_position())
5725         , _nothing_to_drag (false)
5726         , _integral (false)
5727 {
5728         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5729
5730         list<boost::shared_ptr<AutomationLine> > lines;
5731
5732         AudioRegionView*      audio_view;
5733         AutomationRegionView* automation_view;
5734         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5735                 lines.push_back (audio_view->get_gain_line ());
5736         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5737                 lines.push_back (automation_view->line ());
5738                 _integral = true;
5739         } else {
5740                 error << _("Automation range drag created for invalid region type") << endmsg;
5741         }
5742
5743         setup (lines);
5744 }
5745
5746 /** @param lines AutomationLines to drag.
5747  *  @param offset Offset from the session start to the points in the AutomationLines.
5748  */
5749 void
5750 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5751 {
5752         /* find the lines that overlap the ranges being dragged */
5753         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5754         while (i != lines.end ()) {
5755                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5756                 ++j;
5757
5758                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5759
5760                 /* check this range against all the AudioRanges that we are using */
5761                 list<AudioRange>::const_iterator k = _ranges.begin ();
5762                 while (k != _ranges.end()) {
5763                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5764                                 break;
5765                         }
5766                         ++k;
5767                 }
5768
5769                 /* add it to our list if it overlaps at all */
5770                 if (k != _ranges.end()) {
5771                         Line n;
5772                         n.line = *i;
5773                         n.state = 0;
5774                         n.range = r;
5775                         _lines.push_back (n);
5776                 }
5777
5778                 i = j;
5779         }
5780
5781         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5782 }
5783
5784 double
5785 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5786 {
5787         return 1.0 - ((global_y - _y_origin) / line->height());
5788 }
5789
5790 double
5791 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5792 {
5793         const double v = list->eval(x);
5794         return _integral ? rint(v) : v;
5795 }
5796
5797 void
5798 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5799 {
5800         Drag::start_grab (event, cursor);
5801
5802         /* Get line states before we start changing things */
5803         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5804                 i->state = &i->line->get_state ();
5805                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5806         }
5807
5808         if (_ranges.empty()) {
5809
5810                 /* No selected time ranges: drag all points */
5811                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5812                         uint32_t const N = i->line->npoints ();
5813                         for (uint32_t j = 0; j < N; ++j) {
5814                                 i->points.push_back (i->line->nth (j));
5815                         }
5816                 }
5817
5818         }
5819
5820         if (_nothing_to_drag) {
5821                 return;
5822         }
5823 }
5824
5825 void
5826 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5827 {
5828         if (_nothing_to_drag && !first_move) {
5829                 return;
5830         }
5831
5832         if (first_move) {
5833                 _editor->begin_reversible_command (_("automation range move"));
5834
5835                 if (!_ranges.empty()) {
5836
5837                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5838
5839                                 framecnt_t const half = (i->start + i->end) / 2;
5840
5841                                 /* find the line that this audio range starts in */
5842                                 list<Line>::iterator j = _lines.begin();
5843                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5844                                         ++j;
5845                                 }
5846
5847                                 if (j != _lines.end()) {
5848                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5849
5850                                 /* j is the line that this audio range starts in; fade into it;
5851                                    64 samples length plucked out of thin air.
5852                                 */
5853
5854                                         framepos_t a = i->start + 64;
5855                                         if (a > half) {
5856                                                 a = half;
5857                                         }
5858
5859                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5860                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5861
5862                                         XMLNode &before = the_list->get_state();
5863                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5864                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5865
5866                                         if (add_p || add_q) {
5867                                                 _editor->session()->add_command (
5868                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5869                                         }
5870                                 }
5871
5872                                 /* same thing for the end */
5873
5874                                 j = _lines.begin();
5875                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5876                                         ++j;
5877                                 }
5878
5879                                 if (j != _lines.end()) {
5880                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5881
5882                                         /* j is the line that this audio range starts in; fade out of it;
5883                                            64 samples length plucked out of thin air.
5884                                         */
5885
5886                                         framepos_t b = i->end - 64;
5887                                         if (b < half) {
5888                                                 b = half;
5889                                         }
5890
5891                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5892                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5893
5894                                         XMLNode &before = the_list->get_state();
5895                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5896                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5897
5898                                         if (add_p || add_q) {
5899                                                 _editor->session()->add_command (
5900                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5901                                         }
5902                                 }
5903                         }
5904
5905                         _nothing_to_drag = true;
5906
5907                         /* Find all the points that should be dragged and put them in the relevant
5908                            points lists in the Line structs.
5909                         */
5910
5911                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5912
5913                                 uint32_t const N = i->line->npoints ();
5914                                 for (uint32_t j = 0; j < N; ++j) {
5915
5916                                         /* here's a control point on this line */
5917                                         ControlPoint* p = i->line->nth (j);
5918                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5919
5920                                         /* see if it's inside a range */
5921                                         list<AudioRange>::const_iterator k = _ranges.begin ();
5922                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5923                                                 ++k;
5924                                         }
5925
5926                                         if (k != _ranges.end()) {
5927                                                 /* dragging this point */
5928                                                 _nothing_to_drag = false;
5929                                                 i->points.push_back (p);
5930                                         }
5931                                 }
5932                         }
5933                 }
5934
5935                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5936                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5937                 }
5938         }
5939
5940         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5941                 float const f = y_fraction (l->line, current_pointer_y());
5942                 /* we are ignoring x position for this drag, so we can just pass in anything */
5943                 pair<double, float> result;
5944                 uint32_t ignored;
5945                 result = l->line->drag_motion (0, f, true, false, ignored);
5946                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5947         }
5948 }
5949
5950 void
5951 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5952 {
5953         if (_nothing_to_drag || !motion_occurred) {
5954                 return;
5955         }
5956
5957         motion (event, false);
5958         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5959                 i->line->end_drag (false, 0);
5960         }
5961
5962         _editor->commit_reversible_command ();
5963 }
5964
5965 void
5966 AutomationRangeDrag::aborted (bool)
5967 {
5968         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5969                 i->line->reset ();
5970         }
5971 }
5972
5973 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5974         : view (v)
5975         , initial_time_axis_view (itav)
5976 {
5977         /* note that time_axis_view may be null if the regionview was created
5978          * as part of a copy operation.
5979          */
5980         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5981         layer = v->region()->layer ();
5982         initial_y = v->get_canvas_group()->position().y;
5983         initial_playlist = v->region()->playlist ();
5984         initial_position = v->region()->position ();
5985         initial_end = v->region()->position () + v->region()->length ();
5986 }
5987
5988 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5989         : Drag (e, i->canvas_item ())
5990         , _region_view (r)
5991         , _patch_change (i)
5992         , _cumulative_dx (0)
5993 {
5994         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5995                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5996                                                    grab_frame()));
5997 }
5998
5999 void
6000 PatchChangeDrag::motion (GdkEvent* ev, bool)
6001 {
6002         framepos_t f = adjusted_current_frame (ev);
6003         boost::shared_ptr<Region> r = _region_view->region ();
6004         f = max (f, r->position ());
6005         f = min (f, r->last_frame ());
6006
6007         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6008         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6009         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6010         _cumulative_dx = dxu;
6011 }
6012
6013 void
6014 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6015 {
6016         if (!movement_occurred) {
6017                 if (was_double_click()) {
6018                         _region_view->edit_patch_change (_patch_change);
6019                 }
6020                 return;
6021         }
6022
6023         boost::shared_ptr<Region> r (_region_view->region ());
6024         framepos_t f = adjusted_current_frame (ev);
6025         f = max (f, r->position ());
6026         f = min (f, r->last_frame ());
6027
6028         _region_view->move_patch_change (
6029                 *_patch_change,
6030                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6031                 );
6032 }
6033
6034 void
6035 PatchChangeDrag::aborted (bool)
6036 {
6037         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6038 }
6039
6040 void
6041 PatchChangeDrag::setup_pointer_frame_offset ()
6042 {
6043         boost::shared_ptr<Region> region = _region_view->region ();
6044         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6045 }
6046
6047 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6048         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6049         , _region_view (rv)
6050 {
6051
6052 }
6053
6054 void
6055 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6056 {
6057         _region_view->update_drag_selection (
6058                 x1, x2, y1, y2,
6059                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6060 }
6061
6062 void
6063 MidiRubberbandSelectDrag::deselect_things ()
6064 {
6065         /* XXX */
6066 }
6067
6068 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6069         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6070         , _region_view (rv)
6071 {
6072         _vertical_only = true;
6073 }
6074
6075 void
6076 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6077 {
6078         double const y = _region_view->midi_view()->y_position ();
6079
6080         y1 = max (0.0, y1 - y);
6081         y2 = max (0.0, y2 - y);
6082
6083         _region_view->update_vertical_drag_selection (
6084                 y1,
6085                 y2,
6086                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6087                 );
6088 }
6089
6090 void
6091 MidiVerticalSelectDrag::deselect_things ()
6092 {
6093         /* XXX */
6094 }
6095
6096 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6097         : RubberbandSelectDrag (e, i)
6098 {
6099
6100 }
6101
6102 void
6103 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6104 {
6105         if (drag_in_progress) {
6106                 /* We just want to select things at the end of the drag, not during it */
6107                 return;
6108         }
6109
6110         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6111
6112         _editor->begin_reversible_selection_op (X_("rubberband selection"));
6113
6114         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6115
6116         _editor->commit_reversible_selection_op ();
6117 }
6118
6119 void
6120 EditorRubberbandSelectDrag::deselect_things ()
6121 {
6122         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6123
6124         _editor->selection->clear_tracks();
6125         _editor->selection->clear_regions();
6126         _editor->selection->clear_points ();
6127         _editor->selection->clear_lines ();
6128         _editor->selection->clear_midi_notes ();
6129
6130         _editor->commit_reversible_selection_op();
6131 }
6132
6133 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6134         : Drag (e, i)
6135         , _region_view (rv)
6136         , _drag_rect (0)
6137 {
6138         _note[0] = _note[1] = 0;
6139 }
6140
6141 NoteCreateDrag::~NoteCreateDrag ()
6142 {
6143         delete _drag_rect;
6144 }
6145
6146 framecnt_t
6147 NoteCreateDrag::grid_frames (framepos_t t) const
6148 {
6149         bool success;
6150         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6151         if (!success) {
6152                 grid_beats = Evoral::Beats(1);
6153         }
6154
6155         return _region_view->region_beats_to_region_frames (grid_beats);
6156 }
6157
6158 void
6159 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6160 {
6161         Drag::start_grab (event, cursor);
6162
6163         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6164
6165         framepos_t pf = _drags->current_pointer_frame ();
6166         framecnt_t const g = grid_frames (pf);
6167
6168         /* Hack so that we always snap to the note that we are over, instead of snapping
6169            to the next one if we're more than halfway through the one we're over.
6170         */
6171         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6172                 pf -= g / 2;
6173         }
6174
6175         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6176         _note[1] = _note[0];
6177
6178         MidiStreamView* sv = _region_view->midi_stream_view ();
6179         double const x = _editor->sample_to_pixel (_note[0]);
6180         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6181
6182         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6183         _drag_rect->set_outline_all ();
6184         _drag_rect->set_outline_color (0xffffff99);
6185         _drag_rect->set_fill_color (0xffffff66);
6186 }
6187
6188 void
6189 NoteCreateDrag::motion (GdkEvent* event, bool)
6190 {
6191         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6192         double const x0 = _editor->sample_to_pixel (_note[0]);
6193         double const x1 = _editor->sample_to_pixel (_note[1]);
6194         _drag_rect->set_x0 (std::min(x0, x1));
6195         _drag_rect->set_x1 (std::max(x0, x1));
6196 }
6197
6198 void
6199 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6200 {
6201         if (!had_movement) {
6202                 return;
6203         }
6204
6205         framepos_t const start = min (_note[0], _note[1]);
6206         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6207
6208         framecnt_t const g = grid_frames (start);
6209         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6210
6211         if (_editor->snap_mode() == SnapNormal && length < g) {
6212                 length = g;
6213         }
6214
6215         Evoral::Beats length_beats = max (
6216                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6217
6218         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6219 }
6220
6221 double
6222 NoteCreateDrag::y_to_region (double y) const
6223 {
6224         double x = 0;
6225         _region_view->get_canvas_group()->canvas_to_item (x, y);
6226         return y;
6227 }
6228
6229 void
6230 NoteCreateDrag::aborted (bool)
6231 {
6232
6233 }
6234
6235 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6236         : Drag (e, i)
6237         , arv (rv)
6238         , start (start_yn)
6239 {
6240         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
6241 }
6242
6243 void
6244 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6245 {
6246         Drag::start_grab (event, cursor);
6247 }
6248
6249 void
6250 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6251 {
6252         double distance;
6253         double new_length;
6254         framecnt_t len;
6255
6256         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6257
6258         if (start) {
6259                 distance = _drags->current_pointer_x() - grab_x();
6260                 len = ar->fade_in()->back()->when;
6261         } else {
6262                 distance = grab_x() - _drags->current_pointer_x();
6263                 len = ar->fade_out()->back()->when;
6264         }
6265
6266         /* how long should it be ? */
6267
6268         new_length = len + _editor->pixel_to_sample (distance);
6269
6270         /* now check with the region that this is legal */
6271
6272         new_length = ar->verify_xfade_bounds (new_length, start);
6273
6274         if (start) {
6275                 arv->reset_fade_in_shape_width (ar, new_length);
6276         } else {
6277                 arv->reset_fade_out_shape_width (ar, new_length);
6278         }
6279 }
6280
6281 void
6282 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6283 {
6284         double distance;
6285         double new_length;
6286         framecnt_t len;
6287
6288         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6289
6290         if (start) {
6291                 distance = _drags->current_pointer_x() - grab_x();
6292                 len = ar->fade_in()->back()->when;
6293         } else {
6294                 distance = grab_x() - _drags->current_pointer_x();
6295                 len = ar->fade_out()->back()->when;
6296         }
6297
6298         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6299
6300         _editor->begin_reversible_command ("xfade trim");
6301         ar->playlist()->clear_owned_changes ();
6302
6303         if (start) {
6304                 ar->set_fade_in_length (new_length);
6305         } else {
6306                 ar->set_fade_out_length (new_length);
6307         }
6308
6309         /* Adjusting the xfade may affect other regions in the playlist, so we need
6310            to get undo Commands from the whole playlist rather than just the
6311            region.
6312         */
6313
6314         vector<Command*> cmds;
6315         ar->playlist()->rdiff (cmds);
6316         _editor->session()->add_commands (cmds);
6317         _editor->commit_reversible_command ();
6318
6319 }
6320
6321 void
6322 CrossfadeEdgeDrag::aborted (bool)
6323 {
6324         if (start) {
6325                 // arv->redraw_start_xfade ();
6326         } else {
6327                 // arv->redraw_end_xfade ();
6328         }
6329 }
6330
6331 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6332         : Drag (e, item, true)
6333         , line (new EditorCursor (*e))
6334 {
6335         line->set_position (pos);
6336         line->show ();
6337 }
6338
6339 RegionCutDrag::~RegionCutDrag ()
6340 {
6341         delete line;
6342 }
6343
6344 void
6345 RegionCutDrag::motion (GdkEvent*, bool)
6346 {
6347         framepos_t where = _drags->current_pointer_frame();
6348         _editor->snap_to (where);
6349
6350         line->set_position (where);
6351 }
6352
6353 void
6354 RegionCutDrag::finished (GdkEvent* event, bool)
6355 {
6356         _editor->get_track_canvas()->canvas()->re_enter();
6357
6358         framepos_t pos = _drags->current_pointer_frame();
6359
6360         line->hide ();
6361
6362         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6363
6364         if (rs.empty()) {
6365                 return;
6366         }
6367
6368         _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6369 }
6370
6371 void
6372 RegionCutDrag::aborted (bool)
6373 {
6374 }