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