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