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