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