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