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