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