Merge branch 'mingw-updates' of https://github.com/mojofunk/ardour into cairocanvas
[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 const 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                 }
1945
1946                 _editor->begin_reversible_command (trim_type);
1947
1948                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1949                         RegionView* rv = i->view;
1950                         rv->fake_set_opaque (false);
1951                         rv->enable_display (false);
1952                         rv->region()->playlist()->clear_owned_changes ();
1953
1954                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1955
1956                         if (arv) {
1957                                 arv->temporarily_hide_envelope ();
1958                                 arv->drag_start ();
1959                         }
1960
1961                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1962                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1963
1964                         if (insert_result.second) {
1965                                 pl->freeze();
1966                         }
1967                 }
1968         }
1969
1970         bool non_overlap_trim = false;
1971
1972         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1973                 non_overlap_trim = true;
1974         }
1975
1976         switch (_operation) {
1977         case StartTrim:
1978                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1979                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1980                         if (changed && _preserve_fade_anchor) {
1981                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1982                                 if (arv) {
1983                                         double distance;
1984                                         double new_length;
1985                                         framecnt_t len;
1986                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1987                                         distance = _drags->current_pointer_x() - grab_x();
1988                                         len = ar->fade_in()->back()->when;
1989                                         new_length = len - _editor->pixel_to_sample (distance);
1990                                         new_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
1991                                         arv->reset_fade_in_shape_width (ar, new_length);  //the grey shape
1992                                 }
1993                         }
1994                 }
1995                 break;
1996
1997         case EndTrim:
1998                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1999                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2000                         if (changed && _preserve_fade_anchor) {
2001                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2002                                 if (arv) {
2003                                         double distance;
2004                                         double new_length;
2005                                         framecnt_t len;
2006                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2007                                         distance = grab_x() - _drags->current_pointer_x();
2008                                         len = ar->fade_out()->back()->when;
2009                                         new_length = len - _editor->pixel_to_sample (distance);
2010                                         new_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2011                                         arv->reset_fade_out_shape_width (ar, new_length);  //the grey shape
2012                                 }
2013                         }
2014                 }
2015                 break;
2016
2017         case ContentsTrim:
2018                 {
2019                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2020
2021                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2022                                 i->view->move_contents (frame_delta);
2023                         }
2024                 }
2025                 break;
2026         }
2027
2028         switch (_operation) {
2029         case StartTrim:
2030                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2031                 break;
2032         case EndTrim:
2033                 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2034                 break;
2035         case ContentsTrim:
2036                 // show_verbose_cursor_time (frame_delta);
2037                 break;
2038         }
2039 }
2040
2041
2042 void
2043 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2044 {
2045         if (movement_occurred) {
2046                 motion (event, false);
2047
2048                 if (_operation == StartTrim) {
2049                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2050                                 {
2051                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2052                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2053                                            needs to be zero in the undo record.
2054                                         */
2055                                         i->view->trim_front_ending ();
2056                                 }
2057                                 if (_preserve_fade_anchor) {
2058                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2059                                         if (arv) {
2060                                                 double distance;
2061                                                 double new_length;
2062                                                 framecnt_t len;
2063                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2064                                                 distance = _drags->current_pointer_x() - grab_x();
2065                                                 len = ar->fade_in()->back()->when;
2066                                                 new_length = len - _editor->pixel_to_sample (distance);
2067                                                 new_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2068                                                 ar->set_fade_in_length(new_length);
2069                                         }
2070                                 }
2071                                 if (_jump_position_when_done) {
2072                                         i->view->region()->set_position (i->initial_position);
2073                                 }
2074                         }
2075                 } else if (_operation == EndTrim) {
2076                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2077                                 if (_preserve_fade_anchor) {
2078                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2079                                         if (arv) {
2080                                                 double distance;
2081                                                 double new_length;
2082                                                 framecnt_t len;
2083                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2084                                                 distance = _drags->current_pointer_x() - grab_x();
2085                                                 len = ar->fade_out()->back()->when;
2086                                                 new_length = len - _editor->pixel_to_sample (distance);
2087                                                 new_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2088                                                 ar->set_fade_out_length(new_length);
2089                                         }
2090                                 }
2091                                 if (_jump_position_when_done) {
2092                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
2093                                 }
2094                         }
2095                 }
2096
2097                 if (!_views.empty()) {
2098                         if (_operation == StartTrim) {
2099                                 _editor->maybe_locate_with_edit_preroll(
2100                                         _views.begin()->view->region()->position());
2101                         }
2102                         if (_operation == EndTrim) {
2103                                 _editor->maybe_locate_with_edit_preroll(
2104                                         _views.begin()->view->region()->position() +
2105                                         _views.begin()->view->region()->length());
2106                         }
2107                 }
2108         
2109                 if (!_editor->selection->selected (_primary)) {
2110                         _primary->thaw_after_trim ();
2111                 } else {
2112
2113                         set<boost::shared_ptr<Playlist> > diffed_playlists;
2114
2115                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2116                                 i->view->thaw_after_trim ();
2117                                 i->view->enable_display (true);
2118                                 i->view->fake_set_opaque (true);
2119
2120                                 /* Trimming one region may affect others on the playlist, so we need
2121                                    to get undo Commands from the whole playlist rather than just the
2122                                    region.  Use diffed_playlists to make sure we don't diff a given
2123                                    playlist more than once.
2124                                 */
2125                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2126                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2127                                         vector<Command*> cmds;
2128                                         p->rdiff (cmds);
2129                                         _editor->session()->add_commands (cmds);
2130                                         diffed_playlists.insert (p);
2131                                 }
2132                         }
2133                 }
2134
2135                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2136                         (*p)->thaw ();
2137                 }
2138
2139                 _editor->motion_frozen_playlists.clear ();
2140                 _editor->commit_reversible_command();
2141
2142         } else {
2143                 /* no mouse movement */
2144                 _editor->point_trim (event, adjusted_current_frame (event));
2145         }
2146
2147         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2148                 if (_operation == StartTrim) {
2149                         i->view->trim_front_ending ();
2150                 }
2151
2152                 i->view->region()->resume_property_changes ();
2153         }
2154 }
2155
2156 void
2157 TrimDrag::aborted (bool movement_occurred)
2158 {
2159         /* Our motion method is changing model state, so use the Undo system
2160            to cancel.  Perhaps not ideal, as this will leave an Undo point
2161            behind which may be slightly odd from the user's point of view.
2162         */
2163
2164         finished (0, true);
2165
2166         if (movement_occurred) {
2167                 _editor->undo ();
2168         }
2169
2170         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2171                 i->view->region()->resume_property_changes ();
2172         }
2173 }
2174
2175 void
2176 TrimDrag::setup_pointer_frame_offset ()
2177 {
2178         list<DraggingView>::iterator i = _views.begin ();
2179         while (i != _views.end() && i->view != _primary) {
2180                 ++i;
2181         }
2182
2183         if (i == _views.end()) {
2184                 return;
2185         }
2186
2187         switch (_operation) {
2188         case StartTrim:
2189                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2190                 break;
2191         case EndTrim:
2192                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2193                 break;
2194         case ContentsTrim:
2195                 break;
2196         }
2197 }
2198
2199 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2200         : Drag (e, i),
2201           _copy (c)
2202 {
2203         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2204         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2205         assert (_marker);
2206 }
2207
2208 void
2209 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2210 {
2211         Drag::start_grab (event, cursor);
2212         show_verbose_cursor_time (adjusted_current_frame(event));
2213 }
2214
2215 void
2216 MeterMarkerDrag::setup_pointer_frame_offset ()
2217 {
2218         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2219 }
2220
2221 void
2222 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2223 {
2224         if (!_marker->meter().movable()) {
2225                 return;
2226         }
2227
2228         if (first_move) {
2229
2230                 // create a dummy marker for visual representation of moving the
2231                 // section, because whether its a copy or not, we're going to 
2232                 // leave or lose the original marker (leave if its a copy; lose if its
2233                 // not, because we'll remove it from the map).
2234                 
2235                 MeterSection section (_marker->meter());
2236
2237                 if (!section.movable()) {
2238                         return;
2239                 }
2240                 
2241                 char name[64];
2242                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2243                 
2244                 _marker = new MeterMarker (
2245                         *_editor,
2246                         *_editor->meter_group,
2247                         ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2248                         name,
2249                         *new MeterSection (_marker->meter())
2250                 );
2251                 
2252                 /* use the new marker for the grab */
2253                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2254
2255                 if (!_copy) {
2256                         TempoMap& map (_editor->session()->tempo_map());
2257                         /* get current state */
2258                         before_state = &map.get_state();
2259                         /* remove the section while we drag it */
2260                         map.remove_meter (section, true);
2261                 }
2262         }
2263
2264         framepos_t const pf = adjusted_current_frame (event);
2265         _marker->set_position (pf);
2266         show_verbose_cursor_time (pf);
2267 }
2268
2269 void
2270 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2271 {
2272         if (!movement_occurred) {
2273                 if (was_double_click()) {
2274                         _editor->edit_meter_marker (*_marker);
2275                 }
2276                 return;
2277         }
2278
2279         if (!_marker->meter().movable()) {
2280                 return;
2281         }
2282
2283         motion (event, false);
2284
2285         Timecode::BBT_Time when;
2286
2287         TempoMap& map (_editor->session()->tempo_map());
2288         map.bbt_time (last_pointer_frame(), when);
2289         
2290         if (_copy == true) {
2291                 _editor->begin_reversible_command (_("copy meter mark"));
2292                 XMLNode &before = map.get_state();
2293                 map.add_meter (_marker->meter(), when);
2294                 XMLNode &after = map.get_state();
2295                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2296                 _editor->commit_reversible_command ();
2297
2298         } else {
2299                 _editor->begin_reversible_command (_("move meter mark"));
2300
2301                 /* we removed it before, so add it back now */
2302                 
2303                 map.add_meter (_marker->meter(), when);
2304                 XMLNode &after = map.get_state();
2305                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2306                 _editor->commit_reversible_command ();
2307         }
2308
2309         // delete the dummy marker we used for visual representation while moving.
2310         // a new visual marker will show up automatically.
2311         delete _marker;
2312 }
2313
2314 void
2315 MeterMarkerDrag::aborted (bool moved)
2316 {
2317         _marker->set_position (_marker->meter().frame ());
2318
2319         if (moved) {
2320                 TempoMap& map (_editor->session()->tempo_map());
2321                 /* we removed it before, so add it back now */
2322                 map.add_meter (_marker->meter(), _marker->meter().frame());
2323                 // delete the dummy marker we used for visual representation while moving.
2324                 // a new visual marker will show up automatically.
2325                 delete _marker;
2326         }
2327 }
2328
2329 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2330         : Drag (e, i),
2331           _copy (c)
2332 {
2333         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2334
2335         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2336         assert (_marker);
2337 }
2338
2339 void
2340 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2341 {
2342         Drag::start_grab (event, cursor);
2343         show_verbose_cursor_time (adjusted_current_frame (event));
2344 }
2345
2346 void
2347 TempoMarkerDrag::setup_pointer_frame_offset ()
2348 {
2349         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2350 }
2351
2352 void
2353 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2354 {
2355         if (!_marker->tempo().movable()) {
2356                 return;
2357         }
2358
2359         if (first_move) {
2360
2361                 // create a dummy marker for visual representation of moving the
2362                 // section, because whether its a copy or not, we're going to 
2363                 // leave or lose the original marker (leave if its a copy; lose if its
2364                 // not, because we'll remove it from the map).
2365                 
2366                 // create a dummy marker for visual representation of moving the copy.
2367                 // The actual copying is not done before we reach the finish callback.
2368
2369                 char name[64];
2370                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2371
2372                 TempoSection section (_marker->tempo());
2373
2374                 _marker = new TempoMarker (
2375                         *_editor,
2376                         *_editor->tempo_group,
2377                         ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2378                         name,
2379                         *new TempoSection (_marker->tempo())
2380                         );
2381
2382                 /* use the new marker for the grab */
2383                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2384
2385                 if (!_copy) {
2386                         TempoMap& map (_editor->session()->tempo_map());
2387                         /* get current state */
2388                         before_state = &map.get_state();
2389                         /* remove the section while we drag it */
2390                         map.remove_tempo (section, true);
2391                 }
2392         }
2393
2394         framepos_t const pf = adjusted_current_frame (event);
2395         _marker->set_position (pf);
2396         show_verbose_cursor_time (pf);
2397 }
2398
2399 void
2400 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2401 {
2402         if (!movement_occurred) {
2403                 if (was_double_click()) {
2404                         _editor->edit_tempo_marker (*_marker);
2405                 }
2406                 return;
2407         }
2408
2409         if (!_marker->tempo().movable()) {
2410                 return;
2411         }
2412
2413         motion (event, false);
2414
2415         TempoMap& map (_editor->session()->tempo_map());
2416         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2417         Timecode::BBT_Time when;
2418
2419         map.bbt_time (beat_time, when);
2420
2421         if (_copy == true) {
2422                 _editor->begin_reversible_command (_("copy tempo mark"));
2423                 XMLNode &before = map.get_state();
2424                 map.add_tempo (_marker->tempo(), when);
2425                 XMLNode &after = map.get_state();
2426                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2427                 _editor->commit_reversible_command ();
2428
2429         } else {
2430                 _editor->begin_reversible_command (_("move tempo mark"));
2431                 /* we removed it before, so add it back now */
2432                 map.add_tempo (_marker->tempo(), when);
2433                 XMLNode &after = map.get_state();
2434                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2435                 _editor->commit_reversible_command ();
2436         }
2437
2438         // delete the dummy marker we used for visual representation while moving.
2439         // a new visual marker will show up automatically.
2440         delete _marker;
2441 }
2442
2443 void
2444 TempoMarkerDrag::aborted (bool moved)
2445 {
2446         _marker->set_position (_marker->tempo().frame());
2447         if (moved) {
2448                 TempoMap& map (_editor->session()->tempo_map());
2449                 /* we removed it before, so add it back now */
2450                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2451                 // delete the dummy marker we used for visual representation while moving.
2452                 // a new visual marker will show up automatically.
2453                 delete _marker;
2454         }
2455 }
2456
2457 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2458         : Drag (e, &c.time_bar_canvas_item())
2459         , _cursor (c)
2460         , _stop (s)
2461 {
2462         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2463 }
2464
2465 /** Do all the things we do when dragging the playhead to make it look as though
2466  *  we have located, without actually doing the locate (because that would cause
2467  *  the diskstream buffers to be refilled, which is too slow).
2468  */
2469 void
2470 CursorDrag::fake_locate (framepos_t t)
2471 {
2472         _editor->playhead_cursor->set_position (t);
2473
2474         Session* s = _editor->session ();
2475         if (s->timecode_transmission_suspended ()) {
2476                 framepos_t const f = _editor->playhead_cursor->current_frame ();
2477                 /* This is asynchronous so it will be sent "now"
2478                  */
2479                 s->send_mmc_locate (f);
2480                 /* These are synchronous and will be sent during the next
2481                    process cycle
2482                 */
2483                 s->queue_full_time_code ();
2484                 s->queue_song_position_pointer ();
2485         }
2486
2487         show_verbose_cursor_time (t);
2488         _editor->UpdateAllTransportClocks (t);
2489 }
2490
2491 void
2492 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2493 {
2494         Drag::start_grab (event, c);
2495
2496         _grab_zoom = _editor->samples_per_pixel;
2497
2498         framepos_t where = _editor->canvas_event_sample (event);
2499
2500         _editor->snap_to_with_modifier (where, event);
2501
2502         _editor->_dragging_playhead = true;
2503
2504         Session* s = _editor->session ();
2505
2506         /* grab the track canvas item as well */
2507
2508         _cursor.track_canvas_item().grab();
2509
2510         if (s) {
2511                 if (_was_rolling && _stop) {
2512                         s->request_stop ();
2513                 }
2514
2515                 if (s->is_auditioning()) {
2516                         s->cancel_audition ();
2517                 }
2518
2519
2520                 if (AudioEngine::instance()->connected()) {
2521                         
2522                         /* do this only if we're the engine is connected
2523                          * because otherwise this request will never be
2524                          * serviced and we'll busy wait forever. likewise,
2525                          * notice if we are disconnected while waiting for the
2526                          * request to be serviced.
2527                          */
2528
2529                         s->request_suspend_timecode_transmission ();
2530                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2531                                 /* twiddle our thumbs */
2532                         }
2533                 }
2534         }
2535
2536         fake_locate (where);
2537 }
2538
2539 void
2540 CursorDrag::motion (GdkEvent* event, bool)
2541 {
2542         framepos_t const adjusted_frame = adjusted_current_frame (event);
2543         if (adjusted_frame != last_pointer_frame()) {
2544                 fake_locate (adjusted_frame);
2545         }
2546 }
2547
2548 void
2549 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2550 {
2551         _editor->_dragging_playhead = false;
2552
2553         _cursor.track_canvas_item().ungrab();
2554
2555         if (!movement_occurred && _stop) {
2556                 return;
2557         }
2558
2559         motion (event, false);
2560
2561         Session* s = _editor->session ();
2562         if (s) {
2563                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2564                 _editor->_pending_locate_request = true;
2565                 s->request_resume_timecode_transmission ();
2566         }
2567 }
2568
2569 void
2570 CursorDrag::aborted (bool)
2571 {
2572         _cursor.track_canvas_item().ungrab();
2573
2574         if (_editor->_dragging_playhead) {
2575                 _editor->session()->request_resume_timecode_transmission ();
2576                 _editor->_dragging_playhead = false;
2577         }
2578
2579         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2580 }
2581
2582 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2583         : RegionDrag (e, i, p, v)
2584 {
2585         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2586 }
2587
2588 void
2589 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2590 {
2591         Drag::start_grab (event, cursor);
2592
2593         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2594         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2595
2596         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2597 }
2598
2599 void
2600 FadeInDrag::setup_pointer_frame_offset ()
2601 {
2602         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2603         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2604         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2605 }
2606
2607 void
2608 FadeInDrag::motion (GdkEvent* event, bool)
2609 {
2610         framecnt_t fade_length;
2611
2612         framepos_t const pos = adjusted_current_frame (event);
2613
2614         boost::shared_ptr<Region> region = _primary->region ();
2615
2616         if (pos < (region->position() + 64)) {
2617                 fade_length = 64; // this should be a minimum defined somewhere
2618         } else if (pos > region->last_frame()) {
2619                 fade_length = region->length();
2620         } else {
2621                 fade_length = pos - region->position();
2622         }
2623
2624         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2625
2626                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2627
2628                 if (!tmp) {
2629                         continue;
2630                 }
2631
2632                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2633         }
2634
2635         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2636 }
2637
2638 void
2639 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2640 {
2641         if (!movement_occurred) {
2642                 return;
2643         }
2644
2645         framecnt_t fade_length;
2646
2647         framepos_t const pos = adjusted_current_frame (event);
2648
2649         boost::shared_ptr<Region> region = _primary->region ();
2650
2651         if (pos < (region->position() + 64)) {
2652                 fade_length = 64; // this should be a minimum defined somewhere
2653         } else if (pos > region->last_frame()) {
2654                 fade_length = region->length();
2655         } else {
2656                 fade_length = pos - region->position();
2657         }
2658
2659         _editor->begin_reversible_command (_("change fade in length"));
2660
2661         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2662
2663                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2664
2665                 if (!tmp) {
2666                         continue;
2667                 }
2668
2669                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2670                 XMLNode &before = alist->get_state();
2671
2672                 tmp->audio_region()->set_fade_in_length (fade_length);
2673                 tmp->audio_region()->set_fade_in_active (true);
2674
2675                 XMLNode &after = alist->get_state();
2676                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2677         }
2678
2679         _editor->commit_reversible_command ();
2680 }
2681
2682 void
2683 FadeInDrag::aborted (bool)
2684 {
2685         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2686                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2687
2688                 if (!tmp) {
2689                         continue;
2690                 }
2691
2692                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2693         }
2694 }
2695
2696 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2697         : RegionDrag (e, i, p, v)
2698 {
2699         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2700 }
2701
2702 void
2703 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2704 {
2705         Drag::start_grab (event, cursor);
2706
2707         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2708         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2709
2710         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2711 }
2712
2713 void
2714 FadeOutDrag::setup_pointer_frame_offset ()
2715 {
2716         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2717         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2718         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2719 }
2720
2721 void
2722 FadeOutDrag::motion (GdkEvent* event, bool)
2723 {
2724         framecnt_t fade_length;
2725
2726         framepos_t const pos = adjusted_current_frame (event);
2727
2728         boost::shared_ptr<Region> region = _primary->region ();
2729
2730         if (pos > (region->last_frame() - 64)) {
2731                 fade_length = 64; // this should really be a minimum fade defined somewhere
2732         }
2733         else if (pos < region->position()) {
2734                 fade_length = region->length();
2735         }
2736         else {
2737                 fade_length = region->last_frame() - pos;
2738         }
2739
2740         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2741
2742                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2743
2744                 if (!tmp) {
2745                         continue;
2746                 }
2747
2748                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2749         }
2750
2751         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2752 }
2753
2754 void
2755 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2756 {
2757         if (!movement_occurred) {
2758                 return;
2759         }
2760
2761         framecnt_t fade_length;
2762
2763         framepos_t const pos = adjusted_current_frame (event);
2764
2765         boost::shared_ptr<Region> region = _primary->region ();
2766
2767         if (pos > (region->last_frame() - 64)) {
2768                 fade_length = 64; // this should really be a minimum fade defined somewhere
2769         }
2770         else if (pos < region->position()) {
2771                 fade_length = region->length();
2772         }
2773         else {
2774                 fade_length = region->last_frame() - pos;
2775         }
2776
2777         _editor->begin_reversible_command (_("change fade out length"));
2778
2779         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2780
2781                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2782
2783                 if (!tmp) {
2784                         continue;
2785                 }
2786
2787                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2788                 XMLNode &before = alist->get_state();
2789
2790                 tmp->audio_region()->set_fade_out_length (fade_length);
2791                 tmp->audio_region()->set_fade_out_active (true);
2792
2793                 XMLNode &after = alist->get_state();
2794                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2795         }
2796
2797         _editor->commit_reversible_command ();
2798 }
2799
2800 void
2801 FadeOutDrag::aborted (bool)
2802 {
2803         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2804                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2805
2806                 if (!tmp) {
2807                         continue;
2808                 }
2809
2810                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2811         }
2812 }
2813
2814 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2815         : Drag (e, i)
2816 {
2817         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2818
2819         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2820         assert (_marker);
2821
2822         _points.push_back (ArdourCanvas::Duple (0, 0));
2823         _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2824 }
2825
2826 MarkerDrag::~MarkerDrag ()
2827 {
2828         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2829                 delete i->location;
2830         }
2831 }
2832
2833 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2834 {
2835         location = new Location (*l);
2836         markers.push_back (m);
2837         move_both = false;
2838 }
2839
2840 void
2841 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2842 {
2843         Drag::start_grab (event, cursor);
2844
2845         bool is_start;
2846
2847         Location *location = _editor->find_location_from_marker (_marker, is_start);
2848         _editor->_dragging_edit_point = true;
2849
2850         update_item (location);
2851
2852         // _drag_line->show();
2853         // _line->raise_to_top();
2854
2855         if (is_start) {
2856                 show_verbose_cursor_time (location->start());
2857         } else {
2858                 show_verbose_cursor_time (location->end());
2859         }
2860
2861         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2862
2863         switch (op) {
2864         case Selection::Toggle:
2865                 /* we toggle on the button release */
2866                 break;
2867         case Selection::Set:
2868                 if (!_editor->selection->selected (_marker)) {
2869                         _editor->selection->set (_marker);
2870                 }
2871                 break;
2872         case Selection::Extend:
2873         {
2874                 Locations::LocationList ll;
2875                 list<Marker*> to_add;
2876                 framepos_t s, e;
2877                 _editor->selection->markers.range (s, e);
2878                 s = min (_marker->position(), s);
2879                 e = max (_marker->position(), e);
2880                 s = min (s, e);
2881                 e = max (s, e);
2882                 if (e < max_framepos) {
2883                         ++e;
2884                 }
2885                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2886                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2887                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2888                         if (lm) {
2889                                 if (lm->start) {
2890                                         to_add.push_back (lm->start);
2891                                 }
2892                                 if (lm->end) {
2893                                         to_add.push_back (lm->end);
2894                                 }
2895                         }
2896                 }
2897                 if (!to_add.empty()) {
2898                         _editor->selection->add (to_add);
2899                 }
2900                 break;
2901         }
2902         case Selection::Add:
2903                 _editor->selection->add (_marker);
2904                 break;
2905         }
2906
2907         /* Set up copies for us to manipulate during the drag 
2908          */
2909
2910         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2911
2912                 Location* l = _editor->find_location_from_marker (*i, is_start);
2913
2914                 if (!l) {
2915                         continue;
2916                 }
2917
2918                 if (l->is_mark()) {
2919                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2920                 } else {
2921                         /* range: check that the other end of the range isn't
2922                            already there.
2923                         */
2924                         CopiedLocationInfo::iterator x;
2925                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2926                                 if (*(*x).location == *l) {
2927                                         break;
2928                                 }
2929                         }
2930                         if (x == _copied_locations.end()) {
2931                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2932                         } else {
2933                                 (*x).markers.push_back (*i);
2934                                 (*x).move_both = true;
2935                         }
2936                 }
2937                         
2938         }
2939 }
2940
2941 void
2942 MarkerDrag::setup_pointer_frame_offset ()
2943 {
2944         bool is_start;
2945         Location *location = _editor->find_location_from_marker (_marker, is_start);
2946         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2947 }
2948
2949 void
2950 MarkerDrag::motion (GdkEvent* event, bool)
2951 {
2952         framecnt_t f_delta = 0;
2953         bool is_start;
2954         bool move_both = false;
2955         Location *real_location;
2956         Location *copy_location = 0;
2957
2958         framepos_t const newframe = adjusted_current_frame (event);
2959         framepos_t next = newframe;
2960
2961         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2962                 move_both = true;
2963         }
2964
2965         CopiedLocationInfo::iterator x;
2966
2967         /* find the marker we're dragging, and compute the delta */
2968
2969         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2970
2971                 copy_location = (*x).location;
2972
2973                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2974
2975                         /* this marker is represented by this
2976                          * CopiedLocationMarkerInfo 
2977                          */
2978
2979                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2980                                 /* que pasa ?? */
2981                                 return;
2982                         }
2983
2984                         if (real_location->is_mark()) {
2985                                 f_delta = newframe - copy_location->start();
2986                         } else {
2987
2988
2989                                 switch (_marker->type()) {
2990                                 case Marker::SessionStart:
2991                                 case Marker::RangeStart:
2992                                 case Marker::LoopStart:
2993                                 case Marker::PunchIn:
2994                                         f_delta = newframe - copy_location->start();
2995                                         break;
2996
2997                                 case Marker::SessionEnd:
2998                                 case Marker::RangeEnd:
2999                                 case Marker::LoopEnd:
3000                                 case Marker::PunchOut:
3001                                         f_delta = newframe - copy_location->end();
3002                                         break;
3003                                 default:
3004                                         /* what kind of marker is this ? */
3005                                         return;
3006                                 }
3007                         }
3008
3009                         break;
3010                 }
3011         }
3012
3013         if (x == _copied_locations.end()) {
3014                 /* hmm, impossible - we didn't find the dragged marker */
3015                 return;
3016         }
3017
3018         /* now move them all */
3019
3020         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3021
3022                 copy_location = x->location;
3023
3024                 /* call this to find out if its the start or end */
3025
3026                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3027                         continue;
3028                 }
3029
3030                 if (real_location->locked()) {
3031                         continue;
3032                 }
3033
3034                 if (copy_location->is_mark()) {
3035
3036                         /* now move it */
3037
3038                         copy_location->set_start (copy_location->start() + f_delta);
3039
3040                 } else {
3041                         
3042                         framepos_t new_start = copy_location->start() + f_delta;
3043                         framepos_t new_end = copy_location->end() + f_delta;
3044                         
3045                         if (is_start) { // start-of-range marker
3046                                 
3047                                 if (move_both || (*x).move_both) {
3048                                         copy_location->set_start (new_start);
3049                                         copy_location->set_end (new_end);
3050                                 } else  if (new_start < copy_location->end()) {
3051                                         copy_location->set_start (new_start);
3052                                 } else if (newframe > 0) {
3053                                         _editor->snap_to (next, 1, true);
3054                                         copy_location->set_end (next);
3055                                         copy_location->set_start (newframe);
3056                                 }
3057
3058                         } else { // end marker
3059
3060                                 if (move_both || (*x).move_both) {
3061                                         copy_location->set_end (new_end);
3062                                         copy_location->set_start (new_start);
3063                                 } else if (new_end > copy_location->start()) {
3064                                         copy_location->set_end (new_end);
3065                                 } else if (newframe > 0) {
3066                                         _editor->snap_to (next, -1, true);
3067                                         copy_location->set_start (next);
3068                                         copy_location->set_end (newframe);
3069                                 }
3070                         }
3071                 }
3072
3073                 update_item (copy_location);
3074                 
3075                 /* now lookup the actual GUI items used to display this
3076                  * location and move them to wherever the copy of the location
3077                  * is now. This means that the logic in ARDOUR::Location is
3078                  * still enforced, even though we are not (yet) modifying 
3079                  * the real Location itself.
3080                  */
3081                 
3082                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3083
3084                 if (lm) {
3085                         lm->set_position (copy_location->start(), copy_location->end());
3086                 }
3087
3088         }
3089
3090         assert (!_copied_locations.empty());
3091
3092         show_verbose_cursor_time (newframe);
3093 }
3094
3095 void
3096 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3097 {
3098         if (!movement_occurred) {
3099                 
3100                 if (was_double_click()) {
3101                         _editor->rename_marker (_marker);
3102                         return;
3103                 }
3104
3105                 /* just a click, do nothing but finish
3106                    off the selection process
3107                 */
3108
3109                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3110
3111                 switch (op) {
3112                 case Selection::Set:
3113                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3114                                 _editor->selection->set (_marker);
3115                         }
3116                         break;
3117
3118                 case Selection::Toggle:
3119                         /* we toggle on the button release, click only */
3120                         _editor->selection->toggle (_marker);
3121                         break;
3122
3123                 case Selection::Extend:
3124                 case Selection::Add:
3125                         break;
3126                 }
3127
3128                 return;
3129         }
3130
3131         _editor->_dragging_edit_point = false;
3132
3133         _editor->begin_reversible_command ( _("move marker") );
3134         XMLNode &before = _editor->session()->locations()->get_state();
3135
3136         MarkerSelection::iterator i;
3137         CopiedLocationInfo::iterator x;
3138         bool is_start;
3139
3140         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3141              x != _copied_locations.end() && i != _editor->selection->markers.end();
3142              ++i, ++x) {
3143
3144                 Location * location = _editor->find_location_from_marker (*i, is_start);
3145
3146                 if (location) {
3147
3148                         if (location->locked()) {
3149                                 return;
3150                         }
3151
3152                         if (location->is_mark()) {
3153                                 location->set_start (((*x).location)->start());
3154                         } else {
3155                                 location->set (((*x).location)->start(), ((*x).location)->end());
3156                         }
3157                 }
3158         }
3159
3160         XMLNode &after = _editor->session()->locations()->get_state();
3161         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3162         _editor->commit_reversible_command ();
3163 }
3164
3165 void
3166 MarkerDrag::aborted (bool)
3167 {
3168         /* XXX: TODO */
3169 }
3170
3171 void
3172 MarkerDrag::update_item (Location*)
3173 {
3174         /* noop */
3175 }
3176
3177 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3178         : Drag (e, i),
3179           _cumulative_x_drag (0),
3180           _cumulative_y_drag (0)
3181 {
3182         if (_zero_gain_fraction < 0.0) {
3183                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3184         }
3185
3186         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3187
3188         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3189         assert (_point);
3190 }
3191
3192
3193 void
3194 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3195 {
3196         Drag::start_grab (event, _editor->cursors()->fader);
3197
3198         // start the grab at the center of the control point so
3199         // the point doesn't 'jump' to the mouse after the first drag
3200         _fixed_grab_x = _point->get_x();
3201         _fixed_grab_y = _point->get_y();
3202
3203         float const fraction = 1 - (_point->get_y() / _point->line().height());
3204
3205         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3206
3207         _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3208                                         event->button.x + 10, event->button.y + 10);
3209
3210         _editor->verbose_cursor()->show ();
3211
3212         _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3213
3214         if (!_point->can_slide ()) {
3215                 _x_constrained = true;
3216         }
3217 }
3218
3219 void
3220 ControlPointDrag::motion (GdkEvent* event, bool)
3221 {
3222         double dx = _drags->current_pointer_x() - last_pointer_x();
3223         double dy = _drags->current_pointer_y() - last_pointer_y();
3224
3225         if (event->button.state & Keyboard::SecondaryModifier) {
3226                 dx *= 0.1;
3227                 dy *= 0.1;
3228         }
3229
3230         /* coordinate in pixels relative to the start of the region (for region-based automation)
3231            or track (for track-based automation) */
3232         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3233         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3234
3235         // calculate zero crossing point. back off by .01 to stay on the
3236         // positive side of zero
3237         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3238
3239         // make sure we hit zero when passing through
3240         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3241                 cy = zero_gain_y;
3242         }
3243
3244         if (_x_constrained) {
3245                 cx = _fixed_grab_x;
3246         }
3247         if (_y_constrained) {
3248                 cy = _fixed_grab_y;
3249         }
3250
3251         _cumulative_x_drag = cx - _fixed_grab_x;
3252         _cumulative_y_drag = cy - _fixed_grab_y;
3253
3254         cx = max (0.0, cx);
3255         cy = max (0.0, cy);
3256         cy = min ((double) _point->line().height(), cy);
3257
3258         framepos_t cx_frames = _editor->pixel_to_sample (cx);
3259
3260         if (!_x_constrained) {
3261                 _editor->snap_to_with_modifier (cx_frames, event);
3262         }
3263
3264         cx_frames = min (cx_frames, _point->line().maximum_time());
3265
3266         float const fraction = 1.0 - (cy / _point->line().height());
3267
3268         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3269
3270         _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3271 }
3272
3273 void
3274 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3275 {
3276         if (!movement_occurred) {
3277
3278                 /* just a click */
3279
3280                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3281                         _editor->reset_point_selection ();
3282                 }
3283
3284         } else {
3285                 motion (event, false);
3286         }
3287
3288         _point->line().end_drag (_pushing, _final_index);
3289         _editor->session()->commit_reversible_command ();
3290 }
3291
3292 void
3293 ControlPointDrag::aborted (bool)
3294 {
3295         _point->line().reset ();
3296 }
3297
3298 bool
3299 ControlPointDrag::active (Editing::MouseMode m)
3300 {
3301         if (m == Editing::MouseGain) {
3302                 /* always active in mouse gain */
3303                 return true;
3304         }
3305
3306         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3307         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3308 }
3309
3310 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3311         : Drag (e, i),
3312           _line (0),
3313           _cumulative_y_drag (0)
3314 {
3315         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3316 }
3317
3318 void
3319 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3320 {
3321         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3322         assert (_line);
3323
3324         _item = &_line->grab_item ();
3325
3326         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3327            origin, and ditto for y.
3328         */
3329
3330         double cx = event->button.x;
3331         double cy = event->button.y;
3332
3333         _line->parent_group().canvas_to_item (cx, cy);
3334
3335         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3336
3337         uint32_t before;
3338         uint32_t after;
3339
3340         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3341                 /* no adjacent points */
3342                 return;
3343         }
3344
3345         Drag::start_grab (event, _editor->cursors()->fader);
3346
3347         /* store grab start in parent frame */
3348
3349         _fixed_grab_x = cx;
3350         _fixed_grab_y = cy;
3351
3352         double fraction = 1.0 - (cy / _line->height());
3353
3354         _line->start_drag_line (before, after, fraction);
3355
3356         _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3357                                         event->button.x + 10, event->button.y + 10);
3358
3359         _editor->verbose_cursor()->show ();
3360 }
3361
3362 void
3363 LineDrag::motion (GdkEvent* event, bool)
3364 {
3365         double dy = _drags->current_pointer_y() - last_pointer_y();
3366
3367         if (event->button.state & Keyboard::SecondaryModifier) {
3368                 dy *= 0.1;
3369         }
3370
3371         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3372
3373         _cumulative_y_drag = cy - _fixed_grab_y;
3374
3375         cy = max (0.0, cy);
3376         cy = min ((double) _line->height(), cy);
3377
3378         double const fraction = 1.0 - (cy / _line->height());
3379         uint32_t ignored;
3380
3381         /* we are ignoring x position for this drag, so we can just pass in anything */
3382         _line->drag_motion (0, fraction, true, false, ignored);
3383
3384         _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3385 }
3386
3387 void
3388 LineDrag::finished (GdkEvent* event, bool movement_occured)
3389 {
3390         if (movement_occured) {
3391                 motion (event, false);
3392                 _line->end_drag (false, 0);
3393         } else {
3394                 /* add a new control point on the line */
3395
3396                 AutomationTimeAxisView* atv;
3397
3398                 _line->end_drag (false, 0);
3399
3400                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3401                         framepos_t where = _editor->window_event_sample (event, 0, 0);
3402                         atv->add_automation_event (event, where, event->button.y, false);
3403                 }
3404         }
3405
3406         _editor->session()->commit_reversible_command ();
3407 }
3408
3409 void
3410 LineDrag::aborted (bool)
3411 {
3412         _line->reset ();
3413 }
3414
3415 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3416         : Drag (e, i),
3417           _line (0),
3418           _cumulative_x_drag (0)
3419 {
3420         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3421 }
3422
3423 void
3424 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3425 {
3426         Drag::start_grab (event);
3427
3428         _line = reinterpret_cast<Line*> (_item);
3429         assert (_line);
3430
3431         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3432
3433         double cx = event->button.x;
3434         double cy = event->button.y;
3435
3436         _item->parent()->canvas_to_item (cx, cy);
3437
3438         /* store grab start in parent frame */
3439         _region_view_grab_x = cx;
3440
3441         _before = *(float*) _item->get_data ("position");
3442
3443         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3444
3445         _max_x = _editor->sample_to_pixel(_arv->get_duration());
3446 }
3447
3448 void
3449 FeatureLineDrag::motion (GdkEvent*, bool)
3450 {
3451         double dx = _drags->current_pointer_x() - last_pointer_x();
3452
3453         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3454
3455         _cumulative_x_drag += dx;
3456
3457         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3458
3459         if (cx > _max_x){
3460                 cx = _max_x;
3461         }
3462         else if(cx < 0){
3463                 cx = 0;
3464         }
3465
3466         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3467         assert (bbox);
3468         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3469
3470         float *pos = new float;
3471         *pos = cx;
3472
3473         _line->set_data ("position", pos);
3474
3475         _before = cx;
3476 }
3477
3478 void
3479 FeatureLineDrag::finished (GdkEvent*, bool)
3480 {
3481         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3482         _arv->update_transient(_before, _before);
3483 }
3484
3485 void
3486 FeatureLineDrag::aborted (bool)
3487 {
3488         //_line->reset ();
3489 }
3490
3491 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3492         : Drag (e, i)
3493         , _vertical_only (false)
3494 {
3495         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3496 }
3497
3498 void
3499 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3500 {
3501         Drag::start_grab (event);
3502         show_verbose_cursor_time (adjusted_current_frame (event));
3503 }
3504
3505 void
3506 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3507 {
3508         framepos_t start;
3509         framepos_t end;
3510         double y1;
3511         double y2;
3512
3513         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3514
3515         framepos_t grab = grab_frame ();
3516         if (Config->get_rubberbanding_snaps_to_grid ()) {
3517                 _editor->snap_to_with_modifier (grab, event);
3518         }
3519
3520         /* base start and end on initial click position */
3521
3522         if (pf < grab) {
3523                 start = pf;
3524                 end = grab;
3525         } else {
3526                 end = pf;
3527                 start = grab;
3528         }
3529
3530         if (_drags->current_pointer_y() < grab_y()) {
3531                 y1 = _drags->current_pointer_y();
3532                 y2 = grab_y();
3533         } else {
3534                 y2 = _drags->current_pointer_y();
3535                 y1 = grab_y();
3536         }
3537
3538
3539         if (start != end || y1 != y2) {
3540
3541                 double x1 = _editor->sample_to_pixel (start);
3542                 double x2 = _editor->sample_to_pixel (end);
3543                 const double min_dimension = 2.0;
3544
3545                 _editor->rubberband_rect->set_x0 (x1);
3546                 if (_vertical_only) {
3547                         /* fixed 10 pixel width */
3548                         _editor->rubberband_rect->set_x1 (x1 + 10);
3549                 } else {
3550                         if (x2 < x1) {
3551                                 x2 = min (x1 - min_dimension, x2);
3552                         } else {
3553                                 x2 = max (x1 + min_dimension, x2);
3554                         }
3555                         _editor->rubberband_rect->set_x1 (x2);
3556                 } 
3557
3558                 _editor->rubberband_rect->set_y0 (y1);
3559                 if (y2 < y1) {
3560                         y2 = min (y1 - min_dimension, y2);
3561                 } else {
3562                         y2 = max (y1 + min_dimension, y2);
3563                 }
3564
3565                 _editor->rubberband_rect->set_y1 (y2);
3566                 
3567                 _editor->rubberband_rect->show();
3568                 _editor->rubberband_rect->raise_to_top();
3569
3570                 show_verbose_cursor_time (pf);
3571
3572                 do_select_things (event, true);
3573         }
3574 }
3575
3576 void
3577 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3578 {
3579         framepos_t x1;
3580         framepos_t x2;
3581         
3582         if (grab_frame() < last_pointer_frame()) {
3583                 x1 = grab_frame ();
3584                 x2 = last_pointer_frame ();
3585         } else {
3586                 x2 = grab_frame ();
3587                 x1 = last_pointer_frame ();
3588         }
3589
3590         double y1;
3591         double y2;
3592         
3593         if (_drags->current_pointer_y() < grab_y()) {
3594                 y1 = _drags->current_pointer_y();
3595                 y2 = grab_y();
3596         } else {
3597                 y2 = _drags->current_pointer_y();
3598                 y1 = grab_y();
3599         }
3600
3601         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3602 }
3603
3604 void
3605 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3606 {
3607         if (movement_occurred) {
3608
3609                 motion (event, false);
3610                 do_select_things (event, false);
3611
3612         } else {
3613
3614                 /* just a click */
3615
3616                 bool do_deselect = true;
3617                 MidiTimeAxisView* mtv;
3618
3619                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3620                         /* MIDI track */
3621                         if (_editor->selection->empty()) {
3622                                 /* nothing selected */
3623                                 add_midi_region (mtv);
3624                                 do_deselect = false;
3625                         }
3626                 } 
3627
3628                 /* do not deselect if Primary or Tertiary (toggle-select or
3629                  * extend-select are pressed.
3630                  */
3631
3632                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) && 
3633                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) && 
3634                     do_deselect) {
3635                         deselect_things ();
3636                 }
3637
3638         }
3639
3640         _editor->rubberband_rect->hide();
3641 }
3642
3643 void
3644 RubberbandSelectDrag::aborted (bool)
3645 {
3646         _editor->rubberband_rect->hide ();
3647 }
3648
3649 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3650         : RegionDrag (e, i, p, v)
3651 {
3652         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3653 }
3654
3655 void
3656 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3657 {
3658         Drag::start_grab (event, cursor);
3659
3660         show_verbose_cursor_time (adjusted_current_frame (event));
3661 }
3662
3663 void
3664 TimeFXDrag::motion (GdkEvent* event, bool)
3665 {
3666         RegionView* rv = _primary;
3667         StreamView* cv = rv->get_time_axis_view().view ();
3668
3669         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3670         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3671         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3672
3673         framepos_t const pf = adjusted_current_frame (event);
3674
3675         if (pf > rv->region()->position()) {
3676                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3677         }
3678
3679         show_verbose_cursor_time (pf);
3680 }
3681
3682 void
3683 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3684 {
3685         _primary->get_time_axis_view().hide_timestretch ();
3686
3687         if (!movement_occurred) {
3688                 return;
3689         }
3690
3691         if (last_pointer_frame() < _primary->region()->position()) {
3692                 /* backwards drag of the left edge - not usable */
3693                 return;
3694         }
3695
3696         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3697
3698         float percentage = (double) newlen / (double) _primary->region()->length();
3699
3700 #ifndef USE_RUBBERBAND
3701         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3702         if (_primary->region()->data_type() == DataType::AUDIO) {
3703                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3704         }
3705 #endif
3706
3707         if (!_editor->get_selection().regions.empty()) {
3708                 /* primary will already be included in the selection, and edit
3709                    group shared editing will propagate selection across
3710                    equivalent regions, so just use the current region
3711                    selection.
3712                 */
3713
3714                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3715                         error << _("An error occurred while executing time stretch operation") << endmsg;
3716                 }
3717         }
3718 }
3719
3720 void
3721 TimeFXDrag::aborted (bool)
3722 {
3723         _primary->get_time_axis_view().hide_timestretch ();
3724 }
3725
3726 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3727         : Drag (e, i)
3728 {
3729         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3730 }
3731
3732 void
3733 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3734 {
3735         Drag::start_grab (event);
3736 }
3737
3738 void
3739 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3740 {
3741         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3742 }
3743
3744 void
3745 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3746 {
3747         if (movement_occurred && _editor->session()) {
3748                 /* make sure we stop */
3749                 _editor->session()->request_transport_speed (0.0);
3750         }
3751 }
3752
3753 void
3754 ScrubDrag::aborted (bool)
3755 {
3756         /* XXX: TODO */
3757 }
3758
3759 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3760         : Drag (e, i)
3761         , _operation (o)
3762         , _add (false)
3763         , _extend (false)
3764         , _original_pointer_time_axis (-1)
3765         , _last_pointer_time_axis (-1)
3766         , _time_selection_at_start (!_editor->get_selection().time.empty())
3767 {
3768         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3769         
3770         if (_time_selection_at_start) {
3771                 start_at_start = _editor->get_selection().time.start();
3772                 end_at_start = _editor->get_selection().time.end_frame();
3773         }
3774 }
3775
3776 void
3777 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3778 {
3779         if (_editor->session() == 0) {
3780                 return;
3781         }
3782
3783         Gdk::Cursor* cursor = 0;
3784
3785         switch (_operation) {
3786         case CreateSelection:
3787                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3788                         _add = true;
3789                 } else {
3790                         _add = false;
3791                 }
3792                 cursor = _editor->cursors()->selector;
3793                 Drag::start_grab (event, cursor);
3794                 break;
3795
3796         case SelectionStartTrim:
3797                 if (_editor->clicked_axisview) {
3798                         _editor->clicked_axisview->order_selection_trims (_item, true);
3799                 }
3800                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3801                 break;
3802
3803         case SelectionEndTrim:
3804                 if (_editor->clicked_axisview) {
3805                         _editor->clicked_axisview->order_selection_trims (_item, false);
3806                 }
3807                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3808                 break;
3809
3810         case SelectionMove:
3811                 Drag::start_grab (event, cursor);
3812                 break;
3813
3814         case SelectionExtend:
3815                 Drag::start_grab (event, cursor);
3816                 break;
3817         }
3818
3819         if (_operation == SelectionMove) {
3820                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3821         } else {
3822                 show_verbose_cursor_time (adjusted_current_frame (event));
3823         }
3824
3825         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3826 }
3827
3828 void
3829 SelectionDrag::setup_pointer_frame_offset ()
3830 {
3831         switch (_operation) {
3832         case CreateSelection:
3833                 _pointer_frame_offset = 0;
3834                 break;
3835
3836         case SelectionStartTrim:
3837         case SelectionMove:
3838                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3839                 break;
3840
3841         case SelectionEndTrim:
3842                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3843                 break;
3844
3845         case SelectionExtend:
3846                 break;
3847         }
3848 }
3849
3850 void
3851 SelectionDrag::motion (GdkEvent* event, bool first_move)
3852 {
3853         framepos_t start = 0;
3854         framepos_t end = 0;
3855         framecnt_t length = 0;
3856         framecnt_t distance = 0;
3857
3858         framepos_t const pending_position = adjusted_current_frame (event);
3859
3860         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3861                 return;
3862         }
3863
3864         switch (_operation) {
3865         case CreateSelection:
3866         {
3867                 framepos_t grab = grab_frame ();
3868
3869                 if (first_move) {
3870                         grab = adjusted_current_frame (event, false);
3871                         if (grab < pending_position) {
3872                                 _editor->snap_to (grab, -1);
3873                         }  else {
3874                                 _editor->snap_to (grab, 1);
3875                         }
3876                 }
3877
3878                 if (pending_position < grab) {
3879                         start = pending_position;
3880                         end = grab;
3881                 } else {
3882                         end = pending_position;
3883                         start = grab;
3884                 }
3885
3886                 /* first drag: Either add to the selection
3887                    or create a new selection
3888                 */
3889
3890                 if (first_move) {
3891
3892                         if (_add) {
3893
3894                                 /* adding to the selection */
3895                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3896                                 _editor->clicked_selection = _editor->selection->add (start, end);
3897                                 _add = false;
3898                                 
3899                         } else {
3900
3901                                 /* new selection */
3902
3903                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3904                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3905                                 }
3906
3907                                 _editor->clicked_selection = _editor->selection->set (start, end);
3908                         }
3909                 }
3910                 
3911                 /* select all tracks within the rectangle that we've marked out so far */
3912                 TrackViewList to_be_added_to_selection;
3913                 TrackViewList to_be_removed_from_selection;
3914                 TrackViewList& all_tracks (_editor->track_views);
3915
3916                 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3917                         
3918                         if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3919                                 if (!(*i)->get_selected()) {
3920                                         to_be_added_to_selection.push_back (*i);
3921                                 }
3922                         } else {
3923                                 if ((*i)->get_selected()) {
3924                                         to_be_removed_from_selection.push_back (*i);
3925                                 }
3926                         }
3927                 }
3928
3929                 if (!to_be_added_to_selection.empty()) {
3930                         _editor->selection->add (to_be_added_to_selection);
3931                 }
3932
3933                 if (!to_be_removed_from_selection.empty()) {
3934                         _editor->selection->remove (to_be_removed_from_selection);
3935                 }
3936         }
3937         break;
3938
3939         case SelectionStartTrim:
3940
3941                 start = _editor->selection->time[_editor->clicked_selection].start;
3942                 end = _editor->selection->time[_editor->clicked_selection].end;
3943
3944                 if (pending_position > end) {
3945                         start = end;
3946                 } else {
3947                         start = pending_position;
3948                 }
3949                 break;
3950
3951         case SelectionEndTrim:
3952
3953                 start = _editor->selection->time[_editor->clicked_selection].start;
3954                 end = _editor->selection->time[_editor->clicked_selection].end;
3955
3956                 if (pending_position < start) {
3957                         end = start;
3958                 } else {
3959                         end = pending_position;
3960                 }
3961
3962                 break;
3963                 
3964         case SelectionMove:
3965
3966                 start = _editor->selection->time[_editor->clicked_selection].start;
3967                 end = _editor->selection->time[_editor->clicked_selection].end;
3968
3969                 length = end - start;
3970                 distance = pending_position - start;
3971                 start = pending_position;
3972                 _editor->snap_to (start);
3973
3974                 end = start + length;
3975
3976                 break;
3977
3978         case SelectionExtend:
3979                 break;
3980         }
3981
3982         _editor->maybe_autoscroll (true, false, false);
3983
3984         if (start != end) {
3985                 switch (_operation) {
3986                 case SelectionMove:     
3987                         if (_time_selection_at_start) {
3988                                 _editor->selection->move_time (distance);
3989                         }
3990                         break;
3991                 default:
3992                         _editor->selection->replace (_editor->clicked_selection, start, end);
3993                 }
3994         }
3995
3996         if (_operation == SelectionMove) {
3997                 show_verbose_cursor_time(start);
3998         } else {
3999                 show_verbose_cursor_time(pending_position);
4000         }
4001 }
4002
4003 void
4004 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4005 {
4006         Session* s = _editor->session();
4007
4008         if (movement_occurred) {
4009                 motion (event, false);
4010                 /* XXX this is not object-oriented programming at all. ick */
4011                 if (_editor->selection->time.consolidate()) {
4012                         _editor->selection->TimeChanged ();
4013                 }
4014
4015                 /* XXX what if its a music time selection? */
4016                 if (s) {
4017                         if ( s->get_play_range() && s->transport_rolling() ) {
4018                                 s->request_play_range (&_editor->selection->time, true);
4019                         } else {
4020                                 if (Config->get_always_play_range() && !s->transport_rolling()) {
4021                                         s->request_locate (_editor->get_selection().time.start());
4022                                 }
4023                         }
4024                 }
4025
4026         } else {
4027                 /* just a click, no pointer movement.
4028                  */
4029
4030                 if (_operation == SelectionExtend) {
4031                         if (_time_selection_at_start) {
4032                                 framepos_t pos = adjusted_current_frame (event, false);
4033                                 framepos_t start = min (pos, start_at_start);
4034                                 framepos_t end = max (pos, end_at_start);
4035                                 _editor->selection->set (start, end);
4036                         }
4037                 } else {
4038                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4039                                 if (_editor->clicked_selection) {
4040                                         _editor->selection->remove (_editor->clicked_selection);
4041                                 }
4042                         } else {
4043                                 if (!_editor->clicked_selection) {
4044                                         _editor->selection->clear_time();
4045                                 }
4046                         }
4047                 }
4048
4049                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4050                         _editor->selection->set (_editor->clicked_axisview);
4051                 }
4052                         
4053                 if (s && s->get_play_range () && s->transport_rolling()) {
4054                         s->request_stop (false, false);
4055                 }
4056
4057         }
4058
4059         _editor->stop_canvas_autoscroll ();
4060         _editor->clicked_selection = 0;
4061 }
4062
4063 void
4064 SelectionDrag::aborted (bool)
4065 {
4066         /* XXX: TODO */
4067 }
4068
4069 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4070         : Drag (e, i),
4071           _operation (o),
4072           _copy (false)
4073 {
4074         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4075
4076         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group, 
4077                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
4078                                                                       physical_screen_height (_editor->get_window())));
4079         _drag_rect->hide ();
4080
4081         _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4082         _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4083 }
4084
4085 void
4086 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4087 {
4088         if (_editor->session() == 0) {
4089                 return;
4090         }
4091
4092         Gdk::Cursor* cursor = 0;
4093
4094         if (!_editor->temp_location) {
4095                 _editor->temp_location = new Location (*_editor->session());
4096         }
4097
4098         switch (_operation) {
4099         case CreateRangeMarker:
4100         case CreateTransportMarker:
4101         case CreateCDMarker:
4102
4103                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4104                         _copy = true;
4105                 } else {
4106                         _copy = false;
4107                 }
4108                 cursor = _editor->cursors()->selector;
4109                 break;
4110         }
4111
4112         Drag::start_grab (event, cursor);
4113
4114         show_verbose_cursor_time (adjusted_current_frame (event));
4115 }
4116
4117 void
4118 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4119 {
4120         framepos_t start = 0;
4121         framepos_t end = 0;
4122         ArdourCanvas::Rectangle *crect;
4123
4124         switch (_operation) {
4125         case CreateRangeMarker:
4126                 crect = _editor->range_bar_drag_rect;
4127                 break;
4128         case CreateTransportMarker:
4129                 crect = _editor->transport_bar_drag_rect;
4130                 break;
4131         case CreateCDMarker:
4132                 crect = _editor->cd_marker_bar_drag_rect;
4133                 break;
4134         default:
4135                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4136                 return;
4137                 break;
4138         }
4139
4140         framepos_t const pf = adjusted_current_frame (event);
4141
4142         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4143                 framepos_t grab = grab_frame ();
4144                 _editor->snap_to (grab);
4145
4146                 if (pf < grab_frame()) {
4147                         start = pf;
4148                         end = grab;
4149                 } else {
4150                         end = pf;
4151                         start = grab;
4152                 }
4153
4154                 /* first drag: Either add to the selection
4155                    or create a new selection.
4156                 */
4157
4158                 if (first_move) {
4159
4160                         _editor->temp_location->set (start, end);
4161
4162                         crect->show ();
4163
4164                         update_item (_editor->temp_location);
4165                         _drag_rect->show();
4166                         //_drag_rect->raise_to_top();
4167
4168                 }
4169         }
4170
4171         _editor->maybe_autoscroll (true, false, false);
4172
4173         if (start != end) {
4174                 _editor->temp_location->set (start, end);
4175
4176                 double x1 = _editor->sample_to_pixel (start);
4177                 double x2 = _editor->sample_to_pixel (end);
4178                 crect->set_x0 (x1);
4179                 crect->set_x1 (x2);
4180
4181                 update_item (_editor->temp_location);
4182         }
4183
4184         show_verbose_cursor_time (pf);
4185
4186 }
4187
4188 void
4189 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4190 {
4191         Location * newloc = 0;
4192         string rangename;
4193         int flags;
4194
4195         if (movement_occurred) {
4196                 motion (event, false);
4197                 _drag_rect->hide();
4198
4199                 switch (_operation) {
4200                 case CreateRangeMarker:
4201                 case CreateCDMarker:
4202                     {
4203                         _editor->begin_reversible_command (_("new range marker"));
4204                         XMLNode &before = _editor->session()->locations()->get_state();
4205                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
4206                         if (_operation == CreateCDMarker) {
4207                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
4208                                 _editor->cd_marker_bar_drag_rect->hide();
4209                         }
4210                         else {
4211                                 flags = Location::IsRangeMarker;
4212                                 _editor->range_bar_drag_rect->hide();
4213                         }
4214                         newloc = new Location (
4215                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4216                                 );
4217
4218                         _editor->session()->locations()->add (newloc, true);
4219                         XMLNode &after = _editor->session()->locations()->get_state();
4220                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4221                         _editor->commit_reversible_command ();
4222                         break;
4223                     }
4224
4225                 case CreateTransportMarker:
4226                         // popup menu to pick loop or punch
4227                         _editor->new_transport_marker_context_menu (&event->button, _item);
4228                         break;
4229                 }
4230
4231         } else {
4232
4233                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4234
4235                 if (_operation == CreateTransportMarker) {
4236
4237                         /* didn't drag, so just locate */
4238
4239                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4240
4241                 } else if (_operation == CreateCDMarker) {
4242
4243                         /* didn't drag, but mark is already created so do
4244                          * nothing */
4245
4246                 } else { /* operation == CreateRangeMarker */
4247                         
4248
4249                         framepos_t start;
4250                         framepos_t end;
4251
4252                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4253
4254                         if (end == max_framepos) {
4255                                 end = _editor->session()->current_end_frame ();
4256                         }
4257
4258                         if (start == max_framepos) {
4259                                 start = _editor->session()->current_start_frame ();
4260                         }
4261
4262                         switch (_editor->mouse_mode) {
4263                         case MouseObject:
4264                                 /* find the two markers on either side and then make the selection from it */
4265                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4266                                 break;
4267
4268                         case MouseRange:
4269                                 /* find the two markers on either side of the click and make the range out of it */
4270                                 _editor->selection->set (start, end);
4271                                 break;
4272
4273                         default:
4274                                 break;
4275                         }
4276                 }
4277         }
4278
4279         _editor->stop_canvas_autoscroll ();
4280 }
4281
4282 void
4283 RangeMarkerBarDrag::aborted (bool)
4284 {
4285         /* XXX: TODO */
4286 }
4287
4288 void
4289 RangeMarkerBarDrag::update_item (Location* location)
4290 {
4291         double const x1 = _editor->sample_to_pixel (location->start());
4292         double const x2 = _editor->sample_to_pixel (location->end());
4293
4294         _drag_rect->set_x0 (x1);
4295         _drag_rect->set_x1 (x2);
4296 }
4297
4298 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4299         : Drag (e, i)
4300         , _zoom_out (false)
4301 {
4302         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4303 }
4304
4305 void
4306 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4307 {
4308         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4309                 Drag::start_grab (event, _editor->cursors()->zoom_out);
4310                 _zoom_out = true;
4311         } else {
4312                 Drag::start_grab (event, _editor->cursors()->zoom_in);
4313                 _zoom_out = false;
4314         }
4315
4316         show_verbose_cursor_time (adjusted_current_frame (event));
4317 }
4318
4319 void
4320 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4321 {
4322         framepos_t start;
4323         framepos_t end;
4324
4325         framepos_t const pf = adjusted_current_frame (event);
4326
4327         framepos_t grab = grab_frame ();
4328         _editor->snap_to_with_modifier (grab, event);
4329
4330         /* base start and end on initial click position */
4331         if (pf < grab) {
4332                 start = pf;
4333                 end = grab;
4334         } else {
4335                 end = pf;
4336                 start = grab;
4337         }
4338
4339         if (start != end) {
4340
4341                 if (first_move) {
4342                         _editor->zoom_rect->show();
4343                         _editor->zoom_rect->raise_to_top();
4344                 }
4345
4346                 _editor->reposition_zoom_rect(start, end);
4347
4348                 show_verbose_cursor_time (pf);
4349         }
4350 }
4351
4352 void
4353 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4354 {
4355         if (movement_occurred) {
4356                 motion (event, false);
4357
4358                 if (grab_frame() < last_pointer_frame()) {
4359                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4360                 } else {
4361                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4362                 }
4363         } else {
4364                 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4365                         _editor->tav_zoom_step (_zoom_out);
4366                 } else {
4367                         _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4368                 }
4369         }
4370
4371         _editor->zoom_rect->hide();
4372 }
4373
4374 void
4375 MouseZoomDrag::aborted (bool)
4376 {
4377         _editor->zoom_rect->hide ();
4378 }
4379
4380 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4381         : Drag (e, i)
4382         , _cumulative_dx (0)
4383         , _cumulative_dy (0)
4384 {
4385         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4386
4387         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4388         assert (_primary);
4389         _region = &_primary->region_view ();
4390         _note_height = _region->midi_stream_view()->note_height ();
4391 }
4392
4393 void
4394 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4395 {
4396         Drag::start_grab (event);
4397
4398         if (!(_was_selected = _primary->selected())) {
4399
4400                 /* tertiary-click means extend selection - we'll do that on button release,
4401                    so don't add it here, because otherwise we make it hard to figure
4402                    out the "extend-to" range.
4403                 */
4404
4405                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4406
4407                 if (!extend) {
4408                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4409
4410                         if (add) {
4411                                 _region->note_selected (_primary, true);
4412                         } else {
4413                                 _region->unique_select (_primary);
4414                         }
4415                 }
4416         }
4417 }
4418
4419 /** @return Current total drag x change in frames */
4420 frameoffset_t
4421 NoteDrag::total_dx () const
4422 {
4423         /* dx in frames */
4424         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4425
4426         /* primary note time */
4427         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4428
4429         /* new time of the primary note in session frames */
4430         frameoffset_t st = n + dx;
4431
4432         framepos_t const rp = _region->region()->position ();
4433
4434         /* prevent the note being dragged earlier than the region's position */
4435         st = max (st, rp);
4436
4437         /* snap and return corresponding delta */
4438         return _region->snap_frame_to_frame (st - rp) + rp - n;
4439 }
4440
4441 /** @return Current total drag y change in note number */
4442 int8_t
4443 NoteDrag::total_dy () const
4444 {
4445         MidiStreamView* msv = _region->midi_stream_view ();
4446         double const y = _region->midi_view()->y_position ();
4447         /* new current note */
4448         uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4449         /* clamp */
4450         n = max (msv->lowest_note(), n);
4451         n = min (msv->highest_note(), n);
4452         /* and work out delta */
4453         return n - msv->y_to_note (grab_y() - y);
4454 }
4455
4456 void
4457 NoteDrag::motion (GdkEvent *, bool)
4458 {
4459         /* Total change in x and y since the start of the drag */
4460         frameoffset_t const dx = total_dx ();
4461         int8_t const dy = total_dy ();
4462
4463         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4464         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4465         double const tdy = -dy * _note_height - _cumulative_dy;
4466
4467         if (tdx || tdy) {
4468                 _cumulative_dx += tdx;
4469                 _cumulative_dy += tdy;
4470
4471                 int8_t note_delta = total_dy();
4472
4473                 _region->move_selection (tdx, tdy, note_delta);
4474
4475                 /* the new note value may be the same as the old one, but we
4476                  * don't know what that means because the selection may have
4477                  * involved more than one note and we might be doing something
4478                  * odd with them. so show the note value anyway, always.
4479                  */
4480
4481                 char buf[12];
4482                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4483                 
4484                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4485                           (int) floor ((double)new_note));
4486
4487                 show_verbose_cursor_text (buf);
4488         }
4489 }
4490
4491 void
4492 NoteDrag::finished (GdkEvent* ev, bool moved)
4493 {
4494         if (!moved) {
4495                 /* no motion - select note */
4496                 
4497                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4498                     _editor->current_mouse_mode() == Editing::MouseDraw) {
4499                         
4500                         if (_was_selected) {
4501                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4502                                 if (add) {
4503                                         _region->note_deselected (_primary);
4504                                 }
4505                         } else {
4506                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4507                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4508
4509                                 if (!extend && !add && _region->selection_size() > 1) {
4510                                         _region->unique_select (_primary);
4511                                 } else if (extend) {
4512                                         _region->note_selected (_primary, true, true);
4513                                 } else {
4514                                         /* it was added during button press */
4515                                 }
4516                         }
4517                 }
4518         } else {
4519                 _region->note_dropped (_primary, total_dx(), total_dy());
4520         }
4521 }
4522
4523 void
4524 NoteDrag::aborted (bool)
4525 {
4526         /* XXX: TODO */
4527 }
4528
4529 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4530 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4531         : Drag (editor, atv->base_item ())
4532         , _ranges (r)
4533         , _nothing_to_drag (false)
4534 {
4535         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4536         y_origin = atv->y_position();
4537         setup (atv->lines ());
4538 }
4539
4540 /** Make an AutomationRangeDrag for region gain lines */
4541 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4542         : Drag (editor, rv->get_canvas_group ())
4543         , _ranges (r)
4544         , _nothing_to_drag (false)
4545 {
4546         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4547
4548         list<boost::shared_ptr<AutomationLine> > lines;
4549         lines.push_back (rv->get_gain_line ());
4550         y_origin = rv->get_time_axis_view().y_position();
4551         setup (lines);
4552 }
4553
4554 /** @param lines AutomationLines to drag.
4555  *  @param offset Offset from the session start to the points in the AutomationLines.
4556  */
4557 void
4558 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4559 {
4560         /* find the lines that overlap the ranges being dragged */
4561         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4562         while (i != lines.end ()) {
4563                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4564                 ++j;
4565
4566                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4567
4568                 /* check this range against all the AudioRanges that we are using */
4569                 list<AudioRange>::const_iterator k = _ranges.begin ();
4570                 while (k != _ranges.end()) {
4571                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4572                                 break;
4573                         }
4574                         ++k;
4575                 }
4576
4577                 /* add it to our list if it overlaps at all */
4578                 if (k != _ranges.end()) {
4579                         Line n;
4580                         n.line = *i;
4581                         n.state = 0;
4582                         n.range = r;
4583                         _lines.push_back (n);
4584                 }
4585
4586                 i = j;
4587         }
4588
4589         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4590 }
4591
4592 double
4593 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4594 {
4595         return 1.0 - ((global_y - y_origin) / line->height());
4596 }
4597
4598 void
4599 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4600 {
4601         Drag::start_grab (event, cursor);
4602
4603         /* Get line states before we start changing things */
4604         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4605                 i->state = &i->line->get_state ();
4606                 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4607         }
4608
4609         if (_ranges.empty()) {
4610
4611                 /* No selected time ranges: drag all points */
4612                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4613                         uint32_t const N = i->line->npoints ();
4614                         for (uint32_t j = 0; j < N; ++j) {
4615                                 i->points.push_back (i->line->nth (j));
4616                         }
4617                 }
4618
4619         } else {
4620
4621                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4622
4623                         framecnt_t const half = (i->start + i->end) / 2;
4624
4625                         /* find the line that this audio range starts in */
4626                         list<Line>::iterator j = _lines.begin();
4627                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4628                                 ++j;
4629                         }
4630
4631                         if (j != _lines.end()) {
4632                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4633
4634                                 /* j is the line that this audio range starts in; fade into it;
4635                                    64 samples length plucked out of thin air.
4636                                 */
4637
4638                                 framepos_t a = i->start + 64;
4639                                 if (a > half) {
4640                                         a = half;
4641                                 }
4642
4643                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4644                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4645
4646                                 the_list->add (p, the_list->eval (p));
4647                                 the_list->add (q, the_list->eval (q));
4648                         }
4649
4650                         /* same thing for the end */
4651
4652                         j = _lines.begin();
4653                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4654                                 ++j;
4655                         }
4656
4657                         if (j != _lines.end()) {
4658                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4659
4660                                 /* j is the line that this audio range starts in; fade out of it;
4661                                    64 samples length plucked out of thin air.
4662                                 */
4663
4664                                 framepos_t b = i->end - 64;
4665                                 if (b < half) {
4666                                         b = half;
4667                                 }
4668
4669                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4670                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4671
4672                                 the_list->add (p, the_list->eval (p));
4673                                 the_list->add (q, the_list->eval (q));
4674                         }
4675                 }
4676
4677                 _nothing_to_drag = true;
4678
4679                 /* Find all the points that should be dragged and put them in the relevant
4680                    points lists in the Line structs.
4681                 */
4682
4683                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4684
4685                         uint32_t const N = i->line->npoints ();
4686                         for (uint32_t j = 0; j < N; ++j) {
4687
4688                                 /* here's a control point on this line */
4689                                 ControlPoint* p = i->line->nth (j);
4690                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4691
4692                                 /* see if it's inside a range */
4693                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4694                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4695                                         ++k;
4696                                 }
4697
4698                                 if (k != _ranges.end()) {
4699                                         /* dragging this point */
4700                                         _nothing_to_drag = false;
4701                                         i->points.push_back (p);
4702                                 }
4703                         }
4704                 }
4705         }
4706
4707         if (_nothing_to_drag) {
4708                 return;
4709         }
4710
4711         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4712                 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4713         }
4714 }
4715
4716 void
4717 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4718 {
4719         if (_nothing_to_drag) {
4720                 return;
4721         }
4722
4723         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4724                 float const f = y_fraction (l->line, _drags->current_pointer_y());
4725                 /* we are ignoring x position for this drag, so we can just pass in anything */
4726                 uint32_t ignored;
4727                 l->line->drag_motion (0, f, true, false, ignored);
4728                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4729         }
4730 }
4731
4732 void
4733 AutomationRangeDrag::finished (GdkEvent* event, bool)
4734 {
4735         if (_nothing_to_drag) {
4736                 return;
4737         }
4738
4739         motion (event, false);
4740         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4741                 i->line->end_drag (false, 0);
4742         }
4743
4744         _editor->session()->commit_reversible_command ();
4745 }
4746
4747 void
4748 AutomationRangeDrag::aborted (bool)
4749 {
4750         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4751                 i->line->reset ();
4752         }
4753 }
4754
4755 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4756         : view (v)
4757 {
4758         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4759         layer = v->region()->layer ();
4760         initial_y = v->get_canvas_group()->position().y;
4761         initial_playlist = v->region()->playlist ();
4762         initial_position = v->region()->position ();
4763         initial_end = v->region()->position () + v->region()->length ();
4764 }
4765
4766 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4767         : Drag (e, i->canvas_item ())
4768         , _region_view (r)
4769         , _patch_change (i)
4770         , _cumulative_dx (0)
4771 {
4772         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4773                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4774                                                    grab_frame()));
4775 }
4776
4777 void
4778 PatchChangeDrag::motion (GdkEvent* ev, bool)
4779 {
4780         framepos_t f = adjusted_current_frame (ev);
4781         boost::shared_ptr<Region> r = _region_view->region ();
4782         f = max (f, r->position ());
4783         f = min (f, r->last_frame ());
4784
4785         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4786         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4787         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4788         _cumulative_dx = dxu;
4789 }
4790
4791 void
4792 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4793 {
4794         if (!movement_occurred) {
4795                 return;
4796         }
4797
4798         boost::shared_ptr<Region> r (_region_view->region ());
4799         framepos_t f = adjusted_current_frame (ev);
4800         f = max (f, r->position ());
4801         f = min (f, r->last_frame ());
4802
4803         _region_view->move_patch_change (
4804                 *_patch_change,
4805                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4806                 );
4807 }
4808
4809 void
4810 PatchChangeDrag::aborted (bool)
4811 {
4812         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4813 }
4814
4815 void
4816 PatchChangeDrag::setup_pointer_frame_offset ()
4817 {
4818         boost::shared_ptr<Region> region = _region_view->region ();
4819         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4820 }
4821
4822 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4823         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4824         , _region_view (rv)
4825 {
4826
4827 }
4828
4829 void
4830 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4831 {
4832         framepos_t const p = _region_view->region()->position ();
4833         double const y = _region_view->midi_view()->y_position ();
4834
4835         x1 = max ((framepos_t) 0, x1 - p);
4836         x2 = max ((framepos_t) 0, x2 - p);
4837         y1 = max (0.0, y1 - y);
4838         y2 = max (0.0, y2 - y);
4839         
4840         _region_view->update_drag_selection (
4841                 _editor->sample_to_pixel (x1),
4842                 _editor->sample_to_pixel (x2),
4843                 y1,
4844                 y2,
4845                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4846                 );
4847 }
4848
4849 void
4850 MidiRubberbandSelectDrag::deselect_things ()
4851 {
4852         /* XXX */
4853 }
4854
4855 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4856         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4857         , _region_view (rv)
4858 {
4859         _vertical_only = true;
4860 }
4861
4862 void
4863 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4864 {
4865         double const y = _region_view->midi_view()->y_position ();
4866
4867         y1 = max (0.0, y1 - y);
4868         y2 = max (0.0, y2 - y);
4869         
4870         _region_view->update_vertical_drag_selection (
4871                 y1,
4872                 y2,
4873                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4874                 );
4875 }
4876
4877 void
4878 MidiVerticalSelectDrag::deselect_things ()
4879 {
4880         /* XXX */
4881 }
4882
4883 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4884         : RubberbandSelectDrag (e, i)
4885 {
4886
4887 }
4888
4889 void
4890 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4891 {
4892         if (drag_in_progress) {
4893                 /* We just want to select things at the end of the drag, not during it */
4894                 return;
4895         }
4896         
4897         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4898         
4899         _editor->begin_reversible_command (_("rubberband selection"));
4900         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4901         _editor->commit_reversible_command ();
4902 }
4903
4904 void
4905 EditorRubberbandSelectDrag::deselect_things ()
4906 {
4907         if (!getenv("ARDOUR_SAE")) {
4908                 _editor->selection->clear_tracks();
4909         }
4910         _editor->selection->clear_regions();
4911         _editor->selection->clear_points ();
4912         _editor->selection->clear_lines ();
4913 }
4914
4915 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4916         : Drag (e, i)
4917         , _region_view (rv)
4918         , _drag_rect (0)
4919 {
4920         
4921 }
4922
4923 NoteCreateDrag::~NoteCreateDrag ()
4924 {
4925         delete _drag_rect;
4926 }
4927
4928 framecnt_t
4929 NoteCreateDrag::grid_frames (framepos_t t) const
4930 {
4931         bool success;
4932         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4933         if (!success) {
4934                 grid_beats = 1;
4935         }
4936
4937         return _region_view->region_beats_to_region_frames (grid_beats);
4938 }
4939
4940 void
4941 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4942 {
4943         Drag::start_grab (event, cursor);
4944                                                  
4945         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4946
4947         framepos_t pf = _drags->current_pointer_frame ();
4948         framecnt_t const g = grid_frames (pf);
4949
4950         /* Hack so that we always snap to the note that we are over, instead of snapping
4951            to the next one if we're more than halfway through the one we're over.
4952         */
4953         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4954                 pf -= g / 2;
4955         }
4956
4957         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4958
4959         MidiStreamView* sv = _region_view->midi_stream_view ();
4960         double const x = _editor->sample_to_pixel (_note[0]);
4961         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4962
4963         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4964         _drag_rect->set_outline_all ();
4965         _drag_rect->set_outline_color (0xffffff99);
4966         _drag_rect->set_fill_color (0xffffff66);
4967 }
4968
4969 void
4970 NoteCreateDrag::motion (GdkEvent* event, bool)
4971 {
4972         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4973         double const x = _editor->sample_to_pixel (_note[1]);
4974         if (_note[1] > _note[0]) {
4975                 _drag_rect->set_x1 (x);
4976         } else {
4977                 _drag_rect->set_x0 (x);
4978         }
4979 }
4980
4981 void
4982 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4983 {
4984         if (!had_movement) {
4985                 return;
4986         }
4987         
4988         framepos_t const start = min (_note[0], _note[1]);
4989         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
4990
4991         framecnt_t const g = grid_frames (start);
4992         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4993         
4994         if (_editor->snap_mode() == SnapNormal && length < g) {
4995                 length = g - one_tick;
4996         }
4997
4998         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4999
5000         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5001 }
5002
5003 double
5004 NoteCreateDrag::y_to_region (double y) const
5005 {
5006         double x = 0;
5007         _region_view->get_canvas_group()->canvas_to_item (x, y);
5008         return y;
5009 }
5010
5011 void
5012 NoteCreateDrag::aborted (bool)
5013 {
5014         
5015 }
5016
5017 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5018         : Drag (e, i)
5019         , arv (rv)
5020         , start (start_yn)
5021 {
5022         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5023 }
5024
5025 void
5026 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5027 {
5028         Drag::start_grab (event, cursor);
5029 }
5030
5031 void
5032 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5033 {
5034         double distance;
5035         double new_length;
5036         framecnt_t len;
5037
5038         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5039
5040         if (start) {
5041                 distance = _drags->current_pointer_x() - grab_x();
5042                 len = ar->fade_in()->back()->when;
5043         } else {
5044                 distance = grab_x() - _drags->current_pointer_x();
5045                 len = ar->fade_out()->back()->when;
5046         }
5047
5048         /* how long should it be ? */
5049
5050         new_length = len + _editor->pixel_to_sample (distance);
5051
5052         /* now check with the region that this is legal */
5053
5054         new_length = ar->verify_xfade_bounds (new_length, start);
5055
5056         if (start) {
5057                 arv->reset_fade_in_shape_width (ar, new_length);
5058         } else {
5059                 arv->reset_fade_out_shape_width (ar, new_length);
5060         }
5061 }
5062
5063 void
5064 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5065 {
5066         double distance;
5067         double new_length;
5068         framecnt_t len;
5069
5070         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5071
5072         if (start) {
5073                 distance = _drags->current_pointer_x() - grab_x();
5074                 len = ar->fade_in()->back()->when;
5075         } else {
5076                 distance = grab_x() - _drags->current_pointer_x();
5077                 len = ar->fade_out()->back()->when;
5078         }
5079
5080         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5081         
5082         _editor->begin_reversible_command ("xfade trim");
5083         ar->playlist()->clear_owned_changes (); 
5084
5085         if (start) {
5086                 ar->set_fade_in_length (new_length);
5087         } else {
5088                 ar->set_fade_out_length (new_length);
5089         }
5090
5091         /* Adjusting the xfade may affect other regions in the playlist, so we need
5092            to get undo Commands from the whole playlist rather than just the
5093            region.
5094         */
5095
5096         vector<Command*> cmds;
5097         ar->playlist()->rdiff (cmds);
5098         _editor->session()->add_commands (cmds);
5099         _editor->commit_reversible_command ();
5100
5101 }
5102
5103 void
5104 CrossfadeEdgeDrag::aborted (bool)
5105 {
5106         if (start) {
5107                 arv->redraw_start_xfade ();
5108         } else {
5109                 arv->redraw_end_xfade ();
5110         }
5111 }
5112