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