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