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