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