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