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