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