Set up layering_index immediately on an explicit layer, so that undo
[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                         /* remove the section while we drag it */
1957                         map.remove_meter (section, true);
1958                 }
1959         }
1960
1961         framepos_t const pf = adjusted_current_frame (event);
1962         _marker->set_position (pf);
1963         show_verbose_cursor_time (pf);
1964 }
1965
1966 void
1967 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1968 {
1969         if (!movement_occurred) {
1970                 return;
1971         }
1972
1973         motion (event, false);
1974
1975         Timecode::BBT_Time when;
1976
1977         TempoMap& map (_editor->session()->tempo_map());
1978         map.bbt_time (last_pointer_frame(), when);
1979         
1980         if (_copy == true) {
1981                 _editor->begin_reversible_command (_("copy meter mark"));
1982                 XMLNode &before = map.get_state();
1983                 map.add_meter (_marker->meter(), when);
1984                 XMLNode &after = map.get_state();
1985                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1986                 _editor->commit_reversible_command ();
1987
1988         } else {
1989                 _editor->begin_reversible_command (_("move meter mark"));
1990                 XMLNode &before = map.get_state();
1991
1992                 /* we removed it before, so add it back now */
1993                 
1994                 map.add_meter (_marker->meter(), when);
1995                 XMLNode &after = map.get_state();
1996                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1997                 _editor->commit_reversible_command ();
1998         }
1999
2000         // delete the dummy marker we used for visual representation while moving.
2001         // a new visual marker will show up automatically.
2002         delete _marker;
2003 }
2004
2005 void
2006 MeterMarkerDrag::aborted (bool moved)
2007 {
2008         _marker->set_position (_marker->meter().frame ());
2009
2010         if (moved) {
2011                 TempoMap& map (_editor->session()->tempo_map());
2012                 /* we removed it before, so add it back now */
2013                 map.add_meter (_marker->meter(), _marker->meter().frame());
2014                 // delete the dummy marker we used for visual representation while moving.
2015                 // a new visual marker will show up automatically.
2016                 delete _marker;
2017         }
2018 }
2019
2020 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2021         : Drag (e, i),
2022           _copy (c)
2023 {
2024         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2025
2026         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2027         assert (_marker);
2028 }
2029
2030 void
2031 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2032 {
2033         Drag::start_grab (event, cursor);
2034         show_verbose_cursor_time (adjusted_current_frame (event));
2035 }
2036
2037 void
2038 TempoMarkerDrag::setup_pointer_frame_offset ()
2039 {
2040         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2041 }
2042
2043 void
2044 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2045 {
2046         if (first_move) {
2047
2048                 // create a dummy marker for visual representation of moving the
2049                 // section, because whether its a copy or not, we're going to 
2050                 // leave or lose the original marker (leave if its a copy; lose if its
2051                 // not, because we'll remove it from the map).
2052                 
2053                 // create a dummy marker for visual representation of moving the copy.
2054                 // The actual copying is not done before we reach the finish callback.
2055
2056                 char name[64];
2057                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2058
2059                 TempoSection section (_marker->tempo());
2060
2061                 _marker = new TempoMarker (
2062                         *_editor,
2063                         *_editor->tempo_group,
2064                         ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2065                         name,
2066                         *new TempoSection (_marker->tempo())
2067                         );
2068
2069                 /* use the new marker for the grab */
2070                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2071
2072                 if (!_copy) {
2073                         TempoMap& map (_editor->session()->tempo_map());
2074                         /* remove the section while we drag it */
2075                         map.remove_tempo (section, true);
2076                 }
2077         }
2078
2079         framepos_t const pf = adjusted_current_frame (event);
2080         _marker->set_position (pf);
2081         show_verbose_cursor_time (pf);
2082 }
2083
2084 void
2085 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2086 {
2087         if (!movement_occurred) {
2088                 return;
2089         }
2090
2091         motion (event, false);
2092
2093         Timecode::BBT_Time when;
2094
2095         TempoMap& map (_editor->session()->tempo_map());
2096         map.bbt_time (last_pointer_frame(), when);
2097
2098         if (_copy == true) {
2099                 _editor->begin_reversible_command (_("copy tempo mark"));
2100                 XMLNode &before = map.get_state();
2101                 map.add_tempo (_marker->tempo(), when);
2102                 XMLNode &after = map.get_state();
2103                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2104                 _editor->commit_reversible_command ();
2105
2106         } else {
2107                 _editor->begin_reversible_command (_("move tempo mark"));
2108                 XMLNode &before = map.get_state();
2109                 /* we removed it before, so add it back now */
2110                 map.add_tempo (_marker->tempo(), when);
2111                 XMLNode &after = map.get_state();
2112                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2113                 _editor->commit_reversible_command ();
2114         }
2115
2116         // delete the dummy marker we used for visual representation while moving.
2117         // a new visual marker will show up automatically.
2118         delete _marker;
2119 }
2120
2121 void
2122 TempoMarkerDrag::aborted (bool moved)
2123 {
2124         _marker->set_position (_marker->tempo().frame());
2125         if (moved) {
2126                 TempoMap& map (_editor->session()->tempo_map());
2127                 /* we removed it before, so add it back now */
2128                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2129                 // delete the dummy marker we used for visual representation while moving.
2130                 // a new visual marker will show up automatically.
2131                 delete _marker;
2132         }
2133 }
2134
2135 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2136         : Drag (e, i),
2137           _stop (s)
2138 {
2139         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2140 }
2141
2142 /** Do all the things we do when dragging the playhead to make it look as though
2143  *  we have located, without actually doing the locate (because that would cause
2144  *  the diskstream buffers to be refilled, which is too slow).
2145  */
2146 void
2147 CursorDrag::fake_locate (framepos_t t)
2148 {
2149         _editor->playhead_cursor->set_position (t);
2150
2151         Session* s = _editor->session ();
2152         if (s->timecode_transmission_suspended ()) {
2153                 framepos_t const f = _editor->playhead_cursor->current_frame;
2154                 s->send_mmc_locate (f);
2155                 s->send_full_time_code (f);
2156         }
2157
2158         show_verbose_cursor_time (t);
2159         _editor->UpdateAllTransportClocks (t);
2160 }
2161
2162 void
2163 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2164 {
2165         Drag::start_grab (event, c);
2166
2167         _grab_zoom = _editor->frames_per_unit;
2168
2169         framepos_t where = _editor->event_frame (event, 0, 0);
2170         _editor->snap_to_with_modifier (where, event);
2171
2172         _editor->_dragging_playhead = true;
2173
2174         Session* s = _editor->session ();
2175
2176         if (s) {
2177                 if (_was_rolling && _stop) {
2178                         s->request_stop ();
2179                 }
2180
2181                 if (s->is_auditioning()) {
2182                         s->cancel_audition ();
2183                 }
2184
2185                 s->request_suspend_timecode_transmission ();
2186                 while (!s->timecode_transmission_suspended ()) {
2187                         /* twiddle our thumbs */
2188                 }
2189         }
2190
2191         fake_locate (where);
2192 }
2193
2194 void
2195 CursorDrag::motion (GdkEvent* event, bool)
2196 {
2197         framepos_t const adjusted_frame = adjusted_current_frame (event);
2198         if (adjusted_frame != last_pointer_frame()) {
2199                 fake_locate (adjusted_frame);
2200 #ifdef GTKOSX
2201                 _editor->update_canvas_now ();
2202 #endif
2203         }
2204 }
2205
2206 void
2207 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2208 {
2209         _editor->_dragging_playhead = false;
2210
2211         if (!movement_occurred && _stop) {
2212                 return;
2213         }
2214
2215         motion (event, false);
2216
2217         Session* s = _editor->session ();
2218         if (s) {
2219                 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2220                 _editor->_pending_locate_request = true;
2221                 s->request_resume_timecode_transmission ();
2222         }
2223 }
2224
2225 void
2226 CursorDrag::aborted (bool)
2227 {
2228         if (_editor->_dragging_playhead) {
2229                 _editor->session()->request_resume_timecode_transmission ();
2230                 _editor->_dragging_playhead = false;
2231         }
2232
2233         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2234 }
2235
2236 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2237         : RegionDrag (e, i, p, v)
2238 {
2239         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2240 }
2241
2242 void
2243 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2244 {
2245         Drag::start_grab (event, cursor);
2246
2247         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2248         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2249
2250         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2251
2252         arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2253 }
2254
2255 void
2256 FadeInDrag::setup_pointer_frame_offset ()
2257 {
2258         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2259         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2260         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2261 }
2262
2263 void
2264 FadeInDrag::motion (GdkEvent* event, bool)
2265 {
2266         framecnt_t fade_length;
2267
2268         framepos_t const pos = adjusted_current_frame (event);
2269
2270         boost::shared_ptr<Region> region = _primary->region ();
2271
2272         if (pos < (region->position() + 64)) {
2273                 fade_length = 64; // this should be a minimum defined somewhere
2274         } else if (pos > region->last_frame()) {
2275                 fade_length = region->length();
2276         } else {
2277                 fade_length = pos - region->position();
2278         }
2279
2280         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2281
2282                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2283
2284                 if (!tmp) {
2285                         continue;
2286                 }
2287
2288                 tmp->reset_fade_in_shape_width (fade_length);
2289                 tmp->show_fade_line((framecnt_t) fade_length);
2290         }
2291
2292         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2293 }
2294
2295 void
2296 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2297 {
2298         if (!movement_occurred) {
2299                 return;
2300         }
2301
2302         framecnt_t fade_length;
2303
2304         framepos_t const pos = adjusted_current_frame (event);
2305
2306         boost::shared_ptr<Region> region = _primary->region ();
2307
2308         if (pos < (region->position() + 64)) {
2309                 fade_length = 64; // this should be a minimum defined somewhere
2310         } else if (pos > region->last_frame()) {
2311                 fade_length = region->length();
2312         } else {
2313                 fade_length = pos - region->position();
2314         }
2315
2316         _editor->begin_reversible_command (_("change fade in length"));
2317
2318         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2319
2320                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2321
2322                 if (!tmp) {
2323                         continue;
2324                 }
2325
2326                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2327                 XMLNode &before = alist->get_state();
2328
2329                 tmp->audio_region()->set_fade_in_length (fade_length);
2330                 tmp->audio_region()->set_fade_in_active (true);
2331                 tmp->hide_fade_line();
2332
2333                 XMLNode &after = alist->get_state();
2334                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2335         }
2336
2337         _editor->commit_reversible_command ();
2338 }
2339
2340 void
2341 FadeInDrag::aborted (bool)
2342 {
2343         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2344                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2345
2346                 if (!tmp) {
2347                         continue;
2348                 }
2349
2350                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2351                 tmp->hide_fade_line();
2352         }
2353 }
2354
2355 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2356         : RegionDrag (e, i, p, v)
2357 {
2358         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2359 }
2360
2361 void
2362 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2363 {
2364         Drag::start_grab (event, cursor);
2365
2366         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2367         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2368
2369         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2370
2371         arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2372 }
2373
2374 void
2375 FadeOutDrag::setup_pointer_frame_offset ()
2376 {
2377         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2378         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2379         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2380 }
2381
2382 void
2383 FadeOutDrag::motion (GdkEvent* event, bool)
2384 {
2385         framecnt_t fade_length;
2386
2387         framepos_t const pos = adjusted_current_frame (event);
2388
2389         boost::shared_ptr<Region> region = _primary->region ();
2390
2391         if (pos > (region->last_frame() - 64)) {
2392                 fade_length = 64; // this should really be a minimum fade defined somewhere
2393         }
2394         else if (pos < region->position()) {
2395                 fade_length = region->length();
2396         }
2397         else {
2398                 fade_length = region->last_frame() - pos;
2399         }
2400
2401         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2402
2403                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2404
2405                 if (!tmp) {
2406                         continue;
2407                 }
2408
2409                 tmp->reset_fade_out_shape_width (fade_length);
2410                 tmp->show_fade_line(region->length() - fade_length);
2411         }
2412
2413         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2414 }
2415
2416 void
2417 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2418 {
2419         if (!movement_occurred) {
2420                 return;
2421         }
2422
2423         framecnt_t fade_length;
2424
2425         framepos_t const pos = adjusted_current_frame (event);
2426
2427         boost::shared_ptr<Region> region = _primary->region ();
2428
2429         if (pos > (region->last_frame() - 64)) {
2430                 fade_length = 64; // this should really be a minimum fade defined somewhere
2431         }
2432         else if (pos < region->position()) {
2433                 fade_length = region->length();
2434         }
2435         else {
2436                 fade_length = region->last_frame() - pos;
2437         }
2438
2439         _editor->begin_reversible_command (_("change fade out length"));
2440
2441         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2442
2443                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2444
2445                 if (!tmp) {
2446                         continue;
2447                 }
2448
2449                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2450                 XMLNode &before = alist->get_state();
2451
2452                 tmp->audio_region()->set_fade_out_length (fade_length);
2453                 tmp->audio_region()->set_fade_out_active (true);
2454                 tmp->hide_fade_line();
2455
2456                 XMLNode &after = alist->get_state();
2457                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2458         }
2459
2460         _editor->commit_reversible_command ();
2461 }
2462
2463 void
2464 FadeOutDrag::aborted (bool)
2465 {
2466         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2467                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2468
2469                 if (!tmp) {
2470                         continue;
2471                 }
2472
2473                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2474                 tmp->hide_fade_line();
2475         }
2476 }
2477
2478 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2479         : Drag (e, i)
2480 {
2481         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2482
2483         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2484         assert (_marker);
2485
2486         _points.push_back (Gnome::Art::Point (0, 0));
2487         _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2488 }
2489
2490 MarkerDrag::~MarkerDrag ()
2491 {
2492         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2493                 delete *i;
2494         }
2495 }
2496
2497 void
2498 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2499 {
2500         Drag::start_grab (event, cursor);
2501
2502         bool is_start;
2503
2504         Location *location = _editor->find_location_from_marker (_marker, is_start);
2505         _editor->_dragging_edit_point = true;
2506
2507         update_item (location);
2508
2509         // _drag_line->show();
2510         // _line->raise_to_top();
2511
2512         if (is_start) {
2513                 show_verbose_cursor_time (location->start());
2514         } else {
2515                 show_verbose_cursor_time (location->end());
2516         }
2517
2518         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2519
2520         switch (op) {
2521         case Selection::Toggle:
2522                 _editor->selection->toggle (_marker);
2523                 break;
2524         case Selection::Set:
2525                 if (!_editor->selection->selected (_marker)) {
2526                         _editor->selection->set (_marker);
2527                 }
2528                 break;
2529         case Selection::Extend:
2530         {
2531                 Locations::LocationList ll;
2532                 list<Marker*> to_add;
2533                 framepos_t s, e;
2534                 _editor->selection->markers.range (s, e);
2535                 s = min (_marker->position(), s);
2536                 e = max (_marker->position(), e);
2537                 s = min (s, e);
2538                 e = max (s, e);
2539                 if (e < max_framepos) {
2540                         ++e;
2541                 }
2542                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2543                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2544                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2545                         if (lm) {
2546                                 if (lm->start) {
2547                                         to_add.push_back (lm->start);
2548                                 }
2549                                 if (lm->end) {
2550                                         to_add.push_back (lm->end);
2551                                 }
2552                         }
2553                 }
2554                 if (!to_add.empty()) {
2555                         _editor->selection->add (to_add);
2556                 }
2557                 break;
2558         }
2559         case Selection::Add:
2560                 _editor->selection->add (_marker);
2561                 break;
2562         }
2563
2564         /* Set up copies for us to manipulate during the drag */
2565
2566         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2567                 Location* l = _editor->find_location_from_marker (*i, is_start);
2568                 _copied_locations.push_back (new Location (*l));
2569         }
2570 }
2571
2572 void
2573 MarkerDrag::setup_pointer_frame_offset ()
2574 {
2575         bool is_start;
2576         Location *location = _editor->find_location_from_marker (_marker, is_start);
2577         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2578 }
2579
2580 void
2581 MarkerDrag::motion (GdkEvent* event, bool)
2582 {
2583         framecnt_t f_delta = 0;
2584         bool is_start;
2585         bool move_both = false;
2586         Marker* marker;
2587         Location *real_location;
2588         Location *copy_location = 0;
2589
2590         framepos_t const newframe = adjusted_current_frame (event);
2591
2592         framepos_t next = newframe;
2593
2594         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2595                 move_both = true;
2596         }
2597
2598         MarkerSelection::iterator i;
2599         list<Location*>::iterator x;
2600
2601         /* find the marker we're dragging, and compute the delta */
2602
2603         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2604              x != _copied_locations.end() && i != _editor->selection->markers.end();
2605              ++i, ++x) {
2606
2607                 copy_location = *x;
2608                 marker = *i;
2609
2610                 if (marker == _marker) {
2611
2612                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2613                                 /* que pasa ?? */
2614                                 return;
2615                         }
2616
2617                         if (real_location->is_mark()) {
2618                                 f_delta = newframe - copy_location->start();
2619                         } else {
2620
2621
2622                                 switch (marker->type()) {
2623                                 case Marker::SessionStart:
2624                                 case Marker::RangeStart:
2625                                 case Marker::LoopStart:
2626                                 case Marker::PunchIn:
2627                                         f_delta = newframe - copy_location->start();
2628                                         break;
2629
2630                                 case Marker::SessionEnd:
2631                                 case Marker::RangeEnd:
2632                                 case Marker::LoopEnd:
2633                                 case Marker::PunchOut:
2634                                         f_delta = newframe - copy_location->end();
2635                                         break;
2636                                 default:
2637                                         /* what kind of marker is this ? */
2638                                         return;
2639                                 }
2640                         }
2641                         break;
2642                 }
2643         }
2644
2645         if (i == _editor->selection->markers.end()) {
2646                 /* hmm, impossible - we didn't find the dragged marker */
2647                 return;
2648         }
2649
2650         /* now move them all */
2651
2652         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2653              x != _copied_locations.end() && i != _editor->selection->markers.end();
2654              ++i, ++x) {
2655
2656                 copy_location = *x;
2657                 marker = *i;
2658
2659                 /* call this to find out if its the start or end */
2660
2661                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2662                         continue;
2663                 }
2664
2665                 if (real_location->locked()) {
2666                         continue;
2667                 }
2668
2669                 if (copy_location->is_mark()) {
2670
2671                         /* now move it */
2672
2673                         copy_location->set_start (copy_location->start() + f_delta);
2674
2675                 } else {
2676
2677                         framepos_t new_start = copy_location->start() + f_delta;
2678                         framepos_t new_end = copy_location->end() + f_delta;
2679
2680                         if (is_start) { // start-of-range marker
2681
2682                                 if (move_both) {
2683                                         copy_location->set_start (new_start);
2684                                         copy_location->set_end (new_end);
2685                                 } else  if (new_start < copy_location->end()) {
2686                                         copy_location->set_start (new_start);
2687                                 } else if (newframe > 0) {
2688                                         _editor->snap_to (next, 1, true);
2689                                         copy_location->set_end (next);
2690                                         copy_location->set_start (newframe);
2691                                 }
2692
2693                         } else { // end marker
2694
2695                                 if (move_both) {
2696                                         copy_location->set_end (new_end);
2697                                         copy_location->set_start (new_start);
2698                                 } else if (new_end > copy_location->start()) {
2699                                         copy_location->set_end (new_end);
2700                                 } else if (newframe > 0) {
2701                                         _editor->snap_to (next, -1, true);
2702                                         copy_location->set_start (next);
2703                                         copy_location->set_end (newframe);
2704                                 }
2705                         }
2706                 }
2707
2708                 update_item (copy_location);
2709
2710                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2711
2712                 if (lm) {
2713                         lm->set_position (copy_location->start(), copy_location->end());
2714                 }
2715         }
2716
2717         assert (!_copied_locations.empty());
2718
2719         show_verbose_cursor_time (newframe);
2720
2721 #ifdef GTKOSX
2722         _editor->update_canvas_now ();
2723 #endif
2724 }
2725
2726 void
2727 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2728 {
2729         if (!movement_occurred) {
2730
2731                 /* just a click, do nothing but finish
2732                    off the selection process
2733                 */
2734
2735                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2736
2737                 switch (op) {
2738                 case Selection::Set:
2739                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2740                                 _editor->selection->set (_marker);
2741                         }
2742                         break;
2743
2744                 case Selection::Toggle:
2745                 case Selection::Extend:
2746                 case Selection::Add:
2747                         break;
2748                 }
2749
2750                 return;
2751         }
2752
2753         _editor->_dragging_edit_point = false;
2754
2755         _editor->begin_reversible_command ( _("move marker") );
2756         XMLNode &before = _editor->session()->locations()->get_state();
2757
2758         MarkerSelection::iterator i;
2759         list<Location*>::iterator x;
2760         bool is_start;
2761
2762         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2763              x != _copied_locations.end() && i != _editor->selection->markers.end();
2764              ++i, ++x) {
2765
2766                 Location * location = _editor->find_location_from_marker (*i, is_start);
2767
2768                 if (location) {
2769
2770                         if (location->locked()) {
2771                                 return;
2772                         }
2773
2774                         if (location->is_mark()) {
2775                                 location->set_start ((*x)->start());
2776                         } else {
2777                                 location->set ((*x)->start(), (*x)->end());
2778                         }
2779                 }
2780         }
2781
2782         XMLNode &after = _editor->session()->locations()->get_state();
2783         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2784         _editor->commit_reversible_command ();
2785 }
2786
2787 void
2788 MarkerDrag::aborted (bool)
2789 {
2790         /* XXX: TODO */
2791 }
2792
2793 void
2794 MarkerDrag::update_item (Location*)
2795 {
2796         /* noop */
2797 }
2798
2799 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2800         : Drag (e, i),
2801           _cumulative_x_drag (0),
2802           _cumulative_y_drag (0)
2803 {
2804         if (_zero_gain_fraction < 0.0) {
2805                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2806         }
2807
2808         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2809
2810         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2811         assert (_point);
2812 }
2813
2814
2815 void
2816 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2817 {
2818         Drag::start_grab (event, _editor->cursors()->fader);
2819
2820         // start the grab at the center of the control point so
2821         // the point doesn't 'jump' to the mouse after the first drag
2822         _fixed_grab_x = _point->get_x();
2823         _fixed_grab_y = _point->get_y();
2824
2825         float const fraction = 1 - (_point->get_y() / _point->line().height());
2826
2827         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2828
2829         _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2830                                         event->button.x + 10, event->button.y + 10);
2831
2832         _editor->verbose_cursor()->show ();
2833 }
2834
2835 void
2836 ControlPointDrag::motion (GdkEvent* event, bool)
2837 {
2838         double dx = _drags->current_pointer_x() - last_pointer_x();
2839         double dy = _drags->current_pointer_y() - last_pointer_y();
2840
2841         if (event->button.state & Keyboard::SecondaryModifier) {
2842                 dx *= 0.1;
2843                 dy *= 0.1;
2844         }
2845
2846         /* coordinate in pixels relative to the start of the region (for region-based automation)
2847            or track (for track-based automation) */
2848         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2849         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2850
2851         // calculate zero crossing point. back off by .01 to stay on the
2852         // positive side of zero
2853         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2854
2855         // make sure we hit zero when passing through
2856         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2857                 cy = zero_gain_y;
2858         }
2859
2860         if (_x_constrained) {
2861                 cx = _fixed_grab_x;
2862         }
2863         if (_y_constrained) {
2864                 cy = _fixed_grab_y;
2865         }
2866
2867         _cumulative_x_drag = cx - _fixed_grab_x;
2868         _cumulative_y_drag = cy - _fixed_grab_y;
2869
2870         cx = max (0.0, cx);
2871         cy = max (0.0, cy);
2872         cy = min ((double) _point->line().height(), cy);
2873
2874         framepos_t cx_frames = _editor->unit_to_frame (cx);
2875
2876         if (!_x_constrained) {
2877                 _editor->snap_to_with_modifier (cx_frames, event);
2878         }
2879
2880         cx_frames = min (cx_frames, _point->line().maximum_time());
2881
2882         float const fraction = 1.0 - (cy / _point->line().height());
2883
2884         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2885
2886         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2887
2888         _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2889 }
2890
2891 void
2892 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2893 {
2894         if (!movement_occurred) {
2895
2896                 /* just a click */
2897
2898                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2899                         _editor->reset_point_selection ();
2900                 }
2901
2902         } else {
2903                 motion (event, false);
2904         }
2905
2906         _point->line().end_drag ();
2907         _editor->session()->commit_reversible_command ();
2908 }
2909
2910 void
2911 ControlPointDrag::aborted (bool)
2912 {
2913         _point->line().reset ();
2914 }
2915
2916 bool
2917 ControlPointDrag::active (Editing::MouseMode m)
2918 {
2919         if (m == Editing::MouseGain) {
2920                 /* always active in mouse gain */
2921                 return true;
2922         }
2923
2924         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2925         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2926 }
2927
2928 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2929         : Drag (e, i),
2930           _line (0),
2931           _cumulative_y_drag (0)
2932 {
2933         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2934 }
2935
2936 void
2937 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2938 {
2939         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2940         assert (_line);
2941
2942         _item = &_line->grab_item ();
2943
2944         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2945            origin, and ditto for y.
2946         */
2947
2948         double cx = event->button.x;
2949         double cy = event->button.y;
2950
2951         _line->parent_group().w2i (cx, cy);
2952
2953         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2954
2955         uint32_t before;
2956         uint32_t after;
2957
2958         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2959                 /* no adjacent points */
2960                 return;
2961         }
2962
2963         Drag::start_grab (event, _editor->cursors()->fader);
2964
2965         /* store grab start in parent frame */
2966
2967         _fixed_grab_x = cx;
2968         _fixed_grab_y = cy;
2969
2970         double fraction = 1.0 - (cy / _line->height());
2971
2972         _line->start_drag_line (before, after, fraction);
2973
2974         _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2975                                         event->button.x + 10, event->button.y + 10);
2976
2977         _editor->verbose_cursor()->show ();
2978 }
2979
2980 void
2981 LineDrag::motion (GdkEvent* event, bool)
2982 {
2983         double dy = _drags->current_pointer_y() - last_pointer_y();
2984
2985         if (event->button.state & Keyboard::SecondaryModifier) {
2986                 dy *= 0.1;
2987         }
2988
2989         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2990
2991         _cumulative_y_drag = cy - _fixed_grab_y;
2992
2993         cy = max (0.0, cy);
2994         cy = min ((double) _line->height(), cy);
2995
2996         double const fraction = 1.0 - (cy / _line->height());
2997
2998         bool push;
2999
3000         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3001                 push = false;
3002         } else {
3003                 push = true;
3004         }
3005
3006         /* we are ignoring x position for this drag, so we can just pass in anything */
3007         _line->drag_motion (0, fraction, true, push);
3008
3009         _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3010 }
3011
3012 void
3013 LineDrag::finished (GdkEvent* event, bool)
3014 {
3015         motion (event, false);
3016         _line->end_drag ();
3017         _editor->session()->commit_reversible_command ();
3018 }
3019
3020 void
3021 LineDrag::aborted (bool)
3022 {
3023         _line->reset ();
3024 }
3025
3026 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3027         : Drag (e, i),
3028           _line (0),
3029           _cumulative_x_drag (0)
3030 {
3031         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3032 }
3033
3034 void
3035 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3036 {
3037         Drag::start_grab (event);
3038
3039         _line = reinterpret_cast<Line*> (_item);
3040         assert (_line);
3041
3042         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3043
3044         double cx = event->button.x;
3045         double cy = event->button.y;
3046
3047         _item->property_parent().get_value()->w2i(cx, cy);
3048
3049         /* store grab start in parent frame */
3050         _region_view_grab_x = cx;
3051
3052         _before = *(float*) _item->get_data ("position");
3053
3054         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3055
3056         _max_x = _editor->frame_to_pixel(_arv->get_duration());
3057 }
3058
3059 void
3060 FeatureLineDrag::motion (GdkEvent*, bool)
3061 {
3062         double dx = _drags->current_pointer_x() - last_pointer_x();
3063
3064         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3065
3066         _cumulative_x_drag += dx;
3067
3068         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3069
3070         if (cx > _max_x){
3071                 cx = _max_x;
3072         }
3073         else if(cx < 0){
3074                 cx = 0;
3075         }
3076
3077         ArdourCanvas::Points points;
3078
3079         double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3080
3081         _line->get_bounds(x1, y2, x2, y2);
3082
3083         points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3084         points.push_back(Gnome::Art::Point(cx, y2 - y1));
3085
3086         _line->property_points() = points;
3087
3088         float *pos = new float;
3089         *pos = cx;
3090
3091         _line->set_data ("position", pos);
3092
3093         _before = cx;
3094 }
3095
3096 void
3097 FeatureLineDrag::finished (GdkEvent*, bool)
3098 {
3099         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3100         _arv->update_transient(_before, _before);
3101 }
3102
3103 void
3104 FeatureLineDrag::aborted (bool)
3105 {
3106         //_line->reset ();
3107 }
3108
3109 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3110         : Drag (e, i)
3111 {
3112         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3113 }
3114
3115 void
3116 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3117 {
3118         Drag::start_grab (event);
3119         show_verbose_cursor_time (adjusted_current_frame (event));
3120 }
3121
3122 void
3123 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3124 {
3125         framepos_t start;
3126         framepos_t end;
3127         double y1;
3128         double y2;
3129
3130         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3131
3132         framepos_t grab = grab_frame ();
3133         if (Config->get_rubberbanding_snaps_to_grid ()) {
3134                 _editor->snap_to_with_modifier (grab, event);
3135         }
3136
3137         /* base start and end on initial click position */
3138
3139         if (pf < grab) {
3140                 start = pf;
3141                 end = grab;
3142         } else {
3143                 end = pf;
3144                 start = grab;
3145         }
3146
3147         if (_drags->current_pointer_y() < grab_y()) {
3148                 y1 = _drags->current_pointer_y();
3149                 y2 = grab_y();
3150         } else {
3151                 y2 = _drags->current_pointer_y();
3152                 y1 = grab_y();
3153         }
3154
3155
3156         if (start != end || y1 != y2) {
3157
3158                 double x1 = _editor->frame_to_pixel (start);
3159                 double x2 = _editor->frame_to_pixel (end);
3160
3161                 _editor->rubberband_rect->property_x1() = x1;
3162                 _editor->rubberband_rect->property_y1() = y1;
3163                 _editor->rubberband_rect->property_x2() = x2;
3164                 _editor->rubberband_rect->property_y2() = y2;
3165
3166                 _editor->rubberband_rect->show();
3167                 _editor->rubberband_rect->raise_to_top();
3168
3169                 show_verbose_cursor_time (pf);
3170
3171                 do_select_things (event, true);
3172         }
3173 }
3174
3175 void
3176 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3177 {
3178         framepos_t x1;
3179         framepos_t x2;
3180         
3181         if (grab_frame() < last_pointer_frame()) {
3182                 x1 = grab_frame ();
3183                 x2 = last_pointer_frame ();
3184         } else {
3185                 x2 = grab_frame ();
3186                 x1 = last_pointer_frame ();
3187         }
3188
3189         double y1;
3190         double y2;
3191         
3192         if (_drags->current_pointer_y() < grab_y()) {
3193                 y1 = _drags->current_pointer_y();
3194                 y2 = grab_y();
3195         } else {
3196                 y2 = _drags->current_pointer_y();
3197                 y1 = grab_y();
3198         }
3199
3200         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3201 }
3202
3203 void
3204 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3205 {
3206         if (movement_occurred) {
3207
3208                 motion (event, false);
3209                 do_select_things (event, false);
3210
3211         } else {
3212
3213                 deselect_things ();
3214
3215         }
3216
3217         _editor->rubberband_rect->hide();
3218 }
3219
3220 void
3221 RubberbandSelectDrag::aborted (bool)
3222 {
3223         _editor->rubberband_rect->hide ();
3224 }
3225
3226 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3227         : RegionDrag (e, i, p, v)
3228 {
3229         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3230 }
3231
3232 void
3233 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3234 {
3235         Drag::start_grab (event, cursor);
3236
3237         show_verbose_cursor_time (adjusted_current_frame (event));
3238 }
3239
3240 void
3241 TimeFXDrag::motion (GdkEvent* event, bool)
3242 {
3243         RegionView* rv = _primary;
3244
3245         framepos_t const pf = adjusted_current_frame (event);
3246
3247         if (pf > rv->region()->position()) {
3248                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3249         }
3250
3251         show_verbose_cursor_time (pf);
3252 }
3253
3254 void
3255 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3256 {
3257         _primary->get_time_axis_view().hide_timestretch ();
3258
3259         if (!movement_occurred) {
3260                 return;
3261         }
3262
3263         if (last_pointer_frame() < _primary->region()->position()) {
3264                 /* backwards drag of the left edge - not usable */
3265                 return;
3266         }
3267
3268         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3269
3270         float percentage = (double) newlen / (double) _primary->region()->length();
3271
3272 #ifndef USE_RUBBERBAND
3273         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3274         if (_primary->region()->data_type() == DataType::AUDIO) {
3275                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3276         }
3277 #endif
3278
3279         // XXX how do timeFX on multiple regions ?
3280
3281         RegionSelection rs;
3282         rs.add (_primary);
3283
3284         if (_editor->time_stretch (rs, percentage) == -1) {
3285                 error << _("An error occurred while executing time stretch operation") << endmsg;
3286         }
3287 }
3288
3289 void
3290 TimeFXDrag::aborted (bool)
3291 {
3292         _primary->get_time_axis_view().hide_timestretch ();
3293 }
3294
3295 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3296         : Drag (e, i)
3297 {
3298         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3299 }
3300
3301 void
3302 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3303 {
3304         Drag::start_grab (event);
3305 }
3306
3307 void
3308 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3309 {
3310         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3311 }
3312
3313 void
3314 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3315 {
3316         if (movement_occurred && _editor->session()) {
3317                 /* make sure we stop */
3318                 _editor->session()->request_transport_speed (0.0);
3319         }
3320 }
3321
3322 void
3323 ScrubDrag::aborted (bool)
3324 {
3325         /* XXX: TODO */
3326 }
3327
3328 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3329         : Drag (e, i)
3330         , _operation (o)
3331         , _copy (false)
3332         , _original_pointer_time_axis (-1)
3333         , _last_pointer_time_axis (-1)
3334 {
3335         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3336 }
3337
3338 void
3339 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3340 {
3341         if (_editor->session() == 0) {
3342                 return;
3343         }
3344
3345         Gdk::Cursor* cursor = 0;
3346
3347         switch (_operation) {
3348         case CreateSelection:
3349                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3350                         _copy = true;
3351                 } else {
3352                         _copy = false;
3353                 }
3354                 cursor = _editor->cursors()->selector;
3355                 Drag::start_grab (event, cursor);
3356                 break;
3357
3358         case SelectionStartTrim:
3359                 if (_editor->clicked_axisview) {
3360                         _editor->clicked_axisview->order_selection_trims (_item, true);
3361                 }
3362                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3363                 break;
3364
3365         case SelectionEndTrim:
3366                 if (_editor->clicked_axisview) {
3367                         _editor->clicked_axisview->order_selection_trims (_item, false);
3368                 }
3369                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3370                 break;
3371
3372         case SelectionMove:
3373                 Drag::start_grab (event, cursor);
3374                 break;
3375         }
3376
3377         if (_operation == SelectionMove) {
3378                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3379         } else {
3380                 show_verbose_cursor_time (adjusted_current_frame (event));
3381         }
3382
3383         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3384 }
3385
3386 void
3387 SelectionDrag::setup_pointer_frame_offset ()
3388 {
3389         switch (_operation) {
3390         case CreateSelection:
3391                 _pointer_frame_offset = 0;
3392                 break;
3393
3394         case SelectionStartTrim:
3395         case SelectionMove:
3396                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3397                 break;
3398
3399         case SelectionEndTrim:
3400                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3401                 break;
3402         }
3403 }
3404
3405 void
3406 SelectionDrag::motion (GdkEvent* event, bool first_move)
3407 {
3408         framepos_t start = 0;
3409         framepos_t end = 0;
3410         framecnt_t length;
3411
3412         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3413         if (pending_time_axis.first == 0) {
3414                 return;
3415         }
3416
3417         framepos_t const pending_position = adjusted_current_frame (event);
3418
3419         /* only alter selection if things have changed */
3420
3421         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3422                 return;
3423         }
3424
3425         switch (_operation) {
3426         case CreateSelection:
3427         {
3428                 framepos_t grab = grab_frame ();
3429
3430                 if (first_move) {
3431                         _editor->snap_to (grab);
3432                 }
3433
3434                 if (pending_position < grab_frame()) {
3435                         start = pending_position;
3436                         end = grab;
3437                 } else {
3438                         end = pending_position;
3439                         start = grab;
3440                 }
3441
3442                 /* first drag: Either add to the selection
3443                    or create a new selection
3444                 */
3445
3446                 if (first_move) {
3447
3448                         if (_copy) {
3449                                 /* adding to the selection */
3450                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3451                                 //_editor->selection->add (_editor->clicked_axisview);
3452                                 _editor->clicked_selection = _editor->selection->add (start, end);
3453                                 _copy = false;
3454                         } else {
3455                                 /* new selection */
3456
3457                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3458                                         //_editor->selection->set (_editor->clicked_axisview);
3459                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3460                                 }
3461
3462                                 _editor->clicked_selection = _editor->selection->set (start, end);
3463                         }
3464                 }
3465
3466                 /* select the track that we're in */
3467                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3468                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3469                         _editor->selection->add (pending_time_axis.first);
3470                         _added_time_axes.push_back (pending_time_axis.first);
3471                 }
3472
3473                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3474                    tracks that we selected in the first place.
3475                 */
3476
3477                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3478                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3479
3480                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3481                 while (i != _added_time_axes.end()) {
3482
3483                         list<TimeAxisView*>::iterator tmp = i;
3484                         ++tmp;
3485
3486                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3487                                 _editor->selection->remove (*i);
3488                                 _added_time_axes.remove (*i);
3489                         }
3490
3491                         i = tmp;
3492                 }
3493
3494         }
3495         break;
3496
3497         case SelectionStartTrim:
3498
3499                 start = _editor->selection->time[_editor->clicked_selection].start;
3500                 end = _editor->selection->time[_editor->clicked_selection].end;
3501
3502                 if (pending_position > end) {
3503                         start = end;
3504                 } else {
3505                         start = pending_position;
3506                 }
3507                 break;
3508
3509         case SelectionEndTrim:
3510
3511                 start = _editor->selection->time[_editor->clicked_selection].start;
3512                 end = _editor->selection->time[_editor->clicked_selection].end;
3513
3514                 if (pending_position < start) {
3515                         end = start;
3516                 } else {
3517                         end = pending_position;
3518                 }
3519
3520                 break;
3521
3522         case SelectionMove:
3523
3524                 start = _editor->selection->time[_editor->clicked_selection].start;
3525                 end = _editor->selection->time[_editor->clicked_selection].end;
3526
3527                 length = end - start;
3528
3529                 start = pending_position;
3530                 _editor->snap_to (start);
3531
3532                 end = start + length;
3533
3534                 break;
3535         }
3536
3537         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3538                 _editor->start_canvas_autoscroll (1, 0);
3539         }
3540
3541         if (start != end) {
3542                 _editor->selection->replace (_editor->clicked_selection, start, end);
3543         }
3544
3545         if (_operation == SelectionMove) {
3546                 show_verbose_cursor_time(start);
3547         } else {
3548                 show_verbose_cursor_time(pending_position);
3549         }
3550 }
3551
3552 void
3553 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3554 {
3555         Session* s = _editor->session();
3556
3557         if (movement_occurred) {
3558                 motion (event, false);
3559                 /* XXX this is not object-oriented programming at all. ick */
3560                 if (_editor->selection->time.consolidate()) {
3561                         _editor->selection->TimeChanged ();
3562                 }
3563
3564                 /* XXX what if its a music time selection? */
3565                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3566                         s->request_play_range (&_editor->selection->time, true);
3567                 }
3568
3569
3570         } else {
3571                 /* just a click, no pointer movement.*/
3572
3573                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3574                         _editor->selection->clear_time();
3575                 }
3576
3577                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3578                         _editor->selection->set (_editor->clicked_axisview);
3579                 }
3580
3581                 if (s && s->get_play_range () && s->transport_rolling()) {
3582                         s->request_stop (false, false);
3583                 }
3584
3585         }
3586
3587         _editor->stop_canvas_autoscroll ();
3588 }
3589
3590 void
3591 SelectionDrag::aborted (bool)
3592 {
3593         /* XXX: TODO */
3594 }
3595
3596 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3597         : Drag (e, i),
3598           _operation (o),
3599           _copy (false)
3600 {
3601         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3602
3603         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3604                                                    physical_screen_height (_editor->get_window()));
3605         _drag_rect->hide ();
3606
3607         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3608         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3609 }
3610
3611 void
3612 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3613 {
3614         if (_editor->session() == 0) {
3615                 return;
3616         }
3617
3618         Gdk::Cursor* cursor = 0;
3619
3620         if (!_editor->temp_location) {
3621                 _editor->temp_location = new Location (*_editor->session());
3622         }
3623
3624         switch (_operation) {
3625         case CreateRangeMarker:
3626         case CreateTransportMarker:
3627         case CreateCDMarker:
3628
3629                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3630                         _copy = true;
3631                 } else {
3632                         _copy = false;
3633                 }
3634                 cursor = _editor->cursors()->selector;
3635                 break;
3636         }
3637
3638         Drag::start_grab (event, cursor);
3639
3640         show_verbose_cursor_time (adjusted_current_frame (event));
3641 }
3642
3643 void
3644 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3645 {
3646         framepos_t start = 0;
3647         framepos_t end = 0;
3648         ArdourCanvas::SimpleRect *crect;
3649
3650         switch (_operation) {
3651         case CreateRangeMarker:
3652                 crect = _editor->range_bar_drag_rect;
3653                 break;
3654         case CreateTransportMarker:
3655                 crect = _editor->transport_bar_drag_rect;
3656                 break;
3657         case CreateCDMarker:
3658                 crect = _editor->cd_marker_bar_drag_rect;
3659                 break;
3660         default:
3661                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3662                 return;
3663                 break;
3664         }
3665
3666         framepos_t const pf = adjusted_current_frame (event);
3667
3668         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3669                 framepos_t grab = grab_frame ();
3670                 _editor->snap_to (grab);
3671
3672                 if (pf < grab_frame()) {
3673                         start = pf;
3674                         end = grab;
3675                 } else {
3676                         end = pf;
3677                         start = grab;
3678                 }
3679
3680                 /* first drag: Either add to the selection
3681                    or create a new selection.
3682                 */
3683
3684                 if (first_move) {
3685
3686                         _editor->temp_location->set (start, end);
3687
3688                         crect->show ();
3689
3690                         update_item (_editor->temp_location);
3691                         _drag_rect->show();
3692                         //_drag_rect->raise_to_top();
3693
3694                 }
3695         }
3696
3697         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3698                 _editor->start_canvas_autoscroll (1, 0);
3699         }
3700
3701         if (start != end) {
3702                 _editor->temp_location->set (start, end);
3703
3704                 double x1 = _editor->frame_to_pixel (start);
3705                 double x2 = _editor->frame_to_pixel (end);
3706                 crect->property_x1() = x1;
3707                 crect->property_x2() = x2;
3708
3709                 update_item (_editor->temp_location);
3710         }
3711
3712         show_verbose_cursor_time (pf);
3713
3714 }
3715
3716 void
3717 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3718 {
3719         Location * newloc = 0;
3720         string rangename;
3721         int flags;
3722
3723         if (movement_occurred) {
3724                 motion (event, false);
3725                 _drag_rect->hide();
3726
3727                 switch (_operation) {
3728                 case CreateRangeMarker:
3729                 case CreateCDMarker:
3730                     {
3731                         _editor->begin_reversible_command (_("new range marker"));
3732                         XMLNode &before = _editor->session()->locations()->get_state();
3733                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3734                         if (_operation == CreateCDMarker) {
3735                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3736                                 _editor->cd_marker_bar_drag_rect->hide();
3737                         }
3738                         else {
3739                                 flags = Location::IsRangeMarker;
3740                                 _editor->range_bar_drag_rect->hide();
3741                         }
3742                         newloc = new Location (
3743                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3744                                 );
3745
3746                         _editor->session()->locations()->add (newloc, true);
3747                         XMLNode &after = _editor->session()->locations()->get_state();
3748                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3749                         _editor->commit_reversible_command ();
3750                         break;
3751                     }
3752
3753                 case CreateTransportMarker:
3754                         // popup menu to pick loop or punch
3755                         _editor->new_transport_marker_context_menu (&event->button, _item);
3756                         break;
3757                 }
3758         } else {
3759                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3760
3761                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3762
3763                         framepos_t start;
3764                         framepos_t end;
3765
3766                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3767
3768                         if (end == max_framepos) {
3769                                 end = _editor->session()->current_end_frame ();
3770                         }
3771
3772                         if (start == max_framepos) {
3773                                 start = _editor->session()->current_start_frame ();
3774                         }
3775
3776                         switch (_editor->mouse_mode) {
3777                         case MouseObject:
3778                                 /* find the two markers on either side and then make the selection from it */
3779                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3780                                 break;
3781
3782                         case MouseRange:
3783                                 /* find the two markers on either side of the click and make the range out of it */
3784                                 _editor->selection->set (start, end);
3785                                 break;
3786
3787                         default:
3788                                 break;
3789                         }
3790                 }
3791         }
3792
3793         _editor->stop_canvas_autoscroll ();
3794 }
3795
3796 void
3797 RangeMarkerBarDrag::aborted (bool)
3798 {
3799         /* XXX: TODO */
3800 }
3801
3802 void
3803 RangeMarkerBarDrag::update_item (Location* location)
3804 {
3805         double const x1 = _editor->frame_to_pixel (location->start());
3806         double const x2 = _editor->frame_to_pixel (location->end());
3807
3808         _drag_rect->property_x1() = x1;
3809         _drag_rect->property_x2() = x2;
3810 }
3811
3812 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3813         : Drag (e, i)
3814         , _zoom_out (false)
3815 {
3816         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3817 }
3818
3819 void
3820 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3821 {
3822         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3823                 Drag::start_grab (event, _editor->cursors()->zoom_out);
3824                 _zoom_out = true;
3825         } else {
3826                 Drag::start_grab (event, _editor->cursors()->zoom_in);
3827                 _zoom_out = false;
3828         }
3829
3830         show_verbose_cursor_time (adjusted_current_frame (event));
3831 }
3832
3833 void
3834 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3835 {
3836         framepos_t start;
3837         framepos_t end;
3838
3839         framepos_t const pf = adjusted_current_frame (event);
3840
3841         framepos_t grab = grab_frame ();
3842         _editor->snap_to_with_modifier (grab, event);
3843
3844         /* base start and end on initial click position */
3845         if (pf < grab) {
3846                 start = pf;
3847                 end = grab;
3848         } else {
3849                 end = pf;
3850                 start = grab;
3851         }
3852
3853         if (start != end) {
3854
3855                 if (first_move) {
3856                         _editor->zoom_rect->show();
3857                         _editor->zoom_rect->raise_to_top();
3858                 }
3859
3860                 _editor->reposition_zoom_rect(start, end);
3861
3862                 show_verbose_cursor_time (pf);
3863         }
3864 }
3865
3866 void
3867 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3868 {
3869         if (movement_occurred) {
3870                 motion (event, false);
3871
3872                 if (grab_frame() < last_pointer_frame()) {
3873                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3874                 } else {
3875                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3876                 }
3877         } else {
3878                 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3879                         _editor->tav_zoom_step (_zoom_out);
3880                 } else {
3881                         _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3882                 }
3883         }
3884
3885         _editor->zoom_rect->hide();
3886 }
3887
3888 void
3889 MouseZoomDrag::aborted (bool)
3890 {
3891         _editor->zoom_rect->hide ();
3892 }
3893
3894 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3895         : Drag (e, i)
3896         , _cumulative_dx (0)
3897         , _cumulative_dy (0)
3898 {
3899         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3900
3901         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3902         _region = &_primary->region_view ();
3903         _note_height = _region->midi_stream_view()->note_height ();
3904 }
3905
3906 void
3907 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3908 {
3909         Drag::start_grab (event);
3910
3911         if (!(_was_selected = _primary->selected())) {
3912
3913                 /* tertiary-click means extend selection - we'll do that on button release,
3914                    so don't add it here, because otherwise we make it hard to figure
3915                    out the "extend-to" range.
3916                 */
3917
3918                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3919
3920                 if (!extend) {
3921                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3922
3923                         if (add) {
3924                                 _region->note_selected (_primary, true);
3925                         } else {
3926                                 _region->unique_select (_primary);
3927                         }
3928                 }
3929         }
3930 }
3931
3932 /** @return Current total drag x change in frames */
3933 frameoffset_t
3934 NoteDrag::total_dx () const
3935 {
3936         /* dx in frames */
3937         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3938
3939         /* primary note time */
3940         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3941
3942         /* new time of the primary note in session frames */
3943         frameoffset_t st = n + dx;
3944
3945         framepos_t const rp = _region->region()->position ();
3946
3947         /* prevent the note being dragged earlier than the region's position */
3948         st = max (st, rp);
3949
3950         /* snap and return corresponding delta */
3951         return _region->snap_frame_to_frame (st - rp) + rp - n;
3952 }
3953
3954 /** @return Current total drag y change in note number */
3955 int8_t
3956 NoteDrag::total_dy () const
3957 {
3958         return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3959 }
3960
3961 void
3962 NoteDrag::motion (GdkEvent *, bool)
3963 {
3964         /* Total change in x and y since the start of the drag */
3965         frameoffset_t const dx = total_dx ();
3966         int8_t const dy = total_dy ();
3967
3968         /* Now work out what we have to do to the note canvas items to set this new drag delta */
3969         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3970         double const tdy = -dy * _note_height - _cumulative_dy;
3971
3972         if (tdx || tdy) {
3973                 _cumulative_dx += tdx;
3974                 _cumulative_dy += tdy;
3975
3976                 int8_t note_delta = total_dy();
3977
3978                 _region->move_selection (tdx, tdy, note_delta);
3979
3980                 /* the new note value may be the same as the old one, but we
3981                  * don't know what that means because the selection may have
3982                  * involved more than one note and we might be doing something
3983                  * odd with them. so show the note value anyway, always.
3984                  */
3985
3986                 char buf[12];
3987                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3988                 
3989                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3990                           (int) floor (new_note));
3991
3992                 show_verbose_cursor_text (buf);
3993         }
3994 }
3995
3996 void
3997 NoteDrag::finished (GdkEvent* ev, bool moved)
3998 {
3999         if (!moved) {
4000                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4001
4002                         if (_was_selected) {
4003                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4004                                 if (add) {
4005                                         _region->note_deselected (_primary);
4006                                 }
4007                         } else {
4008                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4009                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4010
4011                                 if (!extend && !add && _region->selection_size() > 1) {
4012                                         _region->unique_select (_primary);
4013                                 } else if (extend) {
4014                                         _region->note_selected (_primary, true, true);
4015                                 } else {
4016                                         /* it was added during button press */
4017                                 }
4018                         }
4019                 }
4020         } else {
4021                 _region->note_dropped (_primary, total_dx(), total_dy());
4022         }
4023 }
4024
4025 void
4026 NoteDrag::aborted (bool)
4027 {
4028         /* XXX: TODO */
4029 }
4030
4031 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4032         : Drag (editor, item)
4033         , _ranges (r)
4034         , _nothing_to_drag (false)
4035 {
4036         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4037
4038         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4039         assert (_atav);
4040
4041         /* get all lines in the automation view */
4042         list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4043
4044         /* find those that overlap the ranges being dragged */
4045         list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4046         while (i != lines.end ()) {
4047                 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4048                 ++j;
4049
4050                 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4051
4052                 /* check this range against all the AudioRanges that we are using */
4053                 list<AudioRange>::const_iterator k = _ranges.begin ();
4054                 while (k != _ranges.end()) {
4055                         if (k->coverage (r.first, r.second) != OverlapNone) {
4056                                 break;
4057                         }
4058                         ++k;
4059                 }
4060
4061                 /* add it to our list if it overlaps at all */
4062                 if (k != _ranges.end()) {
4063                         Line n;
4064                         n.line = *i;
4065                         n.state = 0;
4066                         n.range = r;
4067                         _lines.push_back (n);
4068                 }
4069
4070                 i = j;
4071         }
4072
4073         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4074 }
4075
4076 void
4077 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4078 {
4079         Drag::start_grab (event, cursor);
4080
4081         /* Get line states before we start changing things */
4082         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4083                 i->state = &i->line->get_state ();
4084         }
4085
4086         if (_ranges.empty()) {
4087
4088                 /* No selected time ranges: drag all points */
4089                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4090                         uint32_t const N = i->line->npoints ();
4091                         for (uint32_t j = 0; j < N; ++j) {
4092                                 i->points.push_back (i->line->nth (j));
4093                         }
4094                 }
4095
4096         } else {
4097
4098                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4099
4100                         framecnt_t const half = (i->start + i->end) / 2;
4101
4102                         /* find the line that this audio range starts in */
4103                         list<Line>::iterator j = _lines.begin();
4104                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4105                                 ++j;
4106                         }
4107
4108                         if (j != _lines.end()) {
4109                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4110
4111                                 /* j is the line that this audio range starts in; fade into it;
4112                                    64 samples length plucked out of thin air.
4113                                 */
4114
4115                                 framepos_t a = i->start + 64;
4116                                 if (a > half) {
4117                                         a = half;
4118                                 }
4119
4120                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4121                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4122
4123                                 the_list->add (p, the_list->eval (p));
4124                                 j->line->add_always_in_view (p);
4125                                 the_list->add (q, the_list->eval (q));
4126                                 j->line->add_always_in_view (q);
4127                         }
4128
4129                         /* same thing for the end */
4130
4131                         j = _lines.begin();
4132                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4133                                 ++j;
4134                         }
4135
4136                         if (j != _lines.end()) {
4137                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4138
4139                                 /* j is the line that this audio range starts in; fade out of it;
4140                                    64 samples length plucked out of thin air.
4141                                 */
4142
4143                                 framepos_t b = i->end - 64;
4144                                 if (b < half) {
4145                                         b = half;
4146                                 }
4147
4148                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4149                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4150
4151                                 the_list->add (p, the_list->eval (p));
4152                                 j->line->add_always_in_view (p);
4153                                 the_list->add (q, the_list->eval (q));
4154                                 j->line->add_always_in_view (q);
4155                         }
4156                 }
4157
4158                 _nothing_to_drag = true;
4159
4160                 /* Find all the points that should be dragged and put them in the relevant
4161                    points lists in the Line structs.
4162                 */
4163
4164                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4165
4166                         uint32_t const N = i->line->npoints ();
4167                         for (uint32_t j = 0; j < N; ++j) {
4168
4169                                 /* here's a control point on this line */
4170                                 ControlPoint* p = i->line->nth (j);
4171                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4172
4173                                 /* see if it's inside a range */
4174                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4175                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4176                                         ++k;
4177                                 }
4178
4179                                 if (k != _ranges.end()) {
4180                                         /* dragging this point */
4181                                         _nothing_to_drag = false;
4182                                         i->points.push_back (p);
4183                                 }
4184                         }
4185                 }
4186         }
4187
4188         if (_nothing_to_drag) {
4189                 return;
4190         }
4191
4192         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4193                 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4194         }
4195 }
4196
4197 void
4198 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4199 {
4200         if (_nothing_to_drag) {
4201                 return;
4202         }
4203
4204         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4205                 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4206
4207                 /* we are ignoring x position for this drag, so we can just pass in anything */
4208                 i->line->drag_motion (0, f, true, false);
4209         }
4210 }
4211
4212 void
4213 AutomationRangeDrag::finished (GdkEvent* event, bool)
4214 {
4215         if (_nothing_to_drag) {
4216                 return;
4217         }
4218
4219         motion (event, false);
4220         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4221                 i->line->end_drag ();
4222                 i->line->clear_always_in_view ();
4223         }
4224
4225         _editor->session()->commit_reversible_command ();
4226 }
4227
4228 void
4229 AutomationRangeDrag::aborted (bool)
4230 {
4231         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4232                 i->line->clear_always_in_view ();
4233                 i->line->reset ();
4234         }
4235 }
4236
4237 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4238         : view (v)
4239 {
4240         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4241         layer = v->region()->layer ();
4242         initial_y = v->get_canvas_group()->property_y ();
4243         initial_playlist = v->region()->playlist ();
4244         initial_position = v->region()->position ();
4245         initial_end = v->region()->position () + v->region()->length ();
4246 }
4247
4248 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4249         : Drag (e, i)
4250         , _region_view (r)
4251         , _patch_change (i)
4252         , _cumulative_dx (0)
4253 {
4254         DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4255 }
4256
4257 void
4258 PatchChangeDrag::motion (GdkEvent* ev, bool)
4259 {
4260         framepos_t f = adjusted_current_frame (ev);
4261         boost::shared_ptr<Region> r = _region_view->region ();
4262         f = max (f, r->position ());
4263         f = min (f, r->last_frame ());
4264
4265         framecnt_t const dxf = f - grab_frame();
4266         double const dxu = _editor->frame_to_unit (dxf);
4267         _patch_change->move (dxu - _cumulative_dx, 0);
4268         _cumulative_dx = dxu;
4269 }
4270
4271 void
4272 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4273 {
4274         if (!movement_occurred) {
4275                 return;
4276         }
4277
4278         boost::shared_ptr<Region> r (_region_view->region ());
4279
4280         framepos_t f = adjusted_current_frame (ev);
4281         f = max (f, r->position ());
4282         f = min (f, r->last_frame ());
4283
4284         _region_view->move_patch_change (
4285                 *_patch_change,
4286                 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4287                 );
4288 }
4289
4290 void
4291 PatchChangeDrag::aborted (bool)
4292 {
4293         _patch_change->move (-_cumulative_dx, 0);
4294 }
4295
4296 void
4297 PatchChangeDrag::setup_pointer_frame_offset ()
4298 {
4299         boost::shared_ptr<Region> region = _region_view->region ();
4300         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4301 }
4302
4303 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4304         : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4305         , _region_view (rv)
4306 {
4307
4308 }
4309
4310 void
4311 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4312 {
4313         framepos_t const p = _region_view->region()->position ();
4314         double const y = _region_view->midi_view()->y_position ();
4315
4316         x1 = max ((framepos_t) 0, x1 - p);
4317         x2 = max ((framepos_t) 0, x2 - p);
4318         y1 = max (0.0, y1 - y);
4319         y2 = max (0.0, y2 - y);
4320         
4321         _region_view->update_drag_selection (
4322                 _editor->frame_to_pixel (x1),
4323                 _editor->frame_to_pixel (x2),
4324                 y1,
4325                 y2,
4326                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4327                 );
4328 }
4329
4330 void
4331 MidiRubberbandSelectDrag::deselect_things ()
4332 {
4333         /* XXX */
4334 }
4335
4336 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4337         : RubberbandSelectDrag (e, i)
4338 {
4339
4340 }
4341
4342 void
4343 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4344 {
4345         if (drag_in_progress) {
4346                 /* We just want to select things at the end of the drag, not during it */
4347                 return;
4348         }
4349         
4350         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4351         
4352         _editor->begin_reversible_command (_("rubberband selection"));
4353         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4354         _editor->commit_reversible_command ();
4355 }
4356
4357 void
4358 EditorRubberbandSelectDrag::deselect_things ()
4359 {
4360         if (!getenv("ARDOUR_SAE")) {
4361                 _editor->selection->clear_tracks();
4362         }
4363         _editor->selection->clear_regions();
4364         _editor->selection->clear_points ();
4365         _editor->selection->clear_lines ();
4366 }
4367
4368 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4369         : Drag (e, i)
4370         , _region_view (rv)
4371         , _drag_rect (0)
4372 {
4373         
4374 }
4375
4376 NoteCreateDrag::~NoteCreateDrag ()
4377 {
4378         delete _drag_rect;
4379 }
4380
4381 framecnt_t
4382 NoteCreateDrag::grid_frames (framepos_t t) const
4383 {
4384         bool success;
4385         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4386         if (!success) {
4387                 grid_beats = 1;
4388         }
4389
4390         return _region_view->region_beats_to_region_frames (grid_beats);
4391 }
4392
4393 void
4394 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4395 {
4396         Drag::start_grab (event, cursor);
4397                                                  
4398         _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4399
4400         framepos_t pf = _drags->current_pointer_frame ();
4401         framecnt_t const g = grid_frames (pf);
4402
4403         /* Hack so that we always snap to the note that we are over, instead of snapping
4404            to the next one if we're more than halfway through the one we're over.
4405         */
4406         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4407                 pf -= g / 2;
4408         }
4409
4410         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4411
4412         MidiStreamView* sv = _region_view->midi_stream_view ();
4413         double const x = _editor->frame_to_pixel (_note[0]);
4414         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4415
4416         _drag_rect->property_x1() = x;
4417         _drag_rect->property_y1() = y;
4418         _drag_rect->property_x2() = x;
4419         _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4420
4421         _drag_rect->property_outline_what() = 0xff;
4422         _drag_rect->property_outline_color_rgba() = 0xffffff99;
4423         _drag_rect->property_fill_color_rgba()    = 0xffffff66;
4424 }
4425
4426 void
4427 NoteCreateDrag::motion (GdkEvent* event, bool)
4428 {
4429         _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4430         double const x = _editor->frame_to_pixel (_note[1]);
4431         if (_note[1] > _note[0]) {
4432                 _drag_rect->property_x2() = x;
4433         } else {
4434                 _drag_rect->property_x1() = x;
4435         }
4436 }
4437
4438 void
4439 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4440 {
4441         if (!had_movement) {
4442                 return;
4443         }
4444         
4445         framepos_t const start = min (_note[0], _note[1]);
4446         framecnt_t length = abs (_note[0] - _note[1]);
4447
4448         framecnt_t const g = grid_frames (start);
4449         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_bar_division;
4450         
4451         if (_editor->snap_mode() == SnapNormal && length < g) {
4452                 length = g - one_tick;
4453         }
4454
4455         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4456
4457         _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4458 }
4459
4460 double
4461 NoteCreateDrag::y_to_region (double y) const
4462 {
4463         double x = 0;
4464         _region_view->get_canvas_group()->w2i (x, y);
4465         return y;
4466 }
4467
4468 void
4469 NoteCreateDrag::aborted (bool)
4470 {
4471         
4472 }