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