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