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