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