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