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