Suspend transport timecode transmission during playhead drag. Should fix #3324.
[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 (true, 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* /*ignored*/)
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 const region_start = region->get_position_pixels();
1603         double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1604
1605         if (grab_x() <= middle_point) {
1606                 cursor = _editor->left_side_trim_cursor;
1607                 at_front = true;
1608         } else {
1609                 cursor = _editor->right_side_trim_cursor;
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 (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), 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 (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), 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 const region_start = (nframes64_t) (_primary->region()->position() / speed);
1703         nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1704         nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1705
1706         nframes64_t const pf = adjusted_current_frame (event);
1707
1708         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1709                 _operation = ContentsTrim;
1710                 Drag::start_grab (event, _editor->trimmer_cursor);
1711         } else {
1712                 /* These will get overridden for a point trim.*/
1713                 if (pf < (region_start + region_length/2)) {
1714                         /* closer to start */
1715                         _operation = StartTrim;
1716                         Drag::start_grab (event, _editor->left_side_trim_cursor);
1717                 } else {
1718                         /* closer to end */
1719                         _operation = EndTrim;
1720                         Drag::start_grab (event, _editor->right_side_trim_cursor);
1721                 }
1722         }
1723
1724         switch (_operation) {
1725         case StartTrim:
1726                 _editor->show_verbose_time_cursor (region_start, 10);
1727                 break;
1728         case EndTrim:
1729                 _editor->show_verbose_time_cursor (region_end, 10);
1730                 break;
1731         case ContentsTrim:
1732                 _editor->show_verbose_time_cursor (pf, 10);
1733                 break;
1734         }
1735 }
1736
1737 void
1738 TrimDrag::motion (GdkEvent* event, bool first_move)
1739 {
1740         RegionView* rv = _primary;
1741
1742         /* snap modifier works differently here..
1743            its current state has to be passed to the
1744            various trim functions in order to work properly
1745         */
1746
1747         double speed = 1.0;
1748         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1749         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1750         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1751
1752         if (tv && tv->is_track()) {
1753                 speed = tv->track()->speed();
1754         }
1755
1756         nframes64_t const pf = adjusted_current_frame (event);
1757
1758         if (first_move) {
1759
1760                 string trim_type;
1761
1762                 switch (_operation) {
1763                 case StartTrim:
1764                         trim_type = "Region start trim";
1765                         break;
1766                 case EndTrim:
1767                         trim_type = "Region end trim";
1768                         break;
1769                 case ContentsTrim:
1770                         trim_type = "Region content trim";
1771                         break;
1772                 }
1773
1774                 _editor->begin_reversible_command (trim_type);
1775                 _have_transaction = true;
1776
1777                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1778                         RegionView* rv = i->view;
1779                         rv->fake_set_opaque(false);
1780                         rv->enable_display (false);
1781                         rv->region()->clear_history ();
1782                         rv->region()->suspend_property_changes ();
1783
1784                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1785
1786                         if (arv){
1787                                 arv->temporarily_hide_envelope ();
1788                         }
1789
1790                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1791                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1792
1793                         if (insert_result.second) {
1794                                 pl->freeze();
1795                         }
1796                 }
1797         }
1798
1799         bool non_overlap_trim = false;
1800
1801         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1802                 non_overlap_trim = true;
1803         }
1804
1805         switch (_operation) {
1806         case StartTrim:
1807                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1808                         _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1809                 }
1810                 break;
1811
1812         case EndTrim:
1813                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1814                         _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1815                 }
1816                 break;
1817
1818         case ContentsTrim:
1819                 {
1820                         bool swap_direction = false;
1821
1822                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1823                                 swap_direction = true;
1824                         }
1825
1826                         nframes64_t frame_delta = 0;
1827                         
1828                         bool left_direction = false;
1829                         if (last_pointer_frame() > pf) {
1830                                 left_direction = true;
1831                         }
1832
1833                         if (left_direction) {
1834                                 frame_delta = (last_pointer_frame() - pf);
1835                         } else {
1836                                 frame_delta = (pf - last_pointer_frame());
1837                         }
1838
1839                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1840                                 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1841                         }
1842                 }
1843                 break;
1844         }
1845
1846         switch (_operation) {
1847         case StartTrim:
1848                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1849                 break;
1850         case EndTrim:
1851                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1852                 break;
1853         case ContentsTrim:
1854                 _editor->show_verbose_time_cursor (pf, 10);
1855                 break;
1856         }
1857 }
1858
1859
1860 void
1861 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1862 {
1863         if (movement_occurred) {
1864                 motion (event, false);
1865
1866                 if (!_editor->selection->selected (_primary)) {
1867                         _editor->thaw_region_after_trim (*_primary);
1868                 } else {
1869
1870                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1871                                 _editor->thaw_region_after_trim (*i->view);
1872                                 i->view->enable_display (true);
1873                                 i->view->fake_set_opaque (true);
1874                                 if (_have_transaction) {
1875                                         _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1876                                 }
1877                         }
1878                 }
1879                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1880                         (*p)->thaw ();
1881                 }
1882
1883                 _editor->motion_frozen_playlists.clear ();
1884
1885                 if (_have_transaction) {
1886                         _editor->commit_reversible_command();
1887                 }
1888
1889         } else {
1890                 /* no mouse movement */
1891                 _editor->point_trim (event, adjusted_current_frame (event));
1892         }
1893 }
1894
1895 void
1896 TrimDrag::aborted ()
1897 {
1898         /* Our motion method is changing model state, so use the Undo system
1899            to cancel.  Perhaps not ideal, as this will leave an Undo point
1900            behind which may be slightly odd from the user's point of view.
1901         */
1902
1903         finished (0, true);
1904         
1905         if (_have_transaction) {
1906                 _editor->undo ();
1907         }
1908 }
1909
1910 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1911         : Drag (e, i),
1912           _copy (c)
1913 {
1914         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1915         assert (_marker);
1916 }
1917
1918 void
1919 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1920 {
1921         if (_copy) {
1922                 // create a dummy marker for visual representation of moving the copy.
1923                 // The actual copying is not done before we reach the finish callback.
1924                 char name[64];
1925                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1926                 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1927                                                           *new MeterSection (_marker->meter()));
1928
1929                 _item = &new_marker->the_item ();
1930                 _marker = new_marker;
1931
1932         } else {
1933
1934                 MetricSection& section (_marker->meter());
1935
1936                 if (!section.movable()) {
1937                         return;
1938                 }
1939
1940         }
1941
1942         Drag::start_grab (event, cursor);
1943
1944         _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1945
1946         _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1947 }
1948
1949 void
1950 MeterMarkerDrag::motion (GdkEvent* event, bool)
1951 {
1952         nframes64_t const pf = adjusted_current_frame (event);
1953
1954         _marker->set_position (pf);
1955         
1956         _editor->show_verbose_time_cursor (pf, 10);
1957 }
1958
1959 void
1960 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1961 {
1962         if (!movement_occurred) {
1963                 return;
1964         }
1965
1966         motion (event, false);
1967
1968         BBT_Time when;
1969
1970         TempoMap& map (_editor->session()->tempo_map());
1971         map.bbt_time (last_pointer_frame(), when);
1972
1973         if (_copy == true) {
1974                 _editor->begin_reversible_command (_("copy meter mark"));
1975                 XMLNode &before = map.get_state();
1976                 map.add_meter (_marker->meter(), when);
1977                 XMLNode &after = map.get_state();
1978                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1979                 _editor->commit_reversible_command ();
1980
1981                 // delete the dummy marker we used for visual representation of copying.
1982                 // a new visual marker will show up automatically.
1983                 delete _marker;
1984         } else {
1985                 _editor->begin_reversible_command (_("move meter mark"));
1986                 XMLNode &before = map.get_state();
1987                 map.move_meter (_marker->meter(), when);
1988                 XMLNode &after = map.get_state();
1989                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1990                 _editor->commit_reversible_command ();
1991         }
1992 }
1993
1994 void
1995 MeterMarkerDrag::aborted ()
1996 {
1997         _marker->set_position (_marker->meter().frame ());
1998 }
1999
2000 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2001         : Drag (e, i),
2002           _copy (c)
2003 {
2004         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2005         assert (_marker);
2006 }
2007
2008 void
2009 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2010 {
2011
2012         if (_copy) {
2013
2014                 // create a dummy marker for visual representation of moving the copy.
2015                 // The actual copying is not done before we reach the finish callback.
2016                 char name[64];
2017                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2018                 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2019                                                           *new TempoSection (_marker->tempo()));
2020
2021                 _item = &new_marker->the_item ();
2022                 _marker = new_marker;
2023
2024         } else {
2025
2026                 MetricSection& section (_marker->tempo());
2027
2028                 if (!section.movable()) {
2029                         return;
2030                 }
2031         }
2032
2033         Drag::start_grab (event, cursor);
2034
2035         _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2036         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2037 }
2038
2039 void
2040 TempoMarkerDrag::motion (GdkEvent* event, bool)
2041 {
2042         nframes64_t const pf = adjusted_current_frame (event);
2043         _marker->set_position (pf);
2044         _editor->show_verbose_time_cursor (pf, 10);
2045 }
2046
2047 void
2048 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2049 {
2050         if (!movement_occurred) {
2051                 return;
2052         }
2053
2054         motion (event, false);
2055
2056         BBT_Time when;
2057
2058         TempoMap& map (_editor->session()->tempo_map());
2059         map.bbt_time (last_pointer_frame(), when);
2060
2061         if (_copy == true) {
2062                 _editor->begin_reversible_command (_("copy tempo mark"));
2063                 XMLNode &before = map.get_state();
2064                 map.add_tempo (_marker->tempo(), when);
2065                 XMLNode &after = map.get_state();
2066                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2067                 _editor->commit_reversible_command ();
2068
2069                 // delete the dummy marker we used for visual representation of copying.
2070                 // a new visual marker will show up automatically.
2071                 delete _marker;
2072         } else {
2073                 _editor->begin_reversible_command (_("move tempo mark"));
2074                 XMLNode &before = map.get_state();
2075                 map.move_tempo (_marker->tempo(), when);
2076                 XMLNode &after = map.get_state();
2077                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2078                 _editor->commit_reversible_command ();
2079         }
2080 }
2081
2082 void
2083 TempoMarkerDrag::aborted ()
2084 {
2085         _marker->set_position (_marker->tempo().frame());
2086 }
2087
2088 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2089         : Drag (e, i),
2090           _stop (s)
2091 {
2092         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2093         assert (_cursor);
2094 }
2095
2096 void
2097 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2098 {
2099         Drag::start_grab (event, c);
2100
2101         if (!_stop) {
2102
2103                 nframes64_t where = _editor->event_frame (event, 0, 0);
2104
2105                 _editor->snap_to_with_modifier (where, event);
2106                 _editor->playhead_cursor->set_position (where);
2107
2108         }
2109
2110         if (_cursor == _editor->playhead_cursor) {
2111                 _editor->_dragging_playhead = true;
2112
2113                 Session* s = _editor->session ();
2114
2115                 if (s) {
2116                         if (_was_rolling && _stop) {
2117                                 s->request_stop ();
2118                         }
2119
2120                         if (s->is_auditioning()) {
2121                                 s->cancel_audition ();
2122                         }
2123
2124                         s->request_suspend_timecode_transmission ();
2125
2126                         if (s->timecode_transmission_suspended ()) {
2127                                 nframes64_t const f = _editor->playhead_cursor->current_frame;
2128                                 s->send_mmc_locate (f);
2129                                 s->send_full_time_code (f);
2130                         }
2131                 }
2132         }
2133
2134         _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2135
2136         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2137 }
2138
2139 void
2140 CursorDrag::motion (GdkEvent* event, bool)
2141 {
2142         nframes64_t const adjusted_frame = adjusted_current_frame (event);
2143
2144         if (adjusted_frame == last_pointer_frame()) {
2145                 return;
2146         }
2147
2148         _cursor->set_position (adjusted_frame);
2149
2150         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2151
2152         Session* s = _editor->session ();
2153         if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2154                 nframes64_t const f = _editor->playhead_cursor->current_frame;
2155                 s->send_mmc_locate (f);
2156                 s->send_full_time_code (f);
2157         }
2158         
2159
2160 #ifdef GTKOSX
2161         _editor->update_canvas_now ();
2162 #endif
2163         _editor->UpdateAllTransportClocks (_cursor->current_frame);
2164 }
2165
2166 void
2167 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2168 {
2169         _editor->_dragging_playhead = false;
2170
2171         if (!movement_occurred && _stop) {
2172                 return;
2173         }
2174
2175         motion (event, false);
2176
2177         if (_item == &_editor->playhead_cursor->canvas_item) {
2178                 Session* s = _editor->session ();
2179                 if (s) {
2180                         s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2181                         _editor->_pending_locate_request = true;
2182                         s->request_resume_timecode_transmission ();
2183                 }
2184         }
2185 }
2186
2187 void
2188 CursorDrag::aborted ()
2189 {
2190         if (_editor->_dragging_playhead) {
2191                 _editor->session()->request_resume_timecode_transmission ();
2192                 _editor->_dragging_playhead = false;
2193         }
2194         
2195         _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2196 }
2197
2198 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2199         : RegionDrag (e, i, p, v)
2200 {
2201
2202 }
2203
2204 void
2205 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2206 {
2207         Drag::start_grab (event, cursor);
2208
2209         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2210         boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2211
2212         _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2213         _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2214         
2215 }
2216
2217 void
2218 FadeInDrag::motion (GdkEvent* event, bool)
2219 {
2220         nframes64_t fade_length;
2221
2222         nframes64_t const pos = adjusted_current_frame (event);
2223
2224         boost::shared_ptr<Region> region = _primary->region ();
2225
2226         if (pos < (region->position() + 64)) {
2227                 fade_length = 64; // this should be a minimum defined somewhere
2228         } else if (pos > region->last_frame()) {
2229                 fade_length = region->length();
2230         } else {
2231                 fade_length = pos - region->position();
2232         }
2233
2234         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2235
2236                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2237
2238                 if (!tmp) {
2239                         continue;
2240                 }
2241
2242                 tmp->reset_fade_in_shape_width (fade_length);
2243         }
2244
2245         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2246 }
2247
2248 void
2249 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2250 {
2251         if (!movement_occurred) {
2252                 return;
2253         }
2254
2255         nframes64_t fade_length;
2256
2257         nframes64_t const pos = adjusted_current_frame (event);
2258
2259         boost::shared_ptr<Region> region = _primary->region ();
2260
2261         if (pos < (region->position() + 64)) {
2262                 fade_length = 64; // this should be a minimum defined somewhere
2263         } else if (pos > region->last_frame()) {
2264                 fade_length = region->length();
2265         } else {
2266                 fade_length = pos - region->position();
2267         }
2268
2269         _editor->begin_reversible_command (_("change fade in length"));
2270
2271         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2272
2273                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2274
2275                 if (!tmp) {
2276                         continue;
2277                 }
2278
2279                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2280                 XMLNode &before = alist->get_state();
2281
2282                 tmp->audio_region()->set_fade_in_length (fade_length);
2283                 tmp->audio_region()->set_fade_in_active (true);
2284
2285                 XMLNode &after = alist->get_state();
2286                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2287         }
2288
2289         _editor->commit_reversible_command ();
2290 }
2291
2292 void
2293 FadeInDrag::aborted ()
2294 {
2295         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2296                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2297
2298                 if (!tmp) {
2299                         continue;
2300                 }
2301
2302                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2303         }
2304 }
2305
2306 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2307         : RegionDrag (e, i, p, v)
2308 {
2309
2310 }
2311
2312 void
2313 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2314 {
2315         Drag::start_grab (event, cursor);
2316
2317         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2318         boost::shared_ptr<AudioRegion> r = a->audio_region ();
2319
2320         _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2321         _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2322 }
2323
2324 void
2325 FadeOutDrag::motion (GdkEvent* event, bool)
2326 {
2327         nframes64_t fade_length;
2328
2329         nframes64_t const pos = adjusted_current_frame (event);
2330
2331         boost::shared_ptr<Region> region = _primary->region ();
2332
2333         if (pos > (region->last_frame() - 64)) {
2334                 fade_length = 64; // this should really be a minimum fade defined somewhere
2335         }
2336         else if (pos < region->position()) {
2337                 fade_length = region->length();
2338         }
2339         else {
2340                 fade_length = region->last_frame() - pos;
2341         }
2342
2343         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2344
2345                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2346
2347                 if (!tmp) {
2348                         continue;
2349                 }
2350
2351                 tmp->reset_fade_out_shape_width (fade_length);
2352         }
2353
2354         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2355 }
2356
2357 void
2358 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2359 {
2360         if (!movement_occurred) {
2361                 return;
2362         }
2363
2364         nframes64_t fade_length;
2365
2366         nframes64_t const pos = adjusted_current_frame (event);
2367
2368         boost::shared_ptr<Region> region = _primary->region ();
2369
2370         if (pos > (region->last_frame() - 64)) {
2371                 fade_length = 64; // this should really be a minimum fade defined somewhere
2372         }
2373         else if (pos < region->position()) {
2374                 fade_length = region->length();
2375         }
2376         else {
2377                 fade_length = region->last_frame() - pos;
2378         }
2379
2380         _editor->begin_reversible_command (_("change fade out length"));
2381
2382         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2383
2384                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2385
2386                 if (!tmp) {
2387                         continue;
2388                 }
2389
2390                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2391                 XMLNode &before = alist->get_state();
2392
2393                 tmp->audio_region()->set_fade_out_length (fade_length);
2394                 tmp->audio_region()->set_fade_out_active (true);
2395
2396                 XMLNode &after = alist->get_state();
2397                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2398         }
2399
2400         _editor->commit_reversible_command ();
2401 }
2402
2403 void
2404 FadeOutDrag::aborted ()
2405 {
2406         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2407                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2408
2409                 if (!tmp) {
2410                         continue;
2411                 }
2412
2413                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2414         }
2415 }
2416
2417 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2418         : Drag (e, i)
2419 {
2420         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2421         assert (_marker);
2422
2423         _points.push_back (Gnome::Art::Point (0, 0));
2424         _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2425
2426         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2427         _line->property_width_pixels() = 1;
2428         _line->property_points () = _points;
2429         _line->hide ();
2430
2431         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2432 }
2433
2434 MarkerDrag::~MarkerDrag ()
2435 {
2436         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2437                 delete *i;
2438         }
2439 }
2440
2441 void
2442 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2443 {
2444         Drag::start_grab (event, cursor);
2445
2446         bool is_start;
2447
2448         Location *location = _editor->find_location_from_marker (_marker, is_start);
2449         _editor->_dragging_edit_point = true;
2450
2451         _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2452
2453         update_item (location);
2454
2455         // _drag_line->show();
2456         // _line->raise_to_top();
2457
2458         if (is_start) {
2459                 _editor->show_verbose_time_cursor (location->start(), 10);
2460         } else {
2461                 _editor->show_verbose_time_cursor (location->end(), 10);
2462         }
2463
2464         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2465
2466         switch (op) {
2467         case Selection::Toggle:
2468                 _editor->selection->toggle (_marker);
2469                 break;
2470         case Selection::Set:
2471                 if (!_editor->selection->selected (_marker)) {
2472                         _editor->selection->set (_marker);
2473                 }
2474                 break;
2475         case Selection::Extend:
2476         {
2477                 Locations::LocationList ll;
2478                 list<Marker*> to_add;
2479                 nframes64_t s, e;
2480                 _editor->selection->markers.range (s, e);
2481                 s = min (_marker->position(), s);
2482                 e = max (_marker->position(), e);
2483                 s = min (s, e);
2484                 e = max (s, e);
2485                 if (e < max_frames) {
2486                         ++e;
2487                 }
2488                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2489                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2490                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2491                         if (lm) {
2492                                 if (lm->start) {
2493                                         to_add.push_back (lm->start);
2494                                 }
2495                                 if (lm->end) {
2496                                         to_add.push_back (lm->end);
2497                                 }
2498                         }
2499                 }
2500                 if (!to_add.empty()) {
2501                         _editor->selection->add (to_add);
2502                 }
2503                 break;
2504         }
2505         case Selection::Add:
2506                 _editor->selection->add (_marker);
2507                 break;
2508         }
2509
2510         /* Set up copies for us to manipulate during the drag */
2511
2512         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2513                 Location* l = _editor->find_location_from_marker (*i, is_start);
2514                 _copied_locations.push_back (new Location (*l));
2515         }
2516 }
2517
2518 void
2519 MarkerDrag::motion (GdkEvent* event, bool)
2520 {
2521         nframes64_t f_delta = 0;
2522         bool is_start;
2523         bool move_both = false;
2524         Marker* marker;
2525         Location *real_location;
2526         Location *copy_location = 0;
2527
2528         nframes64_t const newframe = adjusted_current_frame (event);
2529
2530         nframes64_t next = newframe;
2531
2532         if (newframe == last_pointer_frame()) {
2533                 return;
2534         }
2535
2536         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2537                 move_both = true;
2538         }
2539
2540         MarkerSelection::iterator i;
2541         list<Location*>::iterator x;
2542
2543         /* find the marker we're dragging, and compute the delta */
2544
2545         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2546              x != _copied_locations.end() && i != _editor->selection->markers.end();
2547              ++i, ++x) {
2548
2549                 copy_location = *x;
2550                 marker = *i;
2551
2552                 if (marker == _marker) {
2553
2554                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2555                                 /* que pasa ?? */
2556                                 return;
2557                         }
2558
2559                         if (real_location->is_mark()) {
2560                                 f_delta = newframe - copy_location->start();
2561                         } else {
2562
2563
2564                                 switch (marker->type()) {
2565                                 case Marker::Start:
2566                                 case Marker::LoopStart:
2567                                 case Marker::PunchIn:
2568                                         f_delta = newframe - copy_location->start();
2569                                         break;
2570
2571                                 case Marker::End:
2572                                 case Marker::LoopEnd:
2573                                 case Marker::PunchOut:
2574                                         f_delta = newframe - copy_location->end();
2575                                         break;
2576                                 default:
2577                                         /* what kind of marker is this ? */
2578                                         return;
2579                                 }
2580                         }
2581                         break;
2582                 }
2583         }
2584
2585         if (i == _editor->selection->markers.end()) {
2586                 /* hmm, impossible - we didn't find the dragged marker */
2587                 return;
2588         }
2589
2590         /* now move them all */
2591
2592         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2593              x != _copied_locations.end() && i != _editor->selection->markers.end();
2594              ++i, ++x) {
2595
2596                 copy_location = *x;
2597                 marker = *i;
2598
2599                 /* call this to find out if its the start or end */
2600
2601                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2602                         continue;
2603                 }
2604
2605                 if (real_location->locked()) {
2606                         continue;
2607                 }
2608
2609                 if (copy_location->is_mark()) {
2610
2611                         /* now move it */
2612
2613                         copy_location->set_start (copy_location->start() + f_delta);
2614
2615                 } else {
2616
2617                         nframes64_t new_start = copy_location->start() + f_delta;
2618                         nframes64_t new_end = copy_location->end() + f_delta;
2619
2620                         if (is_start) { // start-of-range marker
2621
2622                                 if (move_both) {
2623                                         copy_location->set_start (new_start);
2624                                         copy_location->set_end (new_end);
2625                                 } else  if (new_start < copy_location->end()) {
2626                                         copy_location->set_start (new_start);
2627                                 } else {
2628                                         _editor->snap_to (next, 1, true);
2629                                         copy_location->set_end (next);
2630                                         copy_location->set_start (newframe);
2631                                 }
2632
2633                         } else { // end marker
2634
2635                                 if (move_both) {
2636                                         copy_location->set_end (new_end);
2637                                         copy_location->set_start (new_start);
2638                                 } else if (new_end > copy_location->start()) {
2639                                         copy_location->set_end (new_end);
2640                                 } else if (newframe > 0) {
2641                                         _editor->snap_to (next, -1, true);
2642                                         copy_location->set_start (next);
2643                                         copy_location->set_end (newframe);
2644                                 }
2645                         }
2646                 }
2647
2648                 update_item (copy_location);
2649
2650                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2651
2652                 if (lm) {
2653                         lm->set_position (copy_location->start(), copy_location->end());
2654                 }
2655         }
2656
2657         assert (!_copied_locations.empty());
2658
2659         _editor->show_verbose_time_cursor (newframe, 10);
2660
2661 #ifdef GTKOSX
2662         _editor->update_canvas_now ();
2663 #endif
2664 }
2665
2666 void
2667 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2668 {
2669         if (!movement_occurred) {
2670
2671                 /* just a click, do nothing but finish
2672                    off the selection process
2673                 */
2674
2675                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2676
2677                 switch (op) {
2678                 case Selection::Set:
2679                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2680                                 _editor->selection->set (_marker);
2681                         }
2682                         break;
2683
2684                 case Selection::Toggle:
2685                 case Selection::Extend:
2686                 case Selection::Add:
2687                         break;
2688                 }
2689
2690                 return;
2691         }
2692
2693         _editor->_dragging_edit_point = false;
2694
2695         _editor->begin_reversible_command ( _("move marker") );
2696         XMLNode &before = _editor->session()->locations()->get_state();
2697
2698         MarkerSelection::iterator i;
2699         list<Location*>::iterator x;
2700         bool is_start;
2701
2702         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2703              x != _copied_locations.end() && i != _editor->selection->markers.end();
2704              ++i, ++x) {
2705
2706                 Location * location = _editor->find_location_from_marker (*i, is_start);
2707
2708                 if (location) {
2709
2710                         if (location->locked()) {
2711                                 return;
2712                         }
2713
2714                         if (location->is_mark()) {
2715                                 location->set_start ((*x)->start());
2716                         } else {
2717                                 location->set ((*x)->start(), (*x)->end());
2718                         }
2719                 }
2720         }
2721
2722         XMLNode &after = _editor->session()->locations()->get_state();
2723         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2724         _editor->commit_reversible_command ();
2725
2726         _line->hide();
2727 }
2728
2729 void
2730 MarkerDrag::aborted ()
2731 {
2732         /* XXX: TODO */
2733 }
2734
2735 void
2736 MarkerDrag::update_item (Location* location)
2737 {
2738         double const x1 = _editor->frame_to_pixel (location->start());
2739
2740         _points.front().set_x(x1);
2741         _points.back().set_x(x1);
2742         _line->property_points() = _points;
2743 }
2744
2745 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2746         : Drag (e, i),
2747           _cumulative_x_drag (0),
2748           _cumulative_y_drag (0)
2749 {
2750         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2751         assert (_point);
2752 }
2753
2754
2755 void
2756 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2757 {
2758         Drag::start_grab (event, _editor->fader_cursor);
2759
2760         // start the grab at the center of the control point so
2761         // the point doesn't 'jump' to the mouse after the first drag
2762         _time_axis_view_grab_x = _point->get_x();
2763         _time_axis_view_grab_y = _point->get_y();
2764
2765         float const fraction = 1 - (_point->get_y() / _point->line().height());
2766
2767         _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2768
2769         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2770                                             event->button.x + 10, event->button.y + 10);
2771
2772         _editor->show_verbose_canvas_cursor ();
2773 }
2774
2775 void
2776 ControlPointDrag::motion (GdkEvent* event, bool)
2777 {
2778         double dx = _drags->current_pointer_x() - last_pointer_x();
2779         double dy = _drags->current_pointer_y() - last_pointer_y();
2780
2781         if (event->button.state & Keyboard::SecondaryModifier) {
2782                 dx *= 0.1;
2783                 dy *= 0.1;
2784         }
2785
2786         /* coordinate in TimeAxisView's space */
2787         double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2788         double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2789
2790         // calculate zero crossing point. back off by .01 to stay on the
2791         // positive side of zero
2792         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2793
2794         // make sure we hit zero when passing through
2795         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2796                 cy = zero_gain_y;
2797         }
2798
2799         if (_x_constrained) {
2800                 cx = _time_axis_view_grab_x;
2801         }
2802         if (_y_constrained) {
2803                 cy = _time_axis_view_grab_y;
2804         }
2805
2806         _cumulative_x_drag = cx - _time_axis_view_grab_x;
2807         _cumulative_y_drag = cy - _time_axis_view_grab_y;
2808
2809         cx = max (0.0, cx);
2810         cy = max (0.0, cy);
2811         cy = min ((double) _point->line().height(), cy);
2812
2813         nframes64_t cx_frames = _editor->unit_to_frame (cx);
2814
2815         if (!_x_constrained) {
2816                 _editor->snap_to_with_modifier (cx_frames, event);
2817         }
2818
2819         float const fraction = 1.0 - (cy / _point->line().height());
2820
2821         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2822
2823         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2824
2825         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2826 }
2827
2828 void
2829 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2830 {
2831         if (!movement_occurred) {
2832
2833                 /* just a click */
2834
2835                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2836                         _editor->reset_point_selection ();
2837                 }
2838
2839         } else {
2840                 motion (event, false);
2841         }
2842         _point->line().end_drag ();
2843 }
2844
2845 void
2846 ControlPointDrag::aborted ()
2847 {
2848         _point->line().reset ();
2849 }
2850
2851 bool
2852 ControlPointDrag::active (Editing::MouseMode m)
2853 {
2854         if (m == Editing::MouseGain) {
2855                 /* always active in mouse gain */
2856                 return true;
2857         }
2858
2859         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2860         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2861 }
2862
2863 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2864         : Drag (e, i),
2865           _line (0),
2866           _cumulative_y_drag (0)
2867 {
2868
2869 }
2870 void
2871 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2872 {
2873         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2874         assert (_line);
2875
2876         _item = &_line->grab_item ();
2877
2878         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2879            origin, and ditto for y.
2880         */
2881
2882         double cx = event->button.x;
2883         double cy = event->button.y;
2884
2885         _line->parent_group().w2i (cx, cy);
2886
2887         nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2888
2889         uint32_t before;
2890         uint32_t after;
2891         
2892         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2893                 /* no adjacent points */
2894                 return;
2895         }
2896
2897         Drag::start_grab (event, _editor->fader_cursor);
2898
2899         /* store grab start in parent frame */
2900
2901         _time_axis_view_grab_x = cx;
2902         _time_axis_view_grab_y = cy;
2903
2904         double fraction = 1.0 - (cy / _line->height());
2905
2906         _line->start_drag_line (before, after, fraction);
2907
2908         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2909                                             event->button.x + 10, event->button.y + 10);
2910
2911         _editor->show_verbose_canvas_cursor ();
2912 }
2913
2914 void
2915 LineDrag::motion (GdkEvent* event, bool)
2916 {
2917         double dy = _drags->current_pointer_y() - last_pointer_y();
2918
2919         if (event->button.state & Keyboard::SecondaryModifier) {
2920                 dy *= 0.1;
2921         }
2922
2923         double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2924
2925         _cumulative_y_drag = cy - _time_axis_view_grab_y;
2926
2927         cy = max (0.0, cy);
2928         cy = min ((double) _line->height(), cy);
2929
2930         double const fraction = 1.0 - (cy / _line->height());
2931
2932         bool push;
2933
2934         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2935                 push = false;
2936         } else {
2937                 push = true;
2938         }
2939
2940         /* we are ignoring x position for this drag, so we can just pass in anything */
2941         _line->drag_motion (0, fraction, true, push);
2942
2943         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2944 }
2945
2946 void
2947 LineDrag::finished (GdkEvent* event, bool)
2948 {
2949         motion (event, false);
2950         _line->end_drag ();
2951 }
2952
2953 void
2954 LineDrag::aborted ()
2955 {
2956         _line->reset ();
2957 }
2958
2959 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2960         : Drag (e, i),
2961           _line (0),
2962           _cumulative_x_drag (0)
2963 {
2964
2965 }
2966 void
2967 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2968 {
2969   
2970         Drag::start_grab (event);
2971         
2972         _line = reinterpret_cast<SimpleLine*> (_item);
2973         assert (_line);
2974
2975         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2976
2977         double cx = event->button.x;
2978         double cy = event->button.y;
2979
2980         _item->property_parent().get_value()->w2i(cx, cy);
2981
2982         /* store grab start in parent frame */
2983         _region_view_grab_x = cx;
2984         
2985         _before = _line->property_x1();
2986         
2987         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2988                 
2989         _max_x = _editor->frame_to_pixel(_arv->get_duration());
2990 }
2991
2992 void
2993 FeatureLineDrag::motion (GdkEvent* event, bool)
2994 {
2995         double dx = _drags->current_pointer_x() - last_pointer_x();
2996         
2997         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2998         
2999         _cumulative_x_drag += dx;
3000                 
3001         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3002         
3003         if (cx > _max_x){
3004                 cx = _max_x;
3005         }
3006         else if(cx < 0){
3007                 cx = 0;
3008         }
3009         
3010         _line->property_x1() = cx; 
3011         _line->property_x2() = cx;
3012
3013         _before = _line->property_x1();
3014 }
3015
3016 void
3017 FeatureLineDrag::finished (GdkEvent* event, bool)
3018 {
3019         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3020         _arv->update_transient(_before, _line->property_x1());
3021 }
3022
3023 void
3024 FeatureLineDrag::aborted ()
3025 {
3026         //_line->reset ();
3027 }
3028
3029 void
3030 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3031 {
3032         Drag::start_grab (event);
3033         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3034 }
3035
3036 void
3037 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3038 {
3039         nframes64_t start;
3040         nframes64_t end;
3041         double y1;
3042         double y2;
3043
3044         nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3045
3046         nframes64_t grab = grab_frame ();
3047         if (Config->get_rubberbanding_snaps_to_grid ()) {
3048                 _editor->snap_to_with_modifier (grab, event);
3049         }
3050
3051         /* base start and end on initial click position */
3052
3053         if (pf < grab) {
3054                 start = pf;
3055                 end = grab;
3056         } else {
3057                 end = pf;
3058                 start = grab;
3059         }
3060
3061         if (_drags->current_pointer_y() < grab_y()) {
3062                 y1 = _drags->current_pointer_y();
3063                 y2 = grab_y();
3064         } else {
3065                 y2 = _drags->current_pointer_y();
3066                 y1 = grab_y();
3067         }
3068
3069
3070         if (start != end || y1 != y2) {
3071
3072                 double x1 = _editor->frame_to_pixel (start);
3073                 double x2 = _editor->frame_to_pixel (end);
3074
3075                 _editor->rubberband_rect->property_x1() = x1;
3076                 _editor->rubberband_rect->property_y1() = y1;
3077                 _editor->rubberband_rect->property_x2() = x2;
3078                 _editor->rubberband_rect->property_y2() = y2;
3079
3080                 _editor->rubberband_rect->show();
3081                 _editor->rubberband_rect->raise_to_top();
3082
3083                 _editor->show_verbose_time_cursor (pf, 10);
3084         }
3085 }
3086
3087 void
3088 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3089 {
3090         if (movement_occurred) {
3091
3092                 motion (event, false);
3093
3094                 double y1,y2;
3095                 if (_drags->current_pointer_y() < grab_y()) {
3096                         y1 = _drags->current_pointer_y();
3097                         y2 = grab_y();
3098                 } else {
3099                         y2 = _drags->current_pointer_y();
3100                         y1 = grab_y();
3101                 }
3102
3103
3104                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3105                 bool committed;
3106
3107                 _editor->begin_reversible_command (_("rubberband selection"));
3108
3109                 if (grab_frame() < last_pointer_frame()) {
3110                         committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3111                 } else {
3112                         committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3113                 }
3114
3115                 if (!committed) {
3116                         _editor->commit_reversible_command ();
3117                 }
3118
3119         } else {
3120                 if (!getenv("ARDOUR_SAE")) {
3121                         _editor->selection->clear_tracks();
3122                 }
3123                 _editor->selection->clear_regions();
3124                 _editor->selection->clear_points ();
3125                 _editor->selection->clear_lines ();
3126         }
3127
3128         _editor->rubberband_rect->hide();
3129 }
3130
3131 void
3132 RubberbandSelectDrag::aborted ()
3133 {
3134         _editor->rubberband_rect->hide ();
3135 }
3136
3137 void
3138 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3139 {
3140         Drag::start_grab (event);
3141
3142         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3143 }
3144
3145 void
3146 TimeFXDrag::motion (GdkEvent* event, bool)
3147 {
3148         RegionView* rv = _primary;
3149
3150         nframes64_t const pf = adjusted_current_frame (event);
3151
3152         if (pf > rv->region()->position()) {
3153                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3154         }
3155
3156         _editor->show_verbose_time_cursor (pf, 10);
3157 }
3158
3159 void
3160 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3161 {
3162         _primary->get_time_axis_view().hide_timestretch ();
3163
3164         if (!movement_occurred) {
3165                 return;
3166         }
3167
3168         if (last_pointer_frame() < _primary->region()->position()) {
3169                 /* backwards drag of the left edge - not usable */
3170                 return;
3171         }
3172
3173         nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3174
3175         float percentage = (double) newlen / (double) _primary->region()->length();
3176
3177 #ifndef USE_RUBBERBAND
3178         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3179         if (_primary->region()->data_type() == DataType::AUDIO) {
3180                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3181         }
3182 #endif
3183
3184         _editor->begin_reversible_command (_("timestretch"));
3185
3186         // XXX how do timeFX on multiple regions ?
3187
3188         RegionSelection rs;
3189         rs.add (_primary);
3190
3191         if (_editor->time_stretch (rs, percentage) == -1) {
3192                 error << _("An error occurred while executing time stretch operation") << endmsg;
3193         }
3194 }
3195
3196 void
3197 TimeFXDrag::aborted ()
3198 {
3199         _primary->get_time_axis_view().hide_timestretch ();
3200 }
3201
3202
3203 void
3204 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3205 {
3206         Drag::start_grab (event);
3207 }
3208
3209 void
3210 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3211 {
3212         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3213 }
3214
3215 void
3216 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3217 {
3218         if (movement_occurred && _editor->session()) {
3219                 /* make sure we stop */
3220                 _editor->session()->request_transport_speed (0.0);
3221         }
3222 }
3223
3224 void
3225 ScrubDrag::aborted ()
3226 {
3227         /* XXX: TODO */
3228 }
3229
3230 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3231         : Drag (e, i)
3232         , _operation (o)
3233         , _copy (false)
3234         , _original_pointer_time_axis (-1)
3235         , _last_pointer_time_axis (-1)
3236 {
3237
3238 }
3239
3240 void
3241 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3242 {
3243         nframes64_t start = 0;
3244         nframes64_t end = 0;
3245
3246         if (_editor->session() == 0) {
3247                 return;
3248         }
3249
3250         Gdk::Cursor* cursor = 0;
3251
3252         switch (_operation) {
3253         case CreateSelection:
3254                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3255                         _copy = true;
3256                 } else {
3257                         _copy = false;
3258                 }
3259                 cursor = _editor->selector_cursor;
3260                 Drag::start_grab (event, cursor);
3261                 break;
3262
3263         case SelectionStartTrim:
3264                 if (_editor->clicked_axisview) {
3265                         _editor->clicked_axisview->order_selection_trims (_item, true);
3266                 }
3267                 Drag::start_grab (event, _editor->left_side_trim_cursor);
3268                 start = _editor->selection->time[_editor->clicked_selection].start;
3269                 _pointer_frame_offset = grab_frame() - start;
3270                 break;
3271
3272         case SelectionEndTrim:
3273                 if (_editor->clicked_axisview) {
3274                         _editor->clicked_axisview->order_selection_trims (_item, false);
3275                 }
3276                 Drag::start_grab (event, _editor->right_side_trim_cursor);
3277                 end = _editor->selection->time[_editor->clicked_selection].end;
3278                 _pointer_frame_offset = grab_frame() - end;
3279                 break;
3280
3281         case SelectionMove:
3282                 start = _editor->selection->time[_editor->clicked_selection].start;
3283                 Drag::start_grab (event, cursor);
3284                 _pointer_frame_offset = grab_frame() - start;
3285                 break;
3286         }
3287
3288         if (_operation == SelectionMove) {
3289                 _editor->show_verbose_time_cursor (start, 10);
3290         } else {
3291                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3292         }
3293
3294         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3295 }
3296
3297 void
3298 SelectionDrag::motion (GdkEvent* event, bool first_move)
3299 {
3300         nframes64_t start = 0;
3301         nframes64_t end = 0;
3302         nframes64_t length;
3303
3304         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3305         if (pending_time_axis.first == 0) {
3306                 return;
3307         }
3308         
3309         nframes64_t const pending_position = adjusted_current_frame (event);
3310
3311         /* only alter selection if things have changed */
3312
3313         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3314                 return;
3315         }
3316
3317         switch (_operation) {
3318         case CreateSelection:
3319         {
3320                 nframes64_t grab = grab_frame ();
3321
3322                 if (first_move) {
3323                         _editor->snap_to (grab);
3324                 }
3325
3326                 if (pending_position < grab_frame()) {
3327                         start = pending_position;
3328                         end = grab;
3329                 } else {
3330                         end = pending_position;
3331                         start = grab;
3332                 }
3333
3334                 /* first drag: Either add to the selection
3335                    or create a new selection
3336                 */
3337
3338                 if (first_move) {
3339
3340                         if (_copy) {
3341                                 /* adding to the selection */
3342                                 _editor->selection->add (_editor->clicked_axisview);
3343                                 _editor->clicked_selection = _editor->selection->add (start, end);
3344                                 _copy = false;
3345                         } else {
3346                                 /* new selection */
3347
3348                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3349                                         _editor->selection->set (_editor->clicked_axisview);
3350                                 }
3351                                 
3352                                 _editor->clicked_selection = _editor->selection->set (start, end);
3353                         }
3354                 }
3355
3356                 /* select the track that we're in */
3357                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3358                         _editor->selection->add (pending_time_axis.first);
3359                         _added_time_axes.push_back (pending_time_axis.first);
3360                 }
3361
3362                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3363                    tracks that we selected in the first place.
3364                 */
3365                 
3366                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3367                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3368
3369                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3370                 while (i != _added_time_axes.end()) {
3371
3372                         list<TimeAxisView*>::iterator tmp = i;
3373                         ++tmp;
3374                         
3375                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3376                                 _editor->selection->remove (*i);
3377                                 _added_time_axes.remove (*i);
3378                         }
3379
3380                         i = tmp;
3381                 }
3382
3383         }
3384         break;
3385
3386         case SelectionStartTrim:
3387
3388                 start = _editor->selection->time[_editor->clicked_selection].start;
3389                 end = _editor->selection->time[_editor->clicked_selection].end;
3390
3391                 if (pending_position > end) {
3392                         start = end;
3393                 } else {
3394                         start = pending_position;
3395                 }
3396                 break;
3397
3398         case SelectionEndTrim:
3399
3400                 start = _editor->selection->time[_editor->clicked_selection].start;
3401                 end = _editor->selection->time[_editor->clicked_selection].end;
3402
3403                 if (pending_position < start) {
3404                         end = start;
3405                 } else {
3406                         end = pending_position;
3407                 }
3408
3409                 break;
3410
3411         case SelectionMove:
3412
3413                 start = _editor->selection->time[_editor->clicked_selection].start;
3414                 end = _editor->selection->time[_editor->clicked_selection].end;
3415
3416                 length = end - start;
3417
3418                 start = pending_position;
3419                 _editor->snap_to (start);
3420
3421                 end = start + length;
3422
3423                 break;
3424         }
3425
3426         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3427                 _editor->start_canvas_autoscroll (1, 0);
3428         }
3429
3430         if (start != end) {
3431                 _editor->selection->replace (_editor->clicked_selection, start, end);
3432         }
3433
3434         if (_operation == SelectionMove) {
3435                 _editor->show_verbose_time_cursor(start, 10);
3436         } else {
3437                 _editor->show_verbose_time_cursor(pending_position, 10);
3438         }
3439 }
3440
3441 void
3442 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3443 {
3444         Session* s = _editor->session();
3445
3446         if (movement_occurred) {
3447                 motion (event, false);
3448                 /* XXX this is not object-oriented programming at all. ick */
3449                 if (_editor->selection->time.consolidate()) {
3450                         _editor->selection->TimeChanged ();
3451                 }
3452
3453                 /* XXX what if its a music time selection? */
3454                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3455                         s->request_play_range (&_editor->selection->time, true);
3456                 }
3457
3458
3459         } else {
3460                 /* just a click, no pointer movement.*/
3461
3462                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3463                         _editor->selection->clear_time();
3464                 }
3465
3466                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3467                         _editor->selection->set (_editor->clicked_axisview);
3468                 }
3469                 
3470                 if (s && s->get_play_range () && s->transport_rolling()) {
3471                         s->request_stop (false, false);
3472                 }
3473
3474         }
3475
3476         _editor->stop_canvas_autoscroll ();
3477 }
3478
3479 void
3480 SelectionDrag::aborted ()
3481 {
3482         /* XXX: TODO */
3483 }
3484
3485 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3486         : Drag (e, i),
3487           _operation (o),
3488           _copy (false)
3489 {
3490         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3491         _drag_rect->hide ();
3492
3493         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3494         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3495 }
3496
3497 void
3498 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3499 {
3500         if (_editor->session() == 0) {
3501                 return;
3502         }
3503
3504         Gdk::Cursor* cursor = 0;
3505
3506         if (!_editor->temp_location) {
3507                 _editor->temp_location = new Location;
3508         }
3509
3510         switch (_operation) {
3511         case CreateRangeMarker:
3512         case CreateTransportMarker:
3513         case CreateCDMarker:
3514
3515                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3516                         _copy = true;
3517                 } else {
3518                         _copy = false;
3519                 }
3520                 cursor = _editor->selector_cursor;
3521                 break;
3522         }
3523
3524         Drag::start_grab (event, cursor);
3525
3526         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3527 }
3528
3529 void
3530 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3531 {
3532         nframes64_t start = 0;
3533         nframes64_t end = 0;
3534         ArdourCanvas::SimpleRect *crect;
3535
3536         switch (_operation) {
3537         case CreateRangeMarker:
3538                 crect = _editor->range_bar_drag_rect;
3539                 break;
3540         case CreateTransportMarker:
3541                 crect = _editor->transport_bar_drag_rect;
3542                 break;
3543         case CreateCDMarker:
3544                 crect = _editor->cd_marker_bar_drag_rect;
3545                 break;
3546         default:
3547                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3548                 return;
3549                 break;
3550         }
3551
3552         nframes64_t const pf = adjusted_current_frame (event);
3553
3554         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3555                 nframes64_t grab = grab_frame ();
3556                 _editor->snap_to (grab);
3557                 
3558                 if (pf < grab_frame()) {
3559                         start = pf;
3560                         end = grab;
3561                 } else {
3562                         end = pf;
3563                         start = grab;
3564                 }
3565
3566                 /* first drag: Either add to the selection
3567                    or create a new selection.
3568                 */
3569
3570                 if (first_move) {
3571
3572                         _editor->temp_location->set (start, end);
3573
3574                         crect->show ();
3575
3576                         update_item (_editor->temp_location);
3577                         _drag_rect->show();
3578                         //_drag_rect->raise_to_top();
3579
3580                 }
3581         }
3582
3583         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3584                 _editor->start_canvas_autoscroll (1, 0);
3585         }
3586
3587         if (start != end) {
3588                 _editor->temp_location->set (start, end);
3589
3590                 double x1 = _editor->frame_to_pixel (start);
3591                 double x2 = _editor->frame_to_pixel (end);
3592                 crect->property_x1() = x1;
3593                 crect->property_x2() = x2;
3594
3595                 update_item (_editor->temp_location);
3596         }
3597
3598         _editor->show_verbose_time_cursor (pf, 10);
3599
3600 }
3601
3602 void
3603 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3604 {
3605         Location * newloc = 0;
3606         string rangename;
3607         int flags;
3608
3609         if (movement_occurred) {
3610                 motion (event, false);
3611                 _drag_rect->hide();
3612
3613                 switch (_operation) {
3614                 case CreateRangeMarker:
3615                 case CreateCDMarker:
3616                     {
3617                         _editor->begin_reversible_command (_("new range marker"));
3618                         XMLNode &before = _editor->session()->locations()->get_state();
3619                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3620                         if (_operation == CreateCDMarker) {
3621                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3622                                 _editor->cd_marker_bar_drag_rect->hide();
3623                         }
3624                         else {
3625                                 flags = Location::IsRangeMarker;
3626                                 _editor->range_bar_drag_rect->hide();
3627                         }
3628                         newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3629                         _editor->session()->locations()->add (newloc, true);
3630                         XMLNode &after = _editor->session()->locations()->get_state();
3631                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3632                         _editor->commit_reversible_command ();
3633                         break;
3634                     }
3635
3636                 case CreateTransportMarker:
3637                         // popup menu to pick loop or punch
3638                         _editor->new_transport_marker_context_menu (&event->button, _item);
3639                         break;
3640                 }
3641         } else {
3642                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3643
3644                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3645
3646                         nframes64_t start;
3647                         nframes64_t end;
3648
3649                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3650
3651                         if (end == max_frames) {
3652                                 end = _editor->session()->current_end_frame ();
3653                         }
3654
3655                         if (start == max_frames) {
3656                                 start = _editor->session()->current_start_frame ();
3657                         }
3658
3659                         switch (_editor->mouse_mode) {
3660                         case MouseObject:
3661                                 /* find the two markers on either side and then make the selection from it */
3662                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3663                                 break;
3664
3665                         case MouseRange:
3666                                 /* find the two markers on either side of the click and make the range out of it */
3667                                 _editor->selection->set (start, end);
3668                                 break;
3669
3670                         default:
3671                                 break;
3672                         }
3673                 }
3674         }
3675
3676         _editor->stop_canvas_autoscroll ();
3677 }
3678
3679 void
3680 RangeMarkerBarDrag::aborted ()
3681 {
3682         /* XXX: TODO */
3683 }
3684
3685 void
3686 RangeMarkerBarDrag::update_item (Location* location)
3687 {
3688         double const x1 = _editor->frame_to_pixel (location->start());
3689         double const x2 = _editor->frame_to_pixel (location->end());
3690
3691         _drag_rect->property_x1() = x1;
3692         _drag_rect->property_x2() = x2;
3693 }
3694
3695 void
3696 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3697 {
3698         Drag::start_grab (event, _editor->zoom_cursor);
3699         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3700 }
3701
3702 void
3703 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3704 {
3705         nframes64_t start;
3706         nframes64_t end;
3707
3708         nframes64_t const pf = adjusted_current_frame (event);
3709
3710         nframes64_t grab = grab_frame ();
3711         _editor->snap_to_with_modifier (grab, event);
3712
3713         /* base start and end on initial click position */
3714         if (pf < grab) {
3715                 start = pf;
3716                 end = grab;
3717         } else {
3718                 end = pf;
3719                 start = grab;
3720         }
3721
3722         if (start != end) {
3723
3724                 if (first_move) {
3725                         _editor->zoom_rect->show();
3726                         _editor->zoom_rect->raise_to_top();
3727                 }
3728
3729                 _editor->reposition_zoom_rect(start, end);
3730
3731                 _editor->show_verbose_time_cursor (pf, 10);
3732         }
3733 }
3734
3735 void
3736 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3737 {
3738         if (movement_occurred) {
3739                 motion (event, false);
3740
3741                 if (grab_frame() < last_pointer_frame()) {
3742                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3743                 } else {
3744                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3745                 }
3746         } else {
3747                 _editor->temporal_zoom_to_frame (false, grab_frame());
3748                 /*
3749                 temporal_zoom_step (false);
3750                 center_screen (grab_frame());
3751                 */
3752         }
3753
3754         _editor->zoom_rect->hide();
3755 }
3756
3757 void
3758 MouseZoomDrag::aborted ()
3759 {
3760         _editor->zoom_rect->hide ();
3761 }
3762
3763 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3764         : Drag (e, i)
3765 {
3766         CanvasNoteEvent*     cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3767         region = &cnote->region_view();
3768 }
3769
3770 void
3771 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3772 {
3773         Drag::start_grab (event);
3774
3775         drag_delta_x = 0;
3776         drag_delta_note = 0;
3777
3778         double event_x;
3779         double event_y;
3780
3781         event_x = _drags->current_pointer_x();
3782         event_y = _drags->current_pointer_y();
3783
3784         _item->property_parent().get_value()->w2i(event_x, event_y);
3785
3786         last_x = region->snap_to_pixel(event_x);
3787         last_y = event_y;
3788
3789         CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3790
3791         if (!(was_selected = cnote->selected())) {
3792
3793                 /* tertiary-click means extend selection - we'll do that on button release,
3794                    so don't add it here, because otherwise we make it hard to figure
3795                    out the "extend-to" range.
3796                 */
3797
3798                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3799
3800                 if (!extend) {
3801                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3802
3803                         if (add) {
3804                                 region->note_selected (cnote, true);
3805                         } else {
3806                                 region->unique_select (cnote);
3807                         }
3808                 }
3809         }
3810 }
3811
3812 void
3813 NoteDrag::motion (GdkEvent*, bool)
3814 {
3815         MidiStreamView* streamview = region->midi_stream_view();
3816         double event_x;
3817         double event_y;
3818
3819         event_x = _drags->current_pointer_x();
3820         event_y = _drags->current_pointer_y();
3821
3822         _item->property_parent().get_value()->w2i(event_x, event_y);
3823
3824         event_x = region->snap_to_pixel(event_x);
3825
3826         double dx     = event_x - last_x;
3827         double dy     = event_y - last_y;
3828         last_x = event_x;
3829
3830         drag_delta_x += dx;
3831
3832         // Snap to note rows
3833
3834         if (abs (dy) < streamview->note_height()) {
3835                 dy = 0.0;
3836         } else {
3837                 int8_t this_delta_note;
3838                 if (dy > 0) {
3839                         this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3840                 } else {
3841                         this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3842                 }
3843                 drag_delta_note -= this_delta_note;
3844                 dy = streamview->note_height() * this_delta_note;
3845                 last_y = last_y + dy;
3846         }
3847
3848         if (dx || dy) {
3849                 
3850                 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3851                 Evoral::MusicalTime new_time;
3852                 
3853                 if (drag_delta_x) {
3854                         nframes64_t start_frames = region->beats_to_frames(cnote->note()->time());
3855                         if (drag_delta_x >= 0) {
3856                                 start_frames += region->snap_frame_to_frame(_editor->pixel_to_frame(drag_delta_x));
3857                         } else {
3858                                 start_frames -= region->snap_frame_to_frame(_editor->pixel_to_frame(-drag_delta_x));
3859                         }
3860                         new_time = region->frames_to_beats(start_frames);
3861                 } else {
3862                         new_time = cnote->note()->time();
3863                 }
3864                 
3865                 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > check_note (
3866                         new Evoral::Note<Evoral::MusicalTime> (cnote->note()->channel(), 
3867                                                                new_time,
3868                                                                cnote->note()->length(), 
3869                                                                cnote->note()->note() + drag_delta_note,
3870                                                                cnote->note()->velocity()));
3871
3872                 region->move_selection (dx, dy);
3873
3874                 char buf[12];
3875                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (cnote->note()->note()).c_str(),
3876                           (int) floor ((cnote->note()->note() + drag_delta_note)));
3877                 _editor->show_verbose_canvas_cursor_with (buf);
3878         }
3879 }
3880
3881 void
3882 NoteDrag::finished (GdkEvent* ev, bool moved)
3883 {
3884         ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3885
3886         if (!moved) {
3887                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3888
3889                         if (was_selected) {
3890                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3891                                 if (add) {
3892                                         region->note_deselected (cnote);
3893                                 }
3894                         } else {
3895                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3896                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3897
3898                                 if (!extend && !add && region->selection_size() > 1) {
3899                                         region->unique_select(cnote);
3900                                 } else if (extend) {
3901                                         region->note_selected (cnote, true, true);
3902                                 } else {
3903                                         /* it was added during button press */
3904                                 }
3905                         }
3906                 }
3907         } else {
3908                 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3909         }
3910 }
3911
3912 void
3913 NoteDrag::aborted ()
3914 {
3915         /* XXX: TODO */
3916 }
3917
3918 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3919         : Drag (e, i)
3920         , _ranges (r)
3921         , _nothing_to_drag (false)
3922 {
3923         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3924         assert (_atav);
3925
3926         _line = _atav->line ();
3927 }
3928
3929 void
3930 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3931 {
3932         Drag::start_grab (event, cursor);
3933
3934         list<ControlPoint*> points;
3935
3936         XMLNode* state = &_line->get_state ();
3937         
3938         if (_ranges.empty()) {
3939                 
3940                 uint32_t const N = _line->npoints ();
3941                 for (uint32_t i = 0; i < N; ++i) {
3942                         points.push_back (_line->nth (i));
3943                 }
3944                 
3945         } else {
3946
3947                 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3948                 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3949
3950                         /* fade into and out of the region that we're dragging;
3951                            64 samples length plucked out of thin air.
3952                         */
3953                         nframes64_t const h = (j->start + j->end) / 2;
3954                         nframes64_t a = j->start + 64;
3955                         if (a > h) {
3956                                 a = h;
3957                         }
3958                         nframes64_t b = j->end - 64;
3959                         if (b < h) {
3960                                 b = h;
3961                         }
3962                         
3963                         the_list->add (j->start, the_list->eval (j->start));
3964                         _line->add_always_in_view (j->start);
3965                         the_list->add (a, the_list->eval (a));
3966                         _line->add_always_in_view (a);
3967                         the_list->add (b, the_list->eval (b));
3968                         _line->add_always_in_view (b);
3969                         the_list->add (j->end, the_list->eval (j->end));
3970                         _line->add_always_in_view (j->end);
3971                 }
3972
3973                 uint32_t const N = _line->npoints ();
3974                 for (uint32_t i = 0; i < N; ++i) {
3975
3976                         ControlPoint* p = _line->nth (i);
3977
3978                         list<AudioRange>::const_iterator j = _ranges.begin ();
3979                         while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3980                                 ++j;
3981                         }
3982
3983                         if (j != _ranges.end()) {
3984                                 points.push_back (p);
3985                         }
3986                 }
3987         }
3988
3989         if (points.empty()) {
3990                 _nothing_to_drag = true;
3991                 return;
3992         }
3993
3994         _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3995 }
3996
3997 void
3998 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3999 {
4000         if (_nothing_to_drag) {
4001                 return;
4002         }
4003         
4004         float const f = 1 - (_drags->current_pointer_y() / _line->height());
4005
4006         /* we are ignoring x position for this drag, so we can just pass in anything */
4007         _line->drag_motion (0, f, true, false);
4008 }
4009
4010 void
4011 AutomationRangeDrag::finished (GdkEvent* event, bool)
4012 {
4013         if (_nothing_to_drag) {
4014                 return;
4015         }
4016         
4017         motion (event, false);
4018         _line->end_drag ();
4019         _line->clear_always_in_view ();
4020 }
4021
4022 void
4023 AutomationRangeDrag::aborted ()
4024 {
4025         _line->clear_always_in_view ();
4026         _line->reset ();
4027 }
4028
4029 DraggingView::DraggingView (RegionView* v)
4030         : view (v)
4031 {
4032         initial_y = v->get_canvas_group()->property_y ();
4033 }