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