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