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