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