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