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