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