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