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