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