7f95feb0a8258fa4b76b66d585aecb148316f0d7
[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* /*ignored*/)
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
1613         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1614
1615         if (event->motion.state & Keyboard::PrimaryModifier) {
1616                 relative = false;
1617         } else {
1618                 relative = true;
1619         }
1620
1621         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1622
1623         if (ms.size() > 1) {
1624                 /* has to be relative, may make no sense otherwise */
1625                 relative = true;
1626         }
1627
1628         /* select this note; if it is already selected, preserve the existing selection,
1629            otherwise make this note the only one selected.
1630         */
1631         region->note_selected (cnote, cnote->selected ());
1632
1633         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1634                 MidiRegionSelection::iterator next;
1635                 next = r;
1636                 ++next;
1637                 (*r)->begin_resizing (at_front);
1638                 r = next;
1639         }
1640 }
1641
1642 void
1643 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1644 {
1645         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1646         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1647                 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1648         }
1649 }
1650
1651 void
1652 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1653 {
1654         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1655         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1656                 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1657         }
1658 }
1659
1660 void
1661 NoteResizeDrag::aborted ()
1662 {
1663         /* XXX: TODO */
1664 }
1665
1666 void
1667 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1668 {
1669
1670 }
1671
1672 void
1673 RegionGainDrag::finished (GdkEvent *, bool)
1674 {
1675
1676 }
1677
1678 void
1679 RegionGainDrag::aborted ()
1680 {
1681         /* XXX: TODO */
1682 }
1683
1684 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1685         : RegionDrag (e, i, p, v)
1686         , _have_transaction (false)
1687 {
1688
1689 }
1690
1691 void
1692 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1693 {
1694         double speed = 1.0;
1695         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1696         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1697
1698         if (tv && tv->is_track()) {
1699                 speed = tv->track()->speed();
1700         }
1701
1702         nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1703         nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1704         nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1705
1706
1707         nframes64_t const pf = adjusted_current_frame (event);
1708
1709         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1710                 _operation = ContentsTrim;
1711                 Drag::start_grab (event, _editor->trimmer_cursor);
1712         } else {
1713                 /* These will get overridden for a point trim.*/
1714                 if (pf < (region_start + region_length/2)) {
1715                         /* closer to start */
1716                         _operation = StartTrim;
1717                         Drag::start_grab (event, _editor->left_side_trim_cursor);
1718                 } else {
1719                         /* closer to end */
1720                         _operation = EndTrim;
1721                         Drag::start_grab (event, _editor->right_side_trim_cursor);
1722                 }
1723         }
1724
1725         switch (_operation) {
1726         case StartTrim:
1727                 _editor->show_verbose_time_cursor (region_start, 10);
1728                 break;
1729         case EndTrim:
1730                 _editor->show_verbose_time_cursor (region_end, 10);
1731                 break;
1732         case ContentsTrim:
1733                 _editor->show_verbose_time_cursor (pf, 10);
1734                 break;
1735         }
1736 }
1737
1738 void
1739 TrimDrag::motion (GdkEvent* event, bool first_move)
1740 {
1741         RegionView* rv = _primary;
1742
1743         /* snap modifier works differently here..
1744            its current state has to be passed to the
1745            various trim functions in order to work properly
1746         */
1747
1748         double speed = 1.0;
1749         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1750         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1751         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1752
1753         if (tv && tv->is_track()) {
1754                 speed = tv->track()->speed();
1755         }
1756
1757         nframes64_t const pf = adjusted_current_frame (event);
1758
1759         if (first_move) {
1760
1761                 string trim_type;
1762
1763                 switch (_operation) {
1764                 case StartTrim:
1765                         trim_type = "Region start trim";
1766                         break;
1767                 case EndTrim:
1768                         trim_type = "Region end trim";
1769                         break;
1770                 case ContentsTrim:
1771                         trim_type = "Region content trim";
1772                         break;
1773                 }
1774
1775                 _editor->begin_reversible_command (trim_type);
1776                 _have_transaction = true;
1777
1778                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1779                         RegionView* rv = i->view;
1780                         rv->fake_set_opaque(false);
1781                         rv->region()->clear_history ();
1782                         rv->region()->suspend_property_changes ();
1783
1784                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1785
1786                         if (arv){
1787                                 arv->temporarily_hide_envelope ();
1788                         }
1789
1790                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1791                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1792
1793                         if (insert_result.second) {
1794                                 pl->freeze();
1795                         }
1796                 }
1797         }
1798
1799         bool non_overlap_trim = false;
1800
1801         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1802                 non_overlap_trim = true;
1803         }
1804
1805         switch (_operation) {
1806         case StartTrim:
1807                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1808                         _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1809                 }
1810                 break;
1811
1812         case EndTrim:
1813                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1814                         _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1815                 }
1816                 break;
1817
1818         case ContentsTrim:
1819                 {
1820                         bool swap_direction = false;
1821
1822                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1823                                 swap_direction = true;
1824                         }
1825
1826                         nframes64_t frame_delta = 0;
1827                         
1828                         bool left_direction = false;
1829                         if (last_pointer_frame() > pf) {
1830                                 left_direction = true;
1831                         }
1832
1833                         if (left_direction) {
1834                                 frame_delta = (last_pointer_frame() - pf);
1835                         } else {
1836                                 frame_delta = (pf - last_pointer_frame());
1837                         }
1838
1839                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1840                                 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1841                         }
1842                 }
1843                 break;
1844         }
1845
1846         switch (_operation) {
1847         case StartTrim:
1848                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1849                 break;
1850         case EndTrim:
1851                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1852                 break;
1853         case ContentsTrim:
1854                 _editor->show_verbose_time_cursor (pf, 10);
1855                 break;
1856         }
1857 }
1858
1859
1860 void
1861 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1862 {
1863         if (movement_occurred) {
1864                 motion (event, false);
1865
1866                 if (!_editor->selection->selected (_primary)) {
1867                         _editor->thaw_region_after_trim (*_primary);
1868                 } else {
1869
1870                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1871                                 _editor->thaw_region_after_trim (*i->view);
1872                                 i->view->fake_set_opaque (true);
1873                                 if (_have_transaction) {
1874                                         _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1875                                 }
1876                         }
1877                 }
1878                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1879                         (*p)->thaw ();
1880                 }
1881
1882                 _editor->motion_frozen_playlists.clear ();
1883
1884                 if (_have_transaction) {
1885                         _editor->commit_reversible_command();
1886                 }
1887
1888         } else {
1889                 /* no mouse movement */
1890                 _editor->point_trim (event, adjusted_current_frame (event));
1891         }
1892 }
1893
1894 void
1895 TrimDrag::aborted ()
1896 {
1897         /* Our motion method is changing model state, so use the Undo system
1898            to cancel.  Perhaps not ideal, as this will leave an Undo point
1899            behind which may be slightly odd from the user's point of view.
1900         */
1901
1902         finished (0, true);
1903         
1904         if (_have_transaction) {
1905                 _editor->undo ();
1906         }
1907 }
1908
1909 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1910         : Drag (e, i),
1911           _copy (c)
1912 {
1913         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1914         assert (_marker);
1915 }
1916
1917 void
1918 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1919 {
1920         if (_copy) {
1921                 // create a dummy marker for visual representation of moving the copy.
1922                 // The actual copying is not done before we reach the finish callback.
1923                 char name[64];
1924                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1925                 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1926                                                           *new MeterSection (_marker->meter()));
1927
1928                 _item = &new_marker->the_item ();
1929                 _marker = new_marker;
1930
1931         } else {
1932
1933                 MetricSection& section (_marker->meter());
1934
1935                 if (!section.movable()) {
1936                         return;
1937                 }
1938
1939         }
1940
1941         Drag::start_grab (event, cursor);
1942
1943         _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1944
1945         _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1946 }
1947
1948 void
1949 MeterMarkerDrag::motion (GdkEvent* event, bool)
1950 {
1951         nframes64_t const pf = adjusted_current_frame (event);
1952
1953         _marker->set_position (pf);
1954         
1955         _editor->show_verbose_time_cursor (pf, 10);
1956 }
1957
1958 void
1959 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1960 {
1961         if (!movement_occurred) {
1962                 return;
1963         }
1964
1965         motion (event, false);
1966
1967         BBT_Time when;
1968
1969         TempoMap& map (_editor->session()->tempo_map());
1970         map.bbt_time (last_pointer_frame(), when);
1971
1972         if (_copy == true) {
1973                 _editor->begin_reversible_command (_("copy meter mark"));
1974                 XMLNode &before = map.get_state();
1975                 map.add_meter (_marker->meter(), when);
1976                 XMLNode &after = map.get_state();
1977                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1978                 _editor->commit_reversible_command ();
1979
1980                 // delete the dummy marker we used for visual representation of copying.
1981                 // a new visual marker will show up automatically.
1982                 delete _marker;
1983         } else {
1984                 _editor->begin_reversible_command (_("move meter mark"));
1985                 XMLNode &before = map.get_state();
1986                 map.move_meter (_marker->meter(), when);
1987                 XMLNode &after = map.get_state();
1988                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1989                 _editor->commit_reversible_command ();
1990         }
1991 }
1992
1993 void
1994 MeterMarkerDrag::aborted ()
1995 {
1996         _marker->set_position (_marker->meter().frame ());
1997 }
1998
1999 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2000         : Drag (e, i),
2001           _copy (c)
2002 {
2003         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2004         assert (_marker);
2005 }
2006
2007 void
2008 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2009 {
2010
2011         if (_copy) {
2012
2013                 // create a dummy marker for visual representation of moving the copy.
2014                 // The actual copying is not done before we reach the finish callback.
2015                 char name[64];
2016                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2017                 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2018                                                           *new TempoSection (_marker->tempo()));
2019
2020                 _item = &new_marker->the_item ();
2021                 _marker = new_marker;
2022
2023         } else {
2024
2025                 MetricSection& section (_marker->tempo());
2026
2027                 if (!section.movable()) {
2028                         return;
2029                 }
2030         }
2031
2032         Drag::start_grab (event, cursor);
2033
2034         _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2035         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2036 }
2037
2038 void
2039 TempoMarkerDrag::motion (GdkEvent* event, bool)
2040 {
2041         nframes64_t const pf = adjusted_current_frame (event);
2042         _marker->set_position (pf);
2043         _editor->show_verbose_time_cursor (pf, 10);
2044 }
2045
2046 void
2047 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2048 {
2049         if (!movement_occurred) {
2050                 return;
2051         }
2052
2053         motion (event, false);
2054
2055         BBT_Time when;
2056
2057         TempoMap& map (_editor->session()->tempo_map());
2058         map.bbt_time (last_pointer_frame(), when);
2059
2060         if (_copy == true) {
2061                 _editor->begin_reversible_command (_("copy tempo mark"));
2062                 XMLNode &before = map.get_state();
2063                 map.add_tempo (_marker->tempo(), when);
2064                 XMLNode &after = map.get_state();
2065                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2066                 _editor->commit_reversible_command ();
2067
2068                 // delete the dummy marker we used for visual representation of copying.
2069                 // a new visual marker will show up automatically.
2070                 delete _marker;
2071         } else {
2072                 _editor->begin_reversible_command (_("move tempo mark"));
2073                 XMLNode &before = map.get_state();
2074                 map.move_tempo (_marker->tempo(), when);
2075                 XMLNode &after = map.get_state();
2076                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2077                 _editor->commit_reversible_command ();
2078         }
2079 }
2080
2081 void
2082 TempoMarkerDrag::aborted ()
2083 {
2084         _marker->set_position (_marker->tempo().frame());
2085 }
2086
2087 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2088         : Drag (e, i),
2089           _stop (s)
2090 {
2091         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2092         assert (_cursor);
2093 }
2094
2095 void
2096 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2097 {
2098         Drag::start_grab (event, c);
2099
2100         if (!_stop) {
2101
2102                 nframes64_t where = _editor->event_frame (event, 0, 0);
2103
2104                 _editor->snap_to_with_modifier (where, event);
2105                 _editor->playhead_cursor->set_position (where);
2106
2107         }
2108
2109         if (_cursor == _editor->playhead_cursor) {
2110                 _editor->_dragging_playhead = true;
2111
2112                 if (_editor->session() && _was_rolling && _stop) {
2113                         _editor->session()->request_stop ();
2114                 }
2115
2116                 if (_editor->session() && _editor->session()->is_auditioning()) {
2117                         _editor->session()->cancel_audition ();
2118                 }
2119         }
2120
2121         _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2122
2123         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2124 }
2125
2126 void
2127 CursorDrag::motion (GdkEvent* event, bool)
2128 {
2129         nframes64_t const adjusted_frame = adjusted_current_frame (event);
2130
2131         if (adjusted_frame == last_pointer_frame()) {
2132                 return;
2133         }
2134
2135         _cursor->set_position (adjusted_frame);
2136
2137         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2138
2139 #ifdef GTKOSX
2140         _editor->update_canvas_now ();
2141 #endif
2142         _editor->UpdateAllTransportClocks (_cursor->current_frame);
2143 }
2144
2145 void
2146 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2147 {
2148         _editor->_dragging_playhead = false;
2149
2150         if (!movement_occurred && _stop) {
2151                 return;
2152         }
2153
2154         motion (event, false);
2155
2156         if (_item == &_editor->playhead_cursor->canvas_item) {
2157                 if (_editor->session()) {
2158                         _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2159                         _editor->_pending_locate_request = true;
2160                 }
2161         }
2162 }
2163
2164 void
2165 CursorDrag::aborted ()
2166 {
2167         _editor->_dragging_playhead = false;
2168         _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2169 }
2170
2171 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2172         : RegionDrag (e, i, p, v)
2173 {
2174
2175 }
2176
2177 void
2178 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2179 {
2180         Drag::start_grab (event, cursor);
2181
2182         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2183         boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2184
2185         _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2186         _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2187 }
2188
2189 void
2190 FadeInDrag::motion (GdkEvent* event, bool)
2191 {
2192         nframes64_t fade_length;
2193
2194         nframes64_t const pos = adjusted_current_frame (event);
2195
2196         boost::shared_ptr<Region> region = _primary->region ();
2197
2198         if (pos < (region->position() + 64)) {
2199                 fade_length = 64; // this should be a minimum defined somewhere
2200         } else if (pos > region->last_frame()) {
2201                 fade_length = region->length();
2202         } else {
2203                 fade_length = pos - region->position();
2204         }
2205
2206         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2207
2208                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2209
2210                 if (!tmp) {
2211                         continue;
2212                 }
2213
2214                 tmp->reset_fade_in_shape_width (fade_length);
2215         }
2216
2217         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2218 }
2219
2220 void
2221 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2222 {
2223         if (!movement_occurred) {
2224                 return;
2225         }
2226
2227         nframes64_t fade_length;
2228
2229         nframes64_t const pos = adjusted_current_frame (event);
2230
2231         boost::shared_ptr<Region> region = _primary->region ();
2232
2233         if (pos < (region->position() + 64)) {
2234                 fade_length = 64; // this should be a minimum defined somewhere
2235         } else if (pos > region->last_frame()) {
2236                 fade_length = region->length();
2237         } else {
2238                 fade_length = pos - region->position();
2239         }
2240
2241         _editor->begin_reversible_command (_("change fade in length"));
2242
2243         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2244
2245                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2246
2247                 if (!tmp) {
2248                         continue;
2249                 }
2250
2251                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2252                 XMLNode &before = alist->get_state();
2253
2254                 tmp->audio_region()->set_fade_in_length (fade_length);
2255                 tmp->audio_region()->set_fade_in_active (true);
2256
2257                 XMLNode &after = alist->get_state();
2258                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2259         }
2260
2261         _editor->commit_reversible_command ();
2262 }
2263
2264 void
2265 FadeInDrag::aborted ()
2266 {
2267         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2268                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2269
2270                 if (!tmp) {
2271                         continue;
2272                 }
2273
2274                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2275         }
2276 }
2277
2278 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2279         : RegionDrag (e, i, p, v)
2280 {
2281
2282 }
2283
2284 void
2285 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2286 {
2287         Drag::start_grab (event, cursor);
2288
2289         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2290         boost::shared_ptr<AudioRegion> r = a->audio_region ();
2291
2292         _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2293         _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2294 }
2295
2296 void
2297 FadeOutDrag::motion (GdkEvent* event, bool)
2298 {
2299         nframes64_t fade_length;
2300
2301         nframes64_t const pos = adjusted_current_frame (event);
2302
2303         boost::shared_ptr<Region> region = _primary->region ();
2304
2305         if (pos > (region->last_frame() - 64)) {
2306                 fade_length = 64; // this should really be a minimum fade defined somewhere
2307         }
2308         else if (pos < region->position()) {
2309                 fade_length = region->length();
2310         }
2311         else {
2312                 fade_length = region->last_frame() - pos;
2313         }
2314
2315         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2316
2317                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2318
2319                 if (!tmp) {
2320                         continue;
2321                 }
2322
2323                 tmp->reset_fade_out_shape_width (fade_length);
2324         }
2325
2326         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2327 }
2328
2329 void
2330 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2331 {
2332         if (!movement_occurred) {
2333                 return;
2334         }
2335
2336         nframes64_t fade_length;
2337
2338         nframes64_t const pos = adjusted_current_frame (event);
2339
2340         boost::shared_ptr<Region> region = _primary->region ();
2341
2342         if (pos > (region->last_frame() - 64)) {
2343                 fade_length = 64; // this should really be a minimum fade defined somewhere
2344         }
2345         else if (pos < region->position()) {
2346                 fade_length = region->length();
2347         }
2348         else {
2349                 fade_length = region->last_frame() - pos;
2350         }
2351
2352         _editor->begin_reversible_command (_("change fade out length"));
2353
2354         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2355
2356                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2357
2358                 if (!tmp) {
2359                         continue;
2360                 }
2361
2362                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2363                 XMLNode &before = alist->get_state();
2364
2365                 tmp->audio_region()->set_fade_out_length (fade_length);
2366                 tmp->audio_region()->set_fade_out_active (true);
2367
2368                 XMLNode &after = alist->get_state();
2369                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2370         }
2371
2372         _editor->commit_reversible_command ();
2373 }
2374
2375 void
2376 FadeOutDrag::aborted ()
2377 {
2378         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2379                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2380
2381                 if (!tmp) {
2382                         continue;
2383                 }
2384
2385                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2386         }
2387 }
2388
2389 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2390         : Drag (e, i)
2391 {
2392         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2393         assert (_marker);
2394
2395         _points.push_back (Gnome::Art::Point (0, 0));
2396         _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2397
2398         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2399         _line->property_width_pixels() = 1;
2400         _line->property_points () = _points;
2401         _line->hide ();
2402
2403         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2404 }
2405
2406 MarkerDrag::~MarkerDrag ()
2407 {
2408         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2409                 delete *i;
2410         }
2411 }
2412
2413 void
2414 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2415 {
2416         Drag::start_grab (event, cursor);
2417
2418         bool is_start;
2419
2420         Location *location = _editor->find_location_from_marker (_marker, is_start);
2421         _editor->_dragging_edit_point = true;
2422
2423         _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2424
2425         update_item (location);
2426
2427         // _drag_line->show();
2428         // _line->raise_to_top();
2429
2430         if (is_start) {
2431                 _editor->show_verbose_time_cursor (location->start(), 10);
2432         } else {
2433                 _editor->show_verbose_time_cursor (location->end(), 10);
2434         }
2435
2436         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2437
2438         switch (op) {
2439         case Selection::Toggle:
2440                 _editor->selection->toggle (_marker);
2441                 break;
2442         case Selection::Set:
2443                 if (!_editor->selection->selected (_marker)) {
2444                         _editor->selection->set (_marker);
2445                 }
2446                 break;
2447         case Selection::Extend:
2448         {
2449                 Locations::LocationList ll;
2450                 list<Marker*> to_add;
2451                 nframes64_t s, e;
2452                 _editor->selection->markers.range (s, e);
2453                 s = min (_marker->position(), s);
2454                 e = max (_marker->position(), e);
2455                 s = min (s, e);
2456                 e = max (s, e);
2457                 if (e < max_frames) {
2458                         ++e;
2459                 }
2460                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2461                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2462                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2463                         if (lm) {
2464                                 if (lm->start) {
2465                                         to_add.push_back (lm->start);
2466                                 }
2467                                 if (lm->end) {
2468                                         to_add.push_back (lm->end);
2469                                 }
2470                         }
2471                 }
2472                 if (!to_add.empty()) {
2473                         _editor->selection->add (to_add);
2474                 }
2475                 break;
2476         }
2477         case Selection::Add:
2478                 _editor->selection->add (_marker);
2479                 break;
2480         }
2481
2482         /* Set up copies for us to manipulate during the drag */
2483
2484         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2485                 Location* l = _editor->find_location_from_marker (*i, is_start);
2486                 _copied_locations.push_back (new Location (*l));
2487         }
2488 }
2489
2490 void
2491 MarkerDrag::motion (GdkEvent* event, bool)
2492 {
2493         nframes64_t f_delta = 0;
2494         bool is_start;
2495         bool move_both = false;
2496         Marker* marker;
2497         Location *real_location;
2498         Location *copy_location = 0;
2499
2500         nframes64_t const newframe = adjusted_current_frame (event);
2501
2502         nframes64_t next = newframe;
2503
2504         if (newframe == last_pointer_frame()) {
2505                 return;
2506         }
2507
2508         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2509                 move_both = true;
2510         }
2511
2512         MarkerSelection::iterator i;
2513         list<Location*>::iterator x;
2514
2515         /* find the marker we're dragging, and compute the delta */
2516
2517         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2518              x != _copied_locations.end() && i != _editor->selection->markers.end();
2519              ++i, ++x) {
2520
2521                 copy_location = *x;
2522                 marker = *i;
2523
2524                 if (marker == _marker) {
2525
2526                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2527                                 /* que pasa ?? */
2528                                 return;
2529                         }
2530
2531                         if (real_location->is_mark()) {
2532                                 f_delta = newframe - copy_location->start();
2533                         } else {
2534
2535
2536                                 switch (marker->type()) {
2537                                 case Marker::Start:
2538                                 case Marker::LoopStart:
2539                                 case Marker::PunchIn:
2540                                         f_delta = newframe - copy_location->start();
2541                                         break;
2542
2543                                 case Marker::End:
2544                                 case Marker::LoopEnd:
2545                                 case Marker::PunchOut:
2546                                         f_delta = newframe - copy_location->end();
2547                                         break;
2548                                 default:
2549                                         /* what kind of marker is this ? */
2550                                         return;
2551                                 }
2552                         }
2553                         break;
2554                 }
2555         }
2556
2557         if (i == _editor->selection->markers.end()) {
2558                 /* hmm, impossible - we didn't find the dragged marker */
2559                 return;
2560         }
2561
2562         /* now move them all */
2563
2564         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2565              x != _copied_locations.end() && i != _editor->selection->markers.end();
2566              ++i, ++x) {
2567
2568                 copy_location = *x;
2569                 marker = *i;
2570
2571                 /* call this to find out if its the start or end */
2572
2573                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2574                         continue;
2575                 }
2576
2577                 if (real_location->locked()) {
2578                         continue;
2579                 }
2580
2581                 if (copy_location->is_mark()) {
2582
2583                         /* now move it */
2584
2585                         copy_location->set_start (copy_location->start() + f_delta);
2586
2587                 } else {
2588
2589                         nframes64_t new_start = copy_location->start() + f_delta;
2590                         nframes64_t new_end = copy_location->end() + f_delta;
2591
2592                         if (is_start) { // start-of-range marker
2593
2594                                 if (move_both) {
2595                                         copy_location->set_start (new_start);
2596                                         copy_location->set_end (new_end);
2597                                 } else  if (new_start < copy_location->end()) {
2598                                         copy_location->set_start (new_start);
2599                                 } else {
2600                                         _editor->snap_to (next, 1, true);
2601                                         copy_location->set_end (next);
2602                                         copy_location->set_start (newframe);
2603                                 }
2604
2605                         } else { // end marker
2606
2607                                 if (move_both) {
2608                                         copy_location->set_end (new_end);
2609                                         copy_location->set_start (new_start);
2610                                 } else if (new_end > copy_location->start()) {
2611                                         copy_location->set_end (new_end);
2612                                 } else if (newframe > 0) {
2613                                         _editor->snap_to (next, -1, true);
2614                                         copy_location->set_start (next);
2615                                         copy_location->set_end (newframe);
2616                                 }
2617                         }
2618                 }
2619
2620                 update_item (copy_location);
2621
2622                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2623
2624                 if (lm) {
2625                         lm->set_position (copy_location->start(), copy_location->end());
2626                 }
2627         }
2628
2629         assert (!_copied_locations.empty());
2630
2631         _editor->show_verbose_time_cursor (newframe, 10);
2632
2633 #ifdef GTKOSX
2634         _editor->update_canvas_now ();
2635 #endif
2636 }
2637
2638 void
2639 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2640 {
2641         if (!movement_occurred) {
2642
2643                 /* just a click, do nothing but finish
2644                    off the selection process
2645                 */
2646
2647                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2648
2649                 switch (op) {
2650                 case Selection::Set:
2651                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2652                                 _editor->selection->set (_marker);
2653                         }
2654                         break;
2655
2656                 case Selection::Toggle:
2657                 case Selection::Extend:
2658                 case Selection::Add:
2659                         break;
2660                 }
2661
2662                 return;
2663         }
2664
2665         _editor->_dragging_edit_point = false;
2666
2667         _editor->begin_reversible_command ( _("move marker") );
2668         XMLNode &before = _editor->session()->locations()->get_state();
2669
2670         MarkerSelection::iterator i;
2671         list<Location*>::iterator x;
2672         bool is_start;
2673
2674         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2675              x != _copied_locations.end() && i != _editor->selection->markers.end();
2676              ++i, ++x) {
2677
2678                 Location * location = _editor->find_location_from_marker (*i, is_start);
2679
2680                 if (location) {
2681
2682                         if (location->locked()) {
2683                                 return;
2684                         }
2685
2686                         if (location->is_mark()) {
2687                                 location->set_start ((*x)->start());
2688                         } else {
2689                                 location->set ((*x)->start(), (*x)->end());
2690                         }
2691                 }
2692         }
2693
2694         XMLNode &after = _editor->session()->locations()->get_state();
2695         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2696         _editor->commit_reversible_command ();
2697
2698         _line->hide();
2699 }
2700
2701 void
2702 MarkerDrag::aborted ()
2703 {
2704         /* XXX: TODO */
2705 }
2706
2707 void
2708 MarkerDrag::update_item (Location* location)
2709 {
2710         double const x1 = _editor->frame_to_pixel (location->start());
2711
2712         _points.front().set_x(x1);
2713         _points.back().set_x(x1);
2714         _line->property_points() = _points;
2715 }
2716
2717 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2718         : Drag (e, i),
2719           _cumulative_x_drag (0),
2720           _cumulative_y_drag (0)
2721 {
2722         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2723         assert (_point);
2724 }
2725
2726
2727 void
2728 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2729 {
2730         Drag::start_grab (event, _editor->fader_cursor);
2731
2732         // start the grab at the center of the control point so
2733         // the point doesn't 'jump' to the mouse after the first drag
2734         _time_axis_view_grab_x = _point->get_x();
2735         _time_axis_view_grab_y = _point->get_y();
2736
2737         float const fraction = 1 - (_point->get_y() / _point->line().height());
2738
2739         _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2740
2741         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2742                                             event->button.x + 10, event->button.y + 10);
2743
2744         _editor->show_verbose_canvas_cursor ();
2745 }
2746
2747 void
2748 ControlPointDrag::motion (GdkEvent* event, bool)
2749 {
2750         double dx = _drags->current_pointer_x() - last_pointer_x();
2751         double dy = _drags->current_pointer_y() - last_pointer_y();
2752
2753         if (event->button.state & Keyboard::SecondaryModifier) {
2754                 dx *= 0.1;
2755                 dy *= 0.1;
2756         }
2757
2758         /* coordinate in TimeAxisView's space */
2759         double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2760         double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2761
2762         // calculate zero crossing point. back off by .01 to stay on the
2763         // positive side of zero
2764         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2765
2766         // make sure we hit zero when passing through
2767         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2768                 cy = zero_gain_y;
2769         }
2770
2771         if (_x_constrained) {
2772                 cx = _time_axis_view_grab_x;
2773         }
2774         if (_y_constrained) {
2775                 cy = _time_axis_view_grab_y;
2776         }
2777
2778         _cumulative_x_drag = cx - _time_axis_view_grab_x;
2779         _cumulative_y_drag = cy - _time_axis_view_grab_y;
2780
2781         cx = max (0.0, cx);
2782         cy = max (0.0, cy);
2783         cy = min ((double) _point->line().height(), cy);
2784
2785         nframes64_t cx_frames = _editor->unit_to_frame (cx);
2786
2787         if (!_x_constrained) {
2788                 _editor->snap_to_with_modifier (cx_frames, event);
2789         }
2790
2791         float const fraction = 1.0 - (cy / _point->line().height());
2792
2793         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2794
2795         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2796
2797         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2798 }
2799
2800 void
2801 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2802 {
2803         if (!movement_occurred) {
2804
2805                 /* just a click */
2806
2807                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2808                         _editor->reset_point_selection ();
2809                 }
2810
2811         } else {
2812                 motion (event, false);
2813         }
2814         _point->line().end_drag ();
2815 }
2816
2817 void
2818 ControlPointDrag::aborted ()
2819 {
2820         _point->line().reset ();
2821 }
2822
2823 bool
2824 ControlPointDrag::active (Editing::MouseMode m)
2825 {
2826         if (m == Editing::MouseGain) {
2827                 /* always active in mouse gain */
2828                 return true;
2829         }
2830
2831         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2832         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2833 }
2834
2835 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2836         : Drag (e, i),
2837           _line (0),
2838           _cumulative_y_drag (0)
2839 {
2840
2841 }
2842 void
2843 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2844 {
2845         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2846         assert (_line);
2847
2848         _item = &_line->grab_item ();
2849
2850         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2851            origin, and ditto for y.
2852         */
2853
2854         double cx = event->button.x;
2855         double cy = event->button.y;
2856
2857         _line->parent_group().w2i (cx, cy);
2858
2859         nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2860
2861         uint32_t before;
2862         uint32_t after;
2863         
2864         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2865                 /* no adjacent points */
2866                 return;
2867         }
2868
2869         Drag::start_grab (event, _editor->fader_cursor);
2870
2871         /* store grab start in parent frame */
2872
2873         _time_axis_view_grab_x = cx;
2874         _time_axis_view_grab_y = cy;
2875
2876         double fraction = 1.0 - (cy / _line->height());
2877
2878         _line->start_drag_line (before, after, fraction);
2879
2880         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2881                                             event->button.x + 10, event->button.y + 10);
2882
2883         _editor->show_verbose_canvas_cursor ();
2884 }
2885
2886 void
2887 LineDrag::motion (GdkEvent* event, bool)
2888 {
2889         double dy = _drags->current_pointer_y() - last_pointer_y();
2890
2891         if (event->button.state & Keyboard::SecondaryModifier) {
2892                 dy *= 0.1;
2893         }
2894
2895         double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2896
2897         _cumulative_y_drag = cy - _time_axis_view_grab_y;
2898
2899         cy = max (0.0, cy);
2900         cy = min ((double) _line->height(), cy);
2901
2902         double const fraction = 1.0 - (cy / _line->height());
2903
2904         bool push;
2905
2906         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2907                 push = false;
2908         } else {
2909                 push = true;
2910         }
2911
2912         /* we are ignoring x position for this drag, so we can just pass in anything */
2913         _line->drag_motion (0, fraction, true, push);
2914
2915         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2916 }
2917
2918 void
2919 LineDrag::finished (GdkEvent* event, bool)
2920 {
2921         motion (event, false);
2922         _line->end_drag ();
2923 }
2924
2925 void
2926 LineDrag::aborted ()
2927 {
2928         _line->reset ();
2929 }
2930
2931 void
2932 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2933 {
2934         Drag::start_grab (event);
2935         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2936 }
2937
2938 void
2939 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2940 {
2941         nframes64_t start;
2942         nframes64_t end;
2943         double y1;
2944         double y2;
2945
2946         nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2947
2948         nframes64_t grab = grab_frame ();
2949         if (Config->get_rubberbanding_snaps_to_grid ()) {
2950                 _editor->snap_to_with_modifier (grab, event);
2951         }
2952
2953         /* base start and end on initial click position */
2954
2955         if (pf < grab) {
2956                 start = pf;
2957                 end = grab;
2958         } else {
2959                 end = pf;
2960                 start = grab;
2961         }
2962
2963         if (_drags->current_pointer_y() < grab_y()) {
2964                 y1 = _drags->current_pointer_y();
2965                 y2 = grab_y();
2966         } else {
2967                 y2 = _drags->current_pointer_y();
2968                 y1 = grab_y();
2969         }
2970
2971
2972         if (start != end || y1 != y2) {
2973
2974                 double x1 = _editor->frame_to_pixel (start);
2975                 double x2 = _editor->frame_to_pixel (end);
2976
2977                 _editor->rubberband_rect->property_x1() = x1;
2978                 _editor->rubberband_rect->property_y1() = y1;
2979                 _editor->rubberband_rect->property_x2() = x2;
2980                 _editor->rubberband_rect->property_y2() = y2;
2981
2982                 _editor->rubberband_rect->show();
2983                 _editor->rubberband_rect->raise_to_top();
2984
2985                 _editor->show_verbose_time_cursor (pf, 10);
2986         }
2987 }
2988
2989 void
2990 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2991 {
2992         if (movement_occurred) {
2993
2994                 motion (event, false);
2995
2996                 double y1,y2;
2997                 if (_drags->current_pointer_y() < grab_y()) {
2998                         y1 = _drags->current_pointer_y();
2999                         y2 = grab_y();
3000                 } else {
3001                         y2 = _drags->current_pointer_y();
3002                         y1 = grab_y();
3003                 }
3004
3005
3006                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3007                 bool committed;
3008
3009                 _editor->begin_reversible_command (_("rubberband selection"));
3010
3011                 if (grab_frame() < last_pointer_frame()) {
3012                         committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3013                 } else {
3014                         committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3015                 }
3016
3017                 if (!committed) {
3018                         _editor->commit_reversible_command ();
3019                 }
3020
3021         } else {
3022                 if (!getenv("ARDOUR_SAE")) {
3023                         _editor->selection->clear_tracks();
3024                 }
3025                 _editor->selection->clear_regions();
3026                 _editor->selection->clear_points ();
3027                 _editor->selection->clear_lines ();
3028         }
3029
3030         _editor->rubberband_rect->hide();
3031 }
3032
3033 void
3034 RubberbandSelectDrag::aborted ()
3035 {
3036         _editor->rubberband_rect->hide ();
3037 }
3038
3039 void
3040 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3041 {
3042         Drag::start_grab (event);
3043
3044         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3045 }
3046
3047 void
3048 TimeFXDrag::motion (GdkEvent* event, bool)
3049 {
3050         RegionView* rv = _primary;
3051
3052         nframes64_t const pf = adjusted_current_frame (event);
3053
3054         if (pf > rv->region()->position()) {
3055                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3056         }
3057
3058         _editor->show_verbose_time_cursor (pf, 10);
3059 }
3060
3061 void
3062 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3063 {
3064         _primary->get_time_axis_view().hide_timestretch ();
3065
3066         if (!movement_occurred) {
3067                 return;
3068         }
3069
3070         if (last_pointer_frame() < _primary->region()->position()) {
3071                 /* backwards drag of the left edge - not usable */
3072                 return;
3073         }
3074
3075         nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3076
3077         float percentage = (double) newlen / (double) _primary->region()->length();
3078
3079 #ifndef USE_RUBBERBAND
3080         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3081         if (_primary->region()->data_type() == DataType::AUDIO) {
3082                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3083         }
3084 #endif
3085
3086         _editor->begin_reversible_command (_("timestretch"));
3087
3088         // XXX how do timeFX on multiple regions ?
3089
3090         RegionSelection rs;
3091         rs.add (_primary);
3092
3093         if (_editor->time_stretch (rs, percentage) == -1) {
3094                 error << _("An error occurred while executing time stretch operation") << endmsg;
3095         }
3096 }
3097
3098 void
3099 TimeFXDrag::aborted ()
3100 {
3101         _primary->get_time_axis_view().hide_timestretch ();
3102 }
3103
3104
3105 void
3106 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3107 {
3108         Drag::start_grab (event);
3109 }
3110
3111 void
3112 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3113 {
3114         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3115 }
3116
3117 void
3118 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3119 {
3120         if (movement_occurred && _editor->session()) {
3121                 /* make sure we stop */
3122                 _editor->session()->request_transport_speed (0.0);
3123         }
3124 }
3125
3126 void
3127 ScrubDrag::aborted ()
3128 {
3129         /* XXX: TODO */
3130 }
3131
3132 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3133         : Drag (e, i)
3134         , _operation (o)
3135         , _copy (false)
3136         , _original_pointer_time_axis (-1)
3137         , _last_pointer_time_axis (-1)
3138 {
3139
3140 }
3141
3142 void
3143 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3144 {
3145         nframes64_t start = 0;
3146         nframes64_t end = 0;
3147
3148         if (_editor->session() == 0) {
3149                 return;
3150         }
3151
3152         Gdk::Cursor* cursor = 0;
3153
3154         switch (_operation) {
3155         case CreateSelection:
3156                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3157                         _copy = true;
3158                 } else {
3159                         _copy = false;
3160                 }
3161                 cursor = _editor->selector_cursor;
3162                 Drag::start_grab (event, cursor);
3163                 break;
3164
3165         case SelectionStartTrim:
3166                 if (_editor->clicked_axisview) {
3167                         _editor->clicked_axisview->order_selection_trims (_item, true);
3168                 }
3169                 Drag::start_grab (event, _editor->left_side_trim_cursor);
3170                 start = _editor->selection->time[_editor->clicked_selection].start;
3171                 _pointer_frame_offset = grab_frame() - start;
3172                 break;
3173
3174         case SelectionEndTrim:
3175                 if (_editor->clicked_axisview) {
3176                         _editor->clicked_axisview->order_selection_trims (_item, false);
3177                 }
3178                 Drag::start_grab (event, _editor->right_side_trim_cursor);
3179                 end = _editor->selection->time[_editor->clicked_selection].end;
3180                 _pointer_frame_offset = grab_frame() - end;
3181                 break;
3182
3183         case SelectionMove:
3184                 start = _editor->selection->time[_editor->clicked_selection].start;
3185                 Drag::start_grab (event, cursor);
3186                 _pointer_frame_offset = grab_frame() - start;
3187                 break;
3188         }
3189
3190         if (_operation == SelectionMove) {
3191                 _editor->show_verbose_time_cursor (start, 10);
3192         } else {
3193                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3194         }
3195
3196         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3197 }
3198
3199 void
3200 SelectionDrag::motion (GdkEvent* event, bool first_move)
3201 {
3202         nframes64_t start = 0;
3203         nframes64_t end = 0;
3204         nframes64_t length;
3205
3206         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3207         if (pending_time_axis.first == 0) {
3208                 return;
3209         }
3210         
3211         nframes64_t const pending_position = adjusted_current_frame (event);
3212
3213         /* only alter selection if things have changed */
3214
3215         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3216                 return;
3217         }
3218
3219         switch (_operation) {
3220         case CreateSelection:
3221         {
3222                 nframes64_t grab = grab_frame ();
3223
3224                 if (first_move) {
3225                         _editor->snap_to (grab);
3226                 }
3227
3228                 if (pending_position < grab_frame()) {
3229                         start = pending_position;
3230                         end = grab;
3231                 } else {
3232                         end = pending_position;
3233                         start = grab;
3234                 }
3235
3236                 /* first drag: Either add to the selection
3237                    or create a new selection
3238                 */
3239
3240                 if (first_move) {
3241
3242                         if (_copy) {
3243                                 /* adding to the selection */
3244                                 _editor->selection->add (_editor->clicked_axisview);
3245                                 _editor->clicked_selection = _editor->selection->add (start, end);
3246                                 _copy = false;
3247                         } else {
3248                                 /* new selection */
3249
3250                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3251                                         _editor->selection->set (_editor->clicked_axisview);
3252                                 }
3253                                 
3254                                 _editor->clicked_selection = _editor->selection->set (start, end);
3255                         }
3256                 }
3257
3258                 /* select the track that we're in */
3259                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3260                         _editor->selection->add (pending_time_axis.first);
3261                         _added_time_axes.push_back (pending_time_axis.first);
3262                 }
3263
3264                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3265                    tracks that we selected in the first place.
3266                 */
3267                 
3268                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3269                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3270
3271                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3272                 while (i != _added_time_axes.end()) {
3273
3274                         list<TimeAxisView*>::iterator tmp = i;
3275                         ++tmp;
3276                         
3277                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3278                                 _editor->selection->remove (*i);
3279                                 _added_time_axes.remove (*i);
3280                         }
3281
3282                         i = tmp;
3283                 }
3284
3285         }
3286         break;
3287
3288         case SelectionStartTrim:
3289
3290                 start = _editor->selection->time[_editor->clicked_selection].start;
3291                 end = _editor->selection->time[_editor->clicked_selection].end;
3292
3293                 if (pending_position > end) {
3294                         start = end;
3295                 } else {
3296                         start = pending_position;
3297                 }
3298                 break;
3299
3300         case SelectionEndTrim:
3301
3302                 start = _editor->selection->time[_editor->clicked_selection].start;
3303                 end = _editor->selection->time[_editor->clicked_selection].end;
3304
3305                 if (pending_position < start) {
3306                         end = start;
3307                 } else {
3308                         end = pending_position;
3309                 }
3310
3311                 break;
3312
3313         case SelectionMove:
3314
3315                 start = _editor->selection->time[_editor->clicked_selection].start;
3316                 end = _editor->selection->time[_editor->clicked_selection].end;
3317
3318                 length = end - start;
3319
3320                 start = pending_position;
3321                 _editor->snap_to (start);
3322
3323                 end = start + length;
3324
3325                 break;
3326         }
3327
3328         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3329                 _editor->start_canvas_autoscroll (1, 0);
3330         }
3331
3332         if (start != end) {
3333                 _editor->selection->replace (_editor->clicked_selection, start, end);
3334         }
3335
3336         if (_operation == SelectionMove) {
3337                 _editor->show_verbose_time_cursor(start, 10);
3338         } else {
3339                 _editor->show_verbose_time_cursor(pending_position, 10);
3340         }
3341 }
3342
3343 void
3344 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3345 {
3346         Session* s = _editor->session();
3347
3348         if (movement_occurred) {
3349                 motion (event, false);
3350                 /* XXX this is not object-oriented programming at all. ick */
3351                 if (_editor->selection->time.consolidate()) {
3352                         _editor->selection->TimeChanged ();
3353                 }
3354
3355                 /* XXX what if its a music time selection? */
3356                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3357                         s->request_play_range (&_editor->selection->time, true);
3358                 }
3359
3360
3361         } else {
3362                 /* just a click, no pointer movement.*/
3363
3364                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3365                         _editor->selection->clear_time();
3366                 }
3367
3368                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3369                         _editor->selection->set (_editor->clicked_axisview);
3370                 }
3371                 
3372                 if (s && s->get_play_range () && s->transport_rolling()) {
3373                         s->request_stop (false, false);
3374                 }
3375
3376         }
3377
3378         _editor->stop_canvas_autoscroll ();
3379 }
3380
3381 void
3382 SelectionDrag::aborted ()
3383 {
3384         /* XXX: TODO */
3385 }
3386
3387 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3388         : Drag (e, i),
3389           _operation (o),
3390           _copy (false)
3391 {
3392         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3393         _drag_rect->hide ();
3394
3395         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3396         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3397 }
3398
3399 void
3400 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3401 {
3402         if (_editor->session() == 0) {
3403                 return;
3404         }
3405
3406         Gdk::Cursor* cursor = 0;
3407
3408         if (!_editor->temp_location) {
3409                 _editor->temp_location = new Location;
3410         }
3411
3412         switch (_operation) {
3413         case CreateRangeMarker:
3414         case CreateTransportMarker:
3415         case CreateCDMarker:
3416
3417                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3418                         _copy = true;
3419                 } else {
3420                         _copy = false;
3421                 }
3422                 cursor = _editor->selector_cursor;
3423                 break;
3424         }
3425
3426         Drag::start_grab (event, cursor);
3427
3428         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3429 }
3430
3431 void
3432 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3433 {
3434         nframes64_t start = 0;
3435         nframes64_t end = 0;
3436         ArdourCanvas::SimpleRect *crect;
3437
3438         switch (_operation) {
3439         case CreateRangeMarker:
3440                 crect = _editor->range_bar_drag_rect;
3441                 break;
3442         case CreateTransportMarker:
3443                 crect = _editor->transport_bar_drag_rect;
3444                 break;
3445         case CreateCDMarker:
3446                 crect = _editor->cd_marker_bar_drag_rect;
3447                 break;
3448         default:
3449                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3450                 return;
3451                 break;
3452         }
3453
3454         nframes64_t const pf = adjusted_current_frame (event);
3455
3456         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3457                 nframes64_t grab = grab_frame ();
3458                 _editor->snap_to (grab);
3459                 
3460                 if (pf < grab_frame()) {
3461                         start = pf;
3462                         end = grab;
3463                 } else {
3464                         end = pf;
3465                         start = grab;
3466                 }
3467
3468                 /* first drag: Either add to the selection
3469                    or create a new selection.
3470                 */
3471
3472                 if (first_move) {
3473
3474                         _editor->temp_location->set (start, end);
3475
3476                         crect->show ();
3477
3478                         update_item (_editor->temp_location);
3479                         _drag_rect->show();
3480                         //_drag_rect->raise_to_top();
3481
3482                 }
3483         }
3484
3485         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3486                 _editor->start_canvas_autoscroll (1, 0);
3487         }
3488
3489         if (start != end) {
3490                 _editor->temp_location->set (start, end);
3491
3492                 double x1 = _editor->frame_to_pixel (start);
3493                 double x2 = _editor->frame_to_pixel (end);
3494                 crect->property_x1() = x1;
3495                 crect->property_x2() = x2;
3496
3497                 update_item (_editor->temp_location);
3498         }
3499
3500         _editor->show_verbose_time_cursor (pf, 10);
3501
3502 }
3503
3504 void
3505 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3506 {
3507         Location * newloc = 0;
3508         string rangename;
3509         int flags;
3510
3511         if (movement_occurred) {
3512                 motion (event, false);
3513                 _drag_rect->hide();
3514
3515                 switch (_operation) {
3516                 case CreateRangeMarker:
3517                 case CreateCDMarker:
3518                     {
3519                         _editor->begin_reversible_command (_("new range marker"));
3520                         XMLNode &before = _editor->session()->locations()->get_state();
3521                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3522                         if (_operation == CreateCDMarker) {
3523                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3524                                 _editor->cd_marker_bar_drag_rect->hide();
3525                         }
3526                         else {
3527                                 flags = Location::IsRangeMarker;
3528                                 _editor->range_bar_drag_rect->hide();
3529                         }
3530                         newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3531                         _editor->session()->locations()->add (newloc, true);
3532                         XMLNode &after = _editor->session()->locations()->get_state();
3533                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3534                         _editor->commit_reversible_command ();
3535                         break;
3536                     }
3537
3538                 case CreateTransportMarker:
3539                         // popup menu to pick loop or punch
3540                         _editor->new_transport_marker_context_menu (&event->button, _item);
3541                         break;
3542                 }
3543         } else {
3544                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3545
3546                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3547
3548                         nframes64_t start;
3549                         nframes64_t end;
3550
3551                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3552
3553                         if (end == max_frames) {
3554                                 end = _editor->session()->current_end_frame ();
3555                         }
3556
3557                         if (start == max_frames) {
3558                                 start = _editor->session()->current_start_frame ();
3559                         }
3560
3561                         switch (_editor->mouse_mode) {
3562                         case MouseObject:
3563                                 /* find the two markers on either side and then make the selection from it */
3564                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3565                                 break;
3566
3567                         case MouseRange:
3568                                 /* find the two markers on either side of the click and make the range out of it */
3569                                 _editor->selection->set (start, end);
3570                                 break;
3571
3572                         default:
3573                                 break;
3574                         }
3575                 }
3576         }
3577
3578         _editor->stop_canvas_autoscroll ();
3579 }
3580
3581 void
3582 RangeMarkerBarDrag::aborted ()
3583 {
3584         /* XXX: TODO */
3585 }
3586
3587 void
3588 RangeMarkerBarDrag::update_item (Location* location)
3589 {
3590         double const x1 = _editor->frame_to_pixel (location->start());
3591         double const x2 = _editor->frame_to_pixel (location->end());
3592
3593         _drag_rect->property_x1() = x1;
3594         _drag_rect->property_x2() = x2;
3595 }
3596
3597 void
3598 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3599 {
3600         Drag::start_grab (event, _editor->zoom_cursor);
3601         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3602 }
3603
3604 void
3605 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3606 {
3607         nframes64_t start;
3608         nframes64_t end;
3609
3610         nframes64_t const pf = adjusted_current_frame (event);
3611
3612         nframes64_t grab = grab_frame ();
3613         _editor->snap_to_with_modifier (grab, event);
3614
3615         /* base start and end on initial click position */
3616         if (pf < grab) {
3617                 start = pf;
3618                 end = grab;
3619         } else {
3620                 end = pf;
3621                 start = grab;
3622         }
3623
3624         if (start != end) {
3625
3626                 if (first_move) {
3627                         _editor->zoom_rect->show();
3628                         _editor->zoom_rect->raise_to_top();
3629                 }
3630
3631                 _editor->reposition_zoom_rect(start, end);
3632
3633                 _editor->show_verbose_time_cursor (pf, 10);
3634         }
3635 }
3636
3637 void
3638 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3639 {
3640         if (movement_occurred) {
3641                 motion (event, false);
3642
3643                 if (grab_frame() < last_pointer_frame()) {
3644                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3645                 } else {
3646                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3647                 }
3648         } else {
3649                 _editor->temporal_zoom_to_frame (false, grab_frame());
3650                 /*
3651                 temporal_zoom_step (false);
3652                 center_screen (grab_frame());
3653                 */
3654         }
3655
3656         _editor->zoom_rect->hide();
3657 }
3658
3659 void
3660 MouseZoomDrag::aborted ()
3661 {
3662         _editor->zoom_rect->hide ();
3663 }
3664
3665 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3666         : Drag (e, i)
3667 {
3668         CanvasNoteEvent*     cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3669         region = &cnote->region_view();
3670 }
3671
3672 void
3673 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3674 {
3675         Drag::start_grab (event);
3676
3677         drag_delta_x = 0;
3678         drag_delta_note = 0;
3679
3680         double event_x;
3681         double event_y;
3682
3683         event_x = _drags->current_pointer_x();
3684         event_y = _drags->current_pointer_y();
3685
3686         _item->property_parent().get_value()->w2i(event_x, event_y);
3687
3688         last_x = region->snap_to_pixel(event_x);
3689         last_y = event_y;
3690
3691         CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3692
3693         if (!(was_selected = cnote->selected())) {
3694
3695                 /* tertiary-click means extend selection - we'll do that on button release,
3696                    so don't add it here, because otherwise we make it hard to figure
3697                    out the "extend-to" range.
3698                 */
3699
3700                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3701
3702                 if (!extend) {
3703                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3704
3705                         if (add) {
3706                                 region->note_selected (cnote, true);
3707                         } else {
3708                                 region->unique_select (cnote);
3709                         }
3710                 }
3711         }
3712 }
3713
3714 void
3715 NoteDrag::motion (GdkEvent*, bool)
3716 {
3717         MidiStreamView* streamview = region->midi_stream_view();
3718         double event_x;
3719         double event_y;
3720
3721         event_x = _drags->current_pointer_x();
3722         event_y = _drags->current_pointer_y();
3723
3724         _item->property_parent().get_value()->w2i(event_x, event_y);
3725
3726         event_x = region->snap_to_pixel(event_x);
3727
3728         double dx     = event_x - last_x;
3729         double dy     = event_y - last_y;
3730         last_x = event_x;
3731
3732         drag_delta_x += dx;
3733
3734         // Snap to note rows
3735
3736         if (abs (dy) < streamview->note_height()) {
3737                 dy = 0.0;
3738         } else {
3739                 int8_t this_delta_note;
3740                 if (dy > 0) {
3741                         this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3742                 } else {
3743                         this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3744                 }
3745                 drag_delta_note -= this_delta_note;
3746                 dy = streamview->note_height() * this_delta_note;
3747                 last_y = last_y + dy;
3748         }
3749
3750         if (dx || dy) {
3751                 
3752                 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3753                 Evoral::MusicalTime new_time;
3754                 
3755                 if (drag_delta_x) {
3756                         nframes64_t start_frames = region->beats_to_frames(cnote->note()->time());
3757                         if (drag_delta_x >= 0) {
3758                                 start_frames += region->snap_frame_to_frame(_editor->pixel_to_frame(drag_delta_x));
3759                         } else {
3760                                 start_frames -= region->snap_frame_to_frame(_editor->pixel_to_frame(-drag_delta_x));
3761                         }
3762                         new_time = region->frames_to_beats(start_frames);
3763                 } else {
3764                         new_time = cnote->note()->time();
3765                 }
3766                 
3767                 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > check_note (
3768                         new Evoral::Note<Evoral::MusicalTime> (cnote->note()->channel(), 
3769                                                                new_time,
3770                                                                cnote->note()->length(), 
3771                                                                cnote->note()->note() + drag_delta_note,
3772                                                                cnote->note()->velocity()));
3773
3774                 region->move_selection (dx, dy);
3775
3776                 char buf[12];
3777                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (cnote->note()->note()).c_str(),
3778                           (int) floor ((cnote->note()->note() + drag_delta_note)));
3779                 _editor->show_verbose_canvas_cursor_with (buf);
3780         }
3781 }
3782
3783 void
3784 NoteDrag::finished (GdkEvent* ev, bool moved)
3785 {
3786         ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3787
3788         if (!moved) {
3789                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3790
3791                         if (was_selected) {
3792                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3793                                 if (add) {
3794                                         region->note_deselected (cnote);
3795                                 }
3796                         } else {
3797                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3798                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3799
3800                                 if (!extend && !add && region->selection_size() > 1) {
3801                                         region->unique_select(cnote);
3802                                 } else if (extend) {
3803                                         region->note_selected (cnote, true, true);
3804                                 } else {
3805                                         /* it was added during button press */
3806                                 }
3807                         }
3808                 }
3809         } else {
3810                 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3811         }
3812 }
3813
3814 void
3815 NoteDrag::aborted ()
3816 {
3817         /* XXX: TODO */
3818 }
3819
3820 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3821         : Drag (e, i)
3822         , _ranges (r)
3823         , _nothing_to_drag (false)
3824 {
3825         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3826         assert (_atav);
3827
3828         _line = _atav->line ();
3829 }
3830
3831 void
3832 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3833 {
3834         Drag::start_grab (event, cursor);
3835
3836         list<ControlPoint*> points;
3837
3838         XMLNode* state = &_line->get_state ();
3839         
3840         if (_ranges.empty()) {
3841                 
3842                 uint32_t const N = _line->npoints ();
3843                 for (uint32_t i = 0; i < N; ++i) {
3844                         points.push_back (_line->nth (i));
3845                 }
3846                 
3847         } else {
3848
3849                 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3850                 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3851
3852                         /* fade into and out of the region that we're dragging;
3853                            64 samples length plucked out of thin air.
3854                         */
3855                         nframes64_t const h = (j->start + j->end) / 2;
3856                         nframes64_t a = j->start + 64;
3857                         if (a > h) {
3858                                 a = h;
3859                         }
3860                         nframes64_t b = j->end - 64;
3861                         if (b < h) {
3862                                 b = h;
3863                         }
3864                         
3865                         the_list->add (j->start, the_list->eval (j->start));
3866                         _line->add_always_in_view (j->start);
3867                         the_list->add (a, the_list->eval (a));
3868                         _line->add_always_in_view (a);
3869                         the_list->add (b, the_list->eval (b));
3870                         _line->add_always_in_view (b);
3871                         the_list->add (j->end, the_list->eval (j->end));
3872                         _line->add_always_in_view (j->end);
3873                 }
3874
3875                 uint32_t const N = _line->npoints ();
3876                 for (uint32_t i = 0; i < N; ++i) {
3877
3878                         ControlPoint* p = _line->nth (i);
3879
3880                         list<AudioRange>::const_iterator j = _ranges.begin ();
3881                         while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3882                                 ++j;
3883                         }
3884
3885                         if (j != _ranges.end()) {
3886                                 points.push_back (p);
3887                         }
3888                 }
3889         }
3890
3891         if (points.empty()) {
3892                 _nothing_to_drag = true;
3893                 return;
3894         }
3895
3896         _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3897 }
3898
3899 void
3900 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3901 {
3902         if (_nothing_to_drag) {
3903                 return;
3904         }
3905         
3906         float const f = 1 - (_drags->current_pointer_y() / _line->height());
3907
3908         /* we are ignoring x position for this drag, so we can just pass in anything */
3909         _line->drag_motion (0, f, true, false);
3910 }
3911
3912 void
3913 AutomationRangeDrag::finished (GdkEvent* event, bool)
3914 {
3915         if (_nothing_to_drag) {
3916                 return;
3917         }
3918         
3919         motion (event, false);
3920         _line->end_drag ();
3921         _line->clear_always_in_view ();
3922 }
3923
3924 void
3925 AutomationRangeDrag::aborted ()
3926 {
3927         _line->clear_always_in_view ();
3928         _line->reset ();
3929 }
3930
3931 DraggingView::DraggingView (RegionView* v)
3932         : view (v)
3933 {
3934         initial_y = v->get_canvas_group()->property_y ();
3935 }