Pull dragging code out of the Editor class into its own hierarchy.
[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 "pbd/memento_command.h"
21 #include "pbd/basename.h"
22 #include "ardour/diskstream.h"
23 #include "ardour/dB.h"
24 #include "ardour/region_factory.h"
25 #include "ardour/midi_diskstream.h"
26 #include "editor.h"
27 #include "i18n.h"
28 #include "keyboard.h"
29 #include "audio_region_view.h"
30 #include "midi_region_view.h"
31 #include "ardour_ui.h"
32 #include "control_point.h"
33 #include "utils.h"
34 #include "region_gain_line.h"
35 #include "editor_drag.h"
36
37 using namespace std;
38 using namespace ARDOUR;
39 using namespace PBD;
40 using namespace sigc;
41 using namespace Gtk;
42 using namespace Editing;
43
44 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
45
46 Drag::Drag (Editor* e, ArdourCanvas::Item* i) :
47         _editor (e),
48         _item (i),
49         _pointer_frame_offset (0),
50         _grab_frame (0),
51         _last_pointer_frame (0),
52         _current_pointer_frame (0),
53         _copy (false)
54 {
55
56 }
57
58 void
59 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
60 {
61         _item->ungrab (0);
62         _item = new_item;
63
64         if (cursor == 0) {
65                 cursor = _editor->which_grabber_cursor ();
66         }
67
68         _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
69 }
70
71 void
72 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
73 {
74         if (cursor == 0) {
75                 cursor = _editor->which_grabber_cursor ();
76         }
77
78         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
79
80         if (Keyboard::is_button2_event (&event->button)) {
81                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
82                         _y_constrained = true;
83                         _x_constrained = false;
84                 } else {
85                         _y_constrained = false;
86                         _x_constrained = true;
87                 }
88         } else {
89                 _x_constrained = false;
90                 _y_constrained = false;
91         }
92
93         _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
94         _last_pointer_frame = _grab_frame;
95         _current_pointer_frame = _grab_frame;
96         _current_pointer_x = _grab_x;
97         _current_pointer_y = _grab_y;
98         _last_pointer_x = _current_pointer_x;
99         _last_pointer_y = _current_pointer_y;
100         _first_move = true;
101         _move_threshold_passed = false;
102         _want_move_threshold = false;
103
104         _original_x = 0;
105         _original_y = 0;
106         _item->i2w (_original_x, _original_y);
107
108         _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
109                               *cursor,
110                               event->button.time);
111
112         if (_editor->session && _editor->session->transport_rolling()) {
113                 _was_rolling = true;
114         } else {
115                 _was_rolling = false;
116         }
117
118         switch (_editor->snap_type) {
119         case SnapToRegionStart:
120         case SnapToRegionEnd:
121         case SnapToRegionSync:
122         case SnapToRegionBoundary:
123                 _editor->build_region_boundary_cache ();
124                 break;
125         default:
126                 break;
127         }
128 }
129
130 /*  @param event GDK event, or 0 */
131 bool
132 Drag::end_grab (GdkEvent* event)
133 {
134         _ending = true;
135         
136         bool did_drag = false;
137
138         _editor->stop_canvas_autoscroll ();
139
140         _item->ungrab (event ? event->button.time : 0);
141
142         if (event) {
143                 _last_pointer_x = _current_pointer_x;
144                 _last_pointer_y = _current_pointer_y;
145                 finished (event);
146         }
147
148         did_drag = !_first_move;
149
150         _editor->hide_verbose_canvas_cursor();
151
152         _ending = false;
153
154         update_selection ();
155         
156         return did_drag;
157 }
158
159 nframes64_t
160 Drag::adjusted_current_frame () const
161 {
162         if (_current_pointer_frame > _pointer_frame_offset) {
163                 return _current_pointer_frame - _pointer_frame_offset;
164         }
165
166         return 0;
167 }
168
169 bool
170 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
171 {
172         _last_pointer_x = _current_pointer_x;
173         _last_pointer_y = _current_pointer_y;
174         _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
175
176         if (!from_autoscroll && !_move_threshold_passed) {
177                 bool xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
178                 bool yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
179                         
180                 _move_threshold_passed = (xp || yp);
181                         
182                 if (_want_move_threshold && _move_threshold_passed) {
183                         _grab_frame = _current_pointer_frame;
184                         _grab_x = _current_pointer_x;
185                         _grab_y = _current_pointer_y;
186                         _last_pointer_frame = _grab_frame;
187                         _pointer_frame_offset = _grab_frame - _last_frame_position;
188                 }
189         }
190
191         if (active (_editor->mouse_mode)) {
192
193                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
194                         if (!from_autoscroll) {
195                                 _editor->maybe_autoscroll (&event->motion);
196                         }
197
198                         motion (event);
199                         return true;
200                 }
201         }
202
203         return false;
204 }
205
206
207 void
208 Drag::break_drag ()
209 {
210         _editor->stop_canvas_autoscroll ();
211         _editor->hide_verbose_canvas_cursor ();
212
213         if (_item) {
214                 _item->ungrab (0);
215
216                 /* put it back where it came from */
217
218                 double cxw, cyw;
219                 cxw = 0;
220                 cyw = 0;
221                 _item->i2w (cxw, cyw);
222                 _item->move (_original_x - cxw, _original_y - cyw);
223         }
224 }
225
226
227 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
228         : Drag (e, i),
229           _primary (p),
230           _views (v)
231 {
232         RegionView::RegionViewGoingAway.connect (mem_fun (*this, &RegionDrag::region_going_away));
233 }
234
235 void
236 RegionDrag::region_going_away (RegionView* v)
237 {
238         _views.remove (v);
239 }
240
241 void
242 RegionDrag::update_selection ()
243 {
244         list<Selectable*> s;
245         copy (_views.begin(), _views.end(), back_inserter (s));
246         _editor->selection->set (s);
247 }
248
249 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
250         : RegionDrag (e, i, p, v),
251           _brushing (b)
252 {
253         _want_move_threshold = true;
254         _copy = c;
255         
256         _source_trackview = &_primary->get_time_axis_view ();
257         _source_layer = _primary->region()->layer ();
258         _dest_trackview = _source_trackview;
259         _dest_layer = _source_layer;
260
261         double speed = 1;
262         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (_source_trackview);
263         if (tv && tv->is_track()) {
264                 speed = tv->get_diskstream()->speed ();
265         }
266
267         _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
268 }
269
270 void
271 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
272 {
273         Drag::start_grab (event);
274         
275         _pointer_frame_offset = _grab_frame - _last_frame_position;
276         _editor->show_verbose_time_cursor (_last_frame_position, 10);
277 }
278
279 void
280 RegionMoveDrag::motion (GdkEvent* event)
281 {
282         double x_delta;
283         double y_delta = 0;
284         nframes64_t pending_region_position = 0;
285         int32_t pointer_order_span = 0, canvas_pointer_order_span = 0;
286         int32_t pointer_layer_span = 0;
287         
288         bool clamp_y_axis = false;
289         vector<int32_t>::iterator j;
290
291         if (_copy && _move_threshold_passed && _want_move_threshold) {
292                 copy_regions (event);
293                 _want_move_threshold = false; // don't copy again
294         }
295         
296         /* *pointer* variables reflect things about the pointer; as we may be moving
297            multiple regions, much detail must be computed per-region */
298
299         /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
300            current_pointer_layer the current layer on that TimeAxisView */
301         RouteTimeAxisView* current_pointer_view;
302         layer_t current_pointer_layer;
303         if (!check_possible (&current_pointer_view, &current_pointer_layer)) {
304                 return;
305         }
306
307         /* TimeAxisView that we were pointing at last time we entered this method */
308         TimeAxisView const * const last_pointer_view = _dest_trackview;
309         /* the order of the track that we were pointing at last time we entered this method */
310         int32_t const last_pointer_order = last_pointer_view->order ();
311         /* the layer that we were pointing at last time we entered this method */
312         layer_t const last_pointer_layer = _dest_layer;
313
314         /************************************************************
315              Y DELTA COMPUTATION
316         ************************************************************/   
317
318         /* Height of TimeAxisViews, indexed by order */
319         /* XXX: hard-coded limit of TimeAxisViews */
320         vector<int32_t> height_list (512);
321         
322         if (_brushing) {
323                 clamp_y_axis = true;
324                 pointer_order_span = 0;
325                 goto y_axis_done;
326         }
327
328         /* the change in track order between this callback and the last */
329         pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
330         /* the change in layer between this callback and the last;
331            only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
332         pointer_layer_span = last_pointer_layer - current_pointer_layer;
333
334         if (pointer_order_span != 0) {
335
336                 int32_t children = 0;
337                 /* XXX: hard-coded limit of tracks */
338                 bitset <512> tracks (0x00);
339
340                 int visible_y_high;
341                 int visible_y_low;
342                 _editor->visible_order_range (&visible_y_low, &visible_y_high);
343                 
344                 /* get a bitmask representing the visible tracks */
345
346                 for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
347                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
348                         TimeAxisView::Children children_list;
349               
350                         /* zeroes are audio/MIDI tracks. ones are other types. */
351               
352                         if (!rtv->hidden()) {
353                                 
354                                 if (!rtv->is_track()) {
355                                         /* not an audio nor MIDI track */
356                                         tracks = tracks |= (0x01 << rtv->order());
357                                 }
358         
359                                 height_list[rtv->order()] = (*i)->current_height();
360                                 children = 1;
361
362                                 if ((children_list = rtv->get_child_list()).size() > 0) {
363                                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
364                                                 tracks = tracks |= (0x01 << (rtv->order() + children));
365                                                 height_list[rtv->order() + children] = (*j)->current_height();
366                                                 children++;     
367                                         }
368                                 }
369                         }
370                 }
371                 
372                 /* find the actual pointer span, in terms of the number of visible tracks;
373                    to do this, we reduce |pointer_order_span| by the number of hidden tracks
374                    over the span */
375
376                 canvas_pointer_order_span = pointer_order_span;
377                 if (last_pointer_view->order() >= current_pointer_view->order()) {
378                         for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
379                                 if (height_list[y] == 0) {
380                                         canvas_pointer_order_span--;
381                                 }
382                         }
383                 } else {
384                         for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
385                                 if (height_list[y] == 0) {
386                                         canvas_pointer_order_span++;
387                                 }
388                         }
389                 }
390
391                 for (list<RegionView*>::const_iterator i = _editor->selection->regions.by_layer().begin(); i != _editor->selection->regions.by_layer().end(); ++i) {
392                         
393                         RegionView* rv = (*i);
394
395                         if (rv->region()->locked()) {
396                                 continue;
397                         }
398
399                         double ix1, ix2, iy1, iy2;
400                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
401                         rv->get_canvas_frame()->i2w (ix1, iy1);
402                         iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
403
404                         /* get the new trackview for this particular region */
405                         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
406                         assert (tvp.first);
407                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
408
409                         /* I know this method has a slightly excessive argument list, but I think
410                            it's nice to separate the code out all the same, since it has such a
411                            simple result, and it makes it clear that there are no other
412                            side-effects.
413                         */
414
415                         /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
416                            as surely this is a per-region thing... */
417                         
418                         clamp_y_axis = y_movement_disallowed (
419                                 rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high,
420                                 tracks, height_list
421                                 );
422
423                         if (clamp_y_axis) {
424                                 break;
425                         }
426                 }
427
428         } else if (_dest_trackview == current_pointer_view) {
429
430                 if (current_pointer_layer == last_pointer_layer) {
431                         /* No movement; clamp */
432                         clamp_y_axis = true;
433                 } 
434         }
435
436   y_axis_done:
437         if (!clamp_y_axis) {
438                 _dest_trackview = current_pointer_view;
439                 _dest_layer = current_pointer_layer;
440         }
441           
442         /************************************************************
443             X DELTA COMPUTATION
444         ************************************************************/
445
446         /* compute the amount of pointer motion in frames, and where
447            the region would be if we moved it by that much.
448         */
449         if (_move_threshold_passed) {
450
451                 if (_current_pointer_frame >= _pointer_frame_offset) {
452
453                         nframes64_t sync_frame;
454                         nframes64_t sync_offset;
455                         int32_t sync_dir;
456
457                         pending_region_position = _current_pointer_frame - _pointer_frame_offset;
458
459                         sync_offset = _primary->region()->sync_offset (sync_dir);
460
461                         /* we don't handle a sync point that lies before zero.
462                          */
463                         if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
464                                 sync_frame = pending_region_position + (sync_dir*sync_offset);
465
466                                 /* we snap if the snap modifier is not enabled.
467                                  */
468             
469                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
470                                         _editor->snap_to (sync_frame);  
471                                 }
472             
473                                 pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
474
475                         } else {
476                                 pending_region_position = _last_frame_position;
477                         }
478             
479                 } else {
480                         pending_region_position = 0;
481                 }
482           
483                 if (pending_region_position > max_frames - _primary->region()->length()) {
484                         pending_region_position = _last_frame_position;
485                 }
486
487                 bool x_move_allowed;
488                 
489                 if (Config->get_edit_mode() == Lock) {
490                         if (_copy) {
491                                 x_move_allowed = !_x_constrained;
492                         } else {
493                                 /* in locked edit mode, reverse the usual meaning of _x_constrained */
494                                 x_move_allowed = _x_constrained;
495                         }
496                 } else {
497                         x_move_allowed = !_x_constrained;
498                 }
499
500                 if (( pending_region_position != _last_frame_position) && x_move_allowed ) {
501
502                         /* now compute the canvas unit distance we need to move the regionview
503                            to make it appear at the new location.
504                         */
505
506                         if (pending_region_position > _last_frame_position) {
507                                 x_delta = ((double) (pending_region_position - _last_frame_position) / _editor->frames_per_unit);
508                         } else {
509                                 x_delta = -((double) (_last_frame_position - pending_region_position) / _editor->frames_per_unit);
510                                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
511
512                                         RegionView* rv = (*i);
513
514                                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
515         
516                                         double ix1, ix2, iy1, iy2;
517                                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
518                                         rv->get_canvas_frame()->i2w (ix1, iy1);
519
520                                         if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
521                                                 x_delta = 0;
522                                                 pending_region_position = _last_frame_position;
523                                                 break;
524                                         }
525                                 }
526
527                         }
528                 
529                         _last_frame_position = pending_region_position;
530
531                 } else {
532                         x_delta = 0;
533                 }
534
535         } else {
536                 /* threshold not passed */
537
538                 x_delta = 0;
539         }
540         
541         /*************************************************************
542             PREPARE TO MOVE
543         ************************************************************/
544
545         if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
546                 /* haven't reached next snap point, and we're not switching
547                    trackviews nor layers. nothing to do.
548                 */
549                 return;
550         }
551
552         /*************************************************************
553             MOTION                                                                    
554         ************************************************************/
555         bool do_move = true;
556         if (_first_move) {
557                 if (!_move_threshold_passed) {
558                         do_move = false;
559                 }
560         }
561
562         if (do_move) {
563
564                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
565                 
566                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
567
568                         RegionView* rv = (*i);
569
570                         if (rv->region()->locked()) {
571                                 continue;
572                         }
573
574                         /* here we are calculating the y distance from the
575                            top of the first track view to the top of the region
576                            area of the track view that we're working on */
577
578                         /* this x value is just a dummy value so that we have something
579                            to pass to i2w () */
580
581                         double ix1 = 0;
582
583                         /* distance from the top of this track view to the region area
584                            of our track view is always 1 */
585                         
586                         double iy1 = 1;
587
588                         /* convert to world coordinates, ie distance from the top of
589                            the ruler section */
590                         
591                         rv->get_canvas_frame()->i2w (ix1, iy1);
592
593                         /* compensate for the ruler section and the vertical scrollbar position */
594                         iy1 += _editor->get_trackview_group_vertical_offset ();
595
596                         if (_first_move) {
597
598                                 // hide any dependent views 
599         
600                                 rv->get_time_axis_view().hide_dependent_views (*rv);
601
602                                 /* 
603                                    reparent to a non scrolling group so that we can keep the 
604                                    region selection above all time axis views.
605                                    reparenting means we have to move the rv as the two 
606                                    parent groups have different coordinates.
607                                 */
608
609                                 rv->get_canvas_group()->property_y() = iy1 - 1;
610                                 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
611
612                                 rv->fake_set_opaque (true);
613                         }
614
615                         /* current view for this particular region */
616                         pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
617                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
618
619                         if (pointer_order_span != 0 && !clamp_y_axis) {
620
621                                 /* INTER-TRACK MOVEMENT */
622
623                                 /* move through the height list to the track that the region is currently on */
624                                 vector<int32_t>::iterator j = height_list.begin ();
625                                 int32_t x = 0;
626                                 while (j != height_list.end () && x != rtv->order ()) {
627                                         ++x;
628                                         ++j;
629                                 }
630
631                                 y_delta = 0;
632                                 int32_t temp_pointer_order_span = canvas_pointer_order_span;
633
634                                 if (j != height_list.end ()) {
635
636                                         /* Account for layers in the original and
637                                            destination tracks.  If we're moving around in layers we assume
638                                            that only one track is involved, so it's ok to use *pointer*
639                                            variables here. */
640
641                                         StreamView* lv = last_pointer_view->view ();
642                                         assert (lv);
643
644                                         /* move to the top of the last trackview */
645                                         if (lv->layer_display () == Stacked) {
646                                                 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
647                                         }
648                                         
649                                         StreamView* cv = current_pointer_view->view ();
650                                         assert (cv);
651
652                                         /* move to the right layer on the current trackview */
653                                         if (cv->layer_display () == Stacked) {
654                                                 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
655                                         }
656
657                                         /* And for being on a non-topmost layer on the new
658                                            track */
659
660                                         while (temp_pointer_order_span > 0) {
661                                                 /* we're moving up canvas-wise,
662                                                    so we need to find the next track height
663                                                 */
664                                                 if (j != height_list.begin()) {           
665                                                         j--;
666                                                 }
667
668                                                 if (x != last_pointer_order) {
669                                                         if ((*j) == 0) {
670                                                                 ++temp_pointer_order_span;
671                                                         }
672                                                 }
673
674                                                 y_delta -= (*j);
675                                                 temp_pointer_order_span--;
676                                         }
677
678                                         while (temp_pointer_order_span < 0) {
679
680                                                 y_delta += (*j);
681
682                                                 if (x != last_pointer_order) {
683                                                         if ((*j) == 0) {
684                                                                 --temp_pointer_order_span;
685                                                         }
686                                                 }
687                                                 
688                                                 if (j != height_list.end()) {                 
689                                                         j++;
690                                                 }
691
692                                                 temp_pointer_order_span++;
693                                         }
694
695                                         
696                                         /* find out where we'll be when we move and set height accordingly */
697
698                                         pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
699                                         RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
700                                         rv->set_height (temp_rtv->view()->child_height());
701                                 
702                                         /* if you un-comment the following, the region colours will follow
703                                            the track colours whilst dragging; personally
704                                            i think this can confuse things, but never mind.
705                                         */
706                                         
707                                         //const GdkColor& col (temp_rtv->view->get_region_color());
708                                         //rv->set_color (const_cast<GdkColor&>(col));
709                                 }
710                         }
711
712                         if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
713
714                                 /* INTER-LAYER MOVEMENT in the same track */
715                                 y_delta = rtv->view()->child_height () * pointer_layer_span;
716                         }
717
718
719                         if (_brushing) {
720                                 _editor->mouse_brush_insert_region (rv, pending_region_position);
721                         } else {
722                                 rv->move (x_delta, y_delta);
723                         }
724
725                 } /* foreach region */
726
727         } /* if do_move */
728
729         if (_first_move && _move_threshold_passed) {
730                 _editor->cursor_group->raise_to_top();
731                 _first_move = false;
732         }
733
734         if (x_delta != 0 && !_brushing) {
735                 _editor->show_verbose_time_cursor (_last_frame_position, 10);
736         }
737
738
739 void
740 RegionMoveDrag::finished (GdkEvent* event)
741 {
742         bool nocommit = true;
743         vector<RegionView*> copies;
744         RouteTimeAxisView* source_tv;
745         boost::shared_ptr<Diskstream> ds;
746         boost::shared_ptr<Playlist> from_playlist;
747         list<RegionView*> new_views;
748         typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
749         PlaylistSet modified_playlists;
750         PlaylistSet frozen_playlists;
751         list <sigc::connection> modified_playlist_connections;
752         pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
753         nframes64_t drag_delta;
754         bool changed_tracks, changed_position;
755         pair<TimeAxisView*, int> tvp;
756         map<RegionView*, RouteTimeAxisView*> final;
757
758         /* _first_move is set to false if the regionview has been moved in the 
759            motion handler. 
760         */
761
762         if (_first_move) {
763                 /* just a click */
764                 goto out;
765         }
766
767         nocommit = false;
768
769         if (Config->get_edit_mode() == Splice && !_editor->pre_drag_region_selection.empty()) {
770                 _editor->selection->set (_editor->pre_drag_region_selection);
771                 _editor->pre_drag_region_selection.clear ();
772         }
773
774         if (_brushing) {
775                 /* all changes were made during motion event handlers */
776                 
777                 if (_copy) {
778                         for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
779                                 copies.push_back (*i);
780                         }
781                 }
782
783                 goto out;
784         }
785
786         char* op_string;
787
788         /* reverse this here so that we have the correct logic to finalize
789            the drag.
790         */
791         
792         if (Config->get_edit_mode() == Lock && !_copy) {
793                 _x_constrained = !_x_constrained;
794         }
795
796         if (_copy) {
797                 if (_x_constrained) {
798                         op_string = _("fixed time region copy");
799                 } else {
800                         op_string = _("region copy");
801                 } 
802         } else {
803                 if (_x_constrained) {
804                         op_string = _("fixed time region drag");
805                 } else {
806                         op_string = _("region drag");
807                 }
808         }
809
810         _editor->begin_reversible_command (op_string);
811         changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
812         tvp = _editor->trackview_by_y_position (_current_pointer_y);
813         changed_tracks = (tvp.first != &_primary->get_time_axis_view());
814
815         drag_delta = _primary->region()->position() - _last_frame_position;
816
817         _editor->track_canvas->update_now ();
818
819         /* make a list of where each region ended up */
820         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
821
822                 double ix1, ix2, iy1, iy2;
823                 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
824                 (*i)->get_canvas_frame()->i2w (ix1, iy1);
825                 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
826
827                 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
828                 final[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
829         }
830
831         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
832
833                 RegionView* rv = (*i);
834                 RouteTimeAxisView* dest_rtv = final[*i];
835
836                 nframes64_t where;
837
838                 if (rv->region()->locked()) {
839                         ++i;
840                         continue;
841                 }
842
843                 if (changed_position && !_x_constrained) {
844                         where = rv->region()->position() - drag_delta;
845                 } else {
846                         where = rv->region()->position();
847                 }
848                         
849                 boost::shared_ptr<Region> new_region;
850
851                 if (_copy) {
852                         /* we already made a copy */
853                         new_region = rv->region();
854
855                         /* undo the previous hide_dependent_views so that xfades don't
856                            disappear on copying regions 
857                         */
858                 
859                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
860                 
861                 } else if (changed_tracks && dest_rtv->playlist()) {
862                         new_region = RegionFactory::create (rv->region());
863                 }
864
865                 if (changed_tracks || _copy) {
866
867                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
868                         
869                         if (!to_playlist) {
870                                 ++i;
871                                 continue;
872                         }
873
874                         _editor->latest_regionviews.clear ();
875
876                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*_editor, &Editor::collect_new_region_view));
877                         
878                         insert_result = modified_playlists.insert (to_playlist);
879                         
880                         if (insert_result.second) {
881                                 _editor->session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
882                         }
883
884                         to_playlist->add_region (new_region, where);
885
886                         c.disconnect ();
887                                                               
888                         if (!_editor->latest_regionviews.empty()) {
889                                 // XXX why just the first one ? we only expect one
890                                 // commented out in nick_m's canvas reworking. is that intended?
891                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
892                                 new_views.push_back (_editor->latest_regionviews.front());
893                         }
894
895                 } else {
896                         /* 
897                            motion on the same track. plonk the previously reparented region 
898                            back to its original canvas group (its streamview).
899                            No need to do anything for copies as they are fake regions which will be deleted.
900                         */
901
902                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
903                         rv->get_canvas_group()->property_y() = 0;
904                   
905                         /* just change the model */
906                         
907                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
908
909                         insert_result = modified_playlists.insert (playlist);
910                         
911                         if (insert_result.second) {
912                                 _editor->session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
913                         }
914                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
915                         frozen_insert_result = frozen_playlists.insert(playlist);
916                         
917                         if (frozen_insert_result.second) {
918                                 playlist->freeze();
919                         }
920
921                         rv->region()->set_position (where, (void*) this);
922                 }
923
924                 if (changed_tracks && !_copy) {
925
926                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
927                            because we may have copied the region and it has not been attached to a playlist.
928                         */
929
930                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
931                         assert ((ds = source_tv->get_diskstream()));
932                         assert ((from_playlist = ds->playlist()));
933
934                         /* moved to a different audio track, without copying */
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             
942                         rv->hide_region_editor();
943                         rv->fake_set_opaque (false);
944                         
945                         /* remove the region from the old playlist */
946
947                         insert_result = modified_playlists.insert (from_playlist);
948                         
949                         if (insert_result.second) {
950                                 _editor->session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
951                         }
952         
953                         from_playlist->remove_region (rv->region());
954                         
955                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
956                            was selected in all of them, then removing it from a playlist will have removed all
957                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
958                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
959                            corresponding regionview, and the selection is now empty).
960
961                            this could have invalidated any and all iterators into the region selection.
962
963                            the heuristic we use here is: if the region selection is empty, break out of the loop
964                            here. if the region selection is not empty, then restart the loop because we know that
965                            we must have removed at least the region(view) we've just been working on as well as any
966                            that we processed on previous iterations.
967
968                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
969                            we can just iterate.
970                         */
971
972                         if (_views.empty()) {
973                                 break;
974                         } else { 
975                                 i = _views.begin();
976                         }
977
978                 } else {
979                         ++i;
980                 }
981                 
982                 if (_copy) {
983                         copies.push_back (rv);
984                 }
985         }
986         
987         if (new_views.empty()) {
988                 if (_copy) {
989                         /* the region(view)s that are being dragged around are copies and do not
990                            belong to any track. remove them from our list
991                         */
992                         _views.clear ();
993                 }
994         } else {
995                 _views = new_views;
996                 _primary = _views.front ();
997         }
998
999         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1000                 (*p)->thaw();
1001         }
1002                         
1003   out:
1004         if (!nocommit) {
1005                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
1006                         _editor->session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));     
1007                 }
1008
1009                 _editor->commit_reversible_command ();
1010         }
1011
1012         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1013                 delete *x;
1014         }
1015 }
1016         
1017
1018 void
1019 RegionMoveDrag::copy_regions (GdkEvent* event)
1020 {
1021         /* duplicate the regionview(s) and region(s) */
1022
1023         list<RegionView*> new_regionviews;
1024         
1025         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1026                 
1027                 RegionView* rv = (*i);
1028                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1029                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1030                 
1031                 const boost::shared_ptr<const Region> original = rv->region();
1032                 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1033
1034                 RegionView* nrv;
1035                 if (arv) {
1036                         boost::shared_ptr<AudioRegion> audioregion_copy
1037                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1038                         nrv = new AudioRegionView (*arv, audioregion_copy);
1039                 } else if (mrv) {
1040                         boost::shared_ptr<MidiRegion> midiregion_copy
1041                                 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1042                         nrv = new MidiRegionView (*mrv, midiregion_copy);
1043                 } else {
1044                         continue;
1045                 }
1046                 
1047                 nrv->get_canvas_group()->show ();
1048                 new_regionviews.push_back (nrv);
1049         }
1050         
1051         if (new_regionviews.empty()) {
1052                 return;
1053         }
1054         
1055         /* reflect the fact that we are dragging the copies */
1056         
1057         _primary = new_regionviews.front();
1058         _views = new_regionviews;
1059                 
1060         swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
1061         /* 
1062            sync the canvas to what we think is its current state 
1063            without it, the canvas seems to 
1064            "forget" to update properly after the upcoming reparent() 
1065            ..only if the mouse is in rapid motion at the time of the grab. 
1066            something to do with regionview creation raking so long?
1067         */
1068         _editor->track_canvas->update_now();
1069 }
1070
1071 bool
1072 RegionMoveDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1073 {
1074         /* Which trackview is this ? */
1075
1076         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1077         (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1078         (*layer) = tvp.second;
1079
1080         /* The region motion is only processed if the pointer is over
1081            an audio track.
1082         */
1083         
1084         if (!(*tv) || !(*tv)->is_track()) {
1085                 /* To make sure we hide the verbose canvas cursor when the mouse is 
1086                    not held over and audiotrack. 
1087                 */
1088                 _editor->hide_verbose_canvas_cursor ();
1089                 return false;
1090         }
1091
1092         return true;
1093 }
1094
1095 /** @param new_order New track order.
1096  *  @param old_order Old track order.
1097  *  @param visible_y_low Lowest visible order.
1098  *  @param visible_y_high Highest visible order.
1099  *  @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else.
1100  *  @param heigh_list Heights of tracks indexed by order.
1101  *  @return true if y movement should not happen, otherwise false.
1102  */
1103 bool
1104 RegionMoveDrag::y_movement_disallowed (
1105         int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high,
1106         bitset<512> const & tracks, vector<int32_t> const & height_list
1107         ) const
1108 {
1109         if (new_order != old_order) {
1110
1111                 /* this isn't the pointer track */      
1112
1113                 if (y_span > 0) {
1114
1115                         /* moving up the canvas */
1116                         if ( (new_order - y_span) >= visible_y_low) {
1117
1118                                 int32_t n = 0;
1119
1120                                 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1121                                 int32_t visible_tracks = 0;
1122                                 while (visible_tracks < y_span ) {
1123                                         visible_tracks++;
1124                                         while (height_list[new_order - (visible_tracks - n)] == 0) {
1125                                                 /* passing through a hidden track */
1126                                                 n--;
1127                                         }                 
1128                                 }
1129                  
1130                                 if (tracks[new_order - (y_span - n)] != 0x00) {
1131                                         /* moving to a non-track; disallow */
1132                                         return true;
1133                                 }
1134                                 
1135
1136                         } else {
1137                                 /* moving beyond the lowest visible track; disallow */
1138                                 return true;
1139                         }                 
1140                   
1141                 } else if (y_span < 0) {
1142
1143                         /* moving down the canvas */
1144                         if ((new_order - y_span) <= visible_y_high) {
1145
1146                                 int32_t visible_tracks = 0;
1147                                 int32_t n = 0;
1148                                 while (visible_tracks > y_span ) {
1149                                         visible_tracks--;
1150                       
1151                                         while (height_list[new_order - (visible_tracks - n)] == 0) {
1152                                                 /* passing through a hidden track */
1153                                                 n++;
1154                                         }                
1155                                 }
1156                                                 
1157                                 if (tracks[new_order - (y_span - n)] != 0x00) {
1158                                         /* moving to a non-track; disallow */
1159                                         return true;
1160                                 }
1161
1162                                 
1163                         } else {
1164
1165                                 /* moving beyond the highest visible track; disallow */
1166                                 return true;
1167                         }
1168                 }               
1169                 
1170         } else {
1171                 
1172                 /* this is the pointer's track */
1173                 
1174                 if ((new_order - y_span) > visible_y_high) {
1175                         /* we will overflow */
1176                         return true;
1177                 } else if ((new_order - y_span) < visible_y_low) {
1178                         /* we will overflow */
1179                         return true;
1180                 }
1181         }
1182
1183         return false;
1184 }
1185
1186 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1187         : RegionMoveDrag (e, i, p, v, false, false)
1188 {
1189
1190 }
1191
1192 struct RegionSelectionByPosition {
1193     bool operator() (RegionView*a, RegionView* b) {
1194             return a->region()->position () < b->region()->position();
1195     }
1196 };
1197
1198 void
1199 RegionSpliceDrag::motion (GdkEvent* event)
1200 {
1201         RouteTimeAxisView* tv;
1202         layer_t layer;
1203         
1204         if (!check_possible (&tv, &layer)) {
1205                 return;
1206         }
1207
1208         if (!_move_threshold_passed) {
1209                 return;
1210         }
1211
1212         int dir;
1213
1214         if (_current_pointer_x - _grab_x > 0) {
1215                 dir = 1;
1216         } else {
1217                 dir = -1;
1218         }
1219
1220         RegionSelection copy (_editor->selection->regions);
1221
1222         RegionSelectionByPosition cmp;
1223         copy.sort (cmp);
1224
1225         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1226
1227                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1228
1229                 if (!atv) {
1230                         continue;
1231                 }
1232
1233                 boost::shared_ptr<Playlist> playlist;
1234
1235                 if ((playlist = atv->playlist()) == 0) {
1236                         continue;
1237                 }
1238
1239                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1240                         continue;
1241                 } 
1242
1243                 if (dir > 0) {
1244                         if (_current_pointer_frame < (*i)->region()->last_frame() + 1) {
1245                                 continue;
1246                         }
1247                 } else {
1248                         if (_current_pointer_frame > (*i)->region()->first_frame()) {
1249                                 continue;
1250                         }
1251                 }
1252
1253                 
1254                 playlist->shuffle ((*i)->region(), dir);
1255
1256                 _grab_x = _current_pointer_x;
1257         }
1258 }
1259
1260 void
1261 RegionSpliceDrag::finished (GdkEvent* event)
1262 {
1263         
1264 }
1265
1266
1267 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1268         : Drag (e, i),
1269           _view (v)
1270 {
1271         
1272 }
1273
1274 void
1275 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1276 {
1277         _source_trackview = _view;
1278         _dest_trackview = _view;
1279         _dest_layer = _source_layer;
1280         
1281         Drag::start_grab (event);
1282 }
1283
1284
1285 void
1286 RegionCreateDrag::motion (GdkEvent* event)
1287 {
1288         if (_move_threshold_passed) {
1289                 if (_first_move) {
1290                         // TODO: create region-create-drag region view here
1291                         _first_move = false;
1292                 }
1293
1294                 // TODO: resize region-create-drag region view here
1295         }
1296
1297
1298 void
1299 RegionCreateDrag::finished (GdkEvent* event)
1300 {
1301         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1302         if (!mtv) {
1303                 return;
1304         }
1305
1306         const boost::shared_ptr<MidiDiskstream> diskstream =
1307                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
1308         
1309         if (!diskstream) {
1310                 warning << "Cannot create non-MIDI region" << endl;
1311                 return;
1312         }
1313
1314         if (_first_move) {
1315                 _editor->begin_reversible_command (_("create region"));
1316                 XMLNode &before = mtv->playlist()->get_state();
1317
1318                 nframes64_t start = _grab_frame;
1319                 _editor->snap_to (start, -1);
1320                 const Meter& m = _editor->session->tempo_map().meter_at(start);
1321                 const Tempo& t = _editor->session->tempo_map().tempo_at(start);
1322                 double length = floor (m.frames_per_bar(t, _editor->session->frame_rate()));
1323
1324                 boost::shared_ptr<Source> src = _editor->session->create_midi_source_for_session(*diskstream.get());
1325                                 
1326                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
1327                                              (RegionFactory::create(src, 0, (nframes_t) length, 
1328                                                                     PBD::basename_nosuffix(src->name()))), start);
1329                 XMLNode &after = mtv->playlist()->get_state();
1330                 _editor->session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
1331                 _editor->commit_reversible_command();
1332
1333         } else {
1334                 motion (event);
1335                 // TODO: create region-create-drag region here
1336         }
1337 }
1338
1339
1340
1341
1342 void
1343 RegionGainDrag::motion (GdkEvent* event)
1344 {
1345         if (_first_move && _move_threshold_passed) {
1346                 _first_move = false;
1347         }
1348 }
1349
1350 void
1351 RegionGainDrag::finished (GdkEvent *)
1352 {
1353
1354 }
1355
1356 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1357         : RegionDrag (e, i, p, v)
1358 {
1359
1360 }
1361
1362 void
1363 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1364 {
1365         double speed = 1.0;
1366         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1367         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1368
1369         if (tv && tv->is_track()) {
1370                 speed = tv->get_diskstream()->speed();
1371         }
1372         
1373         nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1374         nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1375         nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1376
1377         Drag::start_grab (event, _editor->trimmer_cursor);
1378         
1379         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1380                 _operation = ContentsTrim;
1381         } else {
1382                 /* These will get overridden for a point trim.*/
1383                 if (_current_pointer_frame < (region_start + region_length/2)) {
1384                         /* closer to start */
1385                         _operation = StartTrim;
1386                 } else if (_current_pointer_frame > (region_end - region_length/2)) {
1387                         /* closer to end */
1388                         _operation = EndTrim;
1389                 }
1390         }
1391
1392         switch (_operation) {
1393         case StartTrim:
1394                 _editor->show_verbose_time_cursor (region_start, 10);   
1395                 break;
1396         case EndTrim:
1397                 _editor->show_verbose_time_cursor (region_end, 10);     
1398                 break;
1399         case ContentsTrim:
1400                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
1401                 break;
1402         }
1403 }
1404
1405 void
1406 TrimDrag::motion (GdkEvent* event)
1407 {
1408         RegionView* rv = _primary;
1409         nframes64_t frame_delta = 0;
1410
1411         bool left_direction;
1412         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1413
1414         /* snap modifier works differently here..
1415            its' current state has to be passed to the 
1416            various trim functions in order to work properly 
1417         */ 
1418
1419         double speed = 1.0;
1420         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1421         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1422         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1423
1424         if (tv && tv->is_track()) {
1425                 speed = tv->get_diskstream()->speed();
1426         }
1427         
1428         if (_last_pointer_frame > _current_pointer_frame) {
1429                 left_direction = true;
1430         } else {
1431                 left_direction = false;
1432         }
1433
1434         if (obey_snap) {
1435                 _editor->snap_to (_current_pointer_frame);
1436         }
1437
1438         if (_current_pointer_frame == _last_pointer_frame) {
1439                 return;
1440         }
1441
1442         if (_first_move) {
1443         
1444                 string trim_type;
1445
1446                 switch (_operation) {
1447                 case StartTrim:
1448                         trim_type = "Region start trim";
1449                         break;
1450                 case EndTrim:
1451                         trim_type = "Region end trim";
1452                         break;
1453                 case ContentsTrim:
1454                         trim_type = "Region content trim";
1455                         break;
1456                 }
1457
1458                 _editor->begin_reversible_command (trim_type);
1459
1460                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1461                         (*i)->fake_set_opaque(false);
1462                         (*i)->region()->freeze ();
1463                 
1464                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1465
1466                         if (arv){
1467                                 arv->temporarily_hide_envelope ();
1468                         }
1469
1470                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1471                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1472
1473                         if (insert_result.second) {
1474                                 _editor->session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1475                                 pl->freeze();
1476                         }
1477                 }
1478         }
1479
1480         if (left_direction) {
1481                 frame_delta = (_last_pointer_frame - _current_pointer_frame);
1482         } else {
1483                 frame_delta = (_current_pointer_frame - _last_pointer_frame);
1484         }
1485
1486         bool non_overlap_trim = false;
1487
1488         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1489                 non_overlap_trim = true;
1490         }
1491
1492         switch (_operation) {           
1493         case StartTrim:
1494                 if ((left_direction == false) && (_current_pointer_frame <= rv->region()->first_frame()/speed)) {
1495                         break;
1496                 } else {
1497
1498                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1499                                 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1500                         }
1501                         break;
1502                 }
1503                 
1504         case EndTrim:
1505                 if ((left_direction == true) && (_current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
1506                         break;
1507                 } else {
1508
1509                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1510                                 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1511                         }
1512                         break;
1513                 }
1514                 
1515         case ContentsTrim:
1516                 {
1517                         bool swap_direction = false;
1518
1519                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1520                                 swap_direction = true;
1521                         }
1522                         
1523                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i)
1524                         {
1525                                 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1526                         }
1527                 }
1528                 break;
1529         }
1530
1531         switch (_operation) {
1532         case StartTrim:
1533                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);  
1534                 break;
1535         case EndTrim:
1536                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);        
1537                 break;
1538         case ContentsTrim:
1539                 _editor->show_verbose_time_cursor(_current_pointer_frame, 10);  
1540                 break;
1541         }
1542
1543         _last_pointer_frame = _current_pointer_frame;
1544         _first_move = false;
1545 }
1546
1547
1548 void
1549 TrimDrag::finished (GdkEvent* event)
1550 {
1551         if (!_first_move) {
1552                 motion (event);
1553                 
1554                 if (!_editor->selection->selected (_primary)) {
1555                         _editor->thaw_region_after_trim (*_primary);            
1556                 } else {
1557                         
1558                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1559                                 _editor->thaw_region_after_trim (**i);
1560                                 (*i)->fake_set_opaque (true);
1561                         }
1562                 }
1563                 
1564                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1565                         (*p)->thaw ();
1566                         _editor->session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1567                 }
1568                 
1569                 _editor->motion_frozen_playlists.clear ();
1570
1571                 _editor->commit_reversible_command();
1572         } else {
1573                 /* no mouse movement */
1574                 _editor->point_trim (event);
1575         }
1576 }
1577
1578 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1579         : Drag (e, i)
1580 {
1581         _copy = c;
1582
1583         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1584         assert (_marker);
1585 }
1586
1587 void
1588 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1589 {
1590         if (_copy) {
1591                 // create a dummy marker for visual representation of moving the copy.
1592                 // The actual copying is not done before we reach the finish callback.
1593                 char name[64];
1594                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1595                 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, 
1596                                                           *new MeterSection (_marker->meter()));
1597
1598                 _item = &new_marker->the_item ();
1599                 _marker = new_marker;
1600                 
1601         } else {
1602
1603                 MetricSection& section (_marker->meter());
1604                 
1605                 if (!section.movable()) {
1606                         return;
1607                 }
1608                 
1609         }
1610
1611         Drag::start_grab (event, cursor);
1612         
1613         _pointer_frame_offset = _grab_frame - _marker->meter().frame(); 
1614
1615         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1616 }
1617
1618 void
1619 MeterMarkerDrag::motion (GdkEvent* event)
1620 {
1621         nframes64_t adjusted_frame = adjusted_current_frame ();
1622         
1623         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1624                 _editor->snap_to (adjusted_frame);
1625         }
1626         
1627         if (adjusted_frame == _last_pointer_frame) {
1628                 return;
1629         }
1630
1631         _marker->set_position (adjusted_frame);
1632         
1633         _last_pointer_frame = adjusted_frame;
1634         _first_move = false;
1635
1636         _editor->show_verbose_time_cursor (adjusted_frame, 10);
1637 }
1638
1639 void
1640 MeterMarkerDrag::finished (GdkEvent* event)
1641 {
1642         if (_first_move) {
1643                 return;
1644         }
1645
1646         motion (event);
1647         
1648         BBT_Time when;
1649         
1650         TempoMap& map (_editor->session->tempo_map());
1651         map.bbt_time (_last_pointer_frame, when);
1652         
1653         if (_copy == true) {
1654                 _editor->begin_reversible_command (_("copy meter mark"));
1655                 XMLNode &before = map.get_state();
1656                 map.add_meter (_marker->meter(), when);
1657                 XMLNode &after = map.get_state();
1658                 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1659                 _editor->commit_reversible_command ();
1660
1661                 // delete the dummy marker we used for visual representation of copying.
1662                 // a new visual marker will show up automatically.
1663                 delete _marker;
1664         } else {
1665                 _editor->begin_reversible_command (_("move meter mark"));
1666                 XMLNode &before = map.get_state();
1667                 map.move_meter (_marker->meter(), when);
1668                 XMLNode &after = map.get_state();
1669                 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1670                 _editor->commit_reversible_command ();
1671         }
1672 }
1673
1674 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1675         : Drag (e, i)
1676 {
1677         _copy = c;
1678
1679         TempoMarker* _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1680         assert (_marker);
1681 }
1682
1683 void
1684 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1685 {
1686
1687         if (_copy) {
1688                 
1689                 // create a dummy marker for visual representation of moving the copy.
1690                 // The actual copying is not done before we reach the finish callback.
1691                 char name[64];
1692                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1693                 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, 
1694                                                           *new TempoSection (_marker->tempo()));
1695
1696                 _item = &new_marker->the_item ();
1697                 _marker = new_marker;
1698
1699         } else {
1700
1701                 MetricSection& section (_marker->tempo());
1702                 
1703                 if (!section.movable()) {
1704                         return;
1705                 }
1706         }
1707
1708         Drag::start_grab (event, cursor);
1709
1710         _pointer_frame_offset = _grab_frame - _marker->tempo().frame(); 
1711         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1712 }
1713
1714 void
1715 TempoMarkerDrag::motion (GdkEvent* event)
1716 {
1717         nframes64_t adjusted_frame = adjusted_current_frame ();
1718
1719         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1720                 _editor->snap_to (adjusted_frame);
1721         }
1722         
1723         if (adjusted_frame == _last_pointer_frame) {
1724                 return;
1725         }
1726
1727         /* OK, we've moved far enough to make it worth actually move the thing. */
1728                 
1729         _marker->set_position (adjusted_frame);
1730         
1731         _editor->show_verbose_time_cursor (adjusted_frame, 10);
1732
1733         _last_pointer_frame = adjusted_frame;
1734         _first_move = false;
1735 }
1736
1737 void
1738 TempoMarkerDrag::finished (GdkEvent* event)
1739 {
1740         if (_first_move) {
1741                 return;
1742         }
1743         
1744         motion (event);
1745         
1746         BBT_Time when;
1747         
1748         TempoMap& map (_editor->session->tempo_map());
1749         map.bbt_time (_last_pointer_frame, when);
1750
1751         if (_copy == true) {
1752                 _editor->begin_reversible_command (_("copy tempo mark"));
1753                 XMLNode &before = map.get_state();
1754                 map.add_tempo (_marker->tempo(), when);
1755                 XMLNode &after = map.get_state();
1756                 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1757                 _editor->commit_reversible_command ();
1758                 
1759                 // delete the dummy marker we used for visual representation of copying.
1760                 // a new visual marker will show up automatically.
1761                 delete _marker;
1762         } else {
1763                 _editor->begin_reversible_command (_("move tempo mark"));
1764                 XMLNode &before = map.get_state();
1765                 map.move_tempo (_marker->tempo(), when);
1766                 XMLNode &after = map.get_state();
1767                 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1768                 _editor->commit_reversible_command ();
1769         }
1770 }
1771
1772
1773 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1774         : Drag (e, i),
1775           _stop (s)
1776 {
1777         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1778         assert (_cursor);
1779 }
1780
1781 void
1782 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1783 {
1784         Drag::start_grab (event, c);
1785
1786         if (!_stop) {
1787                 
1788                 nframes64_t where = _editor->event_frame (event, 0, 0);
1789
1790                 _editor->snap_to (where);
1791                 _editor->playhead_cursor->set_position (where);
1792
1793         }
1794
1795         if (_cursor == _editor->playhead_cursor) {
1796                 _editor->_dragging_playhead = true;
1797
1798                 if (_editor->session && _was_rolling && _stop) {
1799                         _editor->session->request_stop ();
1800                 }
1801
1802                 if (_editor->session && _editor->session->is_auditioning()) {
1803                         _editor->session->cancel_audition ();
1804                 }
1805         }
1806
1807         _pointer_frame_offset = _grab_frame - _cursor->current_frame;   
1808         
1809         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1810 }
1811
1812 void
1813 CursorDrag::motion (GdkEvent* event)
1814 {
1815         nframes64_t adjusted_frame = adjusted_current_frame ();
1816         
1817         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1818                 if (_cursor == _editor->playhead_cursor) {
1819                         _editor->snap_to (adjusted_frame);
1820                 }
1821         }
1822         
1823         if (adjusted_frame == _last_pointer_frame) {
1824                 return;
1825         }
1826
1827         _cursor->set_position (adjusted_frame);
1828
1829         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1830
1831 #ifdef GTKOSX
1832         _editor->track_canvas->update_now ();
1833 #endif
1834         _editor->UpdateAllTransportClocks (_cursor->current_frame);
1835
1836         _last_pointer_frame = adjusted_frame;
1837         _first_move = false;
1838 }
1839
1840 void
1841 CursorDrag::finished (GdkEvent* event)
1842 {
1843         _editor->_dragging_playhead = false;
1844
1845         if (_first_move && _stop) {
1846                 return;
1847         }
1848         
1849         motion (event);
1850         
1851         if (_item == &_editor->playhead_cursor->canvas_item) {
1852                 if (_editor->session) {
1853                         _editor->session->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1854                         _editor->_pending_locate_request = true;
1855                 }
1856         } 
1857 }
1858
1859 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1860         : RegionDrag (e, i, p, v)
1861 {
1862         
1863 }
1864
1865 void
1866 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1867 {
1868         Drag::start_grab (event, cursor);
1869
1870         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1871         boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1872         
1873         _pointer_frame_offset = _grab_frame - ((nframes64_t) r->fade_in()->back()->when + r->position());       
1874 }
1875
1876 void
1877 FadeInDrag::motion (GdkEvent* event)
1878 {
1879         nframes64_t fade_length;
1880
1881         nframes64_t pos = adjusted_current_frame ();
1882
1883         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1884                 _editor->snap_to (pos);
1885         }
1886         
1887         boost::shared_ptr<Region> region = _primary->region ();
1888
1889         if (pos < (region->position() + 64)) {
1890                 fade_length = 64; // this should be a minimum defined somewhere
1891         } else if (pos > region->last_frame()) {
1892                 fade_length = region->length();
1893         } else {
1894                 fade_length = pos - region->position();
1895         }               
1896
1897         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1898
1899                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1900                 
1901                 if (!tmp) {
1902                         continue;
1903                 }
1904         
1905                 tmp->reset_fade_in_shape_width (fade_length);
1906         }
1907
1908         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
1909
1910         _first_move = false;
1911 }
1912
1913 void
1914 FadeInDrag::finished (GdkEvent* event)
1915 {
1916         nframes64_t fade_length;
1917
1918         if (_first_move) {
1919                 return;
1920         }
1921
1922         nframes64_t const pos = adjusted_current_frame ();
1923
1924         boost::shared_ptr<Region> region = _primary->region ();
1925
1926         if (pos < (region->position() + 64)) {
1927                 fade_length = 64; // this should be a minimum defined somewhere
1928         } else if (pos > region->last_frame()) {
1929                 fade_length = region->length();
1930         } else {
1931                 fade_length = pos - region->position();
1932         }
1933                 
1934         _editor->begin_reversible_command (_("change fade in length"));
1935
1936         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1937
1938                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1939                 
1940                 if (!tmp) {
1941                         continue;
1942                 }
1943         
1944                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
1945                 XMLNode &before = alist->get_state();
1946
1947                 tmp->audio_region()->set_fade_in_length (fade_length);
1948                 tmp->audio_region()->set_fade_in_active (true);
1949                 
1950                 XMLNode &after = alist->get_state();
1951                 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
1952         }
1953
1954         _editor->commit_reversible_command ();
1955 }
1956
1957 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1958         : RegionDrag (e, i, p, v)
1959 {
1960         
1961 }
1962
1963 void
1964 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1965 {
1966         Drag::start_grab (event, cursor);
1967
1968         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1969         boost::shared_ptr<AudioRegion> r = a->audio_region ();
1970         
1971         _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
1972 }
1973
1974 void
1975 FadeOutDrag::motion (GdkEvent* event)
1976 {
1977         nframes64_t fade_length;
1978
1979         nframes64_t pos = adjusted_current_frame ();
1980
1981         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1982                 _editor->snap_to (pos);
1983         }
1984
1985         boost::shared_ptr<Region> region = _primary->region ();
1986         
1987         if (pos > (region->last_frame() - 64)) {
1988                 fade_length = 64; // this should really be a minimum fade defined somewhere
1989         }
1990         else if (pos < region->position()) {
1991                 fade_length = region->length();
1992         }
1993         else {
1994                 fade_length = region->last_frame() - pos;
1995         }
1996                 
1997         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1998
1999                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2000                 
2001                 if (!tmp) {
2002                         continue;
2003                 }
2004         
2005                 tmp->reset_fade_out_shape_width (fade_length);
2006         }
2007
2008         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2009
2010         _first_move = false;
2011 }
2012
2013 void
2014 FadeOutDrag::finished (GdkEvent* event)
2015 {
2016         if (_first_move) {
2017                 return;
2018         }
2019
2020         nframes64_t fade_length;
2021
2022         nframes64_t pos = adjusted_current_frame ();
2023
2024         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2025                 _editor->snap_to (pos);
2026         }
2027
2028         boost::shared_ptr<Region> region = _primary->region ();
2029
2030         if (pos > (region->last_frame() - 64)) {
2031                 fade_length = 64; // this should really be a minimum fade defined somewhere
2032         }
2033         else if (pos < region->position()) {
2034                 fade_length = region->length();
2035         }
2036         else {
2037                 fade_length = region->last_frame() - pos;
2038         }
2039
2040         _editor->begin_reversible_command (_("change fade out length"));
2041
2042         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2043
2044                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2045                 
2046                 if (!tmp) {
2047                         continue;
2048                 }
2049         
2050                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2051                 XMLNode &before = alist->get_state();
2052                 
2053                 tmp->audio_region()->set_fade_out_length (fade_length);
2054                 tmp->audio_region()->set_fade_out_active (true);
2055
2056                 XMLNode &after = alist->get_state();
2057                 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2058         }
2059
2060         _editor->commit_reversible_command ();
2061 }
2062
2063 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2064         : Drag (e, i)
2065 {
2066         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2067         assert (_marker);
2068
2069         _points.push_back (Gnome::Art::Point (0, 0));
2070         _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2071
2072         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2073         _line->property_width_pixels() = 1;
2074         _line->property_points () = _points;
2075         _line->hide ();
2076
2077         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2078 }
2079
2080 MarkerDrag::~MarkerDrag ()
2081 {
2082         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2083                 delete *i;
2084         }
2085 }
2086
2087 void
2088 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2089 {
2090         Drag::start_grab (event, cursor);
2091         
2092         bool is_start;
2093
2094         Location *location = _editor->find_location_from_marker (_marker, is_start);
2095         _editor->_dragging_edit_point = true;
2096
2097         _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end()); 
2098
2099         update_item (location);
2100
2101         // _drag_line->show();
2102         // _line->raise_to_top();
2103
2104         if (is_start) {
2105                 _editor->show_verbose_time_cursor (location->start(), 10);
2106         } else {
2107                 _editor->show_verbose_time_cursor (location->end(), 10);
2108         }
2109
2110         Selection::Operation op = Keyboard::selection_type (event->button.state);
2111
2112         switch (op) {
2113         case Selection::Toggle:
2114                 _editor->selection->toggle (_marker);
2115                 break;
2116         case Selection::Set:
2117                 if (!_editor->selection->selected (_marker)) {
2118                         _editor->selection->set (_marker);
2119                 }
2120                 break;
2121         case Selection::Extend:
2122         {
2123                 Locations::LocationList ll;
2124                 list<Marker*> to_add;
2125                 nframes64_t s, e;
2126                 _editor->selection->markers.range (s, e);
2127                 s = min (_marker->position(), s);
2128                 e = max (_marker->position(), e);
2129                 s = min (s, e);
2130                 e = max (s, e);
2131                 if (e < max_frames) {
2132                         ++e;
2133                 }
2134                 _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2135                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2136                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2137                         if (lm) {
2138                                 if (lm->start) {
2139                                         to_add.push_back (lm->start);
2140                                 }
2141                                 if (lm->end) {
2142                                         to_add.push_back (lm->end);
2143                                 }
2144                         }
2145                 }
2146                 if (!to_add.empty()) {
2147                         _editor->selection->add (to_add);
2148                 }
2149                 break;
2150         }
2151         case Selection::Add:
2152                 _editor->selection->add (_marker);
2153                 break;
2154         }
2155
2156         /* set up copies for us to manipulate during the drag */
2157
2158         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2159                 Location *l = _editor->find_location_from_marker (*i, is_start);
2160                 _copied_locations.push_back (new Location (*l));
2161         }
2162 }
2163
2164 void
2165 MarkerDrag::motion (GdkEvent* event)
2166 {
2167         nframes64_t f_delta = 0;
2168         bool is_start;
2169         bool move_both = false;
2170         Marker* marker;
2171         Location  *real_location;
2172         Location *copy_location = 0;
2173
2174         nframes64_t newframe = adjusted_current_frame ();
2175
2176         nframes64_t next = newframe;
2177
2178         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2179                 _editor->snap_to (newframe, 0, true);
2180         }
2181         
2182         if (_current_pointer_frame == _last_pointer_frame) { 
2183                 return;
2184         }
2185
2186         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2187                 move_both = true;
2188         }
2189
2190         MarkerSelection::iterator i;
2191         list<Location*>::iterator x;
2192
2193         /* find the marker we're dragging, and compute the delta */
2194
2195         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); 
2196              x != _copied_locations.end() && i != _editor->selection->markers.end(); 
2197              ++i, ++x) {
2198
2199                 copy_location = *x;
2200                 marker = *i;
2201
2202                 if (marker == _marker) {
2203
2204                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2205                                 /* que pasa ?? */
2206                                 return;
2207                         }
2208
2209                         if (real_location->is_mark()) {
2210                                 f_delta = newframe - copy_location->start();
2211                         } else {
2212
2213
2214                                 switch (marker->type()) {
2215                                 case Marker::Start:
2216                                 case Marker::LoopStart:
2217                                 case Marker::PunchIn:
2218                                         f_delta = newframe - copy_location->start();
2219                                         break;
2220
2221                                 case Marker::End:
2222                                 case Marker::LoopEnd:
2223                                 case Marker::PunchOut:
2224                                         f_delta = newframe - copy_location->end();
2225                                         break;
2226                                 default:
2227                                         /* what kind of marker is this ? */
2228                                         return;
2229                                 }
2230                         }
2231                         break;
2232                 }
2233         }
2234
2235         if (i == _editor->selection->markers.end()) {
2236                 /* hmm, impossible - we didn't find the dragged marker */
2237                 return;
2238         }
2239
2240         /* now move them all */
2241
2242         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); 
2243              x != _copied_locations.end() && i != _editor->selection->markers.end(); 
2244              ++i, ++x) {
2245
2246                 copy_location = *x;
2247                 marker = *i;
2248
2249                 /* call this to find out if its the start or end */
2250                 
2251                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2252                         continue;
2253                 }
2254                 
2255                 if (real_location->locked()) {
2256                         continue;
2257                 }
2258
2259                 if (copy_location->is_mark()) {
2260
2261                         /* just move it */
2262                         
2263                         copy_location->set_start (copy_location->start() + f_delta);
2264
2265                 } else {
2266                         
2267                         nframes64_t new_start = copy_location->start() + f_delta;
2268                         nframes64_t new_end = copy_location->end() + f_delta;
2269                         
2270                         if (is_start) { // start-of-range marker
2271                                 
2272                                 if (move_both) {
2273                                         copy_location->set_start (new_start);
2274                                         copy_location->set_end (new_end);
2275                                 } else  if (new_start < copy_location->end()) {
2276                                         copy_location->set_start (new_start);
2277                                 } else { 
2278                                         _editor->snap_to (next, 1, true);
2279                                         copy_location->set_end (next);
2280                                         copy_location->set_start (newframe);
2281                                 }
2282                                 
2283                         } else { // end marker
2284                                 
2285                                 if (move_both) {
2286                                         copy_location->set_end (new_end);
2287                                         copy_location->set_start (new_start);
2288                                 } else if (new_end > copy_location->start()) {
2289                                         copy_location->set_end (new_end);
2290                                 } else if (newframe > 0) {
2291                                         _editor->snap_to (next, -1, true);
2292                                         copy_location->set_start (next);
2293                                         copy_location->set_end (newframe);
2294                                 }
2295                         }
2296                 }
2297
2298                 update_item (copy_location);
2299
2300                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2301
2302                 if (lm) {
2303                         lm->set_position (copy_location->start(), copy_location->end());
2304                 }
2305         }
2306
2307         _last_pointer_frame = _current_pointer_frame;
2308         _first_move = false;
2309
2310         assert (!_copied_locations.empty());
2311
2312         _editor->edit_point_clock.set (_copied_locations.front()->start());
2313         _editor->show_verbose_time_cursor (newframe, 10);
2314
2315 #ifdef GTKOSX
2316         _editor->track_canvas->update_now ();
2317 #endif
2318         _editor->edit_point_clock.set (copy_location->start());
2319 }
2320
2321 void
2322 MarkerDrag::finished (GdkEvent* event)
2323 {
2324         if (_first_move) {
2325
2326                 /* just a click, do nothing but finish
2327                    off the selection process
2328                 */
2329
2330                 Selection::Operation op = Keyboard::selection_type (event->button.state);
2331
2332                 switch (op) {
2333                 case Selection::Set:
2334                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2335                                 _editor->selection->set (_marker);
2336                         }
2337                         break;
2338
2339                 case Selection::Toggle:
2340                 case Selection::Extend:
2341                 case Selection::Add:
2342                         break;
2343                 }
2344                 
2345                 return;
2346         }
2347
2348         _editor->_dragging_edit_point = false;
2349         
2350
2351         _editor->begin_reversible_command ( _("move marker") );
2352         XMLNode &before = _editor->session->locations()->get_state();
2353
2354         MarkerSelection::iterator i;
2355         list<Location*>::iterator x;
2356         bool is_start;
2357
2358         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); 
2359              x != _copied_locations.end() && i != _editor->selection->markers.end(); 
2360              ++i, ++x) {
2361         
2362                 Location * location = _editor->find_location_from_marker (*i, is_start);
2363                 
2364                 if (location) {
2365                         
2366                         if (location->locked()) {
2367                                 return;
2368                         }
2369                         
2370                         if (location->is_mark()) {
2371                                 location->set_start ((*x)->start());
2372                         } else {
2373                                 location->set ((*x)->start(), (*x)->end());
2374                         }
2375                 }
2376         }
2377
2378         XMLNode &after = _editor->session->locations()->get_state();
2379         _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
2380         _editor->commit_reversible_command ();
2381         
2382         _line->hide();
2383 }
2384
2385 void
2386 MarkerDrag::update_item (Location* location)
2387 {
2388         double const x1 = _editor->frame_to_pixel (location->start());
2389
2390         _points.front().set_x(x1);
2391         _points.back().set_x(x1);
2392         _line->property_points() = _points;
2393 }
2394
2395 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2396         : Drag (e, i),
2397           _cumulative_x_drag (0),
2398           _cumulative_y_drag (0)
2399 {
2400         ControlPoint* _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2401         assert (_point);
2402 }
2403
2404
2405 void
2406 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2407 {
2408         Drag::start_grab (event, _editor->fader_cursor);
2409
2410         // start the grab at the center of the control point so
2411         // the point doesn't 'jump' to the mouse after the first drag
2412         _grab_x = _point->get_x();
2413         _grab_y = _point->get_y();
2414
2415         _point->line().parent_group().i2w (_grab_x, _grab_y);
2416         _editor->track_canvas->w2c (_grab_x, _grab_y, _grab_x, _grab_y);
2417
2418         _grab_frame = _editor->pixel_to_frame (_grab_x);
2419
2420         _point->line().start_drag (_point, _grab_frame, 0);
2421
2422         float fraction = 1.0 - (_point->get_y() / _point->line().height());
2423         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction), 
2424                                             _current_pointer_x + 10, _current_pointer_y + 10);
2425
2426         _editor->show_verbose_canvas_cursor ();
2427 }
2428
2429 void
2430 ControlPointDrag::motion (GdkEvent* event)
2431 {
2432         double dx = _current_pointer_x - _last_pointer_x;
2433         double dy = _current_pointer_y - _last_pointer_y;
2434
2435         if (event->button.state & Keyboard::SecondaryModifier) {
2436                 dx *= 0.1;
2437                 dy *= 0.1;
2438         }
2439
2440         double cx = _grab_x + _cumulative_x_drag + dx;
2441         double cy = _grab_y + _cumulative_y_drag + dy;
2442
2443         // calculate zero crossing point. back off by .01 to stay on the
2444         // positive side of zero
2445         double _unused = 0;
2446         double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2447         _point->line().parent_group().i2w(_unused, zero_gain_y);
2448
2449         // make sure we hit zero when passing through
2450         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2451                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2452                 cy = zero_gain_y;
2453         }
2454
2455         if (_x_constrained) {
2456                 cx = _grab_x;
2457         }
2458         if (_y_constrained) {
2459                 cy = _grab_y;
2460         }
2461
2462         _cumulative_x_drag = cx - _grab_x;
2463         _cumulative_y_drag = cy - _grab_y;
2464
2465         _point->line().parent_group().w2i (cx, cy);
2466
2467         cx = max (0.0, cx);
2468         cy = max (0.0, cy);
2469         cy = min ((double) _point->line().height(), cy);
2470
2471         //translate cx to frames
2472         nframes64_t cx_frames = _editor->unit_to_frame (cx);
2473
2474         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !_x_constrained) {
2475                 _editor->snap_to (cx_frames);
2476         }
2477
2478         float const fraction = 1.0 - (cy / _point->line().height());
2479
2480         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2481
2482         _point->line().point_drag (*_point, cx_frames, fraction, push);
2483         
2484         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2485
2486         _first_move = false;
2487 }
2488
2489 void
2490 ControlPointDrag::finished (GdkEvent* event)
2491 {
2492         if (_first_move) {
2493
2494                 /* just a click */
2495                 
2496                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2497                         _editor->reset_point_selection ();
2498                 }
2499
2500         } else {
2501                 motion (event);
2502         }
2503         _point->line().end_drag (_point);
2504 }
2505
2506 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2507         : Drag (e, i),
2508           _line (0),
2509           _cumulative_y_drag (0)
2510 {
2511
2512 }
2513 void
2514 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2515 {
2516         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2517         assert (_line);
2518
2519         _item = &_line->grab_item ();
2520
2521         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2522            origin, and ditto for y.
2523         */
2524
2525         double cx = event->button.x;
2526         double cy = event->button.y;
2527
2528         _line->parent_group().w2i (cx, cy);
2529
2530         nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2531
2532         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
2533                 /* no adjacent points */
2534                 return;
2535         }
2536
2537         Drag::start_grab (event, _editor->fader_cursor);
2538
2539         /* store grab start in parent frame */
2540
2541         _grab_x = cx;
2542         _grab_y = cy;
2543
2544         double fraction = 1.0 - (cy / _line->height());
2545
2546         _line->start_drag (0, _grab_frame, fraction);
2547         
2548         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2549                                             _current_pointer_x + 10, _current_pointer_y + 10);
2550         
2551         _editor->show_verbose_canvas_cursor ();
2552 }
2553
2554 void
2555 LineDrag::motion (GdkEvent* event)
2556 {
2557         double dy = _current_pointer_y - _last_pointer_y;
2558         
2559         if (event->button.state & Keyboard::SecondaryModifier) {
2560                 dy *= 0.1;
2561         }
2562
2563         double cy = _grab_y + _cumulative_y_drag + dy;
2564
2565         _cumulative_y_drag = cy - _grab_y;
2566
2567         cy = max (0.0, cy);
2568         cy = min ((double) _line->height(), cy);
2569
2570         double const fraction = 1.0 - (cy / _line->height());
2571
2572         bool push;
2573
2574         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2575                 push = false;
2576         } else {
2577                 push = true;
2578         }
2579
2580         _line->line_drag (_before, _after, fraction, push);
2581         
2582         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2583 }
2584
2585 void
2586 LineDrag::finished (GdkEvent* event)
2587 {
2588         motion (event);
2589         _line->end_drag (0);
2590 }
2591
2592 void
2593 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2594 {
2595         Drag::start_grab (event);
2596         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2597 }
2598
2599 void
2600 RubberbandSelectDrag::motion (GdkEvent* event)
2601 {
2602         nframes64_t start;
2603         nframes64_t end;
2604         double y1;
2605         double y2;
2606
2607         /* use a bigger drag threshold than the default */
2608
2609         if (abs ((int) (_current_pointer_frame - _grab_frame)) < 8) {
2610                 return;
2611         }
2612
2613         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
2614                 if (_first_move) {
2615                         _editor->snap_to (_grab_frame);
2616                 } 
2617                 _editor->snap_to (_current_pointer_frame);
2618         }
2619
2620         /* base start and end on initial click position */
2621
2622         if (_current_pointer_frame < _grab_frame) {
2623                 start = _current_pointer_frame;
2624                 end = _grab_frame;
2625         } else {
2626                 end = _current_pointer_frame;
2627                 start = _grab_frame;
2628         }
2629
2630         if (_current_pointer_y < _grab_y) {
2631                 y1 = _current_pointer_y;
2632                 y2 = _grab_y;
2633         } else {
2634                 y2 = _current_pointer_y;
2635                 y1 = _grab_y;
2636         }
2637
2638         
2639         if (start != end || y1 != y2) {
2640
2641                 double x1 = _editor->frame_to_pixel (start);
2642                 double x2 = _editor->frame_to_pixel (end);
2643                 
2644                 _editor->rubberband_rect->property_x1() = x1;
2645                 _editor->rubberband_rect->property_y1() = y1;
2646                 _editor->rubberband_rect->property_x2() = x2;
2647                 _editor->rubberband_rect->property_y2() = y2;
2648
2649                 _editor->rubberband_rect->show();
2650                 _editor->rubberband_rect->raise_to_top();
2651                 
2652                 _last_pointer_frame = _current_pointer_frame;
2653                 _first_move = false;
2654
2655                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2656         }
2657 }
2658
2659 void
2660 RubberbandSelectDrag::finished (GdkEvent* event)
2661 {
2662         if (!_first_move) {
2663
2664                 motion (event);
2665
2666                 double y1,y2;
2667                 if (_current_pointer_y < _grab_y) {
2668                         y1 = _current_pointer_y;
2669                         y2 = _grab_y;
2670                 } else {
2671                         y2 = _current_pointer_y;
2672                         y1 = _grab_y;
2673                 }
2674
2675
2676                 Selection::Operation op = Keyboard::selection_type (event->button.state);
2677                 bool commit;
2678
2679                 _editor->begin_reversible_command (_("rubberband selection"));
2680
2681                 if (_grab_frame < _last_pointer_frame) {
2682                         commit = _editor->select_all_within (_grab_frame, _last_pointer_frame, y1, y2, _editor->track_views, op);
2683                 } else {
2684                         commit = _editor->select_all_within (_last_pointer_frame, _grab_frame, y1, y2, _editor->track_views, op);
2685                 }               
2686
2687                 if (commit) {
2688                         _editor->commit_reversible_command ();
2689                 }
2690                 
2691         } else {
2692                 if (!getenv("ARDOUR_SAE")) {
2693                         _editor->selection->clear_tracks();
2694                 }
2695                 _editor->selection->clear_regions();
2696                 _editor->selection->clear_points ();
2697                 _editor->selection->clear_lines ();
2698         }
2699
2700         _editor->rubberband_rect->hide();
2701 }
2702
2703 void
2704 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2705 {
2706         Drag::start_grab (event);
2707         
2708         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2709 }
2710
2711 void
2712 TimeFXDrag::motion (GdkEvent* event)
2713 {
2714         RegionView* rv = _primary;
2715
2716         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2717                 _editor->snap_to (_current_pointer_frame);
2718         }
2719
2720         if (_current_pointer_frame == _last_pointer_frame) {
2721                 return;
2722         }
2723
2724         if (_current_pointer_frame > rv->region()->position()) {
2725                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), _current_pointer_frame);
2726         }
2727
2728         _last_pointer_frame = _current_pointer_frame;
2729         _first_move = false;
2730
2731         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2732 }
2733
2734 void
2735 TimeFXDrag::finished (GdkEvent* event)
2736 {
2737         _primary->get_time_axis_view().hide_timestretch ();
2738
2739         if (_first_move) {
2740                 return;
2741         }
2742
2743         if (_last_pointer_frame < _primary->region()->position()) {
2744                 /* backwards drag of the left edge - not usable */
2745                 return;
2746         }
2747         
2748         nframes64_t newlen = _last_pointer_frame - _primary->region()->position();
2749
2750         float percentage = (double) newlen / (double) _primary->region()->length();
2751         
2752 #ifndef USE_RUBBERBAND
2753         // Soundtouch uses percentage / 100 instead of normal (/ 1) 
2754         if (_primary->region()->data_type() == DataType::AUDIO) {
2755                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2756         }
2757 #endif  
2758         
2759         _editor->begin_reversible_command (_("timestretch"));
2760         
2761         // XXX how do timeFX on multiple regions ?
2762         
2763         RegionSelection rs;
2764         rs.add (_primary);
2765
2766         if (_editor->time_stretch (rs, percentage) == 0) {
2767                 _editor->session->commit_reversible_command ();
2768         }
2769 }
2770
2771 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2772         : Drag (e, i),
2773           _operation (o)
2774 {
2775
2776 }
2777
2778 void
2779 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2780 {
2781         nframes64_t start = 0;
2782         nframes64_t end = 0;
2783
2784         if (_editor->session == 0) {
2785                 return;
2786         }
2787
2788         Gdk::Cursor* cursor = 0;
2789
2790         switch (_operation) {
2791         case CreateSelection:
2792                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2793                         _copy = true;
2794                 } else {
2795                         _copy = false;
2796                 }
2797                 cursor = _editor->selector_cursor;
2798                 Drag::start_grab (event, cursor);
2799                 break;
2800
2801         case SelectionStartTrim:
2802                 if (_editor->clicked_axisview) {
2803                         _editor->clicked_axisview->order_selection_trims (_item, true);
2804                 } 
2805                 Drag::start_grab (event, cursor);
2806                 cursor = _editor->trimmer_cursor;
2807                 start = _editor->selection->time[_editor->clicked_selection].start;
2808                 _pointer_frame_offset = _grab_frame - start;    
2809                 break;
2810                 
2811         case SelectionEndTrim:
2812                 if (_editor->clicked_axisview) {
2813                         _editor->clicked_axisview->order_selection_trims (_item, false);
2814                 }
2815                 Drag::start_grab (event, cursor);
2816                 cursor = _editor->trimmer_cursor;
2817                 end = _editor->selection->time[_editor->clicked_selection].end;
2818                 _pointer_frame_offset = _grab_frame - end;      
2819                 break;
2820
2821         case SelectionMove:
2822                 start = _editor->selection->time[_editor->clicked_selection].start;
2823                 Drag::start_grab (event, cursor);
2824                 _pointer_frame_offset = _grab_frame - start;    
2825                 break;
2826         }
2827
2828         if (_operation == SelectionMove) {
2829                 _editor->show_verbose_time_cursor (start, 10);  
2830         } else {
2831                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
2832         }
2833 }
2834
2835 void
2836 SelectionDrag::motion (GdkEvent* event)
2837 {
2838         nframes64_t start = 0;
2839         nframes64_t end = 0;
2840         nframes64_t length;
2841
2842         nframes64_t pending_position = adjusted_current_frame ();
2843         
2844         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2845                 _editor->snap_to (pending_position);
2846         }
2847
2848         /* only alter selection if the current frame is 
2849            different from the last frame position (adjusted)
2850          */
2851         
2852         if (pending_position == _last_pointer_frame) {
2853                 return;
2854         }
2855         
2856         switch (_operation) {
2857         case CreateSelection:
2858                 
2859                 if (_first_move) {
2860                         _editor->snap_to (_grab_frame);
2861                 }
2862                 
2863                 if (pending_position < _grab_frame) {
2864                         start = pending_position;
2865                         end = _grab_frame;
2866                 } else {
2867                         end = pending_position;
2868                         start = _grab_frame;
2869                 }
2870                 
2871                 /* first drag: Either add to the selection
2872                    or create a new selection->
2873                 */
2874                 
2875                 if (_first_move) {
2876                         
2877                         _editor->begin_reversible_command (_("range selection"));
2878                         
2879                         if (_copy) {
2880                                 /* adding to the selection */
2881                                 _editor->clicked_selection = _editor->selection->add (start, end);
2882                                 _copy = false;
2883                         } else {
2884                                 /* new selection-> */
2885                                 _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end);
2886                         }
2887                 } 
2888                 break;
2889                 
2890         case SelectionStartTrim:
2891                 
2892                 if (_first_move) {
2893                         _editor->begin_reversible_command (_("trim selection start"));
2894                 }
2895                 
2896                 start = _editor->selection->time[_editor->clicked_selection].start;
2897                 end = _editor->selection->time[_editor->clicked_selection].end;
2898
2899                 if (pending_position > end) {
2900                         start = end;
2901                 } else {
2902                         start = pending_position;
2903                 }
2904                 break;
2905                 
2906         case SelectionEndTrim:
2907                 
2908                 if (_first_move) {
2909                         _editor->begin_reversible_command (_("trim selection end"));
2910                 }
2911                 
2912                 start = _editor->selection->time[_editor->clicked_selection].start;
2913                 end = _editor->selection->time[_editor->clicked_selection].end;
2914
2915                 if (pending_position < start) {
2916                         end = start;
2917                 } else {
2918                         end = pending_position;
2919                 }
2920                 
2921                 break;
2922                 
2923         case SelectionMove:
2924                 
2925                 if (_first_move) {
2926                         _editor->begin_reversible_command (_("move selection"));
2927                 }
2928                 
2929                 start = _editor->selection->time[_editor->clicked_selection].start;
2930                 end = _editor->selection->time[_editor->clicked_selection].end;
2931                 
2932                 length = end - start;
2933                 
2934                 start = pending_position;
2935                 _editor->snap_to (start);
2936                 
2937                 end = start + length;
2938                 
2939                 break;
2940         }
2941         
2942         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->canvas_width) {
2943                 _editor->start_canvas_autoscroll (1, 0);
2944         }
2945
2946         if (start != end) {
2947                 _editor->selection->replace (_editor->clicked_selection, start, end);
2948         }
2949
2950         _last_pointer_frame = pending_position;
2951         _first_move = false;
2952
2953         if (_operation == SelectionMove) {
2954                 _editor->show_verbose_time_cursor(start, 10);   
2955         } else {
2956                 _editor->show_verbose_time_cursor(pending_position, 10);        
2957         }
2958 }
2959
2960 void
2961 SelectionDrag::finished (GdkEvent* event)
2962 {
2963         if (!_first_move) {
2964                 motion (event);
2965                 /* XXX this is not object-oriented programming at all. ick */
2966                 if (_editor->selection->time.consolidate()) {
2967                         _editor->selection->TimeChanged ();
2968                 }
2969                 _editor->commit_reversible_command ();
2970         } else {
2971                 /* just a click, no pointer movement.*/
2972
2973                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
2974
2975                         _editor->selection->clear_time();
2976
2977                 } 
2978         }
2979
2980         /* XXX what happens if its a music selection? */
2981         _editor->session->set_audio_range (_editor->selection->time);
2982         _editor->stop_canvas_autoscroll ();
2983 }
2984
2985 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2986         : Drag (e, i),
2987           _operation (o)
2988 {
2989         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
2990         _drag_rect->hide ();
2991
2992         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
2993         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
2994 }
2995
2996 void
2997 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2998 {
2999         if (_editor->session == 0) {
3000                 return;
3001         }
3002
3003         Gdk::Cursor* cursor = 0;
3004
3005         if (!_editor->temp_location) {
3006                 _editor->temp_location = new Location;
3007         }
3008         
3009         switch (_operation) {
3010         case CreateRangeMarker:
3011         case CreateTransportMarker:
3012         case CreateCDMarker:
3013         
3014                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3015                         _copy = true;
3016                 } else {
3017                         _copy = false;
3018                 }
3019                 cursor = _editor->selector_cursor;
3020                 break;
3021         }
3022
3023         Drag::start_grab (event, cursor);
3024
3025         _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
3026 }
3027
3028 void
3029 RangeMarkerBarDrag::motion (GdkEvent* event)
3030 {
3031         nframes64_t start = 0;
3032         nframes64_t end = 0;
3033         ArdourCanvas::SimpleRect *crect;
3034
3035         switch (_operation) {
3036         case CreateRangeMarker:
3037                 crect = _editor->range_bar_drag_rect;
3038                 break;
3039         case CreateTransportMarker:
3040                 crect = _editor->transport_bar_drag_rect;
3041                 break;
3042         case CreateCDMarker:
3043                 crect = _editor->cd_marker_bar_drag_rect;
3044                 break;
3045         default:
3046                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3047                 return;
3048                 break;
3049         }
3050         
3051         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3052                 _editor->snap_to (_current_pointer_frame);
3053         }
3054
3055         /* only alter selection if the current frame is 
3056            different from the last frame position.
3057          */
3058         
3059         if (_current_pointer_frame == _last_pointer_frame) {
3060                 return;
3061         }
3062         
3063         switch (_operation) {
3064         case CreateRangeMarker:
3065         case CreateTransportMarker:
3066         case CreateCDMarker:
3067                 if (_first_move) {
3068                         _editor->snap_to (_grab_frame);
3069                 }
3070                 
3071                 if (_current_pointer_frame < _grab_frame) {
3072                         start = _current_pointer_frame;
3073                         end = _grab_frame;
3074                 } else {
3075                         end = _current_pointer_frame;
3076                         start = _grab_frame;
3077                 }
3078                 
3079                 /* first drag: Either add to the selection
3080                    or create a new selection.
3081                 */
3082                 
3083                 if (_first_move) {
3084                         
3085                         _editor->temp_location->set (start, end);
3086                         
3087                         crect->show ();
3088
3089                         update_item (_editor->temp_location);
3090                         _drag_rect->show();
3091                         //_drag_rect->raise_to_top();
3092                         
3093                 } 
3094                 break;          
3095         }
3096         
3097         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->canvas_width) {
3098                 _editor->start_canvas_autoscroll (1, 0);
3099         }
3100         
3101         if (start != end) {
3102                 _editor->temp_location->set (start, end);
3103
3104                 double x1 = _editor->frame_to_pixel (start);
3105                 double x2 = _editor->frame_to_pixel (end);
3106                 crect->property_x1() = x1;
3107                 crect->property_x2() = x2;
3108
3109                 update_item (_editor->temp_location);
3110         }
3111
3112         _last_pointer_frame = _current_pointer_frame;
3113         _first_move = false;
3114
3115         _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
3116         
3117 }
3118
3119 void
3120 RangeMarkerBarDrag::finished (GdkEvent* event)
3121 {
3122         Location * newloc = 0;
3123         string rangename;
3124         int flags;
3125         
3126         if (!_first_move) {
3127                 motion (event);
3128
3129                 switch (_operation) {
3130                 case CreateRangeMarker:
3131                 case CreateCDMarker:
3132                     {
3133                         _editor->begin_reversible_command (_("new range marker"));
3134                         XMLNode &before = _editor->session->locations()->get_state();
3135                         _editor->session->locations()->next_available_name(rangename,"unnamed");
3136                         if (_operation == CreateCDMarker) {
3137                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3138                                 _editor->cd_marker_bar_drag_rect->hide();
3139                         }
3140                         else {
3141                                 flags = Location::IsRangeMarker;
3142                                 _editor->range_bar_drag_rect->hide();
3143                         }
3144                         newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3145                         _editor->session->locations()->add (newloc, true);
3146                         XMLNode &after = _editor->session->locations()->get_state();
3147                         _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
3148                         _editor->commit_reversible_command ();
3149                         
3150                         _drag_rect->hide();
3151                         break;
3152                     }
3153
3154                 case CreateTransportMarker:
3155                         // popup menu to pick loop or punch
3156                         _editor->new_transport_marker_context_menu (&event->button, _item);
3157                         
3158                         break;
3159                 }
3160         } else {
3161                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3162
3163                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3164
3165                         nframes64_t start;
3166                         nframes64_t end;
3167
3168                         start = _editor->session->locations()->first_mark_before (_grab_frame);
3169                         end = _editor->session->locations()->first_mark_after (_grab_frame);
3170                         
3171                         if (end == max_frames) {
3172                                 end = _editor->session->current_end_frame ();
3173                         }
3174
3175                         if (start == 0) {
3176                                 start = _editor->session->current_start_frame ();
3177                         }
3178
3179                         switch (_editor->mouse_mode) {
3180                         case MouseObject:
3181                                 /* find the two markers on either side and then make the selection from it */
3182                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3183                                 break;
3184
3185                         case MouseRange:
3186                                 /* find the two markers on either side of the click and make the range out of it */
3187                                 _editor->selection->set (0, start, end);
3188                                 break;
3189
3190                         default:
3191                                 break;
3192                         }
3193                 } 
3194         }
3195
3196         _editor->stop_canvas_autoscroll ();
3197 }
3198
3199
3200
3201 void
3202 RangeMarkerBarDrag::update_item (Location* location)
3203 {
3204         double const x1 = _editor->frame_to_pixel (location->start());
3205         double const x2 = _editor->frame_to_pixel (location->end());
3206
3207         _drag_rect->property_x1() = x1;
3208         _drag_rect->property_x2() = x2;
3209 }
3210
3211 void
3212 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3213 {
3214         Drag::start_grab (event, _editor->zoom_cursor);
3215         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3216 }
3217
3218 void
3219 MouseZoomDrag::motion (GdkEvent* event)
3220 {
3221         nframes64_t start;
3222         nframes64_t end;
3223
3224         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3225                 _editor->snap_to (_current_pointer_frame);
3226                 
3227                 if (_first_move) {
3228                         _editor->snap_to (_grab_frame);
3229                 }
3230         }
3231                 
3232         if (_current_pointer_frame == _last_pointer_frame) {
3233                 return;
3234         }
3235
3236         /* base start and end on initial click position */
3237         if (_current_pointer_frame < _grab_frame) {
3238                 start = _current_pointer_frame;
3239                 end = _grab_frame;
3240         } else {
3241                 end = _current_pointer_frame;
3242                 start = _grab_frame;
3243         }
3244         
3245         if (start != end) {
3246
3247                 if (_first_move) {
3248                         _editor->zoom_rect->show();
3249                         _editor->zoom_rect->raise_to_top();
3250                 }
3251
3252                 _editor->reposition_zoom_rect(start, end);
3253
3254                 _last_pointer_frame = _current_pointer_frame;
3255                 _first_move = false;
3256
3257                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3258         }
3259 }
3260
3261 void
3262 MouseZoomDrag::finished (GdkEvent* event)
3263 {
3264         if (!_first_move) {
3265                 motion (event);
3266                 
3267                 if (_grab_frame < _last_pointer_frame) {
3268                         _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");
3269                 } else {
3270                         _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom");
3271                 }               
3272         } else {
3273                 _editor->temporal_zoom_to_frame (false, _grab_frame);
3274                 /*
3275                 temporal_zoom_step (false);
3276                 center_screen (_grab_frame);
3277                 */
3278         }
3279
3280         _editor->zoom_rect->hide();
3281 }