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