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