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