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