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