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