cef27660a6882edc7c821a6b0d3f96600c11658b
[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         if (!movement_occurred) {
813                 /* just a click */
814                 return;
815         }
816
817         /* reverse this here so that we have the correct logic to finalize
818            the drag.
819         */
820
821         if (Config->get_edit_mode() == Lock) {
822                 _x_constrained = !_x_constrained;
823         }
824
825         bool const changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
826         bool const changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
827         framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
828
829         _editor->update_canvas_now ();
830         
831         if (_copy) {
832                 
833                 finished_copy (
834                         find_time_axis_views_and_layers (),
835                         changed_position,
836                         changed_tracks,
837                         drag_delta
838                         );
839                 
840         } else {
841                 
842                 finished_no_copy (
843                         find_time_axis_views_and_layers (),
844                         changed_position,
845                         changed_tracks,
846                         drag_delta
847                         );
848                 
849         }
850 }
851
852 void
853 RegionMoveDrag::finished_copy (
854         map<RegionView*, pair<RouteTimeAxisView*, int> > const & final,
855         bool const changed_position,
856         bool const changed_tracks,
857         framecnt_t const drag_delta
858         )
859 {
860         RegionSelection new_views;
861         PlaylistSet modified_playlists;
862         list<RegionView*> views_to_delete;
863
864         if (_brushing) {
865                 /* all changes were made during motion event handlers */
866
867                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
868                         delete i->view;
869                 }
870
871                 _editor->commit_reversible_command ();
872                 return;
873         }
874
875         if (_x_constrained) {
876                 _editor->begin_reversible_command (_("fixed time region copy"));
877         } else {
878                 _editor->begin_reversible_command (_("region copy"));
879         }
880
881         /* insert the regions into their new playlists */
882         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
883
884                 nframes64_t where;
885
886                 if (i->view->region()->locked()) {
887                         continue;
888                 }
889
890                 if (changed_position && !_x_constrained) {
891                         where = i->view->region()->position() - drag_delta;
892                 } else {
893                         where = i->view->region()->position();
894                 }
895
896                 map<RegionView*, pair<RouteTimeAxisView*, int> >::const_iterator j = final.find (i->view);
897                 assert (j != final.end());
898
899                 RegionView* new_view = insert_region_into_playlist (
900                         i->view->region(), j->second.first, j->second.second, where, modified_playlists
901                         );
902                 
903                 if (new_view == 0) {
904                         continue;
905                 }
906
907                 new_views.push_back (new_view);
908                 
909                 /* we don't need the copied RegionView any more */
910                 views_to_delete.push_back (i->view);
911         }
912
913         /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
914            because when views are deleted they are automagically removed from _views, which messes
915            up the iteration.
916         */
917         for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
918                 delete *i;
919         }
920
921         /* If we've created new regions either by copying or moving 
922            to a new track, we want to replace the old selection with the new ones 
923         */
924
925         if (new_views.size() > 0) {
926                 _editor->selection->set (new_views);
927         }
928
929         /* write commands for the accumulated diffs for all our modified playlists */
930         add_stateful_diff_commands_for_playlists (modified_playlists);
931
932         _editor->commit_reversible_command ();
933 }
934
935 void
936 RegionMoveDrag::finished_no_copy (
937         map<RegionView*, pair<RouteTimeAxisView*, int> > const & final,
938         bool const changed_position,
939         bool const changed_tracks,
940         framecnt_t const drag_delta
941         )
942 {
943         RegionSelection new_views;
944         PlaylistSet modified_playlists;
945         PlaylistSet frozen_playlists;
946
947         if (_brushing) {
948                 /* all changes were made during motion event handlers */
949                 _editor->commit_reversible_command ();
950                 return;
951         }
952
953         if (_x_constrained) {
954                 _editor->begin_reversible_command (_("fixed time region drag"));
955         } else {
956                 _editor->begin_reversible_command (_("region drag"));
957         }
958
959         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
960
961                 RegionView* rv = i->view;
962
963                 map<RegionView*, pair<RouteTimeAxisView*, int> >::const_iterator j = final.find (rv);
964                 assert (j != final.end());
965                 
966                 RouteTimeAxisView* dest_rtv = j->second.first;
967                 layer_t dest_layer = j->second.second;
968
969                 if (rv->region()->locked()) {
970                         ++i;
971                         continue;
972                 }
973
974                 nframes64_t where;
975
976                 if (changed_position && !_x_constrained) {
977                         where = rv->region()->position() - drag_delta;
978                 } else {
979                         where = rv->region()->position();
980                 }
981
982                 if (changed_tracks) {
983
984                         /* insert into new playlist */
985
986                         RegionView* new_view = insert_region_into_playlist (
987                                 RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
988                                 );
989
990                         if (new_view == 0) {
991                                 ++i;
992                                 continue;
993                         }
994
995                         new_views.push_back (new_view);
996
997                         /* remove from old playlist */
998
999                         /* the region that used to be in the old playlist is not
1000                            moved to the new one - we use a copy of it. as a result,
1001                            any existing editor for the region should no longer be
1002                            visible.
1003                         */
1004                         rv->hide_region_editor();
1005                         rv->fake_set_opaque (false);
1006
1007                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1008
1009                 } else {
1010                         
1011                         rv->region()->clear_changes ();
1012
1013                         /*
1014                            motion on the same track. plonk the previously reparented region
1015                            back to its original canvas group (its streamview).
1016                            No need to do anything for copies as they are fake regions which will be deleted.
1017                         */
1018
1019                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1020                         rv->get_canvas_group()->property_y() = i->initial_y;
1021                         rv->get_time_axis_view().reveal_dependent_views (*rv);
1022
1023                         /* just change the model */
1024
1025                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1026                         
1027                         if (dest_rtv->view()->layer_display() == Stacked) {
1028                                 rv->region()->set_layer (dest_layer);
1029                                 rv->region()->set_pending_explicit_relayer (true);
1030                         }
1031                         
1032                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1033
1034                         pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1035
1036                         if (r.second) {
1037                                 playlist->freeze ();
1038                         }
1039
1040                         /* this movement may result in a crossfade being modified, so we need to get undo
1041                            data from the playlist as well as the region.
1042                         */
1043                         
1044                         r = modified_playlists.insert (playlist);
1045                         if (r.second) {
1046                                 playlist->clear_changes ();
1047                         }
1048
1049                         rv->region()->set_position (where, (void*) this);
1050
1051                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1052                 }
1053
1054                 if (changed_tracks) {
1055                         
1056                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1057                            was selected in all of them, then removing it from a playlist will have removed all
1058                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1059                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1060                            corresponding regionview, and _views is now empty).
1061
1062                            This could have invalidated any and all iterators into _views.
1063
1064                            The heuristic we use here is: if the region selection is empty, break out of the loop
1065                            here. if the region selection is not empty, then restart the loop because we know that
1066                            we must have removed at least the region(view) we've just been working on as well as any
1067                            that we processed on previous iterations.
1068
1069                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1070                            we can just iterate.
1071                         */
1072
1073                         
1074                         if (_views.empty()) {
1075                                 break;
1076                         } else {
1077                                 i = _views.begin();
1078                         }
1079
1080                 } else {
1081                         ++i;
1082                 }
1083         }
1084
1085         /* If we've created new regions either by copying or moving 
1086            to a new track, we want to replace the old selection with the new ones 
1087         */
1088
1089         if (new_views.size() > 0) {
1090                 _editor->selection->set (new_views);
1091         }
1092
1093         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1094                 (*p)->thaw();
1095         }
1096
1097         /* write commands for the accumulated diffs for all our modified playlists */
1098         add_stateful_diff_commands_for_playlists (modified_playlists);
1099
1100         _editor->commit_reversible_command ();
1101 }
1102
1103 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1104  *  @param region Region to remove.
1105  *  @param playlist playlist To remove from.
1106  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1107  *  that clear_changes () is only called once per playlist.
1108  */
1109 void
1110 RegionMoveDrag::remove_region_from_playlist (
1111         boost::shared_ptr<Region> region,
1112         boost::shared_ptr<Playlist> playlist,
1113         PlaylistSet& modified_playlists
1114         )
1115 {
1116         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1117
1118         if (r.second) {
1119                 playlist->clear_changes ();
1120         }
1121
1122         playlist->remove_region (region);
1123 }
1124         
1125
1126 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1127  *  clearing the playlist's diff history first if necessary.
1128  *  @param region Region to insert.
1129  *  @param dest_rtv Destination RouteTimeAxisView.
1130  *  @param dest_layer Destination layer.
1131  *  @param where Destination position.
1132  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1133  *  that clear_changes () is only called once per playlist. 
1134  *  @return New RegionView, or 0 if no insert was performed.
1135  */
1136 RegionView *
1137 RegionMoveDrag::insert_region_into_playlist (
1138         boost::shared_ptr<Region> region,
1139         RouteTimeAxisView* dest_rtv,
1140         layer_t dest_layer,
1141         framecnt_t where,
1142         PlaylistSet& modified_playlists
1143         )
1144 {
1145         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1146         if (!dest_playlist) {
1147                 return 0;
1148         }
1149
1150         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1151         _new_region_view = 0;
1152         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1153
1154         /* clear history for the playlist we are about to insert to, provided we haven't already done so */     
1155         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1156         if (r.second) {
1157                 dest_playlist->clear_changes ();
1158         }
1159
1160         dest_playlist->add_region (region, where);
1161
1162         if (dest_rtv->view()->layer_display() == Stacked) {
1163                 region->set_layer (dest_layer);
1164                 region->set_pending_explicit_relayer (true);
1165         }
1166
1167         c.disconnect ();
1168
1169         assert (_new_region_view);
1170
1171         return _new_region_view;
1172 }
1173
1174 void
1175 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1176 {
1177         _new_region_view = rv;
1178 }
1179
1180 void
1181 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1182 {
1183         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1184                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1185                 if (!c->empty()) {
1186                         _editor->session()->add_command (new StatefulDiffCommand (*i));
1187                 } else {
1188                         delete c;
1189                 }
1190         }
1191 }
1192
1193
1194 void
1195 RegionMoveDrag::aborted ()
1196 {
1197         if (_copy) {
1198
1199                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1200                         delete i->view;
1201                 }
1202
1203                 _views.clear ();
1204
1205         } else {
1206                 RegionMotionDrag::aborted ();
1207         }
1208 }
1209
1210 void
1211 RegionMotionDrag::aborted ()
1212 {
1213         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1214                 RegionView* rv = i->view;
1215                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1216                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1217                 assert (rtv);
1218                 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1219                 rv->get_canvas_group()->property_y() = 0;
1220                 rv->get_time_axis_view().reveal_dependent_views (*rv);
1221                 rv->fake_set_opaque (false);
1222                 rv->move (-_total_x_delta, 0);
1223                 rv->set_height (rtv->view()->child_height ());
1224         }
1225
1226         _editor->update_canvas_now ();
1227 }
1228                                       
1229
1230 bool
1231 RegionMotionDrag::x_move_allowed () const
1232 {
1233         if (Config->get_edit_mode() == Lock) {
1234                 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1235                 return _x_constrained;
1236         }
1237
1238         return !_x_constrained;
1239 }
1240
1241 void
1242 RegionMotionDrag::copy_regions (GdkEvent* event)
1243 {
1244         /* duplicate the regionview(s) and region(s) */
1245
1246         list<DraggingView> new_regionviews;
1247
1248         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1249
1250                 RegionView* rv = i->view;
1251                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1252                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1253
1254                 const boost::shared_ptr<const Region> original = rv->region();
1255                 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1256                 region_copy->set_position (original->position(), this);
1257
1258                 RegionView* nrv;
1259                 if (arv) {
1260                         boost::shared_ptr<AudioRegion> audioregion_copy
1261                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1262
1263                         nrv = new AudioRegionView (*arv, audioregion_copy);
1264                 } else if (mrv) {
1265                         boost::shared_ptr<MidiRegion> midiregion_copy
1266                                 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1267                         nrv = new MidiRegionView (*mrv, midiregion_copy);
1268                 } else {
1269                         continue;
1270                 }
1271
1272                 nrv->get_canvas_group()->show ();
1273                 new_regionviews.push_back (DraggingView (nrv));
1274
1275                 /* swap _primary to the copy */
1276
1277                 if (rv == _primary) {
1278                         _primary = nrv;
1279                 }
1280
1281                 /* ..and deselect the one we copied */
1282
1283                 rv->set_selected (false);
1284         }
1285
1286         if (new_regionviews.empty()) {
1287                 return;
1288         }
1289
1290         /* reflect the fact that we are dragging the copies */
1291
1292         _views = new_regionviews;
1293
1294         swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1295
1296         /*
1297            sync the canvas to what we think is its current state
1298            without it, the canvas seems to
1299            "forget" to update properly after the upcoming reparent()
1300            ..only if the mouse is in rapid motion at the time of the grab.
1301            something to do with regionview creation taking so long?
1302         */
1303         _editor->update_canvas_now();
1304 }
1305
1306 bool
1307 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1308 {
1309         /* Which trackview is this ? */
1310
1311         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1312         (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1313         (*layer) = tvp.second;
1314
1315         if (*tv && (*tv)->layer_display() == Overlaid) {
1316                 *layer = 0;
1317         }
1318
1319         /* The region motion is only processed if the pointer is over
1320            an audio track.
1321         */
1322
1323         if (!(*tv) || !(*tv)->is_track()) {
1324                 /* To make sure we hide the verbose canvas cursor when the mouse is
1325                    not held over and audiotrack.
1326                 */
1327                 _editor->hide_verbose_canvas_cursor ();
1328                 return false;
1329         }
1330
1331         return true;
1332 }
1333
1334 /** @param new_order New track order.
1335  *  @param old_order Old track order.
1336  *  @param visible_y_low Lowest visible order.
1337  *  @return true if y movement should not happen, otherwise false.
1338  */
1339 bool
1340 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1341 {
1342         if (new_order != old_order) {
1343
1344                 /* this isn't the pointer track */
1345
1346                 if (y_span > 0) {
1347
1348                         /* moving up the canvas */
1349                         if ( (new_order - y_span) >= tavs.visible_y_low) {
1350
1351                                 int32_t n = 0;
1352
1353                                 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1354                                 int32_t visible_tracks = 0;
1355                                 while (visible_tracks < y_span ) {
1356                                         visible_tracks++;
1357                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1358                                                 /* passing through a hidden track */
1359                                                 n--;
1360                                         }
1361                                 }
1362
1363                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1364                                         /* moving to a non-track; disallow */
1365                                         return true;
1366                                 }
1367
1368
1369                         } else {
1370                                 /* moving beyond the lowest visible track; disallow */
1371                                 return true;
1372                         }
1373
1374                 } else if (y_span < 0) {
1375
1376                         /* moving down the canvas */
1377                         if ((new_order - y_span) <= tavs.visible_y_high) {
1378
1379                                 int32_t visible_tracks = 0;
1380                                 int32_t n = 0;
1381                                 while (visible_tracks > y_span ) {
1382                                         visible_tracks--;
1383
1384                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1385                                                 /* passing through a hidden track */
1386                                                 n++;
1387                                         }
1388                                 }
1389
1390                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1391                                         /* moving to a non-track; disallow */
1392                                         return true;
1393                                 }
1394
1395
1396                         } else {
1397
1398                                 /* moving beyond the highest visible track; disallow */
1399                                 return true;
1400                         }
1401                 }
1402
1403         } else {
1404
1405                 /* this is the pointer's track */
1406
1407                 if ((new_order - y_span) > tavs.visible_y_high) {
1408                         /* we will overflow */
1409                         return true;
1410                 } else if ((new_order - y_span) < tavs.visible_y_low) {
1411                         /* we will overflow */
1412                         return true;
1413                 }
1414         }
1415
1416         return false;
1417 }
1418
1419
1420 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1421         : RegionMotionDrag (e, i, p, v, b),
1422           _copy (c)
1423 {
1424         TimeAxisView* const tv = &_primary->get_time_axis_view ();
1425
1426         _dest_trackview = tv;
1427         if (tv->layer_display() == Overlaid) {
1428                 _dest_layer = 0;
1429         } else {
1430                 _dest_layer = _primary->region()->layer ();
1431         }
1432
1433         double speed = 1;
1434         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1435         if (rtv && rtv->is_track()) {
1436                 speed = rtv->track()->speed ();
1437         }
1438
1439         _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1440 }
1441
1442 void
1443 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1444 {
1445         RegionMotionDrag::start_grab (event, c);
1446
1447         _pointer_frame_offset = grab_frame() - _last_frame_position;
1448 }
1449
1450 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1451         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1452 {
1453         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1454                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1455
1456         _primary = v->view()->create_region_view (r, false, false);
1457
1458         _primary->get_canvas_group()->show ();
1459         _primary->set_position (pos, 0);
1460         _views.push_back (DraggingView (_primary));
1461
1462         _last_frame_position = pos;
1463
1464         _item = _primary->get_canvas_group ();
1465         _dest_trackview = v;
1466         _dest_layer = _primary->region()->layer ();
1467 }
1468
1469 map<RegionView*, pair<RouteTimeAxisView*, int> >
1470 RegionMotionDrag::find_time_axis_views_and_layers ()
1471 {
1472         map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1473
1474         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1475
1476                 double ix1, ix2, iy1, iy2;
1477                 RegionView* rv = i->view;
1478                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1479                 rv->get_canvas_frame()->i2w (ix1, iy1);
1480                 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1481
1482                 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1483                 tav[rv] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1484         }
1485
1486         return tav;
1487 }
1488
1489
1490 void
1491 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1492 {
1493         _editor->update_canvas_now ();
1494
1495         map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1496
1497         RouteTimeAxisView* dest_rtv = final[_primary].first;
1498
1499         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1500         _primary->get_canvas_group()->property_y() = 0;
1501
1502         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1503
1504         _editor->begin_reversible_command (_("insert region"));
1505         playlist->clear_changes ();
1506         playlist->add_region (_primary->region (), _last_frame_position);
1507         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1508         _editor->commit_reversible_command ();
1509
1510         delete _primary;
1511         _primary = 0;
1512         _views.clear ();
1513 }
1514
1515 void
1516 RegionInsertDrag::aborted ()
1517 {
1518         delete _primary;
1519         _primary = 0;
1520         _views.clear ();
1521 }
1522
1523 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1524         : RegionMoveDrag (e, i, p, v, false, false)
1525 {
1526
1527 }
1528
1529 struct RegionSelectionByPosition {
1530     bool operator() (RegionView*a, RegionView* b) {
1531             return a->region()->position () < b->region()->position();
1532     }
1533 };
1534
1535 void
1536 RegionSpliceDrag::motion (GdkEvent* event, bool)
1537 {
1538         RouteTimeAxisView* tv;
1539         layer_t layer;
1540
1541         if (!check_possible (&tv, &layer)) {
1542                 return;
1543         }
1544
1545         int dir;
1546
1547         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1548                 dir = 1;
1549         } else {
1550                 dir = -1;
1551         }
1552
1553         RegionSelection copy (_editor->selection->regions);
1554
1555         RegionSelectionByPosition cmp;
1556         copy.sort (cmp);
1557
1558         nframes64_t const pf = adjusted_current_frame (event);
1559
1560         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1561
1562                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1563
1564                 if (!atv) {
1565                         continue;
1566                 }
1567
1568                 boost::shared_ptr<Playlist> playlist;
1569
1570                 if ((playlist = atv->playlist()) == 0) {
1571                         continue;
1572                 }
1573
1574                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1575                         continue;
1576                 }
1577
1578                 if (dir > 0) {
1579                         if (pf < (*i)->region()->last_frame() + 1) {
1580                                 continue;
1581                         }
1582                 } else {
1583                         if (pf > (*i)->region()->first_frame()) {
1584                                 continue;
1585                         }
1586                 }
1587
1588
1589                 playlist->shuffle ((*i)->region(), dir);
1590         }
1591 }
1592
1593 void
1594 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1595 {
1596
1597 }
1598
1599 void
1600 RegionSpliceDrag::aborted ()
1601 {
1602         /* XXX: TODO */
1603 }
1604
1605 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1606         : Drag (e, i),
1607           _view (dynamic_cast<MidiTimeAxisView*> (v))
1608 {
1609         assert (_view);
1610 }
1611
1612 void
1613 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1614 {
1615         if (first_move) {
1616                 /* don't use a zero-length region otherwise its region view will be hidden when it is created */
1617                 _region = _view->add_region (grab_frame(), 1, false);
1618         } else {
1619                 framepos_t const f = adjusted_current_frame (event);
1620                 if (f < grab_frame()) {
1621                         _region->set_position (f, this);
1622                 }
1623
1624                 /* again, don't use a zero-length region (see above) */
1625                 framecnt_t const len = abs (f - grab_frame ());
1626                 _region->set_length (len < 1 ? 1 : len, this);
1627         }
1628 }
1629
1630 void
1631 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1632 {
1633         if (movement_occurred) {
1634                 _editor->commit_reversible_command ();
1635         }
1636 }
1637
1638 void
1639 RegionCreateDrag::aborted ()
1640 {
1641         /* XXX */
1642 }
1643
1644 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1645         : Drag (e, i)
1646         , region (0)
1647 {
1648
1649 }
1650
1651 void
1652 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1653 {
1654         Gdk::Cursor* cursor;
1655         ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1656
1657         Drag::start_grab (event);
1658
1659         region = &cnote->region_view();
1660
1661         double const region_start = region->get_position_pixels();
1662         double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1663
1664         if (grab_x() <= middle_point) {
1665                 cursor = _editor->left_side_trim_cursor;
1666                 at_front = true;
1667         } else {
1668                 cursor = _editor->right_side_trim_cursor;
1669                 at_front = false;
1670         }
1671
1672         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1673
1674         if (event->motion.state & Keyboard::PrimaryModifier) {
1675                 relative = false;
1676         } else {
1677                 relative = true;
1678         }
1679
1680         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1681
1682         if (ms.size() > 1) {
1683                 /* has to be relative, may make no sense otherwise */
1684                 relative = true;
1685         }
1686
1687         /* select this note; if it is already selected, preserve the existing selection,
1688            otherwise make this note the only one selected.
1689         */
1690         region->note_selected (cnote, cnote->selected ());
1691
1692         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1693                 MidiRegionSelection::iterator next;
1694                 next = r;
1695                 ++next;
1696                 (*r)->begin_resizing (at_front);
1697                 r = next;
1698         }
1699 }
1700
1701 void
1702 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1703 {
1704         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1705         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1706                 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1707         }
1708 }
1709
1710 void
1711 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1712 {
1713         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1714         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1715                 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1716         }
1717 }
1718
1719 void
1720 NoteResizeDrag::aborted ()
1721 {
1722         /* XXX: TODO */
1723 }
1724
1725 void
1726 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1727 {
1728
1729 }
1730
1731 void
1732 RegionGainDrag::finished (GdkEvent *, bool)
1733 {
1734
1735 }
1736
1737 void
1738 RegionGainDrag::aborted ()
1739 {
1740         /* XXX: TODO */
1741 }
1742
1743 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1744         : RegionDrag (e, i, p, v)
1745         , _have_transaction (false)
1746 {
1747
1748 }
1749
1750 void
1751 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1752 {
1753         double speed = 1.0;
1754         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1755         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1756
1757         if (tv && tv->is_track()) {
1758                 speed = tv->track()->speed();
1759         }
1760
1761         nframes64_t const region_start = (nframes64_t) (_primary->region()->position() / speed);
1762         nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1763         nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1764
1765         nframes64_t const pf = adjusted_current_frame (event);
1766
1767         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1768                 _operation = ContentsTrim;
1769                 Drag::start_grab (event, _editor->trimmer_cursor);
1770         } else {
1771                 /* These will get overridden for a point trim.*/
1772                 if (pf < (region_start + region_length/2)) {
1773                         /* closer to start */
1774                         _operation = StartTrim;
1775                         Drag::start_grab (event, _editor->left_side_trim_cursor);
1776                 } else {
1777                         /* closer to end */
1778                         _operation = EndTrim;
1779                         Drag::start_grab (event, _editor->right_side_trim_cursor);
1780                 }
1781         }
1782
1783         switch (_operation) {
1784         case StartTrim:
1785                 _editor->show_verbose_time_cursor (region_start, 10);
1786                 break;
1787         case EndTrim:
1788                 _editor->show_verbose_time_cursor (region_end, 10);
1789                 break;
1790         case ContentsTrim:
1791                 _editor->show_verbose_time_cursor (pf, 10);
1792                 break;
1793         }
1794 }
1795
1796 void
1797 TrimDrag::motion (GdkEvent* event, bool first_move)
1798 {
1799         RegionView* rv = _primary;
1800
1801         /* snap modifier works differently here..
1802            its current state has to be passed to the
1803            various trim functions in order to work properly
1804         */
1805
1806         double speed = 1.0;
1807         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1808         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1809         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1810
1811         if (tv && tv->is_track()) {
1812                 speed = tv->track()->speed();
1813         }
1814
1815         nframes64_t const pf = adjusted_current_frame (event);
1816
1817         if (first_move) {
1818
1819                 string trim_type;
1820
1821                 switch (_operation) {
1822                 case StartTrim:
1823                         trim_type = "Region start trim";
1824                         break;
1825                 case EndTrim:
1826                         trim_type = "Region end trim";
1827                         break;
1828                 case ContentsTrim:
1829                         trim_type = "Region content trim";
1830                         break;
1831                 }
1832
1833                 _editor->begin_reversible_command (trim_type);
1834                 _have_transaction = true;
1835
1836                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1837                         RegionView* rv = i->view;
1838                         rv->fake_set_opaque(false);
1839                         rv->enable_display (false);
1840                         rv->region()->clear_changes ();
1841                         rv->region()->suspend_property_changes ();
1842
1843                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1844
1845                         if (arv){
1846                                 arv->temporarily_hide_envelope ();
1847                         }
1848
1849                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1850                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1851
1852                         if (insert_result.second) {
1853                                 pl->freeze();
1854                         }
1855                 }
1856         }
1857
1858         bool non_overlap_trim = false;
1859
1860         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1861                 non_overlap_trim = true;
1862         }
1863
1864         switch (_operation) {
1865         case StartTrim:
1866                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1867                         _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1868                 }
1869                 break;
1870
1871         case EndTrim:
1872                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1873                         _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1874                 }
1875                 break;
1876
1877         case ContentsTrim:
1878                 {
1879                         bool swap_direction = false;
1880
1881                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1882                                 swap_direction = true;
1883                         }
1884
1885                         nframes64_t frame_delta = 0;
1886                         
1887                         bool left_direction = false;
1888                         if (last_pointer_frame() > pf) {
1889                                 left_direction = true;
1890                         }
1891
1892                         if (left_direction) {
1893                                 frame_delta = (last_pointer_frame() - pf);
1894                         } else {
1895                                 frame_delta = (pf - last_pointer_frame());
1896                         }
1897
1898                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1899                                 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1900                         }
1901                 }
1902                 break;
1903         }
1904
1905         switch (_operation) {
1906         case StartTrim:
1907                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1908                 break;
1909         case EndTrim:
1910                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1911                 break;
1912         case ContentsTrim:
1913                 _editor->show_verbose_time_cursor (pf, 10);
1914                 break;
1915         }
1916 }
1917
1918
1919 void
1920 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1921 {
1922         if (movement_occurred) {
1923                 motion (event, false);
1924
1925                 if (!_editor->selection->selected (_primary)) {
1926                         _editor->thaw_region_after_trim (*_primary);
1927                 } else {
1928
1929                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1930                                 _editor->thaw_region_after_trim (*i->view);
1931                                 i->view->enable_display (true);
1932                                 i->view->fake_set_opaque (true);
1933                                 if (_have_transaction) {
1934                                         _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1935                                 }
1936                         }
1937                 }
1938                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1939                         (*p)->thaw ();
1940                 }
1941
1942                 _editor->motion_frozen_playlists.clear ();
1943
1944                 if (_have_transaction) {
1945                         _editor->commit_reversible_command();
1946                 }
1947
1948         } else {
1949                 /* no mouse movement */
1950                 _editor->point_trim (event, adjusted_current_frame (event));
1951         }
1952 }
1953
1954 void
1955 TrimDrag::aborted ()
1956 {
1957         /* Our motion method is changing model state, so use the Undo system
1958            to cancel.  Perhaps not ideal, as this will leave an Undo point
1959            behind which may be slightly odd from the user's point of view.
1960         */
1961
1962         finished (0, true);
1963         
1964         if (_have_transaction) {
1965                 _editor->undo ();
1966         }
1967 }
1968
1969 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1970         : Drag (e, i),
1971           _copy (c)
1972 {
1973         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1974         assert (_marker);
1975 }
1976
1977 void
1978 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1979 {
1980         if (_copy) {
1981                 // create a dummy marker for visual representation of moving the copy.
1982                 // The actual copying is not done before we reach the finish callback.
1983                 char name[64];
1984                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1985                 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1986                                                           *new MeterSection (_marker->meter()));
1987
1988                 _item = &new_marker->the_item ();
1989                 _marker = new_marker;
1990
1991         } else {
1992
1993                 MetricSection& section (_marker->meter());
1994
1995                 if (!section.movable()) {
1996                         return;
1997                 }
1998
1999         }
2000
2001         Drag::start_grab (event, cursor);
2002
2003         _pointer_frame_offset = grab_frame() - _marker->meter().frame();
2004
2005         _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
2006 }
2007
2008 void
2009 MeterMarkerDrag::motion (GdkEvent* event, bool)
2010 {
2011         nframes64_t const pf = adjusted_current_frame (event);
2012
2013         _marker->set_position (pf);
2014         
2015         _editor->show_verbose_time_cursor (pf, 10);
2016 }
2017
2018 void
2019 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2020 {
2021         if (!movement_occurred) {
2022                 return;
2023         }
2024
2025         motion (event, false);
2026
2027         BBT_Time when;
2028
2029         TempoMap& map (_editor->session()->tempo_map());
2030         map.bbt_time (last_pointer_frame(), when);
2031
2032         if (_copy == true) {
2033                 _editor->begin_reversible_command (_("copy meter mark"));
2034                 XMLNode &before = map.get_state();
2035                 map.add_meter (_marker->meter(), when);
2036                 XMLNode &after = map.get_state();
2037                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2038                 _editor->commit_reversible_command ();
2039
2040                 // delete the dummy marker we used for visual representation of copying.
2041                 // a new visual marker will show up automatically.
2042                 delete _marker;
2043         } else {
2044                 _editor->begin_reversible_command (_("move meter mark"));
2045                 XMLNode &before = map.get_state();
2046                 map.move_meter (_marker->meter(), when);
2047                 XMLNode &after = map.get_state();
2048                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2049                 _editor->commit_reversible_command ();
2050         }
2051 }
2052
2053 void
2054 MeterMarkerDrag::aborted ()
2055 {
2056         _marker->set_position (_marker->meter().frame ());
2057 }
2058
2059 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2060         : Drag (e, i),
2061           _copy (c)
2062 {
2063         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2064         assert (_marker);
2065 }
2066
2067 void
2068 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2069 {
2070
2071         if (_copy) {
2072
2073                 // create a dummy marker for visual representation of moving the copy.
2074                 // The actual copying is not done before we reach the finish callback.
2075                 char name[64];
2076                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2077                 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2078                                                           *new TempoSection (_marker->tempo()));
2079
2080                 _item = &new_marker->the_item ();
2081                 _marker = new_marker;
2082
2083         } else {
2084
2085                 MetricSection& section (_marker->tempo());
2086
2087                 if (!section.movable()) {
2088                         return;
2089                 }
2090         }
2091
2092         Drag::start_grab (event, cursor);
2093
2094         _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2095         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2096 }
2097
2098 void
2099 TempoMarkerDrag::motion (GdkEvent* event, bool)
2100 {
2101         nframes64_t const pf = adjusted_current_frame (event);
2102         _marker->set_position (pf);
2103         _editor->show_verbose_time_cursor (pf, 10);
2104 }
2105
2106 void
2107 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2108 {
2109         if (!movement_occurred) {
2110                 return;
2111         }
2112
2113         motion (event, false);
2114
2115         BBT_Time when;
2116
2117         TempoMap& map (_editor->session()->tempo_map());
2118         map.bbt_time (last_pointer_frame(), when);
2119
2120         if (_copy == true) {
2121                 _editor->begin_reversible_command (_("copy tempo mark"));
2122                 XMLNode &before = map.get_state();
2123                 map.add_tempo (_marker->tempo(), when);
2124                 XMLNode &after = map.get_state();
2125                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2126                 _editor->commit_reversible_command ();
2127
2128                 // delete the dummy marker we used for visual representation of copying.
2129                 // a new visual marker will show up automatically.
2130                 delete _marker;
2131         } else {
2132                 _editor->begin_reversible_command (_("move tempo mark"));
2133                 XMLNode &before = map.get_state();
2134                 map.move_tempo (_marker->tempo(), when);
2135                 XMLNode &after = map.get_state();
2136                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2137                 _editor->commit_reversible_command ();
2138         }
2139 }
2140
2141 void
2142 TempoMarkerDrag::aborted ()
2143 {
2144         _marker->set_position (_marker->tempo().frame());
2145 }
2146
2147 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2148         : Drag (e, i),
2149           _stop (s)
2150 {
2151         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2152         assert (_cursor);
2153 }
2154
2155 void
2156 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2157 {
2158         Drag::start_grab (event, c);
2159
2160         if (!_stop) {
2161
2162                 nframes64_t where = _editor->event_frame (event, 0, 0);
2163
2164                 _editor->snap_to_with_modifier (where, event);
2165                 _editor->playhead_cursor->set_position (where);
2166
2167         }
2168
2169         if (_cursor == _editor->playhead_cursor) {
2170                 _editor->_dragging_playhead = true;
2171
2172                 Session* s = _editor->session ();
2173
2174                 if (s) {
2175                         if (_was_rolling && _stop) {
2176                                 s->request_stop ();
2177                         }
2178
2179                         if (s->is_auditioning()) {
2180                                 s->cancel_audition ();
2181                         }
2182
2183                         s->request_suspend_timecode_transmission ();
2184
2185                         if (s->timecode_transmission_suspended ()) {
2186                                 nframes64_t const f = _editor->playhead_cursor->current_frame;
2187                                 s->send_mmc_locate (f);
2188                                 s->send_full_time_code (f);
2189                         }
2190                 }
2191         }
2192
2193         _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2194
2195         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2196 }
2197
2198 void
2199 CursorDrag::motion (GdkEvent* event, bool)
2200 {
2201         nframes64_t const adjusted_frame = adjusted_current_frame (event);
2202
2203         if (adjusted_frame == last_pointer_frame()) {
2204                 return;
2205         }
2206
2207         _cursor->set_position (adjusted_frame);
2208
2209         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2210
2211         Session* s = _editor->session ();
2212         if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2213                 nframes64_t const f = _editor->playhead_cursor->current_frame;
2214                 s->send_mmc_locate (f);
2215                 s->send_full_time_code (f);
2216         }
2217         
2218
2219 #ifdef GTKOSX
2220         _editor->update_canvas_now ();
2221 #endif
2222         _editor->UpdateAllTransportClocks (_cursor->current_frame);
2223 }
2224
2225 void
2226 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2227 {
2228         _editor->_dragging_playhead = false;
2229
2230         if (!movement_occurred && _stop) {
2231                 return;
2232         }
2233
2234         motion (event, false);
2235
2236         if (_item == &_editor->playhead_cursor->canvas_item) {
2237                 Session* s = _editor->session ();
2238                 if (s) {
2239                         s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2240                         _editor->_pending_locate_request = true;
2241                         s->request_resume_timecode_transmission ();
2242                 }
2243         }
2244 }
2245
2246 void
2247 CursorDrag::aborted ()
2248 {
2249         if (_editor->_dragging_playhead) {
2250                 _editor->session()->request_resume_timecode_transmission ();
2251                 _editor->_dragging_playhead = false;
2252         }
2253         
2254         _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2255 }
2256
2257 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2258         : RegionDrag (e, i, p, v)
2259 {
2260
2261 }
2262
2263 void
2264 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2265 {
2266         Drag::start_grab (event, cursor);
2267
2268         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2269         boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2270
2271         _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2272         _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2273         
2274 }
2275
2276 void
2277 FadeInDrag::motion (GdkEvent* event, bool)
2278 {
2279         nframes64_t fade_length;
2280
2281         nframes64_t const pos = adjusted_current_frame (event);
2282
2283         boost::shared_ptr<Region> region = _primary->region ();
2284
2285         if (pos < (region->position() + 64)) {
2286                 fade_length = 64; // this should be a minimum defined somewhere
2287         } else if (pos > region->last_frame()) {
2288                 fade_length = region->length();
2289         } else {
2290                 fade_length = pos - region->position();
2291         }
2292
2293         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2294
2295                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2296
2297                 if (!tmp) {
2298                         continue;
2299                 }
2300
2301                 tmp->reset_fade_in_shape_width (fade_length);
2302         }
2303
2304         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2305 }
2306
2307 void
2308 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2309 {
2310         if (!movement_occurred) {
2311                 return;
2312         }
2313
2314         nframes64_t fade_length;
2315
2316         nframes64_t const pos = adjusted_current_frame (event);
2317
2318         boost::shared_ptr<Region> region = _primary->region ();
2319
2320         if (pos < (region->position() + 64)) {
2321                 fade_length = 64; // this should be a minimum defined somewhere
2322         } else if (pos > region->last_frame()) {
2323                 fade_length = region->length();
2324         } else {
2325                 fade_length = pos - region->position();
2326         }
2327
2328         _editor->begin_reversible_command (_("change fade in length"));
2329
2330         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2331
2332                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2333
2334                 if (!tmp) {
2335                         continue;
2336                 }
2337
2338                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2339                 XMLNode &before = alist->get_state();
2340
2341                 tmp->audio_region()->set_fade_in_length (fade_length);
2342                 tmp->audio_region()->set_fade_in_active (true);
2343
2344                 XMLNode &after = alist->get_state();
2345                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2346         }
2347
2348         _editor->commit_reversible_command ();
2349 }
2350
2351 void
2352 FadeInDrag::aborted ()
2353 {
2354         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2355                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2356
2357                 if (!tmp) {
2358                         continue;
2359                 }
2360
2361                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2362         }
2363 }
2364
2365 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2366         : RegionDrag (e, i, p, v)
2367 {
2368
2369 }
2370
2371 void
2372 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2373 {
2374         Drag::start_grab (event, cursor);
2375
2376         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2377         boost::shared_ptr<AudioRegion> r = a->audio_region ();
2378
2379         _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2380         _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2381 }
2382
2383 void
2384 FadeOutDrag::motion (GdkEvent* event, bool)
2385 {
2386         nframes64_t fade_length;
2387
2388         nframes64_t const pos = adjusted_current_frame (event);
2389
2390         boost::shared_ptr<Region> region = _primary->region ();
2391
2392         if (pos > (region->last_frame() - 64)) {
2393                 fade_length = 64; // this should really be a minimum fade defined somewhere
2394         }
2395         else if (pos < region->position()) {
2396                 fade_length = region->length();
2397         }
2398         else {
2399                 fade_length = region->last_frame() - pos;
2400         }
2401
2402         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2403
2404                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2405
2406                 if (!tmp) {
2407                         continue;
2408                 }
2409
2410                 tmp->reset_fade_out_shape_width (fade_length);
2411         }
2412
2413         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2414 }
2415
2416 void
2417 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2418 {
2419         if (!movement_occurred) {
2420                 return;
2421         }
2422
2423         nframes64_t fade_length;
2424
2425         nframes64_t const pos = adjusted_current_frame (event);
2426
2427         boost::shared_ptr<Region> region = _primary->region ();
2428
2429         if (pos > (region->last_frame() - 64)) {
2430                 fade_length = 64; // this should really be a minimum fade defined somewhere
2431         }
2432         else if (pos < region->position()) {
2433                 fade_length = region->length();
2434         }
2435         else {
2436                 fade_length = region->last_frame() - pos;
2437         }
2438
2439         _editor->begin_reversible_command (_("change fade out length"));
2440
2441         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2442
2443                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2444
2445                 if (!tmp) {
2446                         continue;
2447                 }
2448
2449                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2450                 XMLNode &before = alist->get_state();
2451
2452                 tmp->audio_region()->set_fade_out_length (fade_length);
2453                 tmp->audio_region()->set_fade_out_active (true);
2454
2455                 XMLNode &after = alist->get_state();
2456                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2457         }
2458
2459         _editor->commit_reversible_command ();
2460 }
2461
2462 void
2463 FadeOutDrag::aborted ()
2464 {
2465         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2466                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2467
2468                 if (!tmp) {
2469                         continue;
2470                 }
2471
2472                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2473         }
2474 }
2475
2476 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2477         : Drag (e, i)
2478 {
2479         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2480         assert (_marker);
2481
2482         _points.push_back (Gnome::Art::Point (0, 0));
2483         _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2484
2485         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2486         _line->property_width_pixels() = 1;
2487         _line->property_points () = _points;
2488         _line->hide ();
2489
2490         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2491 }
2492
2493 MarkerDrag::~MarkerDrag ()
2494 {
2495         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2496                 delete *i;
2497         }
2498 }
2499
2500 void
2501 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2502 {
2503         Drag::start_grab (event, cursor);
2504
2505         bool is_start;
2506
2507         Location *location = _editor->find_location_from_marker (_marker, is_start);
2508         _editor->_dragging_edit_point = true;
2509
2510         _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2511
2512         update_item (location);
2513
2514         // _drag_line->show();
2515         // _line->raise_to_top();
2516
2517         if (is_start) {
2518                 _editor->show_verbose_time_cursor (location->start(), 10);
2519         } else {
2520                 _editor->show_verbose_time_cursor (location->end(), 10);
2521         }
2522
2523         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2524
2525         switch (op) {
2526         case Selection::Toggle:
2527                 _editor->selection->toggle (_marker);
2528                 break;
2529         case Selection::Set:
2530                 if (!_editor->selection->selected (_marker)) {
2531                         _editor->selection->set (_marker);
2532                 }
2533                 break;
2534         case Selection::Extend:
2535         {
2536                 Locations::LocationList ll;
2537                 list<Marker*> to_add;
2538                 nframes64_t s, e;
2539                 _editor->selection->markers.range (s, e);
2540                 s = min (_marker->position(), s);
2541                 e = max (_marker->position(), e);
2542                 s = min (s, e);
2543                 e = max (s, e);
2544                 if (e < max_frames) {
2545                         ++e;
2546                 }
2547                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2548                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2549                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2550                         if (lm) {
2551                                 if (lm->start) {
2552                                         to_add.push_back (lm->start);
2553                                 }
2554                                 if (lm->end) {
2555                                         to_add.push_back (lm->end);
2556                                 }
2557                         }
2558                 }
2559                 if (!to_add.empty()) {
2560                         _editor->selection->add (to_add);
2561                 }
2562                 break;
2563         }
2564         case Selection::Add:
2565                 _editor->selection->add (_marker);
2566                 break;
2567         }
2568
2569         /* Set up copies for us to manipulate during the drag */
2570
2571         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2572                 Location* l = _editor->find_location_from_marker (*i, is_start);
2573                 _copied_locations.push_back (new Location (*l));
2574         }
2575 }
2576
2577 void
2578 MarkerDrag::motion (GdkEvent* event, bool)
2579 {
2580         nframes64_t f_delta = 0;
2581         bool is_start;
2582         bool move_both = false;
2583         Marker* marker;
2584         Location *real_location;
2585         Location *copy_location = 0;
2586
2587         nframes64_t const newframe = adjusted_current_frame (event);
2588
2589         nframes64_t next = newframe;
2590
2591         if (newframe == last_pointer_frame()) {
2592                 return;
2593         }
2594
2595         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2596                 move_both = true;
2597         }
2598
2599         MarkerSelection::iterator i;
2600         list<Location*>::iterator x;
2601
2602         /* find the marker we're dragging, and compute the delta */
2603
2604         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2605              x != _copied_locations.end() && i != _editor->selection->markers.end();
2606              ++i, ++x) {
2607
2608                 copy_location = *x;
2609                 marker = *i;
2610
2611                 if (marker == _marker) {
2612
2613                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2614                                 /* que pasa ?? */
2615                                 return;
2616                         }
2617
2618                         if (real_location->is_mark()) {
2619                                 f_delta = newframe - copy_location->start();
2620                         } else {
2621
2622
2623                                 switch (marker->type()) {
2624                                 case Marker::Start:
2625                                 case Marker::LoopStart:
2626                                 case Marker::PunchIn:
2627                                         f_delta = newframe - copy_location->start();
2628                                         break;
2629
2630                                 case Marker::End:
2631                                 case Marker::LoopEnd:
2632                                 case Marker::PunchOut:
2633                                         f_delta = newframe - copy_location->end();
2634                                         break;
2635                                 default:
2636                                         /* what kind of marker is this ? */
2637                                         return;
2638                                 }
2639                         }
2640                         break;
2641                 }
2642         }
2643
2644         if (i == _editor->selection->markers.end()) {
2645                 /* hmm, impossible - we didn't find the dragged marker */
2646                 return;
2647         }
2648
2649         /* now move them all */
2650
2651         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2652              x != _copied_locations.end() && i != _editor->selection->markers.end();
2653              ++i, ++x) {
2654
2655                 copy_location = *x;
2656                 marker = *i;
2657
2658                 /* call this to find out if its the start or end */
2659
2660                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2661                         continue;
2662                 }
2663
2664                 if (real_location->locked()) {
2665                         continue;
2666                 }
2667
2668                 if (copy_location->is_mark()) {
2669
2670                         /* now move it */
2671
2672                         copy_location->set_start (copy_location->start() + f_delta);
2673
2674                 } else {
2675
2676                         nframes64_t new_start = copy_location->start() + f_delta;
2677                         nframes64_t new_end = copy_location->end() + f_delta;
2678
2679                         if (is_start) { // start-of-range marker
2680
2681                                 if (move_both) {
2682                                         copy_location->set_start (new_start);
2683                                         copy_location->set_end (new_end);
2684                                 } else  if (new_start < copy_location->end()) {
2685                                         copy_location->set_start (new_start);
2686                                 } else {
2687                                         _editor->snap_to (next, 1, true);
2688                                         copy_location->set_end (next);
2689                                         copy_location->set_start (newframe);
2690                                 }
2691
2692                         } else { // end marker
2693
2694                                 if (move_both) {
2695                                         copy_location->set_end (new_end);
2696                                         copy_location->set_start (new_start);
2697                                 } else if (new_end > copy_location->start()) {
2698                                         copy_location->set_end (new_end);
2699                                 } else if (newframe > 0) {
2700                                         _editor->snap_to (next, -1, true);
2701                                         copy_location->set_start (next);
2702                                         copy_location->set_end (newframe);
2703                                 }
2704                         }
2705                 }
2706
2707                 update_item (copy_location);
2708
2709                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2710
2711                 if (lm) {
2712                         lm->set_position (copy_location->start(), copy_location->end());
2713                 }
2714         }
2715
2716         assert (!_copied_locations.empty());
2717
2718         _editor->show_verbose_time_cursor (newframe, 10);
2719
2720 #ifdef GTKOSX
2721         _editor->update_canvas_now ();
2722 #endif
2723 }
2724
2725 void
2726 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2727 {
2728         if (!movement_occurred) {
2729
2730                 /* just a click, do nothing but finish
2731                    off the selection process
2732                 */
2733
2734                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2735
2736                 switch (op) {
2737                 case Selection::Set:
2738                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2739                                 _editor->selection->set (_marker);
2740                         }
2741                         break;
2742
2743                 case Selection::Toggle:
2744                 case Selection::Extend:
2745                 case Selection::Add:
2746                         break;
2747                 }
2748
2749                 return;
2750         }
2751
2752         _editor->_dragging_edit_point = false;
2753
2754         _editor->begin_reversible_command ( _("move marker") );
2755         XMLNode &before = _editor->session()->locations()->get_state();
2756
2757         MarkerSelection::iterator i;
2758         list<Location*>::iterator x;
2759         bool is_start;
2760
2761         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2762              x != _copied_locations.end() && i != _editor->selection->markers.end();
2763              ++i, ++x) {
2764
2765                 Location * location = _editor->find_location_from_marker (*i, is_start);
2766
2767                 if (location) {
2768
2769                         if (location->locked()) {
2770                                 return;
2771                         }
2772
2773                         if (location->is_mark()) {
2774                                 location->set_start ((*x)->start());
2775                         } else {
2776                                 location->set ((*x)->start(), (*x)->end());
2777                         }
2778                 }
2779         }
2780
2781         XMLNode &after = _editor->session()->locations()->get_state();
2782         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2783         _editor->commit_reversible_command ();
2784
2785         _line->hide();
2786 }
2787
2788 void
2789 MarkerDrag::aborted ()
2790 {
2791         /* XXX: TODO */
2792 }
2793
2794 void
2795 MarkerDrag::update_item (Location* location)
2796 {
2797         double const x1 = _editor->frame_to_pixel (location->start());
2798
2799         _points.front().set_x(x1);
2800         _points.back().set_x(x1);
2801         _line->property_points() = _points;
2802 }
2803
2804 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2805         : Drag (e, i),
2806           _cumulative_x_drag (0),
2807           _cumulative_y_drag (0)
2808 {
2809         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2810         assert (_point);
2811 }
2812
2813
2814 void
2815 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2816 {
2817         Drag::start_grab (event, _editor->fader_cursor);
2818
2819         // start the grab at the center of the control point so
2820         // the point doesn't 'jump' to the mouse after the first drag
2821         _fixed_grab_x = _point->get_x();
2822         _fixed_grab_y = _point->get_y();
2823
2824         float const fraction = 1 - (_point->get_y() / _point->line().height());
2825
2826         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2827
2828         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2829                                             event->button.x + 10, event->button.y + 10);
2830
2831         _editor->show_verbose_canvas_cursor ();
2832 }
2833
2834 void
2835 ControlPointDrag::motion (GdkEvent* event, bool)
2836 {
2837         double dx = _drags->current_pointer_x() - last_pointer_x();
2838         double dy = _drags->current_pointer_y() - last_pointer_y();
2839
2840         if (event->button.state & Keyboard::SecondaryModifier) {
2841                 dx *= 0.1;
2842                 dy *= 0.1;
2843         }
2844
2845         /* coordinate in pixels relative to the start of the region (for region-based automation)
2846            or track (for track-based automation) */
2847         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2848         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2849
2850         // calculate zero crossing point. back off by .01 to stay on the
2851         // positive side of zero
2852         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2853
2854         // make sure we hit zero when passing through
2855         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2856                 cy = zero_gain_y;
2857         }
2858
2859         if (_x_constrained) {
2860                 cx = _fixed_grab_x;
2861         }
2862         if (_y_constrained) {
2863                 cy = _fixed_grab_y;
2864         }
2865
2866         _cumulative_x_drag = cx - _fixed_grab_x;
2867         _cumulative_y_drag = cy - _fixed_grab_y;
2868
2869         cx = max (0.0, cx);
2870         cy = max (0.0, cy);
2871         cy = min ((double) _point->line().height(), cy);
2872
2873         framepos_t cx_frames = _editor->unit_to_frame (cx);
2874         
2875         if (!_x_constrained) {
2876                 _editor->snap_to_with_modifier (cx_frames, event);
2877         }
2878
2879         cx_frames = min (cx_frames, _point->line().maximum_time());
2880
2881         float const fraction = 1.0 - (cy / _point->line().height());
2882
2883         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2884
2885         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2886
2887         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2888 }
2889
2890 void
2891 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2892 {
2893         if (!movement_occurred) {
2894
2895                 /* just a click */
2896
2897                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2898                         _editor->reset_point_selection ();
2899                 }
2900
2901         } else {
2902                 motion (event, false);
2903         }
2904         _point->line().end_drag ();
2905 }
2906
2907 void
2908 ControlPointDrag::aborted ()
2909 {
2910         _point->line().reset ();
2911 }
2912
2913 bool
2914 ControlPointDrag::active (Editing::MouseMode m)
2915 {
2916         if (m == Editing::MouseGain) {
2917                 /* always active in mouse gain */
2918                 return true;
2919         }
2920
2921         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2922         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2923 }
2924
2925 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2926         : Drag (e, i),
2927           _line (0),
2928           _cumulative_y_drag (0)
2929 {
2930
2931 }
2932 void
2933 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2934 {
2935         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2936         assert (_line);
2937
2938         _item = &_line->grab_item ();
2939
2940         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2941            origin, and ditto for y.
2942         */
2943
2944         double cx = event->button.x;
2945         double cy = event->button.y;
2946
2947         _line->parent_group().w2i (cx, cy);
2948
2949         nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2950
2951         uint32_t before;
2952         uint32_t after;
2953         
2954         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2955                 /* no adjacent points */
2956                 return;
2957         }
2958
2959         Drag::start_grab (event, _editor->fader_cursor);
2960
2961         /* store grab start in parent frame */
2962
2963         _fixed_grab_x = cx;
2964         _fixed_grab_y = cy;
2965
2966         double fraction = 1.0 - (cy / _line->height());
2967
2968         _line->start_drag_line (before, after, fraction);
2969
2970         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2971                                             event->button.x + 10, event->button.y + 10);
2972
2973         _editor->show_verbose_canvas_cursor ();
2974 }
2975
2976 void
2977 LineDrag::motion (GdkEvent* event, bool)
2978 {
2979         double dy = _drags->current_pointer_y() - last_pointer_y();
2980
2981         if (event->button.state & Keyboard::SecondaryModifier) {
2982                 dy *= 0.1;
2983         }
2984
2985         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2986
2987         _cumulative_y_drag = cy - _fixed_grab_y;
2988
2989         cy = max (0.0, cy);
2990         cy = min ((double) _line->height(), cy);
2991
2992         double const fraction = 1.0 - (cy / _line->height());
2993
2994         bool push;
2995
2996         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2997                 push = false;
2998         } else {
2999                 push = true;
3000         }
3001
3002         /* we are ignoring x position for this drag, so we can just pass in anything */
3003         _line->drag_motion (0, fraction, true, push);
3004
3005         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
3006 }
3007
3008 void
3009 LineDrag::finished (GdkEvent* event, bool)
3010 {
3011         motion (event, false);
3012         _line->end_drag ();
3013 }
3014
3015 void
3016 LineDrag::aborted ()
3017 {
3018         _line->reset ();
3019 }
3020
3021 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3022         : Drag (e, i),
3023           _line (0),
3024           _cumulative_x_drag (0)
3025 {
3026
3027 }
3028 void
3029 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3030 {
3031   
3032         Drag::start_grab (event);
3033         
3034         _line = reinterpret_cast<SimpleLine*> (_item);
3035         assert (_line);
3036
3037         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3038
3039         double cx = event->button.x;
3040         double cy = event->button.y;
3041
3042         _item->property_parent().get_value()->w2i(cx, cy);
3043
3044         /* store grab start in parent frame */
3045         _region_view_grab_x = cx;
3046         
3047         _before = _line->property_x1();
3048         
3049         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3050                 
3051         _max_x = _editor->frame_to_pixel(_arv->get_duration());
3052 }
3053
3054 void
3055 FeatureLineDrag::motion (GdkEvent* event, bool)
3056 {
3057         double dx = _drags->current_pointer_x() - last_pointer_x();
3058         
3059         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3060         
3061         _cumulative_x_drag += dx;
3062                 
3063         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3064         
3065         if (cx > _max_x){
3066                 cx = _max_x;
3067         }
3068         else if(cx < 0){
3069                 cx = 0;
3070         }
3071         
3072         _line->property_x1() = cx; 
3073         _line->property_x2() = cx;
3074
3075         _before = _line->property_x1();
3076 }
3077
3078 void
3079 FeatureLineDrag::finished (GdkEvent* event, bool)
3080 {
3081         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3082         _arv->update_transient(_before, _line->property_x1());
3083 }
3084
3085 void
3086 FeatureLineDrag::aborted ()
3087 {
3088         //_line->reset ();
3089 }
3090
3091 void
3092 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3093 {
3094         Drag::start_grab (event);
3095         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3096 }
3097
3098 void
3099 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3100 {
3101         nframes64_t start;
3102         nframes64_t end;
3103         double y1;
3104         double y2;
3105
3106         nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3107
3108         nframes64_t grab = grab_frame ();
3109         if (Config->get_rubberbanding_snaps_to_grid ()) {
3110                 _editor->snap_to_with_modifier (grab, event);
3111         }
3112
3113         /* base start and end on initial click position */
3114
3115         if (pf < grab) {
3116                 start = pf;
3117                 end = grab;
3118         } else {
3119                 end = pf;
3120                 start = grab;
3121         }
3122
3123         if (_drags->current_pointer_y() < grab_y()) {
3124                 y1 = _drags->current_pointer_y();
3125                 y2 = grab_y();
3126         } else {
3127                 y2 = _drags->current_pointer_y();
3128                 y1 = grab_y();
3129         }
3130
3131
3132         if (start != end || y1 != y2) {
3133
3134                 double x1 = _editor->frame_to_pixel (start);
3135                 double x2 = _editor->frame_to_pixel (end);
3136
3137                 _editor->rubberband_rect->property_x1() = x1;
3138                 _editor->rubberband_rect->property_y1() = y1;
3139                 _editor->rubberband_rect->property_x2() = x2;
3140                 _editor->rubberband_rect->property_y2() = y2;
3141
3142                 _editor->rubberband_rect->show();
3143                 _editor->rubberband_rect->raise_to_top();
3144
3145                 _editor->show_verbose_time_cursor (pf, 10);
3146         }
3147 }
3148
3149 void
3150 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3151 {
3152         if (movement_occurred) {
3153
3154                 motion (event, false);
3155
3156                 double y1,y2;
3157                 if (_drags->current_pointer_y() < grab_y()) {
3158                         y1 = _drags->current_pointer_y();
3159                         y2 = grab_y();
3160                 } else {
3161                         y2 = _drags->current_pointer_y();
3162                         y1 = grab_y();
3163                 }
3164
3165
3166                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3167                 bool committed;
3168
3169                 _editor->begin_reversible_command (_("rubberband selection"));
3170
3171                 if (grab_frame() < last_pointer_frame()) {
3172                         committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3173                 } else {
3174                         committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3175                 }
3176
3177                 if (!committed) {
3178                         _editor->commit_reversible_command ();
3179                 }
3180
3181         } else {
3182                 if (!getenv("ARDOUR_SAE")) {
3183                         _editor->selection->clear_tracks();
3184                 }
3185                 _editor->selection->clear_regions();
3186                 _editor->selection->clear_points ();
3187                 _editor->selection->clear_lines ();
3188         }
3189
3190         _editor->rubberband_rect->hide();
3191 }
3192
3193 void
3194 RubberbandSelectDrag::aborted ()
3195 {
3196         _editor->rubberband_rect->hide ();
3197 }
3198
3199 void
3200 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3201 {
3202         Drag::start_grab (event);
3203
3204         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3205 }
3206
3207 void
3208 TimeFXDrag::motion (GdkEvent* event, bool)
3209 {
3210         RegionView* rv = _primary;
3211
3212         nframes64_t const pf = adjusted_current_frame (event);
3213
3214         if (pf > rv->region()->position()) {
3215                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3216         }
3217
3218         _editor->show_verbose_time_cursor (pf, 10);
3219 }
3220
3221 void
3222 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3223 {
3224         _primary->get_time_axis_view().hide_timestretch ();
3225
3226         if (!movement_occurred) {
3227                 return;
3228         }
3229
3230         if (last_pointer_frame() < _primary->region()->position()) {
3231                 /* backwards drag of the left edge - not usable */
3232                 return;
3233         }
3234
3235         nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3236
3237         float percentage = (double) newlen / (double) _primary->region()->length();
3238
3239 #ifndef USE_RUBBERBAND
3240         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3241         if (_primary->region()->data_type() == DataType::AUDIO) {
3242                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3243         }
3244 #endif
3245
3246         _editor->begin_reversible_command (_("timestretch"));
3247
3248         // XXX how do timeFX on multiple regions ?
3249
3250         RegionSelection rs;
3251         rs.add (_primary);
3252
3253         if (_editor->time_stretch (rs, percentage) == -1) {
3254                 error << _("An error occurred while executing time stretch operation") << endmsg;
3255         }
3256 }
3257
3258 void
3259 TimeFXDrag::aborted ()
3260 {
3261         _primary->get_time_axis_view().hide_timestretch ();
3262 }
3263
3264
3265 void
3266 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3267 {
3268         Drag::start_grab (event);
3269 }
3270
3271 void
3272 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3273 {
3274         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3275 }
3276
3277 void
3278 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3279 {
3280         if (movement_occurred && _editor->session()) {
3281                 /* make sure we stop */
3282                 _editor->session()->request_transport_speed (0.0);
3283         }
3284 }
3285
3286 void
3287 ScrubDrag::aborted ()
3288 {
3289         /* XXX: TODO */
3290 }
3291
3292 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3293         : Drag (e, i)
3294         , _operation (o)
3295         , _copy (false)
3296         , _original_pointer_time_axis (-1)
3297         , _last_pointer_time_axis (-1)
3298 {
3299
3300 }
3301
3302 void
3303 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3304 {
3305         nframes64_t start = 0;
3306         nframes64_t end = 0;
3307
3308         if (_editor->session() == 0) {
3309                 return;
3310         }
3311
3312         Gdk::Cursor* cursor = 0;
3313
3314         switch (_operation) {
3315         case CreateSelection:
3316                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3317                         _copy = true;
3318                 } else {
3319                         _copy = false;
3320                 }
3321                 cursor = _editor->selector_cursor;
3322                 Drag::start_grab (event, cursor);
3323                 break;
3324
3325         case SelectionStartTrim:
3326                 if (_editor->clicked_axisview) {
3327                         _editor->clicked_axisview->order_selection_trims (_item, true);
3328                 }
3329                 Drag::start_grab (event, _editor->left_side_trim_cursor);
3330                 start = _editor->selection->time[_editor->clicked_selection].start;
3331                 _pointer_frame_offset = grab_frame() - start;
3332                 break;
3333
3334         case SelectionEndTrim:
3335                 if (_editor->clicked_axisview) {
3336                         _editor->clicked_axisview->order_selection_trims (_item, false);
3337                 }
3338                 Drag::start_grab (event, _editor->right_side_trim_cursor);
3339                 end = _editor->selection->time[_editor->clicked_selection].end;
3340                 _pointer_frame_offset = grab_frame() - end;
3341                 break;
3342
3343         case SelectionMove:
3344                 start = _editor->selection->time[_editor->clicked_selection].start;
3345                 Drag::start_grab (event, cursor);
3346                 _pointer_frame_offset = grab_frame() - start;
3347                 break;
3348         }
3349
3350         if (_operation == SelectionMove) {
3351                 _editor->show_verbose_time_cursor (start, 10);
3352         } else {
3353                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3354         }
3355
3356         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3357 }
3358
3359 void
3360 SelectionDrag::motion (GdkEvent* event, bool first_move)
3361 {
3362         nframes64_t start = 0;
3363         nframes64_t end = 0;
3364         nframes64_t length;
3365
3366         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3367         if (pending_time_axis.first == 0) {
3368                 return;
3369         }
3370         
3371         nframes64_t const pending_position = adjusted_current_frame (event);
3372
3373         /* only alter selection if things have changed */
3374
3375         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3376                 return;
3377         }
3378
3379         switch (_operation) {
3380         case CreateSelection:
3381         {
3382                 nframes64_t grab = grab_frame ();
3383
3384                 if (first_move) {
3385                         _editor->snap_to (grab);
3386                 }
3387
3388                 if (pending_position < grab_frame()) {
3389                         start = pending_position;
3390                         end = grab;
3391                 } else {
3392                         end = pending_position;
3393                         start = grab;
3394                 }
3395
3396                 /* first drag: Either add to the selection
3397                    or create a new selection
3398                 */
3399
3400                 if (first_move) {
3401
3402                         if (_copy) {
3403                                 /* adding to the selection */
3404                                 _editor->selection->add (_editor->clicked_axisview);
3405                                 _editor->clicked_selection = _editor->selection->add (start, end);
3406                                 _copy = false;
3407                         } else {
3408                                 /* new selection */
3409
3410                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3411                                         _editor->selection->set (_editor->clicked_axisview);
3412                                 }
3413                                 
3414                                 _editor->clicked_selection = _editor->selection->set (start, end);
3415                         }
3416                 }
3417
3418                 /* select the track that we're in */
3419                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3420                         _editor->selection->add (pending_time_axis.first);
3421                         _added_time_axes.push_back (pending_time_axis.first);
3422                 }
3423
3424                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3425                    tracks that we selected in the first place.
3426                 */
3427                 
3428                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3429                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3430
3431                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3432                 while (i != _added_time_axes.end()) {
3433
3434                         list<TimeAxisView*>::iterator tmp = i;
3435                         ++tmp;
3436                         
3437                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3438                                 _editor->selection->remove (*i);
3439                                 _added_time_axes.remove (*i);
3440                         }
3441
3442                         i = tmp;
3443                 }
3444
3445         }
3446         break;
3447
3448         case SelectionStartTrim:
3449
3450                 start = _editor->selection->time[_editor->clicked_selection].start;
3451                 end = _editor->selection->time[_editor->clicked_selection].end;
3452
3453                 if (pending_position > end) {
3454                         start = end;
3455                 } else {
3456                         start = pending_position;
3457                 }
3458                 break;
3459
3460         case SelectionEndTrim:
3461
3462                 start = _editor->selection->time[_editor->clicked_selection].start;
3463                 end = _editor->selection->time[_editor->clicked_selection].end;
3464
3465                 if (pending_position < start) {
3466                         end = start;
3467                 } else {
3468                         end = pending_position;
3469                 }
3470
3471                 break;
3472
3473         case SelectionMove:
3474
3475                 start = _editor->selection->time[_editor->clicked_selection].start;
3476                 end = _editor->selection->time[_editor->clicked_selection].end;
3477
3478                 length = end - start;
3479
3480                 start = pending_position;
3481                 _editor->snap_to (start);
3482
3483                 end = start + length;
3484
3485                 break;
3486         }
3487
3488         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3489                 _editor->start_canvas_autoscroll (1, 0);
3490         }
3491
3492         if (start != end) {
3493                 _editor->selection->replace (_editor->clicked_selection, start, end);
3494         }
3495
3496         if (_operation == SelectionMove) {
3497                 _editor->show_verbose_time_cursor(start, 10);
3498         } else {
3499                 _editor->show_verbose_time_cursor(pending_position, 10);
3500         }
3501 }
3502
3503 void
3504 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3505 {
3506         Session* s = _editor->session();
3507
3508         if (movement_occurred) {
3509                 motion (event, false);
3510                 /* XXX this is not object-oriented programming at all. ick */
3511                 if (_editor->selection->time.consolidate()) {
3512                         _editor->selection->TimeChanged ();
3513                 }
3514
3515                 /* XXX what if its a music time selection? */
3516                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3517                         s->request_play_range (&_editor->selection->time, true);
3518                 }
3519
3520
3521         } else {
3522                 /* just a click, no pointer movement.*/
3523
3524                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3525                         _editor->selection->clear_time();
3526                 }
3527
3528                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3529                         _editor->selection->set (_editor->clicked_axisview);
3530                 }
3531                 
3532                 if (s && s->get_play_range () && s->transport_rolling()) {
3533                         s->request_stop (false, false);
3534                 }
3535
3536         }
3537
3538         _editor->stop_canvas_autoscroll ();
3539 }
3540
3541 void
3542 SelectionDrag::aborted ()
3543 {
3544         /* XXX: TODO */
3545 }
3546
3547 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3548         : Drag (e, i),
3549           _operation (o),
3550           _copy (false)
3551 {
3552         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, 
3553                                                    physical_screen_height (_editor->get_window()));
3554         _drag_rect->hide ();
3555
3556         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3557         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3558 }
3559
3560 void
3561 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3562 {
3563         if (_editor->session() == 0) {
3564                 return;
3565         }
3566
3567         Gdk::Cursor* cursor = 0;
3568
3569         if (!_editor->temp_location) {
3570                 _editor->temp_location = new Location (*_editor->session());
3571         }
3572
3573         switch (_operation) {
3574         case CreateRangeMarker:
3575         case CreateTransportMarker:
3576         case CreateCDMarker:
3577
3578                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3579                         _copy = true;
3580                 } else {
3581                         _copy = false;
3582                 }
3583                 cursor = _editor->selector_cursor;
3584                 break;
3585         }
3586
3587         Drag::start_grab (event, cursor);
3588
3589         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3590 }
3591
3592 void
3593 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3594 {
3595         nframes64_t start = 0;
3596         nframes64_t end = 0;
3597         ArdourCanvas::SimpleRect *crect;
3598
3599         switch (_operation) {
3600         case CreateRangeMarker:
3601                 crect = _editor->range_bar_drag_rect;
3602                 break;
3603         case CreateTransportMarker:
3604                 crect = _editor->transport_bar_drag_rect;
3605                 break;
3606         case CreateCDMarker:
3607                 crect = _editor->cd_marker_bar_drag_rect;
3608                 break;
3609         default:
3610                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3611                 return;
3612                 break;
3613         }
3614
3615         nframes64_t const pf = adjusted_current_frame (event);
3616
3617         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3618                 nframes64_t grab = grab_frame ();
3619                 _editor->snap_to (grab);
3620                 
3621                 if (pf < grab_frame()) {
3622                         start = pf;
3623                         end = grab;
3624                 } else {
3625                         end = pf;
3626                         start = grab;
3627                 }
3628
3629                 /* first drag: Either add to the selection
3630                    or create a new selection.
3631                 */
3632
3633                 if (first_move) {
3634
3635                         _editor->temp_location->set (start, end);
3636
3637                         crect->show ();
3638
3639                         update_item (_editor->temp_location);
3640                         _drag_rect->show();
3641                         //_drag_rect->raise_to_top();
3642
3643                 }
3644         }
3645
3646         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3647                 _editor->start_canvas_autoscroll (1, 0);
3648         }
3649
3650         if (start != end) {
3651                 _editor->temp_location->set (start, end);
3652
3653                 double x1 = _editor->frame_to_pixel (start);
3654                 double x2 = _editor->frame_to_pixel (end);
3655                 crect->property_x1() = x1;
3656                 crect->property_x2() = x2;
3657
3658                 update_item (_editor->temp_location);
3659         }
3660
3661         _editor->show_verbose_time_cursor (pf, 10);
3662
3663 }
3664
3665 void
3666 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3667 {
3668         Location * newloc = 0;
3669         string rangename;
3670         int flags;
3671
3672         if (movement_occurred) {
3673                 motion (event, false);
3674                 _drag_rect->hide();
3675
3676                 switch (_operation) {
3677                 case CreateRangeMarker:
3678                 case CreateCDMarker:
3679                     {
3680                         _editor->begin_reversible_command (_("new range marker"));
3681                         XMLNode &before = _editor->session()->locations()->get_state();
3682                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3683                         if (_operation == CreateCDMarker) {
3684                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3685                                 _editor->cd_marker_bar_drag_rect->hide();
3686                         }
3687                         else {
3688                                 flags = Location::IsRangeMarker;
3689                                 _editor->range_bar_drag_rect->hide();
3690                         }
3691                         newloc = new Location (
3692                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3693                                 );
3694                         
3695                         _editor->session()->locations()->add (newloc, true);
3696                         XMLNode &after = _editor->session()->locations()->get_state();
3697                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3698                         _editor->commit_reversible_command ();
3699                         break;
3700                     }
3701
3702                 case CreateTransportMarker:
3703                         // popup menu to pick loop or punch
3704                         _editor->new_transport_marker_context_menu (&event->button, _item);
3705                         break;
3706                 }
3707         } else {
3708                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3709
3710                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3711
3712                         nframes64_t start;
3713                         nframes64_t end;
3714
3715                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3716
3717                         if (end == max_frames) {
3718                                 end = _editor->session()->current_end_frame ();
3719                         }
3720
3721                         if (start == max_frames) {
3722                                 start = _editor->session()->current_start_frame ();
3723                         }
3724
3725                         switch (_editor->mouse_mode) {
3726                         case MouseObject:
3727                                 /* find the two markers on either side and then make the selection from it */
3728                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3729                                 break;
3730
3731                         case MouseRange:
3732                                 /* find the two markers on either side of the click and make the range out of it */
3733                                 _editor->selection->set (start, end);
3734                                 break;
3735
3736                         default:
3737                                 break;
3738                         }
3739                 }
3740         }
3741
3742         _editor->stop_canvas_autoscroll ();
3743 }
3744
3745 void
3746 RangeMarkerBarDrag::aborted ()
3747 {
3748         /* XXX: TODO */
3749 }
3750
3751 void
3752 RangeMarkerBarDrag::update_item (Location* location)
3753 {
3754         double const x1 = _editor->frame_to_pixel (location->start());
3755         double const x2 = _editor->frame_to_pixel (location->end());
3756
3757         _drag_rect->property_x1() = x1;
3758         _drag_rect->property_x2() = x2;
3759 }
3760
3761 void
3762 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3763 {
3764         Drag::start_grab (event, _editor->zoom_cursor);
3765         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3766 }
3767
3768 void
3769 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3770 {
3771         nframes64_t start;
3772         nframes64_t end;
3773
3774         nframes64_t const pf = adjusted_current_frame (event);
3775
3776         nframes64_t grab = grab_frame ();
3777         _editor->snap_to_with_modifier (grab, event);
3778
3779         /* base start and end on initial click position */
3780         if (pf < grab) {
3781                 start = pf;
3782                 end = grab;
3783         } else {
3784                 end = pf;
3785                 start = grab;
3786         }
3787
3788         if (start != end) {
3789
3790                 if (first_move) {
3791                         _editor->zoom_rect->show();
3792                         _editor->zoom_rect->raise_to_top();
3793                 }
3794
3795                 _editor->reposition_zoom_rect(start, end);
3796
3797                 _editor->show_verbose_time_cursor (pf, 10);
3798         }
3799 }
3800
3801 void
3802 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3803 {
3804         if (movement_occurred) {
3805                 motion (event, false);
3806
3807                 if (grab_frame() < last_pointer_frame()) {
3808                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3809                 } else {
3810                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3811                 }
3812         } else {
3813                 _editor->temporal_zoom_to_frame (false, grab_frame());
3814                 /*
3815                 temporal_zoom_step (false);
3816                 center_screen (grab_frame());
3817                 */
3818         }
3819
3820         _editor->zoom_rect->hide();
3821 }
3822
3823 void
3824 MouseZoomDrag::aborted ()
3825 {
3826         _editor->zoom_rect->hide ();
3827 }
3828
3829 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3830         : Drag (e, i)
3831         , _cumulative_dx (0)
3832         , _cumulative_dy (0)
3833 {
3834         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3835         _region = &_primary->region_view ();
3836         _note_height = _region->midi_stream_view()->note_height ();
3837 }
3838
3839 void
3840 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3841 {
3842         Drag::start_grab (event);
3843
3844         if (!(_was_selected = _primary->selected())) {
3845
3846                 /* tertiary-click means extend selection - we'll do that on button release,
3847                    so don't add it here, because otherwise we make it hard to figure
3848                    out the "extend-to" range.
3849                 */
3850
3851                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3852
3853                 if (!extend) {
3854                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3855
3856                         if (add) {
3857                                 _region->note_selected (_primary, true);
3858                         } else {
3859                                 _region->unique_select (_primary);
3860                         }
3861                 }
3862         }
3863 }
3864
3865 /** @return Current total drag x change in frames */
3866 frameoffset_t
3867 NoteDrag::total_dx () const
3868 {
3869         /* dx in frames */
3870         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3871
3872         /* primary note time */
3873         frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3874         
3875         /* new time of the primary note relative to the region position */
3876         frameoffset_t const st = n + dx;
3877
3878         /* snap and return corresponding delta */
3879         return _region->snap_frame_to_frame (st) - n;
3880 }
3881
3882 /** @return Current total drag y change in notes */
3883 int8_t
3884 NoteDrag::total_dy () const
3885 {
3886         /* this is `backwards' to make increasing note number go in the right direction */
3887         double const dy = _drags->current_pointer_y() - grab_y();
3888
3889         /* dy in notes */
3890         int8_t ndy = 0;
3891
3892         if (abs (dy) >= _note_height) {
3893                 if (dy > 0) {
3894                         ndy = (int8_t) ceil (dy / _note_height / 2.0);
3895                 } else {
3896                         ndy = (int8_t) floor (dy / _note_height / 2.0);
3897                 }
3898         }
3899
3900         return ndy;
3901 }
3902         
3903
3904 void
3905 NoteDrag::motion (GdkEvent *, bool)
3906 {
3907         /* Total change in x and y since the start of the drag */
3908         frameoffset_t const dx = total_dx ();
3909         int8_t const dy = total_dy ();
3910
3911         /* Now work out what we have to do to the note canvas items to set this new drag delta */
3912         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3913         double const tdy = dy * _note_height - _cumulative_dy;
3914
3915         if (tdx || tdy) {
3916                 _region->move_selection (tdx, tdy);
3917                 _cumulative_dx += tdx;
3918                 _cumulative_dy += tdy;
3919
3920                 char buf[12];
3921                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3922                           (int) floor (_primary->note()->note() + dy));
3923                 
3924                 _editor->show_verbose_canvas_cursor_with (buf);
3925         }
3926 }
3927
3928 void
3929 NoteDrag::finished (GdkEvent* ev, bool moved)
3930 {
3931         if (!moved) {
3932                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3933
3934                         if (_was_selected) {
3935                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3936                                 if (add) {
3937                                         _region->note_deselected (_primary);
3938                                 }
3939                         } else {
3940                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3941                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3942
3943                                 if (!extend && !add && _region->selection_size() > 1) {
3944                                         _region->unique_select (_primary);
3945                                 } else if (extend) {
3946                                         _region->note_selected (_primary, true, true);
3947                                 } else {
3948                                         /* it was added during button press */
3949                                 }
3950                         }
3951                 }
3952         } else {
3953                 _region->note_dropped (_primary, total_dx(), - total_dy());
3954         }
3955 }
3956
3957 void
3958 NoteDrag::aborted ()
3959 {
3960         /* XXX: TODO */
3961 }
3962
3963 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3964         : Drag (e, i)
3965         , _ranges (r)
3966         , _nothing_to_drag (false)
3967 {
3968         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3969         assert (_atav);
3970
3971         _line = _atav->line ();
3972 }
3973
3974 void
3975 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3976 {
3977         Drag::start_grab (event, cursor);
3978
3979         list<ControlPoint*> points;
3980
3981         XMLNode* state = &_line->get_state ();
3982         
3983         if (_ranges.empty()) {
3984                 
3985                 uint32_t const N = _line->npoints ();
3986                 for (uint32_t i = 0; i < N; ++i) {
3987                         points.push_back (_line->nth (i));
3988                 }
3989                 
3990         } else {
3991
3992                 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3993                 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3994
3995                         /* fade into and out of the region that we're dragging;
3996                            64 samples length plucked out of thin air.
3997                         */
3998                         nframes64_t const h = (j->start + j->end) / 2;
3999                         nframes64_t a = j->start + 64;
4000                         if (a > h) {
4001                                 a = h;
4002                         }
4003                         nframes64_t b = j->end - 64;
4004                         if (b < h) {
4005                                 b = h;
4006                         }
4007                         
4008                         the_list->add (j->start, the_list->eval (j->start));
4009                         _line->add_always_in_view (j->start);
4010                         the_list->add (a, the_list->eval (a));
4011                         _line->add_always_in_view (a);
4012                         the_list->add (b, the_list->eval (b));
4013                         _line->add_always_in_view (b);
4014                         the_list->add (j->end, the_list->eval (j->end));
4015                         _line->add_always_in_view (j->end);
4016                 }
4017
4018                 uint32_t const N = _line->npoints ();
4019                 for (uint32_t i = 0; i < N; ++i) {
4020
4021                         ControlPoint* p = _line->nth (i);
4022
4023                         list<AudioRange>::const_iterator j = _ranges.begin ();
4024                         while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
4025                                 ++j;
4026                         }
4027
4028                         if (j != _ranges.end()) {
4029                                 points.push_back (p);
4030                         }
4031                 }
4032         }
4033
4034         if (points.empty()) {
4035                 _nothing_to_drag = true;
4036                 return;
4037         }
4038
4039         _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
4040 }
4041
4042 void
4043 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
4044 {
4045         if (_nothing_to_drag) {
4046                 return;
4047         }
4048         
4049         float const f = 1 - (_drags->current_pointer_y() / _line->height());
4050
4051         /* we are ignoring x position for this drag, so we can just pass in anything */
4052         _line->drag_motion (0, f, true, false);
4053 }
4054
4055 void
4056 AutomationRangeDrag::finished (GdkEvent* event, bool)
4057 {
4058         if (_nothing_to_drag) {
4059                 return;
4060         }
4061         
4062         motion (event, false);
4063         _line->end_drag ();
4064         _line->clear_always_in_view ();
4065 }
4066
4067 void
4068 AutomationRangeDrag::aborted ()
4069 {
4070         _line->clear_always_in_view ();
4071         _line->reset ();
4072 }
4073
4074 DraggingView::DraggingView (RegionView* v)
4075         : view (v)
4076 {
4077         initial_y = v->get_canvas_group()->property_y ();
4078         initial_playlist = v->region()->playlist ();
4079 }