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