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