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