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