Trim include tree.
[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/audioregion.h"
33 #include "ardour/dB.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/operations.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
38
39 #include "editor.h"
40 #include "i18n.h"
41 #include "keyboard.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
44 #include "ardour_ui.h"
45 #include "gui_thread.h"
46 #include "control_point.h"
47 #include "utils.h"
48 #include "region_gain_line.h"
49 #include "editor_drag.h"
50 #include "audio_time_axis.h"
51 #include "midi_time_axis.h"
52 #include "canvas-note.h"
53 #include "selection.h"
54 #include "midi_selection.h"
55 #include "automation_time_axis.h"
56 #include "debug.h"
57 #include "editor_cursors.h"
58 #include "mouse_cursors.h"
59 #include "verbose_cursor.h"
60
61 using namespace std;
62 using namespace ARDOUR;
63 using namespace PBD;
64 using namespace Gtk;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67 using namespace ArdourCanvas;
68
69 using Gtkmm2ext::Keyboard;
70
71 double ControlPointDrag::_zero_gain_fraction = -1.0;
72
73 DragManager::DragManager (Editor* e)
74         : _editor (e)
75         , _ending (false)
76         , _current_pointer_frame (0)
77 {
78 }
79
80 DragManager::~DragManager ()
81 {
82         abort ();
83 }
84
85 /** Call abort for each active drag */
86 void
87 DragManager::abort ()
88 {
89         _ending = true;
90
91         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
92                 (*i)->abort ();
93                 delete *i;
94         }
95
96         if (!_drags.empty ()) {
97                 _editor->set_follow_playhead (_old_follow_playhead, false);
98         }
99
100         _drags.clear ();
101
102         _ending = false;
103 }
104
105 void
106 DragManager::add (Drag* d)
107 {
108         d->set_manager (this);
109         _drags.push_back (d);
110 }
111
112 void
113 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
114 {
115         d->set_manager (this);
116         _drags.push_back (d);
117         start_grab (e, c);
118 }
119
120 void
121 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
122 {
123         /* Prevent follow playhead during the drag to be nice to the user */
124         _old_follow_playhead = _editor->follow_playhead ();
125         _editor->set_follow_playhead (false);
126
127         _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
128
129         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
130                 (*i)->start_grab (e, c);
131         }
132 }
133
134 /** Call end_grab for each active drag.
135  *  @return true if any drag reported movement having occurred.
136  */
137 bool
138 DragManager::end_grab (GdkEvent* e)
139 {
140         _ending = true;
141
142         bool r = false;
143         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144                 bool const t = (*i)->end_grab (e);
145                 if (t) {
146                         r = true;
147                 }
148                 delete *i;
149         }
150
151         _drags.clear ();
152
153         _ending = false;
154
155         _editor->set_follow_playhead (_old_follow_playhead, false);
156
157         return r;
158 }
159
160 bool
161 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
162 {
163         bool r = false;
164
165         _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
166
167         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
168                 bool const t = (*i)->motion_handler (e, from_autoscroll);
169                 if (t) {
170                         r = true;
171                 }
172
173         }
174
175         return r;
176 }
177
178 bool
179 DragManager::have_item (ArdourCanvas::Item* i) const
180 {
181         list<Drag*>::const_iterator j = _drags.begin ();
182         while (j != _drags.end() && (*j)->item () != i) {
183                 ++j;
184         }
185
186         return j != _drags.end ();
187 }
188
189 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
190         : _editor (e)
191         , _item (i)
192         , _pointer_frame_offset (0)
193         , _move_threshold_passed (false)
194         , _raw_grab_frame (0)
195         , _grab_frame (0)
196         , _last_pointer_frame (0)
197 {
198
199 }
200
201 void
202 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
203 {
204         _item->ungrab (0);
205         _item = new_item;
206
207         if (cursor == 0) {
208                 cursor = _editor->which_grabber_cursor ();
209         }
210
211         _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
212 }
213
214 void
215 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
216 {
217         if (cursor == 0) {
218                 cursor = _editor->which_grabber_cursor ();
219         }
220
221         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
222
223         if (Keyboard::is_button2_event (&event->button)) {
224                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
225                         _y_constrained = true;
226                         _x_constrained = false;
227                 } else {
228                         _y_constrained = false;
229                         _x_constrained = true;
230                 }
231         } else {
232                 _x_constrained = false;
233                 _y_constrained = false;
234         }
235
236         _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
237         setup_pointer_frame_offset ();
238         _grab_frame = adjusted_frame (_raw_grab_frame, event);
239         _last_pointer_frame = _grab_frame;
240         _last_pointer_x = _grab_x;
241         _last_pointer_y = _grab_y;
242
243         _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
244                      *cursor,
245                      event->button.time);
246
247         if (_editor->session() && _editor->session()->transport_rolling()) {
248                 _was_rolling = true;
249         } else {
250                 _was_rolling = false;
251         }
252
253         switch (_editor->snap_type()) {
254         case SnapToRegionStart:
255         case SnapToRegionEnd:
256         case SnapToRegionSync:
257         case SnapToRegionBoundary:
258                 _editor->build_region_boundary_cache ();
259                 break;
260         default:
261                 break;
262         }
263 }
264
265 /** Call to end a drag `successfully'.  Ungrabs item and calls
266  *  subclass' finished() method.
267  *
268  *  @param event GDK event, or 0.
269  *  @return true if some movement occurred, otherwise false.
270  */
271 bool
272 Drag::end_grab (GdkEvent* event)
273 {
274         _editor->stop_canvas_autoscroll ();
275
276         _item->ungrab (event ? event->button.time : 0);
277
278         finished (event, _move_threshold_passed);
279
280         _editor->verbose_cursor()->hide ();
281
282         return _move_threshold_passed;
283 }
284
285 framepos_t
286 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
287 {
288         framepos_t pos = 0;
289
290         if (f > _pointer_frame_offset) {
291                 pos = f - _pointer_frame_offset;
292         }
293
294         if (snap) {
295                 _editor->snap_to_with_modifier (pos, event);
296         }
297
298         return pos;
299 }
300
301 framepos_t
302 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
303 {
304         return adjusted_frame (_drags->current_pointer_frame (), event, snap);
305 }
306
307 bool
308 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
309 {
310         /* check to see if we have moved in any way that matters since the last motion event */
311         if (_move_threshold_passed &&
312             (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
313             (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
314                 return false;
315         }
316
317         pair<framecnt_t, int> const threshold = move_threshold ();
318
319         bool const old_move_threshold_passed = _move_threshold_passed;
320
321         if (!from_autoscroll && !_move_threshold_passed) {
322
323                 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
324                 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
325
326                 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
327         }
328
329         if (active (_editor->mouse_mode) && _move_threshold_passed) {
330
331                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
332                         if (!from_autoscroll) {
333                                 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
334                         }
335
336                         motion (event, _move_threshold_passed != old_move_threshold_passed);
337
338                         _last_pointer_x = _drags->current_pointer_x ();
339                         _last_pointer_y = _drags->current_pointer_y ();
340                         _last_pointer_frame = adjusted_current_frame (event);
341
342                         return true;
343                 }
344         }
345         return false;
346 }
347
348 /** Call to abort a drag.  Ungrabs item and calls subclass's aborted () */
349 void
350 Drag::abort ()
351 {
352         if (_item) {
353                 _item->ungrab (0);
354         }
355
356         aborted (_move_threshold_passed);
357
358         _editor->stop_canvas_autoscroll ();
359         _editor->verbose_cursor()->hide ();
360 }
361
362 void
363 Drag::show_verbose_cursor_time (framepos_t frame)
364 {
365         _editor->verbose_cursor()->set_time (
366                 frame,
367                 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
368                 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
369                 );
370
371         _editor->verbose_cursor()->show ();
372 }
373
374 void
375 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
376 {
377         _editor->verbose_cursor()->show (xoffset);
378
379         _editor->verbose_cursor()->set_duration (
380                 start, end,
381                 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
382                 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
383                 );
384 }
385
386 void
387 Drag::show_verbose_cursor_text (string const & text)
388 {
389         _editor->verbose_cursor()->show ();
390
391         _editor->verbose_cursor()->set (
392                 text,
393                 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
394                 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
395                 );
396 }
397
398
399 struct EditorOrderTimeAxisViewSorter {
400     bool operator() (TimeAxisView* a, TimeAxisView* b) {
401             RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
402             RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
403             assert (ra && rb);
404             return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
405     }
406 };
407
408 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
409         : Drag (e, i),
410           _primary (p)
411 {
412         _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
413
414         /* Make a list of non-hidden tracks to refer to during the drag */
415
416         TrackViewList track_views = _editor->track_views;
417         track_views.sort (EditorOrderTimeAxisViewSorter ());
418
419         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
420                 if (!(*i)->hidden()) {
421
422                         _time_axis_views.push_back (*i);
423
424                         TimeAxisView::Children children_list = (*i)->get_child_list ();
425                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
426                                 _time_axis_views.push_back (j->get());
427                         }
428                 }
429         }
430
431         /* the list of views can be empty at this point if this is a region list-insert drag
432          */
433
434         for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
435                 _views.push_back (DraggingView (*i, this));
436         }
437
438         RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
439 }
440
441 void
442 RegionDrag::region_going_away (RegionView* v)
443 {
444         list<DraggingView>::iterator i = _views.begin ();
445         while (i != _views.end() && i->view != v) {
446                 ++i;
447         }
448
449         if (i != _views.end()) {
450                 _views.erase (i);
451         }
452 }
453
454 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
455 int
456 RegionDrag::find_time_axis_view (TimeAxisView* t) const
457 {
458         int i = 0;
459         int const N = _time_axis_views.size ();
460         while (i < N && _time_axis_views[i] != t) {
461                 ++i;
462         }
463
464         if (i == N) {
465                 return -1;
466         }
467
468         return i;
469 }
470
471 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
472         : RegionDrag (e, i, p, v),
473           _brushing (b),
474           _total_x_delta (0)
475 {
476
477 }
478
479
480 void
481 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
482 {
483         Drag::start_grab (event, cursor);
484
485         show_verbose_cursor_time (_last_frame_position);
486
487         pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
488         _last_pointer_time_axis_view = find_time_axis_view (tv.first);
489         _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
490 }
491
492 double
493 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
494 {
495         /* compute the amount of pointer motion in frames, and where
496            the region would be if we moved it by that much.
497         */
498         *pending_region_position = adjusted_current_frame (event);
499
500         framepos_t sync_frame;
501         framecnt_t sync_offset;
502         int32_t sync_dir;
503
504         sync_offset = _primary->region()->sync_offset (sync_dir);
505
506         /* we don't handle a sync point that lies before zero.
507          */
508         if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
509
510                 sync_frame = *pending_region_position + (sync_dir*sync_offset);
511
512                 _editor->snap_to_with_modifier (sync_frame, event);
513
514                 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
515
516         } else {
517                 *pending_region_position = _last_frame_position;
518         }
519
520         if (*pending_region_position > max_framepos - _primary->region()->length()) {
521                 *pending_region_position = _last_frame_position;
522         }
523
524         double dx = 0;
525
526         /* in locked edit mode, reverse the usual meaning of _x_constrained */
527         bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
528
529         if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
530
531                 /* x movement since last time (in pixels) */
532                 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
533
534                 /* total x movement */
535                 framecnt_t total_dx = *pending_region_position;
536                 if (regions_came_from_canvas()) {
537                         total_dx = total_dx - grab_frame ();
538                 }
539
540                 /* check that no regions have gone off the start of the session */
541                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
542                         if ((i->view->region()->position() + total_dx) < 0) {
543                                 dx = 0;
544                                 *pending_region_position = _last_frame_position;
545                                 break;
546                         }
547                 }
548
549                 _last_frame_position = *pending_region_position;
550         }
551
552         return dx;
553 }
554
555 bool
556 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
557 {
558         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
559                 int const n = i->time_axis_view + delta_track;
560                 if (n < 0 || n >= int (_time_axis_views.size())) {
561                         /* off the top or bottom track */
562                         return false;
563                 }
564
565                 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
566                 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
567                         /* not a track, or the wrong type */
568                         return false;
569                 }
570
571                 int const l = i->layer + delta_layer;
572                 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
573                         /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
574                            If it has, the layers will be munged later anyway, so it's ok.
575                         */
576                         return false;
577                 }
578         }
579
580         /* all regions being dragged are ok with this change */
581         return true;
582 }
583
584 void
585 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
586 {
587         assert (!_views.empty ());
588
589         /* Find the TimeAxisView that the pointer is now over */
590         pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
591
592         /* Bail early if we're not over a track */
593         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
594         if (!rtv || !rtv->is_track()) {
595                 _editor->verbose_cursor()->hide ();
596                 return;
597         }
598
599         /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
600
601         /* Here's the current pointer position in terms of time axis view and layer */
602         int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
603         layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
604
605         /* Work out the change in x */
606         framepos_t pending_region_position;
607         double const x_delta = compute_x_delta (event, &pending_region_position);
608
609         /* Work out the change in y */
610         int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
611         int delta_layer = current_pointer_layer - _last_pointer_layer;
612
613         if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
614                 /* this y movement is not allowed, so do no y movement this time */
615                 delta_time_axis_view = 0;
616                 delta_layer = 0;
617         }
618
619         if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
620                 /* haven't reached next snap point, and we're not switching
621                    trackviews nor layers. nothing to do.
622                 */
623                 return;
624         }
625
626         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
627
628         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
629
630                 RegionView* rv = i->view;
631
632                 if (rv->region()->locked()) {
633                         continue;
634                 }
635
636                 if (first_move) {
637
638                         /* here we are calculating the y distance from the
639                            top of the first track view to the top of the region
640                            area of the track view that we're working on */
641
642                         /* this x value is just a dummy value so that we have something
643                            to pass to i2w () */
644
645                         double ix1 = 0;
646
647                         /* distance from the top of this track view to the region area
648                            of our track view is always 1 */
649
650                         double iy1 = 1;
651
652                         /* convert to world coordinates, ie distance from the top of
653                            the ruler section */
654
655                         rv->get_canvas_frame()->i2w (ix1, iy1);
656
657                         /* compensate for the ruler section and the vertical scrollbar position */
658                         iy1 += _editor->get_trackview_group_vertical_offset ();
659
660                         // hide any dependent views
661
662                         rv->get_time_axis_view().hide_dependent_views (*rv);
663
664                         /*
665                           reparent to a non scrolling group so that we can keep the
666                           region selection above all time axis views.
667                           reparenting means we have to move the rv as the two
668                           parent groups have different coordinates.
669                         */
670
671                         rv->get_canvas_group()->property_y() = iy1 - 1;
672                         rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
673
674                         rv->fake_set_opaque (true);
675                 }
676
677                 /* Work out the change in y position of this region view */
678
679                 double y_delta = 0;
680
681                 /* If we have moved tracks, we'll fudge the layer delta so that the
682                    region gets moved back onto layer 0 on its new track; this avoids
683                    confusion when dragging regions from non-zero layers onto different
684                    tracks.
685                 */
686                 int this_delta_layer = delta_layer;
687                 if (delta_time_axis_view != 0) {
688                         this_delta_layer = - i->layer;
689                 }
690
691                 /* Move this region to layer 0 on its old track */
692                 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
693                 if (lv->layer_display() == Stacked) {
694                         y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
695                 }
696
697                 /* Now move it to its right layer on the current track */
698                 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
699                 if (cv->layer_display() == Stacked) {
700                         y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
701                 }
702
703                 /* Move tracks */
704                 if (delta_time_axis_view > 0) {
705                         for (int j = 0; j < delta_time_axis_view; ++j) {
706                                 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
707                         }
708                 } else {
709                         /* start by subtracting the height of the track above where we are now */
710                         for (int j = 1; j <= -delta_time_axis_view; ++j) {
711                                 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
712                         }
713                 }
714
715                 /* Set height */
716                 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
717
718                 /* Update the DraggingView */
719                 i->time_axis_view += delta_time_axis_view;
720                 i->layer += this_delta_layer;
721
722                 if (_brushing) {
723                         _editor->mouse_brush_insert_region (rv, pending_region_position);
724                 } else {
725                         rv->move (x_delta, y_delta);
726                 }
727
728         } /* foreach region */
729
730         _total_x_delta += x_delta;
731
732         if (first_move) {
733                 _editor->cursor_group->raise_to_top();
734         }
735
736         if (x_delta != 0 && !_brushing) {
737                 show_verbose_cursor_time (_last_frame_position);
738         }
739
740         _last_pointer_time_axis_view += delta_time_axis_view;
741         _last_pointer_layer += delta_layer;
742 }
743
744 void
745 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
746 {
747         if (_copy && first_move) {
748
749                 /* duplicate the regionview(s) and region(s) */
750
751                 list<DraggingView> new_regionviews;
752
753                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
754
755                         RegionView* rv = i->view;
756                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
757                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
758
759                         const boost::shared_ptr<const Region> original = rv->region();
760                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
761                         region_copy->set_position (original->position());
762
763                         RegionView* nrv;
764                         if (arv) {
765                                 boost::shared_ptr<AudioRegion> audioregion_copy
766                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
767
768                                 nrv = new AudioRegionView (*arv, audioregion_copy);
769                         } else if (mrv) {
770                                 boost::shared_ptr<MidiRegion> midiregion_copy
771                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
772                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
773                         } else {
774                                 continue;
775                         }
776
777                         nrv->get_canvas_group()->show ();
778                         new_regionviews.push_back (DraggingView (nrv, this));
779
780                         /* swap _primary to the copy */
781
782                         if (rv == _primary) {
783                                 _primary = nrv;
784                         }
785
786                         /* ..and deselect the one we copied */
787
788                         rv->set_selected (false);
789                 }
790
791                 if (!new_regionviews.empty()) {
792
793                         /* reflect the fact that we are dragging the copies */
794
795                         _views = new_regionviews;
796
797                         swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
798
799                         /*
800                           sync the canvas to what we think is its current state
801                           without it, the canvas seems to
802                           "forget" to update properly after the upcoming reparent()
803                           ..only if the mouse is in rapid motion at the time of the grab.
804                           something to do with regionview creation taking so long?
805                         */
806                         _editor->update_canvas_now();
807                 }
808         }
809
810         RegionMotionDrag::motion (event, first_move);
811 }
812
813 void
814 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
815 {
816         if (!movement_occurred) {
817                 /* just a click */
818                 return;
819         }
820
821         /* reverse this here so that we have the correct logic to finalize
822            the drag.
823         */
824
825         if (Config->get_edit_mode() == Lock) {
826                 _x_constrained = !_x_constrained;
827         }
828
829         assert (!_views.empty ());
830
831         bool const changed_position = (_last_frame_position != _primary->region()->position());
832         bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
833         framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
834
835         _editor->update_canvas_now ();
836
837         if (_copy) {
838
839                 finished_copy (
840                         changed_position,
841                         changed_tracks,
842                         drag_delta
843                         );
844
845         } else {
846
847                 finished_no_copy (
848                         changed_position,
849                         changed_tracks,
850                         drag_delta
851                         );
852
853         }
854 }
855
856 void
857 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
858 {
859         RegionSelection new_views;
860         PlaylistSet modified_playlists;
861         list<RegionView*> views_to_delete;
862
863         if (_brushing) {
864                 /* all changes were made during motion event handlers */
865
866                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
867                         delete i->view;
868                 }
869
870                 _editor->commit_reversible_command ();
871                 return;
872         }
873
874         if (_x_constrained) {
875                 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
876         } else {
877                 _editor->begin_reversible_command (Operations::region_copy);
878         }
879
880         /* insert the regions into their new playlists */
881         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
882
883                 if (i->view->region()->locked()) {
884                         continue;
885                 }
886
887                 framepos_t where;
888
889                 if (changed_position && !_x_constrained) {
890                         where = i->view->region()->position() - drag_delta;
891                 } else {
892                         where = i->view->region()->position();
893                 }
894
895                 RegionView* new_view = insert_region_into_playlist (
896                         i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
897                         );
898
899                 if (new_view == 0) {
900                         continue;
901                 }
902
903                 new_views.push_back (new_view);
904
905                 /* we don't need the copied RegionView any more */
906                 views_to_delete.push_back (i->view);
907         }
908
909         /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
910            because when views are deleted they are automagically removed from _views, which messes
911            up the iteration.
912         */
913         for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
914                 delete *i;
915         }
916
917         /* If we've created new regions either by copying or moving
918            to a new track, we want to replace the old selection with the new ones
919         */
920
921         if (new_views.size() > 0) {
922                 _editor->selection->set (new_views);
923         }
924
925         /* write commands for the accumulated diffs for all our modified playlists */
926         add_stateful_diff_commands_for_playlists (modified_playlists);
927
928         _editor->commit_reversible_command ();
929 }
930
931 void
932 RegionMoveDrag::finished_no_copy (
933         bool const changed_position,
934         bool const changed_tracks,
935         framecnt_t const drag_delta
936         )
937 {
938         RegionSelection new_views;
939         PlaylistSet modified_playlists;
940         PlaylistSet frozen_playlists;
941
942         if (_brushing) {
943                 /* all changes were made during motion event handlers */
944                 _editor->commit_reversible_command ();
945                 return;
946         }
947
948         if (_x_constrained) {
949                 _editor->begin_reversible_command (_("fixed time region drag"));
950         } else {
951                 _editor->begin_reversible_command (Operations::region_drag);
952         }
953
954         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
955
956                 RegionView* rv = i->view;
957
958                 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
959                 layer_t const dest_layer = i->layer;
960
961                 if (rv->region()->locked()) {
962                         ++i;
963                         continue;
964                 }
965
966                 framepos_t where;
967
968                 if (changed_position && !_x_constrained) {
969                         where = rv->region()->position() - drag_delta;
970                 } else {
971                         where = rv->region()->position();
972                 }
973
974                 if (changed_tracks) {
975
976                         /* insert into new playlist */
977
978                         RegionView* new_view = insert_region_into_playlist (
979                                 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
980                                 );
981
982                         if (new_view == 0) {
983                                 ++i;
984                                 continue;
985                         }
986
987                         new_views.push_back (new_view);
988
989                         /* remove from old playlist */
990
991                         /* the region that used to be in the old playlist is not
992                            moved to the new one - we use a copy of it. as a result,
993                            any existing editor for the region should no longer be
994                            visible.
995                         */
996                         rv->hide_region_editor();
997                         rv->fake_set_opaque (false);
998
999                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1000
1001                 } else {
1002
1003                         rv->region()->clear_changes ();
1004
1005                         /*
1006                            motion on the same track. plonk the previously reparented region
1007                            back to its original canvas group (its streamview).
1008                            No need to do anything for copies as they are fake regions which will be deleted.
1009                         */
1010
1011                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1012                         rv->get_canvas_group()->property_y() = i->initial_y;
1013                         rv->get_time_axis_view().reveal_dependent_views (*rv);
1014
1015                         /* just change the model */
1016
1017                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1018
1019                         if (dest_rtv->view()->layer_display() == Stacked) {
1020                                 rv->region()->set_layer (dest_layer);
1021                                 rv->region()->set_pending_explicit_relayer (true);
1022                         }
1023
1024                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1025
1026                         pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1027
1028                         if (r.second) {
1029                                 playlist->freeze ();
1030                         }
1031
1032                         /* this movement may result in a crossfade being modified, so we need to get undo
1033                            data from the playlist as well as the region.
1034                         */
1035
1036                         r = modified_playlists.insert (playlist);
1037                         if (r.second) {
1038                                 playlist->clear_changes ();
1039                         }
1040
1041                         rv->region()->set_position (where);
1042
1043                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1044                 }
1045
1046                 if (changed_tracks) {
1047
1048                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1049                            was selected in all of them, then removing it from a playlist will have removed all
1050                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1051                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1052                            corresponding regionview, and _views is now empty).
1053
1054                            This could have invalidated any and all iterators into _views.
1055
1056                            The heuristic we use here is: if the region selection is empty, break out of the loop
1057                            here. if the region selection is not empty, then restart the loop because we know that
1058                            we must have removed at least the region(view) we've just been working on as well as any
1059                            that we processed on previous iterations.
1060
1061                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1062                            we can just iterate.
1063                         */
1064
1065
1066                         if (_views.empty()) {
1067                                 break;
1068                         } else {
1069                                 i = _views.begin();
1070                         }
1071
1072                 } else {
1073                         ++i;
1074                 }
1075         }
1076
1077         /* If we've created new regions either by copying or moving
1078            to a new track, we want to replace the old selection with the new ones
1079         */
1080
1081         if (new_views.size() > 0) {
1082                 _editor->selection->set (new_views);
1083         }
1084
1085         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1086                 (*p)->thaw();
1087         }
1088
1089         /* write commands for the accumulated diffs for all our modified playlists */
1090         add_stateful_diff_commands_for_playlists (modified_playlists);
1091
1092         _editor->commit_reversible_command ();
1093 }
1094
1095 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1096  *  @param region Region to remove.
1097  *  @param playlist playlist To remove from.
1098  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1099  *  that clear_changes () is only called once per playlist.
1100  */
1101 void
1102 RegionMoveDrag::remove_region_from_playlist (
1103         boost::shared_ptr<Region> region,
1104         boost::shared_ptr<Playlist> playlist,
1105         PlaylistSet& modified_playlists
1106         )
1107 {
1108         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1109
1110         if (r.second) {
1111                 playlist->clear_changes ();
1112         }
1113
1114         playlist->remove_region (region);
1115 }
1116
1117
1118 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1119  *  clearing the playlist's diff history first if necessary.
1120  *  @param region Region to insert.
1121  *  @param dest_rtv Destination RouteTimeAxisView.
1122  *  @param dest_layer Destination layer.
1123  *  @param where Destination position.
1124  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1125  *  that clear_changes () is only called once per playlist.
1126  *  @return New RegionView, or 0 if no insert was performed.
1127  */
1128 RegionView *
1129 RegionMoveDrag::insert_region_into_playlist (
1130         boost::shared_ptr<Region> region,
1131         RouteTimeAxisView* dest_rtv,
1132         layer_t dest_layer,
1133         framecnt_t where,
1134         PlaylistSet& modified_playlists
1135         )
1136 {
1137         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1138         if (!dest_playlist) {
1139                 return 0;
1140         }
1141
1142         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1143         _new_region_view = 0;
1144         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1145
1146         /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1147         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1148         if (r.second) {
1149                 dest_playlist->clear_changes ();
1150         }
1151
1152         dest_playlist->add_region (region, where);
1153
1154         if (dest_rtv->view()->layer_display() == Stacked) {
1155                 region->set_layer (dest_layer);
1156                 region->set_pending_explicit_relayer (true);
1157         }
1158
1159         c.disconnect ();
1160
1161         assert (_new_region_view);
1162
1163         return _new_region_view;
1164 }
1165
1166 void
1167 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1168 {
1169         _new_region_view = rv;
1170 }
1171
1172 void
1173 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1174 {
1175         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1176                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1177                 if (!c->empty()) {
1178                         _editor->session()->add_command (c);
1179                 } else {
1180                         delete c;
1181                 }
1182         }
1183 }
1184
1185
1186 void
1187 RegionMoveDrag::aborted (bool movement_occurred)
1188 {
1189         if (_copy) {
1190
1191                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1192                         delete i->view;
1193                 }
1194
1195                 _views.clear ();
1196
1197         } else {
1198                 RegionMotionDrag::aborted (movement_occurred);
1199         }
1200 }
1201
1202 void
1203 RegionMotionDrag::aborted (bool)
1204 {
1205         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1206                 RegionView* rv = i->view;
1207                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1208                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1209                 assert (rtv);
1210                 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1211                 rv->get_canvas_group()->property_y() = 0;
1212                 rv->get_time_axis_view().reveal_dependent_views (*rv);
1213                 rv->fake_set_opaque (false);
1214                 rv->move (-_total_x_delta, 0);
1215                 rv->set_height (rtv->view()->child_height ());
1216         }
1217
1218         _editor->update_canvas_now ();
1219 }
1220
1221 /** @param b true to brush, otherwise false.
1222  *  @param c true to make copies of the regions being moved, otherwise false.
1223  */
1224 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1225         : RegionMotionDrag (e, i, p, v, b),
1226           _copy (c)
1227 {
1228         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1229
1230         double speed = 1;
1231         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1232         if (rtv && rtv->is_track()) {
1233                 speed = rtv->track()->speed ();
1234         }
1235
1236         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1237 }
1238
1239 void
1240 RegionMoveDrag::setup_pointer_frame_offset ()
1241 {
1242         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1243 }
1244
1245 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1246         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1247 {
1248         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1249
1250         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1251                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1252
1253         _primary = v->view()->create_region_view (r, false, false);
1254
1255         _primary->get_canvas_group()->show ();
1256         _primary->set_position (pos, 0);
1257         _views.push_back (DraggingView (_primary, this));
1258
1259         _last_frame_position = pos;
1260
1261         _item = _primary->get_canvas_group ();
1262 }
1263
1264 void
1265 RegionInsertDrag::finished (GdkEvent *, bool)
1266 {
1267         _editor->update_canvas_now ();
1268
1269         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1270
1271         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1272         _primary->get_canvas_group()->property_y() = 0;
1273
1274         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1275
1276         _editor->begin_reversible_command (Operations::insert_region);
1277         playlist->clear_changes ();
1278         playlist->add_region (_primary->region (), _last_frame_position);
1279         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1280         _editor->commit_reversible_command ();
1281
1282         delete _primary;
1283         _primary = 0;
1284         _views.clear ();
1285 }
1286
1287 void
1288 RegionInsertDrag::aborted (bool)
1289 {
1290         delete _primary;
1291         _primary = 0;
1292         _views.clear ();
1293 }
1294
1295 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1296         : RegionMoveDrag (e, i, p, v, false, false)
1297 {
1298         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1299 }
1300
1301 struct RegionSelectionByPosition {
1302     bool operator() (RegionView*a, RegionView* b) {
1303             return a->region()->position () < b->region()->position();
1304     }
1305 };
1306
1307 void
1308 RegionSpliceDrag::motion (GdkEvent* event, bool)
1309 {
1310         /* Which trackview is this ? */
1311
1312         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1313         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1314
1315         /* The region motion is only processed if the pointer is over
1316            an audio track.
1317         */
1318
1319         if (!tv || !tv->is_track()) {
1320                 /* To make sure we hide the verbose canvas cursor when the mouse is
1321                    not held over and audiotrack.
1322                 */
1323                 _editor->verbose_cursor()->hide ();
1324                 return;
1325         }
1326
1327         int dir;
1328
1329         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1330                 dir = 1;
1331         } else {
1332                 dir = -1;
1333         }
1334
1335         RegionSelection copy (_editor->selection->regions);
1336
1337         RegionSelectionByPosition cmp;
1338         copy.sort (cmp);
1339
1340         framepos_t const pf = adjusted_current_frame (event);
1341
1342         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1343
1344                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1345
1346                 if (!atv) {
1347                         continue;
1348                 }
1349
1350                 boost::shared_ptr<Playlist> playlist;
1351
1352                 if ((playlist = atv->playlist()) == 0) {
1353                         continue;
1354                 }
1355
1356                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1357                         continue;
1358                 }
1359
1360                 if (dir > 0) {
1361                         if (pf < (*i)->region()->last_frame() + 1) {
1362                                 continue;
1363                         }
1364                 } else {
1365                         if (pf > (*i)->region()->first_frame()) {
1366                                 continue;
1367                         }
1368                 }
1369
1370
1371                 playlist->shuffle ((*i)->region(), dir);
1372         }
1373 }
1374
1375 void
1376 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1377 {
1378         RegionMoveDrag::finished (event, movement_occurred);
1379 }
1380
1381 void
1382 RegionSpliceDrag::aborted (bool)
1383 {
1384         /* XXX: TODO */
1385 }
1386
1387 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1388         : Drag (e, i),
1389           _view (dynamic_cast<MidiTimeAxisView*> (v))
1390 {
1391         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1392
1393         assert (_view);
1394 }
1395
1396 void
1397 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1398 {
1399         if (first_move) {
1400                 add_region();
1401                 _view->playlist()->freeze ();
1402         } else {
1403                 if (_region) {
1404                         framepos_t const f = adjusted_current_frame (event);
1405                         if (f < grab_frame()) {
1406                                 _region->set_position (f);
1407                         }
1408
1409                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1410                            so that if this region is duplicated, its duplicate starts on
1411                            a snap point rather than 1 frame after a snap point.  Otherwise things get
1412                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
1413                            place snapped notes at the start of the region.
1414                         */
1415
1416                         framecnt_t const len = abs (f - grab_frame () - 1);
1417                         _region->set_length (len < 1 ? 1 : len);
1418                 }
1419         }
1420 }
1421
1422 void
1423 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1424 {
1425         if (!movement_occurred) {
1426                 add_region ();
1427         } else {
1428                 _view->playlist()->thaw ();
1429         }
1430
1431         if (_region) {
1432                 _editor->commit_reversible_command ();
1433         }
1434 }
1435
1436 void
1437 RegionCreateDrag::add_region ()
1438 {
1439         if (_editor->session()) {
1440                 const TempoMap& map (_editor->session()->tempo_map());
1441                 framecnt_t pos = grab_frame();
1442                 const Meter& m = map.meter_at (pos);
1443                 /* not that the frame rate used here can be affected by pull up/down which
1444                    might be wrong.
1445                 */
1446                 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1447                 _region = _view->add_region (grab_frame(), len, false);
1448         }
1449 }
1450
1451 void
1452 RegionCreateDrag::aborted (bool)
1453 {
1454         if (_region) {
1455                 _view->playlist()->thaw ();
1456         }
1457
1458         /* XXX */
1459 }
1460
1461 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1462         : Drag (e, i)
1463         , region (0)
1464 {
1465         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1466 }
1467
1468 void
1469 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1470 {
1471         Gdk::Cursor* cursor;
1472         ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1473         float x_fraction = cnote->mouse_x_fraction ();
1474
1475         if (x_fraction > 0.0 && x_fraction < 0.25) {
1476                 cursor = _editor->cursors()->left_side_trim;
1477         } else  {
1478                 cursor = _editor->cursors()->right_side_trim;
1479         }
1480
1481         Drag::start_grab (event, cursor);
1482
1483         region = &cnote->region_view();
1484
1485         double const region_start = region->get_position_pixels();
1486         double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1487
1488         if (grab_x() <= middle_point) {
1489                 cursor = _editor->cursors()->left_side_trim;
1490                 at_front = true;
1491         } else {
1492                 cursor = _editor->cursors()->right_side_trim;
1493                 at_front = false;
1494         }
1495
1496         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1497
1498         if (event->motion.state & Keyboard::PrimaryModifier) {
1499                 relative = false;
1500         } else {
1501                 relative = true;
1502         }
1503
1504         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1505
1506         if (ms.size() > 1) {
1507                 /* has to be relative, may make no sense otherwise */
1508                 relative = true;
1509         }
1510
1511         /* select this note; if it is already selected, preserve the existing selection,
1512            otherwise make this note the only one selected.
1513         */
1514         region->note_selected (cnote, cnote->selected ());
1515
1516         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1517                 MidiRegionSelection::iterator next;
1518                 next = r;
1519                 ++next;
1520                 (*r)->begin_resizing (at_front);
1521                 r = next;
1522         }
1523 }
1524
1525 void
1526 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1527 {
1528         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1529         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1530                 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1531         }
1532 }
1533
1534 void
1535 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1536 {
1537         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1538         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1539                 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1540         }
1541 }
1542
1543 void
1544 NoteResizeDrag::aborted (bool)
1545 {
1546         /* XXX: TODO */
1547 }
1548
1549 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1550         : Drag (e, i)
1551 {
1552         DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1553 }
1554
1555 void
1556 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1557 {
1558
1559 }
1560
1561 void
1562 RegionGainDrag::finished (GdkEvent *, bool)
1563 {
1564
1565 }
1566
1567 void
1568 RegionGainDrag::aborted (bool)
1569 {
1570         /* XXX: TODO */
1571 }
1572
1573 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1574         : RegionDrag (e, i, p, v)
1575 {
1576         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1577 }
1578
1579 void
1580 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1581 {
1582         double speed = 1.0;
1583         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1584         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1585
1586         if (tv && tv->is_track()) {
1587                 speed = tv->track()->speed();
1588         }
1589
1590         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1591         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1592         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1593
1594         framepos_t const pf = adjusted_current_frame (event);
1595
1596         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1597                 /* Move the contents of the region around without changing the region bounds */
1598                 _operation = ContentsTrim;
1599                 Drag::start_grab (event, _editor->cursors()->trimmer);
1600         } else {
1601                 /* These will get overridden for a point trim.*/
1602                 if (pf < (region_start + region_length/2)) {
1603                         /* closer to front */
1604                         _operation = StartTrim;
1605                         Drag::start_grab (event, _editor->cursors()->left_side_trim);
1606                 } else {
1607                         /* closer to end */
1608                         _operation = EndTrim;
1609                         Drag::start_grab (event, _editor->cursors()->right_side_trim);
1610                 }
1611         }
1612
1613         switch (_operation) {
1614         case StartTrim:
1615                 show_verbose_cursor_time (region_start);
1616                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1617                         i->view->trim_front_starting ();
1618                 }
1619                 break;
1620         case EndTrim:
1621                 show_verbose_cursor_time (region_end);
1622                 break;
1623         case ContentsTrim:
1624                 show_verbose_cursor_time (pf);
1625                 break;
1626         }
1627
1628         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1629                 i->view->region()->suspend_property_changes ();
1630         }
1631 }
1632
1633 void
1634 TrimDrag::motion (GdkEvent* event, bool first_move)
1635 {
1636         RegionView* rv = _primary;
1637
1638         double speed = 1.0;
1639         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1640         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1641         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1642
1643         if (tv && tv->is_track()) {
1644                 speed = tv->track()->speed();
1645         }
1646
1647         framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1648
1649         if (first_move) {
1650
1651                 string trim_type;
1652
1653                 switch (_operation) {
1654                 case StartTrim:
1655                         trim_type = "Region start trim";
1656                         break;
1657                 case EndTrim:
1658                         trim_type = "Region end trim";
1659                         break;
1660                 case ContentsTrim:
1661                         trim_type = "Region content trim";
1662                         break;
1663                 }
1664
1665                 _editor->begin_reversible_command (trim_type);
1666
1667                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1668                         RegionView* rv = i->view;
1669                         rv->fake_set_opaque (false);
1670                         rv->enable_display (false);
1671                         rv->region()->playlist()->clear_owned_changes ();
1672
1673                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1674
1675                         if (arv) {
1676                                 arv->temporarily_hide_envelope ();
1677                         }
1678
1679                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1680                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1681
1682                         if (insert_result.second) {
1683                                 pl->freeze();
1684                         }
1685                 }
1686         }
1687
1688         bool non_overlap_trim = false;
1689
1690         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1691                 non_overlap_trim = true;
1692         }
1693
1694         switch (_operation) {
1695         case StartTrim:
1696                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1697                         i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1698                 }
1699                 break;
1700
1701         case EndTrim:
1702                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1703                         i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1704                 }
1705                 break;
1706
1707         case ContentsTrim:
1708                 {
1709                         bool swap_direction = false;
1710
1711                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1712                                 swap_direction = true;
1713                         }
1714
1715                         framecnt_t frame_delta = 0;
1716
1717                         bool left_direction = false;
1718                         if (last_pointer_frame() > adjusted_current_frame(event)) {
1719                                 left_direction = true;
1720                         }
1721
1722                         if (left_direction) {
1723                                 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1724                         } else {
1725                                 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1726                         }
1727
1728                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1729                                 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1730                         }
1731                 }
1732                 break;
1733         }
1734
1735         switch (_operation) {
1736         case StartTrim:
1737                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1738                 break;
1739         case EndTrim:
1740                 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1741                 break;
1742         case ContentsTrim:
1743                 show_verbose_cursor_time (adjusted_current_frame (event));
1744                 break;
1745         }
1746 }
1747
1748
1749 void
1750 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1751 {
1752         if (movement_occurred) {
1753                 motion (event, false);
1754
1755                 /* This must happen before the region's StatefulDiffCommand is created, as it may
1756                    `correct' (ahem) the region's _start from being negative to being zero.  It
1757                    needs to be zero in the undo record.
1758                 */
1759                 if (_operation == StartTrim) {
1760                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1761                                 i->view->trim_front_ending ();
1762                         }
1763                 }
1764
1765                 if (!_editor->selection->selected (_primary)) {
1766                         _primary->thaw_after_trim ();
1767                 } else {
1768
1769                         set<boost::shared_ptr<Playlist> > diffed_playlists;
1770
1771                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1772                                 i->view->thaw_after_trim ();
1773                                 i->view->enable_display (true);
1774                                 i->view->fake_set_opaque (true);
1775
1776                                 /* Trimming one region may affect others on the playlist, so we need
1777                                    to get undo Commands from the whole playlist rather than just the
1778                                    region.  Use diffed_playlists to make sure we don't diff a given
1779                                    playlist more than once.
1780                                 */
1781                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1782                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1783                                         vector<Command*> cmds;
1784                                         p->rdiff (cmds);
1785                                         _editor->session()->add_commands (cmds);
1786                                         diffed_playlists.insert (p);
1787                                 }
1788                         }
1789                 }
1790                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1791                         (*p)->thaw ();
1792                 }
1793
1794                 _editor->motion_frozen_playlists.clear ();
1795                 _editor->commit_reversible_command();
1796
1797         } else {
1798                 /* no mouse movement */
1799                 _editor->point_trim (event, adjusted_current_frame (event));
1800         }
1801
1802         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1803                 if (_operation == StartTrim) {
1804                         i->view->trim_front_ending ();
1805                 }
1806
1807                 i->view->region()->resume_property_changes ();
1808         }
1809 }
1810
1811 void
1812 TrimDrag::aborted (bool movement_occurred)
1813 {
1814         /* Our motion method is changing model state, so use the Undo system
1815            to cancel.  Perhaps not ideal, as this will leave an Undo point
1816            behind which may be slightly odd from the user's point of view.
1817         */
1818
1819         finished (0, true);
1820
1821         if (movement_occurred) {
1822                 _editor->undo ();
1823         }
1824
1825         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1826                 i->view->region()->resume_property_changes ();
1827         }
1828 }
1829
1830 void
1831 TrimDrag::setup_pointer_frame_offset ()
1832 {
1833         list<DraggingView>::iterator i = _views.begin ();
1834         while (i != _views.end() && i->view != _primary) {
1835                 ++i;
1836         }
1837
1838         if (i == _views.end()) {
1839                 return;
1840         }
1841
1842         switch (_operation) {
1843         case StartTrim:
1844                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1845                 break;
1846         case EndTrim:
1847                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1848                 break;
1849         case ContentsTrim:
1850                 break;
1851         }
1852 }
1853
1854 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1855         : Drag (e, i),
1856           _copy (c)
1857 {
1858         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1859
1860         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1861         assert (_marker);
1862 }
1863
1864 void
1865 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1866 {
1867         if (_copy) {
1868                 // create a dummy marker for visual representation of moving the copy.
1869                 // The actual copying is not done before we reach the finish callback.
1870                 char name[64];
1871                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1872
1873                 MeterMarker* new_marker = new MeterMarker (
1874                         *_editor,
1875                         *_editor->meter_group,
1876                         ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1877                         name,
1878                         *new MeterSection (_marker->meter())
1879                         );
1880
1881                 _item = &new_marker->the_item ();
1882                 _marker = new_marker;
1883
1884         } else {
1885
1886                 MetricSection& section (_marker->meter());
1887
1888                 if (!section.movable()) {
1889                         return;
1890                 }
1891
1892         }
1893
1894         Drag::start_grab (event, cursor);
1895
1896         show_verbose_cursor_time (adjusted_current_frame(event));
1897 }
1898
1899 void
1900 MeterMarkerDrag::setup_pointer_frame_offset ()
1901 {
1902         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1903 }
1904
1905 void
1906 MeterMarkerDrag::motion (GdkEvent* event, bool)
1907 {
1908         framepos_t const pf = adjusted_current_frame (event);
1909
1910         _marker->set_position (pf);
1911
1912         show_verbose_cursor_time (pf);
1913 }
1914
1915 void
1916 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1917 {
1918         if (!movement_occurred) {
1919                 return;
1920         }
1921
1922         motion (event, false);
1923
1924         Timecode::BBT_Time when;
1925
1926         TempoMap& map (_editor->session()->tempo_map());
1927         map.bbt_time (last_pointer_frame(), when);
1928
1929         if (_copy == true) {
1930                 _editor->begin_reversible_command (_("copy meter mark"));
1931                 XMLNode &before = map.get_state();
1932                 map.add_meter (_marker->meter(), when);
1933                 XMLNode &after = map.get_state();
1934                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1935                 _editor->commit_reversible_command ();
1936
1937                 // delete the dummy marker we used for visual representation of copying.
1938                 // a new visual marker will show up automatically.
1939                 delete _marker;
1940         } else {
1941                 _editor->begin_reversible_command (_("move meter mark"));
1942                 XMLNode &before = map.get_state();
1943                 map.move_meter (_marker->meter(), when);
1944                 XMLNode &after = map.get_state();
1945                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1946                 _editor->commit_reversible_command ();
1947         }
1948 }
1949
1950 void
1951 MeterMarkerDrag::aborted (bool)
1952 {
1953         _marker->set_position (_marker->meter().frame ());
1954 }
1955
1956 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1957         : Drag (e, i),
1958           _copy (c)
1959 {
1960         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1961
1962         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1963         assert (_marker);
1964 }
1965
1966 void
1967 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1968 {
1969         if (_copy) {
1970
1971                 // create a dummy marker for visual representation of moving the copy.
1972                 // The actual copying is not done before we reach the finish callback.
1973                 char name[64];
1974                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1975
1976                 TempoMarker* new_marker = new TempoMarker (
1977                         *_editor,
1978                         *_editor->tempo_group,
1979                         ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1980                         name,
1981                         *new TempoSection (_marker->tempo())
1982                         );
1983
1984                 _item = &new_marker->the_item ();
1985                 _marker = new_marker;
1986
1987         }
1988
1989         Drag::start_grab (event, cursor);
1990
1991         show_verbose_cursor_time (adjusted_current_frame (event));
1992 }
1993
1994 void
1995 TempoMarkerDrag::setup_pointer_frame_offset ()
1996 {
1997         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1998 }
1999
2000 void
2001 TempoMarkerDrag::motion (GdkEvent* event, bool)
2002 {
2003         framepos_t const pf = adjusted_current_frame (event);
2004         _marker->set_position (pf);
2005         show_verbose_cursor_time (pf);
2006 }
2007
2008 void
2009 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2010 {
2011         if (!movement_occurred) {
2012                 return;
2013         }
2014
2015         motion (event, false);
2016
2017         Timecode::BBT_Time when;
2018
2019         TempoMap& map (_editor->session()->tempo_map());
2020         map.bbt_time (last_pointer_frame(), when);
2021
2022         if (_copy == true) {
2023                 _editor->begin_reversible_command (_("copy tempo mark"));
2024                 XMLNode &before = map.get_state();
2025                 map.add_tempo (_marker->tempo(), when);
2026                 XMLNode &after = map.get_state();
2027                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2028                 _editor->commit_reversible_command ();
2029
2030                 // delete the dummy marker we used for visual representation of copying.
2031                 // a new visual marker will show up automatically.
2032                 delete _marker;
2033         } else {
2034                 _editor->begin_reversible_command (_("move tempo mark"));
2035                 XMLNode &before = map.get_state();
2036                 map.move_tempo (_marker->tempo(), when);
2037                 XMLNode &after = map.get_state();
2038                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2039                 _editor->commit_reversible_command ();
2040         }
2041 }
2042
2043 void
2044 TempoMarkerDrag::aborted (bool)
2045 {
2046         _marker->set_position (_marker->tempo().frame());
2047 }
2048
2049 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2050         : Drag (e, i),
2051           _stop (s)
2052 {
2053         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2054 }
2055
2056 /** Do all the things we do when dragging the playhead to make it look as though
2057  *  we have located, without actually doing the locate (because that would cause
2058  *  the diskstream buffers to be refilled, which is too slow).
2059  */
2060 void
2061 CursorDrag::fake_locate (framepos_t t)
2062 {
2063         _editor->playhead_cursor->set_position (t);
2064
2065         Session* s = _editor->session ();
2066         if (s->timecode_transmission_suspended ()) {
2067                 framepos_t const f = _editor->playhead_cursor->current_frame;
2068                 s->send_mmc_locate (f);
2069                 s->send_full_time_code (f);
2070         }
2071
2072         show_verbose_cursor_time (t);
2073         _editor->UpdateAllTransportClocks (t);
2074 }
2075
2076 void
2077 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2078 {
2079         Drag::start_grab (event, c);
2080
2081         _grab_zoom = _editor->frames_per_unit;
2082
2083         framepos_t where = _editor->event_frame (event, 0, 0);
2084         _editor->snap_to_with_modifier (where, event);
2085
2086         _editor->_dragging_playhead = true;
2087
2088         Session* s = _editor->session ();
2089
2090         if (s) {
2091                 if (_was_rolling && _stop) {
2092                         s->request_stop ();
2093                 }
2094
2095                 if (s->is_auditioning()) {
2096                         s->cancel_audition ();
2097                 }
2098
2099                 s->request_suspend_timecode_transmission ();
2100                 while (!s->timecode_transmission_suspended ()) {
2101                         /* twiddle our thumbs */
2102                 }
2103         }
2104
2105         fake_locate (where);
2106 }
2107
2108 void
2109 CursorDrag::motion (GdkEvent* event, bool)
2110 {
2111         if (_drags->current_pointer_y() != last_pointer_y()) {
2112
2113                 /* zoom when we move the pointer up and down */
2114
2115                 /* y range to operate over (pixels) */
2116                 double const y_range = 512;
2117                 /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
2118                 double const scale_range = 4;
2119                 /* dead zone around the grab point in which to do no zooming (pixels) */
2120                 double const dead_zone = 100;
2121
2122                 /* current dy */
2123                 double dy = _drags->current_pointer_y() - grab_y();
2124
2125                 if (dy < -dead_zone || dy > dead_zone) {
2126                         /* we are outside the dead zone; remove it from our calculation */
2127                         if (dy < 0) {
2128                                 dy += dead_zone;
2129                         } else {
2130                                 dy -= dead_zone;
2131                         }
2132
2133                         /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
2134                         double udy = max (min (dy / y_range, 1.0), -1.0);
2135
2136                         /* and zoom, using playhead focus temporarily */
2137                         Editing::ZoomFocus const zf = _editor->get_zoom_focus ();
2138                         _editor->set_zoom_focus (Editing::ZoomFocusPlayhead);
2139                         _editor->temporal_zoom (_grab_zoom * pow (scale_range, -udy));
2140                         _editor->set_zoom_focus (zf);
2141                 }
2142         }
2143
2144         framepos_t const adjusted_frame = adjusted_current_frame (event);
2145         if (adjusted_frame != last_pointer_frame()) {
2146                 fake_locate (adjusted_frame);
2147 #ifdef GTKOSX
2148                 _editor->update_canvas_now ();
2149 #endif
2150         }
2151 }
2152
2153 void
2154 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2155 {
2156         _editor->_dragging_playhead = false;
2157
2158         if (!movement_occurred && _stop) {
2159                 return;
2160         }
2161
2162         motion (event, false);
2163
2164         Session* s = _editor->session ();
2165         if (s) {
2166                 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2167                 _editor->_pending_locate_request = true;
2168                 s->request_resume_timecode_transmission ();
2169         }
2170 }
2171
2172 void
2173 CursorDrag::aborted (bool)
2174 {
2175         if (_editor->_dragging_playhead) {
2176                 _editor->session()->request_resume_timecode_transmission ();
2177                 _editor->_dragging_playhead = false;
2178         }
2179
2180         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2181 }
2182
2183 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2184         : RegionDrag (e, i, p, v)
2185 {
2186         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2187 }
2188
2189 void
2190 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2191 {
2192         Drag::start_grab (event, cursor);
2193
2194         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2195         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2196
2197         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2198
2199         arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2200 }
2201
2202 void
2203 FadeInDrag::setup_pointer_frame_offset ()
2204 {
2205         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2206         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2207         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2208 }
2209
2210 void
2211 FadeInDrag::motion (GdkEvent* event, bool)
2212 {
2213         framecnt_t fade_length;
2214
2215         framepos_t const pos = adjusted_current_frame (event);
2216
2217         boost::shared_ptr<Region> region = _primary->region ();
2218
2219         if (pos < (region->position() + 64)) {
2220                 fade_length = 64; // this should be a minimum defined somewhere
2221         } else if (pos > region->last_frame()) {
2222                 fade_length = region->length();
2223         } else {
2224                 fade_length = pos - region->position();
2225         }
2226
2227         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2228
2229                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2230
2231                 if (!tmp) {
2232                         continue;
2233                 }
2234
2235                 tmp->reset_fade_in_shape_width (fade_length);
2236                 tmp->show_fade_line((framecnt_t) fade_length);
2237         }
2238
2239         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2240 }
2241
2242 void
2243 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2244 {
2245         if (!movement_occurred) {
2246                 return;
2247         }
2248
2249         framecnt_t fade_length;
2250
2251         framepos_t const pos = adjusted_current_frame (event);
2252
2253         boost::shared_ptr<Region> region = _primary->region ();
2254
2255         if (pos < (region->position() + 64)) {
2256                 fade_length = 64; // this should be a minimum defined somewhere
2257         } else if (pos > region->last_frame()) {
2258                 fade_length = region->length();
2259         } else {
2260                 fade_length = pos - region->position();
2261         }
2262
2263         _editor->begin_reversible_command (_("change fade in length"));
2264
2265         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2266
2267                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2268
2269                 if (!tmp) {
2270                         continue;
2271                 }
2272
2273                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2274                 XMLNode &before = alist->get_state();
2275
2276                 tmp->audio_region()->set_fade_in_length (fade_length);
2277                 tmp->audio_region()->set_fade_in_active (true);
2278                 tmp->hide_fade_line();
2279
2280                 XMLNode &after = alist->get_state();
2281                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2282         }
2283
2284         _editor->commit_reversible_command ();
2285 }
2286
2287 void
2288 FadeInDrag::aborted (bool)
2289 {
2290         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2291                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2292
2293                 if (!tmp) {
2294                         continue;
2295                 }
2296
2297                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2298                 tmp->hide_fade_line();
2299         }
2300 }
2301
2302 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2303         : RegionDrag (e, i, p, v)
2304 {
2305         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2306 }
2307
2308 void
2309 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2310 {
2311         Drag::start_grab (event, cursor);
2312
2313         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2314         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2315
2316         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2317
2318         arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2319 }
2320
2321 void
2322 FadeOutDrag::setup_pointer_frame_offset ()
2323 {
2324         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2325         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2326         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2327 }
2328
2329 void
2330 FadeOutDrag::motion (GdkEvent* event, bool)
2331 {
2332         framecnt_t fade_length;
2333
2334         framepos_t const pos = adjusted_current_frame (event);
2335
2336         boost::shared_ptr<Region> region = _primary->region ();
2337
2338         if (pos > (region->last_frame() - 64)) {
2339                 fade_length = 64; // this should really be a minimum fade defined somewhere
2340         }
2341         else if (pos < region->position()) {
2342                 fade_length = region->length();
2343         }
2344         else {
2345                 fade_length = region->last_frame() - pos;
2346         }
2347
2348         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2349
2350                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2351
2352                 if (!tmp) {
2353                         continue;
2354                 }
2355
2356                 tmp->reset_fade_out_shape_width (fade_length);
2357                 tmp->show_fade_line(region->length() - fade_length);
2358         }
2359
2360         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2361 }
2362
2363 void
2364 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2365 {
2366         if (!movement_occurred) {
2367                 return;
2368         }
2369
2370         framecnt_t fade_length;
2371
2372         framepos_t const pos = adjusted_current_frame (event);
2373
2374         boost::shared_ptr<Region> region = _primary->region ();
2375
2376         if (pos > (region->last_frame() - 64)) {
2377                 fade_length = 64; // this should really be a minimum fade defined somewhere
2378         }
2379         else if (pos < region->position()) {
2380                 fade_length = region->length();
2381         }
2382         else {
2383                 fade_length = region->last_frame() - pos;
2384         }
2385
2386         _editor->begin_reversible_command (_("change fade out length"));
2387
2388         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2389
2390                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2391
2392                 if (!tmp) {
2393                         continue;
2394                 }
2395
2396                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2397                 XMLNode &before = alist->get_state();
2398
2399                 tmp->audio_region()->set_fade_out_length (fade_length);
2400                 tmp->audio_region()->set_fade_out_active (true);
2401                 tmp->hide_fade_line();
2402
2403                 XMLNode &after = alist->get_state();
2404                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2405         }
2406
2407         _editor->commit_reversible_command ();
2408 }
2409
2410 void
2411 FadeOutDrag::aborted (bool)
2412 {
2413         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2414                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2415
2416                 if (!tmp) {
2417                         continue;
2418                 }
2419
2420                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2421                 tmp->hide_fade_line();
2422         }
2423 }
2424
2425 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2426         : Drag (e, i)
2427 {
2428         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2429
2430         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2431         assert (_marker);
2432
2433         _points.push_back (Gnome::Art::Point (0, 0));
2434         _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2435 }
2436
2437 MarkerDrag::~MarkerDrag ()
2438 {
2439         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2440                 delete *i;
2441         }
2442 }
2443
2444 void
2445 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2446 {
2447         Drag::start_grab (event, cursor);
2448
2449         bool is_start;
2450
2451         Location *location = _editor->find_location_from_marker (_marker, is_start);
2452         _editor->_dragging_edit_point = true;
2453
2454         update_item (location);
2455
2456         // _drag_line->show();
2457         // _line->raise_to_top();
2458
2459         if (is_start) {
2460                 show_verbose_cursor_time (location->start());
2461         } else {
2462                 show_verbose_cursor_time (location->end());
2463         }
2464
2465         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2466
2467         switch (op) {
2468         case Selection::Toggle:
2469                 _editor->selection->toggle (_marker);
2470                 break;
2471         case Selection::Set:
2472                 if (!_editor->selection->selected (_marker)) {
2473                         _editor->selection->set (_marker);
2474                 }
2475                 break;
2476         case Selection::Extend:
2477         {
2478                 Locations::LocationList ll;
2479                 list<Marker*> to_add;
2480                 framepos_t s, e;
2481                 _editor->selection->markers.range (s, e);
2482                 s = min (_marker->position(), s);
2483                 e = max (_marker->position(), e);
2484                 s = min (s, e);
2485                 e = max (s, e);
2486                 if (e < max_framepos) {
2487                         ++e;
2488                 }
2489                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2490                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2491                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2492                         if (lm) {
2493                                 if (lm->start) {
2494                                         to_add.push_back (lm->start);
2495                                 }
2496                                 if (lm->end) {
2497                                         to_add.push_back (lm->end);
2498                                 }
2499                         }
2500                 }
2501                 if (!to_add.empty()) {
2502                         _editor->selection->add (to_add);
2503                 }
2504                 break;
2505         }
2506         case Selection::Add:
2507                 _editor->selection->add (_marker);
2508                 break;
2509         }
2510
2511         /* Set up copies for us to manipulate during the drag */
2512
2513         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2514                 Location* l = _editor->find_location_from_marker (*i, is_start);
2515                 _copied_locations.push_back (new Location (*l));
2516         }
2517 }
2518
2519 void
2520 MarkerDrag::setup_pointer_frame_offset ()
2521 {
2522         bool is_start;
2523         Location *location = _editor->find_location_from_marker (_marker, is_start);
2524         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2525 }
2526
2527 void
2528 MarkerDrag::motion (GdkEvent* event, bool)
2529 {
2530         framecnt_t f_delta = 0;
2531         bool is_start;
2532         bool move_both = false;
2533         Marker* marker;
2534         Location *real_location;
2535         Location *copy_location = 0;
2536
2537         framepos_t const newframe = adjusted_current_frame (event);
2538
2539         framepos_t next = newframe;
2540
2541         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2542                 move_both = true;
2543         }
2544
2545         MarkerSelection::iterator i;
2546         list<Location*>::iterator x;
2547
2548         /* find the marker we're dragging, and compute the delta */
2549
2550         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2551              x != _copied_locations.end() && i != _editor->selection->markers.end();
2552              ++i, ++x) {
2553
2554                 copy_location = *x;
2555                 marker = *i;
2556
2557                 if (marker == _marker) {
2558
2559                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2560                                 /* que pasa ?? */
2561                                 return;
2562                         }
2563
2564                         if (real_location->is_mark()) {
2565                                 f_delta = newframe - copy_location->start();
2566                         } else {
2567
2568
2569                                 switch (marker->type()) {
2570                                 case Marker::SessionStart:
2571                                 case Marker::RangeStart:
2572                                 case Marker::LoopStart:
2573                                 case Marker::PunchIn:
2574                                         f_delta = newframe - copy_location->start();
2575                                         break;
2576
2577                                 case Marker::SessionEnd:
2578                                 case Marker::RangeEnd:
2579                                 case Marker::LoopEnd:
2580                                 case Marker::PunchOut:
2581                                         f_delta = newframe - copy_location->end();
2582                                         break;
2583                                 default:
2584                                         /* what kind of marker is this ? */
2585                                         return;
2586                                 }
2587                         }
2588                         break;
2589                 }
2590         }
2591
2592         if (i == _editor->selection->markers.end()) {
2593                 /* hmm, impossible - we didn't find the dragged marker */
2594                 return;
2595         }
2596
2597         /* now move them all */
2598
2599         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2600              x != _copied_locations.end() && i != _editor->selection->markers.end();
2601              ++i, ++x) {
2602
2603                 copy_location = *x;
2604                 marker = *i;
2605
2606                 /* call this to find out if its the start or end */
2607
2608                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2609                         continue;
2610                 }
2611
2612                 if (real_location->locked()) {
2613                         continue;
2614                 }
2615
2616                 if (copy_location->is_mark()) {
2617
2618                         /* now move it */
2619
2620                         copy_location->set_start (copy_location->start() + f_delta);
2621
2622                 } else {
2623
2624                         framepos_t new_start = copy_location->start() + f_delta;
2625                         framepos_t new_end = copy_location->end() + f_delta;
2626
2627                         if (is_start) { // start-of-range marker
2628
2629                                 if (move_both) {
2630                                         copy_location->set_start (new_start);
2631                                         copy_location->set_end (new_end);
2632                                 } else  if (new_start < copy_location->end()) {
2633                                         copy_location->set_start (new_start);
2634                                 } else if (newframe > 0) {
2635                                         _editor->snap_to (next, 1, true);
2636                                         copy_location->set_end (next);
2637                                         copy_location->set_start (newframe);
2638                                 }
2639
2640                         } else { // end marker
2641
2642                                 if (move_both) {
2643                                         copy_location->set_end (new_end);
2644                                         copy_location->set_start (new_start);
2645                                 } else if (new_end > copy_location->start()) {
2646                                         copy_location->set_end (new_end);
2647                                 } else if (newframe > 0) {
2648                                         _editor->snap_to (next, -1, true);
2649                                         copy_location->set_start (next);
2650                                         copy_location->set_end (newframe);
2651                                 }
2652                         }
2653                 }
2654
2655                 update_item (copy_location);
2656
2657                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2658
2659                 if (lm) {
2660                         lm->set_position (copy_location->start(), copy_location->end());
2661                 }
2662         }
2663
2664         assert (!_copied_locations.empty());
2665
2666         show_verbose_cursor_time (newframe);
2667
2668 #ifdef GTKOSX
2669         _editor->update_canvas_now ();
2670 #endif
2671 }
2672
2673 void
2674 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2675 {
2676         if (!movement_occurred) {
2677
2678                 /* just a click, do nothing but finish
2679                    off the selection process
2680                 */
2681
2682                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2683
2684                 switch (op) {
2685                 case Selection::Set:
2686                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2687                                 _editor->selection->set (_marker);
2688                         }
2689                         break;
2690
2691                 case Selection::Toggle:
2692                 case Selection::Extend:
2693                 case Selection::Add:
2694                         break;
2695                 }
2696
2697                 return;
2698         }
2699
2700         _editor->_dragging_edit_point = false;
2701
2702         _editor->begin_reversible_command ( _("move marker") );
2703         XMLNode &before = _editor->session()->locations()->get_state();
2704
2705         MarkerSelection::iterator i;
2706         list<Location*>::iterator x;
2707         bool is_start;
2708
2709         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2710              x != _copied_locations.end() && i != _editor->selection->markers.end();
2711              ++i, ++x) {
2712
2713                 Location * location = _editor->find_location_from_marker (*i, is_start);
2714
2715                 if (location) {
2716
2717                         if (location->locked()) {
2718                                 return;
2719                         }
2720
2721                         if (location->is_mark()) {
2722                                 location->set_start ((*x)->start());
2723                         } else {
2724                                 location->set ((*x)->start(), (*x)->end());
2725                         }
2726                 }
2727         }
2728
2729         XMLNode &after = _editor->session()->locations()->get_state();
2730         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2731         _editor->commit_reversible_command ();
2732 }
2733
2734 void
2735 MarkerDrag::aborted (bool)
2736 {
2737         /* XXX: TODO */
2738 }
2739
2740 void
2741 MarkerDrag::update_item (Location*)
2742 {
2743         /* noop */
2744 }
2745
2746 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2747         : Drag (e, i),
2748           _cumulative_x_drag (0),
2749           _cumulative_y_drag (0)
2750 {
2751         if (_zero_gain_fraction < 0.0) {
2752                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2753         }
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->source_beats_to_absolute_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 note number */
3896 int8_t
3897 NoteDrag::total_dy () const
3898 {
3899         return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3900 }
3901
3902 void
3903 NoteDrag::motion (GdkEvent *, bool)
3904 {
3905         /* Total change in x and y since the start of the drag */
3906         frameoffset_t const dx = total_dx ();
3907         int8_t const dy = total_dy ();
3908
3909         /* Now work out what we have to do to the note canvas items to set this new drag delta */
3910         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3911         double const tdy = -dy * _note_height - _cumulative_dy;
3912
3913         if (tdx || tdy) {
3914                 _cumulative_dx += tdx;
3915                 _cumulative_dy += tdy;
3916
3917                 int8_t note_delta = total_dy();
3918
3919                 _region->move_selection (tdx, tdy, note_delta);
3920
3921                 /* the new note value may be the same as the old one, but we
3922                  * don't know what that means because the selection may have
3923                  * involved more than one note and we might be doing something
3924                  * odd with them. so show the note value anyway, always.
3925                  */
3926
3927                 char buf[12];
3928                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3929                 
3930                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3931                           (int) floor (new_note));
3932
3933                 show_verbose_cursor_text (buf);
3934         }
3935 }
3936
3937 void
3938 NoteDrag::finished (GdkEvent* ev, bool moved)
3939 {
3940         if (!moved) {
3941                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3942
3943                         if (_was_selected) {
3944                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3945                                 if (add) {
3946                                         _region->note_deselected (_primary);
3947                                 }
3948                         } else {
3949                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3950                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3951
3952                                 if (!extend && !add && _region->selection_size() > 1) {
3953                                         _region->unique_select (_primary);
3954                                 } else if (extend) {
3955                                         _region->note_selected (_primary, true, true);
3956                                 } else {
3957                                         /* it was added during button press */
3958                                 }
3959                         }
3960                 }
3961         } else {
3962                 _region->note_dropped (_primary, total_dx(), total_dy());
3963         }
3964 }
3965
3966 void
3967 NoteDrag::aborted (bool)
3968 {
3969         /* XXX: TODO */
3970 }
3971
3972 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3973         : Drag (editor, item)
3974         , _ranges (r)
3975         , _nothing_to_drag (false)
3976 {
3977         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3978
3979         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3980         assert (_atav);
3981
3982         /* get all lines in the automation view */
3983         list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3984
3985         /* find those that overlap the ranges being dragged */
3986         list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3987         while (i != lines.end ()) {
3988                 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3989                 ++j;
3990
3991                 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3992
3993                 /* check this range against all the AudioRanges that we are using */
3994                 list<AudioRange>::const_iterator k = _ranges.begin ();
3995                 while (k != _ranges.end()) {
3996                         if (k->coverage (r.first, r.second) != OverlapNone) {
3997                                 break;
3998                         }
3999                         ++k;
4000                 }
4001
4002                 /* add it to our list if it overlaps at all */
4003                 if (k != _ranges.end()) {
4004                         Line n;
4005                         n.line = *i;
4006                         n.state = 0;
4007                         n.range = r;
4008                         _lines.push_back (n);
4009                 }
4010
4011                 i = j;
4012         }
4013
4014         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4015 }
4016
4017 void
4018 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4019 {
4020         Drag::start_grab (event, cursor);
4021
4022         /* Get line states before we start changing things */
4023         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4024                 i->state = &i->line->get_state ();
4025         }
4026
4027         if (_ranges.empty()) {
4028
4029                 /* No selected time ranges: drag all points */
4030                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4031                         uint32_t const N = i->line->npoints ();
4032                         for (uint32_t j = 0; j < N; ++j) {
4033                                 i->points.push_back (i->line->nth (j));
4034                         }
4035                 }
4036
4037         } else {
4038
4039                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4040
4041                         framecnt_t const half = (i->start + i->end) / 2;
4042
4043                         /* find the line that this audio range starts in */
4044                         list<Line>::iterator j = _lines.begin();
4045                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4046                                 ++j;
4047                         }
4048
4049                         if (j != _lines.end()) {
4050                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4051
4052                                 /* j is the line that this audio range starts in; fade into it;
4053                                    64 samples length plucked out of thin air.
4054                                 */
4055
4056                                 framepos_t a = i->start + 64;
4057                                 if (a > half) {
4058                                         a = half;
4059                                 }
4060
4061                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4062                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4063
4064                                 the_list->add (p, the_list->eval (p));
4065                                 j->line->add_always_in_view (p);
4066                                 the_list->add (q, the_list->eval (q));
4067                                 j->line->add_always_in_view (q);
4068                         }
4069
4070                         /* same thing for the end */
4071
4072                         j = _lines.begin();
4073                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4074                                 ++j;
4075                         }
4076
4077                         if (j != _lines.end()) {
4078                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4079
4080                                 /* j is the line that this audio range starts in; fade out of it;
4081                                    64 samples length plucked out of thin air.
4082                                 */
4083
4084                                 framepos_t b = i->end - 64;
4085                                 if (b < half) {
4086                                         b = half;
4087                                 }
4088
4089                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4090                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4091
4092                                 the_list->add (p, the_list->eval (p));
4093                                 j->line->add_always_in_view (p);
4094                                 the_list->add (q, the_list->eval (q));
4095                                 j->line->add_always_in_view (q);
4096                         }
4097                 }
4098
4099                 _nothing_to_drag = true;
4100
4101                 /* Find all the points that should be dragged and put them in the relevant
4102                    points lists in the Line structs.
4103                 */
4104
4105                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4106
4107                         uint32_t const N = i->line->npoints ();
4108                         for (uint32_t j = 0; j < N; ++j) {
4109
4110                                 /* here's a control point on this line */
4111                                 ControlPoint* p = i->line->nth (j);
4112                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4113
4114                                 /* see if it's inside a range */
4115                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4116                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4117                                         ++k;
4118                                 }
4119
4120                                 if (k != _ranges.end()) {
4121                                         /* dragging this point */
4122                                         _nothing_to_drag = false;
4123                                         i->points.push_back (p);
4124                                 }
4125                         }
4126                 }
4127         }
4128
4129         if (_nothing_to_drag) {
4130                 return;
4131         }
4132
4133         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4134                 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4135         }
4136 }
4137
4138 void
4139 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4140 {
4141         if (_nothing_to_drag) {
4142                 return;
4143         }
4144
4145         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4146                 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4147
4148                 /* we are ignoring x position for this drag, so we can just pass in anything */
4149                 i->line->drag_motion (0, f, true, false);
4150         }
4151 }
4152
4153 void
4154 AutomationRangeDrag::finished (GdkEvent* event, bool)
4155 {
4156         if (_nothing_to_drag) {
4157                 return;
4158         }
4159
4160         motion (event, false);
4161         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4162                 i->line->end_drag ();
4163                 i->line->clear_always_in_view ();
4164         }
4165
4166         _editor->session()->commit_reversible_command ();
4167 }
4168
4169 void
4170 AutomationRangeDrag::aborted (bool)
4171 {
4172         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4173                 i->line->clear_always_in_view ();
4174                 i->line->reset ();
4175         }
4176 }
4177
4178 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4179         : view (v)
4180 {
4181         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4182         layer = v->region()->layer ();
4183         initial_y = v->get_canvas_group()->property_y ();
4184         initial_playlist = v->region()->playlist ();
4185         initial_position = v->region()->position ();
4186         initial_end = v->region()->position () + v->region()->length ();
4187 }
4188
4189 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4190         : Drag (e, i)
4191         , _region_view (r)
4192         , _patch_change (i)
4193         , _cumulative_dx (0)
4194 {
4195         DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4196 }
4197
4198 void
4199 PatchChangeDrag::motion (GdkEvent* ev, bool)
4200 {
4201         framepos_t f = adjusted_current_frame (ev);
4202         boost::shared_ptr<Region> r = _region_view->region ();
4203         f = max (f, r->position ());
4204         f = min (f, r->last_frame ());
4205
4206         framecnt_t const dxf = f - grab_frame();
4207         double const dxu = _editor->frame_to_unit (dxf);
4208         _patch_change->move (dxu - _cumulative_dx, 0);
4209         _cumulative_dx = dxu;
4210 }
4211
4212 void
4213 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4214 {
4215         if (!movement_occurred) {
4216                 return;
4217         }
4218
4219         boost::shared_ptr<Region> r (_region_view->region ());
4220
4221         framepos_t f = adjusted_current_frame (ev);
4222         f = max (f, r->position ());
4223         f = min (f, r->last_frame ());
4224
4225         _region_view->move_patch_change (
4226                 *_patch_change,
4227                 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4228                 );
4229 }
4230
4231 void
4232 PatchChangeDrag::aborted (bool)
4233 {
4234         _patch_change->move (-_cumulative_dx, 0);
4235 }
4236
4237 void
4238 PatchChangeDrag::setup_pointer_frame_offset ()
4239 {
4240         boost::shared_ptr<Region> region = _region_view->region ();
4241         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4242 }
4243