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