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