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