Strip trailing whitespace and fix other whitespace errors (e.g. space/tab mixing...
[ardour.git] / gtk2_ardour / editor_drag.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "pbd/memento_command.h"
21 #include "pbd/basename.h"
22 #include "ardour/diskstream.h"
23 #include "ardour/dB.h"
24 #include "ardour/region_factory.h"
25 #include "ardour/midi_diskstream.h"
26 #include "editor.h"
27 #include "i18n.h"
28 #include "keyboard.h"
29 #include "audio_region_view.h"
30 #include "midi_region_view.h"
31 #include "ardour_ui.h"
32 #include "control_point.h"
33 #include "utils.h"
34 #include "region_gain_line.h"
35 #include "editor_drag.h"
36 #include "audio_time_axis.h"
37 #include "midi_time_axis.h"
38 #include "canvas-note.h"
39 #include "selection.h"
40 #include "midi_selection.h"
41
42 using namespace std;
43 using namespace ARDOUR;
44 using namespace PBD;
45 using namespace sigc;
46 using namespace Gtk;
47 using namespace Editing;
48 using namespace ArdourCanvas;
49
50 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
51
52 Drag::Drag (Editor* e, ArdourCanvas::Item* i) :
53         _editor (e),
54         _item (i),
55         _pointer_frame_offset (0),
56         _grab_frame (0),
57         _last_pointer_frame (0),
58         _current_pointer_frame (0),
59         _had_movement (false),
60         _move_threshold_passed (false)
61 {
62
63 }
64
65 void
66 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
67 {
68         _item->ungrab (0);
69         _item = new_item;
70
71         if (cursor == 0) {
72                 cursor = _editor->which_grabber_cursor ();
73         }
74
75         _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
76 }
77
78 void
79 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
80 {
81         if (cursor == 0) {
82                 cursor = _editor->which_grabber_cursor ();
83         }
84
85         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
86
87         if (Keyboard::is_button2_event (&event->button)) {
88                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
89                         _y_constrained = true;
90                         _x_constrained = false;
91                 } else {
92                         _y_constrained = false;
93                         _x_constrained = true;
94                 }
95         } else {
96                 _x_constrained = false;
97                 _y_constrained = false;
98         }
99
100         _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
101         _last_pointer_frame = _grab_frame;
102         _current_pointer_frame = _grab_frame;
103         _current_pointer_x = _grab_x;
104         _current_pointer_y = _grab_y;
105         _last_pointer_x = _current_pointer_x;
106         _last_pointer_y = _current_pointer_y;
107
108         _original_x = 0;
109         _original_y = 0;
110         _item->i2w (_original_x, _original_y);
111
112         _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
113                               *cursor,
114                               event->button.time);
115
116         if (_editor->session && _editor->session->transport_rolling()) {
117                 _was_rolling = true;
118         } else {
119                 _was_rolling = false;
120         }
121
122         switch (_editor->snap_type) {
123         case SnapToRegionStart:
124         case SnapToRegionEnd:
125         case SnapToRegionSync:
126         case SnapToRegionBoundary:
127                 _editor->build_region_boundary_cache ();
128                 break;
129         default:
130                 break;
131         }
132 }
133
134 /** @param event GDK event, or 0.
135  *  @return true if some movement occurred, otherwise false.
136  */
137 bool
138 Drag::end_grab (GdkEvent* event)
139 {
140         _ending = true;
141
142         _editor->stop_canvas_autoscroll ();
143
144         _item->ungrab (event ? event->button.time : 0);
145
146         _last_pointer_x = _current_pointer_x;
147         _last_pointer_y = _current_pointer_y;
148         finished (event, _had_movement);
149
150         _editor->hide_verbose_canvas_cursor();
151
152         _ending = false;
153
154         return _had_movement;
155 }
156
157 nframes64_t
158 Drag::adjusted_current_frame (GdkEvent* event) const
159 {
160         nframes64_t pos = 0;
161
162         if (_current_pointer_frame > _pointer_frame_offset) {
163                 pos = _current_pointer_frame - _pointer_frame_offset;
164         }
165
166         _editor->snap_to_with_modifier (pos, event);
167
168         return pos;
169 }
170
171 bool
172 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
173 {
174         _last_pointer_x = _current_pointer_x;
175         _last_pointer_y = _current_pointer_y;
176         _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
177
178         if (!from_autoscroll && !_move_threshold_passed) {
179
180                 bool const xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
181                 bool const yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
182
183                 _move_threshold_passed = (xp || yp);
184
185                 if (apply_move_threshold() && _move_threshold_passed) {
186
187                         _grab_frame = _current_pointer_frame;
188                         _grab_x = _current_pointer_x;
189                         _grab_y = _current_pointer_y;
190                         _last_pointer_frame = _grab_frame;
191                         _pointer_frame_offset = _grab_frame - _last_frame_position;
192
193                 }
194         }
195
196         bool old_had_movement = _had_movement;
197
198         /* a motion event has happened, so we've had movement... */
199         _had_movement = true;
200
201         /* ... unless we're using a move threshold and we've not yet passed it */
202         if (apply_move_threshold() && !_move_threshold_passed) {
203                 _had_movement = false;
204         }
205
206         if (active (_editor->mouse_mode)) {
207
208                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
209                         if (!from_autoscroll) {
210                                 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
211                         }
212
213                         motion (event, _had_movement != old_had_movement);
214                         return true;
215                 }
216         }
217
218         return false;
219 }
220
221
222 void
223 Drag::break_drag ()
224 {
225         _editor->stop_canvas_autoscroll ();
226         _editor->hide_verbose_canvas_cursor ();
227
228         if (_item) {
229                 _item->ungrab (0);
230
231                 /* put it back where it came from */
232
233                 double cxw, cyw;
234                 cxw = 0;
235                 cyw = 0;
236                 _item->i2w (cxw, cyw);
237                 _item->move (_original_x - cxw, _original_y - cyw);
238         }
239 }
240
241
242 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
243         : Drag (e, i),
244           _primary (p),
245           _views (v)
246 {
247         RegionView::RegionViewGoingAway.connect (mem_fun (*this, &RegionDrag::region_going_away));
248 }
249
250 void
251 RegionDrag::region_going_away (RegionView* v)
252 {
253         _views.remove (v);
254 }
255
256 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
257         : RegionDrag (e, i, p, v),
258           _dest_trackview (0),
259           _dest_layer (0),
260           _brushing (b)
261 {
262
263 }
264
265
266 void
267 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
268 {
269         Drag::start_grab (event);
270
271         _editor->show_verbose_time_cursor (_last_frame_position, 10);
272 }
273
274 RegionMotionDrag::TimeAxisViewSummary
275 RegionMotionDrag::get_time_axis_view_summary ()
276 {
277         int32_t children = 0;
278         TimeAxisViewSummary sum;
279
280         _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
281
282         /* get a bitmask representing the visible tracks */
283
284         for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
285                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
286                 TimeAxisView::Children children_list;
287
288                 /* zeroes are audio/MIDI tracks. ones are other types. */
289
290                 if (!rtv->hidden()) {
291
292                         if (!rtv->is_track()) {
293                                 /* not an audio nor MIDI track */
294                                 sum.tracks = sum.tracks |= (0x01 << rtv->order());
295                         }
296
297                         sum.height_list[rtv->order()] = (*i)->current_height();
298                         children = 1;
299
300                         if ((children_list = rtv->get_child_list()).size() > 0) {
301                                 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
302                                         sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
303                                         sum.height_list[rtv->order() + children] = (*j)->current_height();
304                                         children++;
305                                 }
306                         }
307                 }
308         }
309
310         return sum;
311 }
312
313 bool
314 RegionMotionDrag::compute_y_delta (
315         TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
316         int32_t last_pointer_layer, int32_t current_pointer_layer,
317         TimeAxisViewSummary const & tavs,
318         int32_t* pointer_order_span, int32_t* pointer_layer_span,
319         int32_t* canvas_pointer_order_span
320         )
321 {
322         if (_brushing) {
323                 *pointer_order_span = 0;
324                 *pointer_layer_span = 0;
325                 return true;
326         }
327
328         bool clamp_y_axis = false;
329
330         /* the change in track order between this callback and the last */
331         *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
332         /* the change in layer between this callback and the last;
333            only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
334         *pointer_layer_span = last_pointer_layer - current_pointer_layer;
335
336         if (*pointer_order_span != 0) {
337
338                 /* find the actual pointer span, in terms of the number of visible tracks;
339                    to do this, we reduce |pointer_order_span| by the number of hidden tracks
340                    over the span */
341
342                 *canvas_pointer_order_span = *pointer_order_span;
343                 if (last_pointer_view->order() >= current_pointer_view->order()) {
344                         for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
345                                 if (tavs.height_list[y] == 0) {
346                                         *canvas_pointer_order_span--;
347                                 }
348                         }
349                 } else {
350                         for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
351                                 if (tavs.height_list[y] == 0) {
352                                         *canvas_pointer_order_span++;
353                                 }
354                         }
355                 }
356
357                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
358
359                         RegionView* rv = (*i);
360
361                         if (rv->region()->locked()) {
362                                 continue;
363                         }
364
365                         double ix1, ix2, iy1, iy2;
366                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
367                         rv->get_canvas_frame()->i2w (ix1, iy1);
368                         iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
369
370                         /* get the new trackview for this particular region */
371                         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
372                         assert (tvp.first);
373                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
374
375                         /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
376                            as surely this is a per-region thing... */
377
378                         clamp_y_axis = y_movement_disallowed (
379                                 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
380                                 );
381
382                         if (clamp_y_axis) {
383                                 break;
384                         }
385                 }
386
387         } else if (_dest_trackview == current_pointer_view) {
388
389                 if (current_pointer_layer == last_pointer_layer) {
390                         /* No movement; clamp */
391                         clamp_y_axis = true;
392                 }
393         }
394
395         if (!clamp_y_axis) {
396                 _dest_trackview = current_pointer_view;
397                 _dest_layer = current_pointer_layer;
398         }
399
400         return clamp_y_axis;
401 }
402
403
404 double
405 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
406 {
407         *pending_region_position = 0;
408
409         /* compute the amount of pointer motion in frames, and where
410            the region would be if we moved it by that much.
411         */
412         if (_current_pointer_frame >= _pointer_frame_offset) {
413
414                 nframes64_t sync_frame;
415                 nframes64_t sync_offset;
416                 int32_t sync_dir;
417
418                 *pending_region_position = _current_pointer_frame - _pointer_frame_offset;
419
420                 sync_offset = _primary->region()->sync_offset (sync_dir);
421
422                 /* we don't handle a sync point that lies before zero.
423                  */
424                 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
425
426                         sync_frame = *pending_region_position + (sync_dir*sync_offset);
427
428                         _editor->snap_to_with_modifier (sync_frame, event);
429
430                         *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
431
432                 } else {
433                         *pending_region_position = _last_frame_position;
434                 }
435
436         }
437
438         if (*pending_region_position > max_frames - _primary->region()->length()) {
439                 *pending_region_position = _last_frame_position;
440         }
441
442         double x_delta = 0;
443
444         if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
445
446                 /* now compute the canvas unit distance we need to move the regionview
447                    to make it appear at the new location.
448                 */
449
450                 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
451
452                 if (*pending_region_position <= _last_frame_position) {
453
454                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
455
456                                 RegionView* rv = (*i);
457
458                                 // If any regionview is at zero, we need to know so we can stop further leftward motion.
459
460                                 double ix1, ix2, iy1, iy2;
461                                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
462                                 rv->get_canvas_frame()->i2w (ix1, iy1);
463
464                                 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
465                                         x_delta = 0;
466                                         *pending_region_position = _last_frame_position;
467                                         break;
468                                 }
469                         }
470
471                 }
472
473                 _last_frame_position = *pending_region_position;
474         }
475
476         return x_delta;
477 }
478
479 void
480 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
481 {
482         double y_delta = 0;
483
484         TimeAxisViewSummary tavs = get_time_axis_view_summary ();
485
486         vector<int32_t>::iterator j;
487
488         /* *pointer* variables reflect things about the pointer; as we may be moving
489            multiple regions, much detail must be computed per-region */
490
491         /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
492            current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
493            are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
494            is always 0 regardless of what the region's "real" layer is */
495         RouteTimeAxisView* current_pointer_view;
496         layer_t current_pointer_layer;
497         if (!check_possible (&current_pointer_view, &current_pointer_layer)) {
498                 return;
499         }
500
501         /* TimeAxisView that we were pointing at last time we entered this method */
502         TimeAxisView const * const last_pointer_view = _dest_trackview;
503         /* the order of the track that we were pointing at last time we entered this method */
504         int32_t const last_pointer_order = last_pointer_view->order ();
505         /* the layer that we were pointing at last time we entered this method */
506         layer_t const last_pointer_layer = _dest_layer;
507
508         int32_t pointer_order_span;
509         int32_t pointer_layer_span;
510         int32_t canvas_pointer_order_span;
511
512         bool const clamp_y_axis = compute_y_delta (
513                 last_pointer_view, current_pointer_view,
514                 last_pointer_layer, current_pointer_layer, tavs,
515                 &pointer_order_span, &pointer_layer_span,
516                 &canvas_pointer_order_span
517                 );
518
519         nframes64_t pending_region_position;
520         double const x_delta = compute_x_delta (event, &pending_region_position);
521
522         /*************************************************************
523             PREPARE TO MOVE
524         ************************************************************/
525
526         if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
527                 /* haven't reached next snap point, and we're not switching
528                    trackviews nor layers. nothing to do.
529                 */
530                 return;
531         }
532
533         /*************************************************************
534             MOTION
535         ************************************************************/
536
537         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
538
539         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
540
541                 RegionView* rv = (*i);
542
543                 if (rv->region()->locked()) {
544                         continue;
545                 }
546
547                 /* here we are calculating the y distance from the
548                    top of the first track view to the top of the region
549                    area of the track view that we're working on */
550
551                 /* this x value is just a dummy value so that we have something
552                    to pass to i2w () */
553
554                 double ix1 = 0;
555
556                 /* distance from the top of this track view to the region area
557                    of our track view is always 1 */
558
559                 double iy1 = 1;
560
561                 /* convert to world coordinates, ie distance from the top of
562                    the ruler section */
563
564                 rv->get_canvas_frame()->i2w (ix1, iy1);
565
566                 /* compensate for the ruler section and the vertical scrollbar position */
567                 iy1 += _editor->get_trackview_group_vertical_offset ();
568
569                 if (first_move) {
570
571                         // hide any dependent views
572
573                         rv->get_time_axis_view().hide_dependent_views (*rv);
574
575                         /*
576                            reparent to a non scrolling group so that we can keep the
577                            region selection above all time axis views.
578                            reparenting means we have to move the rv as the two
579                            parent groups have different coordinates.
580                         */
581
582                         rv->get_canvas_group()->property_y() = iy1 - 1;
583                         rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
584
585                         rv->fake_set_opaque (true);
586                 }
587
588                 /* current view for this particular region */
589                 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
590                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
591
592                 if (pointer_order_span != 0 && !clamp_y_axis) {
593
594                         /* INTER-TRACK MOVEMENT */
595
596                         /* move through the height list to the track that the region is currently on */
597                         vector<int32_t>::iterator j = tavs.height_list.begin ();
598                         int32_t x = 0;
599                         while (j != tavs.height_list.end () && x != rtv->order ()) {
600                                 ++x;
601                                 ++j;
602                         }
603
604                         y_delta = 0;
605                         int32_t temp_pointer_order_span = canvas_pointer_order_span;
606
607                         if (j != tavs.height_list.end ()) {
608
609                                 /* Account for layers in the original and
610                                    destination tracks.  If we're moving around in layers we assume
611                                    that only one track is involved, so it's ok to use *pointer*
612                                    variables here. */
613
614                                 StreamView* lv = last_pointer_view->view ();
615                                 assert (lv);
616
617                                 /* move to the top of the last trackview */
618                                 if (lv->layer_display () == Stacked) {
619                                         y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
620                                 }
621
622                                 StreamView* cv = current_pointer_view->view ();
623                                 assert (cv);
624
625                                 /* move to the right layer on the current trackview */
626                                 if (cv->layer_display () == Stacked) {
627                                         y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
628                                 }
629
630                                 /* And for being on a non-topmost layer on the new
631                                    track */
632
633                                 while (temp_pointer_order_span > 0) {
634                                         /* we're moving up canvas-wise,
635                                            so we need to find the next track height
636                                         */
637                                         if (j != tavs.height_list.begin()) {
638                                                 j--;
639                                         }
640
641                                         if (x != last_pointer_order) {
642                                                 if ((*j) == 0) {
643                                                         ++temp_pointer_order_span;
644                                                 }
645                                         }
646
647                                         y_delta -= (*j);
648                                         temp_pointer_order_span--;
649                                 }
650
651                                 while (temp_pointer_order_span < 0) {
652
653                                         y_delta += (*j);
654
655                                         if (x != last_pointer_order) {
656                                                 if ((*j) == 0) {
657                                                         --temp_pointer_order_span;
658                                                 }
659                                         }
660
661                                         if (j != tavs.height_list.end()) {
662                                                 j++;
663                                         }
664
665                                         temp_pointer_order_span++;
666                                 }
667
668
669                                 /* find out where we'll be when we move and set height accordingly */
670
671                                 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
672                                 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
673                                 rv->set_height (temp_rtv->view()->child_height());
674
675                                 /* if you un-comment the following, the region colours will follow
676                                    the track colours whilst dragging; personally
677                                    i think this can confuse things, but never mind.
678                                 */
679
680                                 //const GdkColor& col (temp_rtv->view->get_region_color());
681                                 //rv->set_color (const_cast<GdkColor&>(col));
682                         }
683                 }
684
685                 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
686
687                         /* INTER-LAYER MOVEMENT in the same track */
688                         y_delta = rtv->view()->child_height () * pointer_layer_span;
689                 }
690
691
692                 if (_brushing) {
693                         _editor->mouse_brush_insert_region (rv, pending_region_position);
694                 } else {
695                         rv->move (x_delta, y_delta);
696                 }
697
698         } /* foreach region */
699
700         if (first_move) {
701                 _editor->cursor_group->raise_to_top();
702         }
703
704         if (x_delta != 0 && !_brushing) {
705                 _editor->show_verbose_time_cursor (_last_frame_position, 10);
706         }
707 }
708
709 void
710 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
711 {
712         if (_copy && first_move) {
713                 copy_regions (event);
714         }
715
716         RegionMotionDrag::motion (event, first_move);
717 }
718
719 void
720 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
721 {
722         vector<RegionView*> copies;
723         boost::shared_ptr<Diskstream> ds;
724         boost::shared_ptr<Playlist> from_playlist;
725         RegionSelection new_views;
726         typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
727         PlaylistSet modified_playlists;
728         PlaylistSet frozen_playlists;
729         list <sigc::connection> modified_playlist_connections;
730         pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
731         nframes64_t drag_delta;
732         bool changed_tracks, changed_position;
733         map<RegionView*, pair<RouteTimeAxisView*, int> > final;
734         RouteTimeAxisView* source_tv;
735
736         if (!movement_occurred) {
737                 /* just a click */
738                 return;
739         }
740
741         if (Config->get_edit_mode() == Splice && !_editor->pre_drag_region_selection.empty()) {
742                 _editor->selection->set (_editor->pre_drag_region_selection);
743                 _editor->pre_drag_region_selection.clear ();
744         }
745
746         if (_brushing) {
747                 /* all changes were made during motion event handlers */
748
749                 if (_copy) {
750                         for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
751                                 copies.push_back (*i);
752                         }
753                 }
754
755                 goto out;
756         }
757
758         /* reverse this here so that we have the correct logic to finalize
759            the drag.
760         */
761
762         if (Config->get_edit_mode() == Lock && !_copy) {
763                 _x_constrained = !_x_constrained;
764         }
765
766         if (_copy) {
767                 if (_x_constrained) {
768                         _editor->begin_reversible_command (_("fixed time region copy"));
769                 } else {
770                         _editor->begin_reversible_command (_("region copy"));
771                 }
772         } else {
773                 if (_x_constrained) {
774                         _editor->begin_reversible_command (_("fixed time region drag"));
775                 } else {
776                         _editor->begin_reversible_command (_("region drag"));
777                 }
778         }
779
780         changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
781         changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
782
783         drag_delta = _primary->region()->position() - _last_frame_position;
784
785         _editor->update_canvas_now ();
786
787         /* make a list of where each region ended up */
788         final = find_time_axis_views_and_layers ();
789
790         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
791
792                 RegionView* rv = (*i);
793                 RouteTimeAxisView* dest_rtv = final[*i].first;
794                 layer_t dest_layer = final[*i].second;
795
796                 nframes64_t where;
797
798                 if (rv->region()->locked()) {
799                         ++i;
800                         continue;
801                 }
802
803                 if (changed_position && !_x_constrained) {
804                         where = rv->region()->position() - drag_delta;
805                 } else {
806                         where = rv->region()->position();
807                 }
808
809                 boost::shared_ptr<Region> new_region;
810
811                 if (_copy) {
812                         /* we already made a copy */
813                         new_region = rv->region();
814
815                         /* undo the previous hide_dependent_views so that xfades don't
816                            disappear on copying regions
817                         */
818
819                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
820
821                 } else if (changed_tracks && dest_rtv->playlist()) {
822                         new_region = RegionFactory::create (rv->region());
823                 }
824
825                 if (changed_tracks || _copy) {
826
827                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
828
829                         if (!to_playlist) {
830                                 ++i;
831                                 continue;
832                         }
833
834                         _editor->latest_regionviews.clear ();
835
836                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*_editor, &Editor::collect_new_region_view));
837
838                         insert_result = modified_playlists.insert (to_playlist);
839
840                         if (insert_result.second) {
841                                 _editor->session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
842                         }
843
844                         to_playlist->add_region (new_region, where);
845                         if (dest_rtv->view()->layer_display() == Stacked) {
846                                 new_region->set_layer (dest_layer);
847                                 new_region->set_pending_explicit_relayer (true);
848                         }
849
850                         c.disconnect ();
851
852                         if (!_editor->latest_regionviews.empty()) {
853                                 // XXX why just the first one ? we only expect one
854                                 // commented out in nick_m's canvas reworking. is that intended?
855                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
856                                 new_views.push_back (_editor->latest_regionviews.front());
857                         }
858
859                 } else {
860                         /*
861                            motion on the same track. plonk the previously reparented region
862                            back to its original canvas group (its streamview).
863                            No need to do anything for copies as they are fake regions which will be deleted.
864                         */
865
866                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
867                         rv->get_canvas_group()->property_y() = 0;
868
869                         /* just change the model */
870
871                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
872
873                         if (dest_rtv->view()->layer_display() == Stacked) {
874                                 rv->region()->set_layer (dest_layer);
875                                 rv->region()->set_pending_explicit_relayer (true);
876                         }
877
878                         insert_result = modified_playlists.insert (playlist);
879
880                         if (insert_result.second) {
881                                 _editor->session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
882                         }
883                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
884                         frozen_insert_result = frozen_playlists.insert(playlist);
885
886                         if (frozen_insert_result.second) {
887                                 playlist->freeze();
888                         }
889
890                         rv->region()->set_position (where, (void*) this);
891                 }
892
893                 if (changed_tracks && !_copy) {
894
895                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
896                            because we may have copied the region and it has not been attached to a playlist.
897                         */
898
899                         source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
900                         ds = source_tv->get_diskstream();
901                         from_playlist = ds->playlist();
902
903                         assert (source_tv);
904                         assert (ds);
905                         assert (from_playlist);
906
907                         /* moved to a different audio track, without copying */
908
909                         /* the region that used to be in the old playlist is not
910                            moved to the new one - we use a copy of it. as a result,
911                            any existing editor for the region should no longer be
912                            visible.
913                         */
914
915                         rv->hide_region_editor();
916                         rv->fake_set_opaque (false);
917
918                         /* remove the region from the old playlist */
919
920                         insert_result = modified_playlists.insert (from_playlist);
921
922                         if (insert_result.second) {
923                                 _editor->session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
924                         }
925
926                         from_playlist->remove_region (rv->region());
927
928                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
929                            was selected in all of them, then removing it from a playlist will have removed all
930                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
931                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
932                            corresponding regionview, and the selection is now empty).
933
934                            this could have invalidated any and all iterators into the region selection.
935
936                            the heuristic we use here is: if the region selection is empty, break out of the loop
937                            here. if the region selection is not empty, then restart the loop because we know that
938                            we must have removed at least the region(view) we've just been working on as well as any
939                            that we processed on previous iterations.
940
941                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
942                            we can just iterate.
943                         */
944
945                         if (_views.empty()) {
946                                 break;
947                         } else {
948                                 i = _views.begin();
949                         }
950
951                 } else {
952                         ++i;
953                 }
954
955                 if (_copy) {
956                         copies.push_back (rv);
957                 }
958         }
959
960         _editor->selection->add (new_views);
961
962         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
963                 (*p)->thaw();
964         }
965
966   out:
967         for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
968                 _editor->session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
969         }
970
971         _editor->commit_reversible_command ();
972
973         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
974                 delete *x;
975         }
976 }
977
978
979 bool
980 RegionMoveDrag::x_move_allowed () const
981 {
982         if (Config->get_edit_mode() == Lock) {
983                 if (_copy) {
984                         return !_x_constrained;
985                 } else {
986                         /* in locked edit mode, reverse the usual meaning of _x_constrained */
987                         return _x_constrained;
988                 }
989         }
990
991         return !_x_constrained;
992 }
993
994 bool
995 RegionInsertDrag::x_move_allowed () const
996 {
997         if (Config->get_edit_mode() == Lock) {
998                 return _x_constrained;
999         }
1000
1001         return !_x_constrained;
1002 }
1003
1004 void
1005 RegionMotionDrag::copy_regions (GdkEvent* event)
1006 {
1007         /* duplicate the regionview(s) and region(s) */
1008
1009         list<RegionView*> new_regionviews;
1010
1011         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1012
1013                 RegionView* rv = (*i);
1014                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1015                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1016
1017                 const boost::shared_ptr<const Region> original = rv->region();
1018                 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1019
1020                 RegionView* nrv;
1021                 if (arv) {
1022                         boost::shared_ptr<AudioRegion> audioregion_copy
1023                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1024                         nrv = new AudioRegionView (*arv, audioregion_copy);
1025                 } else if (mrv) {
1026                         boost::shared_ptr<MidiRegion> midiregion_copy
1027                                 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1028                         nrv = new MidiRegionView (*mrv, midiregion_copy);
1029                 } else {
1030                         continue;
1031                 }
1032
1033                 nrv->get_canvas_group()->show ();
1034                 new_regionviews.push_back (nrv);
1035         }
1036
1037         if (new_regionviews.empty()) {
1038                 return;
1039         }
1040
1041         /* reflect the fact that we are dragging the copies */
1042
1043         _primary = new_regionviews.front();
1044         _views = new_regionviews;
1045
1046         swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1047
1048         /*
1049            sync the canvas to what we think is its current state
1050            without it, the canvas seems to
1051            "forget" to update properly after the upcoming reparent()
1052            ..only if the mouse is in rapid motion at the time of the grab.
1053            something to do with regionview creation raking so long?
1054         */
1055         _editor->update_canvas_now();
1056 }
1057
1058 bool
1059 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1060 {
1061         /* Which trackview is this ? */
1062
1063         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1064         (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1065         (*layer) = tvp.second;
1066
1067         if (*tv && (*tv)->layer_display() == Overlaid) {
1068                 *layer = 0;
1069         }
1070
1071         /* The region motion is only processed if the pointer is over
1072            an audio track.
1073         */
1074
1075         if (!(*tv) || !(*tv)->is_track()) {
1076                 /* To make sure we hide the verbose canvas cursor when the mouse is
1077                    not held over and audiotrack.
1078                 */
1079                 _editor->hide_verbose_canvas_cursor ();
1080                 return false;
1081         }
1082
1083         return true;
1084 }
1085
1086 /** @param new_order New track order.
1087  *  @param old_order Old track order.
1088  *  @param visible_y_low Lowest visible order.
1089  *  @return true if y movement should not happen, otherwise false.
1090  */
1091 bool
1092 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1093 {
1094         if (new_order != old_order) {
1095
1096                 /* this isn't the pointer track */
1097
1098                 if (y_span > 0) {
1099
1100                         /* moving up the canvas */
1101                         if ( (new_order - y_span) >= tavs.visible_y_low) {
1102
1103                                 int32_t n = 0;
1104
1105                                 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1106                                 int32_t visible_tracks = 0;
1107                                 while (visible_tracks < y_span ) {
1108                                         visible_tracks++;
1109                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1110                                                 /* passing through a hidden track */
1111                                                 n--;
1112                                         }
1113                                 }
1114
1115                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1116                                         /* moving to a non-track; disallow */
1117                                         return true;
1118                                 }
1119
1120
1121                         } else {
1122                                 /* moving beyond the lowest visible track; disallow */
1123                                 return true;
1124                         }
1125
1126                 } else if (y_span < 0) {
1127
1128                         /* moving down the canvas */
1129                         if ((new_order - y_span) <= tavs.visible_y_high) {
1130
1131                                 int32_t visible_tracks = 0;
1132                                 int32_t n = 0;
1133                                 while (visible_tracks > y_span ) {
1134                                         visible_tracks--;
1135
1136                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1137                                                 /* passing through a hidden track */
1138                                                 n++;
1139                                         }
1140                                 }
1141
1142                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1143                                         /* moving to a non-track; disallow */
1144                                         return true;
1145                                 }
1146
1147
1148                         } else {
1149
1150                                 /* moving beyond the highest visible track; disallow */
1151                                 return true;
1152                         }
1153                 }
1154
1155         } else {
1156
1157                 /* this is the pointer's track */
1158
1159                 if ((new_order - y_span) > tavs.visible_y_high) {
1160                         /* we will overflow */
1161                         return true;
1162                 } else if ((new_order - y_span) < tavs.visible_y_low) {
1163                         /* we will overflow */
1164                         return true;
1165                 }
1166         }
1167
1168         return false;
1169 }
1170
1171
1172 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1173         : RegionMotionDrag (e, i, p, v, b),
1174           _copy (c)
1175 {
1176         TimeAxisView* const tv = &_primary->get_time_axis_view ();
1177
1178         _dest_trackview = tv;
1179         if (tv->layer_display() == Overlaid) {
1180                 _dest_layer = 0;
1181         } else {
1182                 _dest_layer = _primary->region()->layer ();
1183         }
1184
1185         double speed = 1;
1186         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1187         if (rtv && rtv->is_track()) {
1188                 speed = rtv->get_diskstream()->speed ();
1189         }
1190
1191         _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1192 }
1193
1194 void
1195 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1196 {
1197         RegionMotionDrag::start_grab (event, c);
1198
1199         _pointer_frame_offset = _grab_frame - _last_frame_position;
1200 }
1201
1202 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1203         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1204 {
1205         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1206                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1207
1208         _primary = v->view()->create_region_view (r, false, false);
1209
1210         _primary->get_canvas_group()->show ();
1211         _primary->set_position (pos, 0);
1212         _views.push_back (_primary);
1213
1214         _last_frame_position = pos;
1215
1216         _item = _primary->get_canvas_group ();
1217         _dest_trackview = v;
1218         _dest_layer = _primary->region()->layer ();
1219 }
1220
1221 map<RegionView*, pair<RouteTimeAxisView*, int> >
1222 RegionMotionDrag::find_time_axis_views_and_layers ()
1223 {
1224         map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1225
1226         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1227
1228                 double ix1, ix2, iy1, iy2;
1229                 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1230                 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1231                 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1232
1233                 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1234                 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1235         }
1236
1237         return tav;
1238 }
1239
1240
1241 void
1242 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1243 {
1244         _editor->update_canvas_now ();
1245
1246         map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1247
1248         RouteTimeAxisView* dest_rtv = final[_primary].first;
1249
1250         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1251         _primary->get_canvas_group()->property_y() = 0;
1252
1253         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1254
1255         _editor->begin_reversible_command (_("insert region"));
1256         XMLNode& before = playlist->get_state ();
1257         playlist->add_region (_primary->region (), _last_frame_position);
1258         _editor->session->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1259         _editor->commit_reversible_command ();
1260
1261         delete _primary;
1262         _primary = 0;
1263         _views.clear ();
1264 }
1265
1266 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1267         : RegionMoveDrag (e, i, p, v, false, false)
1268 {
1269
1270 }
1271
1272 struct RegionSelectionByPosition {
1273     bool operator() (RegionView*a, RegionView* b) {
1274             return a->region()->position () < b->region()->position();
1275     }
1276 };
1277
1278 void
1279 RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
1280 {
1281         RouteTimeAxisView* tv;
1282         layer_t layer;
1283
1284         if (!check_possible (&tv, &layer)) {
1285                 return;
1286         }
1287
1288         int dir;
1289
1290         if (_current_pointer_x - _grab_x > 0) {
1291                 dir = 1;
1292         } else {
1293                 dir = -1;
1294         }
1295
1296         RegionSelection copy (_editor->selection->regions);
1297
1298         RegionSelectionByPosition cmp;
1299         copy.sort (cmp);
1300
1301         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1302
1303                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1304
1305                 if (!atv) {
1306                         continue;
1307                 }
1308
1309                 boost::shared_ptr<Playlist> playlist;
1310
1311                 if ((playlist = atv->playlist()) == 0) {
1312                         continue;
1313                 }
1314
1315                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1316                         continue;
1317                 }
1318
1319                 if (dir > 0) {
1320                         if (_current_pointer_frame < (*i)->region()->last_frame() + 1) {
1321                                 continue;
1322                         }
1323                 } else {
1324                         if (_current_pointer_frame > (*i)->region()->first_frame()) {
1325                                 continue;
1326                         }
1327                 }
1328
1329
1330                 playlist->shuffle ((*i)->region(), dir);
1331
1332                 _grab_x = _current_pointer_x;
1333         }
1334 }
1335
1336 void
1337 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1338 {
1339
1340 }
1341
1342
1343 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1344         : Drag (e, i),
1345           _view (v)
1346 {
1347
1348 }
1349
1350 void
1351 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1352 {
1353         _dest_trackview = _view;
1354
1355         Drag::start_grab (event);
1356 }
1357
1358
1359 void
1360 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1361 {
1362         if (first_move) {
1363                 // TODO: create region-create-drag region view here
1364         }
1365
1366         // TODO: resize region-create-drag region view here
1367 }
1368
1369 void
1370 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1371 {
1372         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1373
1374         if (!mtv) {
1375                 return;
1376         }
1377
1378         if (!movement_occurred) {
1379                 mtv->add_region (_grab_frame);
1380         } else {
1381                 motion (event, false);
1382                 // TODO: create region-create-drag region here
1383         }
1384 }
1385
1386 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1387         : Drag (e, i)
1388         , region (0)
1389 {
1390
1391 }
1392
1393 void
1394 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1395 {
1396         Gdk::Cursor     cursor;
1397         ArdourCanvas::CanvasNote*     cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1398
1399         Drag::start_grab (event);
1400
1401         region = &cnote->region_view();
1402
1403         double region_start = region->get_position_pixels();
1404         double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1405
1406         if (_grab_x <= middle_point) {
1407                 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1408                 at_front = true;
1409         } else {
1410                 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1411                 at_front = false;
1412         }
1413
1414         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1415
1416         if (event->motion.state & Keyboard::PrimaryModifier) {
1417                 relative = false;
1418         } else {
1419                 relative = true;
1420         }
1421
1422         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1423
1424         if (ms.size() > 1) {
1425                 /* has to be relative, may make no sense otherwise */
1426                 relative = true;
1427         }
1428
1429         region->note_selected (cnote, true);
1430
1431         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1432                 MidiRegionSelection::iterator next;
1433                 next = r;
1434                 ++next;
1435                 (*r)->begin_resizing (at_front);
1436                 r = next;
1437         }
1438 }
1439
1440 void
1441 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1442 {
1443         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1444         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1445                 (*r)->update_resizing (at_front, _current_pointer_x - _grab_x, relative);
1446         }
1447 }
1448
1449 void
1450 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1451 {
1452         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1453         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1454                 (*r)->commit_resizing (at_front, _current_pointer_x - _grab_x, relative);
1455         }
1456 }
1457
1458 void
1459 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1460 {
1461
1462 }
1463
1464 void
1465 RegionGainDrag::finished (GdkEvent *, bool)
1466 {
1467
1468 }
1469
1470 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1471         : RegionDrag (e, i, p, v)
1472 {
1473
1474 }
1475
1476 void
1477 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1478 {
1479         double speed = 1.0;
1480         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1481         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1482
1483         if (tv && tv->is_track()) {
1484                 speed = tv->get_diskstream()->speed();
1485         }
1486
1487         nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1488         nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1489         nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1490
1491         Drag::start_grab (event, _editor->trimmer_cursor);
1492
1493         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1494                 _operation = ContentsTrim;
1495         } else {
1496                 /* These will get overridden for a point trim.*/
1497                 if (_current_pointer_frame < (region_start + region_length/2)) {
1498                         /* closer to start */
1499                         _operation = StartTrim;
1500                 } else if (_current_pointer_frame > (region_end - region_length/2)) {
1501                         /* closer to end */
1502                         _operation = EndTrim;
1503                 }
1504         }
1505
1506         switch (_operation) {
1507         case StartTrim:
1508                 _editor->show_verbose_time_cursor (region_start, 10);
1509                 break;
1510         case EndTrim:
1511                 _editor->show_verbose_time_cursor (region_end, 10);
1512                 break;
1513         case ContentsTrim:
1514                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1515                 break;
1516         }
1517 }
1518
1519 void
1520 TrimDrag::motion (GdkEvent* event, bool first_move)
1521 {
1522         RegionView* rv = _primary;
1523         nframes64_t frame_delta = 0;
1524
1525         bool left_direction;
1526         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1527
1528         /* snap modifier works differently here..
1529            its' current state has to be passed to the
1530            various trim functions in order to work properly
1531         */
1532
1533         double speed = 1.0;
1534         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1535         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1536         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1537
1538         if (tv && tv->is_track()) {
1539                 speed = tv->get_diskstream()->speed();
1540         }
1541
1542         if (_last_pointer_frame > _current_pointer_frame) {
1543                 left_direction = true;
1544         } else {
1545                 left_direction = false;
1546         }
1547
1548         _editor->snap_to_with_modifier (_current_pointer_frame, event);
1549
1550         if (first_move) {
1551
1552                 string trim_type;
1553
1554                 switch (_operation) {
1555                 case StartTrim:
1556                         trim_type = "Region start trim";
1557                         break;
1558                 case EndTrim:
1559                         trim_type = "Region end trim";
1560                         break;
1561                 case ContentsTrim:
1562                         trim_type = "Region content trim";
1563                         break;
1564                 }
1565
1566                 _editor->begin_reversible_command (trim_type);
1567
1568                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1569                         (*i)->fake_set_opaque(false);
1570                         (*i)->region()->freeze ();
1571
1572                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1573
1574                         if (arv){
1575                                 arv->temporarily_hide_envelope ();
1576                         }
1577
1578                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1579                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1580
1581                         if (insert_result.second) {
1582                                 _editor->session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1583                                 pl->freeze();
1584                         }
1585                 }
1586         }
1587
1588         if (_current_pointer_frame == _last_pointer_frame) {
1589                 return;
1590         }
1591
1592         if (left_direction) {
1593                 frame_delta = (_last_pointer_frame - _current_pointer_frame);
1594         } else {
1595                 frame_delta = (_current_pointer_frame - _last_pointer_frame);
1596         }
1597
1598         bool non_overlap_trim = false;
1599
1600         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1601                 non_overlap_trim = true;
1602         }
1603
1604         switch (_operation) {
1605         case StartTrim:
1606                 if ((left_direction == false) && (_current_pointer_frame <= rv->region()->first_frame()/speed)) {
1607                         break;
1608                 } else {
1609
1610                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1611                                 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1612                         }
1613                         break;
1614                 }
1615
1616         case EndTrim:
1617                 if ((left_direction == true) && (_current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
1618                         break;
1619                 } else {
1620
1621                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1622                                 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1623                         }
1624                         break;
1625                 }
1626
1627         case ContentsTrim:
1628                 {
1629                         bool swap_direction = false;
1630
1631                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1632                                 swap_direction = true;
1633                         }
1634
1635                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i)
1636                         {
1637                                 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1638                         }
1639                 }
1640                 break;
1641         }
1642
1643         switch (_operation) {
1644         case StartTrim:
1645                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1646                 break;
1647         case EndTrim:
1648                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1649                 break;
1650         case ContentsTrim:
1651                 _editor->show_verbose_time_cursor(_current_pointer_frame, 10);
1652                 break;
1653         }
1654
1655         _last_pointer_frame = _current_pointer_frame;
1656 }
1657
1658
1659 void
1660 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1661 {
1662         if (movement_occurred) {
1663                 motion (event, false);
1664
1665                 if (!_editor->selection->selected (_primary)) {
1666                         _editor->thaw_region_after_trim (*_primary);
1667                 } else {
1668
1669                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1670                                 _editor->thaw_region_after_trim (**i);
1671                                 (*i)->fake_set_opaque (true);
1672                         }
1673                 }
1674
1675                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1676                         (*p)->thaw ();
1677                         _editor->session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1678                 }
1679
1680                 _editor->motion_frozen_playlists.clear ();
1681
1682                 _editor->commit_reversible_command();
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 }
1965
1966 void
1967 FadeInDrag::motion (GdkEvent* event, bool)
1968 {
1969         nframes64_t fade_length;
1970
1971         nframes64_t const pos = adjusted_current_frame (event);
1972
1973         boost::shared_ptr<Region> region = _primary->region ();
1974
1975         if (pos < (region->position() + 64)) {
1976                 fade_length = 64; // this should be a minimum defined somewhere
1977         } else if (pos > region->last_frame()) {
1978                 fade_length = region->length();
1979         } else {
1980                 fade_length = pos - region->position();
1981         }
1982
1983         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1984
1985                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1986
1987                 if (!tmp) {
1988                         continue;
1989                 }
1990
1991                 tmp->reset_fade_in_shape_width (fade_length);
1992         }
1993
1994         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
1995 }
1996
1997 void
1998 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
1999 {
2000         if (!movement_occurred) {
2001                 return;
2002         }
2003
2004         nframes64_t fade_length;
2005
2006         nframes64_t const pos = adjusted_current_frame (event);
2007
2008         boost::shared_ptr<Region> region = _primary->region ();
2009
2010         if (pos < (region->position() + 64)) {
2011                 fade_length = 64; // this should be a minimum defined somewhere
2012         } else if (pos > region->last_frame()) {
2013                 fade_length = region->length();
2014         } else {
2015                 fade_length = pos - region->position();
2016         }
2017
2018         _editor->begin_reversible_command (_("change fade in length"));
2019
2020         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2021
2022                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2023
2024                 if (!tmp) {
2025                         continue;
2026                 }
2027
2028                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2029                 XMLNode &before = alist->get_state();
2030
2031                 tmp->audio_region()->set_fade_in_length (fade_length);
2032                 tmp->audio_region()->set_fade_in_active (true);
2033
2034                 XMLNode &after = alist->get_state();
2035                 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2036         }
2037
2038         _editor->commit_reversible_command ();
2039 }
2040
2041 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2042         : RegionDrag (e, i, p, v)
2043 {
2044
2045 }
2046
2047 void
2048 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2049 {
2050         Drag::start_grab (event, cursor);
2051
2052         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2053         boost::shared_ptr<AudioRegion> r = a->audio_region ();
2054
2055         _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2056 }
2057
2058 void
2059 FadeOutDrag::motion (GdkEvent* event, bool)
2060 {
2061         nframes64_t fade_length;
2062
2063         nframes64_t const pos = adjusted_current_frame (event);
2064
2065         boost::shared_ptr<Region> region = _primary->region ();
2066
2067         if (pos > (region->last_frame() - 64)) {
2068                 fade_length = 64; // this should really be a minimum fade defined somewhere
2069         }
2070         else if (pos < region->position()) {
2071                 fade_length = region->length();
2072         }
2073         else {
2074                 fade_length = region->last_frame() - pos;
2075         }
2076
2077         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2078
2079                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2080
2081                 if (!tmp) {
2082                         continue;
2083                 }
2084
2085                 tmp->reset_fade_out_shape_width (fade_length);
2086         }
2087
2088         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2089 }
2090
2091 void
2092 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2093 {
2094         if (!movement_occurred) {
2095                 return;
2096         }
2097
2098         nframes64_t fade_length;
2099
2100         nframes64_t const pos = adjusted_current_frame (event);
2101
2102         boost::shared_ptr<Region> region = _primary->region ();
2103
2104         if (pos > (region->last_frame() - 64)) {
2105                 fade_length = 64; // this should really be a minimum fade defined somewhere
2106         }
2107         else if (pos < region->position()) {
2108                 fade_length = region->length();
2109         }
2110         else {
2111                 fade_length = region->last_frame() - pos;
2112         }
2113
2114         _editor->begin_reversible_command (_("change fade out length"));
2115
2116         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2117
2118                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2119
2120                 if (!tmp) {
2121                         continue;
2122                 }
2123
2124                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2125                 XMLNode &before = alist->get_state();
2126
2127                 tmp->audio_region()->set_fade_out_length (fade_length);
2128                 tmp->audio_region()->set_fade_out_active (true);
2129
2130                 XMLNode &after = alist->get_state();
2131                 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2132         }
2133
2134         _editor->commit_reversible_command ();
2135 }
2136
2137 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2138         : Drag (e, i)
2139 {
2140         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2141         assert (_marker);
2142
2143         _points.push_back (Gnome::Art::Point (0, 0));
2144         _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2145
2146         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2147         _line->property_width_pixels() = 1;
2148         _line->property_points () = _points;
2149         _line->hide ();
2150
2151         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2152 }
2153
2154 MarkerDrag::~MarkerDrag ()
2155 {
2156         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2157                 delete *i;
2158         }
2159 }
2160
2161 void
2162 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2163 {
2164         Drag::start_grab (event, cursor);
2165
2166         bool is_start;
2167
2168         Location *location = _editor->find_location_from_marker (_marker, is_start);
2169         _editor->_dragging_edit_point = true;
2170
2171         _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end());
2172
2173         update_item (location);
2174
2175         // _drag_line->show();
2176         // _line->raise_to_top();
2177
2178         if (is_start) {
2179                 _editor->show_verbose_time_cursor (location->start(), 10);
2180         } else {
2181                 _editor->show_verbose_time_cursor (location->end(), 10);
2182         }
2183
2184         Selection::Operation op = Keyboard::selection_type (event->button.state);
2185
2186         switch (op) {
2187         case Selection::Toggle:
2188                 _editor->selection->toggle (_marker);
2189                 break;
2190         case Selection::Set:
2191                 if (!_editor->selection->selected (_marker)) {
2192                         _editor->selection->set (_marker);
2193                 }
2194                 break;
2195         case Selection::Extend:
2196         {
2197                 Locations::LocationList ll;
2198                 list<Marker*> to_add;
2199                 nframes64_t s, e;
2200                 _editor->selection->markers.range (s, e);
2201                 s = min (_marker->position(), s);
2202                 e = max (_marker->position(), e);
2203                 s = min (s, e);
2204                 e = max (s, e);
2205                 if (e < max_frames) {
2206                         ++e;
2207                 }
2208                 _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2209                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2210                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2211                         if (lm) {
2212                                 if (lm->start) {
2213                                         to_add.push_back (lm->start);
2214                                 }
2215                                 if (lm->end) {
2216                                         to_add.push_back (lm->end);
2217                                 }
2218                         }
2219                 }
2220                 if (!to_add.empty()) {
2221                         _editor->selection->add (to_add);
2222                 }
2223                 break;
2224         }
2225         case Selection::Add:
2226                 _editor->selection->add (_marker);
2227                 break;
2228         }
2229
2230         /* set up copies for us to manipulate during the drag */
2231
2232         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2233                 Location *l = _editor->find_location_from_marker (*i, is_start);
2234                 _copied_locations.push_back (new Location (*l));
2235         }
2236 }
2237
2238 void
2239 MarkerDrag::motion (GdkEvent* event, bool)
2240 {
2241         nframes64_t f_delta = 0;
2242         bool is_start;
2243         bool move_both = false;
2244         Marker* marker;
2245         Location  *real_location;
2246         Location *copy_location = 0;
2247
2248         nframes64_t const newframe = adjusted_current_frame (event);
2249
2250         nframes64_t next = newframe;
2251
2252         if (_current_pointer_frame == _last_pointer_frame) {
2253                 return;
2254         }
2255
2256         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2257                 move_both = true;
2258         }
2259
2260         MarkerSelection::iterator i;
2261         list<Location*>::iterator x;
2262
2263         /* find the marker we're dragging, and compute the delta */
2264
2265         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2266              x != _copied_locations.end() && i != _editor->selection->markers.end();
2267              ++i, ++x) {
2268
2269                 copy_location = *x;
2270                 marker = *i;
2271
2272                 if (marker == _marker) {
2273
2274                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2275                                 /* que pasa ?? */
2276                                 return;
2277                         }
2278
2279                         if (real_location->is_mark()) {
2280                                 f_delta = newframe - copy_location->start();
2281                         } else {
2282
2283
2284                                 switch (marker->type()) {
2285                                 case Marker::Start:
2286                                 case Marker::LoopStart:
2287                                 case Marker::PunchIn:
2288                                         f_delta = newframe - copy_location->start();
2289                                         break;
2290
2291                                 case Marker::End:
2292                                 case Marker::LoopEnd:
2293                                 case Marker::PunchOut:
2294                                         f_delta = newframe - copy_location->end();
2295                                         break;
2296                                 default:
2297                                         /* what kind of marker is this ? */
2298                                         return;
2299                                 }
2300                         }
2301                         break;
2302                 }
2303         }
2304
2305         if (i == _editor->selection->markers.end()) {
2306                 /* hmm, impossible - we didn't find the dragged marker */
2307                 return;
2308         }
2309
2310         /* now move them all */
2311
2312         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2313              x != _copied_locations.end() && i != _editor->selection->markers.end();
2314              ++i, ++x) {
2315
2316                 copy_location = *x;
2317                 marker = *i;
2318
2319                 /* call this to find out if its the start or end */
2320
2321                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2322                         continue;
2323                 }
2324
2325                 if (real_location->locked()) {
2326                         continue;
2327                 }
2328
2329                 if (copy_location->is_mark()) {
2330
2331                         /* just move it */
2332
2333                         copy_location->set_start (copy_location->start() + f_delta);
2334
2335                 } else {
2336
2337                         nframes64_t new_start = copy_location->start() + f_delta;
2338                         nframes64_t new_end = copy_location->end() + f_delta;
2339
2340                         if (is_start) { // start-of-range marker
2341
2342                                 if (move_both) {
2343                                         copy_location->set_start (new_start);
2344                                         copy_location->set_end (new_end);
2345                                 } else  if (new_start < copy_location->end()) {
2346                                         copy_location->set_start (new_start);
2347                                 } else {
2348                                         _editor->snap_to (next, 1, true);
2349                                         copy_location->set_end (next);
2350                                         copy_location->set_start (newframe);
2351                                 }
2352
2353                         } else { // end marker
2354
2355                                 if (move_both) {
2356                                         copy_location->set_end (new_end);
2357                                         copy_location->set_start (new_start);
2358                                 } else if (new_end > copy_location->start()) {
2359                                         copy_location->set_end (new_end);
2360                                 } else if (newframe > 0) {
2361                                         _editor->snap_to (next, -1, true);
2362                                         copy_location->set_start (next);
2363                                         copy_location->set_end (newframe);
2364                                 }
2365                         }
2366                 }
2367
2368                 update_item (copy_location);
2369
2370                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2371
2372                 if (lm) {
2373                         lm->set_position (copy_location->start(), copy_location->end());
2374                 }
2375         }
2376
2377         _last_pointer_frame = _current_pointer_frame;
2378
2379         assert (!_copied_locations.empty());
2380
2381         _editor->edit_point_clock.set (_copied_locations.front()->start());
2382         _editor->show_verbose_time_cursor (newframe, 10);
2383
2384 #ifdef GTKOSX
2385         _editor->update_canvas_now ();
2386 #endif
2387         _editor->edit_point_clock.set (copy_location->start());
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 = Keyboard::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 = Keyboard::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         nframes64_t const pending_position = adjusted_current_frame (event);
2939
2940         /* only alter selection if the current frame is
2941            different from the last frame position (adjusted)
2942          */
2943
2944         if (pending_position == _last_pointer_frame) {
2945                 return;
2946         }
2947
2948         switch (_operation) {
2949         case CreateSelection:
2950
2951                 if (first_move) {
2952                         _editor->snap_to (_grab_frame);
2953                 }
2954
2955                 if (pending_position < _grab_frame) {
2956                         start = pending_position;
2957                         end = _grab_frame;
2958                 } else {
2959                         end = pending_position;
2960                         start = _grab_frame;
2961                 }
2962
2963                 /* first drag: Either add to the selection
2964                    or create a new selection->
2965                 */
2966
2967                 if (first_move) {
2968
2969                         _editor->begin_reversible_command (_("range selection"));
2970
2971                         if (_copy) {
2972                                 /* adding to the selection */
2973                                 _editor->clicked_selection = _editor->selection->add (start, end);
2974                                 _copy = false;
2975                         } else {
2976                                 /* new selection-> */
2977                                 _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end);
2978                         }
2979                 }
2980                 break;
2981
2982         case SelectionStartTrim:
2983
2984                 if (first_move) {
2985                         _editor->begin_reversible_command (_("trim selection start"));
2986                 }
2987
2988                 start = _editor->selection->time[_editor->clicked_selection].start;
2989                 end = _editor->selection->time[_editor->clicked_selection].end;
2990
2991                 if (pending_position > end) {
2992                         start = end;
2993                 } else {
2994                         start = pending_position;
2995                 }
2996                 break;
2997
2998         case SelectionEndTrim:
2999
3000                 if (first_move) {
3001                         _editor->begin_reversible_command (_("trim selection end"));
3002                 }
3003
3004                 start = _editor->selection->time[_editor->clicked_selection].start;
3005                 end = _editor->selection->time[_editor->clicked_selection].end;
3006
3007                 if (pending_position < start) {
3008                         end = start;
3009                 } else {
3010                         end = pending_position;
3011                 }
3012
3013                 break;
3014
3015         case SelectionMove:
3016
3017                 if (first_move) {
3018                         _editor->begin_reversible_command (_("move selection"));
3019                 }
3020
3021                 start = _editor->selection->time[_editor->clicked_selection].start;
3022                 end = _editor->selection->time[_editor->clicked_selection].end;
3023
3024                 length = end - start;
3025
3026                 start = pending_position;
3027                 _editor->snap_to (start);
3028
3029                 end = start + length;
3030
3031                 break;
3032         }
3033
3034         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3035                 _editor->start_canvas_autoscroll (1, 0);
3036         }
3037
3038         if (start != end) {
3039                 _editor->selection->replace (_editor->clicked_selection, start, end);
3040         }
3041
3042         _last_pointer_frame = pending_position;
3043
3044         if (_operation == SelectionMove) {
3045                 _editor->show_verbose_time_cursor(start, 10);
3046         } else {
3047                 _editor->show_verbose_time_cursor(pending_position, 10);
3048         }
3049 }
3050
3051 void
3052 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3053 {
3054         if (movement_occurred) {
3055                 motion (event, false);
3056                 /* XXX this is not object-oriented programming at all. ick */
3057                 if (_editor->selection->time.consolidate()) {
3058                         _editor->selection->TimeChanged ();
3059                 }
3060                 _editor->commit_reversible_command ();
3061         } else {
3062                 /* just a click, no pointer movement.*/
3063
3064                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3065
3066                         _editor->selection->clear_time();
3067
3068                 }
3069         }
3070
3071         /* XXX what happens if its a music selection? */
3072         _editor->session->set_audio_range (_editor->selection->time);
3073         _editor->stop_canvas_autoscroll ();
3074 }
3075
3076 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3077         : Drag (e, i),
3078           _operation (o),
3079           _copy (false)
3080 {
3081         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3082         _drag_rect->hide ();
3083
3084         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3085         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3086 }
3087
3088 void
3089 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3090 {
3091         if (_editor->session == 0) {
3092                 return;
3093         }
3094
3095         Gdk::Cursor* cursor = 0;
3096
3097         if (!_editor->temp_location) {
3098                 _editor->temp_location = new Location;
3099         }
3100
3101         switch (_operation) {
3102         case CreateRangeMarker:
3103         case CreateTransportMarker:
3104         case CreateCDMarker:
3105
3106                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3107                         _copy = true;
3108                 } else {
3109                         _copy = false;
3110                 }
3111                 cursor = _editor->selector_cursor;
3112                 break;
3113         }
3114
3115         Drag::start_grab (event, cursor);
3116
3117         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3118 }
3119
3120 void
3121 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3122 {
3123         nframes64_t start = 0;
3124         nframes64_t end = 0;
3125         ArdourCanvas::SimpleRect *crect;
3126
3127         switch (_operation) {
3128         case CreateRangeMarker:
3129                 crect = _editor->range_bar_drag_rect;
3130                 break;
3131         case CreateTransportMarker:
3132                 crect = _editor->transport_bar_drag_rect;
3133                 break;
3134         case CreateCDMarker:
3135                 crect = _editor->cd_marker_bar_drag_rect;
3136                 break;
3137         default:
3138                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3139                 return;
3140                 break;
3141         }
3142
3143         _editor->snap_to_with_modifier (_current_pointer_frame, event);
3144
3145         /* only alter selection if the current frame is
3146            different from the last frame position.
3147          */
3148
3149         if (_current_pointer_frame == _last_pointer_frame) {
3150                 return;
3151         }
3152
3153         switch (_operation) {
3154         case CreateRangeMarker:
3155         case CreateTransportMarker:
3156         case CreateCDMarker:
3157                 if (first_move) {
3158                         _editor->snap_to (_grab_frame);
3159                 }
3160
3161                 if (_current_pointer_frame < _grab_frame) {
3162                         start = _current_pointer_frame;
3163                         end = _grab_frame;
3164                 } else {
3165                         end = _current_pointer_frame;
3166                         start = _grab_frame;
3167                 }
3168
3169                 /* first drag: Either add to the selection
3170                    or create a new selection.
3171                 */
3172
3173                 if (first_move) {
3174
3175                         _editor->temp_location->set (start, end);
3176
3177                         crect->show ();
3178
3179                         update_item (_editor->temp_location);
3180                         _drag_rect->show();
3181                         //_drag_rect->raise_to_top();
3182
3183                 }
3184                 break;
3185         }
3186
3187         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3188                 _editor->start_canvas_autoscroll (1, 0);
3189         }
3190
3191         if (start != end) {
3192                 _editor->temp_location->set (start, end);
3193
3194                 double x1 = _editor->frame_to_pixel (start);
3195                 double x2 = _editor->frame_to_pixel (end);
3196                 crect->property_x1() = x1;
3197                 crect->property_x2() = x2;
3198
3199                 update_item (_editor->temp_location);
3200         }
3201
3202         _last_pointer_frame = _current_pointer_frame;
3203
3204         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3205
3206 }
3207
3208 void
3209 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3210 {
3211         Location * newloc = 0;
3212         string rangename;
3213         int flags;
3214
3215         if (movement_occurred) {
3216                 motion (event, false);
3217                 _drag_rect->hide();
3218
3219                 switch (_operation) {
3220                 case CreateRangeMarker:
3221                 case CreateCDMarker:
3222                     {
3223                         _editor->begin_reversible_command (_("new range marker"));
3224                         XMLNode &before = _editor->session->locations()->get_state();
3225                         _editor->session->locations()->next_available_name(rangename,"unnamed");
3226                         if (_operation == CreateCDMarker) {
3227                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3228                                 _editor->cd_marker_bar_drag_rect->hide();
3229                         }
3230                         else {
3231                                 flags = Location::IsRangeMarker;
3232                                 _editor->range_bar_drag_rect->hide();
3233                         }
3234                         newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3235                         _editor->session->locations()->add (newloc, true);
3236                         XMLNode &after = _editor->session->locations()->get_state();
3237                         _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
3238                         _editor->commit_reversible_command ();
3239                         break;
3240                     }
3241
3242                 case CreateTransportMarker:
3243                         // popup menu to pick loop or punch
3244                         _editor->new_transport_marker_context_menu (&event->button, _item);
3245                         break;
3246                 }
3247         } else {
3248                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3249
3250                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3251
3252                         nframes64_t start;
3253                         nframes64_t end;
3254
3255                         start = _editor->session->locations()->first_mark_before (_grab_frame);
3256                         end = _editor->session->locations()->first_mark_after (_grab_frame);
3257
3258                         if (end == max_frames) {
3259                                 end = _editor->session->current_end_frame ();
3260                         }
3261
3262                         if (start == 0) {
3263                                 start = _editor->session->current_start_frame ();
3264                         }
3265
3266                         switch (_editor->mouse_mode) {
3267                         case MouseObject:
3268                                 /* find the two markers on either side and then make the selection from it */
3269                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3270                                 break;
3271
3272                         case MouseRange:
3273                                 /* find the two markers on either side of the click and make the range out of it */
3274                                 _editor->selection->set (0, start, end);
3275                                 break;
3276
3277                         default:
3278                                 break;
3279                         }
3280                 }
3281         }
3282
3283         _editor->stop_canvas_autoscroll ();
3284 }
3285
3286
3287
3288 void
3289 RangeMarkerBarDrag::update_item (Location* location)
3290 {
3291         double const x1 = _editor->frame_to_pixel (location->start());
3292         double const x2 = _editor->frame_to_pixel (location->end());
3293
3294         _drag_rect->property_x1() = x1;
3295         _drag_rect->property_x2() = x2;
3296 }
3297
3298 void
3299 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3300 {
3301         Drag::start_grab (event, _editor->zoom_cursor);
3302         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3303 }
3304
3305 void
3306 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3307 {
3308         nframes64_t start;
3309         nframes64_t end;
3310
3311         _editor->snap_to_with_modifier (_current_pointer_frame, event);
3312
3313         if (first_move) {
3314                 _editor->snap_to_with_modifier (_grab_frame, event);
3315         }
3316
3317         if (_current_pointer_frame == _last_pointer_frame) {
3318                 return;
3319         }
3320
3321         /* base start and end on initial click position */
3322         if (_current_pointer_frame < _grab_frame) {
3323                 start = _current_pointer_frame;
3324                 end = _grab_frame;
3325         } else {
3326                 end = _current_pointer_frame;
3327                 start = _grab_frame;
3328         }
3329
3330         if (start != end) {
3331
3332                 if (first_move) {
3333                         _editor->zoom_rect->show();
3334                         _editor->zoom_rect->raise_to_top();
3335                 }
3336
3337                 _editor->reposition_zoom_rect(start, end);
3338
3339                 _last_pointer_frame = _current_pointer_frame;
3340
3341                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3342         }
3343 }
3344
3345 void
3346 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3347 {
3348         if (movement_occurred) {
3349                 motion (event, false);
3350
3351                 if (_grab_frame < _last_pointer_frame) {
3352                         _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");
3353                 } else {
3354                         _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom");
3355                 }
3356         } else {
3357                 _editor->temporal_zoom_to_frame (false, _grab_frame);
3358                 /*
3359                 temporal_zoom_step (false);
3360                 center_screen (_grab_frame);
3361                 */
3362         }
3363
3364         _editor->zoom_rect->hide();
3365 }
3366
3367 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3368         : Drag (e, i)
3369 {
3370         CanvasNoteEvent*     cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3371         region = &cnote->region_view();
3372 }
3373
3374 void
3375 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3376 {
3377         Drag::start_grab (event);
3378
3379         drag_delta_x = 0;
3380         drag_delta_note = 0;
3381
3382         double event_x;
3383         double event_y;
3384
3385         event_x = _current_pointer_x;
3386         event_y = _current_pointer_y;
3387
3388         _item->property_parent().get_value()->w2i(event_x, event_y);
3389
3390         last_x = region->snap_to_pixel(event_x);
3391         last_y = event_y;
3392
3393         CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3394
3395         if (!(was_selected = cnote->selected())) {
3396
3397                 /* tertiary-click means extend selection - we'll do that on button release,
3398                    so don't add it here, because otherwise we make it hard to figure
3399                    out the "extend-to" range.
3400                 */
3401
3402                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3403
3404                 if (!extend) {
3405                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3406
3407                         if (add) {
3408                                 region->note_selected (cnote, true);
3409                         } else {
3410                                 region->unique_select (cnote);
3411                         }
3412                 }
3413         }
3414 }
3415
3416 void
3417 NoteDrag::motion (GdkEvent*, bool)
3418 {
3419         MidiStreamView* streamview = region->midi_stream_view();
3420         double event_x;
3421         double event_y;
3422
3423         event_x = _current_pointer_x;
3424         event_y = _current_pointer_y;
3425
3426         _item->property_parent().get_value()->w2i(event_x, event_y);
3427
3428         event_x = region->snap_to_pixel(event_x);
3429
3430         double dx     = event_x - last_x;
3431         double dy     = event_y - last_y;
3432         last_x = event_x;
3433
3434         drag_delta_x += dx;
3435
3436         // Snap to note rows
3437
3438         if (abs (dy) < streamview->note_height()) {
3439                 dy = 0.0;
3440         } else {
3441                 int8_t this_delta_note;
3442                 if (dy > 0) {
3443                         this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3444                 } else {
3445                         this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3446                 }
3447                 drag_delta_note -= this_delta_note;
3448                 dy = streamview->note_height() * this_delta_note;
3449                 last_y = last_y + dy;
3450         }
3451
3452         if (dx || dy) {
3453                 region->move_selection (dx, dy);
3454
3455                 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3456                 char buf[4];
3457                 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3458                 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3459                 _editor->show_verbose_canvas_cursor_with (buf);
3460         }
3461 }
3462
3463 void
3464 NoteDrag::finished (GdkEvent* ev, bool moved)
3465 {
3466         ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3467
3468         if (!moved) {
3469                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3470
3471                         if (was_selected) {
3472                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3473                                 if (add) {
3474                                         region->note_deselected (cnote);
3475                                 }
3476                         } else {
3477                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3478                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3479
3480                                 if (!extend && !add && region->selection_size() > 1) {
3481                                         region->unique_select(cnote);
3482                                 } else if (extend) {
3483                                         region->note_selected (cnote, true, true);
3484                                 } else {
3485                                         /* it was added during button press */
3486                                 }
3487                         }
3488                 }
3489         } else {
3490                 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3491         }
3492 }