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