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