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