promote Playlist::RegionList to ARDOUR::RegionList; fix timefx on multiple regions...
[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
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
29
30 #include "gtkmm2ext/utils.h"
31
32 #include "ardour/audioregion.h"
33 #include "ardour/dB.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/operations.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
38
39 #include "editor.h"
40 #include "i18n.h"
41 #include "keyboard.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
44 #include "ardour_ui.h"
45 #include "gui_thread.h"
46 #include "control_point.h"
47 #include "utils.h"
48 #include "region_gain_line.h"
49 #include "editor_drag.h"
50 #include "audio_time_axis.h"
51 #include "midi_time_axis.h"
52 #include "canvas-note.h"
53 #include "selection.h"
54 #include "midi_selection.h"
55 #include "automation_time_axis.h"
56 #include "debug.h"
57 #include "editor_cursors.h"
58 #include "mouse_cursors.h"
59 #include "verbose_cursor.h"
60
61 using namespace std;
62 using namespace ARDOUR;
63 using namespace PBD;
64 using namespace Gtk;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67 using namespace ArdourCanvas;
68
69 using Gtkmm2ext::Keyboard;
70
71 double ControlPointDrag::_zero_gain_fraction = -1.0;
72
73 DragManager::DragManager (Editor* e)
74         : _editor (e)
75         , _ending (false)
76         , _current_pointer_frame (0)
77 {
78 }
79
80 DragManager::~DragManager ()
81 {
82         abort ();
83 }
84
85 /** Call abort for each active drag */
86 void
87 DragManager::abort ()
88 {
89         _ending = true;
90
91         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
92                 (*i)->abort ();
93                 delete *i;
94         }
95
96         if (!_drags.empty ()) {
97                 _editor->set_follow_playhead (_old_follow_playhead, false);
98         }
99
100         _drags.clear ();
101
102         _ending = false;
103 }
104
105 void
106 DragManager::add (Drag* d)
107 {
108         d->set_manager (this);
109         _drags.push_back (d);
110 }
111
112 void
113 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
114 {
115         d->set_manager (this);
116         _drags.push_back (d);
117         start_grab (e, c);
118 }
119
120 void
121 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
122 {
123         /* Prevent follow playhead during the drag to be nice to the user */
124         _old_follow_playhead = _editor->follow_playhead ();
125         _editor->set_follow_playhead (false);
126
127         _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
128
129         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
130                 (*i)->start_grab (e, c);
131         }
132 }
133
134 /** Call end_grab for each active drag.
135  *  @return true if any drag reported movement having occurred.
136  */
137 bool
138 DragManager::end_grab (GdkEvent* e)
139 {
140         _ending = true;
141
142         bool r = false;
143         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144                 bool const t = (*i)->end_grab (e);
145                 if (t) {
146                         r = true;
147                 }
148                 delete *i;
149         }
150
151         _drags.clear ();
152
153         _ending = false;
154
155         _editor->set_follow_playhead (_old_follow_playhead, false);
156
157         return r;
158 }
159
160 bool
161 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
162 {
163         bool r = false;
164
165         _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
166
167         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
168                 bool const t = (*i)->motion_handler (e, from_autoscroll);
169                 if (t) {
170                         r = true;
171                 }
172
173         }
174
175         return r;
176 }
177
178 bool
179 DragManager::have_item (ArdourCanvas::Item* i) const
180 {
181         list<Drag*>::const_iterator j = _drags.begin ();
182         while (j != _drags.end() && (*j)->item () != i) {
183                 ++j;
184         }
185
186         return j != _drags.end ();
187 }
188
189 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
190         : _editor (e)
191         , _item (i)
192         , _pointer_frame_offset (0)
193         , _move_threshold_passed (false)
194         , _raw_grab_frame (0)
195         , _grab_frame (0)
196         , _last_pointer_frame (0)
197 {
198
199 }
200
201 void
202 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
203 {
204         _item->ungrab (0);
205         _item = new_item;
206
207         if (cursor == 0) {
208                 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
209         } else {
210                 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
211         }
212 }
213
214 void
215 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
216 {
217         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
218
219         if (Keyboard::is_button2_event (&event->button)) {
220                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
221                         _y_constrained = true;
222                         _x_constrained = false;
223                 } else {
224                         _y_constrained = false;
225                         _x_constrained = true;
226                 }
227         } else {
228                 _x_constrained = false;
229                 _y_constrained = false;
230         }
231
232         _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
233         setup_pointer_frame_offset ();
234         _grab_frame = adjusted_frame (_raw_grab_frame, event);
235         _last_pointer_frame = _grab_frame;
236         _last_pointer_x = _grab_x;
237         _last_pointer_y = _grab_y;
238
239         if (cursor == 0) {
240                 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
241                              event->button.time);
242         } else {
243                 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
244                              *cursor,
245                              event->button.time);
246         }
247
248         if (_editor->session() && _editor->session()->transport_rolling()) {
249                 _was_rolling = true;
250         } else {
251                 _was_rolling = false;
252         }
253
254         switch (_editor->snap_type()) {
255         case SnapToRegionStart:
256         case SnapToRegionEnd:
257         case SnapToRegionSync:
258         case SnapToRegionBoundary:
259                 _editor->build_region_boundary_cache ();
260                 break;
261         default:
262                 break;
263         }
264 }
265
266 /** Call to end a drag `successfully'.  Ungrabs item and calls
267  *  subclass' finished() method.
268  *
269  *  @param event GDK event, or 0.
270  *  @return true if some movement occurred, otherwise false.
271  */
272 bool
273 Drag::end_grab (GdkEvent* event)
274 {
275         _editor->stop_canvas_autoscroll ();
276
277         _item->ungrab (event ? event->button.time : 0);
278
279         finished (event, _move_threshold_passed);
280
281         _editor->verbose_cursor()->hide ();
282
283         return _move_threshold_passed;
284 }
285
286 framepos_t
287 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
288 {
289         framepos_t pos = 0;
290
291         if (f > _pointer_frame_offset) {
292                 pos = f - _pointer_frame_offset;
293         }
294
295         if (snap) {
296                 _editor->snap_to_with_modifier (pos, event);
297         }
298
299         return pos;
300 }
301
302 framepos_t
303 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
304 {
305         return adjusted_frame (_drags->current_pointer_frame (), event, snap);
306 }
307
308 bool
309 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
310 {
311         /* check to see if we have moved in any way that matters since the last motion event */
312         if (_move_threshold_passed &&
313             (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
314             (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
315                 return false;
316         }
317
318         pair<framecnt_t, int> const threshold = move_threshold ();
319
320         bool const old_move_threshold_passed = _move_threshold_passed;
321
322         if (!from_autoscroll && !_move_threshold_passed) {
323
324                 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
325                 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
326
327                 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
328         }
329
330         if (active (_editor->mouse_mode) && _move_threshold_passed) {
331
332                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
333                         if (!from_autoscroll) {
334                                 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
335                         }
336
337                         motion (event, _move_threshold_passed != old_move_threshold_passed);
338
339                         _last_pointer_x = _drags->current_pointer_x ();
340                         _last_pointer_y = _drags->current_pointer_y ();
341                         _last_pointer_frame = adjusted_current_frame (event);
342
343                         return true;
344                 }
345         }
346         return false;
347 }
348
349 /** Call to abort a drag.  Ungrabs item and calls subclass's aborted () */
350 void
351 Drag::abort ()
352 {
353         if (_item) {
354                 _item->ungrab (0);
355         }
356
357         aborted (_move_threshold_passed);
358
359         _editor->stop_canvas_autoscroll ();
360         _editor->verbose_cursor()->hide ();
361 }
362
363 void
364 Drag::show_verbose_cursor_time (framepos_t frame)
365 {
366         _editor->verbose_cursor()->set_time (
367                 frame,
368                 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
369                 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
370                 );
371
372         _editor->verbose_cursor()->show ();
373 }
374
375 void
376 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
377 {
378         _editor->verbose_cursor()->show (xoffset);
379
380         _editor->verbose_cursor()->set_duration (
381                 start, end,
382                 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
383                 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
384                 );
385 }
386
387 void
388 Drag::show_verbose_cursor_text (string const & text)
389 {
390         _editor->verbose_cursor()->show ();
391
392         _editor->verbose_cursor()->set (
393                 text,
394                 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
395                 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
396                 );
397 }
398
399 boost::shared_ptr<Region>
400 Drag::add_midi_region (MidiTimeAxisView* view)
401 {
402         if (_editor->session()) {
403                 const TempoMap& map (_editor->session()->tempo_map());
404                 framecnt_t pos = grab_frame();
405                 const Meter& m = map.meter_at (pos);
406                 /* not that the frame rate used here can be affected by pull up/down which
407                    might be wrong.
408                 */
409                 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
410                 return view->add_region (grab_frame(), len, true);
411         }
412
413         return boost::shared_ptr<Region>();
414 }
415
416 struct EditorOrderTimeAxisViewSorter {
417     bool operator() (TimeAxisView* a, TimeAxisView* b) {
418             RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
419             RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
420             assert (ra && rb);
421             return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
422     }
423 };
424
425 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
426         : Drag (e, i),
427           _primary (p)
428 {
429         _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
430
431         /* Make a list of tracks to refer to during the drag; we include hidden tracks,
432            as some of the regions we are dragging may be on such tracks.
433         */
434
435         TrackViewList track_views = _editor->track_views;
436         track_views.sort (EditorOrderTimeAxisViewSorter ());
437
438         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
439                 _time_axis_views.push_back (*i);
440                 
441                 TimeAxisView::Children children_list = (*i)->get_child_list ();
442                 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
443                         _time_axis_views.push_back (j->get());
444                 }
445         }
446
447         /* the list of views can be empty at this point if this is a region list-insert drag
448          */
449
450         for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
451                 _views.push_back (DraggingView (*i, this));
452         }
453
454         RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
455 }
456
457 void
458 RegionDrag::region_going_away (RegionView* v)
459 {
460         list<DraggingView>::iterator i = _views.begin ();
461         while (i != _views.end() && i->view != v) {
462                 ++i;
463         }
464
465         if (i != _views.end()) {
466                 _views.erase (i);
467         }
468 }
469
470 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
471  *  or -1 if it is not found.
472  */
473 int
474 RegionDrag::find_time_axis_view (TimeAxisView* t) const
475 {
476         int i = 0;
477         int const N = _time_axis_views.size ();
478         while (i < N && _time_axis_views[i] != t) {
479                 ++i;
480         }
481
482         if (i == N) {
483                 return -1;
484         }
485
486         return i;
487 }
488
489 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
490         : RegionDrag (e, i, p, v),
491           _brushing (b),
492           _total_x_delta (0)
493 {
494
495 }
496
497
498 void
499 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
500 {
501         Drag::start_grab (event, cursor);
502
503         show_verbose_cursor_time (_last_frame_position);
504
505         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
506         _last_pointer_time_axis_view = find_time_axis_view (tv.first);
507         _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
508 }
509
510 double
511 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
512 {
513         /* compute the amount of pointer motion in frames, and where
514            the region would be if we moved it by that much.
515         */
516         *pending_region_position = adjusted_current_frame (event);
517
518         framepos_t sync_frame;
519         framecnt_t sync_offset;
520         int32_t sync_dir;
521
522         sync_offset = _primary->region()->sync_offset (sync_dir);
523
524         /* we don't handle a sync point that lies before zero.
525          */
526         if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
527
528                 sync_frame = *pending_region_position + (sync_dir*sync_offset);
529
530                 _editor->snap_to_with_modifier (sync_frame, event);
531
532                 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
533
534         } else {
535                 *pending_region_position = _last_frame_position;
536         }
537
538         if (*pending_region_position > max_framepos - _primary->region()->length()) {
539                 *pending_region_position = _last_frame_position;
540         }
541
542         double dx = 0;
543
544         /* in locked edit mode, reverse the usual meaning of _x_constrained */
545         bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
546
547         if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
548
549                 /* x movement since last time (in pixels) */
550                 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
551
552                 /* total x movement */
553                 framecnt_t total_dx = *pending_region_position;
554                 if (regions_came_from_canvas()) {
555                         total_dx = total_dx - grab_frame ();
556                 }
557
558                 /* check that no regions have gone off the start of the session */
559                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
560                         if ((i->view->region()->position() + total_dx) < 0) {
561                                 dx = 0;
562                                 *pending_region_position = _last_frame_position;
563                                 break;
564                         }
565                 }
566
567                 _last_frame_position = *pending_region_position;
568         }
569
570         return dx;
571 }
572
573 bool
574 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
575 {
576         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
577                 int const n = i->time_axis_view + delta_track;
578                 if (n < 0 || n >= int (_time_axis_views.size())) {
579                         /* off the top or bottom track */
580                         return false;
581                 }
582
583                 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
584                 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
585                         /* not a track, or the wrong type */
586                         return false;
587                 }
588
589                 double const l = i->layer + delta_layer;
590
591                 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
592                    mode to allow the user to place a region below another on layer 0.
593                 */
594                 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
595                         /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
596                            If it has, the layers will be munged later anyway, so it's ok.
597                         */
598                         return false;
599                 }
600         }
601
602         /* all regions being dragged are ok with this change */
603         return true;
604 }
605
606 void
607 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
608 {
609         assert (!_views.empty ());
610
611         /* Find the TimeAxisView that the pointer is now over */
612         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
613
614         if (first_move && tv.first->view()->layer_display() == Stacked) {
615                 tv.first->view()->set_layer_display (Expanded);
616         }
617
618         /* Bail early if we're not over a track */
619         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
620         if (!rtv || !rtv->is_track()) {
621                 _editor->verbose_cursor()->hide ();
622                 return;
623         }
624
625         /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
626
627         /* Here's the current pointer position in terms of time axis view and layer */
628         int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
629         double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
630
631         /* Work out the change in x */
632         framepos_t pending_region_position;
633         double const x_delta = compute_x_delta (event, &pending_region_position);
634
635         /* Work out the change in y */
636         int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
637         double delta_layer = current_pointer_layer - _last_pointer_layer;
638
639         if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
640                 /* this y movement is not allowed, so do no y movement this time */
641                 delta_time_axis_view = 0;
642                 delta_layer = 0;
643         }
644
645         if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
646                 /* haven't reached next snap point, and we're not switching
647                    trackviews nor layers. nothing to do.
648                 */
649                 return;
650         }
651
652         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
653
654         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
655
656                 RegionView* rv = i->view;
657
658                 if (rv->region()->locked()) {
659                         continue;
660                 }
661
662                 if (first_move) {
663
664                         rv->get_time_axis_view().hide_dependent_views (*rv);
665
666                         /* Absolutely no idea why this is necessary, but it is; without
667                            it, the region view disappears after the reparent.
668                         */
669                         _editor->update_canvas_now ();
670                         
671                         /* Reparent to a non scrolling group so that we can keep the
672                            region selection above all time axis views.
673                            Reparenting means that we will have to move the region view
674                            later, as the two parent groups have different coordinates.
675                         */
676
677                         rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
678                         
679                         rv->fake_set_opaque (true);
680                 }
681
682                 /* If we have moved tracks, we'll fudge the layer delta so that the
683                    region gets moved back onto layer 0 on its new track; this avoids
684                    confusion when dragging regions from non-zero layers onto different
685                    tracks.
686                 */
687                 double this_delta_layer = delta_layer;
688                 if (delta_time_axis_view != 0) {
689                         this_delta_layer = - i->layer;
690                 }
691
692                 /* The TimeAxisView that this region is now on */
693                 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
694
695                 /* Ensure it is moved from stacked -> expanded if appropriate */
696                 if (tv->view()->layer_display() == Stacked) {
697                         tv->view()->set_layer_display (Expanded);
698                 }
699
700                 /* We're only allowed to go -ve in layer on Expanded views */
701                 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
702                         this_delta_layer = - i->layer;
703                 }
704                         
705                 /* Set height */
706                 rv->set_height (tv->view()->child_height ());
707
708                 /* Update show/hidden status as the region view may have come from a hidden track,
709                    or have moved to one.
710                 */
711                 if (tv->hidden ()) {
712                         rv->get_canvas_group()->hide ();
713                 } else {
714                         rv->get_canvas_group()->show ();
715                 }
716
717                 /* Update the DraggingView */
718                 i->time_axis_view += delta_time_axis_view;
719                 i->layer += this_delta_layer;
720
721                 if (_brushing) {
722                         _editor->mouse_brush_insert_region (rv, pending_region_position);
723                 } else {
724                         double x = 0;
725                         double y = 0;
726
727                         /* Get the y coordinate of the top of the track that this region is now on */
728                         tv->canvas_display()->i2w (x, y);
729                         y += _editor->get_trackview_group_vertical_offset();
730                         
731                         /* And adjust for the layer that it should be on */
732                         StreamView* cv = tv->view ();
733                         switch (cv->layer_display ()) {
734                         case Overlaid:
735                                 break;
736                         case Stacked:
737                                 y += (cv->layers() - i->layer - 1) * cv->child_height ();
738                                 break;
739                         case Expanded:
740                                 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
741                                 break;
742                         }
743
744                         /* Now move the region view */
745                         rv->move (x_delta, y - rv->get_canvas_group()->property_y());
746                 }
747
748         } /* foreach region */
749
750         _total_x_delta += x_delta;
751
752         if (first_move) {
753                 _editor->cursor_group->raise_to_top();
754         }
755
756         if (x_delta != 0 && !_brushing) {
757                 show_verbose_cursor_time (_last_frame_position);
758         }
759
760         _last_pointer_time_axis_view += delta_time_axis_view;
761         _last_pointer_layer += delta_layer;
762 }
763
764 void
765 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
766 {
767         if (_copy && first_move) {
768
769                 /* duplicate the regionview(s) and region(s) */
770
771                 list<DraggingView> new_regionviews;
772
773                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
774
775                         RegionView* rv = i->view;
776                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
777                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
778
779                         const boost::shared_ptr<const Region> original = rv->region();
780                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
781                         region_copy->set_position (original->position());
782
783                         RegionView* nrv;
784                         if (arv) {
785                                 boost::shared_ptr<AudioRegion> audioregion_copy
786                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
787
788                                 nrv = new AudioRegionView (*arv, audioregion_copy);
789                         } else if (mrv) {
790                                 boost::shared_ptr<MidiRegion> midiregion_copy
791                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
792                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
793                         } else {
794                                 continue;
795                         }
796
797                         nrv->get_canvas_group()->show ();
798                         new_regionviews.push_back (DraggingView (nrv, this));
799
800                         /* swap _primary to the copy */
801
802                         if (rv == _primary) {
803                                 _primary = nrv;
804                         }
805
806                         /* ..and deselect the one we copied */
807
808                         rv->set_selected (false);
809                 }
810
811                 if (!new_regionviews.empty()) {
812
813                         /* reflect the fact that we are dragging the copies */
814
815                         _views = new_regionviews;
816
817                         swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
818
819                         /*
820                           sync the canvas to what we think is its current state
821                           without it, the canvas seems to
822                           "forget" to update properly after the upcoming reparent()
823                           ..only if the mouse is in rapid motion at the time of the grab.
824                           something to do with regionview creation taking so long?
825                         */
826                         _editor->update_canvas_now();
827                 }
828         }
829
830         RegionMotionDrag::motion (event, first_move);
831 }
832
833 void
834 RegionMotionDrag::finished (GdkEvent *, bool)
835 {
836         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
837                 if (!(*i)->view()) {
838                         continue;
839                 }
840
841                 if ((*i)->view()->layer_display() == Expanded) {
842                         (*i)->view()->set_layer_display (Stacked);
843                 }
844         }
845 }
846
847 void
848 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
849 {
850         RegionMotionDrag::finished (ev, movement_occurred);
851         
852         if (!movement_occurred) {
853                 /* just a click */
854                 return;
855         }
856
857         /* reverse this here so that we have the correct logic to finalize
858            the drag.
859         */
860
861         if (Config->get_edit_mode() == Lock) {
862                 _x_constrained = !_x_constrained;
863         }
864
865         assert (!_views.empty ());
866
867         /* We might have hidden region views so that they weren't visible during the drag
868            (when they have been reparented).  Now everything can be shown again, as region
869            views are back in their track parent groups.
870         */
871         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
872                 i->view->get_canvas_group()->show ();
873         }
874         
875         bool const changed_position = (_last_frame_position != _primary->region()->position());
876         bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
877         framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
878         
879         _editor->update_canvas_now ();
880
881         if (_copy) {
882
883                 finished_copy (
884                         changed_position,
885                         changed_tracks,
886                         drag_delta
887                         );
888
889         } else {
890
891                 finished_no_copy (
892                         changed_position,
893                         changed_tracks,
894                         drag_delta
895                         );
896
897         }
898 }
899
900 void
901 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
902 {
903         RegionSelection new_views;
904         PlaylistSet modified_playlists;
905         list<RegionView*> views_to_delete;
906
907         if (_brushing) {
908                 /* all changes were made during motion event handlers */
909
910                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
911                         delete i->view;
912                 }
913
914                 _editor->commit_reversible_command ();
915                 return;
916         }
917
918         if (_x_constrained) {
919                 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
920         } else {
921                 _editor->begin_reversible_command (Operations::region_copy);
922         }
923
924         /* insert the regions into their new playlists */
925         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
926
927                 if (i->view->region()->locked()) {
928                         continue;
929                 }
930
931                 framepos_t where;
932
933                 if (changed_position && !_x_constrained) {
934                         where = i->view->region()->position() - drag_delta;
935                 } else {
936                         where = i->view->region()->position();
937                 }
938
939                 RegionView* new_view = insert_region_into_playlist (
940                         i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
941                         );
942
943                 if (new_view == 0) {
944                         continue;
945                 }
946
947                 new_views.push_back (new_view);
948
949                 /* we don't need the copied RegionView any more */
950                 views_to_delete.push_back (i->view);
951         }
952
953         /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
954            because when views are deleted they are automagically removed from _views, which messes
955            up the iteration.
956         */
957         for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
958                 delete *i;
959         }
960
961         /* If we've created new regions either by copying or moving
962            to a new track, we want to replace the old selection with the new ones
963         */
964
965         if (new_views.size() > 0) {
966                 _editor->selection->set (new_views);
967         }
968
969         /* write commands for the accumulated diffs for all our modified playlists */
970         add_stateful_diff_commands_for_playlists (modified_playlists);
971
972         _editor->commit_reversible_command ();
973 }
974
975 void
976 RegionMoveDrag::finished_no_copy (
977         bool const changed_position,
978         bool const changed_tracks,
979         framecnt_t const drag_delta
980         )
981 {
982         RegionSelection new_views;
983         PlaylistSet modified_playlists;
984         PlaylistSet frozen_playlists;
985         set<RouteTimeAxisView*> views_to_update;
986
987         if (_brushing) {
988                 /* all changes were made during motion event handlers */
989                 _editor->commit_reversible_command ();
990                 return;
991         }
992
993         if (_x_constrained) {
994                 _editor->begin_reversible_command (_("fixed time region drag"));
995         } else {
996                 _editor->begin_reversible_command (Operations::region_drag);
997         }
998
999         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1000
1001                 RegionView* rv = i->view;
1002
1003                 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1004                 double const dest_layer = i->layer;
1005
1006                 if (rv->region()->locked()) {
1007                         ++i;
1008                         continue;
1009                 }
1010
1011                 views_to_update.insert (dest_rtv);
1012
1013                 framepos_t where;
1014
1015                 if (changed_position && !_x_constrained) {
1016                         where = rv->region()->position() - drag_delta;
1017                 } else {
1018                         where = rv->region()->position();
1019                 }
1020
1021                 if (changed_tracks) {
1022
1023                         /* insert into new playlist */
1024
1025                         RegionView* new_view = insert_region_into_playlist (
1026                                 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1027                                 );
1028
1029                         if (new_view == 0) {
1030                                 ++i;
1031                                 continue;
1032                         }
1033
1034                         new_views.push_back (new_view);
1035
1036                         /* remove from old playlist */
1037
1038                         /* the region that used to be in the old playlist is not
1039                            moved to the new one - we use a copy of it. as a result,
1040                            any existing editor for the region should no longer be
1041                            visible.
1042                         */
1043                         rv->hide_region_editor();
1044                         rv->fake_set_opaque (false);
1045
1046                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1047
1048                 } else {
1049
1050                         rv->region()->clear_changes ();
1051
1052                         /*
1053                            motion on the same track. plonk the previously reparented region
1054                            back to its original canvas group (its streamview).
1055                            No need to do anything for copies as they are fake regions which will be deleted.
1056                         */
1057
1058                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1059                         rv->get_canvas_group()->property_y() = i->initial_y;
1060                         rv->get_time_axis_view().reveal_dependent_views (*rv);
1061
1062                         /* just change the model */
1063
1064                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1065
1066                         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1067                                 playlist->set_layer (rv->region(), dest_layer);
1068                         }
1069
1070                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1071
1072                         pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1073
1074                         if (r.second) {
1075                                 playlist->freeze ();
1076                         }
1077
1078                         /* this movement may result in a crossfade being modified, so we need to get undo
1079                            data from the playlist as well as the region.
1080                         */
1081
1082                         r = modified_playlists.insert (playlist);
1083                         if (r.second) {
1084                                 playlist->clear_changes ();
1085                         }
1086
1087                         rv->region()->set_position (where);
1088
1089                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1090                 }
1091
1092                 if (changed_tracks) {
1093
1094                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1095                            was selected in all of them, then removing it from a playlist will have removed all
1096                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1097                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1098                            corresponding regionview, and _views is now empty).
1099
1100                            This could have invalidated any and all iterators into _views.
1101
1102                            The heuristic we use here is: if the region selection is empty, break out of the loop
1103                            here. if the region selection is not empty, then restart the loop because we know that
1104                            we must have removed at least the region(view) we've just been working on as well as any
1105                            that we processed on previous iterations.
1106
1107                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1108                            we can just iterate.
1109                         */
1110
1111
1112                         if (_views.empty()) {
1113                                 break;
1114                         } else {
1115                                 i = _views.begin();
1116                         }
1117
1118                 } else {
1119                         ++i;
1120                 }
1121         }
1122
1123         /* If we've created new regions either by copying or moving
1124            to a new track, we want to replace the old selection with the new ones
1125         */
1126
1127         if (new_views.size() > 0) {
1128                 _editor->selection->set (new_views);
1129         }
1130
1131         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1132                 (*p)->thaw();
1133         }
1134
1135         /* write commands for the accumulated diffs for all our modified playlists */
1136         add_stateful_diff_commands_for_playlists (modified_playlists);
1137
1138         _editor->commit_reversible_command ();
1139
1140         /* We have futzed with the layering of canvas items on our streamviews.
1141            If any region changed layer, this will have resulted in the stream
1142            views being asked to set up their region views, and all will be well.
1143            If not, we might now have badly-ordered region views.  Ask the StreamViews
1144            involved to sort themselves out, just in case.
1145         */
1146
1147         for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1148                 (*i)->view()->playlist_layered ((*i)->track ());
1149         }
1150 }
1151
1152 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1153  *  @param region Region to remove.
1154  *  @param playlist playlist To remove from.
1155  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1156  *  that clear_changes () is only called once per playlist.
1157  */
1158 void
1159 RegionMoveDrag::remove_region_from_playlist (
1160         boost::shared_ptr<Region> region,
1161         boost::shared_ptr<Playlist> playlist,
1162         PlaylistSet& modified_playlists
1163         )
1164 {
1165         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1166
1167         if (r.second) {
1168                 playlist->clear_changes ();
1169         }
1170
1171         playlist->remove_region (region);
1172 }
1173
1174
1175 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1176  *  clearing the playlist's diff history first if necessary.
1177  *  @param region Region to insert.
1178  *  @param dest_rtv Destination RouteTimeAxisView.
1179  *  @param dest_layer Destination layer.
1180  *  @param where Destination position.
1181  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1182  *  that clear_changes () is only called once per playlist.
1183  *  @return New RegionView, or 0 if no insert was performed.
1184  */
1185 RegionView *
1186 RegionMoveDrag::insert_region_into_playlist (
1187         boost::shared_ptr<Region> region,
1188         RouteTimeAxisView* dest_rtv,
1189         layer_t dest_layer,
1190         framecnt_t where,
1191         PlaylistSet& modified_playlists
1192         )
1193 {
1194         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1195         if (!dest_playlist) {
1196                 return 0;
1197         }
1198
1199         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1200         _new_region_view = 0;
1201         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1202
1203         /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1204         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1205         if (r.second) {
1206                 dest_playlist->clear_changes ();
1207         }
1208
1209         dest_playlist->add_region (region, where);
1210
1211         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1212                 dest_playlist->set_layer (region, dest_layer);
1213         }
1214
1215         c.disconnect ();
1216
1217         assert (_new_region_view);
1218
1219         return _new_region_view;
1220 }
1221
1222 void
1223 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1224 {
1225         _new_region_view = rv;
1226 }
1227
1228 void
1229 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1230 {
1231         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1232                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1233                 if (!c->empty()) {
1234                         _editor->session()->add_command (c);
1235                 } else {
1236                         delete c;
1237                 }
1238         }
1239 }
1240
1241
1242 void
1243 RegionMoveDrag::aborted (bool movement_occurred)
1244 {
1245         if (_copy) {
1246
1247                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1248                         delete i->view;
1249                 }
1250
1251                 _views.clear ();
1252
1253         } else {
1254                 RegionMotionDrag::aborted (movement_occurred);
1255         }
1256 }
1257
1258 void
1259 RegionMotionDrag::aborted (bool)
1260 {
1261         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1262                 if ((*i)->view()->layer_display() == Expanded) {
1263                         (*i)->view()->set_layer_display (Stacked);
1264                 }
1265         }
1266         
1267         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1268                 RegionView* rv = i->view;
1269                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1270                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1271                 assert (rtv);
1272                 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1273                 rv->get_canvas_group()->property_y() = 0;
1274                 rv->get_time_axis_view().reveal_dependent_views (*rv);
1275                 rv->fake_set_opaque (false);
1276                 rv->move (-_total_x_delta, 0);
1277                 rv->set_height (rtv->view()->child_height ());
1278         }
1279
1280         _editor->update_canvas_now ();
1281 }
1282
1283 /** @param b true to brush, otherwise false.
1284  *  @param c true to make copies of the regions being moved, otherwise false.
1285  */
1286 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1287         : RegionMotionDrag (e, i, p, v, b),
1288           _copy (c)
1289 {
1290         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1291
1292         double speed = 1;
1293         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1294         if (rtv && rtv->is_track()) {
1295                 speed = rtv->track()->speed ();
1296         }
1297
1298         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1299 }
1300
1301 void
1302 RegionMoveDrag::setup_pointer_frame_offset ()
1303 {
1304         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1305 }
1306
1307 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1308         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1309 {
1310         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1311
1312         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1313                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1314
1315         _primary = v->view()->create_region_view (r, false, false);
1316
1317         _primary->get_canvas_group()->show ();
1318         _primary->set_position (pos, 0);
1319         _views.push_back (DraggingView (_primary, this));
1320
1321         _last_frame_position = pos;
1322
1323         _item = _primary->get_canvas_group ();
1324 }
1325
1326 void
1327 RegionInsertDrag::finished (GdkEvent *, bool)
1328 {
1329         _editor->update_canvas_now ();
1330
1331         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1332
1333         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1334         _primary->get_canvas_group()->property_y() = 0;
1335
1336         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1337
1338         _editor->begin_reversible_command (Operations::insert_region);
1339         playlist->clear_changes ();
1340         playlist->add_region (_primary->region (), _last_frame_position);
1341         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1342         _editor->commit_reversible_command ();
1343
1344         delete _primary;
1345         _primary = 0;
1346         _views.clear ();
1347 }
1348
1349 void
1350 RegionInsertDrag::aborted (bool)
1351 {
1352         delete _primary;
1353         _primary = 0;
1354         _views.clear ();
1355 }
1356
1357 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1358         : RegionMoveDrag (e, i, p, v, false, false)
1359 {
1360         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1361 }
1362
1363 struct RegionSelectionByPosition {
1364     bool operator() (RegionView*a, RegionView* b) {
1365             return a->region()->position () < b->region()->position();
1366     }
1367 };
1368
1369 void
1370 RegionSpliceDrag::motion (GdkEvent* event, bool)
1371 {
1372         /* Which trackview is this ? */
1373
1374         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1375         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1376
1377         /* The region motion is only processed if the pointer is over
1378            an audio track.
1379         */
1380
1381         if (!tv || !tv->is_track()) {
1382                 /* To make sure we hide the verbose canvas cursor when the mouse is
1383                    not held over and audiotrack.
1384                 */
1385                 _editor->verbose_cursor()->hide ();
1386                 return;
1387         }
1388
1389         int dir;
1390
1391         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1392                 dir = 1;
1393         } else {
1394                 dir = -1;
1395         }
1396
1397         RegionSelection copy (_editor->selection->regions);
1398
1399         RegionSelectionByPosition cmp;
1400         copy.sort (cmp);
1401
1402         framepos_t const pf = adjusted_current_frame (event);
1403
1404         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1405
1406                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1407
1408                 if (!atv) {
1409                         continue;
1410                 }
1411
1412                 boost::shared_ptr<Playlist> playlist;
1413
1414                 if ((playlist = atv->playlist()) == 0) {
1415                         continue;
1416                 }
1417
1418                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1419                         continue;
1420                 }
1421
1422                 if (dir > 0) {
1423                         if (pf < (*i)->region()->last_frame() + 1) {
1424                                 continue;
1425                         }
1426                 } else {
1427                         if (pf > (*i)->region()->first_frame()) {
1428                                 continue;
1429                         }
1430                 }
1431
1432
1433                 playlist->shuffle ((*i)->region(), dir);
1434         }
1435 }
1436
1437 void
1438 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1439 {
1440         RegionMoveDrag::finished (event, movement_occurred);
1441 }
1442
1443 void
1444 RegionSpliceDrag::aborted (bool)
1445 {
1446         /* XXX: TODO */
1447 }
1448
1449 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1450         : Drag (e, i),
1451           _view (dynamic_cast<MidiTimeAxisView*> (v))
1452 {
1453         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1454
1455         assert (_view);
1456 }
1457
1458 void
1459 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1460 {
1461         if (first_move) {
1462                 _region = add_midi_region (_view);
1463                 _view->playlist()->freeze ();
1464         } else {
1465                 if (_region) {
1466                         framepos_t const f = adjusted_current_frame (event);
1467                         if (f < grab_frame()) {
1468                                 _region->set_position (f);
1469                         }
1470
1471                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1472                            so that if this region is duplicated, its duplicate starts on
1473                            a snap point rather than 1 frame after a snap point.  Otherwise things get
1474                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
1475                            place snapped notes at the start of the region.
1476                         */
1477
1478                         framecnt_t const len = abs (f - grab_frame () - 1);
1479                         _region->set_length (len < 1 ? 1 : len);
1480                 }
1481         }
1482 }
1483
1484 void
1485 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1486 {
1487         if (!movement_occurred) {
1488                 add_midi_region (_view);
1489         } else {
1490                 _view->playlist()->thaw ();
1491         }
1492
1493         if (_region) {
1494                 _editor->commit_reversible_command ();
1495         }
1496 }
1497
1498 void
1499 RegionCreateDrag::aborted (bool)
1500 {
1501         if (_region) {
1502                 _view->playlist()->thaw ();
1503         }
1504
1505         /* XXX */
1506 }
1507
1508 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1509         : Drag (e, i)
1510         , region (0)
1511 {
1512         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1513 }
1514
1515 void
1516 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1517 {
1518         Gdk::Cursor* cursor;
1519         ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1520         float x_fraction = cnote->mouse_x_fraction ();
1521
1522         if (x_fraction > 0.0 && x_fraction < 0.25) {
1523                 cursor = _editor->cursors()->left_side_trim;
1524         } else  {
1525                 cursor = _editor->cursors()->right_side_trim;
1526         }
1527
1528         Drag::start_grab (event, cursor);
1529
1530         region = &cnote->region_view();
1531
1532         double const region_start = region->get_position_pixels();
1533         double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1534
1535         if (grab_x() <= middle_point) {
1536                 cursor = _editor->cursors()->left_side_trim;
1537                 at_front = true;
1538         } else {
1539                 cursor = _editor->cursors()->right_side_trim;
1540                 at_front = false;
1541         }
1542
1543         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1544
1545         if (event->motion.state & Keyboard::PrimaryModifier) {
1546                 relative = false;
1547         } else {
1548                 relative = true;
1549         }
1550
1551         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1552
1553         if (ms.size() > 1) {
1554                 /* has to be relative, may make no sense otherwise */
1555                 relative = true;
1556         }
1557
1558         /* select this note; if it is already selected, preserve the existing selection,
1559            otherwise make this note the only one selected.
1560         */
1561         region->note_selected (cnote, cnote->selected ());
1562
1563         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1564                 MidiRegionSelection::iterator next;
1565                 next = r;
1566                 ++next;
1567                 (*r)->begin_resizing (at_front);
1568                 r = next;
1569         }
1570 }
1571
1572 void
1573 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1574 {
1575         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1576         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1577                 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1578         }
1579 }
1580
1581 void
1582 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1583 {
1584         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1585         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1586                 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1587         }
1588 }
1589
1590 void
1591 NoteResizeDrag::aborted (bool)
1592 {
1593         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1594         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1595                 (*r)->abort_resizing ();
1596         }
1597 }
1598
1599 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1600         : Drag (e, i)
1601 {
1602         DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1603 }
1604
1605 void
1606 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1607 {
1608
1609 }
1610
1611 void
1612 RegionGainDrag::finished (GdkEvent *, bool)
1613 {
1614
1615 }
1616
1617 void
1618 RegionGainDrag::aborted (bool)
1619 {
1620         /* XXX: TODO */
1621 }
1622
1623 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1624         : RegionDrag (e, i, p, v)
1625 {
1626         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1627 }
1628
1629 void
1630 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1631 {
1632         double speed = 1.0;
1633         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1634         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1635
1636         if (tv && tv->is_track()) {
1637                 speed = tv->track()->speed();
1638         }
1639
1640         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1641         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1642         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1643
1644         framepos_t const pf = adjusted_current_frame (event);
1645
1646         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1647                 /* Move the contents of the region around without changing the region bounds */
1648                 _operation = ContentsTrim;
1649                 Drag::start_grab (event, _editor->cursors()->trimmer);
1650         } else {
1651                 /* These will get overridden for a point trim.*/
1652                 if (pf < (region_start + region_length/2)) {
1653                         /* closer to front */
1654                         _operation = StartTrim;
1655                         Drag::start_grab (event, _editor->cursors()->left_side_trim);
1656                 } else {
1657                         /* closer to end */
1658                         _operation = EndTrim;
1659                         Drag::start_grab (event, _editor->cursors()->right_side_trim);
1660                 }
1661         }
1662
1663         switch (_operation) {
1664         case StartTrim:
1665                 show_verbose_cursor_time (region_start);
1666                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1667                         i->view->trim_front_starting ();
1668                 }
1669                 break;
1670         case EndTrim:
1671                 show_verbose_cursor_time (region_end);
1672                 break;
1673         case ContentsTrim:
1674                 show_verbose_cursor_time (pf);
1675                 break;
1676         }
1677
1678         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1679                 i->view->region()->suspend_property_changes ();
1680         }
1681 }
1682
1683 void
1684 TrimDrag::motion (GdkEvent* event, bool first_move)
1685 {
1686         RegionView* rv = _primary;
1687
1688         double speed = 1.0;
1689         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1690         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1691         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1692
1693         if (tv && tv->is_track()) {
1694                 speed = tv->track()->speed();
1695         }
1696
1697         framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1698
1699         if (first_move) {
1700
1701                 string trim_type;
1702
1703                 switch (_operation) {
1704                 case StartTrim:
1705                         trim_type = "Region start trim";
1706                         break;
1707                 case EndTrim:
1708                         trim_type = "Region end trim";
1709                         break;
1710                 case ContentsTrim:
1711                         trim_type = "Region content trim";
1712                         break;
1713                 }
1714
1715                 _editor->begin_reversible_command (trim_type);
1716
1717                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1718                         RegionView* rv = i->view;
1719                         rv->fake_set_opaque (false);
1720                         rv->enable_display (false);
1721                         rv->region()->playlist()->clear_owned_changes ();
1722
1723                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1724
1725                         if (arv) {
1726                                 arv->temporarily_hide_envelope ();
1727                         }
1728
1729                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1730                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1731
1732                         if (insert_result.second) {
1733                                 pl->freeze();
1734                         }
1735                 }
1736         }
1737
1738         bool non_overlap_trim = false;
1739
1740         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1741                 non_overlap_trim = true;
1742         }
1743
1744         switch (_operation) {
1745         case StartTrim:
1746                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1747                         i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1748                 }
1749                 break;
1750
1751         case EndTrim:
1752                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1753                         i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1754                 }
1755                 break;
1756
1757         case ContentsTrim:
1758                 {
1759                         bool swap_direction = false;
1760
1761                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1762                                 swap_direction = true;
1763                         }
1764
1765                         framecnt_t frame_delta = 0;
1766
1767                         bool left_direction = false;
1768                         if (last_pointer_frame() > adjusted_current_frame(event)) {
1769                                 left_direction = true;
1770                         }
1771
1772                         if (left_direction) {
1773                                 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1774                         } else {
1775                                 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1776                         }
1777
1778                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1779                                 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1780                         }
1781                 }
1782                 break;
1783         }
1784
1785         switch (_operation) {
1786         case StartTrim:
1787                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1788                 break;
1789         case EndTrim:
1790                 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1791                 break;
1792         case ContentsTrim:
1793                 show_verbose_cursor_time (adjusted_current_frame (event));
1794                 break;
1795         }
1796 }
1797
1798
1799 void
1800 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1801 {
1802         if (movement_occurred) {
1803                 motion (event, false);
1804
1805                 /* This must happen before the region's StatefulDiffCommand is created, as it may
1806                    `correct' (ahem) the region's _start from being negative to being zero.  It
1807                    needs to be zero in the undo record.
1808                 */
1809                 if (_operation == StartTrim) {
1810                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1811                                 i->view->trim_front_ending ();
1812                         }
1813                 }
1814
1815                 if (!_editor->selection->selected (_primary)) {
1816                         _primary->thaw_after_trim ();
1817                 } else {
1818
1819                         set<boost::shared_ptr<Playlist> > diffed_playlists;
1820
1821                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1822                                 i->view->thaw_after_trim ();
1823                                 i->view->enable_display (true);
1824                                 i->view->fake_set_opaque (true);
1825
1826                                 /* Trimming one region may affect others on the playlist, so we need
1827                                    to get undo Commands from the whole playlist rather than just the
1828                                    region.  Use diffed_playlists to make sure we don't diff a given
1829                                    playlist more than once.
1830                                 */
1831                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1832                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1833                                         vector<Command*> cmds;
1834                                         p->rdiff (cmds);
1835                                         _editor->session()->add_commands (cmds);
1836                                         diffed_playlists.insert (p);
1837                                 }
1838                         }
1839                 }
1840                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1841                         (*p)->thaw ();
1842                 }
1843
1844                 _editor->motion_frozen_playlists.clear ();
1845                 _editor->commit_reversible_command();
1846
1847         } else {
1848                 /* no mouse movement */
1849                 _editor->point_trim (event, adjusted_current_frame (event));
1850         }
1851
1852         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1853                 if (_operation == StartTrim) {
1854                         i->view->trim_front_ending ();
1855                 }
1856
1857                 i->view->region()->resume_property_changes ();
1858         }
1859 }
1860
1861 void
1862 TrimDrag::aborted (bool movement_occurred)
1863 {
1864         /* Our motion method is changing model state, so use the Undo system
1865            to cancel.  Perhaps not ideal, as this will leave an Undo point
1866            behind which may be slightly odd from the user's point of view.
1867         */
1868
1869         finished (0, true);
1870
1871         if (movement_occurred) {
1872                 _editor->undo ();
1873         }
1874
1875         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1876                 i->view->region()->resume_property_changes ();
1877         }
1878 }
1879
1880 void
1881 TrimDrag::setup_pointer_frame_offset ()
1882 {
1883         list<DraggingView>::iterator i = _views.begin ();
1884         while (i != _views.end() && i->view != _primary) {
1885                 ++i;
1886         }
1887
1888         if (i == _views.end()) {
1889                 return;
1890         }
1891
1892         switch (_operation) {
1893         case StartTrim:
1894                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1895                 break;
1896         case EndTrim:
1897                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1898                 break;
1899         case ContentsTrim:
1900                 break;
1901         }
1902 }
1903
1904 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1905         : Drag (e, i),
1906           _copy (c)
1907 {
1908         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1909         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1910         assert (_marker);
1911 }
1912
1913 void
1914 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1915 {
1916         Drag::start_grab (event, cursor);
1917         show_verbose_cursor_time (adjusted_current_frame(event));
1918 }
1919
1920 void
1921 MeterMarkerDrag::setup_pointer_frame_offset ()
1922 {
1923         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1924 }
1925
1926 void
1927 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1928 {
1929         if (first_move) {
1930
1931                 // create a dummy marker for visual representation of moving the
1932                 // section, because whether its a copy or not, we're going to 
1933                 // leave or lose the original marker (leave if its a copy; lose if its
1934                 // not, because we'll remove it from the map).
1935                 
1936                 MeterSection section (_marker->meter());
1937
1938                 if (!section.movable()) {
1939                         return;
1940                 }
1941                 
1942                 char name[64];
1943                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1944                 
1945                 _marker = new MeterMarker (
1946                         *_editor,
1947                         *_editor->meter_group,
1948                         ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1949                         name,
1950                         *new MeterSection (_marker->meter())
1951                 );
1952                 
1953                 /* use the new marker for the grab */
1954                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1955
1956                 if (!_copy) {
1957                         TempoMap& map (_editor->session()->tempo_map());
1958                         /* get current state */
1959                         before_state = &map.get_state();
1960                         /* remove the section while we drag it */
1961                         map.remove_meter (section, true);
1962                 }
1963         }
1964
1965         framepos_t const pf = adjusted_current_frame (event);
1966         _marker->set_position (pf);
1967         show_verbose_cursor_time (pf);
1968 }
1969
1970 void
1971 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1972 {
1973         if (!movement_occurred) {
1974                 return;
1975         }
1976
1977         motion (event, false);
1978
1979         Timecode::BBT_Time when;
1980
1981         TempoMap& map (_editor->session()->tempo_map());
1982         map.bbt_time (last_pointer_frame(), when);
1983         
1984         if (_copy == true) {
1985                 _editor->begin_reversible_command (_("copy meter mark"));
1986                 XMLNode &before = map.get_state();
1987                 map.add_meter (_marker->meter(), when);
1988                 XMLNode &after = map.get_state();
1989                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1990                 _editor->commit_reversible_command ();
1991
1992         } else {
1993                 _editor->begin_reversible_command (_("move meter mark"));
1994
1995                 /* we removed it before, so add it back now */
1996                 
1997                 map.add_meter (_marker->meter(), when);
1998                 XMLNode &after = map.get_state();
1999                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2000                 _editor->commit_reversible_command ();
2001         }
2002
2003         // delete the dummy marker we used for visual representation while moving.
2004         // a new visual marker will show up automatically.
2005         delete _marker;
2006 }
2007
2008 void
2009 MeterMarkerDrag::aborted (bool moved)
2010 {
2011         _marker->set_position (_marker->meter().frame ());
2012
2013         if (moved) {
2014                 TempoMap& map (_editor->session()->tempo_map());
2015                 /* we removed it before, so add it back now */
2016                 map.add_meter (_marker->meter(), _marker->meter().frame());
2017                 // delete the dummy marker we used for visual representation while moving.
2018                 // a new visual marker will show up automatically.
2019                 delete _marker;
2020         }
2021 }
2022
2023 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2024         : Drag (e, i),
2025           _copy (c)
2026 {
2027         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2028
2029         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2030         assert (_marker);
2031 }
2032
2033 void
2034 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2035 {
2036         Drag::start_grab (event, cursor);
2037         show_verbose_cursor_time (adjusted_current_frame (event));
2038 }
2039
2040 void
2041 TempoMarkerDrag::setup_pointer_frame_offset ()
2042 {
2043         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2044 }
2045
2046 void
2047 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2048 {
2049         if (first_move) {
2050
2051                 // create a dummy marker for visual representation of moving the
2052                 // section, because whether its a copy or not, we're going to 
2053                 // leave or lose the original marker (leave if its a copy; lose if its
2054                 // not, because we'll remove it from the map).
2055                 
2056                 // create a dummy marker for visual representation of moving the copy.
2057                 // The actual copying is not done before we reach the finish callback.
2058
2059                 char name[64];
2060                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2061
2062                 TempoSection section (_marker->tempo());
2063
2064                 _marker = new TempoMarker (
2065                         *_editor,
2066                         *_editor->tempo_group,
2067                         ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2068                         name,
2069                         *new TempoSection (_marker->tempo())
2070                         );
2071
2072                 /* use the new marker for the grab */
2073                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2074
2075                 if (!_copy) {
2076                         TempoMap& map (_editor->session()->tempo_map());
2077                         /* get current state */
2078                         before_state = &map.get_state();
2079                         /* remove the section while we drag it */
2080                         map.remove_tempo (section, true);
2081                 }
2082         }
2083
2084         framepos_t const pf = adjusted_current_frame (event);
2085         _marker->set_position (pf);
2086         show_verbose_cursor_time (pf);
2087 }
2088
2089 void
2090 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2091 {
2092         if (!movement_occurred) {
2093                 return;
2094         }
2095
2096         motion (event, false);
2097
2098         TempoMap& map (_editor->session()->tempo_map());
2099         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2100         Timecode::BBT_Time when;
2101
2102         map.bbt_time (beat_time, when);
2103
2104         if (_copy == true) {
2105                 _editor->begin_reversible_command (_("copy tempo mark"));
2106                 XMLNode &before = map.get_state();
2107                 map.add_tempo (_marker->tempo(), when);
2108                 XMLNode &after = map.get_state();
2109                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2110                 _editor->commit_reversible_command ();
2111
2112         } else {
2113                 _editor->begin_reversible_command (_("move tempo mark"));
2114                 /* we removed it before, so add it back now */
2115                 map.add_tempo (_marker->tempo(), when);
2116                 XMLNode &after = map.get_state();
2117                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2118                 _editor->commit_reversible_command ();
2119         }
2120
2121         // delete the dummy marker we used for visual representation while moving.
2122         // a new visual marker will show up automatically.
2123         delete _marker;
2124 }
2125
2126 void
2127 TempoMarkerDrag::aborted (bool moved)
2128 {
2129         _marker->set_position (_marker->tempo().frame());
2130         if (moved) {
2131                 TempoMap& map (_editor->session()->tempo_map());
2132                 /* we removed it before, so add it back now */
2133                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2134                 // delete the dummy marker we used for visual representation while moving.
2135                 // a new visual marker will show up automatically.
2136                 delete _marker;
2137         }
2138 }
2139
2140 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2141         : Drag (e, i),
2142           _stop (s)
2143 {
2144         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2145 }
2146
2147 /** Do all the things we do when dragging the playhead to make it look as though
2148  *  we have located, without actually doing the locate (because that would cause
2149  *  the diskstream buffers to be refilled, which is too slow).
2150  */
2151 void
2152 CursorDrag::fake_locate (framepos_t t)
2153 {
2154         _editor->playhead_cursor->set_position (t);
2155
2156         Session* s = _editor->session ();
2157         if (s->timecode_transmission_suspended ()) {
2158                 framepos_t const f = _editor->playhead_cursor->current_frame;
2159                 s->send_mmc_locate (f);
2160                 s->send_full_time_code (f);
2161         }
2162
2163         show_verbose_cursor_time (t);
2164         _editor->UpdateAllTransportClocks (t);
2165 }
2166
2167 void
2168 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2169 {
2170         Drag::start_grab (event, c);
2171
2172         _grab_zoom = _editor->frames_per_unit;
2173
2174         framepos_t where = _editor->event_frame (event, 0, 0);
2175         _editor->snap_to_with_modifier (where, event);
2176
2177         _editor->_dragging_playhead = true;
2178
2179         Session* s = _editor->session ();
2180
2181         if (s) {
2182                 if (_was_rolling && _stop) {
2183                         s->request_stop ();
2184                 }
2185
2186                 if (s->is_auditioning()) {
2187                         s->cancel_audition ();
2188                 }
2189
2190                 s->request_suspend_timecode_transmission ();
2191                 while (!s->timecode_transmission_suspended ()) {
2192                         /* twiddle our thumbs */
2193                 }
2194         }
2195
2196         fake_locate (where);
2197 }
2198
2199 void
2200 CursorDrag::motion (GdkEvent* event, bool)
2201 {
2202         framepos_t const adjusted_frame = adjusted_current_frame (event);
2203         if (adjusted_frame != last_pointer_frame()) {
2204                 fake_locate (adjusted_frame);
2205 #ifdef GTKOSX
2206                 _editor->update_canvas_now ();
2207 #endif
2208         }
2209 }
2210
2211 void
2212 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2213 {
2214         _editor->_dragging_playhead = false;
2215
2216         if (!movement_occurred && _stop) {
2217                 return;
2218         }
2219
2220         motion (event, false);
2221
2222         Session* s = _editor->session ();
2223         if (s) {
2224                 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2225                 _editor->_pending_locate_request = true;
2226                 s->request_resume_timecode_transmission ();
2227         }
2228 }
2229
2230 void
2231 CursorDrag::aborted (bool)
2232 {
2233         if (_editor->_dragging_playhead) {
2234                 _editor->session()->request_resume_timecode_transmission ();
2235                 _editor->_dragging_playhead = false;
2236         }
2237
2238         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2239 }
2240
2241 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2242         : RegionDrag (e, i, p, v)
2243 {
2244         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2245 }
2246
2247 void
2248 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2249 {
2250         Drag::start_grab (event, cursor);
2251
2252         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2253         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2254
2255         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2256
2257         arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2258 }
2259
2260 void
2261 FadeInDrag::setup_pointer_frame_offset ()
2262 {
2263         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2264         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2265         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2266 }
2267
2268 void
2269 FadeInDrag::motion (GdkEvent* event, bool)
2270 {
2271         framecnt_t fade_length;
2272
2273         framepos_t const pos = adjusted_current_frame (event);
2274
2275         boost::shared_ptr<Region> region = _primary->region ();
2276
2277         if (pos < (region->position() + 64)) {
2278                 fade_length = 64; // this should be a minimum defined somewhere
2279         } else if (pos > region->last_frame()) {
2280                 fade_length = region->length();
2281         } else {
2282                 fade_length = pos - region->position();
2283         }
2284
2285         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2286
2287                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2288
2289                 if (!tmp) {
2290                         continue;
2291                 }
2292
2293                 tmp->reset_fade_in_shape_width (fade_length);
2294                 tmp->show_fade_line((framecnt_t) fade_length);
2295         }
2296
2297         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2298 }
2299
2300 void
2301 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2302 {
2303         if (!movement_occurred) {
2304                 return;
2305         }
2306
2307         framecnt_t fade_length;
2308
2309         framepos_t const pos = adjusted_current_frame (event);
2310
2311         boost::shared_ptr<Region> region = _primary->region ();
2312
2313         if (pos < (region->position() + 64)) {
2314                 fade_length = 64; // this should be a minimum defined somewhere
2315         } else if (pos > region->last_frame()) {
2316                 fade_length = region->length();
2317         } else {
2318                 fade_length = pos - region->position();
2319         }
2320
2321         _editor->begin_reversible_command (_("change fade in length"));
2322
2323         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2324
2325                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2326
2327                 if (!tmp) {
2328                         continue;
2329                 }
2330
2331                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2332                 XMLNode &before = alist->get_state();
2333
2334                 tmp->audio_region()->set_fade_in_length (fade_length);
2335                 tmp->audio_region()->set_fade_in_active (true);
2336                 tmp->hide_fade_line();
2337
2338                 XMLNode &after = alist->get_state();
2339                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2340         }
2341
2342         _editor->commit_reversible_command ();
2343 }
2344
2345 void
2346 FadeInDrag::aborted (bool)
2347 {
2348         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2349                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2350
2351                 if (!tmp) {
2352                         continue;
2353                 }
2354
2355                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2356                 tmp->hide_fade_line();
2357         }
2358 }
2359
2360 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2361         : RegionDrag (e, i, p, v)
2362 {
2363         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2364 }
2365
2366 void
2367 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2368 {
2369         Drag::start_grab (event, cursor);
2370
2371         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2372         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2373
2374         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2375
2376         arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2377 }
2378
2379 void
2380 FadeOutDrag::setup_pointer_frame_offset ()
2381 {
2382         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2383         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2384         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2385 }
2386
2387 void
2388 FadeOutDrag::motion (GdkEvent* event, bool)
2389 {
2390         framecnt_t fade_length;
2391
2392         framepos_t const pos = adjusted_current_frame (event);
2393
2394         boost::shared_ptr<Region> region = _primary->region ();
2395
2396         if (pos > (region->last_frame() - 64)) {
2397                 fade_length = 64; // this should really be a minimum fade defined somewhere
2398         }
2399         else if (pos < region->position()) {
2400                 fade_length = region->length();
2401         }
2402         else {
2403                 fade_length = region->last_frame() - pos;
2404         }
2405
2406         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2407
2408                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2409
2410                 if (!tmp) {
2411                         continue;
2412                 }
2413
2414                 tmp->reset_fade_out_shape_width (fade_length);
2415                 tmp->show_fade_line(region->length() - fade_length);
2416         }
2417
2418         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2419 }
2420
2421 void
2422 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2423 {
2424         if (!movement_occurred) {
2425                 return;
2426         }
2427
2428         framecnt_t fade_length;
2429
2430         framepos_t const pos = adjusted_current_frame (event);
2431
2432         boost::shared_ptr<Region> region = _primary->region ();
2433
2434         if (pos > (region->last_frame() - 64)) {
2435                 fade_length = 64; // this should really be a minimum fade defined somewhere
2436         }
2437         else if (pos < region->position()) {
2438                 fade_length = region->length();
2439         }
2440         else {
2441                 fade_length = region->last_frame() - pos;
2442         }
2443
2444         _editor->begin_reversible_command (_("change fade out length"));
2445
2446         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2447
2448                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2449
2450                 if (!tmp) {
2451                         continue;
2452                 }
2453
2454                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2455                 XMLNode &before = alist->get_state();
2456
2457                 tmp->audio_region()->set_fade_out_length (fade_length);
2458                 tmp->audio_region()->set_fade_out_active (true);
2459                 tmp->hide_fade_line();
2460
2461                 XMLNode &after = alist->get_state();
2462                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2463         }
2464
2465         _editor->commit_reversible_command ();
2466 }
2467
2468 void
2469 FadeOutDrag::aborted (bool)
2470 {
2471         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2472                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2473
2474                 if (!tmp) {
2475                         continue;
2476                 }
2477
2478                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2479                 tmp->hide_fade_line();
2480         }
2481 }
2482
2483 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2484         : Drag (e, i)
2485 {
2486         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2487
2488         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2489         assert (_marker);
2490
2491         _points.push_back (Gnome::Art::Point (0, 0));
2492         _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2493 }
2494
2495 MarkerDrag::~MarkerDrag ()
2496 {
2497         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2498                 delete *i;
2499         }
2500 }
2501
2502 void
2503 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2504 {
2505         Drag::start_grab (event, cursor);
2506
2507         bool is_start;
2508
2509         Location *location = _editor->find_location_from_marker (_marker, is_start);
2510         _editor->_dragging_edit_point = true;
2511
2512         update_item (location);
2513
2514         // _drag_line->show();
2515         // _line->raise_to_top();
2516
2517         if (is_start) {
2518                 show_verbose_cursor_time (location->start());
2519         } else {
2520                 show_verbose_cursor_time (location->end());
2521         }
2522
2523         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2524
2525         switch (op) {
2526         case Selection::Toggle:
2527                 _editor->selection->toggle (_marker);
2528                 break;
2529         case Selection::Set:
2530                 if (!_editor->selection->selected (_marker)) {
2531                         _editor->selection->set (_marker);
2532                 }
2533                 break;
2534         case Selection::Extend:
2535         {
2536                 Locations::LocationList ll;
2537                 list<Marker*> to_add;
2538                 framepos_t s, e;
2539                 _editor->selection->markers.range (s, e);
2540                 s = min (_marker->position(), s);
2541                 e = max (_marker->position(), e);
2542                 s = min (s, e);
2543                 e = max (s, e);
2544                 if (e < max_framepos) {
2545                         ++e;
2546                 }
2547                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2548                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2549                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2550                         if (lm) {
2551                                 if (lm->start) {
2552                                         to_add.push_back (lm->start);
2553                                 }
2554                                 if (lm->end) {
2555                                         to_add.push_back (lm->end);
2556                                 }
2557                         }
2558                 }
2559                 if (!to_add.empty()) {
2560                         _editor->selection->add (to_add);
2561                 }
2562                 break;
2563         }
2564         case Selection::Add:
2565                 _editor->selection->add (_marker);
2566                 break;
2567         }
2568
2569         /* Set up copies for us to manipulate during the drag */
2570
2571         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2572                 Location* l = _editor->find_location_from_marker (*i, is_start);
2573                 _copied_locations.push_back (new Location (*l));
2574         }
2575 }
2576
2577 void
2578 MarkerDrag::setup_pointer_frame_offset ()
2579 {
2580         bool is_start;
2581         Location *location = _editor->find_location_from_marker (_marker, is_start);
2582         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2583 }
2584
2585 void
2586 MarkerDrag::motion (GdkEvent* event, bool)
2587 {
2588         framecnt_t f_delta = 0;
2589         bool is_start;
2590         bool move_both = false;
2591         Marker* marker;
2592         Location *real_location;
2593         Location *copy_location = 0;
2594
2595         framepos_t const newframe = adjusted_current_frame (event);
2596
2597         framepos_t next = newframe;
2598
2599         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2600                 move_both = true;
2601         }
2602
2603         MarkerSelection::iterator i;
2604         list<Location*>::iterator x;
2605
2606         /* find the marker we're dragging, and compute the delta */
2607
2608         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2609              x != _copied_locations.end() && i != _editor->selection->markers.end();
2610              ++i, ++x) {
2611
2612                 copy_location = *x;
2613                 marker = *i;
2614
2615                 if (marker == _marker) {
2616
2617                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2618                                 /* que pasa ?? */
2619                                 return;
2620                         }
2621
2622                         if (real_location->is_mark()) {
2623                                 f_delta = newframe - copy_location->start();
2624                         } else {
2625
2626
2627                                 switch (marker->type()) {
2628                                 case Marker::SessionStart:
2629                                 case Marker::RangeStart:
2630                                 case Marker::LoopStart:
2631                                 case Marker::PunchIn:
2632                                         f_delta = newframe - copy_location->start();
2633                                         break;
2634
2635                                 case Marker::SessionEnd:
2636                                 case Marker::RangeEnd:
2637                                 case Marker::LoopEnd:
2638                                 case Marker::PunchOut:
2639                                         f_delta = newframe - copy_location->end();
2640                                         break;
2641                                 default:
2642                                         /* what kind of marker is this ? */
2643                                         return;
2644                                 }
2645                         }
2646                         break;
2647                 }
2648         }
2649
2650         if (i == _editor->selection->markers.end()) {
2651                 /* hmm, impossible - we didn't find the dragged marker */
2652                 return;
2653         }
2654
2655         /* now move them all */
2656
2657         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2658              x != _copied_locations.end() && i != _editor->selection->markers.end();
2659              ++i, ++x) {
2660
2661                 copy_location = *x;
2662                 marker = *i;
2663
2664                 /* call this to find out if its the start or end */
2665
2666                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2667                         continue;
2668                 }
2669
2670                 if (real_location->locked()) {
2671                         continue;
2672                 }
2673
2674                 if (copy_location->is_mark()) {
2675
2676                         /* now move it */
2677
2678                         copy_location->set_start (copy_location->start() + f_delta);
2679
2680                 } else {
2681
2682                         framepos_t new_start = copy_location->start() + f_delta;
2683                         framepos_t new_end = copy_location->end() + f_delta;
2684
2685                         if (is_start) { // start-of-range marker
2686
2687                                 if (move_both) {
2688                                         copy_location->set_start (new_start);
2689                                         copy_location->set_end (new_end);
2690                                 } else  if (new_start < copy_location->end()) {
2691                                         copy_location->set_start (new_start);
2692                                 } else if (newframe > 0) {
2693                                         _editor->snap_to (next, 1, true);
2694                                         copy_location->set_end (next);
2695                                         copy_location->set_start (newframe);
2696                                 }
2697
2698                         } else { // end marker
2699
2700                                 if (move_both) {
2701                                         copy_location->set_end (new_end);
2702                                         copy_location->set_start (new_start);
2703                                 } else if (new_end > copy_location->start()) {
2704                                         copy_location->set_end (new_end);
2705                                 } else if (newframe > 0) {
2706                                         _editor->snap_to (next, -1, true);
2707                                         copy_location->set_start (next);
2708                                         copy_location->set_end (newframe);
2709                                 }
2710                         }
2711                 }
2712
2713                 update_item (copy_location);
2714
2715                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2716
2717                 if (lm) {
2718                         lm->set_position (copy_location->start(), copy_location->end());
2719                 }
2720         }
2721
2722         assert (!_copied_locations.empty());
2723
2724         show_verbose_cursor_time (newframe);
2725
2726 #ifdef GTKOSX
2727         _editor->update_canvas_now ();
2728 #endif
2729 }
2730
2731 void
2732 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2733 {
2734         if (!movement_occurred) {
2735
2736                 /* just a click, do nothing but finish
2737                    off the selection process
2738                 */
2739
2740                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2741
2742                 switch (op) {
2743                 case Selection::Set:
2744                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2745                                 _editor->selection->set (_marker);
2746                         }
2747                         break;
2748
2749                 case Selection::Toggle:
2750                 case Selection::Extend:
2751                 case Selection::Add:
2752                         break;
2753                 }
2754
2755                 return;
2756         }
2757
2758         _editor->_dragging_edit_point = false;
2759
2760         _editor->begin_reversible_command ( _("move marker") );
2761         XMLNode &before = _editor->session()->locations()->get_state();
2762
2763         MarkerSelection::iterator i;
2764         list<Location*>::iterator x;
2765         bool is_start;
2766
2767         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2768              x != _copied_locations.end() && i != _editor->selection->markers.end();
2769              ++i, ++x) {
2770
2771                 Location * location = _editor->find_location_from_marker (*i, is_start);
2772
2773                 if (location) {
2774
2775                         if (location->locked()) {
2776                                 return;
2777                         }
2778
2779                         if (location->is_mark()) {
2780                                 location->set_start ((*x)->start());
2781                         } else {
2782                                 location->set ((*x)->start(), (*x)->end());
2783                         }
2784                 }
2785         }
2786
2787         XMLNode &after = _editor->session()->locations()->get_state();
2788         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2789         _editor->commit_reversible_command ();
2790 }
2791
2792 void
2793 MarkerDrag::aborted (bool)
2794 {
2795         /* XXX: TODO */
2796 }
2797
2798 void
2799 MarkerDrag::update_item (Location*)
2800 {
2801         /* noop */
2802 }
2803
2804 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2805         : Drag (e, i),
2806           _cumulative_x_drag (0),
2807           _cumulative_y_drag (0)
2808 {
2809         if (_zero_gain_fraction < 0.0) {
2810                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2811         }
2812
2813         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2814
2815         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2816         assert (_point);
2817 }
2818
2819
2820 void
2821 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2822 {
2823         Drag::start_grab (event, _editor->cursors()->fader);
2824
2825         // start the grab at the center of the control point so
2826         // the point doesn't 'jump' to the mouse after the first drag
2827         _fixed_grab_x = _point->get_x();
2828         _fixed_grab_y = _point->get_y();
2829
2830         float const fraction = 1 - (_point->get_y() / _point->line().height());
2831
2832         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2833
2834         _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2835                                         event->button.x + 10, event->button.y + 10);
2836
2837         _editor->verbose_cursor()->show ();
2838 }
2839
2840 void
2841 ControlPointDrag::motion (GdkEvent* event, bool)
2842 {
2843         double dx = _drags->current_pointer_x() - last_pointer_x();
2844         double dy = _drags->current_pointer_y() - last_pointer_y();
2845
2846         if (event->button.state & Keyboard::SecondaryModifier) {
2847                 dx *= 0.1;
2848                 dy *= 0.1;
2849         }
2850
2851         /* coordinate in pixels relative to the start of the region (for region-based automation)
2852            or track (for track-based automation) */
2853         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2854         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2855
2856         // calculate zero crossing point. back off by .01 to stay on the
2857         // positive side of zero
2858         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2859
2860         // make sure we hit zero when passing through
2861         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2862                 cy = zero_gain_y;
2863         }
2864
2865         if (_x_constrained) {
2866                 cx = _fixed_grab_x;
2867         }
2868         if (_y_constrained) {
2869                 cy = _fixed_grab_y;
2870         }
2871
2872         _cumulative_x_drag = cx - _fixed_grab_x;
2873         _cumulative_y_drag = cy - _fixed_grab_y;
2874
2875         cx = max (0.0, cx);
2876         cy = max (0.0, cy);
2877         cy = min ((double) _point->line().height(), cy);
2878
2879         framepos_t cx_frames = _editor->unit_to_frame (cx);
2880
2881         if (!_x_constrained) {
2882                 _editor->snap_to_with_modifier (cx_frames, event);
2883         }
2884
2885         cx_frames = min (cx_frames, _point->line().maximum_time());
2886
2887         float const fraction = 1.0 - (cy / _point->line().height());
2888
2889         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2890
2891         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2892
2893         _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2894 }
2895
2896 void
2897 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2898 {
2899         if (!movement_occurred) {
2900
2901                 /* just a click */
2902
2903                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2904                         _editor->reset_point_selection ();
2905                 }
2906
2907         } else {
2908                 motion (event, false);
2909         }
2910
2911         _point->line().end_drag ();
2912         _editor->session()->commit_reversible_command ();
2913 }
2914
2915 void
2916 ControlPointDrag::aborted (bool)
2917 {
2918         _point->line().reset ();
2919 }
2920
2921 bool
2922 ControlPointDrag::active (Editing::MouseMode m)
2923 {
2924         if (m == Editing::MouseGain) {
2925                 /* always active in mouse gain */
2926                 return true;
2927         }
2928
2929         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2930         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2931 }
2932
2933 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2934         : Drag (e, i),
2935           _line (0),
2936           _cumulative_y_drag (0)
2937 {
2938         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2939 }
2940
2941 void
2942 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2943 {
2944         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2945         assert (_line);
2946
2947         _item = &_line->grab_item ();
2948
2949         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2950            origin, and ditto for y.
2951         */
2952
2953         double cx = event->button.x;
2954         double cy = event->button.y;
2955
2956         _line->parent_group().w2i (cx, cy);
2957
2958         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2959
2960         uint32_t before;
2961         uint32_t after;
2962
2963         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2964                 /* no adjacent points */
2965                 return;
2966         }
2967
2968         Drag::start_grab (event, _editor->cursors()->fader);
2969
2970         /* store grab start in parent frame */
2971
2972         _fixed_grab_x = cx;
2973         _fixed_grab_y = cy;
2974
2975         double fraction = 1.0 - (cy / _line->height());
2976
2977         _line->start_drag_line (before, after, fraction);
2978
2979         _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2980                                         event->button.x + 10, event->button.y + 10);
2981
2982         _editor->verbose_cursor()->show ();
2983 }
2984
2985 void
2986 LineDrag::motion (GdkEvent* event, bool)
2987 {
2988         double dy = _drags->current_pointer_y() - last_pointer_y();
2989
2990         if (event->button.state & Keyboard::SecondaryModifier) {
2991                 dy *= 0.1;
2992         }
2993
2994         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2995
2996         _cumulative_y_drag = cy - _fixed_grab_y;
2997
2998         cy = max (0.0, cy);
2999         cy = min ((double) _line->height(), cy);
3000
3001         double const fraction = 1.0 - (cy / _line->height());
3002
3003         bool push;
3004
3005         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3006                 push = false;
3007         } else {
3008                 push = true;
3009         }
3010
3011         /* we are ignoring x position for this drag, so we can just pass in anything */
3012         _line->drag_motion (0, fraction, true, push);
3013
3014         _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3015 }
3016
3017 void
3018 LineDrag::finished (GdkEvent* event, bool)
3019 {
3020         motion (event, false);
3021         _line->end_drag ();
3022         _editor->session()->commit_reversible_command ();
3023 }
3024
3025 void
3026 LineDrag::aborted (bool)
3027 {
3028         _line->reset ();
3029 }
3030
3031 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3032         : Drag (e, i),
3033           _line (0),
3034           _cumulative_x_drag (0)
3035 {
3036         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3037 }
3038
3039 void
3040 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3041 {
3042         Drag::start_grab (event);
3043
3044         _line = reinterpret_cast<Line*> (_item);
3045         assert (_line);
3046
3047         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3048
3049         double cx = event->button.x;
3050         double cy = event->button.y;
3051
3052         _item->property_parent().get_value()->w2i(cx, cy);
3053
3054         /* store grab start in parent frame */
3055         _region_view_grab_x = cx;
3056
3057         _before = *(float*) _item->get_data ("position");
3058
3059         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3060
3061         _max_x = _editor->frame_to_pixel(_arv->get_duration());
3062 }
3063
3064 void
3065 FeatureLineDrag::motion (GdkEvent*, bool)
3066 {
3067         double dx = _drags->current_pointer_x() - last_pointer_x();
3068
3069         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3070
3071         _cumulative_x_drag += dx;
3072
3073         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3074
3075         if (cx > _max_x){
3076                 cx = _max_x;
3077         }
3078         else if(cx < 0){
3079                 cx = 0;
3080         }
3081
3082         ArdourCanvas::Points points;
3083
3084         double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3085
3086         _line->get_bounds(x1, y2, x2, y2);
3087
3088         points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3089         points.push_back(Gnome::Art::Point(cx, y2 - y1));
3090
3091         _line->property_points() = points;
3092
3093         float *pos = new float;
3094         *pos = cx;
3095
3096         _line->set_data ("position", pos);
3097
3098         _before = cx;
3099 }
3100
3101 void
3102 FeatureLineDrag::finished (GdkEvent*, bool)
3103 {
3104         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3105         _arv->update_transient(_before, _before);
3106 }
3107
3108 void
3109 FeatureLineDrag::aborted (bool)
3110 {
3111         //_line->reset ();
3112 }
3113
3114 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3115         : Drag (e, i)
3116         , _vertical_only (false)
3117 {
3118         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3119 }
3120
3121 void
3122 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3123 {
3124         Drag::start_grab (event);
3125         show_verbose_cursor_time (adjusted_current_frame (event));
3126 }
3127
3128 void
3129 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3130 {
3131         framepos_t start;
3132         framepos_t end;
3133         double y1;
3134         double y2;
3135
3136         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3137
3138         framepos_t grab = grab_frame ();
3139         if (Config->get_rubberbanding_snaps_to_grid ()) {
3140                 _editor->snap_to_with_modifier (grab, event);
3141         }
3142
3143         /* base start and end on initial click position */
3144
3145         if (pf < grab) {
3146                 start = pf;
3147                 end = grab;
3148         } else {
3149                 end = pf;
3150                 start = grab;
3151         }
3152
3153         if (_drags->current_pointer_y() < grab_y()) {
3154                 y1 = _drags->current_pointer_y();
3155                 y2 = grab_y();
3156         } else {
3157                 y2 = _drags->current_pointer_y();
3158                 y1 = grab_y();
3159         }
3160
3161
3162         if (start != end || y1 != y2) {
3163
3164                 double x1 = _editor->frame_to_pixel (start);
3165                 double x2 = _editor->frame_to_pixel (end);
3166
3167                 _editor->rubberband_rect->property_x1() = x1;
3168                 if (_vertical_only) {
3169                         /* fixed 10 pixel width */
3170                         _editor->rubberband_rect->property_x2() = x1 + 10;
3171                 } else {
3172                         _editor->rubberband_rect->property_x2() = x2;
3173                 } 
3174
3175                 _editor->rubberband_rect->property_y1() = y1;
3176                 _editor->rubberband_rect->property_y2() = y2;
3177
3178                 _editor->rubberband_rect->show();
3179                 _editor->rubberband_rect->raise_to_top();
3180
3181                 show_verbose_cursor_time (pf);
3182
3183                 do_select_things (event, true);
3184         }
3185 }
3186
3187 void
3188 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3189 {
3190         framepos_t x1;
3191         framepos_t x2;
3192         
3193         if (grab_frame() < last_pointer_frame()) {
3194                 x1 = grab_frame ();
3195                 x2 = last_pointer_frame ();
3196         } else {
3197                 x2 = grab_frame ();
3198                 x1 = last_pointer_frame ();
3199         }
3200
3201         double y1;
3202         double y2;
3203         
3204         if (_drags->current_pointer_y() < grab_y()) {
3205                 y1 = _drags->current_pointer_y();
3206                 y2 = grab_y();
3207         } else {
3208                 y2 = _drags->current_pointer_y();
3209                 y1 = grab_y();
3210         }
3211
3212         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3213 }
3214
3215 void
3216 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3217 {
3218         if (movement_occurred) {
3219
3220                 motion (event, false);
3221                 do_select_things (event, false);
3222
3223         } else {
3224
3225                 /* just a click */
3226
3227                 bool do_deselect = true;
3228                 MidiTimeAxisView* mtv;
3229
3230                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3231                         /* MIDI track */
3232                         if (_editor->selection->empty()) {
3233                                 /* nothing selected */
3234                                 add_midi_region (mtv);
3235                                 do_deselect = false;
3236                         }
3237                 } 
3238
3239                 if (do_deselect) {
3240                         deselect_things ();
3241                 }
3242
3243         }
3244
3245         _editor->rubberband_rect->hide();
3246 }
3247
3248 void
3249 RubberbandSelectDrag::aborted (bool)
3250 {
3251         _editor->rubberband_rect->hide ();
3252 }
3253
3254 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3255         : RegionDrag (e, i, p, v)
3256 {
3257         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3258 }
3259
3260 void
3261 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3262 {
3263         Drag::start_grab (event, cursor);
3264
3265         show_verbose_cursor_time (adjusted_current_frame (event));
3266 }
3267
3268 void
3269 TimeFXDrag::motion (GdkEvent* event, bool)
3270 {
3271         RegionView* rv = _primary;
3272
3273         framepos_t const pf = adjusted_current_frame (event);
3274
3275         if (pf > rv->region()->position()) {
3276                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3277         }
3278
3279         show_verbose_cursor_time (pf);
3280 }
3281
3282 void
3283 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3284 {
3285         _primary->get_time_axis_view().hide_timestretch ();
3286
3287         if (!movement_occurred) {
3288                 return;
3289         }
3290
3291         if (last_pointer_frame() < _primary->region()->position()) {
3292                 /* backwards drag of the left edge - not usable */
3293                 return;
3294         }
3295
3296         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3297
3298         float percentage = (double) newlen / (double) _primary->region()->length();
3299
3300 #ifndef USE_RUBBERBAND
3301         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3302         if (_primary->region()->data_type() == DataType::AUDIO) {
3303                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3304         }
3305 #endif
3306
3307         if (!_editor->get_selection().regions.empty()) {
3308                 /* primary will already be included in the selection, and edit
3309                    group shared editing will propagate selection across
3310                    equivalent regions, so just use the current region
3311                    selection.
3312                 */
3313
3314                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3315                         error << _("An error occurred while executing time stretch operation") << endmsg;
3316                 }
3317         }
3318 }
3319
3320 void
3321 TimeFXDrag::aborted (bool)
3322 {
3323         _primary->get_time_axis_view().hide_timestretch ();
3324 }
3325
3326 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3327         : Drag (e, i)
3328 {
3329         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3330 }
3331
3332 void
3333 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3334 {
3335         Drag::start_grab (event);
3336 }
3337
3338 void
3339 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3340 {
3341         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3342 }
3343
3344 void
3345 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3346 {
3347         if (movement_occurred && _editor->session()) {
3348                 /* make sure we stop */
3349                 _editor->session()->request_transport_speed (0.0);
3350         }
3351 }
3352
3353 void
3354 ScrubDrag::aborted (bool)
3355 {
3356         /* XXX: TODO */
3357 }
3358
3359 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3360         : Drag (e, i)
3361         , _operation (o)
3362         , _copy (false)
3363         , _original_pointer_time_axis (-1)
3364         , _last_pointer_time_axis (-1)
3365 {
3366         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3367 }
3368
3369 void
3370 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3371 {
3372         if (_editor->session() == 0) {
3373                 return;
3374         }
3375
3376         Gdk::Cursor* cursor = 0;
3377
3378         switch (_operation) {
3379         case CreateSelection:
3380                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3381                         _copy = true;
3382                 } else {
3383                         _copy = false;
3384                 }
3385                 cursor = _editor->cursors()->selector;
3386                 Drag::start_grab (event, cursor);
3387                 break;
3388
3389         case SelectionStartTrim:
3390                 if (_editor->clicked_axisview) {
3391                         _editor->clicked_axisview->order_selection_trims (_item, true);
3392                 }
3393                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3394                 break;
3395
3396         case SelectionEndTrim:
3397                 if (_editor->clicked_axisview) {
3398                         _editor->clicked_axisview->order_selection_trims (_item, false);
3399                 }
3400                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3401                 break;
3402
3403         case SelectionMove:
3404                 Drag::start_grab (event, cursor);
3405                 break;
3406         }
3407
3408         if (_operation == SelectionMove) {
3409                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3410         } else {
3411                 show_verbose_cursor_time (adjusted_current_frame (event));
3412         }
3413
3414         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3415 }
3416
3417 void
3418 SelectionDrag::setup_pointer_frame_offset ()
3419 {
3420         switch (_operation) {
3421         case CreateSelection:
3422                 _pointer_frame_offset = 0;
3423                 break;
3424
3425         case SelectionStartTrim:
3426         case SelectionMove:
3427                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3428                 break;
3429
3430         case SelectionEndTrim:
3431                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3432                 break;
3433         }
3434 }
3435
3436 void
3437 SelectionDrag::motion (GdkEvent* event, bool first_move)
3438 {
3439         framepos_t start = 0;
3440         framepos_t end = 0;
3441         framecnt_t length;
3442
3443         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3444         if (pending_time_axis.first == 0) {
3445                 return;
3446         }
3447
3448         framepos_t const pending_position = adjusted_current_frame (event);
3449
3450         /* only alter selection if things have changed */
3451
3452         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3453                 return;
3454         }
3455
3456         switch (_operation) {
3457         case CreateSelection:
3458         {
3459                 framepos_t grab = grab_frame ();
3460
3461                 if (first_move) {
3462                         _editor->snap_to (grab);
3463                 }
3464
3465                 if (pending_position < grab_frame()) {
3466                         start = pending_position;
3467                         end = grab;
3468                 } else {
3469                         end = pending_position;
3470                         start = grab;
3471                 }
3472
3473                 /* first drag: Either add to the selection
3474                    or create a new selection
3475                 */
3476
3477                 if (first_move) {
3478
3479                         if (_copy) {
3480                                 /* adding to the selection */
3481                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3482                                 //_editor->selection->add (_editor->clicked_axisview);
3483                                 _editor->clicked_selection = _editor->selection->add (start, end);
3484                                 _copy = false;
3485                         } else {
3486                                 /* new selection */
3487
3488                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3489                                         //_editor->selection->set (_editor->clicked_axisview);
3490                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3491                                 }
3492
3493                                 _editor->clicked_selection = _editor->selection->set (start, end);
3494                         }
3495                 }
3496
3497                 /* select the track that we're in */
3498                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3499                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3500                         _editor->selection->add (pending_time_axis.first);
3501                         _added_time_axes.push_back (pending_time_axis.first);
3502                 }
3503
3504                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3505                    tracks that we selected in the first place.
3506                 */
3507
3508                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3509                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3510
3511                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3512                 while (i != _added_time_axes.end()) {
3513
3514                         list<TimeAxisView*>::iterator tmp = i;
3515                         ++tmp;
3516
3517                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3518                                 _editor->selection->remove (*i);
3519                                 _added_time_axes.remove (*i);
3520                         }
3521
3522                         i = tmp;
3523                 }
3524
3525         }
3526         break;
3527
3528         case SelectionStartTrim:
3529
3530                 start = _editor->selection->time[_editor->clicked_selection].start;
3531                 end = _editor->selection->time[_editor->clicked_selection].end;
3532
3533                 if (pending_position > end) {
3534                         start = end;
3535                 } else {
3536                         start = pending_position;
3537                 }
3538                 break;
3539
3540         case SelectionEndTrim:
3541
3542                 start = _editor->selection->time[_editor->clicked_selection].start;
3543                 end = _editor->selection->time[_editor->clicked_selection].end;
3544
3545                 if (pending_position < start) {
3546                         end = start;
3547                 } else {
3548                         end = pending_position;
3549                 }
3550
3551                 break;
3552
3553         case SelectionMove:
3554
3555                 start = _editor->selection->time[_editor->clicked_selection].start;
3556                 end = _editor->selection->time[_editor->clicked_selection].end;
3557
3558                 length = end - start;
3559
3560                 start = pending_position;
3561                 _editor->snap_to (start);
3562
3563                 end = start + length;
3564
3565                 break;
3566         }
3567
3568         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3569                 _editor->start_canvas_autoscroll (1, 0);
3570         }
3571
3572         if (start != end) {
3573                 _editor->selection->replace (_editor->clicked_selection, start, end);
3574         }
3575
3576         if (_operation == SelectionMove) {
3577                 show_verbose_cursor_time(start);
3578         } else {
3579                 show_verbose_cursor_time(pending_position);
3580         }
3581 }
3582
3583 void
3584 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3585 {
3586         Session* s = _editor->session();
3587
3588         if (movement_occurred) {
3589                 motion (event, false);
3590                 /* XXX this is not object-oriented programming at all. ick */
3591                 if (_editor->selection->time.consolidate()) {
3592                         _editor->selection->TimeChanged ();
3593                 }
3594
3595                 /* XXX what if its a music time selection? */
3596                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3597                         s->request_play_range (&_editor->selection->time, true);
3598                 }
3599
3600
3601         } else {
3602                 /* just a click, no pointer movement.*/
3603
3604                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3605                         _editor->selection->clear_time();
3606                 }
3607
3608                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3609                         _editor->selection->set (_editor->clicked_axisview);
3610                 }
3611
3612                 if (s && s->get_play_range () && s->transport_rolling()) {
3613                         s->request_stop (false, false);
3614                 }
3615
3616         }
3617
3618         _editor->stop_canvas_autoscroll ();
3619 }
3620
3621 void
3622 SelectionDrag::aborted (bool)
3623 {
3624         /* XXX: TODO */
3625 }
3626
3627 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3628         : Drag (e, i),
3629           _operation (o),
3630           _copy (false)
3631 {
3632         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3633
3634         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3635                                                    physical_screen_height (_editor->get_window()));
3636         _drag_rect->hide ();
3637
3638         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3639         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3640 }
3641
3642 void
3643 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3644 {
3645         if (_editor->session() == 0) {
3646                 return;
3647         }
3648
3649         Gdk::Cursor* cursor = 0;
3650
3651         if (!_editor->temp_location) {
3652                 _editor->temp_location = new Location (*_editor->session());
3653         }
3654
3655         switch (_operation) {
3656         case CreateRangeMarker:
3657         case CreateTransportMarker:
3658         case CreateCDMarker:
3659
3660                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3661                         _copy = true;
3662                 } else {
3663                         _copy = false;
3664                 }
3665                 cursor = _editor->cursors()->selector;
3666                 break;
3667         }
3668
3669         Drag::start_grab (event, cursor);
3670
3671         show_verbose_cursor_time (adjusted_current_frame (event));
3672 }
3673
3674 void
3675 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3676 {
3677         framepos_t start = 0;
3678         framepos_t end = 0;
3679         ArdourCanvas::SimpleRect *crect;
3680
3681         switch (_operation) {
3682         case CreateRangeMarker:
3683                 crect = _editor->range_bar_drag_rect;
3684                 break;
3685         case CreateTransportMarker:
3686                 crect = _editor->transport_bar_drag_rect;
3687                 break;
3688         case CreateCDMarker:
3689                 crect = _editor->cd_marker_bar_drag_rect;
3690                 break;
3691         default:
3692                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3693                 return;
3694                 break;
3695         }
3696
3697         framepos_t const pf = adjusted_current_frame (event);
3698
3699         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3700                 framepos_t grab = grab_frame ();
3701                 _editor->snap_to (grab);
3702
3703                 if (pf < grab_frame()) {
3704                         start = pf;
3705                         end = grab;
3706                 } else {
3707                         end = pf;
3708                         start = grab;
3709                 }
3710
3711                 /* first drag: Either add to the selection
3712                    or create a new selection.
3713                 */
3714
3715                 if (first_move) {
3716
3717                         _editor->temp_location->set (start, end);
3718
3719                         crect->show ();
3720
3721                         update_item (_editor->temp_location);
3722                         _drag_rect->show();
3723                         //_drag_rect->raise_to_top();
3724
3725                 }
3726         }
3727
3728         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3729                 _editor->start_canvas_autoscroll (1, 0);
3730         }
3731
3732         if (start != end) {
3733                 _editor->temp_location->set (start, end);
3734
3735                 double x1 = _editor->frame_to_pixel (start);
3736                 double x2 = _editor->frame_to_pixel (end);
3737                 crect->property_x1() = x1;
3738                 crect->property_x2() = x2;
3739
3740                 update_item (_editor->temp_location);
3741         }
3742
3743         show_verbose_cursor_time (pf);
3744
3745 }
3746
3747 void
3748 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3749 {
3750         Location * newloc = 0;
3751         string rangename;
3752         int flags;
3753
3754         if (movement_occurred) {
3755                 motion (event, false);
3756                 _drag_rect->hide();
3757
3758                 switch (_operation) {
3759                 case CreateRangeMarker:
3760                 case CreateCDMarker:
3761                     {
3762                         _editor->begin_reversible_command (_("new range marker"));
3763                         XMLNode &before = _editor->session()->locations()->get_state();
3764                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3765                         if (_operation == CreateCDMarker) {
3766                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3767                                 _editor->cd_marker_bar_drag_rect->hide();
3768                         }
3769                         else {
3770                                 flags = Location::IsRangeMarker;
3771                                 _editor->range_bar_drag_rect->hide();
3772                         }
3773                         newloc = new Location (
3774                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3775                                 );
3776
3777                         _editor->session()->locations()->add (newloc, true);
3778                         XMLNode &after = _editor->session()->locations()->get_state();
3779                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3780                         _editor->commit_reversible_command ();
3781                         break;
3782                     }
3783
3784                 case CreateTransportMarker:
3785                         // popup menu to pick loop or punch
3786                         _editor->new_transport_marker_context_menu (&event->button, _item);
3787                         break;
3788                 }
3789         } else {
3790                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3791
3792                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3793
3794                         framepos_t start;
3795                         framepos_t end;
3796
3797                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3798
3799                         if (end == max_framepos) {
3800                                 end = _editor->session()->current_end_frame ();
3801                         }
3802
3803                         if (start == max_framepos) {
3804                                 start = _editor->session()->current_start_frame ();
3805                         }
3806
3807                         switch (_editor->mouse_mode) {
3808                         case MouseObject:
3809                                 /* find the two markers on either side and then make the selection from it */
3810                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3811                                 break;
3812
3813                         case MouseRange:
3814                                 /* find the two markers on either side of the click and make the range out of it */
3815                                 _editor->selection->set (start, end);
3816                                 break;
3817
3818                         default:
3819                                 break;
3820                         }
3821                 }
3822         }
3823
3824         _editor->stop_canvas_autoscroll ();
3825 }
3826
3827 void
3828 RangeMarkerBarDrag::aborted (bool)
3829 {
3830         /* XXX: TODO */
3831 }
3832
3833 void
3834 RangeMarkerBarDrag::update_item (Location* location)
3835 {
3836         double const x1 = _editor->frame_to_pixel (location->start());
3837         double const x2 = _editor->frame_to_pixel (location->end());
3838
3839         _drag_rect->property_x1() = x1;
3840         _drag_rect->property_x2() = x2;
3841 }
3842
3843 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3844         : Drag (e, i)
3845         , _zoom_out (false)
3846 {
3847         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3848 }
3849
3850 void
3851 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3852 {
3853         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3854                 Drag::start_grab (event, _editor->cursors()->zoom_out);
3855                 _zoom_out = true;
3856         } else {
3857                 Drag::start_grab (event, _editor->cursors()->zoom_in);
3858                 _zoom_out = false;
3859         }
3860
3861         show_verbose_cursor_time (adjusted_current_frame (event));
3862 }
3863
3864 void
3865 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3866 {
3867         framepos_t start;
3868         framepos_t end;
3869
3870         framepos_t const pf = adjusted_current_frame (event);
3871
3872         framepos_t grab = grab_frame ();
3873         _editor->snap_to_with_modifier (grab, event);
3874
3875         /* base start and end on initial click position */
3876         if (pf < grab) {
3877                 start = pf;
3878                 end = grab;
3879         } else {
3880                 end = pf;
3881                 start = grab;
3882         }
3883
3884         if (start != end) {
3885
3886                 if (first_move) {
3887                         _editor->zoom_rect->show();
3888                         _editor->zoom_rect->raise_to_top();
3889                 }
3890
3891                 _editor->reposition_zoom_rect(start, end);
3892
3893                 show_verbose_cursor_time (pf);
3894         }
3895 }
3896
3897 void
3898 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3899 {
3900         if (movement_occurred) {
3901                 motion (event, false);
3902
3903                 if (grab_frame() < last_pointer_frame()) {
3904                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3905                 } else {
3906                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3907                 }
3908         } else {
3909                 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3910                         _editor->tav_zoom_step (_zoom_out);
3911                 } else {
3912                         _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3913                 }
3914         }
3915
3916         _editor->zoom_rect->hide();
3917 }
3918
3919 void
3920 MouseZoomDrag::aborted (bool)
3921 {
3922         _editor->zoom_rect->hide ();
3923 }
3924
3925 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3926         : Drag (e, i)
3927         , _cumulative_dx (0)
3928         , _cumulative_dy (0)
3929 {
3930         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3931
3932         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3933         _region = &_primary->region_view ();
3934         _note_height = _region->midi_stream_view()->note_height ();
3935 }
3936
3937 void
3938 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3939 {
3940         Drag::start_grab (event);
3941
3942         if (!(_was_selected = _primary->selected())) {
3943
3944                 /* tertiary-click means extend selection - we'll do that on button release,
3945                    so don't add it here, because otherwise we make it hard to figure
3946                    out the "extend-to" range.
3947                 */
3948
3949                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3950
3951                 if (!extend) {
3952                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3953
3954                         if (add) {
3955                                 _region->note_selected (_primary, true);
3956                         } else {
3957                                 _region->unique_select (_primary);
3958                         }
3959                 }
3960         }
3961 }
3962
3963 /** @return Current total drag x change in frames */
3964 frameoffset_t
3965 NoteDrag::total_dx () const
3966 {
3967         /* dx in frames */
3968         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3969
3970         /* primary note time */
3971         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3972
3973         /* new time of the primary note in session frames */
3974         frameoffset_t st = n + dx;
3975
3976         framepos_t const rp = _region->region()->position ();
3977
3978         /* prevent the note being dragged earlier than the region's position */
3979         st = max (st, rp);
3980
3981         /* snap and return corresponding delta */
3982         return _region->snap_frame_to_frame (st - rp) + rp - n;
3983 }
3984
3985 /** @return Current total drag y change in note number */
3986 int8_t
3987 NoteDrag::total_dy () const
3988 {
3989         return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3990 }
3991
3992 void
3993 NoteDrag::motion (GdkEvent *, bool)
3994 {
3995         /* Total change in x and y since the start of the drag */
3996         frameoffset_t const dx = total_dx ();
3997         int8_t const dy = total_dy ();
3998
3999         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4000         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4001         double const tdy = -dy * _note_height - _cumulative_dy;
4002
4003         if (tdx || tdy) {
4004                 _cumulative_dx += tdx;
4005                 _cumulative_dy += tdy;
4006
4007                 int8_t note_delta = total_dy();
4008
4009                 _region->move_selection (tdx, tdy, note_delta);
4010
4011                 /* the new note value may be the same as the old one, but we
4012                  * don't know what that means because the selection may have
4013                  * involved more than one note and we might be doing something
4014                  * odd with them. so show the note value anyway, always.
4015                  */
4016
4017                 char buf[12];
4018                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4019                 
4020                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4021                           (int) floor (new_note));
4022
4023                 show_verbose_cursor_text (buf);
4024         }
4025 }
4026
4027 void
4028 NoteDrag::finished (GdkEvent* ev, bool moved)
4029 {
4030         if (!moved) {
4031                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4032
4033                         if (_was_selected) {
4034                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4035                                 if (add) {
4036                                         _region->note_deselected (_primary);
4037                                 }
4038                         } else {
4039                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4040                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4041
4042                                 if (!extend && !add && _region->selection_size() > 1) {
4043                                         _region->unique_select (_primary);
4044                                 } else if (extend) {
4045                                         _region->note_selected (_primary, true, true);
4046                                 } else {
4047                                         /* it was added during button press */
4048                                 }
4049                         }
4050                 }
4051         } else {
4052                 _region->note_dropped (_primary, total_dx(), total_dy());
4053         }
4054 }
4055
4056 void
4057 NoteDrag::aborted (bool)
4058 {
4059         /* XXX: TODO */
4060 }
4061
4062 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4063         : Drag (editor, item)
4064         , _ranges (r)
4065         , _nothing_to_drag (false)
4066 {
4067         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4068
4069         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4070         assert (_atav);
4071
4072         /* get all lines in the automation view */
4073         list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4074
4075         /* find those that overlap the ranges being dragged */
4076         list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4077         while (i != lines.end ()) {
4078                 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4079                 ++j;
4080
4081                 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4082
4083                 /* check this range against all the AudioRanges that we are using */
4084                 list<AudioRange>::const_iterator k = _ranges.begin ();
4085                 while (k != _ranges.end()) {
4086                         if (k->coverage (r.first, r.second) != OverlapNone) {
4087                                 break;
4088                         }
4089                         ++k;
4090                 }
4091
4092                 /* add it to our list if it overlaps at all */
4093                 if (k != _ranges.end()) {
4094                         Line n;
4095                         n.line = *i;
4096                         n.state = 0;
4097                         n.range = r;
4098                         _lines.push_back (n);
4099                 }
4100
4101                 i = j;
4102         }
4103
4104         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4105 }
4106
4107 void
4108 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4109 {
4110         Drag::start_grab (event, cursor);
4111
4112         /* Get line states before we start changing things */
4113         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4114                 i->state = &i->line->get_state ();
4115         }
4116
4117         if (_ranges.empty()) {
4118
4119                 /* No selected time ranges: drag all points */
4120                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4121                         uint32_t const N = i->line->npoints ();
4122                         for (uint32_t j = 0; j < N; ++j) {
4123                                 i->points.push_back (i->line->nth (j));
4124                         }
4125                 }
4126
4127         } else {
4128
4129                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4130
4131                         framecnt_t const half = (i->start + i->end) / 2;
4132
4133                         /* find the line that this audio range starts in */
4134                         list<Line>::iterator j = _lines.begin();
4135                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4136                                 ++j;
4137                         }
4138
4139                         if (j != _lines.end()) {
4140                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4141
4142                                 /* j is the line that this audio range starts in; fade into it;
4143                                    64 samples length plucked out of thin air.
4144                                 */
4145
4146                                 framepos_t a = i->start + 64;
4147                                 if (a > half) {
4148                                         a = half;
4149                                 }
4150
4151                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4152                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4153
4154                                 the_list->add (p, the_list->eval (p));
4155                                 j->line->add_always_in_view (p);
4156                                 the_list->add (q, the_list->eval (q));
4157                                 j->line->add_always_in_view (q);
4158                         }
4159
4160                         /* same thing for the end */
4161
4162                         j = _lines.begin();
4163                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4164                                 ++j;
4165                         }
4166
4167                         if (j != _lines.end()) {
4168                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4169
4170                                 /* j is the line that this audio range starts in; fade out of it;
4171                                    64 samples length plucked out of thin air.
4172                                 */
4173
4174                                 framepos_t b = i->end - 64;
4175                                 if (b < half) {
4176                                         b = half;
4177                                 }
4178
4179                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4180                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4181
4182                                 the_list->add (p, the_list->eval (p));
4183                                 j->line->add_always_in_view (p);
4184                                 the_list->add (q, the_list->eval (q));
4185                                 j->line->add_always_in_view (q);
4186                         }
4187                 }
4188
4189                 _nothing_to_drag = true;
4190
4191                 /* Find all the points that should be dragged and put them in the relevant
4192                    points lists in the Line structs.
4193                 */
4194
4195                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4196
4197                         uint32_t const N = i->line->npoints ();
4198                         for (uint32_t j = 0; j < N; ++j) {
4199
4200                                 /* here's a control point on this line */
4201                                 ControlPoint* p = i->line->nth (j);
4202                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4203
4204                                 /* see if it's inside a range */
4205                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4206                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4207                                         ++k;
4208                                 }
4209
4210                                 if (k != _ranges.end()) {
4211                                         /* dragging this point */
4212                                         _nothing_to_drag = false;
4213                                         i->points.push_back (p);
4214                                 }
4215                         }
4216                 }
4217         }
4218
4219         if (_nothing_to_drag) {
4220                 return;
4221         }
4222
4223         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4224                 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4225         }
4226 }
4227
4228 void
4229 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4230 {
4231         if (_nothing_to_drag) {
4232                 return;
4233         }
4234
4235         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4236                 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4237
4238                 /* we are ignoring x position for this drag, so we can just pass in anything */
4239                 i->line->drag_motion (0, f, true, false);
4240         }
4241 }
4242
4243 void
4244 AutomationRangeDrag::finished (GdkEvent* event, bool)
4245 {
4246         if (_nothing_to_drag) {
4247                 return;
4248         }
4249
4250         motion (event, false);
4251         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4252                 i->line->end_drag ();
4253                 i->line->clear_always_in_view ();
4254         }
4255
4256         _editor->session()->commit_reversible_command ();
4257 }
4258
4259 void
4260 AutomationRangeDrag::aborted (bool)
4261 {
4262         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4263                 i->line->clear_always_in_view ();
4264                 i->line->reset ();
4265         }
4266 }
4267
4268 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4269         : view (v)
4270 {
4271         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4272         layer = v->region()->layer ();
4273         initial_y = v->get_canvas_group()->property_y ();
4274         initial_playlist = v->region()->playlist ();
4275         initial_position = v->region()->position ();
4276         initial_end = v->region()->position () + v->region()->length ();
4277 }
4278
4279 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4280         : Drag (e, i)
4281         , _region_view (r)
4282         , _patch_change (i)
4283         , _cumulative_dx (0)
4284 {
4285         DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4286 }
4287
4288 void
4289 PatchChangeDrag::motion (GdkEvent* ev, bool)
4290 {
4291         framepos_t f = adjusted_current_frame (ev);
4292         boost::shared_ptr<Region> r = _region_view->region ();
4293         f = max (f, r->position ());
4294         f = min (f, r->last_frame ());
4295
4296         framecnt_t const dxf = f - grab_frame();
4297         double const dxu = _editor->frame_to_unit (dxf);
4298         _patch_change->move (dxu - _cumulative_dx, 0);
4299         _cumulative_dx = dxu;
4300 }
4301
4302 void
4303 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4304 {
4305         if (!movement_occurred) {
4306                 return;
4307         }
4308
4309         boost::shared_ptr<Region> r (_region_view->region ());
4310
4311         framepos_t f = adjusted_current_frame (ev);
4312         f = max (f, r->position ());
4313         f = min (f, r->last_frame ());
4314
4315         _region_view->move_patch_change (
4316                 *_patch_change,
4317                 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4318                 );
4319 }
4320
4321 void
4322 PatchChangeDrag::aborted (bool)
4323 {
4324         _patch_change->move (-_cumulative_dx, 0);
4325 }
4326
4327 void
4328 PatchChangeDrag::setup_pointer_frame_offset ()
4329 {
4330         boost::shared_ptr<Region> region = _region_view->region ();
4331         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4332 }
4333
4334 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4335         : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4336         , _region_view (rv)
4337 {
4338
4339 }
4340
4341 void
4342 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4343 {
4344         framepos_t const p = _region_view->region()->position ();
4345         double const y = _region_view->midi_view()->y_position ();
4346
4347         x1 = max ((framepos_t) 0, x1 - p);
4348         x2 = max ((framepos_t) 0, x2 - p);
4349         y1 = max (0.0, y1 - y);
4350         y2 = max (0.0, y2 - y);
4351         
4352         _region_view->update_drag_selection (
4353                 _editor->frame_to_pixel (x1),
4354                 _editor->frame_to_pixel (x2),
4355                 y1,
4356                 y2,
4357                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4358                 );
4359 }
4360
4361 void
4362 MidiRubberbandSelectDrag::deselect_things ()
4363 {
4364         /* XXX */
4365 }
4366
4367 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4368         : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4369         , _region_view (rv)
4370 {
4371         _vertical_only = true;
4372 }
4373
4374 void
4375 MidiVerticalSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4376 {
4377         double const y = _region_view->midi_view()->y_position ();
4378
4379         y1 = max (0.0, y1 - y);
4380         y2 = max (0.0, y2 - y);
4381         
4382         _region_view->update_vertical_drag_selection (
4383                 y1,
4384                 y2,
4385                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4386                 );
4387 }
4388
4389 void
4390 MidiVerticalSelectDrag::deselect_things ()
4391 {
4392         /* XXX */
4393 }
4394
4395 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4396         : RubberbandSelectDrag (e, i)
4397 {
4398
4399 }
4400
4401 void
4402 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4403 {
4404         if (drag_in_progress) {
4405                 /* We just want to select things at the end of the drag, not during it */
4406                 return;
4407         }
4408         
4409         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4410         
4411         _editor->begin_reversible_command (_("rubberband selection"));
4412         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4413         _editor->commit_reversible_command ();
4414 }
4415
4416 void
4417 EditorRubberbandSelectDrag::deselect_things ()
4418 {
4419         if (!getenv("ARDOUR_SAE")) {
4420                 _editor->selection->clear_tracks();
4421         }
4422         _editor->selection->clear_regions();
4423         _editor->selection->clear_points ();
4424         _editor->selection->clear_lines ();
4425 }
4426
4427 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4428         : Drag (e, i)
4429         , _region_view (rv)
4430         , _drag_rect (0)
4431 {
4432         
4433 }
4434
4435 NoteCreateDrag::~NoteCreateDrag ()
4436 {
4437         delete _drag_rect;
4438 }
4439
4440 framecnt_t
4441 NoteCreateDrag::grid_frames (framepos_t t) const
4442 {
4443         bool success;
4444         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4445         if (!success) {
4446                 grid_beats = 1;
4447         }
4448
4449         return _region_view->region_beats_to_region_frames (grid_beats);
4450 }
4451
4452 void
4453 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4454 {
4455         Drag::start_grab (event, cursor);
4456                                                  
4457         _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4458
4459         framepos_t pf = _drags->current_pointer_frame ();
4460         framecnt_t const g = grid_frames (pf);
4461
4462         /* Hack so that we always snap to the note that we are over, instead of snapping
4463            to the next one if we're more than halfway through the one we're over.
4464         */
4465         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4466                 pf -= g / 2;
4467         }
4468
4469         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4470
4471         MidiStreamView* sv = _region_view->midi_stream_view ();
4472         double const x = _editor->frame_to_pixel (_note[0]);
4473         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4474
4475         _drag_rect->property_x1() = x;
4476         _drag_rect->property_y1() = y;
4477         _drag_rect->property_x2() = x;
4478         _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4479
4480         _drag_rect->property_outline_what() = 0xff;
4481         _drag_rect->property_outline_color_rgba() = 0xffffff99;
4482         _drag_rect->property_fill_color_rgba()    = 0xffffff66;
4483 }
4484
4485 void
4486 NoteCreateDrag::motion (GdkEvent* event, bool)
4487 {
4488         _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4489         double const x = _editor->frame_to_pixel (_note[1]);
4490         if (_note[1] > _note[0]) {
4491                 _drag_rect->property_x2() = x;
4492         } else {
4493                 _drag_rect->property_x1() = x;
4494         }
4495 }
4496
4497 void
4498 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4499 {
4500         if (!had_movement) {
4501                 return;
4502         }
4503         
4504         framepos_t const start = min (_note[0], _note[1]);
4505         framecnt_t length = abs (_note[0] - _note[1]);
4506
4507         framecnt_t const g = grid_frames (start);
4508         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4509         
4510         if (_editor->snap_mode() == SnapNormal && length < g) {
4511                 length = g - one_tick;
4512         }
4513
4514         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4515
4516         _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4517 }
4518
4519 double
4520 NoteCreateDrag::y_to_region (double y) const
4521 {
4522         double x = 0;
4523         _region_view->get_canvas_group()->w2i (x, y);
4524         return y;
4525 }
4526
4527 void
4528 NoteCreateDrag::aborted (bool)
4529 {
4530         
4531 }