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