Allow drags of automation in time ranges where the automation is on a MIDI track...
[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         
2653         _point->line().end_drag ();
2654         _editor->session()->commit_reversible_command ();
2655 }
2656
2657 void
2658 ControlPointDrag::aborted ()
2659 {
2660         _point->line().reset ();
2661 }
2662
2663 bool
2664 ControlPointDrag::active (Editing::MouseMode m)
2665 {
2666         if (m == Editing::MouseGain) {
2667                 /* always active in mouse gain */
2668                 return true;
2669         }
2670
2671         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2672         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2673 }
2674
2675 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2676         : Drag (e, i),
2677           _line (0),
2678           _cumulative_y_drag (0)
2679 {
2680         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2681 }
2682
2683 void
2684 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2685 {
2686         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2687         assert (_line);
2688
2689         _item = &_line->grab_item ();
2690
2691         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2692            origin, and ditto for y.
2693         */
2694
2695         double cx = event->button.x;
2696         double cy = event->button.y;
2697
2698         _line->parent_group().w2i (cx, cy);
2699
2700         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2701
2702         uint32_t before;
2703         uint32_t after;
2704         
2705         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2706                 /* no adjacent points */
2707                 return;
2708         }
2709
2710         Drag::start_grab (event, _editor->fader_cursor);
2711
2712         /* store grab start in parent frame */
2713
2714         _fixed_grab_x = cx;
2715         _fixed_grab_y = cy;
2716
2717         double fraction = 1.0 - (cy / _line->height());
2718
2719         _line->start_drag_line (before, after, fraction);
2720
2721         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2722                                             event->button.x + 10, event->button.y + 10);
2723
2724         _editor->show_verbose_canvas_cursor ();
2725 }
2726
2727 void
2728 LineDrag::motion (GdkEvent* event, bool)
2729 {
2730         double dy = _drags->current_pointer_y() - last_pointer_y();
2731
2732         if (event->button.state & Keyboard::SecondaryModifier) {
2733                 dy *= 0.1;
2734         }
2735
2736         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2737
2738         _cumulative_y_drag = cy - _fixed_grab_y;
2739
2740         cy = max (0.0, cy);
2741         cy = min ((double) _line->height(), cy);
2742
2743         double const fraction = 1.0 - (cy / _line->height());
2744
2745         bool push;
2746
2747         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2748                 push = false;
2749         } else {
2750                 push = true;
2751         }
2752
2753         /* we are ignoring x position for this drag, so we can just pass in anything */
2754         _line->drag_motion (0, fraction, true, push);
2755
2756         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2757 }
2758
2759 void
2760 LineDrag::finished (GdkEvent* event, bool)
2761 {
2762         motion (event, false);
2763         _line->end_drag ();
2764         _editor->session()->commit_reversible_command ();
2765 }
2766
2767 void
2768 LineDrag::aborted ()
2769 {
2770         _line->reset ();
2771 }
2772
2773 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2774         : Drag (e, i),
2775           _line (0),
2776           _cumulative_x_drag (0)
2777 {
2778         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2779 }
2780
2781 void
2782 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2783 {
2784         Drag::start_grab (event);
2785         
2786         _line = reinterpret_cast<SimpleLine*> (_item);
2787         assert (_line);
2788
2789         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2790
2791         double cx = event->button.x;
2792         double cy = event->button.y;
2793
2794         _item->property_parent().get_value()->w2i(cx, cy);
2795
2796         /* store grab start in parent frame */
2797         _region_view_grab_x = cx;
2798         
2799         _before = _line->property_x1();
2800         
2801         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2802                 
2803         _max_x = _editor->frame_to_pixel(_arv->get_duration());
2804 }
2805
2806 void
2807 FeatureLineDrag::motion (GdkEvent* event, bool)
2808 {
2809         double dx = _drags->current_pointer_x() - last_pointer_x();
2810         
2811         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2812         
2813         _cumulative_x_drag += dx;
2814                 
2815         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2816         
2817         if (cx > _max_x){
2818                 cx = _max_x;
2819         }
2820         else if(cx < 0){
2821                 cx = 0;
2822         }
2823         
2824         _line->property_x1() = cx; 
2825         _line->property_x2() = cx;
2826
2827         _before = _line->property_x1();
2828 }
2829
2830 void
2831 FeatureLineDrag::finished (GdkEvent* event, bool)
2832 {
2833         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2834         _arv->update_transient(_before, _line->property_x1());
2835 }
2836
2837 void
2838 FeatureLineDrag::aborted ()
2839 {
2840         //_line->reset ();
2841 }
2842
2843 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2844         : Drag (e, i)
2845 {
2846         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2847 }
2848
2849 void
2850 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2851 {
2852         Drag::start_grab (event);
2853         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2854 }
2855
2856 void
2857 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2858 {
2859         framepos_t start;
2860         framepos_t end;
2861         double y1;
2862         double y2;
2863
2864         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2865
2866         framepos_t grab = grab_frame ();
2867         if (Config->get_rubberbanding_snaps_to_grid ()) {
2868                 _editor->snap_to_with_modifier (grab, event);
2869         }
2870
2871         /* base start and end on initial click position */
2872
2873         if (pf < grab) {
2874                 start = pf;
2875                 end = grab;
2876         } else {
2877                 end = pf;
2878                 start = grab;
2879         }
2880
2881         if (_drags->current_pointer_y() < grab_y()) {
2882                 y1 = _drags->current_pointer_y();
2883                 y2 = grab_y();
2884         } else {
2885                 y2 = _drags->current_pointer_y();
2886                 y1 = grab_y();
2887         }
2888
2889
2890         if (start != end || y1 != y2) {
2891
2892                 double x1 = _editor->frame_to_pixel (start);
2893                 double x2 = _editor->frame_to_pixel (end);
2894
2895                 _editor->rubberband_rect->property_x1() = x1;
2896                 _editor->rubberband_rect->property_y1() = y1;
2897                 _editor->rubberband_rect->property_x2() = x2;
2898                 _editor->rubberband_rect->property_y2() = y2;
2899
2900                 _editor->rubberband_rect->show();
2901                 _editor->rubberband_rect->raise_to_top();
2902
2903                 _editor->show_verbose_time_cursor (pf, 10);
2904         }
2905 }
2906
2907 void
2908 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2909 {
2910         if (movement_occurred) {
2911
2912                 motion (event, false);
2913
2914                 double y1,y2;
2915                 if (_drags->current_pointer_y() < grab_y()) {
2916                         y1 = _drags->current_pointer_y();
2917                         y2 = grab_y();
2918                 } else {
2919                         y2 = _drags->current_pointer_y();
2920                         y1 = grab_y();
2921                 }
2922
2923
2924                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2925                 bool committed;
2926
2927                 _editor->begin_reversible_command (_("rubberband selection"));
2928
2929                 if (grab_frame() < last_pointer_frame()) {
2930                         committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2931                 } else {
2932                         committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2933                 }
2934
2935                 if (!committed) {
2936                         _editor->commit_reversible_command ();
2937                 }
2938
2939         } else {
2940                 if (!getenv("ARDOUR_SAE")) {
2941                         _editor->selection->clear_tracks();
2942                 }
2943                 _editor->selection->clear_regions();
2944                 _editor->selection->clear_points ();
2945                 _editor->selection->clear_lines ();
2946         }
2947
2948         _editor->rubberband_rect->hide();
2949 }
2950
2951 void
2952 RubberbandSelectDrag::aborted ()
2953 {
2954         _editor->rubberband_rect->hide ();
2955 }
2956
2957 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
2958         : RegionDrag (e, i, p, v)
2959 {
2960         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
2961 }
2962
2963 void
2964 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2965 {
2966         Drag::start_grab (event, cursor);
2967
2968         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2969 }
2970
2971 void
2972 TimeFXDrag::motion (GdkEvent* event, bool)
2973 {
2974         RegionView* rv = _primary;
2975
2976         framepos_t const pf = adjusted_current_frame (event);
2977
2978         if (pf > rv->region()->position()) {
2979                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2980         }
2981
2982         _editor->show_verbose_time_cursor (pf, 10);
2983 }
2984
2985 void
2986 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2987 {
2988         _primary->get_time_axis_view().hide_timestretch ();
2989
2990         if (!movement_occurred) {
2991                 return;
2992         }
2993
2994         if (last_pointer_frame() < _primary->region()->position()) {
2995                 /* backwards drag of the left edge - not usable */
2996                 return;
2997         }
2998
2999         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3000
3001         float percentage = (double) newlen / (double) _primary->region()->length();
3002
3003 #ifndef USE_RUBBERBAND
3004         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3005         if (_primary->region()->data_type() == DataType::AUDIO) {
3006                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3007         }
3008 #endif
3009
3010         _editor->begin_reversible_command (_("timestretch"));
3011
3012         // XXX how do timeFX on multiple regions ?
3013
3014         RegionSelection rs;
3015         rs.add (_primary);
3016
3017         if (_editor->time_stretch (rs, percentage) == -1) {
3018                 error << _("An error occurred while executing time stretch operation") << endmsg;
3019         }
3020 }
3021
3022 void
3023 TimeFXDrag::aborted ()
3024 {
3025         _primary->get_time_axis_view().hide_timestretch ();
3026 }
3027
3028 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3029         : Drag (e, i)
3030 {
3031         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3032 }
3033
3034 void
3035 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3036 {
3037         Drag::start_grab (event);
3038 }
3039
3040 void
3041 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3042 {
3043         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3044 }
3045
3046 void
3047 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3048 {
3049         if (movement_occurred && _editor->session()) {
3050                 /* make sure we stop */
3051                 _editor->session()->request_transport_speed (0.0);
3052         }
3053 }
3054
3055 void
3056 ScrubDrag::aborted ()
3057 {
3058         /* XXX: TODO */
3059 }
3060
3061 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3062         : Drag (e, i)
3063         , _operation (o)
3064         , _copy (false)
3065         , _original_pointer_time_axis (-1)
3066         , _last_pointer_time_axis (-1)
3067 {
3068         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3069 }
3070
3071 void
3072 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3073 {
3074         framepos_t start = 0;
3075         framepos_t end = 0;
3076
3077         if (_editor->session() == 0) {
3078                 return;
3079         }
3080
3081         Gdk::Cursor* cursor = 0;
3082
3083         switch (_operation) {
3084         case CreateSelection:
3085                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3086                         _copy = true;
3087                 } else {
3088                         _copy = false;
3089                 }
3090                 cursor = _editor->selector_cursor;
3091                 Drag::start_grab (event, cursor);
3092                 break;
3093
3094         case SelectionStartTrim:
3095                 if (_editor->clicked_axisview) {
3096                         _editor->clicked_axisview->order_selection_trims (_item, true);
3097                 }
3098                 Drag::start_grab (event, _editor->left_side_trim_cursor);
3099                 start = _editor->selection->time[_editor->clicked_selection].start;
3100                 _pointer_frame_offset = raw_grab_frame() - start;
3101                 break;
3102
3103         case SelectionEndTrim:
3104                 if (_editor->clicked_axisview) {
3105                         _editor->clicked_axisview->order_selection_trims (_item, false);
3106                 }
3107                 Drag::start_grab (event, _editor->right_side_trim_cursor);
3108                 end = _editor->selection->time[_editor->clicked_selection].end;
3109                 _pointer_frame_offset = raw_grab_frame() - end;
3110                 break;
3111
3112         case SelectionMove:
3113                 start = _editor->selection->time[_editor->clicked_selection].start;
3114                 Drag::start_grab (event, cursor);
3115                 _pointer_frame_offset = raw_grab_frame() - start;
3116                 break;
3117         }
3118
3119         if (_operation == SelectionMove) {
3120                 _editor->show_verbose_time_cursor (start, 10);
3121         } else {
3122                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3123         }
3124
3125         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3126 }
3127
3128 void
3129 SelectionDrag::motion (GdkEvent* event, bool first_move)
3130 {
3131         framepos_t start = 0;
3132         framepos_t end = 0;
3133         framecnt_t length;
3134
3135         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3136         if (pending_time_axis.first == 0) {
3137                 return;
3138         }
3139         
3140         framepos_t const pending_position = adjusted_current_frame (event);
3141
3142         /* only alter selection if things have changed */
3143
3144         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3145                 return;
3146         }
3147
3148         switch (_operation) {
3149         case CreateSelection:
3150         {
3151                 framepos_t grab = grab_frame ();
3152
3153                 if (first_move) {
3154                         _editor->snap_to (grab);
3155                 }
3156
3157                 if (pending_position < grab_frame()) {
3158                         start = pending_position;
3159                         end = grab;
3160                 } else {
3161                         end = pending_position;
3162                         start = grab;
3163                 }
3164
3165                 /* first drag: Either add to the selection
3166                    or create a new selection
3167                 */
3168
3169                 if (first_move) {
3170
3171                         if (_copy) {
3172                                 /* adding to the selection */
3173                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3174                                 //_editor->selection->add (_editor->clicked_axisview);
3175                                 _editor->clicked_selection = _editor->selection->add (start, end);
3176                                 _copy = false;
3177                         } else {
3178                                 /* new selection */
3179
3180                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3181                                         //_editor->selection->set (_editor->clicked_axisview);
3182                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3183                                 }
3184                                 
3185                                 _editor->clicked_selection = _editor->selection->set (start, end);
3186                         }
3187                 }
3188
3189                 /* select the track that we're in */
3190                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3191                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3192                         _editor->selection->add (pending_time_axis.first);
3193                         _added_time_axes.push_back (pending_time_axis.first);
3194                 }
3195
3196                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3197                    tracks that we selected in the first place.
3198                 */
3199                 
3200                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3201                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3202
3203                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3204                 while (i != _added_time_axes.end()) {
3205
3206                         list<TimeAxisView*>::iterator tmp = i;
3207                         ++tmp;
3208                         
3209                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3210                                 _editor->selection->remove (*i);
3211                                 _added_time_axes.remove (*i);
3212                         }
3213
3214                         i = tmp;
3215                 }
3216
3217         }
3218         break;
3219
3220         case SelectionStartTrim:
3221
3222                 start = _editor->selection->time[_editor->clicked_selection].start;
3223                 end = _editor->selection->time[_editor->clicked_selection].end;
3224
3225                 if (pending_position > end) {
3226                         start = end;
3227                 } else {
3228                         start = pending_position;
3229                 }
3230                 break;
3231
3232         case SelectionEndTrim:
3233
3234                 start = _editor->selection->time[_editor->clicked_selection].start;
3235                 end = _editor->selection->time[_editor->clicked_selection].end;
3236
3237                 if (pending_position < start) {
3238                         end = start;
3239                 } else {
3240                         end = pending_position;
3241                 }
3242
3243                 break;
3244
3245         case SelectionMove:
3246
3247                 start = _editor->selection->time[_editor->clicked_selection].start;
3248                 end = _editor->selection->time[_editor->clicked_selection].end;
3249
3250                 length = end - start;
3251
3252                 start = pending_position;
3253                 _editor->snap_to (start);
3254
3255                 end = start + length;
3256
3257                 break;
3258         }
3259
3260         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3261                 _editor->start_canvas_autoscroll (1, 0);
3262         }
3263
3264         if (start != end) {
3265                 _editor->selection->replace (_editor->clicked_selection, start, end);
3266         }
3267
3268         if (_operation == SelectionMove) {
3269                 _editor->show_verbose_time_cursor(start, 10);
3270         } else {
3271                 _editor->show_verbose_time_cursor(pending_position, 10);
3272         }
3273 }
3274
3275 void
3276 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3277 {
3278         Session* s = _editor->session();
3279
3280         if (movement_occurred) {
3281                 motion (event, false);
3282                 /* XXX this is not object-oriented programming at all. ick */
3283                 if (_editor->selection->time.consolidate()) {
3284                         _editor->selection->TimeChanged ();
3285                 }
3286
3287                 /* XXX what if its a music time selection? */
3288                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3289                         s->request_play_range (&_editor->selection->time, true);
3290                 }
3291
3292
3293         } else {
3294                 /* just a click, no pointer movement.*/
3295
3296                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3297                         _editor->selection->clear_time();
3298                 }
3299
3300                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3301                         _editor->selection->set (_editor->clicked_axisview);
3302                 }
3303                 
3304                 if (s && s->get_play_range () && s->transport_rolling()) {
3305                         s->request_stop (false, false);
3306                 }
3307
3308         }
3309
3310         _editor->stop_canvas_autoscroll ();
3311 }
3312
3313 void
3314 SelectionDrag::aborted ()
3315 {
3316         /* XXX: TODO */
3317 }
3318
3319 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3320         : Drag (e, i),
3321           _operation (o),
3322           _copy (false)
3323 {
3324         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3325         
3326         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, 
3327                                                    physical_screen_height (_editor->get_window()));
3328         _drag_rect->hide ();
3329
3330         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3331         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3332 }
3333
3334 void
3335 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3336 {
3337         if (_editor->session() == 0) {
3338                 return;
3339         }
3340
3341         Gdk::Cursor* cursor = 0;
3342
3343         if (!_editor->temp_location) {
3344                 _editor->temp_location = new Location (*_editor->session());
3345         }
3346
3347         switch (_operation) {
3348         case CreateRangeMarker:
3349         case CreateTransportMarker:
3350         case CreateCDMarker:
3351
3352                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3353                         _copy = true;
3354                 } else {
3355                         _copy = false;
3356                 }
3357                 cursor = _editor->selector_cursor;
3358                 break;
3359         }
3360
3361         Drag::start_grab (event, cursor);
3362
3363         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3364 }
3365
3366 void
3367 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3368 {
3369         framepos_t start = 0;
3370         framepos_t end = 0;
3371         ArdourCanvas::SimpleRect *crect;
3372
3373         switch (_operation) {
3374         case CreateRangeMarker:
3375                 crect = _editor->range_bar_drag_rect;
3376                 break;
3377         case CreateTransportMarker:
3378                 crect = _editor->transport_bar_drag_rect;
3379                 break;
3380         case CreateCDMarker:
3381                 crect = _editor->cd_marker_bar_drag_rect;
3382                 break;
3383         default:
3384                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3385                 return;
3386                 break;
3387         }
3388
3389         framepos_t const pf = adjusted_current_frame (event);
3390
3391         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3392                 framepos_t grab = grab_frame ();
3393                 _editor->snap_to (grab);
3394                 
3395                 if (pf < grab_frame()) {
3396                         start = pf;
3397                         end = grab;
3398                 } else {
3399                         end = pf;
3400                         start = grab;
3401                 }
3402
3403                 /* first drag: Either add to the selection
3404                    or create a new selection.
3405                 */
3406
3407                 if (first_move) {
3408
3409                         _editor->temp_location->set (start, end);
3410
3411                         crect->show ();
3412
3413                         update_item (_editor->temp_location);
3414                         _drag_rect->show();
3415                         //_drag_rect->raise_to_top();
3416
3417                 }
3418         }
3419
3420         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3421                 _editor->start_canvas_autoscroll (1, 0);
3422         }
3423
3424         if (start != end) {
3425                 _editor->temp_location->set (start, end);
3426
3427                 double x1 = _editor->frame_to_pixel (start);
3428                 double x2 = _editor->frame_to_pixel (end);
3429                 crect->property_x1() = x1;
3430                 crect->property_x2() = x2;
3431
3432                 update_item (_editor->temp_location);
3433         }
3434
3435         _editor->show_verbose_time_cursor (pf, 10);
3436
3437 }
3438
3439 void
3440 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3441 {
3442         Location * newloc = 0;
3443         string rangename;
3444         int flags;
3445
3446         if (movement_occurred) {
3447                 motion (event, false);
3448                 _drag_rect->hide();
3449
3450                 switch (_operation) {
3451                 case CreateRangeMarker:
3452                 case CreateCDMarker:
3453                     {
3454                         _editor->begin_reversible_command (_("new range marker"));
3455                         XMLNode &before = _editor->session()->locations()->get_state();
3456                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3457                         if (_operation == CreateCDMarker) {
3458                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3459                                 _editor->cd_marker_bar_drag_rect->hide();
3460                         }
3461                         else {
3462                                 flags = Location::IsRangeMarker;
3463                                 _editor->range_bar_drag_rect->hide();
3464                         }
3465                         newloc = new Location (
3466                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3467                                 );
3468                         
3469                         _editor->session()->locations()->add (newloc, true);
3470                         XMLNode &after = _editor->session()->locations()->get_state();
3471                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3472                         _editor->commit_reversible_command ();
3473                         break;
3474                     }
3475
3476                 case CreateTransportMarker:
3477                         // popup menu to pick loop or punch
3478                         _editor->new_transport_marker_context_menu (&event->button, _item);
3479                         break;
3480                 }
3481         } else {
3482                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3483
3484                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3485
3486                         framepos_t start;
3487                         framepos_t end;
3488
3489                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3490
3491                         if (end == max_frames) {
3492                                 end = _editor->session()->current_end_frame ();
3493                         }
3494
3495                         if (start == max_frames) {
3496                                 start = _editor->session()->current_start_frame ();
3497                         }
3498
3499                         switch (_editor->mouse_mode) {
3500                         case MouseObject:
3501                                 /* find the two markers on either side and then make the selection from it */
3502                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3503                                 break;
3504
3505                         case MouseRange:
3506                                 /* find the two markers on either side of the click and make the range out of it */
3507                                 _editor->selection->set (start, end);
3508                                 break;
3509
3510                         default:
3511                                 break;
3512                         }
3513                 }
3514         }
3515
3516         _editor->stop_canvas_autoscroll ();
3517 }
3518
3519 void
3520 RangeMarkerBarDrag::aborted ()
3521 {
3522         /* XXX: TODO */
3523 }
3524
3525 void
3526 RangeMarkerBarDrag::update_item (Location* location)
3527 {
3528         double const x1 = _editor->frame_to_pixel (location->start());
3529         double const x2 = _editor->frame_to_pixel (location->end());
3530
3531         _drag_rect->property_x1() = x1;
3532         _drag_rect->property_x2() = x2;
3533 }
3534
3535 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3536         : Drag (e, i)
3537 {
3538         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3539 }
3540
3541 void
3542 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3543 {
3544         Drag::start_grab (event, _editor->zoom_cursor);
3545         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3546 }
3547
3548 void
3549 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3550 {
3551         framepos_t start;
3552         framepos_t end;
3553
3554         framepos_t const pf = adjusted_current_frame (event);
3555
3556         framepos_t grab = grab_frame ();
3557         _editor->snap_to_with_modifier (grab, event);
3558
3559         /* base start and end on initial click position */
3560         if (pf < grab) {
3561                 start = pf;
3562                 end = grab;
3563         } else {
3564                 end = pf;
3565                 start = grab;
3566         }
3567
3568         if (start != end) {
3569
3570                 if (first_move) {
3571                         _editor->zoom_rect->show();
3572                         _editor->zoom_rect->raise_to_top();
3573                 }
3574
3575                 _editor->reposition_zoom_rect(start, end);
3576
3577                 _editor->show_verbose_time_cursor (pf, 10);
3578         }
3579 }
3580
3581 void
3582 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3583 {
3584         if (movement_occurred) {
3585                 motion (event, false);
3586
3587                 if (grab_frame() < last_pointer_frame()) {
3588                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3589                 } else {
3590                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3591                 }
3592         } else {
3593                 _editor->temporal_zoom_to_frame (false, grab_frame());
3594                 /*
3595                 temporal_zoom_step (false);
3596                 center_screen (grab_frame());
3597                 */
3598         }
3599
3600         _editor->zoom_rect->hide();
3601 }
3602
3603 void
3604 MouseZoomDrag::aborted ()
3605 {
3606         _editor->zoom_rect->hide ();
3607 }
3608
3609 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3610         : Drag (e, i)
3611         , _cumulative_dx (0)
3612         , _cumulative_dy (0)
3613 {
3614         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3615
3616         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3617         _region = &_primary->region_view ();
3618         _note_height = _region->midi_stream_view()->note_height ();
3619 }
3620
3621 void
3622 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3623 {
3624         Drag::start_grab (event);
3625
3626         if (!(_was_selected = _primary->selected())) {
3627
3628                 /* tertiary-click means extend selection - we'll do that on button release,
3629                    so don't add it here, because otherwise we make it hard to figure
3630                    out the "extend-to" range.
3631                 */
3632
3633                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3634
3635                 if (!extend) {
3636                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3637
3638                         if (add) {
3639                                 _region->note_selected (_primary, true);
3640                         } else {
3641                                 _region->unique_select (_primary);
3642                         }
3643                 }
3644         }
3645 }
3646
3647 /** @return Current total drag x change in frames */
3648 frameoffset_t
3649 NoteDrag::total_dx () const
3650 {
3651         /* dx in frames */
3652         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3653
3654         /* primary note time */
3655         frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3656         
3657         /* new time of the primary note relative to the region position */
3658         frameoffset_t const st = n + dx;
3659
3660         /* snap and return corresponding delta */
3661         return _region->snap_frame_to_frame (st) - n;
3662 }
3663
3664 /** @return Current total drag y change in notes */
3665 int8_t
3666 NoteDrag::total_dy () const
3667 {
3668         /* this is `backwards' to make increasing note number go in the right direction */
3669         double const dy = _drags->current_pointer_y() - grab_y();
3670
3671         /* dy in notes */
3672         int8_t ndy = 0;
3673
3674         if (abs (dy) >= _note_height) {
3675                 if (dy > 0) {
3676                         ndy = (int8_t) ceil (dy / _note_height / 2.0);
3677                 } else {
3678                         ndy = (int8_t) floor (dy / _note_height / 2.0);
3679                 }
3680         }
3681
3682         return ndy;
3683 }
3684         
3685
3686 void
3687 NoteDrag::motion (GdkEvent *, bool)
3688 {
3689         /* Total change in x and y since the start of the drag */
3690         frameoffset_t const dx = total_dx ();
3691         int8_t const dy = total_dy ();
3692
3693         /* Now work out what we have to do to the note canvas items to set this new drag delta */
3694         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3695         double const tdy = dy * _note_height - _cumulative_dy;
3696
3697         if (tdx || tdy) {
3698                 _region->move_selection (tdx, tdy);
3699                 _cumulative_dx += tdx;
3700                 _cumulative_dy += tdy;
3701
3702                 char buf[12];
3703                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3704                           (int) floor (_primary->note()->note() + dy));
3705                 
3706                 _editor->show_verbose_canvas_cursor_with (buf);
3707         }
3708 }
3709
3710 void
3711 NoteDrag::finished (GdkEvent* ev, bool moved)
3712 {
3713         if (!moved) {
3714                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3715
3716                         if (_was_selected) {
3717                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3718                                 if (add) {
3719                                         _region->note_deselected (_primary);
3720                                 }
3721                         } else {
3722                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3723                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3724
3725                                 if (!extend && !add && _region->selection_size() > 1) {
3726                                         _region->unique_select (_primary);
3727                                 } else if (extend) {
3728                                         _region->note_selected (_primary, true, true);
3729                                 } else {
3730                                         /* it was added during button press */
3731                                 }
3732                         }
3733                 }
3734         } else {
3735                 _region->note_dropped (_primary, total_dx(), - total_dy());
3736         }
3737 }
3738
3739 void
3740 NoteDrag::aborted ()
3741 {
3742         /* XXX: TODO */
3743 }
3744
3745 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3746         : Drag (editor, item)
3747         , _ranges (r)
3748         , _nothing_to_drag (false)
3749 {
3750         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3751         
3752         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3753         assert (_atav);
3754
3755         /* get all lines in the automation view */
3756         list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3757
3758         /* find those that overlap the ranges being dragged */
3759         list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3760         while (i != lines.end ()) {
3761                 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3762                 ++j;
3763
3764                 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3765
3766                 /* check this range against all the AudioRanges that we are using */
3767                 list<AudioRange>::const_iterator k = _ranges.begin ();
3768                 while (k != _ranges.end()) {
3769                         if (k->coverage (r.first, r.second) != OverlapNone) {
3770                                 break;
3771                         }
3772                         ++k;
3773                 }
3774
3775                 /* add it to our list if it overlaps at all */
3776                 if (k != _ranges.end()) {
3777                         Line n;
3778                         n.line = *i;
3779                         n.state = 0;
3780                         n.range = r;
3781                         _lines.push_back (n);
3782                 }
3783
3784                 i = j;
3785         }
3786
3787         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3788 }
3789
3790 void
3791 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3792 {
3793         Drag::start_grab (event, cursor);
3794
3795         /* Get line states before we start changing things */
3796         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3797                 i->state = &i->line->get_state ();
3798         }
3799
3800         if (_ranges.empty()) {
3801
3802                 /* No selected time ranges: drag all points */
3803                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3804                         uint32_t const N = i->line->npoints ();
3805                         for (uint32_t j = 0; j < N; ++j) {
3806                                 i->points.push_back (i->line->nth (j));
3807                         }
3808                 }
3809                 
3810         } else {
3811
3812                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3813
3814                         framecnt_t const half = (i->start + i->end) / 2;
3815                         
3816                         /* find the line that this audio range starts in */
3817                         list<Line>::iterator j = _lines.begin();
3818                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3819                                 ++j;
3820                         }
3821
3822                         if (j != _lines.end()) {
3823                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3824                                 
3825                                 /* j is the line that this audio range starts in; fade into it;
3826                                    64 samples length plucked out of thin air.
3827                                 */
3828
3829                                 framepos_t a = i->start + 64;
3830                                 if (a > half) {
3831                                         a = half;
3832                                 }
3833
3834                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3835                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3836
3837                                 the_list->add (p, the_list->eval (p));
3838                                 j->line->add_always_in_view (p);
3839                                 the_list->add (q, the_list->eval (q));
3840                                 j->line->add_always_in_view (q);
3841                         }
3842
3843                         /* same thing for the end */
3844                         
3845                         j = _lines.begin();
3846                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3847                                 ++j;
3848                         }
3849
3850                         if (j != _lines.end()) {
3851                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3852                                 
3853                                 /* j is the line that this audio range starts in; fade out of it;
3854                                    64 samples length plucked out of thin air.
3855                                 */
3856                                 
3857                                 framepos_t b = i->end - 64;
3858                                 if (b < half) {
3859                                         b = half;
3860                                 }
3861
3862                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3863                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3864                                 
3865                                 the_list->add (p, the_list->eval (p));
3866                                 j->line->add_always_in_view (p);
3867                                 the_list->add (q, the_list->eval (q));
3868                                 j->line->add_always_in_view (q);
3869                         }
3870                 }
3871
3872                 _nothing_to_drag = true;
3873
3874                 /* Find all the points that should be dragged and put them in the relevant
3875                    points lists in the Line structs.
3876                 */
3877
3878                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3879
3880                         uint32_t const N = i->line->npoints ();
3881                         for (uint32_t j = 0; j < N; ++j) {
3882
3883                                 /* here's a control point on this line */
3884                                 ControlPoint* p = i->line->nth (j);
3885                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3886
3887                                 /* see if it's inside a range */
3888                                 list<AudioRange>::const_iterator k = _ranges.begin ();
3889                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3890                                         ++k;
3891                                 }
3892
3893                                 if (k != _ranges.end()) {
3894                                         /* dragging this point */
3895                                         _nothing_to_drag = false;
3896                                         i->points.push_back (p);
3897                                 }
3898                         }
3899                 }
3900         }
3901
3902         if (_nothing_to_drag) {
3903                 return;
3904         }
3905
3906         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3907                 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3908         }
3909 }
3910
3911 void
3912 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3913 {
3914         if (_nothing_to_drag) {
3915                 return;
3916         }
3917
3918         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3919                 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3920
3921                 /* we are ignoring x position for this drag, so we can just pass in anything */
3922                 i->line->drag_motion (0, f, true, false);
3923         }
3924 }
3925
3926 void
3927 AutomationRangeDrag::finished (GdkEvent* event, bool)
3928 {
3929         if (_nothing_to_drag) {
3930                 return;
3931         }
3932         
3933         motion (event, false);
3934         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3935                 i->line->end_drag ();
3936                 i->line->clear_always_in_view ();
3937         }
3938
3939         _editor->session()->commit_reversible_command ();
3940 }
3941
3942 void
3943 AutomationRangeDrag::aborted ()
3944 {
3945         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3946                 i->line->clear_always_in_view ();
3947                 i->line->reset ();
3948         }
3949 }
3950
3951 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3952         : view (v)
3953 {
3954         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3955         layer = v->region()->layer ();
3956         initial_y = v->get_canvas_group()->property_y ();
3957         initial_playlist = v->region()->playlist ();
3958 }