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