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