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