Don't catch up with the playhead after drags.
[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, false);
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
3043                 _editor->begin_reversible_command (_("rubberband selection"));
3044
3045                 if (grab_frame() < last_pointer_frame()) {
3046                         _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3047                 } else {
3048                         _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3049                 }
3050
3051                 _editor->commit_reversible_command ();
3052         } else {
3053                 if (!getenv("ARDOUR_SAE")) {
3054                         _editor->selection->clear_tracks();
3055                 }
3056                 _editor->selection->clear_regions();
3057                 _editor->selection->clear_points ();
3058                 _editor->selection->clear_lines ();
3059         }
3060
3061         _editor->rubberband_rect->hide();
3062 }
3063
3064 void
3065 RubberbandSelectDrag::aborted (bool)
3066 {
3067         _editor->rubberband_rect->hide ();
3068 }
3069
3070 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3071         : RegionDrag (e, i, p, v)
3072 {
3073         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3074 }
3075
3076 void
3077 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3078 {
3079         Drag::start_grab (event, cursor);
3080
3081         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3082 }
3083
3084 void
3085 TimeFXDrag::motion (GdkEvent* event, bool)
3086 {
3087         RegionView* rv = _primary;
3088
3089         framepos_t const pf = adjusted_current_frame (event);
3090
3091         if (pf > rv->region()->position()) {
3092                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3093         }
3094
3095         _editor->show_verbose_time_cursor (pf, 10);
3096 }
3097
3098 void
3099 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3100 {
3101         _primary->get_time_axis_view().hide_timestretch ();
3102
3103         if (!movement_occurred) {
3104                 return;
3105         }
3106
3107         if (last_pointer_frame() < _primary->region()->position()) {
3108                 /* backwards drag of the left edge - not usable */
3109                 return;
3110         }
3111
3112         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3113
3114         float percentage = (double) newlen / (double) _primary->region()->length();
3115
3116 #ifndef USE_RUBBERBAND
3117         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3118         if (_primary->region()->data_type() == DataType::AUDIO) {
3119                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3120         }
3121 #endif
3122
3123         _editor->begin_reversible_command (_("timestretch"));
3124
3125         // XXX how do timeFX on multiple regions ?
3126
3127         RegionSelection rs;
3128         rs.add (_primary);
3129
3130         if (_editor->time_stretch (rs, percentage) == -1) {
3131                 error << _("An error occurred while executing time stretch operation") << endmsg;
3132         }
3133 }
3134
3135 void
3136 TimeFXDrag::aborted (bool)
3137 {
3138         _primary->get_time_axis_view().hide_timestretch ();
3139 }
3140
3141 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3142         : Drag (e, i)
3143 {
3144         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3145 }
3146
3147 void
3148 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3149 {
3150         Drag::start_grab (event);
3151 }
3152
3153 void
3154 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3155 {
3156         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3157 }
3158
3159 void
3160 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3161 {
3162         if (movement_occurred && _editor->session()) {
3163                 /* make sure we stop */
3164                 _editor->session()->request_transport_speed (0.0);
3165         }
3166 }
3167
3168 void
3169 ScrubDrag::aborted (bool)
3170 {
3171         /* XXX: TODO */
3172 }
3173
3174 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3175         : Drag (e, i)
3176         , _operation (o)
3177         , _copy (false)
3178         , _original_pointer_time_axis (-1)
3179         , _last_pointer_time_axis (-1)
3180 {
3181         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3182 }
3183
3184 void
3185 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3186 {
3187         if (_editor->session() == 0) {
3188                 return;
3189         }
3190
3191         Gdk::Cursor* cursor = 0;
3192
3193         switch (_operation) {
3194         case CreateSelection:
3195                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3196                         _copy = true;
3197                 } else {
3198                         _copy = false;
3199                 }
3200                 cursor = _editor->cursors()->selector;
3201                 Drag::start_grab (event, cursor);
3202                 break;
3203
3204         case SelectionStartTrim:
3205                 if (_editor->clicked_axisview) {
3206                         _editor->clicked_axisview->order_selection_trims (_item, true);
3207                 }
3208                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3209                 break;
3210
3211         case SelectionEndTrim:
3212                 if (_editor->clicked_axisview) {
3213                         _editor->clicked_axisview->order_selection_trims (_item, false);
3214                 }
3215                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3216                 break;
3217
3218         case SelectionMove:
3219                 Drag::start_grab (event, cursor);
3220                 break;
3221         }
3222
3223         if (_operation == SelectionMove) {
3224                 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3225         } else {
3226                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3227         }
3228
3229         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3230 }
3231
3232 void
3233 SelectionDrag::setup_pointer_frame_offset ()
3234 {
3235         switch (_operation) {
3236         case CreateSelection:
3237                 _pointer_frame_offset = 0;
3238                 break;
3239
3240         case SelectionStartTrim:
3241         case SelectionMove:
3242                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3243                 break;
3244
3245         case SelectionEndTrim:
3246                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3247                 break;
3248         }
3249 }
3250
3251 void
3252 SelectionDrag::motion (GdkEvent* event, bool first_move)
3253 {
3254         framepos_t start = 0;
3255         framepos_t end = 0;
3256         framecnt_t length;
3257
3258         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3259         if (pending_time_axis.first == 0) {
3260                 return;
3261         }
3262         
3263         framepos_t const pending_position = adjusted_current_frame (event);
3264
3265         /* only alter selection if things have changed */
3266
3267         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3268                 return;
3269         }
3270
3271         switch (_operation) {
3272         case CreateSelection:
3273         {
3274                 framepos_t grab = grab_frame ();
3275
3276                 if (first_move) {
3277                         _editor->snap_to (grab);
3278                 }
3279
3280                 if (pending_position < grab_frame()) {
3281                         start = pending_position;
3282                         end = grab;
3283                 } else {
3284                         end = pending_position;
3285                         start = grab;
3286                 }
3287
3288                 /* first drag: Either add to the selection
3289                    or create a new selection
3290                 */
3291
3292                 if (first_move) {
3293
3294                         if (_copy) {
3295                                 /* adding to the selection */
3296                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3297                                 //_editor->selection->add (_editor->clicked_axisview);
3298                                 _editor->clicked_selection = _editor->selection->add (start, end);
3299                                 _copy = false;
3300                         } else {
3301                                 /* new selection */
3302
3303                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3304                                         //_editor->selection->set (_editor->clicked_axisview);
3305                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3306                                 }
3307                                 
3308                                 _editor->clicked_selection = _editor->selection->set (start, end);
3309                         }
3310                 }
3311
3312                 /* select the track that we're in */
3313                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3314                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3315                         _editor->selection->add (pending_time_axis.first);
3316                         _added_time_axes.push_back (pending_time_axis.first);
3317                 }
3318
3319                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3320                    tracks that we selected in the first place.
3321                 */
3322                 
3323                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3324                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3325
3326                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3327                 while (i != _added_time_axes.end()) {
3328
3329                         list<TimeAxisView*>::iterator tmp = i;
3330                         ++tmp;
3331                         
3332                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3333                                 _editor->selection->remove (*i);
3334                                 _added_time_axes.remove (*i);
3335                         }
3336
3337                         i = tmp;
3338                 }
3339
3340         }
3341         break;
3342
3343         case SelectionStartTrim:
3344
3345                 start = _editor->selection->time[_editor->clicked_selection].start;
3346                 end = _editor->selection->time[_editor->clicked_selection].end;
3347
3348                 if (pending_position > end) {
3349                         start = end;
3350                 } else {
3351                         start = pending_position;
3352                 }
3353                 break;
3354
3355         case SelectionEndTrim:
3356
3357                 start = _editor->selection->time[_editor->clicked_selection].start;
3358                 end = _editor->selection->time[_editor->clicked_selection].end;
3359
3360                 if (pending_position < start) {
3361                         end = start;
3362                 } else {
3363                         end = pending_position;
3364                 }
3365
3366                 break;
3367
3368         case SelectionMove:
3369
3370                 start = _editor->selection->time[_editor->clicked_selection].start;
3371                 end = _editor->selection->time[_editor->clicked_selection].end;
3372
3373                 length = end - start;
3374
3375                 start = pending_position;
3376                 _editor->snap_to (start);
3377
3378                 end = start + length;
3379
3380                 break;
3381         }
3382
3383         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3384                 _editor->start_canvas_autoscroll (1, 0);
3385         }
3386
3387         if (start != end) {
3388                 _editor->selection->replace (_editor->clicked_selection, start, end);
3389         }
3390
3391         if (_operation == SelectionMove) {
3392                 _editor->show_verbose_time_cursor(start, 10);
3393         } else {
3394                 _editor->show_verbose_time_cursor(pending_position, 10);
3395         }
3396 }
3397
3398 void
3399 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3400 {
3401         Session* s = _editor->session();
3402
3403         if (movement_occurred) {
3404                 motion (event, false);
3405                 /* XXX this is not object-oriented programming at all. ick */
3406                 if (_editor->selection->time.consolidate()) {
3407                         _editor->selection->TimeChanged ();
3408                 }
3409
3410                 /* XXX what if its a music time selection? */
3411                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3412                         s->request_play_range (&_editor->selection->time, true);
3413                 }
3414
3415
3416         } else {
3417                 /* just a click, no pointer movement.*/
3418
3419                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3420                         _editor->selection->clear_time();
3421                 }
3422
3423                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3424                         _editor->selection->set (_editor->clicked_axisview);
3425                 }
3426                 
3427                 if (s && s->get_play_range () && s->transport_rolling()) {
3428                         s->request_stop (false, false);
3429                 }
3430
3431         }
3432
3433         _editor->stop_canvas_autoscroll ();
3434 }
3435
3436 void
3437 SelectionDrag::aborted (bool)
3438 {
3439         /* XXX: TODO */
3440 }
3441
3442 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3443         : Drag (e, i),
3444           _operation (o),
3445           _copy (false)
3446 {
3447         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3448         
3449         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, 
3450                                                    physical_screen_height (_editor->get_window()));
3451         _drag_rect->hide ();
3452
3453         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3454         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3455 }
3456
3457 void
3458 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3459 {
3460         if (_editor->session() == 0) {
3461                 return;
3462         }
3463
3464         Gdk::Cursor* cursor = 0;
3465
3466         if (!_editor->temp_location) {
3467                 _editor->temp_location = new Location (*_editor->session());
3468         }
3469
3470         switch (_operation) {
3471         case CreateRangeMarker:
3472         case CreateTransportMarker:
3473         case CreateCDMarker:
3474
3475                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3476                         _copy = true;
3477                 } else {
3478                         _copy = false;
3479                 }
3480                 cursor = _editor->cursors()->selector;
3481                 break;
3482         }
3483
3484         Drag::start_grab (event, cursor);
3485
3486         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3487 }
3488
3489 void
3490 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3491 {
3492         framepos_t start = 0;
3493         framepos_t end = 0;
3494         ArdourCanvas::SimpleRect *crect;
3495
3496         switch (_operation) {
3497         case CreateRangeMarker:
3498                 crect = _editor->range_bar_drag_rect;
3499                 break;
3500         case CreateTransportMarker:
3501                 crect = _editor->transport_bar_drag_rect;
3502                 break;
3503         case CreateCDMarker:
3504                 crect = _editor->cd_marker_bar_drag_rect;
3505                 break;
3506         default:
3507                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3508                 return;
3509                 break;
3510         }
3511
3512         framepos_t const pf = adjusted_current_frame (event);
3513
3514         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3515                 framepos_t grab = grab_frame ();
3516                 _editor->snap_to (grab);
3517                 
3518                 if (pf < grab_frame()) {
3519                         start = pf;
3520                         end = grab;
3521                 } else {
3522                         end = pf;
3523                         start = grab;
3524                 }
3525
3526                 /* first drag: Either add to the selection
3527                    or create a new selection.
3528                 */
3529
3530                 if (first_move) {
3531
3532                         _editor->temp_location->set (start, end);
3533
3534                         crect->show ();
3535
3536                         update_item (_editor->temp_location);
3537                         _drag_rect->show();
3538                         //_drag_rect->raise_to_top();
3539
3540                 }
3541         }
3542
3543         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3544                 _editor->start_canvas_autoscroll (1, 0);
3545         }
3546
3547         if (start != end) {
3548                 _editor->temp_location->set (start, end);
3549
3550                 double x1 = _editor->frame_to_pixel (start);
3551                 double x2 = _editor->frame_to_pixel (end);
3552                 crect->property_x1() = x1;
3553                 crect->property_x2() = x2;
3554
3555                 update_item (_editor->temp_location);
3556         }
3557
3558         _editor->show_verbose_time_cursor (pf, 10);
3559
3560 }
3561
3562 void
3563 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3564 {
3565         Location * newloc = 0;
3566         string rangename;
3567         int flags;
3568
3569         if (movement_occurred) {
3570                 motion (event, false);
3571                 _drag_rect->hide();
3572
3573                 switch (_operation) {
3574                 case CreateRangeMarker:
3575                 case CreateCDMarker:
3576                     {
3577                         _editor->begin_reversible_command (_("new range marker"));
3578                         XMLNode &before = _editor->session()->locations()->get_state();
3579                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3580                         if (_operation == CreateCDMarker) {
3581                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3582                                 _editor->cd_marker_bar_drag_rect->hide();
3583                         }
3584                         else {
3585                                 flags = Location::IsRangeMarker;
3586                                 _editor->range_bar_drag_rect->hide();
3587                         }
3588                         newloc = new Location (
3589                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3590                                 );
3591                         
3592                         _editor->session()->locations()->add (newloc, true);
3593                         XMLNode &after = _editor->session()->locations()->get_state();
3594                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3595                         _editor->commit_reversible_command ();
3596                         break;
3597                     }
3598
3599                 case CreateTransportMarker:
3600                         // popup menu to pick loop or punch
3601                         _editor->new_transport_marker_context_menu (&event->button, _item);
3602                         break;
3603                 }
3604         } else {
3605                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3606
3607                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3608
3609                         framepos_t start;
3610                         framepos_t end;
3611
3612                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3613
3614                         if (end == max_framepos) {
3615                                 end = _editor->session()->current_end_frame ();
3616                         }
3617
3618                         if (start == max_framepos) {
3619                                 start = _editor->session()->current_start_frame ();
3620                         }
3621
3622                         switch (_editor->mouse_mode) {
3623                         case MouseObject:
3624                                 /* find the two markers on either side and then make the selection from it */
3625                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3626                                 break;
3627
3628                         case MouseRange:
3629                                 /* find the two markers on either side of the click and make the range out of it */
3630                                 _editor->selection->set (start, end);
3631                                 break;
3632
3633                         default:
3634                                 break;
3635                         }
3636                 }
3637         }
3638
3639         _editor->stop_canvas_autoscroll ();
3640 }
3641
3642 void
3643 RangeMarkerBarDrag::aborted (bool)
3644 {
3645         /* XXX: TODO */
3646 }
3647
3648 void
3649 RangeMarkerBarDrag::update_item (Location* location)
3650 {
3651         double const x1 = _editor->frame_to_pixel (location->start());
3652         double const x2 = _editor->frame_to_pixel (location->end());
3653
3654         _drag_rect->property_x1() = x1;
3655         _drag_rect->property_x2() = x2;
3656 }
3657
3658 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3659         : Drag (e, i)
3660         , _zoom_out (false)
3661 {
3662         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3663 }
3664
3665 void
3666 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3667 {
3668         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3669                 Drag::start_grab (event, _editor->cursors()->zoom_out);
3670                 _zoom_out = true;
3671         } else {
3672                 Drag::start_grab (event, _editor->cursors()->zoom_in);
3673                 _zoom_out = false;
3674         }
3675                 
3676         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3677 }
3678
3679 void
3680 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3681 {
3682         framepos_t start;
3683         framepos_t end;
3684
3685         framepos_t const pf = adjusted_current_frame (event);
3686
3687         framepos_t grab = grab_frame ();
3688         _editor->snap_to_with_modifier (grab, event);
3689
3690         /* base start and end on initial click position */
3691         if (pf < grab) {
3692                 start = pf;
3693                 end = grab;
3694         } else {
3695                 end = pf;
3696                 start = grab;
3697         }
3698
3699         if (start != end) {
3700
3701                 if (first_move) {
3702                         _editor->zoom_rect->show();
3703                         _editor->zoom_rect->raise_to_top();
3704                 }
3705
3706                 _editor->reposition_zoom_rect(start, end);
3707
3708                 _editor->show_verbose_time_cursor (pf, 10);
3709         }
3710 }
3711
3712 void
3713 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3714 {
3715         if (movement_occurred) {
3716                 motion (event, false);
3717
3718                 if (grab_frame() < last_pointer_frame()) {
3719                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3720                 } else {
3721                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3722                 }
3723         } else {
3724                 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3725         }
3726
3727         _editor->zoom_rect->hide();
3728 }
3729
3730 void
3731 MouseZoomDrag::aborted (bool)
3732 {
3733         _editor->zoom_rect->hide ();
3734 }
3735
3736 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3737         : Drag (e, i)
3738         , _cumulative_dx (0)
3739         , _cumulative_dy (0)
3740 {
3741         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3742
3743         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3744         _region = &_primary->region_view ();
3745         _note_height = _region->midi_stream_view()->note_height ();
3746 }
3747
3748 void
3749 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3750 {
3751         Drag::start_grab (event);
3752
3753         if (!(_was_selected = _primary->selected())) {
3754
3755                 /* tertiary-click means extend selection - we'll do that on button release,
3756                    so don't add it here, because otherwise we make it hard to figure
3757                    out the "extend-to" range.
3758                 */
3759
3760                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3761
3762                 if (!extend) {
3763                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3764
3765                         if (add) {
3766                                 _region->note_selected (_primary, true);
3767                         } else {
3768                                 _region->unique_select (_primary);
3769                         }
3770                 }
3771         }
3772 }
3773
3774 /** @return Current total drag x change in frames */
3775 frameoffset_t
3776 NoteDrag::total_dx () const
3777 {
3778         /* dx in frames */
3779         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3780
3781         /* primary note time */
3782         frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3783         
3784         /* new time of the primary note relative to the region position */
3785         frameoffset_t const st = n + dx;
3786
3787         /* snap and return corresponding delta */
3788         return _region->snap_frame_to_frame (st) - n;
3789 }
3790
3791 /** @return Current total drag y change in notes */
3792 int8_t
3793 NoteDrag::total_dy () const
3794 {
3795         /* this is `backwards' to make increasing note number go in the right direction */
3796         double const dy = _drags->current_pointer_y() - grab_y();
3797
3798         /* dy in notes */
3799         int8_t ndy = 0;
3800
3801         if (abs (dy) >= _note_height) {
3802                 if (dy > 0) {
3803                         ndy = (int8_t) ceil (dy / _note_height / 2.0);
3804                 } else {
3805                         ndy = (int8_t) floor (dy / _note_height / 2.0);
3806                 }
3807         }
3808
3809         /* more positive value = higher pitch and higher y-axis position on track,
3810            which is the inverse of the X-centric geometric universe 
3811         */
3812
3813         return -ndy; 
3814 }       
3815
3816 void
3817 NoteDrag::motion (GdkEvent *, bool)
3818 {
3819         /* Total change in x and y since the start of the drag */
3820         frameoffset_t const dx = total_dx ();
3821         int8_t const dy = -total_dy ();
3822
3823         /* Now work out what we have to do to the note canvas items to set this new drag delta */
3824         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3825         double const tdy = dy * _note_height - _cumulative_dy;
3826
3827         if (tdx || tdy) {
3828                 _cumulative_dx += tdx;
3829                 _cumulative_dy += tdy;
3830
3831                 int8_t note_delta = total_dy();
3832
3833                 _region->move_selection (tdx, tdy, note_delta);
3834
3835                 char buf[12];
3836                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3837                           (int) floor (_primary->note()->note() + note_delta));
3838                 
3839                 _editor->show_verbose_canvas_cursor_with (buf);
3840         }
3841 }
3842
3843 void
3844 NoteDrag::finished (GdkEvent* ev, bool moved)
3845 {
3846         if (!moved) {
3847                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3848
3849                         if (_was_selected) {
3850                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3851                                 if (add) {
3852                                         _region->note_deselected (_primary);
3853                                 }
3854                         } else {
3855                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3856                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3857
3858                                 if (!extend && !add && _region->selection_size() > 1) {
3859                                         _region->unique_select (_primary);
3860                                 } else if (extend) {
3861                                         _region->note_selected (_primary, true, true);
3862                                 } else {
3863                                         /* it was added during button press */
3864                                 }
3865                         }
3866                 }
3867         } else {
3868                 _region->note_dropped (_primary, total_dx(), total_dy());
3869         }
3870 }
3871
3872 void
3873 NoteDrag::aborted (bool)
3874 {
3875         /* XXX: TODO */
3876 }
3877
3878 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3879         : Drag (editor, item)
3880         , _ranges (r)
3881         , _nothing_to_drag (false)
3882 {
3883         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3884         
3885         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3886         assert (_atav);
3887
3888         /* get all lines in the automation view */
3889         list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3890
3891         /* find those that overlap the ranges being dragged */
3892         list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3893         while (i != lines.end ()) {
3894                 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3895                 ++j;
3896
3897                 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3898
3899                 /* check this range against all the AudioRanges that we are using */
3900                 list<AudioRange>::const_iterator k = _ranges.begin ();
3901                 while (k != _ranges.end()) {
3902                         if (k->coverage (r.first, r.second) != OverlapNone) {
3903                                 break;
3904                         }
3905                         ++k;
3906                 }
3907
3908                 /* add it to our list if it overlaps at all */
3909                 if (k != _ranges.end()) {
3910                         Line n;
3911                         n.line = *i;
3912                         n.state = 0;
3913                         n.range = r;
3914                         _lines.push_back (n);
3915                 }
3916
3917                 i = j;
3918         }
3919
3920         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3921 }
3922
3923 void
3924 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3925 {
3926         Drag::start_grab (event, cursor);
3927
3928         /* Get line states before we start changing things */
3929         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3930                 i->state = &i->line->get_state ();
3931         }
3932
3933         if (_ranges.empty()) {
3934
3935                 /* No selected time ranges: drag all points */
3936                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3937                         uint32_t const N = i->line->npoints ();
3938                         for (uint32_t j = 0; j < N; ++j) {
3939                                 i->points.push_back (i->line->nth (j));
3940                         }
3941                 }
3942                 
3943         } else {
3944
3945                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3946
3947                         framecnt_t const half = (i->start + i->end) / 2;
3948                         
3949                         /* find the line that this audio range starts in */
3950                         list<Line>::iterator j = _lines.begin();
3951                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3952                                 ++j;
3953                         }
3954
3955                         if (j != _lines.end()) {
3956                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3957                                 
3958                                 /* j is the line that this audio range starts in; fade into it;
3959                                    64 samples length plucked out of thin air.
3960                                 */
3961
3962                                 framepos_t a = i->start + 64;
3963                                 if (a > half) {
3964                                         a = half;
3965                                 }
3966
3967                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3968                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3969
3970                                 the_list->add (p, the_list->eval (p));
3971                                 j->line->add_always_in_view (p);
3972                                 the_list->add (q, the_list->eval (q));
3973                                 j->line->add_always_in_view (q);
3974                         }
3975
3976                         /* same thing for the end */
3977                         
3978                         j = _lines.begin();
3979                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3980                                 ++j;
3981                         }
3982
3983                         if (j != _lines.end()) {
3984                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3985                                 
3986                                 /* j is the line that this audio range starts in; fade out of it;
3987                                    64 samples length plucked out of thin air.
3988                                 */
3989                                 
3990                                 framepos_t b = i->end - 64;
3991                                 if (b < half) {
3992                                         b = half;
3993                                 }
3994
3995                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3996                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3997                                 
3998                                 the_list->add (p, the_list->eval (p));
3999                                 j->line->add_always_in_view (p);
4000                                 the_list->add (q, the_list->eval (q));
4001                                 j->line->add_always_in_view (q);
4002                         }
4003                 }
4004
4005                 _nothing_to_drag = true;
4006
4007                 /* Find all the points that should be dragged and put them in the relevant
4008                    points lists in the Line structs.
4009                 */
4010
4011                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4012
4013                         uint32_t const N = i->line->npoints ();
4014                         for (uint32_t j = 0; j < N; ++j) {
4015
4016                                 /* here's a control point on this line */
4017                                 ControlPoint* p = i->line->nth (j);
4018                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4019
4020                                 /* see if it's inside a range */
4021                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4022                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4023                                         ++k;
4024                                 }
4025
4026                                 if (k != _ranges.end()) {
4027                                         /* dragging this point */
4028                                         _nothing_to_drag = false;
4029                                         i->points.push_back (p);
4030                                 }
4031                         }
4032                 }
4033         }
4034
4035         if (_nothing_to_drag) {
4036                 return;
4037         }
4038
4039         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4040                 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4041         }
4042 }
4043
4044 void
4045 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4046 {
4047         if (_nothing_to_drag) {
4048                 return;
4049         }
4050
4051         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4052                 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4053
4054                 /* we are ignoring x position for this drag, so we can just pass in anything */
4055                 i->line->drag_motion (0, f, true, false);
4056         }
4057 }
4058
4059 void
4060 AutomationRangeDrag::finished (GdkEvent* event, bool)
4061 {
4062         if (_nothing_to_drag) {
4063                 return;
4064         }
4065         
4066         motion (event, false);
4067         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4068                 i->line->end_drag ();
4069                 i->line->clear_always_in_view ();
4070         }
4071
4072         _editor->session()->commit_reversible_command ();
4073 }
4074
4075 void
4076 AutomationRangeDrag::aborted (bool)
4077 {
4078         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4079                 i->line->clear_always_in_view ();
4080                 i->line->reset ();
4081         }
4082 }
4083
4084 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4085         : view (v)
4086 {
4087         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4088         layer = v->region()->layer ();
4089         initial_y = v->get_canvas_group()->property_y ();
4090         initial_playlist = v->region()->playlist ();
4091         initial_position = v->region()->position ();
4092         initial_end = v->region()->position () + v->region()->length ();
4093 }
4094
4095 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4096         : Drag (e, i)
4097         , _region_view (r)
4098         , _patch_change (i)
4099         , _cumulative_dx (0)
4100 {
4101         DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4102 }
4103
4104 void
4105 PatchChangeDrag::motion (GdkEvent* ev, bool)
4106 {
4107         framepos_t f = adjusted_current_frame (ev);
4108         boost::shared_ptr<Region> r = _region_view->region ();
4109         f = max (f, r->position ());
4110         f = min (f, r->last_frame ());
4111         
4112         framecnt_t const dxf = f - grab_frame();
4113         double const dxu = _editor->frame_to_unit (dxf);
4114         _patch_change->move (dxu - _cumulative_dx, 0);
4115         _cumulative_dx = dxu;
4116 }
4117
4118 void
4119 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4120 {
4121         if (!movement_occurred) {
4122                 return;
4123         }
4124
4125         boost::shared_ptr<Region> r (_region_view->region ());
4126         
4127         framepos_t f = adjusted_current_frame (ev);
4128         f = max (f, r->position ());
4129         f = min (f, r->last_frame ());
4130         
4131         _region_view->move_patch_change (
4132                 *_patch_change,
4133                 _region_view->frames_to_beats (f - r->position() - r->start())
4134                 );
4135 }
4136
4137 void
4138 PatchChangeDrag::aborted (bool)
4139 {
4140         _patch_change->move (-_cumulative_dx, 0);
4141 }
4142
4143 void
4144 PatchChangeDrag::setup_pointer_frame_offset ()
4145 {
4146         boost::shared_ptr<Region> region = _region_view->region ();
4147         _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();
4148 }
4149