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