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