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