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