Remove apparently erroneous commit_reversible_command.
[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), boost::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->drag_start (); 
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->drag_end ();
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->drag_end ();
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
1500 void
1501 RegionCreateDrag::aborted (bool)
1502 {
1503         if (_region) {
1504                 _view->playlist()->thaw ();
1505         }
1506
1507         /* XXX */
1508 }
1509
1510 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1511         : Drag (e, i)
1512         , region (0)
1513 {
1514         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1515 }
1516
1517 void
1518 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1519 {
1520         Gdk::Cursor* cursor;
1521         ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1522         float x_fraction = cnote->mouse_x_fraction ();
1523
1524         if (x_fraction > 0.0 && x_fraction < 0.25) {
1525                 cursor = _editor->cursors()->left_side_trim;
1526         } else  {
1527                 cursor = _editor->cursors()->right_side_trim;
1528         }
1529
1530         Drag::start_grab (event, cursor);
1531
1532         region = &cnote->region_view();
1533
1534         double const region_start = region->get_position_pixels();
1535         double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1536
1537         if (grab_x() <= middle_point) {
1538                 cursor = _editor->cursors()->left_side_trim;
1539                 at_front = true;
1540         } else {
1541                 cursor = _editor->cursors()->right_side_trim;
1542                 at_front = false;
1543         }
1544
1545         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1546
1547         if (event->motion.state & Keyboard::PrimaryModifier) {
1548                 relative = false;
1549         } else {
1550                 relative = true;
1551         }
1552
1553         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1554
1555         if (ms.size() > 1) {
1556                 /* has to be relative, may make no sense otherwise */
1557                 relative = true;
1558         }
1559
1560         /* select this note; if it is already selected, preserve the existing selection,
1561            otherwise make this note the only one selected.
1562         */
1563         region->note_selected (cnote, cnote->selected ());
1564
1565         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1566                 MidiRegionSelection::iterator next;
1567                 next = r;
1568                 ++next;
1569                 (*r)->begin_resizing (at_front);
1570                 r = next;
1571         }
1572 }
1573
1574 void
1575 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1576 {
1577         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1578         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1579                 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1580         }
1581 }
1582
1583 void
1584 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1585 {
1586         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1587         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1588                 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1589         }
1590 }
1591
1592 void
1593 NoteResizeDrag::aborted (bool)
1594 {
1595         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1596         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1597                 (*r)->abort_resizing ();
1598         }
1599 }
1600
1601 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1602         : RegionDrag (e, i, p, v)
1603 {
1604         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1605 }
1606
1607 void
1608 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1609 {
1610         double speed = 1.0;
1611         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1612         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1613
1614         if (tv && tv->is_track()) {
1615                 speed = tv->track()->speed();
1616         }
1617
1618         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1619         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1620         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1621
1622         framepos_t const pf = adjusted_current_frame (event);
1623
1624         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1625                 /* Move the contents of the region around without changing the region bounds */
1626                 _operation = ContentsTrim;
1627                 Drag::start_grab (event, _editor->cursors()->trimmer);
1628         } else {
1629                 /* These will get overridden for a point trim.*/
1630                 if (pf < (region_start + region_length/2)) {
1631                         /* closer to front */
1632                         _operation = StartTrim;
1633                         Drag::start_grab (event, _editor->cursors()->left_side_trim);
1634                 } else {
1635                         /* closer to end */
1636                         _operation = EndTrim;
1637                         Drag::start_grab (event, _editor->cursors()->right_side_trim);
1638                 }
1639         }
1640
1641         switch (_operation) {
1642         case StartTrim:
1643                 show_verbose_cursor_time (region_start);
1644                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1645                         i->view->trim_front_starting ();
1646                 }
1647                 break;
1648         case EndTrim:
1649                 show_verbose_cursor_time (region_end);
1650                 break;
1651         case ContentsTrim:
1652                 show_verbose_cursor_time (pf);
1653                 break;
1654         }
1655
1656         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1657                 i->view->region()->suspend_property_changes ();
1658         }
1659 }
1660
1661 void
1662 TrimDrag::motion (GdkEvent* event, bool first_move)
1663 {
1664         RegionView* rv = _primary;
1665
1666         double speed = 1.0;
1667         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1668         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1669         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1670
1671         if (tv && tv->is_track()) {
1672                 speed = tv->track()->speed();
1673         }
1674
1675         framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1676
1677         if (first_move) {
1678
1679                 string trim_type;
1680
1681                 switch (_operation) {
1682                 case StartTrim:
1683                         trim_type = "Region start trim";
1684                         break;
1685                 case EndTrim:
1686                         trim_type = "Region end trim";
1687                         break;
1688                 case ContentsTrim:
1689                         trim_type = "Region content trim";
1690                         break;
1691                 }
1692
1693                 _editor->begin_reversible_command (trim_type);
1694
1695                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1696                         RegionView* rv = i->view;
1697                         rv->fake_set_opaque (false);
1698                         rv->enable_display (false);
1699                         rv->region()->playlist()->clear_owned_changes ();
1700
1701                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1702
1703                         if (arv) {
1704                                 arv->temporarily_hide_envelope ();
1705                                 arv->drag_start ();
1706                         }
1707
1708                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1709                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1710
1711                         if (insert_result.second) {
1712                                 pl->freeze();
1713                         }
1714                 }
1715         }
1716
1717         bool non_overlap_trim = false;
1718
1719         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1720                 non_overlap_trim = true;
1721         }
1722
1723         switch (_operation) {
1724         case StartTrim:
1725                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1726                         i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1727                 }
1728                 break;
1729
1730         case EndTrim:
1731                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1732                         i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1733                 }
1734                 break;
1735
1736         case ContentsTrim:
1737                 {
1738                         bool swap_direction = false;
1739
1740                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1741                                 swap_direction = true;
1742                         }
1743
1744                         framecnt_t frame_delta = 0;
1745
1746                         bool left_direction = false;
1747                         if (last_pointer_frame() > adjusted_current_frame(event)) {
1748                                 left_direction = true;
1749                         }
1750
1751                         if (left_direction) {
1752                                 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1753                         } else {
1754                                 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1755                         }
1756
1757                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1758                                 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1759                         }
1760                 }
1761                 break;
1762         }
1763
1764         switch (_operation) {
1765         case StartTrim:
1766                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1767                 break;
1768         case EndTrim:
1769                 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1770                 break;
1771         case ContentsTrim:
1772                 show_verbose_cursor_time (adjusted_current_frame (event));
1773                 break;
1774         }
1775 }
1776
1777
1778 void
1779 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1780 {
1781         if (movement_occurred) {
1782                 motion (event, false);
1783
1784                 /* This must happen before the region's StatefulDiffCommand is created, as it may
1785                    `correct' (ahem) the region's _start from being negative to being zero.  It
1786                    needs to be zero in the undo record.
1787                 */
1788                 if (_operation == StartTrim) {
1789                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1790                                 i->view->trim_front_ending ();
1791                         }
1792                 }
1793
1794                 if (!_editor->selection->selected (_primary)) {
1795                         _primary->thaw_after_trim ();
1796                 } else {
1797
1798                         set<boost::shared_ptr<Playlist> > diffed_playlists;
1799
1800                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1801                                 i->view->thaw_after_trim ();
1802                                 i->view->enable_display (true);
1803                                 i->view->fake_set_opaque (true);
1804
1805                                 /* Trimming one region may affect others on the playlist, so we need
1806                                    to get undo Commands from the whole playlist rather than just the
1807                                    region.  Use diffed_playlists to make sure we don't diff a given
1808                                    playlist more than once.
1809                                 */
1810                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1811                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1812                                         vector<Command*> cmds;
1813                                         p->rdiff (cmds);
1814                                         _editor->session()->add_commands (cmds);
1815                                         diffed_playlists.insert (p);
1816                                 }
1817                         }
1818                 }
1819                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1820                         (*p)->thaw ();
1821                 }
1822
1823                 _editor->motion_frozen_playlists.clear ();
1824                 _editor->commit_reversible_command();
1825
1826         } else {
1827                 /* no mouse movement */
1828                 _editor->point_trim (event, adjusted_current_frame (event));
1829         }
1830
1831         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1832                 if (_operation == StartTrim) {
1833                         i->view->trim_front_ending ();
1834                 }
1835
1836                 i->view->region()->resume_property_changes ();
1837         }
1838 }
1839
1840 void
1841 TrimDrag::aborted (bool movement_occurred)
1842 {
1843         /* Our motion method is changing model state, so use the Undo system
1844            to cancel.  Perhaps not ideal, as this will leave an Undo point
1845            behind which may be slightly odd from the user's point of view.
1846         */
1847
1848         finished (0, true);
1849
1850         if (movement_occurred) {
1851                 _editor->undo ();
1852         }
1853
1854         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1855                 i->view->region()->resume_property_changes ();
1856         }
1857 }
1858
1859 void
1860 TrimDrag::setup_pointer_frame_offset ()
1861 {
1862         list<DraggingView>::iterator i = _views.begin ();
1863         while (i != _views.end() && i->view != _primary) {
1864                 ++i;
1865         }
1866
1867         if (i == _views.end()) {
1868                 return;
1869         }
1870
1871         switch (_operation) {
1872         case StartTrim:
1873                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1874                 break;
1875         case EndTrim:
1876                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1877                 break;
1878         case ContentsTrim:
1879                 break;
1880         }
1881 }
1882
1883 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1884         : Drag (e, i),
1885           _copy (c)
1886 {
1887         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1888         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1889         assert (_marker);
1890 }
1891
1892 void
1893 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1894 {
1895         Drag::start_grab (event, cursor);
1896         show_verbose_cursor_time (adjusted_current_frame(event));
1897 }
1898
1899 void
1900 MeterMarkerDrag::setup_pointer_frame_offset ()
1901 {
1902         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1903 }
1904
1905 void
1906 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1907 {
1908         if (first_move) {
1909
1910                 // create a dummy marker for visual representation of moving the
1911                 // section, because whether its a copy or not, we're going to 
1912                 // leave or lose the original marker (leave if its a copy; lose if its
1913                 // not, because we'll remove it from the map).
1914                 
1915                 MeterSection section (_marker->meter());
1916
1917                 if (!section.movable()) {
1918                         return;
1919                 }
1920                 
1921                 char name[64];
1922                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1923                 
1924                 _marker = new MeterMarker (
1925                         *_editor,
1926                         *_editor->meter_group,
1927                         ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1928                         name,
1929                         *new MeterSection (_marker->meter())
1930                 );
1931                 
1932                 /* use the new marker for the grab */
1933                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1934
1935                 if (!_copy) {
1936                         TempoMap& map (_editor->session()->tempo_map());
1937                         /* get current state */
1938                         before_state = &map.get_state();
1939                         /* remove the section while we drag it */
1940                         map.remove_meter (section, true);
1941                 }
1942         }
1943
1944         framepos_t const pf = adjusted_current_frame (event);
1945         _marker->set_position (pf);
1946         show_verbose_cursor_time (pf);
1947 }
1948
1949 void
1950 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1951 {
1952         if (!movement_occurred) {
1953                 return;
1954         }
1955
1956         motion (event, false);
1957
1958         Timecode::BBT_Time when;
1959
1960         TempoMap& map (_editor->session()->tempo_map());
1961         map.bbt_time (last_pointer_frame(), when);
1962         
1963         if (_copy == true) {
1964                 _editor->begin_reversible_command (_("copy meter mark"));
1965                 XMLNode &before = map.get_state();
1966                 map.add_meter (_marker->meter(), when);
1967                 XMLNode &after = map.get_state();
1968                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1969                 _editor->commit_reversible_command ();
1970
1971         } else {
1972                 _editor->begin_reversible_command (_("move meter mark"));
1973
1974                 /* we removed it before, so add it back now */
1975                 
1976                 map.add_meter (_marker->meter(), when);
1977                 XMLNode &after = map.get_state();
1978                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
1979                 _editor->commit_reversible_command ();
1980         }
1981
1982         // delete the dummy marker we used for visual representation while moving.
1983         // a new visual marker will show up automatically.
1984         delete _marker;
1985 }
1986
1987 void
1988 MeterMarkerDrag::aborted (bool moved)
1989 {
1990         _marker->set_position (_marker->meter().frame ());
1991
1992         if (moved) {
1993                 TempoMap& map (_editor->session()->tempo_map());
1994                 /* we removed it before, so add it back now */
1995                 map.add_meter (_marker->meter(), _marker->meter().frame());
1996                 // delete the dummy marker we used for visual representation while moving.
1997                 // a new visual marker will show up automatically.
1998                 delete _marker;
1999         }
2000 }
2001
2002 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2003         : Drag (e, i),
2004           _copy (c)
2005 {
2006         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2007
2008         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2009         assert (_marker);
2010 }
2011
2012 void
2013 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2014 {
2015         Drag::start_grab (event, cursor);
2016         show_verbose_cursor_time (adjusted_current_frame (event));
2017 }
2018
2019 void
2020 TempoMarkerDrag::setup_pointer_frame_offset ()
2021 {
2022         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2023 }
2024
2025 void
2026 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2027 {
2028         if (first_move) {
2029
2030                 // create a dummy marker for visual representation of moving the
2031                 // section, because whether its a copy or not, we're going to 
2032                 // leave or lose the original marker (leave if its a copy; lose if its
2033                 // not, because we'll remove it from the map).
2034                 
2035                 // create a dummy marker for visual representation of moving the copy.
2036                 // The actual copying is not done before we reach the finish callback.
2037
2038                 char name[64];
2039                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2040
2041                 TempoSection section (_marker->tempo());
2042
2043                 _marker = new TempoMarker (
2044                         *_editor,
2045                         *_editor->tempo_group,
2046                         ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2047                         name,
2048                         *new TempoSection (_marker->tempo())
2049                         );
2050
2051                 /* use the new marker for the grab */
2052                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2053
2054                 if (!_copy) {
2055                         TempoMap& map (_editor->session()->tempo_map());
2056                         /* get current state */
2057                         before_state = &map.get_state();
2058                         /* remove the section while we drag it */
2059                         map.remove_tempo (section, true);
2060                 }
2061         }
2062
2063         framepos_t const pf = adjusted_current_frame (event);
2064         _marker->set_position (pf);
2065         show_verbose_cursor_time (pf);
2066 }
2067
2068 void
2069 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2070 {
2071         if (!movement_occurred) {
2072                 return;
2073         }
2074
2075         motion (event, false);
2076
2077         TempoMap& map (_editor->session()->tempo_map());
2078         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2079         Timecode::BBT_Time when;
2080
2081         map.bbt_time (beat_time, when);
2082
2083         if (_copy == true) {
2084                 _editor->begin_reversible_command (_("copy tempo mark"));
2085                 XMLNode &before = map.get_state();
2086                 map.add_tempo (_marker->tempo(), when);
2087                 XMLNode &after = map.get_state();
2088                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2089                 _editor->commit_reversible_command ();
2090
2091         } else {
2092                 _editor->begin_reversible_command (_("move tempo mark"));
2093                 /* we removed it before, so add it back now */
2094                 map.add_tempo (_marker->tempo(), when);
2095                 XMLNode &after = map.get_state();
2096                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2097                 _editor->commit_reversible_command ();
2098         }
2099
2100         // delete the dummy marker we used for visual representation while moving.
2101         // a new visual marker will show up automatically.
2102         delete _marker;
2103 }
2104
2105 void
2106 TempoMarkerDrag::aborted (bool moved)
2107 {
2108         _marker->set_position (_marker->tempo().frame());
2109         if (moved) {
2110                 TempoMap& map (_editor->session()->tempo_map());
2111                 /* we removed it before, so add it back now */
2112                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2113                 // delete the dummy marker we used for visual representation while moving.
2114                 // a new visual marker will show up automatically.
2115                 delete _marker;
2116         }
2117 }
2118
2119 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2120         : Drag (e, i),
2121           _stop (s)
2122 {
2123         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2124 }
2125
2126 /** Do all the things we do when dragging the playhead to make it look as though
2127  *  we have located, without actually doing the locate (because that would cause
2128  *  the diskstream buffers to be refilled, which is too slow).
2129  */
2130 void
2131 CursorDrag::fake_locate (framepos_t t)
2132 {
2133         _editor->playhead_cursor->set_position (t);
2134
2135         Session* s = _editor->session ();
2136         if (s->timecode_transmission_suspended ()) {
2137                 framepos_t const f = _editor->playhead_cursor->current_frame;
2138                 s->send_mmc_locate (f);
2139                 s->send_full_time_code (f);
2140         }
2141
2142         show_verbose_cursor_time (t);
2143         _editor->UpdateAllTransportClocks (t);
2144 }
2145
2146 void
2147 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2148 {
2149         Drag::start_grab (event, c);
2150
2151         _grab_zoom = _editor->frames_per_unit;
2152
2153         framepos_t where = _editor->event_frame (event, 0, 0);
2154         _editor->snap_to_with_modifier (where, event);
2155
2156         _editor->_dragging_playhead = true;
2157
2158         Session* s = _editor->session ();
2159
2160         if (s) {
2161                 if (_was_rolling && _stop) {
2162                         s->request_stop ();
2163                 }
2164
2165                 if (s->is_auditioning()) {
2166                         s->cancel_audition ();
2167                 }
2168
2169                 s->request_suspend_timecode_transmission ();
2170                 while (!s->timecode_transmission_suspended ()) {
2171                         /* twiddle our thumbs */
2172                 }
2173         }
2174
2175         fake_locate (where);
2176 }
2177
2178 void
2179 CursorDrag::motion (GdkEvent* event, bool)
2180 {
2181         framepos_t const adjusted_frame = adjusted_current_frame (event);
2182         if (adjusted_frame != last_pointer_frame()) {
2183                 fake_locate (adjusted_frame);
2184 #ifdef GTKOSX
2185                 _editor->update_canvas_now ();
2186 #endif
2187         }
2188 }
2189
2190 void
2191 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2192 {
2193         _editor->_dragging_playhead = false;
2194
2195         if (!movement_occurred && _stop) {
2196                 return;
2197         }
2198
2199         motion (event, false);
2200
2201         Session* s = _editor->session ();
2202         if (s) {
2203                 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2204                 _editor->_pending_locate_request = true;
2205                 s->request_resume_timecode_transmission ();
2206         }
2207 }
2208
2209 void
2210 CursorDrag::aborted (bool)
2211 {
2212         if (_editor->_dragging_playhead) {
2213                 _editor->session()->request_resume_timecode_transmission ();
2214                 _editor->_dragging_playhead = false;
2215         }
2216
2217         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2218 }
2219
2220 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2221         : RegionDrag (e, i, p, v)
2222 {
2223         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2224 }
2225
2226 void
2227 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2228 {
2229         Drag::start_grab (event, cursor);
2230
2231         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2232         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2233
2234         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2235
2236         arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2237 }
2238
2239 void
2240 FadeInDrag::setup_pointer_frame_offset ()
2241 {
2242         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2243         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2244         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2245 }
2246
2247 void
2248 FadeInDrag::motion (GdkEvent* event, bool)
2249 {
2250         framecnt_t fade_length;
2251
2252         framepos_t const pos = adjusted_current_frame (event);
2253
2254         boost::shared_ptr<Region> region = _primary->region ();
2255
2256         if (pos < (region->position() + 64)) {
2257                 fade_length = 64; // this should be a minimum defined somewhere
2258         } else if (pos > region->last_frame()) {
2259                 fade_length = region->length();
2260         } else {
2261                 fade_length = pos - region->position();
2262         }
2263
2264         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2265
2266                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2267
2268                 if (!tmp) {
2269                         continue;
2270                 }
2271
2272                 tmp->reset_fade_in_shape_width (fade_length);
2273                 tmp->show_fade_line((framecnt_t) fade_length);
2274         }
2275
2276         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2277 }
2278
2279 void
2280 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2281 {
2282         if (!movement_occurred) {
2283                 return;
2284         }
2285
2286         framecnt_t fade_length;
2287
2288         framepos_t const pos = adjusted_current_frame (event);
2289
2290         boost::shared_ptr<Region> region = _primary->region ();
2291
2292         if (pos < (region->position() + 64)) {
2293                 fade_length = 64; // this should be a minimum defined somewhere
2294         } else if (pos > region->last_frame()) {
2295                 fade_length = region->length();
2296         } else {
2297                 fade_length = pos - region->position();
2298         }
2299
2300         _editor->begin_reversible_command (_("change fade in length"));
2301
2302         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2303
2304                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2305
2306                 if (!tmp) {
2307                         continue;
2308                 }
2309
2310                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2311                 XMLNode &before = alist->get_state();
2312
2313                 tmp->audio_region()->set_fade_in_length (fade_length);
2314                 tmp->audio_region()->set_fade_in_active (true);
2315                 tmp->hide_fade_line();
2316
2317                 XMLNode &after = alist->get_state();
2318                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2319         }
2320
2321         _editor->commit_reversible_command ();
2322 }
2323
2324 void
2325 FadeInDrag::aborted (bool)
2326 {
2327         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2328                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2329
2330                 if (!tmp) {
2331                         continue;
2332                 }
2333
2334                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2335                 tmp->hide_fade_line();
2336         }
2337 }
2338
2339 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2340         : RegionDrag (e, i, p, v)
2341 {
2342         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2343 }
2344
2345 void
2346 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2347 {
2348         Drag::start_grab (event, cursor);
2349
2350         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2351         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2352
2353         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2354
2355         arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2356 }
2357
2358 void
2359 FadeOutDrag::setup_pointer_frame_offset ()
2360 {
2361         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2362         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2363         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2364 }
2365
2366 void
2367 FadeOutDrag::motion (GdkEvent* event, bool)
2368 {
2369         framecnt_t fade_length;
2370
2371         framepos_t const pos = adjusted_current_frame (event);
2372
2373         boost::shared_ptr<Region> region = _primary->region ();
2374
2375         if (pos > (region->last_frame() - 64)) {
2376                 fade_length = 64; // this should really be a minimum fade defined somewhere
2377         }
2378         else if (pos < region->position()) {
2379                 fade_length = region->length();
2380         }
2381         else {
2382                 fade_length = region->last_frame() - pos;
2383         }
2384
2385         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2386
2387                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2388
2389                 if (!tmp) {
2390                         continue;
2391                 }
2392
2393                 tmp->reset_fade_out_shape_width (fade_length);
2394                 tmp->show_fade_line(region->length() - fade_length);
2395         }
2396
2397         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2398 }
2399
2400 void
2401 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2402 {
2403         if (!movement_occurred) {
2404                 return;
2405         }
2406
2407         framecnt_t fade_length;
2408
2409         framepos_t const pos = adjusted_current_frame (event);
2410
2411         boost::shared_ptr<Region> region = _primary->region ();
2412
2413         if (pos > (region->last_frame() - 64)) {
2414                 fade_length = 64; // this should really be a minimum fade defined somewhere
2415         }
2416         else if (pos < region->position()) {
2417                 fade_length = region->length();
2418         }
2419         else {
2420                 fade_length = region->last_frame() - pos;
2421         }
2422
2423         _editor->begin_reversible_command (_("change fade out length"));
2424
2425         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2426
2427                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2428
2429                 if (!tmp) {
2430                         continue;
2431                 }
2432
2433                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2434                 XMLNode &before = alist->get_state();
2435
2436                 tmp->audio_region()->set_fade_out_length (fade_length);
2437                 tmp->audio_region()->set_fade_out_active (true);
2438                 tmp->hide_fade_line();
2439
2440                 XMLNode &after = alist->get_state();
2441                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2442         }
2443
2444         _editor->commit_reversible_command ();
2445 }
2446
2447 void
2448 FadeOutDrag::aborted (bool)
2449 {
2450         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2451                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2452
2453                 if (!tmp) {
2454                         continue;
2455                 }
2456
2457                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2458                 tmp->hide_fade_line();
2459         }
2460 }
2461
2462 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2463         : Drag (e, i)
2464 {
2465         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2466
2467         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2468         assert (_marker);
2469
2470         _points.push_back (Gnome::Art::Point (0, 0));
2471         _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2472 }
2473
2474 MarkerDrag::~MarkerDrag ()
2475 {
2476         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2477                 delete *i;
2478         }
2479 }
2480
2481 void
2482 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2483 {
2484         Drag::start_grab (event, cursor);
2485
2486         bool is_start;
2487
2488         Location *location = _editor->find_location_from_marker (_marker, is_start);
2489         _editor->_dragging_edit_point = true;
2490
2491         update_item (location);
2492
2493         // _drag_line->show();
2494         // _line->raise_to_top();
2495
2496         if (is_start) {
2497                 show_verbose_cursor_time (location->start());
2498         } else {
2499                 show_verbose_cursor_time (location->end());
2500         }
2501
2502         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2503
2504         switch (op) {
2505         case Selection::Toggle:
2506                 _editor->selection->toggle (_marker);
2507                 break;
2508         case Selection::Set:
2509                 if (!_editor->selection->selected (_marker)) {
2510                         _editor->selection->set (_marker);
2511                 }
2512                 break;
2513         case Selection::Extend:
2514         {
2515                 Locations::LocationList ll;
2516                 list<Marker*> to_add;
2517                 framepos_t s, e;
2518                 _editor->selection->markers.range (s, e);
2519                 s = min (_marker->position(), s);
2520                 e = max (_marker->position(), e);
2521                 s = min (s, e);
2522                 e = max (s, e);
2523                 if (e < max_framepos) {
2524                         ++e;
2525                 }
2526                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2527                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2528                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2529                         if (lm) {
2530                                 if (lm->start) {
2531                                         to_add.push_back (lm->start);
2532                                 }
2533                                 if (lm->end) {
2534                                         to_add.push_back (lm->end);
2535                                 }
2536                         }
2537                 }
2538                 if (!to_add.empty()) {
2539                         _editor->selection->add (to_add);
2540                 }
2541                 break;
2542         }
2543         case Selection::Add:
2544                 _editor->selection->add (_marker);
2545                 break;
2546         }
2547
2548         /* Set up copies for us to manipulate during the drag */
2549
2550         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2551                 Location* l = _editor->find_location_from_marker (*i, is_start);
2552                 _copied_locations.push_back (new Location (*l));
2553         }
2554 }
2555
2556 void
2557 MarkerDrag::setup_pointer_frame_offset ()
2558 {
2559         bool is_start;
2560         Location *location = _editor->find_location_from_marker (_marker, is_start);
2561         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2562 }
2563
2564 void
2565 MarkerDrag::motion (GdkEvent* event, bool)
2566 {
2567         framecnt_t f_delta = 0;
2568         bool is_start;
2569         bool move_both = false;
2570         Marker* marker;
2571         Location *real_location;
2572         Location *copy_location = 0;
2573
2574         framepos_t const newframe = adjusted_current_frame (event);
2575
2576         framepos_t next = newframe;
2577
2578         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2579                 move_both = true;
2580         }
2581
2582         MarkerSelection::iterator i;
2583         list<Location*>::iterator x;
2584
2585         /* find the marker we're dragging, and compute the delta */
2586
2587         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2588              x != _copied_locations.end() && i != _editor->selection->markers.end();
2589              ++i, ++x) {
2590
2591                 copy_location = *x;
2592                 marker = *i;
2593
2594                 if (marker == _marker) {
2595
2596                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2597                                 /* que pasa ?? */
2598                                 return;
2599                         }
2600
2601                         if (real_location->is_mark()) {
2602                                 f_delta = newframe - copy_location->start();
2603                         } else {
2604
2605
2606                                 switch (marker->type()) {
2607                                 case Marker::SessionStart:
2608                                 case Marker::RangeStart:
2609                                 case Marker::LoopStart:
2610                                 case Marker::PunchIn:
2611                                         f_delta = newframe - copy_location->start();
2612                                         break;
2613
2614                                 case Marker::SessionEnd:
2615                                 case Marker::RangeEnd:
2616                                 case Marker::LoopEnd:
2617                                 case Marker::PunchOut:
2618                                         f_delta = newframe - copy_location->end();
2619                                         break;
2620                                 default:
2621                                         /* what kind of marker is this ? */
2622                                         return;
2623                                 }
2624                         }
2625                         break;
2626                 }
2627         }
2628
2629         if (i == _editor->selection->markers.end()) {
2630                 /* hmm, impossible - we didn't find the dragged marker */
2631                 return;
2632         }
2633
2634         /* now move them all */
2635
2636         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2637              x != _copied_locations.end() && i != _editor->selection->markers.end();
2638              ++i, ++x) {
2639
2640                 copy_location = *x;
2641                 marker = *i;
2642
2643                 /* call this to find out if its the start or end */
2644
2645                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2646                         continue;
2647                 }
2648
2649                 if (real_location->locked()) {
2650                         continue;
2651                 }
2652
2653                 if (copy_location->is_mark()) {
2654
2655                         /* now move it */
2656
2657                         copy_location->set_start (copy_location->start() + f_delta);
2658
2659                 } else {
2660
2661                         framepos_t new_start = copy_location->start() + f_delta;
2662                         framepos_t new_end = copy_location->end() + f_delta;
2663
2664                         if (is_start) { // start-of-range marker
2665
2666                                 if (move_both) {
2667                                         copy_location->set_start (new_start);
2668                                         copy_location->set_end (new_end);
2669                                 } else  if (new_start < copy_location->end()) {
2670                                         copy_location->set_start (new_start);
2671                                 } else if (newframe > 0) {
2672                                         _editor->snap_to (next, 1, true);
2673                                         copy_location->set_end (next);
2674                                         copy_location->set_start (newframe);
2675                                 }
2676
2677                         } else { // end marker
2678
2679                                 if (move_both) {
2680                                         copy_location->set_end (new_end);
2681                                         copy_location->set_start (new_start);
2682                                 } else if (new_end > copy_location->start()) {
2683                                         copy_location->set_end (new_end);
2684                                 } else if (newframe > 0) {
2685                                         _editor->snap_to (next, -1, true);
2686                                         copy_location->set_start (next);
2687                                         copy_location->set_end (newframe);
2688                                 }
2689                         }
2690                 }
2691
2692                 update_item (copy_location);
2693
2694                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2695
2696                 if (lm) {
2697                         lm->set_position (copy_location->start(), copy_location->end());
2698                 }
2699         }
2700
2701         assert (!_copied_locations.empty());
2702
2703         show_verbose_cursor_time (newframe);
2704
2705 #ifdef GTKOSX
2706         _editor->update_canvas_now ();
2707 #endif
2708 }
2709
2710 void
2711 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2712 {
2713         if (!movement_occurred) {
2714
2715                 /* just a click, do nothing but finish
2716                    off the selection process
2717                 */
2718
2719                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2720
2721                 switch (op) {
2722                 case Selection::Set:
2723                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2724                                 _editor->selection->set (_marker);
2725                         }
2726                         break;
2727
2728                 case Selection::Toggle:
2729                 case Selection::Extend:
2730                 case Selection::Add:
2731                         break;
2732                 }
2733
2734                 return;
2735         }
2736
2737         _editor->_dragging_edit_point = false;
2738
2739         _editor->begin_reversible_command ( _("move marker") );
2740         XMLNode &before = _editor->session()->locations()->get_state();
2741
2742         MarkerSelection::iterator i;
2743         list<Location*>::iterator x;
2744         bool is_start;
2745
2746         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2747              x != _copied_locations.end() && i != _editor->selection->markers.end();
2748              ++i, ++x) {
2749
2750                 Location * location = _editor->find_location_from_marker (*i, is_start);
2751
2752                 if (location) {
2753
2754                         if (location->locked()) {
2755                                 return;
2756                         }
2757
2758                         if (location->is_mark()) {
2759                                 location->set_start ((*x)->start());
2760                         } else {
2761                                 location->set ((*x)->start(), (*x)->end());
2762                         }
2763                 }
2764         }
2765
2766         XMLNode &after = _editor->session()->locations()->get_state();
2767         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2768         _editor->commit_reversible_command ();
2769 }
2770
2771 void
2772 MarkerDrag::aborted (bool)
2773 {
2774         /* XXX: TODO */
2775 }
2776
2777 void
2778 MarkerDrag::update_item (Location*)
2779 {
2780         /* noop */
2781 }
2782
2783 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2784         : Drag (e, i),
2785           _cumulative_x_drag (0),
2786           _cumulative_y_drag (0)
2787 {
2788         if (_zero_gain_fraction < 0.0) {
2789                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2790         }
2791
2792         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2793
2794         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2795         assert (_point);
2796 }
2797
2798
2799 void
2800 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2801 {
2802         Drag::start_grab (event, _editor->cursors()->fader);
2803
2804         // start the grab at the center of the control point so
2805         // the point doesn't 'jump' to the mouse after the first drag
2806         _fixed_grab_x = _point->get_x();
2807         _fixed_grab_y = _point->get_y();
2808
2809         float const fraction = 1 - (_point->get_y() / _point->line().height());
2810
2811         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2812
2813         _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2814                                         event->button.x + 10, event->button.y + 10);
2815
2816         _editor->verbose_cursor()->show ();
2817
2818         if (!_point->can_slide ()) {
2819                 _x_constrained = true;
2820         }
2821 }
2822
2823 void
2824 ControlPointDrag::motion (GdkEvent* event, bool)
2825 {
2826         double dx = _drags->current_pointer_x() - last_pointer_x();
2827         double dy = _drags->current_pointer_y() - last_pointer_y();
2828
2829         if (event->button.state & Keyboard::SecondaryModifier) {
2830                 dx *= 0.1;
2831                 dy *= 0.1;
2832         }
2833
2834         /* coordinate in pixels relative to the start of the region (for region-based automation)
2835            or track (for track-based automation) */
2836         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2837         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2838
2839         // calculate zero crossing point. back off by .01 to stay on the
2840         // positive side of zero
2841         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2842
2843         // make sure we hit zero when passing through
2844         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2845                 cy = zero_gain_y;
2846         }
2847
2848         if (_x_constrained) {
2849                 cx = _fixed_grab_x;
2850         }
2851         if (_y_constrained) {
2852                 cy = _fixed_grab_y;
2853         }
2854
2855         _cumulative_x_drag = cx - _fixed_grab_x;
2856         _cumulative_y_drag = cy - _fixed_grab_y;
2857
2858         cx = max (0.0, cx);
2859         cy = max (0.0, cy);
2860         cy = min ((double) _point->line().height(), cy);
2861
2862         framepos_t cx_frames = _editor->unit_to_frame (cx);
2863
2864         if (!_x_constrained) {
2865                 _editor->snap_to_with_modifier (cx_frames, event);
2866         }
2867
2868         cx_frames = min (cx_frames, _point->line().maximum_time());
2869
2870         float const fraction = 1.0 - (cy / _point->line().height());
2871
2872         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2873
2874         _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, push);
2875
2876         _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2877 }
2878
2879 void
2880 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2881 {
2882         if (!movement_occurred) {
2883
2884                 /* just a click */
2885
2886                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2887                         _editor->reset_point_selection ();
2888                 }
2889
2890         } else {
2891                 motion (event, false);
2892         }
2893
2894         _point->line().end_drag ();
2895         _editor->session()->commit_reversible_command ();
2896 }
2897
2898 void
2899 ControlPointDrag::aborted (bool)
2900 {
2901         _point->line().reset ();
2902 }
2903
2904 bool
2905 ControlPointDrag::active (Editing::MouseMode m)
2906 {
2907         if (m == Editing::MouseGain) {
2908                 /* always active in mouse gain */
2909                 return true;
2910         }
2911
2912         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2913         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2914 }
2915
2916 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2917         : Drag (e, i),
2918           _line (0),
2919           _cumulative_y_drag (0)
2920 {
2921         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2922 }
2923
2924 void
2925 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2926 {
2927         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2928         assert (_line);
2929
2930         _item = &_line->grab_item ();
2931
2932         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2933            origin, and ditto for y.
2934         */
2935
2936         double cx = event->button.x;
2937         double cy = event->button.y;
2938
2939         _line->parent_group().w2i (cx, cy);
2940
2941         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2942
2943         uint32_t before;
2944         uint32_t after;
2945
2946         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2947                 /* no adjacent points */
2948                 return;
2949         }
2950
2951         Drag::start_grab (event, _editor->cursors()->fader);
2952
2953         /* store grab start in parent frame */
2954
2955         _fixed_grab_x = cx;
2956         _fixed_grab_y = cy;
2957
2958         double fraction = 1.0 - (cy / _line->height());
2959
2960         _line->start_drag_line (before, after, fraction);
2961
2962         _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2963                                         event->button.x + 10, event->button.y + 10);
2964
2965         _editor->verbose_cursor()->show ();
2966 }
2967
2968 void
2969 LineDrag::motion (GdkEvent* event, bool)
2970 {
2971         double dy = _drags->current_pointer_y() - last_pointer_y();
2972
2973         if (event->button.state & Keyboard::SecondaryModifier) {
2974                 dy *= 0.1;
2975         }
2976
2977         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2978
2979         _cumulative_y_drag = cy - _fixed_grab_y;
2980
2981         cy = max (0.0, cy);
2982         cy = min ((double) _line->height(), cy);
2983
2984         double const fraction = 1.0 - (cy / _line->height());
2985         bool const push = !Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2986
2987         /* we are ignoring x position for this drag, so we can just pass in anything */
2988         _line->drag_motion (0, fraction, true, push);
2989
2990         _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2991 }
2992
2993 void
2994 LineDrag::finished (GdkEvent* event, bool)
2995 {
2996         motion (event, false);
2997         _line->end_drag ();
2998         _editor->session()->commit_reversible_command ();
2999 }
3000
3001 void
3002 LineDrag::aborted (bool)
3003 {
3004         _line->reset ();
3005 }
3006
3007 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3008         : Drag (e, i),
3009           _line (0),
3010           _cumulative_x_drag (0)
3011 {
3012         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3013 }
3014
3015 void
3016 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3017 {
3018         Drag::start_grab (event);
3019
3020         _line = reinterpret_cast<Line*> (_item);
3021         assert (_line);
3022
3023         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3024
3025         double cx = event->button.x;
3026         double cy = event->button.y;
3027
3028         _item->property_parent().get_value()->w2i(cx, cy);
3029
3030         /* store grab start in parent frame */
3031         _region_view_grab_x = cx;
3032
3033         _before = *(float*) _item->get_data ("position");
3034
3035         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3036
3037         _max_x = _editor->frame_to_pixel(_arv->get_duration());
3038 }
3039
3040 void
3041 FeatureLineDrag::motion (GdkEvent*, bool)
3042 {
3043         double dx = _drags->current_pointer_x() - last_pointer_x();
3044
3045         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3046
3047         _cumulative_x_drag += dx;
3048
3049         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3050
3051         if (cx > _max_x){
3052                 cx = _max_x;
3053         }
3054         else if(cx < 0){
3055                 cx = 0;
3056         }
3057
3058         ArdourCanvas::Points points;
3059
3060         double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3061
3062         _line->get_bounds(x1, y2, x2, y2);
3063
3064         points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3065         points.push_back(Gnome::Art::Point(cx, y2 - y1));
3066
3067         _line->property_points() = points;
3068
3069         float *pos = new float;
3070         *pos = cx;
3071
3072         _line->set_data ("position", pos);
3073
3074         _before = cx;
3075 }
3076
3077 void
3078 FeatureLineDrag::finished (GdkEvent*, bool)
3079 {
3080         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3081         _arv->update_transient(_before, _before);
3082 }
3083
3084 void
3085 FeatureLineDrag::aborted (bool)
3086 {
3087         //_line->reset ();
3088 }
3089
3090 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3091         : Drag (e, i)
3092         , _vertical_only (false)
3093 {
3094         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3095 }
3096
3097 void
3098 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3099 {
3100         Drag::start_grab (event);
3101         show_verbose_cursor_time (adjusted_current_frame (event));
3102 }
3103
3104 void
3105 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3106 {
3107         framepos_t start;
3108         framepos_t end;
3109         double y1;
3110         double y2;
3111
3112         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3113
3114         framepos_t grab = grab_frame ();
3115         if (Config->get_rubberbanding_snaps_to_grid ()) {
3116                 _editor->snap_to_with_modifier (grab, event);
3117         }
3118
3119         /* base start and end on initial click position */
3120
3121         if (pf < grab) {
3122                 start = pf;
3123                 end = grab;
3124         } else {
3125                 end = pf;
3126                 start = grab;
3127         }
3128
3129         if (_drags->current_pointer_y() < grab_y()) {
3130                 y1 = _drags->current_pointer_y();
3131                 y2 = grab_y();
3132         } else {
3133                 y2 = _drags->current_pointer_y();
3134                 y1 = grab_y();
3135         }
3136
3137
3138         if (start != end || y1 != y2) {
3139
3140                 double x1 = _editor->frame_to_pixel (start);
3141                 double x2 = _editor->frame_to_pixel (end);
3142
3143                 _editor->rubberband_rect->property_x1() = x1;
3144                 if (_vertical_only) {
3145                         /* fixed 10 pixel width */
3146                         _editor->rubberband_rect->property_x2() = x1 + 10;
3147                 } else {
3148                         _editor->rubberband_rect->property_x2() = x2;
3149                 } 
3150
3151                 _editor->rubberband_rect->property_y1() = y1;
3152                 _editor->rubberband_rect->property_y2() = y2;
3153
3154                 _editor->rubberband_rect->show();
3155                 _editor->rubberband_rect->raise_to_top();
3156
3157                 show_verbose_cursor_time (pf);
3158
3159                 do_select_things (event, true);
3160         }
3161 }
3162
3163 void
3164 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3165 {
3166         framepos_t x1;
3167         framepos_t x2;
3168         
3169         if (grab_frame() < last_pointer_frame()) {
3170                 x1 = grab_frame ();
3171                 x2 = last_pointer_frame ();
3172         } else {
3173                 x2 = grab_frame ();
3174                 x1 = last_pointer_frame ();
3175         }
3176
3177         double y1;
3178         double y2;
3179         
3180         if (_drags->current_pointer_y() < grab_y()) {
3181                 y1 = _drags->current_pointer_y();
3182                 y2 = grab_y();
3183         } else {
3184                 y2 = _drags->current_pointer_y();
3185                 y1 = grab_y();
3186         }
3187
3188         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3189 }
3190
3191 void
3192 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3193 {
3194         if (movement_occurred) {
3195
3196                 motion (event, false);
3197                 do_select_things (event, false);
3198
3199         } else {
3200
3201                 /* just a click */
3202
3203                 bool do_deselect = true;
3204                 MidiTimeAxisView* mtv;
3205
3206                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3207                         /* MIDI track */
3208                         if (_editor->selection->empty()) {
3209                                 /* nothing selected */
3210                                 add_midi_region (mtv);
3211                                 do_deselect = false;
3212                         }
3213                 } 
3214
3215                 if (do_deselect) {
3216                         deselect_things ();
3217                 }
3218
3219         }
3220
3221         _editor->rubberband_rect->hide();
3222 }
3223
3224 void
3225 RubberbandSelectDrag::aborted (bool)
3226 {
3227         _editor->rubberband_rect->hide ();
3228 }
3229
3230 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3231         : RegionDrag (e, i, p, v)
3232 {
3233         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3234 }
3235
3236 void
3237 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3238 {
3239         Drag::start_grab (event, cursor);
3240
3241         show_verbose_cursor_time (adjusted_current_frame (event));
3242 }
3243
3244 void
3245 TimeFXDrag::motion (GdkEvent* event, bool)
3246 {
3247         RegionView* rv = _primary;
3248         StreamView* cv = rv->get_time_axis_view().view ();
3249
3250         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3251         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3252         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3253
3254         framepos_t const pf = adjusted_current_frame (event);
3255
3256         if (pf > rv->region()->position()) {
3257                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3258         }
3259
3260         show_verbose_cursor_time (pf);
3261 }
3262
3263 void
3264 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3265 {
3266         _primary->get_time_axis_view().hide_timestretch ();
3267
3268         if (!movement_occurred) {
3269                 return;
3270         }
3271
3272         if (last_pointer_frame() < _primary->region()->position()) {
3273                 /* backwards drag of the left edge - not usable */
3274                 return;
3275         }
3276
3277         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3278
3279         float percentage = (double) newlen / (double) _primary->region()->length();
3280
3281 #ifndef USE_RUBBERBAND
3282         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3283         if (_primary->region()->data_type() == DataType::AUDIO) {
3284                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3285         }
3286 #endif
3287
3288         if (!_editor->get_selection().regions.empty()) {
3289                 /* primary will already be included in the selection, and edit
3290                    group shared editing will propagate selection across
3291                    equivalent regions, so just use the current region
3292                    selection.
3293                 */
3294
3295                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3296                         error << _("An error occurred while executing time stretch operation") << endmsg;
3297                 }
3298         }
3299 }
3300
3301 void
3302 TimeFXDrag::aborted (bool)
3303 {
3304         _primary->get_time_axis_view().hide_timestretch ();
3305 }
3306
3307 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3308         : Drag (e, i)
3309 {
3310         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3311 }
3312
3313 void
3314 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3315 {
3316         Drag::start_grab (event);
3317 }
3318
3319 void
3320 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3321 {
3322         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3323 }
3324
3325 void
3326 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3327 {
3328         if (movement_occurred && _editor->session()) {
3329                 /* make sure we stop */
3330                 _editor->session()->request_transport_speed (0.0);
3331         }
3332 }
3333
3334 void
3335 ScrubDrag::aborted (bool)
3336 {
3337         /* XXX: TODO */
3338 }
3339
3340 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3341         : Drag (e, i)
3342         , _operation (o)
3343         , _copy (false)
3344         , _original_pointer_time_axis (-1)
3345         , _last_pointer_time_axis (-1)
3346         , _time_selection_at_start (!_editor->get_selection().time.empty())
3347 {
3348         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3349 }
3350
3351 void
3352 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3353 {
3354         if (_editor->session() == 0) {
3355                 return;
3356         }
3357
3358         Gdk::Cursor* cursor = 0;
3359
3360         switch (_operation) {
3361         case CreateSelection:
3362                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3363                         _copy = true;
3364                 } else {
3365                         _copy = false;
3366                 }
3367                 cursor = _editor->cursors()->selector;
3368                 Drag::start_grab (event, cursor);
3369                 break;
3370
3371         case SelectionStartTrim:
3372                 if (_editor->clicked_axisview) {
3373                         _editor->clicked_axisview->order_selection_trims (_item, true);
3374                 }
3375                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3376                 break;
3377
3378         case SelectionEndTrim:
3379                 if (_editor->clicked_axisview) {
3380                         _editor->clicked_axisview->order_selection_trims (_item, false);
3381                 }
3382                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3383                 break;
3384
3385         case SelectionMove:
3386                 Drag::start_grab (event, cursor);
3387                 break;
3388         }
3389
3390         if (_operation == SelectionMove) {
3391                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3392         } else {
3393                 show_verbose_cursor_time (adjusted_current_frame (event));
3394         }
3395
3396         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3397 }
3398
3399 void
3400 SelectionDrag::setup_pointer_frame_offset ()
3401 {
3402         switch (_operation) {
3403         case CreateSelection:
3404                 _pointer_frame_offset = 0;
3405                 break;
3406
3407         case SelectionStartTrim:
3408         case SelectionMove:
3409                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3410                 break;
3411
3412         case SelectionEndTrim:
3413                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3414                 break;
3415         }
3416 }
3417
3418 void
3419 SelectionDrag::motion (GdkEvent* event, bool first_move)
3420 {
3421         framepos_t start = 0;
3422         framepos_t end = 0;
3423         framecnt_t length;
3424
3425         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3426         if (pending_time_axis.first == 0) {
3427                 return;
3428         }
3429
3430         framepos_t const pending_position = adjusted_current_frame (event);
3431
3432         /* only alter selection if things have changed */
3433
3434         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3435                 return;
3436         }
3437
3438         switch (_operation) {
3439         case CreateSelection:
3440         {
3441                 framepos_t grab = grab_frame ();
3442
3443                 if (first_move) {
3444                         _editor->snap_to (grab);
3445                 }
3446
3447                 if (pending_position < grab_frame()) {
3448                         start = pending_position;
3449                         end = grab;
3450                 } else {
3451                         end = pending_position;
3452                         start = grab;
3453                 }
3454
3455                 /* first drag: Either add to the selection
3456                    or create a new selection
3457                 */
3458
3459                 if (first_move) {
3460
3461                         if (_copy) {
3462                                 /* adding to the selection */
3463                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3464                                 //_editor->selection->add (_editor->clicked_axisview);
3465                                 _editor->clicked_selection = _editor->selection->add (start, end);
3466                                 _copy = false;
3467                         } else {
3468                                 /* new selection */
3469
3470                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3471                                         //_editor->selection->set (_editor->clicked_axisview);
3472                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3473                                 }
3474
3475                                 _editor->clicked_selection = _editor->selection->set (start, end);
3476                         }
3477                 }
3478
3479                 /* select the track that we're in */
3480                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3481                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3482                         _editor->selection->add (pending_time_axis.first);
3483                         _added_time_axes.push_back (pending_time_axis.first);
3484                 }
3485
3486                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3487                    tracks that we selected in the first place.
3488                 */
3489
3490                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3491                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3492
3493                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3494                 while (i != _added_time_axes.end()) {
3495
3496                         list<TimeAxisView*>::iterator tmp = i;
3497                         ++tmp;
3498
3499                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3500                                 _editor->selection->remove (*i);
3501                                 _added_time_axes.remove (*i);
3502                         }
3503
3504                         i = tmp;
3505                 }
3506
3507         }
3508         break;
3509
3510         case SelectionStartTrim:
3511
3512                 start = _editor->selection->time[_editor->clicked_selection].start;
3513                 end = _editor->selection->time[_editor->clicked_selection].end;
3514
3515                 if (pending_position > end) {
3516                         start = end;
3517                 } else {
3518                         start = pending_position;
3519                 }
3520                 break;
3521
3522         case SelectionEndTrim:
3523
3524                 start = _editor->selection->time[_editor->clicked_selection].start;
3525                 end = _editor->selection->time[_editor->clicked_selection].end;
3526
3527                 if (pending_position < start) {
3528                         end = start;
3529                 } else {
3530                         end = pending_position;
3531                 }
3532
3533                 break;
3534
3535         case SelectionMove:
3536
3537                 start = _editor->selection->time[_editor->clicked_selection].start;
3538                 end = _editor->selection->time[_editor->clicked_selection].end;
3539
3540                 length = end - start;
3541
3542                 start = pending_position;
3543                 _editor->snap_to (start);
3544
3545                 end = start + length;
3546
3547                 break;
3548         }
3549
3550         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3551                 _editor->start_canvas_autoscroll (1, 0);
3552         }
3553
3554         if (start != end) {
3555                 _editor->selection->replace (_editor->clicked_selection, start, end);
3556         }
3557
3558         if (_operation == SelectionMove) {
3559                 show_verbose_cursor_time(start);
3560         } else {
3561                 show_verbose_cursor_time(pending_position);
3562         }
3563 }
3564
3565 void
3566 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3567 {
3568         Session* s = _editor->session();
3569
3570         if (movement_occurred) {
3571                 motion (event, false);
3572                 /* XXX this is not object-oriented programming at all. ick */
3573                 if (_editor->selection->time.consolidate()) {
3574                         _editor->selection->TimeChanged ();
3575                 }
3576
3577                 /* XXX what if its a music time selection? */
3578                 if (s) {
3579                         if ((s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3580                                 s->request_play_range (&_editor->selection->time, true);
3581                         } else {
3582                                 if (Config->get_always_play_range()) {
3583                                         if (_editor->doing_range_stuff()) {
3584                                                 s->request_locate (_editor->get_selection().time.start());
3585                                         } 
3586                                 }
3587                         }
3588                 }
3589
3590         } else {
3591                 /* just a click, no pointer movement.
3592                  */
3593
3594                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3595                         if (!_time_selection_at_start) {
3596                                 if (_editor->clicked_regionview) {
3597                                         if (_editor->get_selection().selected (_editor->clicked_regionview)) {
3598                                                 /* range select the entire current
3599                                                    region selection
3600                                                 */
3601                                                 _editor->select_range (_editor->get_selection().regions.start(), 
3602                                                                        _editor->get_selection().regions.end_frame());
3603                                         } else {
3604                                                 /* range select this (unselected)
3605                                                  * region
3606                                                  */
3607                                                 _editor->select_range (_editor->clicked_regionview->region()->position(), 
3608                                                                        _editor->clicked_regionview->region()->last_frame());
3609                                         }
3610                                 }
3611                         } else {
3612                                 _editor->selection->clear_time();
3613                         }
3614                 }
3615
3616                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3617                         _editor->selection->set (_editor->clicked_axisview);
3618                 }
3619
3620                 if (s && s->get_play_range () && s->transport_rolling()) {
3621                         s->request_stop (false, false);
3622                 }
3623
3624                 if (Config->get_always_play_range()) {
3625                         if (_editor->doing_range_stuff()) {
3626                                 s->request_locate (_editor->get_selection().time.start());
3627                         } 
3628                 }
3629         }
3630
3631         _editor->stop_canvas_autoscroll ();
3632 }
3633
3634 void
3635 SelectionDrag::aborted (bool)
3636 {
3637         /* XXX: TODO */
3638 }
3639
3640 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3641         : Drag (e, i),
3642           _operation (o),
3643           _copy (false)
3644 {
3645         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3646
3647         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3648                                                    physical_screen_height (_editor->get_window()));
3649         _drag_rect->hide ();
3650
3651         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3652         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3653 }
3654
3655 void
3656 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3657 {
3658         if (_editor->session() == 0) {
3659                 return;
3660         }
3661
3662         Gdk::Cursor* cursor = 0;
3663
3664         if (!_editor->temp_location) {
3665                 _editor->temp_location = new Location (*_editor->session());
3666         }
3667
3668         switch (_operation) {
3669         case CreateRangeMarker:
3670         case CreateTransportMarker:
3671         case CreateCDMarker:
3672
3673                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3674                         _copy = true;
3675                 } else {
3676                         _copy = false;
3677                 }
3678                 cursor = _editor->cursors()->selector;
3679                 break;
3680         }
3681
3682         Drag::start_grab (event, cursor);
3683
3684         show_verbose_cursor_time (adjusted_current_frame (event));
3685 }
3686
3687 void
3688 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3689 {
3690         framepos_t start = 0;
3691         framepos_t end = 0;
3692         ArdourCanvas::SimpleRect *crect;
3693
3694         switch (_operation) {
3695         case CreateRangeMarker:
3696                 crect = _editor->range_bar_drag_rect;
3697                 break;
3698         case CreateTransportMarker:
3699                 crect = _editor->transport_bar_drag_rect;
3700                 break;
3701         case CreateCDMarker:
3702                 crect = _editor->cd_marker_bar_drag_rect;
3703                 break;
3704         default:
3705                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3706                 return;
3707                 break;
3708         }
3709
3710         framepos_t const pf = adjusted_current_frame (event);
3711
3712         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3713                 framepos_t grab = grab_frame ();
3714                 _editor->snap_to (grab);
3715
3716                 if (pf < grab_frame()) {
3717                         start = pf;
3718                         end = grab;
3719                 } else {
3720                         end = pf;
3721                         start = grab;
3722                 }
3723
3724                 /* first drag: Either add to the selection
3725                    or create a new selection.
3726                 */
3727
3728                 if (first_move) {
3729
3730                         _editor->temp_location->set (start, end);
3731
3732                         crect->show ();
3733
3734                         update_item (_editor->temp_location);
3735                         _drag_rect->show();
3736                         //_drag_rect->raise_to_top();
3737
3738                 }
3739         }
3740
3741         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3742                 _editor->start_canvas_autoscroll (1, 0);
3743         }
3744
3745         if (start != end) {
3746                 _editor->temp_location->set (start, end);
3747
3748                 double x1 = _editor->frame_to_pixel (start);
3749                 double x2 = _editor->frame_to_pixel (end);
3750                 crect->property_x1() = x1;
3751                 crect->property_x2() = x2;
3752
3753                 update_item (_editor->temp_location);
3754         }
3755
3756         show_verbose_cursor_time (pf);
3757
3758 }
3759
3760 void
3761 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3762 {
3763         Location * newloc = 0;
3764         string rangename;
3765         int flags;
3766
3767         if (movement_occurred) {
3768                 motion (event, false);
3769                 _drag_rect->hide();
3770
3771                 switch (_operation) {
3772                 case CreateRangeMarker:
3773                 case CreateCDMarker:
3774                     {
3775                         _editor->begin_reversible_command (_("new range marker"));
3776                         XMLNode &before = _editor->session()->locations()->get_state();
3777                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3778                         if (_operation == CreateCDMarker) {
3779                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3780                                 _editor->cd_marker_bar_drag_rect->hide();
3781                         }
3782                         else {
3783                                 flags = Location::IsRangeMarker;
3784                                 _editor->range_bar_drag_rect->hide();
3785                         }
3786                         newloc = new Location (
3787                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3788                                 );
3789
3790                         _editor->session()->locations()->add (newloc, true);
3791                         XMLNode &after = _editor->session()->locations()->get_state();
3792                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3793                         _editor->commit_reversible_command ();
3794                         break;
3795                     }
3796
3797                 case CreateTransportMarker:
3798                         // popup menu to pick loop or punch
3799                         _editor->new_transport_marker_context_menu (&event->button, _item);
3800                         break;
3801                 }
3802         } else {
3803                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3804
3805                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3806
3807                         framepos_t start;
3808                         framepos_t end;
3809
3810                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3811
3812                         if (end == max_framepos) {
3813                                 end = _editor->session()->current_end_frame ();
3814                         }
3815
3816                         if (start == max_framepos) {
3817                                 start = _editor->session()->current_start_frame ();
3818                         }
3819
3820                         switch (_editor->mouse_mode) {
3821                         case MouseObject:
3822                                 /* find the two markers on either side and then make the selection from it */
3823                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3824                                 break;
3825
3826                         case MouseRange:
3827                                 /* find the two markers on either side of the click and make the range out of it */
3828                                 _editor->selection->set (start, end);
3829                                 break;
3830
3831                         default:
3832                                 break;
3833                         }
3834                 }
3835         }
3836
3837         _editor->stop_canvas_autoscroll ();
3838 }
3839
3840 void
3841 RangeMarkerBarDrag::aborted (bool)
3842 {
3843         /* XXX: TODO */
3844 }
3845
3846 void
3847 RangeMarkerBarDrag::update_item (Location* location)
3848 {
3849         double const x1 = _editor->frame_to_pixel (location->start());
3850         double const x2 = _editor->frame_to_pixel (location->end());
3851
3852         _drag_rect->property_x1() = x1;
3853         _drag_rect->property_x2() = x2;
3854 }
3855
3856 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3857         : Drag (e, i)
3858         , _zoom_out (false)
3859 {
3860         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3861 }
3862
3863 void
3864 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3865 {
3866         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3867                 Drag::start_grab (event, _editor->cursors()->zoom_out);
3868                 _zoom_out = true;
3869         } else {
3870                 Drag::start_grab (event, _editor->cursors()->zoom_in);
3871                 _zoom_out = false;
3872         }
3873
3874         show_verbose_cursor_time (adjusted_current_frame (event));
3875 }
3876
3877 void
3878 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3879 {
3880         framepos_t start;
3881         framepos_t end;
3882
3883         framepos_t const pf = adjusted_current_frame (event);
3884
3885         framepos_t grab = grab_frame ();
3886         _editor->snap_to_with_modifier (grab, event);
3887
3888         /* base start and end on initial click position */
3889         if (pf < grab) {
3890                 start = pf;
3891                 end = grab;
3892         } else {
3893                 end = pf;
3894                 start = grab;
3895         }
3896
3897         if (start != end) {
3898
3899                 if (first_move) {
3900                         _editor->zoom_rect->show();
3901                         _editor->zoom_rect->raise_to_top();
3902                 }
3903
3904                 _editor->reposition_zoom_rect(start, end);
3905
3906                 show_verbose_cursor_time (pf);
3907         }
3908 }
3909
3910 void
3911 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3912 {
3913         if (movement_occurred) {
3914                 motion (event, false);
3915
3916                 if (grab_frame() < last_pointer_frame()) {
3917                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
3918                 } else {
3919                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
3920                 }
3921         } else {
3922                 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3923                         _editor->tav_zoom_step (_zoom_out);
3924                 } else {
3925                         _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3926                 }
3927         }
3928
3929         _editor->zoom_rect->hide();
3930 }
3931
3932 void
3933 MouseZoomDrag::aborted (bool)
3934 {
3935         _editor->zoom_rect->hide ();
3936 }
3937
3938 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3939         : Drag (e, i)
3940         , _cumulative_dx (0)
3941         , _cumulative_dy (0)
3942 {
3943         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3944
3945         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3946         _region = &_primary->region_view ();
3947         _note_height = _region->midi_stream_view()->note_height ();
3948 }
3949
3950 void
3951 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3952 {
3953         Drag::start_grab (event);
3954
3955         if (!(_was_selected = _primary->selected())) {
3956
3957                 /* tertiary-click means extend selection - we'll do that on button release,
3958                    so don't add it here, because otherwise we make it hard to figure
3959                    out the "extend-to" range.
3960                 */
3961
3962                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3963
3964                 if (!extend) {
3965                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3966
3967                         if (add) {
3968                                 _region->note_selected (_primary, true);
3969                         } else {
3970                                 _region->unique_select (_primary);
3971                         }
3972                 }
3973         }
3974 }
3975
3976 /** @return Current total drag x change in frames */
3977 frameoffset_t
3978 NoteDrag::total_dx () const
3979 {
3980         /* dx in frames */
3981         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3982
3983         /* primary note time */
3984         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3985
3986         /* new time of the primary note in session frames */
3987         frameoffset_t st = n + dx;
3988
3989         framepos_t const rp = _region->region()->position ();
3990
3991         /* prevent the note being dragged earlier than the region's position */
3992         st = max (st, rp);
3993
3994         /* snap and return corresponding delta */
3995         return _region->snap_frame_to_frame (st - rp) + rp - n;
3996 }
3997
3998 /** @return Current total drag y change in note number */
3999 int8_t
4000 NoteDrag::total_dy () const
4001 {
4002         return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
4003 }
4004
4005 void
4006 NoteDrag::motion (GdkEvent *, bool)
4007 {
4008         /* Total change in x and y since the start of the drag */
4009         frameoffset_t const dx = total_dx ();
4010         int8_t const dy = total_dy ();
4011
4012         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4013         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4014         double const tdy = -dy * _note_height - _cumulative_dy;
4015
4016         if (tdx || tdy) {
4017                 _cumulative_dx += tdx;
4018                 _cumulative_dy += tdy;
4019
4020                 int8_t note_delta = total_dy();
4021
4022                 _region->move_selection (tdx, tdy, note_delta);
4023
4024                 /* the new note value may be the same as the old one, but we
4025                  * don't know what that means because the selection may have
4026                  * involved more than one note and we might be doing something
4027                  * odd with them. so show the note value anyway, always.
4028                  */
4029
4030                 char buf[12];
4031                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4032                 
4033                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4034                           (int) floor (new_note));
4035
4036                 show_verbose_cursor_text (buf);
4037         }
4038 }
4039
4040 void
4041 NoteDrag::finished (GdkEvent* ev, bool moved)
4042 {
4043         if (!moved) {
4044                 /* no motion - select note */
4045                 
4046                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4047                     _editor->current_mouse_mode() == Editing::MouseDraw) {
4048                         
4049                         if (_was_selected) {
4050                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4051                                 if (add) {
4052                                         _region->note_deselected (_primary);
4053                                 }
4054                         } else {
4055                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4056                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4057
4058                                 if (!extend && !add && _region->selection_size() > 1) {
4059                                         _region->unique_select (_primary);
4060                                 } else if (extend) {
4061                                         _region->note_selected (_primary, true, true);
4062                                 } else {
4063                                         /* it was added during button press */
4064                                 }
4065                         }
4066                 }
4067         } else {
4068                 _region->note_dropped (_primary, total_dx(), total_dy());
4069         }
4070 }
4071
4072 void
4073 NoteDrag::aborted (bool)
4074 {
4075         /* XXX: TODO */
4076 }
4077
4078 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4079 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4080         : Drag (editor, atv->base_item ())
4081         , _ranges (r)
4082         , _nothing_to_drag (false)
4083 {
4084         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4085
4086         setup (atv->lines ());
4087 }
4088
4089 /** Make an AutomationRangeDrag for region gain lines */
4090 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4091         : Drag (editor, rv->get_canvas_group ())
4092         , _ranges (r)
4093         , _nothing_to_drag (false)
4094 {
4095         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4096
4097         list<boost::shared_ptr<AutomationLine> > lines;
4098         lines.push_back (rv->get_gain_line ());
4099         setup (lines);
4100 }
4101
4102 /** @param lines AutomationLines to drag.
4103  *  @param offset Offset from the session start to the points in the AutomationLines.
4104  */
4105 void
4106 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4107 {
4108         /* find the lines that overlap the ranges being dragged */
4109         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4110         while (i != lines.end ()) {
4111                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4112                 ++j;
4113
4114                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4115
4116                 /* check this range against all the AudioRanges that we are using */
4117                 list<AudioRange>::const_iterator k = _ranges.begin ();
4118                 while (k != _ranges.end()) {
4119                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4120                                 break;
4121                         }
4122                         ++k;
4123                 }
4124
4125                 /* add it to our list if it overlaps at all */
4126                 if (k != _ranges.end()) {
4127                         Line n;
4128                         n.line = *i;
4129                         n.state = 0;
4130                         n.range = r;
4131                         _lines.push_back (n);
4132                 }
4133
4134                 i = j;
4135         }
4136
4137         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4138 }
4139
4140 void
4141 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4142 {
4143         Drag::start_grab (event, cursor);
4144
4145         /* Get line states before we start changing things */
4146         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4147                 i->state = &i->line->get_state ();
4148         }
4149
4150         if (_ranges.empty()) {
4151
4152                 /* No selected time ranges: drag all points */
4153                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4154                         uint32_t const N = i->line->npoints ();
4155                         for (uint32_t j = 0; j < N; ++j) {
4156                                 i->points.push_back (i->line->nth (j));
4157                         }
4158                 }
4159
4160         } else {
4161
4162                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4163
4164                         framecnt_t const half = (i->start + i->end) / 2;
4165
4166                         /* find the line that this audio range starts in */
4167                         list<Line>::iterator j = _lines.begin();
4168                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4169                                 ++j;
4170                         }
4171
4172                         if (j != _lines.end()) {
4173                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4174
4175                                 /* j is the line that this audio range starts in; fade into it;
4176                                    64 samples length plucked out of thin air.
4177                                 */
4178
4179                                 framepos_t a = i->start + 64;
4180                                 if (a > half) {
4181                                         a = half;
4182                                 }
4183
4184                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4185                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4186
4187                                 the_list->add (p, the_list->eval (p));
4188                                 the_list->add (q, the_list->eval (q));
4189                         }
4190
4191                         /* same thing for the end */
4192
4193                         j = _lines.begin();
4194                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4195                                 ++j;
4196                         }
4197
4198                         if (j != _lines.end()) {
4199                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4200
4201                                 /* j is the line that this audio range starts in; fade out of it;
4202                                    64 samples length plucked out of thin air.
4203                                 */
4204
4205                                 framepos_t b = i->end - 64;
4206                                 if (b < half) {
4207                                         b = half;
4208                                 }
4209
4210                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4211                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4212
4213                                 the_list->add (p, the_list->eval (p));
4214                                 the_list->add (q, the_list->eval (q));
4215                         }
4216                 }
4217
4218                 _nothing_to_drag = true;
4219
4220                 /* Find all the points that should be dragged and put them in the relevant
4221                    points lists in the Line structs.
4222                 */
4223
4224                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4225
4226                         uint32_t const N = i->line->npoints ();
4227                         for (uint32_t j = 0; j < N; ++j) {
4228
4229                                 /* here's a control point on this line */
4230                                 ControlPoint* p = i->line->nth (j);
4231                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4232
4233                                 /* see if it's inside a range */
4234                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4235                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4236                                         ++k;
4237                                 }
4238
4239                                 if (k != _ranges.end()) {
4240                                         /* dragging this point */
4241                                         _nothing_to_drag = false;
4242                                         i->points.push_back (p);
4243                                 }
4244                         }
4245                 }
4246         }
4247
4248         if (_nothing_to_drag) {
4249                 return;
4250         }
4251
4252         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4253                 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4254         }
4255 }
4256
4257 void
4258 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4259 {
4260         if (_nothing_to_drag) {
4261                 return;
4262         }
4263
4264         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4265                 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4266
4267                 /* we are ignoring x position for this drag, so we can just pass in anything */
4268                 i->line->drag_motion (0, f, true, false);
4269         }
4270 }
4271
4272 void
4273 AutomationRangeDrag::finished (GdkEvent* event, bool)
4274 {
4275         if (_nothing_to_drag) {
4276                 return;
4277         }
4278
4279         motion (event, false);
4280         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4281                 i->line->end_drag ();
4282         }
4283
4284         _editor->session()->commit_reversible_command ();
4285 }
4286
4287 void
4288 AutomationRangeDrag::aborted (bool)
4289 {
4290         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4291                 i->line->reset ();
4292         }
4293 }
4294
4295 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4296         : view (v)
4297 {
4298         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4299         layer = v->region()->layer ();
4300         initial_y = v->get_canvas_group()->property_y ();
4301         initial_playlist = v->region()->playlist ();
4302         initial_position = v->region()->position ();
4303         initial_end = v->region()->position () + v->region()->length ();
4304 }
4305
4306 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4307         : Drag (e, i)
4308         , _region_view (r)
4309         , _patch_change (i)
4310         , _cumulative_dx (0)
4311 {
4312         DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4313 }
4314
4315 void
4316 PatchChangeDrag::motion (GdkEvent* ev, bool)
4317 {
4318         framepos_t f = adjusted_current_frame (ev);
4319         boost::shared_ptr<Region> r = _region_view->region ();
4320         f = max (f, r->position ());
4321         f = min (f, r->last_frame ());
4322
4323         framecnt_t const dxf = f - grab_frame();
4324         double const dxu = _editor->frame_to_unit (dxf);
4325         _patch_change->move (dxu - _cumulative_dx, 0);
4326         _cumulative_dx = dxu;
4327 }
4328
4329 void
4330 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4331 {
4332         if (!movement_occurred) {
4333                 return;
4334         }
4335
4336         boost::shared_ptr<Region> r (_region_view->region ());
4337
4338         framepos_t f = adjusted_current_frame (ev);
4339         f = max (f, r->position ());
4340         f = min (f, r->last_frame ());
4341
4342         _region_view->move_patch_change (
4343                 *_patch_change,
4344                 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4345                 );
4346 }
4347
4348 void
4349 PatchChangeDrag::aborted (bool)
4350 {
4351         _patch_change->move (-_cumulative_dx, 0);
4352 }
4353
4354 void
4355 PatchChangeDrag::setup_pointer_frame_offset ()
4356 {
4357         boost::shared_ptr<Region> region = _region_view->region ();
4358         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4359 }
4360
4361 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4362         : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4363         , _region_view (rv)
4364 {
4365
4366 }
4367
4368 void
4369 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4370 {
4371         framepos_t const p = _region_view->region()->position ();
4372         double const y = _region_view->midi_view()->y_position ();
4373
4374         x1 = max ((framepos_t) 0, x1 - p);
4375         x2 = max ((framepos_t) 0, x2 - p);
4376         y1 = max (0.0, y1 - y);
4377         y2 = max (0.0, y2 - y);
4378         
4379         _region_view->update_drag_selection (
4380                 _editor->frame_to_pixel (x1),
4381                 _editor->frame_to_pixel (x2),
4382                 y1,
4383                 y2,
4384                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4385                 );
4386 }
4387
4388 void
4389 MidiRubberbandSelectDrag::deselect_things ()
4390 {
4391         /* XXX */
4392 }
4393
4394 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4395         : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4396         , _region_view (rv)
4397 {
4398         _vertical_only = true;
4399 }
4400
4401 void
4402 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4403 {
4404         double const y = _region_view->midi_view()->y_position ();
4405
4406         y1 = max (0.0, y1 - y);
4407         y2 = max (0.0, y2 - y);
4408         
4409         _region_view->update_vertical_drag_selection (
4410                 y1,
4411                 y2,
4412                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4413                 );
4414 }
4415
4416 void
4417 MidiVerticalSelectDrag::deselect_things ()
4418 {
4419         /* XXX */
4420 }
4421
4422 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4423         : RubberbandSelectDrag (e, i)
4424 {
4425
4426 }
4427
4428 void
4429 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4430 {
4431         if (drag_in_progress) {
4432                 /* We just want to select things at the end of the drag, not during it */
4433                 return;
4434         }
4435         
4436         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4437         
4438         _editor->begin_reversible_command (_("rubberband selection"));
4439         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4440         _editor->commit_reversible_command ();
4441 }
4442
4443 void
4444 EditorRubberbandSelectDrag::deselect_things ()
4445 {
4446         if (!getenv("ARDOUR_SAE")) {
4447                 _editor->selection->clear_tracks();
4448         }
4449         _editor->selection->clear_regions();
4450         _editor->selection->clear_points ();
4451         _editor->selection->clear_lines ();
4452 }
4453
4454 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4455         : Drag (e, i)
4456         , _region_view (rv)
4457         , _drag_rect (0)
4458 {
4459         
4460 }
4461
4462 NoteCreateDrag::~NoteCreateDrag ()
4463 {
4464         delete _drag_rect;
4465 }
4466
4467 framecnt_t
4468 NoteCreateDrag::grid_frames (framepos_t t) const
4469 {
4470         bool success;
4471         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4472         if (!success) {
4473                 grid_beats = 1;
4474         }
4475
4476         return _region_view->region_beats_to_region_frames (grid_beats);
4477 }
4478
4479 void
4480 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4481 {
4482         Drag::start_grab (event, cursor);
4483                                                  
4484         _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4485
4486         framepos_t pf = _drags->current_pointer_frame ();
4487         framecnt_t const g = grid_frames (pf);
4488
4489         /* Hack so that we always snap to the note that we are over, instead of snapping
4490            to the next one if we're more than halfway through the one we're over.
4491         */
4492         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4493                 pf -= g / 2;
4494         }
4495
4496         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4497
4498         MidiStreamView* sv = _region_view->midi_stream_view ();
4499         double const x = _editor->frame_to_pixel (_note[0]);
4500         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4501
4502         _drag_rect->property_x1() = x;
4503         _drag_rect->property_y1() = y;
4504         _drag_rect->property_x2() = x;
4505         _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4506
4507         _drag_rect->property_outline_what() = 0xff;
4508         _drag_rect->property_outline_color_rgba() = 0xffffff99;
4509         _drag_rect->property_fill_color_rgba()    = 0xffffff66;
4510 }
4511
4512 void
4513 NoteCreateDrag::motion (GdkEvent* event, bool)
4514 {
4515         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4516         double const x = _editor->frame_to_pixel (_note[1]);
4517         if (_note[1] > _note[0]) {
4518                 _drag_rect->property_x2() = x;
4519         } else {
4520                 _drag_rect->property_x1() = x;
4521         }
4522 }
4523
4524 void
4525 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4526 {
4527         if (!had_movement) {
4528                 return;
4529         }
4530         
4531         framepos_t const start = min (_note[0], _note[1]);
4532         framecnt_t length = abs (_note[0] - _note[1]);
4533
4534         framecnt_t const g = grid_frames (start);
4535         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4536         
4537         if (_editor->snap_mode() == SnapNormal && length < g) {
4538                 length = g - one_tick;
4539         }
4540
4541         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4542
4543         _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4544 }
4545
4546 double
4547 NoteCreateDrag::y_to_region (double y) const
4548 {
4549         double x = 0;
4550         _region_view->get_canvas_group()->w2i (x, y);
4551         return y;
4552 }
4553
4554 void
4555 NoteCreateDrag::aborted (bool)
4556 {
4557         
4558 }
4559
4560 /*------------*/
4561
4562 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4563         : Drag (e, i)
4564         , arv (rv)
4565         , start (start_yn)
4566 {
4567 }
4568
4569 void
4570 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4571 {
4572         Drag::start_grab (event, cursor);
4573 }
4574
4575 void
4576 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4577 {
4578         double distance;
4579         double new_length;
4580         framecnt_t len;
4581
4582         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4583
4584         if (start) {
4585                 distance = _drags->current_pointer_x() - grab_x();
4586                 len = ar->fade_in()->back()->when;
4587         } else {
4588                 distance = grab_x() - _drags->current_pointer_x();
4589                 len = ar->fade_out()->back()->when;
4590         }
4591
4592         /* how long should it be ? */
4593
4594         new_length = len + _editor->unit_to_frame (distance);
4595
4596         /* now check with the region that this is legal */
4597
4598         new_length = ar->verify_xfade_bounds (new_length, start);
4599
4600         if (start) {
4601                 arv->redraw_start_xfade_to (ar, new_length);
4602         } else {
4603                 arv->redraw_end_xfade_to (ar, new_length);
4604         }
4605 }
4606
4607 void
4608 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4609 {
4610         double distance;
4611         double new_length;
4612         framecnt_t len;
4613
4614         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4615
4616         if (start) {
4617                 distance = _drags->current_pointer_x() - grab_x();
4618                 len = ar->fade_in()->back()->when;
4619         } else {
4620                 distance = grab_x() - _drags->current_pointer_x();
4621                 len = ar->fade_out()->back()->when;
4622         }
4623
4624         new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start);
4625         
4626         _editor->begin_reversible_command ("xfade trim");
4627         ar->playlist()->clear_owned_changes (); 
4628
4629         if (start) {
4630                 ar->set_fade_in_length (new_length);
4631         } else {
4632                 ar->set_fade_out_length (new_length);
4633         }
4634
4635         /* Adjusting the xfade may affect other regions in the playlist, so we need
4636            to get undo Commands from the whole playlist rather than just the
4637            region.
4638         */
4639
4640         vector<Command*> cmds;
4641         ar->playlist()->rdiff (cmds);
4642         _editor->session()->add_commands (cmds);
4643
4644 }
4645
4646 void
4647 CrossfadeEdgeDrag::aborted (bool)
4648 {
4649         if (start) {
4650                 arv->redraw_start_xfade ();
4651         } else {
4652                 arv->redraw_end_xfade ();
4653         }
4654 }
4655