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