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