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