Fix a drag crash and some warnings.
[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
743         if (!movement_occurred) {
744                 /* just a click */
745                 goto out;
746         }
747
748         nocommit = false;
749
750         if (Config->get_edit_mode() == Splice && !_editor->pre_drag_region_selection.empty()) {
751                 _editor->selection->set (_editor->pre_drag_region_selection);
752                 _editor->pre_drag_region_selection.clear ();
753         }
754
755         if (_brushing) {
756                 /* all changes were made during motion event handlers */
757                 
758                 if (_copy) {
759                         for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
760                                 copies.push_back (*i);
761                         }
762                 }
763
764                 goto out;
765         }
766
767         /* reverse this here so that we have the correct logic to finalize
768            the drag.
769         */
770         
771         if (Config->get_edit_mode() == Lock && !_copy) {
772                 _x_constrained = !_x_constrained;
773         }
774
775         if (_copy) {
776                 if (_x_constrained) {
777                         _editor->begin_reversible_command (_("fixed time region copy"));
778                 } else {
779                         _editor->begin_reversible_command (_("region copy"));
780                 } 
781         } else {
782                 if (_x_constrained) {
783                         _editor->begin_reversible_command (_("fixed time region drag"));
784                 } else {
785                         _editor->begin_reversible_command (_("region drag"));
786                 }
787         }
788
789         changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
790         changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
791
792         drag_delta = _primary->region()->position() - _last_frame_position;
793
794         _editor->track_canvas->update_now ();
795
796         /* make a list of where each region ended up */
797         final = find_time_axis_views ();
798
799         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
800
801                 RegionView* rv = (*i);
802                 RouteTimeAxisView* dest_rtv = final[*i];
803
804                 nframes64_t where;
805
806                 if (rv->region()->locked()) {
807                         ++i;
808                         continue;
809                 }
810
811                 if (changed_position && !_x_constrained) {
812                         where = rv->region()->position() - drag_delta;
813                 } else {
814                         where = rv->region()->position();
815                 }
816                         
817                 boost::shared_ptr<Region> new_region;
818
819                 if (_copy) {
820                         /* we already made a copy */
821                         new_region = rv->region();
822
823                         /* undo the previous hide_dependent_views so that xfades don't
824                            disappear on copying regions 
825                         */
826                 
827                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
828                 
829                 } else if (changed_tracks && dest_rtv->playlist()) {
830                         new_region = RegionFactory::create (rv->region());
831                 }
832
833                 if (changed_tracks || _copy) {
834
835                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
836                         
837                         if (!to_playlist) {
838                                 ++i;
839                                 continue;
840                         }
841
842                         _editor->latest_regionviews.clear ();
843
844                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*_editor, &Editor::collect_new_region_view));
845                         
846                         insert_result = modified_playlists.insert (to_playlist);
847                         
848                         if (insert_result.second) {
849                                 _editor->session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
850                         }
851
852                         to_playlist->add_region (new_region, where);
853
854                         c.disconnect ();
855                                                               
856                         if (!_editor->latest_regionviews.empty()) {
857                                 // XXX why just the first one ? we only expect one
858                                 // commented out in nick_m's canvas reworking. is that intended?
859                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
860                                 new_views.push_back (_editor->latest_regionviews.front());
861                         }
862
863                 } else {
864                         /* 
865                            motion on the same track. plonk the previously reparented region 
866                            back to its original canvas group (its streamview).
867                            No need to do anything for copies as they are fake regions which will be deleted.
868                         */
869
870                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
871                         rv->get_canvas_group()->property_y() = 0;
872                   
873                         /* just change the model */
874                         
875                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
876
877                         insert_result = modified_playlists.insert (playlist);
878                         
879                         if (insert_result.second) {
880                                 _editor->session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
881                         }
882                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
883                         frozen_insert_result = frozen_playlists.insert(playlist);
884                         
885                         if (frozen_insert_result.second) {
886                                 playlist->freeze();
887                         }
888
889                         rv->region()->set_position (where, (void*) this);
890                 }
891
892                 if (changed_tracks && !_copy) {
893
894                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
895                            because we may have copied the region and it has not been attached to a playlist.
896                         */
897
898                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
899                         assert ((ds = source_tv->get_diskstream()));
900                         assert ((from_playlist = ds->playlist()));
901
902                         /* moved to a different audio track, without copying */
903
904                         /* the region that used to be in the old playlist is not
905                            moved to the new one - we use a copy of it. as a result,
906                            any existing editor for the region should no longer be
907                            visible.
908                         */ 
909             
910                         rv->hide_region_editor();
911                         rv->fake_set_opaque (false);
912                         
913                         /* remove the region from the old playlist */
914
915                         insert_result = modified_playlists.insert (from_playlist);
916                         
917                         if (insert_result.second) {
918                                 _editor->session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
919                         }
920         
921                         from_playlist->remove_region (rv->region());
922                         
923                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
924                            was selected in all of them, then removing it from a playlist will have removed all
925                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
926                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
927                            corresponding regionview, and the selection is now empty).
928
929                            this could have invalidated any and all iterators into the region selection.
930
931                            the heuristic we use here is: if the region selection is empty, break out of the loop
932                            here. if the region selection is not empty, then restart the loop because we know that
933                            we must have removed at least the region(view) we've just been working on as well as any
934                            that we processed on previous iterations.
935
936                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
937                            we can just iterate.
938                         */
939
940                         if (_views.empty()) {
941                                 break;
942                         } else { 
943                                 i = _views.begin();
944                         }
945
946                 } else {
947                         ++i;
948                 }
949                 
950                 if (_copy) {
951                         copies.push_back (rv);
952                 }
953         }
954         
955         if (new_views.empty()) {
956                 if (_copy) {
957                         /* the region(view)s that are being dragged around are copies and do not
958                            belong to any track. remove them from our list
959                         */
960                         _views.clear ();
961                 }
962         } else {
963                 _views = new_views;
964                 _primary = _views.front ();
965         }
966
967         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
968                 (*p)->thaw();
969         }
970                         
971   out:
972         if (!nocommit) {
973                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
974                         _editor->session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));     
975                 }
976
977                 _editor->commit_reversible_command ();
978         }
979
980         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
981                 delete *x;
982         }
983 }
984
985
986 bool
987 RegionMoveDrag::x_move_allowed () const
988 {
989         if (Config->get_edit_mode() == Lock) {
990                 if (_copy) {
991                         return !_x_constrained;
992                 } else {
993                         /* in locked edit mode, reverse the usual meaning of _x_constrained */
994                         return _x_constrained;
995                 }
996         }
997         
998         return !_x_constrained;
999 }
1000
1001 bool
1002 RegionInsertDrag::x_move_allowed () const
1003 {
1004         if (Config->get_edit_mode() == Lock) {
1005                 return _x_constrained;
1006         }
1007
1008         return !_x_constrained;
1009 }
1010
1011 void
1012 RegionMotionDrag::copy_regions (GdkEvent* event)
1013 {
1014         /* duplicate the regionview(s) and region(s) */
1015
1016         list<RegionView*> new_regionviews;
1017         
1018         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1019                 
1020                 RegionView* rv = (*i);
1021                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1022                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1023                 
1024                 const boost::shared_ptr<const Region> original = rv->region();
1025                 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1026
1027                 RegionView* nrv;
1028                 if (arv) {
1029                         boost::shared_ptr<AudioRegion> audioregion_copy
1030                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1031                         nrv = new AudioRegionView (*arv, audioregion_copy);
1032                 } else if (mrv) {
1033                         boost::shared_ptr<MidiRegion> midiregion_copy
1034                                 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1035                         nrv = new MidiRegionView (*mrv, midiregion_copy);
1036                 } else {
1037                         continue;
1038                 }
1039                 
1040                 nrv->get_canvas_group()->show ();
1041                 new_regionviews.push_back (nrv);
1042         }
1043         
1044         if (new_regionviews.empty()) {
1045                 return;
1046         }
1047         
1048         /* reflect the fact that we are dragging the copies */
1049         
1050         _primary = new_regionviews.front();
1051         _views = new_regionviews;
1052                 
1053         swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1054         
1055         /* 
1056            sync the canvas to what we think is its current state 
1057            without it, the canvas seems to 
1058            "forget" to update properly after the upcoming reparent() 
1059            ..only if the mouse is in rapid motion at the time of the grab. 
1060            something to do with regionview creation raking so long?
1061         */
1062         _editor->track_canvas->update_now();
1063 }
1064
1065 bool
1066 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1067 {
1068         /* Which trackview is this ? */
1069
1070         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1071         (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1072         (*layer) = tvp.second;
1073
1074         if (*tv && (*tv)->layer_display() == Overlaid) {
1075                 *layer = 0;
1076         }
1077
1078         /* The region motion is only processed if the pointer is over
1079            an audio track.
1080         */
1081         
1082         if (!(*tv) || !(*tv)->is_track()) {
1083                 /* To make sure we hide the verbose canvas cursor when the mouse is 
1084                    not held over and audiotrack. 
1085                 */
1086                 _editor->hide_verbose_canvas_cursor ();
1087                 return false;
1088         }
1089
1090         return true;
1091 }
1092
1093 /** @param new_order New track order.
1094  *  @param old_order Old track order.
1095  *  @param visible_y_low Lowest visible order.
1096  *  @return true if y movement should not happen, otherwise false.
1097  */
1098 bool
1099 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1100 {
1101         if (new_order != old_order) {
1102
1103                 /* this isn't the pointer track */      
1104
1105                 if (y_span > 0) {
1106
1107                         /* moving up the canvas */
1108                         if ( (new_order - y_span) >= tavs.visible_y_low) {
1109
1110                                 int32_t n = 0;
1111
1112                                 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1113                                 int32_t visible_tracks = 0;
1114                                 while (visible_tracks < y_span ) {
1115                                         visible_tracks++;
1116                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1117                                                 /* passing through a hidden track */
1118                                                 n--;
1119                                         }                 
1120                                 }
1121                  
1122                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1123                                         /* moving to a non-track; disallow */
1124                                         return true;
1125                                 }
1126                                 
1127
1128                         } else {
1129                                 /* moving beyond the lowest visible track; disallow */
1130                                 return true;
1131                         }                 
1132                   
1133                 } else if (y_span < 0) {
1134
1135                         /* moving down the canvas */
1136                         if ((new_order - y_span) <= tavs.visible_y_high) {
1137
1138                                 int32_t visible_tracks = 0;
1139                                 int32_t n = 0;
1140                                 while (visible_tracks > y_span ) {
1141                                         visible_tracks--;
1142                       
1143                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1144                                                 /* passing through a hidden track */
1145                                                 n++;
1146                                         }                
1147                                 }
1148                                                 
1149                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1150                                         /* moving to a non-track; disallow */
1151                                         return true;
1152                                 }
1153
1154                                 
1155                         } else {
1156
1157                                 /* moving beyond the highest visible track; disallow */
1158                                 return true;
1159                         }
1160                 }               
1161                 
1162         } else {
1163                 
1164                 /* this is the pointer's track */
1165                 
1166                 if ((new_order - y_span) > tavs.visible_y_high) {
1167                         /* we will overflow */
1168                         return true;
1169                 } else if ((new_order - y_span) < tavs.visible_y_low) {
1170                         /* we will overflow */
1171                         return true;
1172                 }
1173         }
1174
1175         return false;
1176 }
1177
1178
1179 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1180         : RegionMotionDrag (e, i, p, v, b),
1181           _copy (c)
1182 {
1183         TimeAxisView* const tv = &_primary->get_time_axis_view ();
1184         
1185         _dest_trackview = tv;
1186         if (tv->layer_display() == Overlaid) {
1187                 _dest_layer = 0;
1188         } else {
1189                 _dest_layer = _primary->region()->layer ();
1190         }
1191
1192         double speed = 1;
1193         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1194         if (rtv && rtv->is_track()) {
1195                 speed = rtv->get_diskstream()->speed ();
1196         }
1197
1198         _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1199 }
1200
1201 void
1202 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1203 {
1204         RegionMotionDrag::start_grab (event, c);
1205         
1206         _pointer_frame_offset = _grab_frame - _last_frame_position;
1207 }
1208
1209 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1210         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1211 {
1212         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1213                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1214
1215         _primary = v->view()->create_region_view (r, false, false);
1216         
1217         _primary->get_canvas_group()->show ();
1218         _primary->set_position (pos, 0);
1219         _views.push_back (_primary);
1220
1221         _last_frame_position = pos;
1222
1223         _item = _primary->get_canvas_group ();
1224         _dest_trackview = v;
1225         _dest_layer = _primary->region()->layer ();
1226 }
1227
1228 map<RegionView*, RouteTimeAxisView*>
1229 RegionMotionDrag::find_time_axis_views ()
1230 {
1231         map<RegionView*, RouteTimeAxisView*> tav;
1232         
1233         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1234
1235                 double ix1, ix2, iy1, iy2;
1236                 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1237                 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1238                 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1239
1240                 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1241                 tav[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
1242         }
1243
1244         return tav;
1245 }
1246
1247
1248 void
1249 RegionInsertDrag::finished (GdkEvent* event, bool movement_occurred)
1250 {
1251         _editor->track_canvas->update_now ();
1252
1253         map<RegionView*, RouteTimeAxisView*> final = find_time_axis_views ();
1254         
1255         RouteTimeAxisView* dest_rtv = final[_primary];
1256
1257         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1258         _primary->get_canvas_group()->property_y() = 0;
1259
1260         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1261
1262         _editor->begin_reversible_command (_("insert region"));
1263         XMLNode& before = playlist->get_state ();
1264         playlist->add_region (_primary->region (), _last_frame_position);
1265         _editor->session->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1266         _editor->commit_reversible_command ();
1267
1268         delete _primary;
1269         _primary = 0;
1270         _views.clear ();
1271 }
1272
1273 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1274         : RegionMoveDrag (e, i, p, v, false, false)
1275 {
1276
1277 }
1278
1279 struct RegionSelectionByPosition {
1280     bool operator() (RegionView*a, RegionView* b) {
1281             return a->region()->position () < b->region()->position();
1282     }
1283 };
1284
1285 void
1286 RegionSpliceDrag::motion (GdkEvent* event, bool)
1287 {
1288         RouteTimeAxisView* tv;
1289         layer_t layer;
1290         
1291         if (!check_possible (&tv, &layer)) {
1292                 return;
1293         }
1294
1295         int dir;
1296
1297         if (_current_pointer_x - _grab_x > 0) {
1298                 dir = 1;
1299         } else {
1300                 dir = -1;
1301         }
1302
1303         RegionSelection copy (_editor->selection->regions);
1304
1305         RegionSelectionByPosition cmp;
1306         copy.sort (cmp);
1307
1308         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1309
1310                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1311
1312                 if (!atv) {
1313                         continue;
1314                 }
1315
1316                 boost::shared_ptr<Playlist> playlist;
1317
1318                 if ((playlist = atv->playlist()) == 0) {
1319                         continue;
1320                 }
1321
1322                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1323                         continue;
1324                 } 
1325
1326                 if (dir > 0) {
1327                         if (_current_pointer_frame < (*i)->region()->last_frame() + 1) {
1328                                 continue;
1329                         }
1330                 } else {
1331                         if (_current_pointer_frame > (*i)->region()->first_frame()) {
1332                                 continue;
1333                         }
1334                 }
1335
1336                 
1337                 playlist->shuffle ((*i)->region(), dir);
1338
1339                 _grab_x = _current_pointer_x;
1340         }
1341 }
1342
1343 void
1344 RegionSpliceDrag::finished (GdkEvent* event, bool)
1345 {
1346         
1347 }
1348
1349
1350 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1351         : Drag (e, i),
1352           _view (v)
1353 {
1354         
1355 }
1356
1357 void
1358 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1359 {
1360         _dest_trackview = _view;
1361         
1362         Drag::start_grab (event);
1363 }
1364
1365
1366 void
1367 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1368 {
1369         if (first_move) {
1370                 // TODO: create region-create-drag region view here
1371         }
1372
1373         // TODO: resize region-create-drag region view here
1374
1375
1376 void
1377 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1378 {
1379         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1380         if (!mtv) {
1381                 return;
1382         }
1383
1384         const boost::shared_ptr<MidiDiskstream> diskstream =
1385                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
1386         
1387         if (!diskstream) {
1388                 warning << "Cannot create non-MIDI region" << endl;
1389                 return;
1390         }
1391
1392         if (!movement_occurred) {
1393                 _editor->begin_reversible_command (_("create region"));
1394                 XMLNode &before = mtv->playlist()->get_state();
1395
1396                 nframes64_t start = _grab_frame;
1397                 _editor->snap_to (start, -1);
1398                 const Meter& m = _editor->session->tempo_map().meter_at(start);
1399                 const Tempo& t = _editor->session->tempo_map().tempo_at(start);
1400                 double length = floor (m.frames_per_bar(t, _editor->session->frame_rate()));
1401
1402                 boost::shared_ptr<Source> src = _editor->session->create_midi_source_for_session(*diskstream.get());
1403                                 
1404                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
1405                                              (RegionFactory::create(src, 0, (nframes_t) length, 
1406                                                                     PBD::basename_nosuffix(src->name()))), start);
1407                 XMLNode &after = mtv->playlist()->get_state();
1408                 _editor->session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
1409                 _editor->commit_reversible_command();
1410
1411         } else {
1412                 motion (event, false);
1413                 // TODO: create region-create-drag region here
1414         }
1415 }
1416
1417
1418
1419
1420 void
1421 RegionGainDrag::motion (GdkEvent* event, bool)
1422 {
1423         
1424 }
1425
1426 void
1427 RegionGainDrag::finished (GdkEvent *, bool)
1428 {
1429
1430 }
1431
1432 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1433         : RegionDrag (e, i, p, v)
1434 {
1435
1436 }
1437
1438 void
1439 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1440 {
1441         double speed = 1.0;
1442         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1443         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1444
1445         if (tv && tv->is_track()) {
1446                 speed = tv->get_diskstream()->speed();
1447         }
1448         
1449         nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1450         nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1451         nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1452
1453         Drag::start_grab (event, _editor->trimmer_cursor);
1454         
1455         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1456                 _operation = ContentsTrim;
1457         } else {
1458                 /* These will get overridden for a point trim.*/
1459                 if (_current_pointer_frame < (region_start + region_length/2)) {
1460                         /* closer to start */
1461                         _operation = StartTrim;
1462                 } else if (_current_pointer_frame > (region_end - region_length/2)) {
1463                         /* closer to end */
1464                         _operation = EndTrim;
1465                 }
1466         }
1467
1468         switch (_operation) {
1469         case StartTrim:
1470                 _editor->show_verbose_time_cursor (region_start, 10);   
1471                 break;
1472         case EndTrim:
1473                 _editor->show_verbose_time_cursor (region_end, 10);     
1474                 break;
1475         case ContentsTrim:
1476                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
1477                 break;
1478         }
1479 }
1480
1481 void
1482 TrimDrag::motion (GdkEvent* event, bool first_move)
1483 {
1484         RegionView* rv = _primary;
1485         nframes64_t frame_delta = 0;
1486
1487         bool left_direction;
1488         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1489
1490         /* snap modifier works differently here..
1491            its' current state has to be passed to the 
1492            various trim functions in order to work properly 
1493         */ 
1494
1495         double speed = 1.0;
1496         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1497         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1498         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1499
1500         if (tv && tv->is_track()) {
1501                 speed = tv->get_diskstream()->speed();
1502         }
1503         
1504         if (_last_pointer_frame > _current_pointer_frame) {
1505                 left_direction = true;
1506         } else {
1507                 left_direction = false;
1508         }
1509
1510         if (obey_snap) {
1511                 _editor->snap_to (_current_pointer_frame);
1512         }
1513
1514         if (_current_pointer_frame == _last_pointer_frame) {
1515                 return;
1516         }
1517
1518         if (first_move) {
1519         
1520                 string trim_type;
1521
1522                 switch (_operation) {
1523                 case StartTrim:
1524                         trim_type = "Region start trim";
1525                         break;
1526                 case EndTrim:
1527                         trim_type = "Region end trim";
1528                         break;
1529                 case ContentsTrim:
1530                         trim_type = "Region content trim";
1531                         break;
1532                 }
1533
1534                 _editor->begin_reversible_command (trim_type);
1535
1536                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1537                         (*i)->fake_set_opaque(false);
1538                         (*i)->region()->freeze ();
1539                 
1540                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1541
1542                         if (arv){
1543                                 arv->temporarily_hide_envelope ();
1544                         }
1545
1546                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1547                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1548
1549                         if (insert_result.second) {
1550                                 _editor->session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1551                                 pl->freeze();
1552                         }
1553                 }
1554         }
1555
1556         if (left_direction) {
1557                 frame_delta = (_last_pointer_frame - _current_pointer_frame);
1558         } else {
1559                 frame_delta = (_current_pointer_frame - _last_pointer_frame);
1560         }
1561
1562         bool non_overlap_trim = false;
1563
1564         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1565                 non_overlap_trim = true;
1566         }
1567
1568         switch (_operation) {           
1569         case StartTrim:
1570                 if ((left_direction == false) && (_current_pointer_frame <= rv->region()->first_frame()/speed)) {
1571                         break;
1572                 } else {
1573
1574                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1575                                 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1576                         }
1577                         break;
1578                 }
1579                 
1580         case EndTrim:
1581                 if ((left_direction == true) && (_current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
1582                         break;
1583                 } else {
1584
1585                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1586                                 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1587                         }
1588                         break;
1589                 }
1590                 
1591         case ContentsTrim:
1592                 {
1593                         bool swap_direction = false;
1594
1595                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1596                                 swap_direction = true;
1597                         }
1598                         
1599                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i)
1600                         {
1601                                 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1602                         }
1603                 }
1604                 break;
1605         }
1606
1607         switch (_operation) {
1608         case StartTrim:
1609                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);  
1610                 break;
1611         case EndTrim:
1612                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);        
1613                 break;
1614         case ContentsTrim:
1615                 _editor->show_verbose_time_cursor(_current_pointer_frame, 10);  
1616                 break;
1617         }
1618
1619         _last_pointer_frame = _current_pointer_frame;
1620 }
1621
1622
1623 void
1624 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1625 {
1626         if (movement_occurred) {
1627                 motion (event, false);
1628                 
1629                 if (!_editor->selection->selected (_primary)) {
1630                         _editor->thaw_region_after_trim (*_primary);            
1631                 } else {
1632                         
1633                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1634                                 _editor->thaw_region_after_trim (**i);
1635                                 (*i)->fake_set_opaque (true);
1636                         }
1637                 }
1638                 
1639                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1640                         (*p)->thaw ();
1641                         _editor->session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1642                 }
1643                 
1644                 _editor->motion_frozen_playlists.clear ();
1645
1646                 _editor->commit_reversible_command();
1647         } else {
1648                 /* no mouse movement */
1649                 _editor->point_trim (event);
1650         }
1651 }
1652
1653 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1654         : Drag (e, i),
1655           _copy (c)
1656 {
1657         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1658         assert (_marker);
1659 }
1660
1661 void
1662 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1663 {
1664         if (_copy) {
1665                 // create a dummy marker for visual representation of moving the copy.
1666                 // The actual copying is not done before we reach the finish callback.
1667                 char name[64];
1668                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1669                 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, 
1670                                                           *new MeterSection (_marker->meter()));
1671
1672                 _item = &new_marker->the_item ();
1673                 _marker = new_marker;
1674                 
1675         } else {
1676
1677                 MetricSection& section (_marker->meter());
1678                 
1679                 if (!section.movable()) {
1680                         return;
1681                 }
1682                 
1683         }
1684
1685         Drag::start_grab (event, cursor);
1686         
1687         _pointer_frame_offset = _grab_frame - _marker->meter().frame(); 
1688
1689         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1690 }
1691
1692 void
1693 MeterMarkerDrag::motion (GdkEvent* event, bool)
1694 {
1695         nframes64_t adjusted_frame = adjusted_current_frame ();
1696         
1697         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1698                 _editor->snap_to (adjusted_frame);
1699         }
1700         
1701         if (adjusted_frame == _last_pointer_frame) {
1702                 return;
1703         }
1704
1705         _marker->set_position (adjusted_frame);
1706         
1707         _last_pointer_frame = adjusted_frame;
1708
1709         _editor->show_verbose_time_cursor (adjusted_frame, 10);
1710 }
1711
1712 void
1713 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1714 {
1715         if (!movement_occurred) {
1716                 return;
1717         }
1718
1719         motion (event, false);
1720         
1721         BBT_Time when;
1722         
1723         TempoMap& map (_editor->session->tempo_map());
1724         map.bbt_time (_last_pointer_frame, when);
1725         
1726         if (_copy == true) {
1727                 _editor->begin_reversible_command (_("copy meter mark"));
1728                 XMLNode &before = map.get_state();
1729                 map.add_meter (_marker->meter(), when);
1730                 XMLNode &after = map.get_state();
1731                 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1732                 _editor->commit_reversible_command ();
1733
1734                 // delete the dummy marker we used for visual representation of copying.
1735                 // a new visual marker will show up automatically.
1736                 delete _marker;
1737         } else {
1738                 _editor->begin_reversible_command (_("move meter mark"));
1739                 XMLNode &before = map.get_state();
1740                 map.move_meter (_marker->meter(), when);
1741                 XMLNode &after = map.get_state();
1742                 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1743                 _editor->commit_reversible_command ();
1744         }
1745 }
1746
1747 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1748         : Drag (e, i),
1749           _copy (c)
1750 {
1751         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1752         assert (_marker);
1753 }
1754
1755 void
1756 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1757 {
1758
1759         if (_copy) {
1760                 
1761                 // create a dummy marker for visual representation of moving the copy.
1762                 // The actual copying is not done before we reach the finish callback.
1763                 char name[64];
1764                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1765                 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, 
1766                                                           *new TempoSection (_marker->tempo()));
1767
1768                 _item = &new_marker->the_item ();
1769                 _marker = new_marker;
1770
1771         } else {
1772
1773                 MetricSection& section (_marker->tempo());
1774                 
1775                 if (!section.movable()) {
1776                         return;
1777                 }
1778         }
1779
1780         Drag::start_grab (event, cursor);
1781
1782         _pointer_frame_offset = _grab_frame - _marker->tempo().frame(); 
1783         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1784 }
1785
1786 void
1787 TempoMarkerDrag::motion (GdkEvent* event, bool)
1788 {
1789         nframes64_t adjusted_frame = adjusted_current_frame ();
1790
1791         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1792                 _editor->snap_to (adjusted_frame);
1793         }
1794         
1795         if (adjusted_frame == _last_pointer_frame) {
1796                 return;
1797         }
1798
1799         /* OK, we've moved far enough to make it worth actually move the thing. */
1800                 
1801         _marker->set_position (adjusted_frame);
1802         
1803         _editor->show_verbose_time_cursor (adjusted_frame, 10);
1804
1805         _last_pointer_frame = adjusted_frame;
1806 }
1807
1808 void
1809 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1810 {
1811         if (!movement_occurred) {
1812                 return;
1813         }
1814         
1815         motion (event, false);
1816         
1817         BBT_Time when;
1818         
1819         TempoMap& map (_editor->session->tempo_map());
1820         map.bbt_time (_last_pointer_frame, when);
1821
1822         if (_copy == true) {
1823                 _editor->begin_reversible_command (_("copy tempo mark"));
1824                 XMLNode &before = map.get_state();
1825                 map.add_tempo (_marker->tempo(), when);
1826                 XMLNode &after = map.get_state();
1827                 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1828                 _editor->commit_reversible_command ();
1829                 
1830                 // delete the dummy marker we used for visual representation of copying.
1831                 // a new visual marker will show up automatically.
1832                 delete _marker;
1833         } else {
1834                 _editor->begin_reversible_command (_("move tempo mark"));
1835                 XMLNode &before = map.get_state();
1836                 map.move_tempo (_marker->tempo(), when);
1837                 XMLNode &after = map.get_state();
1838                 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1839                 _editor->commit_reversible_command ();
1840         }
1841 }
1842
1843
1844 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1845         : Drag (e, i),
1846           _stop (s)
1847 {
1848         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1849         assert (_cursor);
1850 }
1851
1852 void
1853 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1854 {
1855         Drag::start_grab (event, c);
1856
1857         if (!_stop) {
1858                 
1859                 nframes64_t where = _editor->event_frame (event, 0, 0);
1860
1861                 _editor->snap_to (where);
1862                 _editor->playhead_cursor->set_position (where);
1863
1864         }
1865
1866         if (_cursor == _editor->playhead_cursor) {
1867                 _editor->_dragging_playhead = true;
1868
1869                 if (_editor->session && _was_rolling && _stop) {
1870                         _editor->session->request_stop ();
1871                 }
1872
1873                 if (_editor->session && _editor->session->is_auditioning()) {
1874                         _editor->session->cancel_audition ();
1875                 }
1876         }
1877
1878         _pointer_frame_offset = _grab_frame - _cursor->current_frame;   
1879         
1880         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1881 }
1882
1883 void
1884 CursorDrag::motion (GdkEvent* event, bool)
1885 {
1886         nframes64_t adjusted_frame = adjusted_current_frame ();
1887         
1888         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1889                 if (_cursor == _editor->playhead_cursor) {
1890                         _editor->snap_to (adjusted_frame);
1891                 }
1892         }
1893         
1894         if (adjusted_frame == _last_pointer_frame) {
1895                 return;
1896         }
1897
1898         _cursor->set_position (adjusted_frame);
1899
1900         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1901
1902 #ifdef GTKOSX
1903         _editor->track_canvas->update_now ();
1904 #endif
1905         _editor->UpdateAllTransportClocks (_cursor->current_frame);
1906
1907         _last_pointer_frame = adjusted_frame;
1908 }
1909
1910 void
1911 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1912 {
1913         _editor->_dragging_playhead = false;
1914
1915         if (!movement_occurred && _stop) {
1916                 return;
1917         }
1918         
1919         motion (event, false);
1920         
1921         if (_item == &_editor->playhead_cursor->canvas_item) {
1922                 if (_editor->session) {
1923                         _editor->session->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1924                         _editor->_pending_locate_request = true;
1925                 }
1926         } 
1927 }
1928
1929 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1930         : RegionDrag (e, i, p, v)
1931 {
1932         
1933 }
1934
1935 void
1936 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1937 {
1938         Drag::start_grab (event, cursor);
1939
1940         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1941         boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1942         
1943         _pointer_frame_offset = _grab_frame - ((nframes64_t) r->fade_in()->back()->when + r->position());       
1944 }
1945
1946 void
1947 FadeInDrag::motion (GdkEvent* event, bool)
1948 {
1949         nframes64_t fade_length;
1950
1951         nframes64_t pos = adjusted_current_frame ();
1952
1953         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1954                 _editor->snap_to (pos);
1955         }
1956         
1957         boost::shared_ptr<Region> region = _primary->region ();
1958
1959         if (pos < (region->position() + 64)) {
1960                 fade_length = 64; // this should be a minimum defined somewhere
1961         } else if (pos > region->last_frame()) {
1962                 fade_length = region->length();
1963         } else {
1964                 fade_length = pos - region->position();
1965         }               
1966
1967         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1968
1969                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1970                 
1971                 if (!tmp) {
1972                         continue;
1973                 }
1974         
1975                 tmp->reset_fade_in_shape_width (fade_length);
1976         }
1977
1978         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
1979 }
1980
1981 void
1982 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
1983 {
1984         if (!movement_occurred) {
1985                 return;
1986         }
1987
1988         nframes64_t fade_length;
1989
1990         nframes64_t const pos = adjusted_current_frame ();
1991
1992         boost::shared_ptr<Region> region = _primary->region ();
1993
1994         if (pos < (region->position() + 64)) {
1995                 fade_length = 64; // this should be a minimum defined somewhere
1996         } else if (pos > region->last_frame()) {
1997                 fade_length = region->length();
1998         } else {
1999                 fade_length = pos - region->position();
2000         }
2001                 
2002         _editor->begin_reversible_command (_("change fade in length"));
2003
2004         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2005
2006                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2007                 
2008                 if (!tmp) {
2009                         continue;
2010                 }
2011         
2012                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2013                 XMLNode &before = alist->get_state();
2014
2015                 tmp->audio_region()->set_fade_in_length (fade_length);
2016                 tmp->audio_region()->set_fade_in_active (true);
2017                 
2018                 XMLNode &after = alist->get_state();
2019                 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2020         }
2021
2022         _editor->commit_reversible_command ();
2023 }
2024
2025 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2026         : RegionDrag (e, i, p, v)
2027 {
2028         
2029 }
2030
2031 void
2032 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2033 {
2034         Drag::start_grab (event, cursor);
2035
2036         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2037         boost::shared_ptr<AudioRegion> r = a->audio_region ();
2038         
2039         _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2040 }
2041
2042 void
2043 FadeOutDrag::motion (GdkEvent* event, bool)
2044 {
2045         nframes64_t fade_length;
2046
2047         nframes64_t pos = adjusted_current_frame ();
2048
2049         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2050                 _editor->snap_to (pos);
2051         }
2052
2053         boost::shared_ptr<Region> region = _primary->region ();
2054         
2055         if (pos > (region->last_frame() - 64)) {
2056                 fade_length = 64; // this should really be a minimum fade defined somewhere
2057         }
2058         else if (pos < region->position()) {
2059                 fade_length = region->length();
2060         }
2061         else {
2062                 fade_length = region->last_frame() - pos;
2063         }
2064                 
2065         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2066
2067                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2068                 
2069                 if (!tmp) {
2070                         continue;
2071                 }
2072         
2073                 tmp->reset_fade_out_shape_width (fade_length);
2074         }
2075
2076         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2077 }
2078
2079 void
2080 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2081 {
2082         if (!movement_occurred) {
2083                 return;
2084         }
2085
2086         nframes64_t fade_length;
2087
2088         nframes64_t pos = adjusted_current_frame ();
2089
2090         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2091                 _editor->snap_to (pos);
2092         }
2093
2094         boost::shared_ptr<Region> region = _primary->region ();
2095
2096         if (pos > (region->last_frame() - 64)) {
2097                 fade_length = 64; // this should really be a minimum fade defined somewhere
2098         }
2099         else if (pos < region->position()) {
2100                 fade_length = region->length();
2101         }
2102         else {
2103                 fade_length = region->last_frame() - pos;
2104         }
2105
2106         _editor->begin_reversible_command (_("change fade out length"));
2107
2108         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2109
2110                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2111                 
2112                 if (!tmp) {
2113                         continue;
2114                 }
2115         
2116                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2117                 XMLNode &before = alist->get_state();
2118                 
2119                 tmp->audio_region()->set_fade_out_length (fade_length);
2120                 tmp->audio_region()->set_fade_out_active (true);
2121
2122                 XMLNode &after = alist->get_state();
2123                 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2124         }
2125
2126         _editor->commit_reversible_command ();
2127 }
2128
2129 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2130         : Drag (e, i)
2131 {
2132         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2133         assert (_marker);
2134
2135         _points.push_back (Gnome::Art::Point (0, 0));
2136         _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2137
2138         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2139         _line->property_width_pixels() = 1;
2140         _line->property_points () = _points;
2141         _line->hide ();
2142
2143         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2144 }
2145
2146 MarkerDrag::~MarkerDrag ()
2147 {
2148         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2149                 delete *i;
2150         }
2151 }
2152
2153 void
2154 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2155 {
2156         Drag::start_grab (event, cursor);
2157         
2158         bool is_start;
2159
2160         Location *location = _editor->find_location_from_marker (_marker, is_start);
2161         _editor->_dragging_edit_point = true;
2162
2163         _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end()); 
2164
2165         update_item (location);
2166
2167         // _drag_line->show();
2168         // _line->raise_to_top();
2169
2170         if (is_start) {
2171                 _editor->show_verbose_time_cursor (location->start(), 10);
2172         } else {
2173                 _editor->show_verbose_time_cursor (location->end(), 10);
2174         }
2175
2176         Selection::Operation op = Keyboard::selection_type (event->button.state);
2177
2178         switch (op) {
2179         case Selection::Toggle:
2180                 _editor->selection->toggle (_marker);
2181                 break;
2182         case Selection::Set:
2183                 if (!_editor->selection->selected (_marker)) {
2184                         _editor->selection->set (_marker);
2185                 }
2186                 break;
2187         case Selection::Extend:
2188         {
2189                 Locations::LocationList ll;
2190                 list<Marker*> to_add;
2191                 nframes64_t s, e;
2192                 _editor->selection->markers.range (s, e);
2193                 s = min (_marker->position(), s);
2194                 e = max (_marker->position(), e);
2195                 s = min (s, e);
2196                 e = max (s, e);
2197                 if (e < max_frames) {
2198                         ++e;
2199                 }
2200                 _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2201                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2202                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2203                         if (lm) {
2204                                 if (lm->start) {
2205                                         to_add.push_back (lm->start);
2206                                 }
2207                                 if (lm->end) {
2208                                         to_add.push_back (lm->end);
2209                                 }
2210                         }
2211                 }
2212                 if (!to_add.empty()) {
2213                         _editor->selection->add (to_add);
2214                 }
2215                 break;
2216         }
2217         case Selection::Add:
2218                 _editor->selection->add (_marker);
2219                 break;
2220         }
2221
2222         /* set up copies for us to manipulate during the drag */
2223
2224         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2225                 Location *l = _editor->find_location_from_marker (*i, is_start);
2226                 _copied_locations.push_back (new Location (*l));
2227         }
2228 }
2229
2230 void
2231 MarkerDrag::motion (GdkEvent* event, bool)
2232 {
2233         nframes64_t f_delta = 0;
2234         bool is_start;
2235         bool move_both = false;
2236         Marker* marker;
2237         Location  *real_location;
2238         Location *copy_location = 0;
2239
2240         nframes64_t newframe = adjusted_current_frame ();
2241
2242         nframes64_t next = newframe;
2243
2244         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2245                 _editor->snap_to (newframe, 0, true);
2246         }
2247         
2248         if (_current_pointer_frame == _last_pointer_frame) { 
2249                 return;
2250         }
2251
2252         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2253                 move_both = true;
2254         }
2255
2256         MarkerSelection::iterator i;
2257         list<Location*>::iterator x;
2258
2259         /* find the marker we're dragging, and compute the delta */
2260
2261         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); 
2262              x != _copied_locations.end() && i != _editor->selection->markers.end(); 
2263              ++i, ++x) {
2264
2265                 copy_location = *x;
2266                 marker = *i;
2267
2268                 if (marker == _marker) {
2269
2270                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2271                                 /* que pasa ?? */
2272                                 return;
2273                         }
2274
2275                         if (real_location->is_mark()) {
2276                                 f_delta = newframe - copy_location->start();
2277                         } else {
2278
2279
2280                                 switch (marker->type()) {
2281                                 case Marker::Start:
2282                                 case Marker::LoopStart:
2283                                 case Marker::PunchIn:
2284                                         f_delta = newframe - copy_location->start();
2285                                         break;
2286
2287                                 case Marker::End:
2288                                 case Marker::LoopEnd:
2289                                 case Marker::PunchOut:
2290                                         f_delta = newframe - copy_location->end();
2291                                         break;
2292                                 default:
2293                                         /* what kind of marker is this ? */
2294                                         return;
2295                                 }
2296                         }
2297                         break;
2298                 }
2299         }
2300
2301         if (i == _editor->selection->markers.end()) {
2302                 /* hmm, impossible - we didn't find the dragged marker */
2303                 return;
2304         }
2305
2306         /* now move them all */
2307
2308         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); 
2309              x != _copied_locations.end() && i != _editor->selection->markers.end(); 
2310              ++i, ++x) {
2311
2312                 copy_location = *x;
2313                 marker = *i;
2314
2315                 /* call this to find out if its the start or end */
2316                 
2317                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2318                         continue;
2319                 }
2320                 
2321                 if (real_location->locked()) {
2322                         continue;
2323                 }
2324
2325                 if (copy_location->is_mark()) {
2326
2327                         /* just move it */
2328                         
2329                         copy_location->set_start (copy_location->start() + f_delta);
2330
2331                 } else {
2332                         
2333                         nframes64_t new_start = copy_location->start() + f_delta;
2334                         nframes64_t new_end = copy_location->end() + f_delta;
2335                         
2336                         if (is_start) { // start-of-range marker
2337                                 
2338                                 if (move_both) {
2339                                         copy_location->set_start (new_start);
2340                                         copy_location->set_end (new_end);
2341                                 } else  if (new_start < copy_location->end()) {
2342                                         copy_location->set_start (new_start);
2343                                 } else { 
2344                                         _editor->snap_to (next, 1, true);
2345                                         copy_location->set_end (next);
2346                                         copy_location->set_start (newframe);
2347                                 }
2348                                 
2349                         } else { // end marker
2350                                 
2351                                 if (move_both) {
2352                                         copy_location->set_end (new_end);
2353                                         copy_location->set_start (new_start);
2354                                 } else if (new_end > copy_location->start()) {
2355                                         copy_location->set_end (new_end);
2356                                 } else if (newframe > 0) {
2357                                         _editor->snap_to (next, -1, true);
2358                                         copy_location->set_start (next);
2359                                         copy_location->set_end (newframe);
2360                                 }
2361                         }
2362                 }
2363
2364                 update_item (copy_location);
2365
2366                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2367
2368                 if (lm) {
2369                         lm->set_position (copy_location->start(), copy_location->end());
2370                 }
2371         }
2372
2373         _last_pointer_frame = _current_pointer_frame;
2374
2375         assert (!_copied_locations.empty());
2376
2377         _editor->edit_point_clock.set (_copied_locations.front()->start());
2378         _editor->show_verbose_time_cursor (newframe, 10);
2379
2380 #ifdef GTKOSX
2381         _editor->track_canvas->update_now ();
2382 #endif
2383         _editor->edit_point_clock.set (copy_location->start());
2384 }
2385
2386 void
2387 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2388 {
2389         if (!movement_occurred) {
2390
2391                 /* just a click, do nothing but finish
2392                    off the selection process
2393                 */
2394
2395                 Selection::Operation op = Keyboard::selection_type (event->button.state);
2396
2397                 switch (op) {
2398                 case Selection::Set:
2399                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2400                                 _editor->selection->set (_marker);
2401                         }
2402                         break;
2403
2404                 case Selection::Toggle:
2405                 case Selection::Extend:
2406                 case Selection::Add:
2407                         break;
2408                 }
2409                 
2410                 return;
2411         }
2412
2413         _editor->_dragging_edit_point = false;
2414         
2415         _editor->begin_reversible_command ( _("move marker") );
2416         XMLNode &before = _editor->session->locations()->get_state();
2417
2418         MarkerSelection::iterator i;
2419         list<Location*>::iterator x;
2420         bool is_start;
2421
2422         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin(); 
2423              x != _copied_locations.end() && i != _editor->selection->markers.end(); 
2424              ++i, ++x) {
2425         
2426                 Location * location = _editor->find_location_from_marker (*i, is_start);
2427                 
2428                 if (location) {
2429                         
2430                         if (location->locked()) {
2431                                 return;
2432                         }
2433                         
2434                         if (location->is_mark()) {
2435                                 location->set_start ((*x)->start());
2436                         } else {
2437                                 location->set ((*x)->start(), (*x)->end());
2438                         }
2439                 }
2440         }
2441
2442         XMLNode &after = _editor->session->locations()->get_state();
2443         _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
2444         _editor->commit_reversible_command ();
2445         
2446         _line->hide();
2447 }
2448
2449 void
2450 MarkerDrag::update_item (Location* location)
2451 {
2452         double const x1 = _editor->frame_to_pixel (location->start());
2453
2454         _points.front().set_x(x1);
2455         _points.back().set_x(x1);
2456         _line->property_points() = _points;
2457 }
2458
2459 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2460         : Drag (e, i),
2461           _cumulative_x_drag (0),
2462           _cumulative_y_drag (0)
2463 {
2464         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2465         assert (_point);
2466 }
2467
2468
2469 void
2470 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2471 {
2472         Drag::start_grab (event, _editor->fader_cursor);
2473
2474         // start the grab at the center of the control point so
2475         // the point doesn't 'jump' to the mouse after the first drag
2476         _grab_x = _point->get_x();
2477         _grab_y = _point->get_y();
2478
2479         _point->line().parent_group().i2w (_grab_x, _grab_y);
2480         _editor->track_canvas->w2c (_grab_x, _grab_y, _grab_x, _grab_y);
2481
2482         _grab_frame = _editor->pixel_to_frame (_grab_x);
2483
2484         _point->line().start_drag (_point, _grab_frame, 0);
2485
2486         float fraction = 1.0 - (_point->get_y() / _point->line().height());
2487         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction), 
2488                                             _current_pointer_x + 10, _current_pointer_y + 10);
2489
2490         _editor->show_verbose_canvas_cursor ();
2491 }
2492
2493 void
2494 ControlPointDrag::motion (GdkEvent* event, bool)
2495 {
2496         double dx = _current_pointer_x - _last_pointer_x;
2497         double dy = _current_pointer_y - _last_pointer_y;
2498
2499         if (event->button.state & Keyboard::SecondaryModifier) {
2500                 dx *= 0.1;
2501                 dy *= 0.1;
2502         }
2503
2504         double cx = _grab_x + _cumulative_x_drag + dx;
2505         double cy = _grab_y + _cumulative_y_drag + dy;
2506
2507         // calculate zero crossing point. back off by .01 to stay on the
2508         // positive side of zero
2509         double _unused = 0;
2510         double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2511         _point->line().parent_group().i2w(_unused, zero_gain_y);
2512
2513         // make sure we hit zero when passing through
2514         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2515                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2516                 cy = zero_gain_y;
2517         }
2518
2519         if (_x_constrained) {
2520                 cx = _grab_x;
2521         }
2522         if (_y_constrained) {
2523                 cy = _grab_y;
2524         }
2525
2526         _cumulative_x_drag = cx - _grab_x;
2527         _cumulative_y_drag = cy - _grab_y;
2528
2529         _point->line().parent_group().w2i (cx, cy);
2530
2531         cx = max (0.0, cx);
2532         cy = max (0.0, cy);
2533         cy = min ((double) _point->line().height(), cy);
2534
2535         //translate cx to frames
2536         nframes64_t cx_frames = _editor->unit_to_frame (cx);
2537
2538         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !_x_constrained) {
2539                 _editor->snap_to (cx_frames);
2540         }
2541
2542         float const fraction = 1.0 - (cy / _point->line().height());
2543
2544         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2545
2546         _point->line().point_drag (*_point, cx_frames, fraction, push);
2547         
2548         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2549 }
2550
2551 void
2552 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2553 {
2554         if (!movement_occurred) {
2555
2556                 /* just a click */
2557                 
2558                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2559                         _editor->reset_point_selection ();
2560                 }
2561
2562         } else {
2563                 motion (event, false);
2564         }
2565         _point->line().end_drag (_point);
2566 }
2567
2568 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2569         : Drag (e, i),
2570           _line (0),
2571           _cumulative_y_drag (0)
2572 {
2573
2574 }
2575 void
2576 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2577 {
2578         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2579         assert (_line);
2580
2581         _item = &_line->grab_item ();
2582
2583         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2584            origin, and ditto for y.
2585         */
2586
2587         double cx = event->button.x;
2588         double cy = event->button.y;
2589
2590         _line->parent_group().w2i (cx, cy);
2591
2592         nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2593
2594         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
2595                 /* no adjacent points */
2596                 return;
2597         }
2598
2599         Drag::start_grab (event, _editor->fader_cursor);
2600
2601         /* store grab start in parent frame */
2602
2603         _grab_x = cx;
2604         _grab_y = cy;
2605
2606         double fraction = 1.0 - (cy / _line->height());
2607
2608         _line->start_drag (0, _grab_frame, fraction);
2609         
2610         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2611                                             _current_pointer_x + 10, _current_pointer_y + 10);
2612         
2613         _editor->show_verbose_canvas_cursor ();
2614 }
2615
2616 void
2617 LineDrag::motion (GdkEvent* event, bool)
2618 {
2619         double dy = _current_pointer_y - _last_pointer_y;
2620         
2621         if (event->button.state & Keyboard::SecondaryModifier) {
2622                 dy *= 0.1;
2623         }
2624
2625         double cy = _grab_y + _cumulative_y_drag + dy;
2626
2627         _cumulative_y_drag = cy - _grab_y;
2628
2629         cy = max (0.0, cy);
2630         cy = min ((double) _line->height(), cy);
2631
2632         double const fraction = 1.0 - (cy / _line->height());
2633
2634         bool push;
2635
2636         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2637                 push = false;
2638         } else {
2639                 push = true;
2640         }
2641
2642         _line->line_drag (_before, _after, fraction, push);
2643         
2644         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2645 }
2646
2647 void
2648 LineDrag::finished (GdkEvent* event, bool)
2649 {
2650         motion (event, false);
2651         _line->end_drag (0);
2652 }
2653
2654 void
2655 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2656 {
2657         Drag::start_grab (event);
2658         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2659 }
2660
2661 void
2662 RubberbandSelectDrag::motion (GdkEvent* event, bool first_move)
2663 {
2664         nframes64_t start;
2665         nframes64_t end;
2666         double y1;
2667         double y2;
2668
2669         /* use a bigger drag threshold than the default */
2670
2671         if (abs ((int) (_current_pointer_frame - _grab_frame)) < 8) {
2672                 return;
2673         }
2674
2675         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
2676                 if (first_move) {
2677                         _editor->snap_to (_grab_frame);
2678                 } 
2679                 _editor->snap_to (_current_pointer_frame);
2680         }
2681
2682         /* base start and end on initial click position */
2683
2684         if (_current_pointer_frame < _grab_frame) {
2685                 start = _current_pointer_frame;
2686                 end = _grab_frame;
2687         } else {
2688                 end = _current_pointer_frame;
2689                 start = _grab_frame;
2690         }
2691
2692         if (_current_pointer_y < _grab_y) {
2693                 y1 = _current_pointer_y;
2694                 y2 = _grab_y;
2695         } else {
2696                 y2 = _current_pointer_y;
2697                 y1 = _grab_y;
2698         }
2699
2700         
2701         if (start != end || y1 != y2) {
2702
2703                 double x1 = _editor->frame_to_pixel (start);
2704                 double x2 = _editor->frame_to_pixel (end);
2705                 
2706                 _editor->rubberband_rect->property_x1() = x1;
2707                 _editor->rubberband_rect->property_y1() = y1;
2708                 _editor->rubberband_rect->property_x2() = x2;
2709                 _editor->rubberband_rect->property_y2() = y2;
2710
2711                 _editor->rubberband_rect->show();
2712                 _editor->rubberband_rect->raise_to_top();
2713                 
2714                 _last_pointer_frame = _current_pointer_frame;
2715
2716                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2717         }
2718 }
2719
2720 void
2721 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2722 {
2723         if (movement_occurred) {
2724
2725                 motion (event, false);
2726
2727                 double y1,y2;
2728                 if (_current_pointer_y < _grab_y) {
2729                         y1 = _current_pointer_y;
2730                         y2 = _grab_y;
2731                 } else {
2732                         y2 = _current_pointer_y;
2733                         y1 = _grab_y;
2734                 }
2735
2736
2737                 Selection::Operation op = Keyboard::selection_type (event->button.state);
2738                 bool commit;
2739
2740                 _editor->begin_reversible_command (_("rubberband selection"));
2741
2742                 if (_grab_frame < _last_pointer_frame) {
2743                         commit = _editor->select_all_within (_grab_frame, _last_pointer_frame, y1, y2, _editor->track_views, op);
2744                 } else {
2745                         commit = _editor->select_all_within (_last_pointer_frame, _grab_frame, y1, y2, _editor->track_views, op);
2746                 }               
2747
2748                 if (commit) {
2749                         _editor->commit_reversible_command ();
2750                 }
2751                 
2752         } else {
2753                 if (!getenv("ARDOUR_SAE")) {
2754                         _editor->selection->clear_tracks();
2755                 }
2756                 _editor->selection->clear_regions();
2757                 _editor->selection->clear_points ();
2758                 _editor->selection->clear_lines ();
2759         }
2760
2761         _editor->rubberband_rect->hide();
2762 }
2763
2764 void
2765 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2766 {
2767         Drag::start_grab (event);
2768         
2769         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2770 }
2771
2772 void
2773 TimeFXDrag::motion (GdkEvent* event, bool)
2774 {
2775         RegionView* rv = _primary;
2776
2777         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2778                 _editor->snap_to (_current_pointer_frame);
2779         }
2780
2781         if (_current_pointer_frame == _last_pointer_frame) {
2782                 return;
2783         }
2784
2785         if (_current_pointer_frame > rv->region()->position()) {
2786                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), _current_pointer_frame);
2787         }
2788
2789         _last_pointer_frame = _current_pointer_frame;
2790
2791         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2792 }
2793
2794 void
2795 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
2796 {
2797         _primary->get_time_axis_view().hide_timestretch ();
2798
2799         if (!movement_occurred) {
2800                 return;
2801         }
2802
2803         if (_last_pointer_frame < _primary->region()->position()) {
2804                 /* backwards drag of the left edge - not usable */
2805                 return;
2806         }
2807         
2808         nframes64_t newlen = _last_pointer_frame - _primary->region()->position();
2809
2810         float percentage = (double) newlen / (double) _primary->region()->length();
2811         
2812 #ifndef USE_RUBBERBAND
2813         // Soundtouch uses percentage / 100 instead of normal (/ 1) 
2814         if (_primary->region()->data_type() == DataType::AUDIO) {
2815                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2816         }
2817 #endif  
2818         
2819         _editor->begin_reversible_command (_("timestretch"));
2820         
2821         // XXX how do timeFX on multiple regions ?
2822         
2823         RegionSelection rs;
2824         rs.add (_primary);
2825
2826         if (_editor->time_stretch (rs, percentage) == 0) {
2827                 _editor->session->commit_reversible_command ();
2828         }
2829 }
2830
2831 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2832         : Drag (e, i),
2833           _operation (o),
2834           _copy (false)
2835 {
2836
2837 }
2838
2839 void
2840 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2841 {
2842         nframes64_t start = 0;
2843         nframes64_t end = 0;
2844
2845         if (_editor->session == 0) {
2846                 return;
2847         }
2848
2849         Gdk::Cursor* cursor = 0;
2850
2851         switch (_operation) {
2852         case CreateSelection:
2853                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2854                         _copy = true;
2855                 } else {
2856                         _copy = false;
2857                 }
2858                 cursor = _editor->selector_cursor;
2859                 Drag::start_grab (event, cursor);
2860                 break;
2861
2862         case SelectionStartTrim:
2863                 if (_editor->clicked_axisview) {
2864                         _editor->clicked_axisview->order_selection_trims (_item, true);
2865                 } 
2866                 Drag::start_grab (event, cursor);
2867                 cursor = _editor->trimmer_cursor;
2868                 start = _editor->selection->time[_editor->clicked_selection].start;
2869                 _pointer_frame_offset = _grab_frame - start;    
2870                 break;
2871                 
2872         case SelectionEndTrim:
2873                 if (_editor->clicked_axisview) {
2874                         _editor->clicked_axisview->order_selection_trims (_item, false);
2875                 }
2876                 Drag::start_grab (event, cursor);
2877                 cursor = _editor->trimmer_cursor;
2878                 end = _editor->selection->time[_editor->clicked_selection].end;
2879                 _pointer_frame_offset = _grab_frame - end;      
2880                 break;
2881
2882         case SelectionMove:
2883                 start = _editor->selection->time[_editor->clicked_selection].start;
2884                 Drag::start_grab (event, cursor);
2885                 _pointer_frame_offset = _grab_frame - start;    
2886                 break;
2887         }
2888
2889         if (_operation == SelectionMove) {
2890                 _editor->show_verbose_time_cursor (start, 10);  
2891         } else {
2892                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
2893         }
2894 }
2895
2896 void
2897 SelectionDrag::motion (GdkEvent* event, bool first_move)
2898 {
2899         nframes64_t start = 0;
2900         nframes64_t end = 0;
2901         nframes64_t length;
2902
2903         nframes64_t pending_position = adjusted_current_frame ();
2904         
2905         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2906                 _editor->snap_to (pending_position);
2907         }
2908
2909         /* only alter selection if the current frame is 
2910            different from the last frame position (adjusted)
2911          */
2912         
2913         if (pending_position == _last_pointer_frame) {
2914                 return;
2915         }
2916         
2917         switch (_operation) {
2918         case CreateSelection:
2919                 
2920                 if (first_move) {
2921                         _editor->snap_to (_grab_frame);
2922                 }
2923                 
2924                 if (pending_position < _grab_frame) {
2925                         start = pending_position;
2926                         end = _grab_frame;
2927                 } else {
2928                         end = pending_position;
2929                         start = _grab_frame;
2930                 }
2931                 
2932                 /* first drag: Either add to the selection
2933                    or create a new selection->
2934                 */
2935                 
2936                 if (first_move) {
2937                         
2938                         _editor->begin_reversible_command (_("range selection"));
2939                         
2940                         if (_copy) {
2941                                 /* adding to the selection */
2942                                 _editor->clicked_selection = _editor->selection->add (start, end);
2943                                 _copy = false;
2944                         } else {
2945                                 /* new selection-> */
2946                                 _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end);
2947                         }
2948                 } 
2949                 break;
2950                 
2951         case SelectionStartTrim:
2952                 
2953                 if (first_move) {
2954                         _editor->begin_reversible_command (_("trim selection start"));
2955                 }
2956                 
2957                 start = _editor->selection->time[_editor->clicked_selection].start;
2958                 end = _editor->selection->time[_editor->clicked_selection].end;
2959
2960                 if (pending_position > end) {
2961                         start = end;
2962                 } else {
2963                         start = pending_position;
2964                 }
2965                 break;
2966                 
2967         case SelectionEndTrim:
2968                 
2969                 if (first_move) {
2970                         _editor->begin_reversible_command (_("trim selection end"));
2971                 }
2972                 
2973                 start = _editor->selection->time[_editor->clicked_selection].start;
2974                 end = _editor->selection->time[_editor->clicked_selection].end;
2975
2976                 if (pending_position < start) {
2977                         end = start;
2978                 } else {
2979                         end = pending_position;
2980                 }
2981                 
2982                 break;
2983                 
2984         case SelectionMove:
2985                 
2986                 if (first_move) {
2987                         _editor->begin_reversible_command (_("move selection"));
2988                 }
2989                 
2990                 start = _editor->selection->time[_editor->clicked_selection].start;
2991                 end = _editor->selection->time[_editor->clicked_selection].end;
2992                 
2993                 length = end - start;
2994                 
2995                 start = pending_position;
2996                 _editor->snap_to (start);
2997                 
2998                 end = start + length;
2999                 
3000                 break;
3001         }
3002         
3003         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->canvas_width) {
3004                 _editor->start_canvas_autoscroll (1, 0);
3005         }
3006
3007         if (start != end) {
3008                 _editor->selection->replace (_editor->clicked_selection, start, end);
3009         }
3010
3011         _last_pointer_frame = pending_position;
3012
3013         if (_operation == SelectionMove) {
3014                 _editor->show_verbose_time_cursor(start, 10);   
3015         } else {
3016                 _editor->show_verbose_time_cursor(pending_position, 10);        
3017         }
3018 }
3019
3020 void
3021 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3022 {
3023         if (movement_occurred) {
3024                 motion (event, false);
3025                 /* XXX this is not object-oriented programming at all. ick */
3026                 if (_editor->selection->time.consolidate()) {
3027                         _editor->selection->TimeChanged ();
3028                 }
3029                 _editor->commit_reversible_command ();
3030         } else {
3031                 /* just a click, no pointer movement.*/
3032
3033                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3034
3035                         _editor->selection->clear_time();
3036
3037                 } 
3038         }
3039
3040         /* XXX what happens if its a music selection? */
3041         _editor->session->set_audio_range (_editor->selection->time);
3042         _editor->stop_canvas_autoscroll ();
3043 }
3044
3045 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3046         : Drag (e, i),
3047           _operation (o),
3048           _copy (false)
3049 {
3050         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3051         _drag_rect->hide ();
3052
3053         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3054         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3055 }
3056
3057 void
3058 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3059 {
3060         if (_editor->session == 0) {
3061                 return;
3062         }
3063
3064         Gdk::Cursor* cursor = 0;
3065
3066         if (!_editor->temp_location) {
3067                 _editor->temp_location = new Location;
3068         }
3069         
3070         switch (_operation) {
3071         case CreateRangeMarker:
3072         case CreateTransportMarker:
3073         case CreateCDMarker:
3074         
3075                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3076                         _copy = true;
3077                 } else {
3078                         _copy = false;
3079                 }
3080                 cursor = _editor->selector_cursor;
3081                 break;
3082         }
3083
3084         Drag::start_grab (event, cursor);
3085
3086         _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
3087 }
3088
3089 void
3090 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3091 {
3092         nframes64_t start = 0;
3093         nframes64_t end = 0;
3094         ArdourCanvas::SimpleRect *crect;
3095
3096         switch (_operation) {
3097         case CreateRangeMarker:
3098                 crect = _editor->range_bar_drag_rect;
3099                 break;
3100         case CreateTransportMarker:
3101                 crect = _editor->transport_bar_drag_rect;
3102                 break;
3103         case CreateCDMarker:
3104                 crect = _editor->cd_marker_bar_drag_rect;
3105                 break;
3106         default:
3107                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3108                 return;
3109                 break;
3110         }
3111         
3112         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3113                 _editor->snap_to (_current_pointer_frame);
3114         }
3115
3116         /* only alter selection if the current frame is 
3117            different from the last frame position.
3118          */
3119         
3120         if (_current_pointer_frame == _last_pointer_frame) {
3121                 return;
3122         }
3123         
3124         switch (_operation) {
3125         case CreateRangeMarker:
3126         case CreateTransportMarker:
3127         case CreateCDMarker:
3128                 if (first_move) {
3129                         _editor->snap_to (_grab_frame);
3130                 }
3131                 
3132                 if (_current_pointer_frame < _grab_frame) {
3133                         start = _current_pointer_frame;
3134                         end = _grab_frame;
3135                 } else {
3136                         end = _current_pointer_frame;
3137                         start = _grab_frame;
3138                 }
3139                 
3140                 /* first drag: Either add to the selection
3141                    or create a new selection.
3142                 */
3143                 
3144                 if (first_move) {
3145                         
3146                         _editor->temp_location->set (start, end);
3147                         
3148                         crect->show ();
3149
3150                         update_item (_editor->temp_location);
3151                         _drag_rect->show();
3152                         //_drag_rect->raise_to_top();
3153                         
3154                 } 
3155                 break;          
3156         }
3157         
3158         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->canvas_width) {
3159                 _editor->start_canvas_autoscroll (1, 0);
3160         }
3161         
3162         if (start != end) {
3163                 _editor->temp_location->set (start, end);
3164
3165                 double x1 = _editor->frame_to_pixel (start);
3166                 double x2 = _editor->frame_to_pixel (end);
3167                 crect->property_x1() = x1;
3168                 crect->property_x2() = x2;
3169
3170                 update_item (_editor->temp_location);
3171         }
3172
3173         _last_pointer_frame = _current_pointer_frame;
3174
3175         _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
3176         
3177 }
3178
3179 void
3180 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3181 {
3182         Location * newloc = 0;
3183         string rangename;
3184         int flags;
3185         
3186         if (movement_occurred) {
3187                 motion (event, false);
3188
3189                 switch (_operation) {
3190                 case CreateRangeMarker:
3191                 case CreateCDMarker:
3192                     {
3193                         _editor->begin_reversible_command (_("new range marker"));
3194                         XMLNode &before = _editor->session->locations()->get_state();
3195                         _editor->session->locations()->next_available_name(rangename,"unnamed");
3196                         if (_operation == CreateCDMarker) {
3197                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3198                                 _editor->cd_marker_bar_drag_rect->hide();
3199                         }
3200                         else {
3201                                 flags = Location::IsRangeMarker;
3202                                 _editor->range_bar_drag_rect->hide();
3203                         }
3204                         newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3205                         _editor->session->locations()->add (newloc, true);
3206                         XMLNode &after = _editor->session->locations()->get_state();
3207                         _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
3208                         _editor->commit_reversible_command ();
3209                         
3210                         _drag_rect->hide();
3211                         break;
3212                     }
3213
3214                 case CreateTransportMarker:
3215                         // popup menu to pick loop or punch
3216                         _editor->new_transport_marker_context_menu (&event->button, _item);
3217                         
3218                         break;
3219                 }
3220         } else {
3221                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3222
3223                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3224
3225                         nframes64_t start;
3226                         nframes64_t end;
3227
3228                         start = _editor->session->locations()->first_mark_before (_grab_frame);
3229                         end = _editor->session->locations()->first_mark_after (_grab_frame);
3230                         
3231                         if (end == max_frames) {
3232                                 end = _editor->session->current_end_frame ();
3233                         }
3234
3235                         if (start == 0) {
3236                                 start = _editor->session->current_start_frame ();
3237                         }
3238
3239                         switch (_editor->mouse_mode) {
3240                         case MouseObject:
3241                                 /* find the two markers on either side and then make the selection from it */
3242                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3243                                 break;
3244
3245                         case MouseRange:
3246                                 /* find the two markers on either side of the click and make the range out of it */
3247                                 _editor->selection->set (0, start, end);
3248                                 break;
3249
3250                         default:
3251                                 break;
3252                         }
3253                 } 
3254         }
3255
3256         _editor->stop_canvas_autoscroll ();
3257 }
3258
3259
3260
3261 void
3262 RangeMarkerBarDrag::update_item (Location* location)
3263 {
3264         double const x1 = _editor->frame_to_pixel (location->start());
3265         double const x2 = _editor->frame_to_pixel (location->end());
3266
3267         _drag_rect->property_x1() = x1;
3268         _drag_rect->property_x2() = x2;
3269 }
3270
3271 void
3272 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3273 {
3274         Drag::start_grab (event, _editor->zoom_cursor);
3275         _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3276 }
3277
3278 void
3279 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3280 {
3281         nframes64_t start;
3282         nframes64_t end;
3283
3284         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3285                 _editor->snap_to (_current_pointer_frame);
3286                 
3287                 if (first_move) {
3288                         _editor->snap_to (_grab_frame);
3289                 }
3290         }
3291                 
3292         if (_current_pointer_frame == _last_pointer_frame) {
3293                 return;
3294         }
3295
3296         /* base start and end on initial click position */
3297         if (_current_pointer_frame < _grab_frame) {
3298                 start = _current_pointer_frame;
3299                 end = _grab_frame;
3300         } else {
3301                 end = _current_pointer_frame;
3302                 start = _grab_frame;
3303         }
3304         
3305         if (start != end) {
3306
3307                 if (first_move) {
3308                         _editor->zoom_rect->show();
3309                         _editor->zoom_rect->raise_to_top();
3310                 }
3311
3312                 _editor->reposition_zoom_rect(start, end);
3313
3314                 _last_pointer_frame = _current_pointer_frame;
3315
3316                 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3317         }
3318 }
3319
3320 void
3321 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3322 {
3323         if (movement_occurred) {
3324                 motion (event, false);
3325                 
3326                 if (_grab_frame < _last_pointer_frame) {
3327                         _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");
3328                 } else {
3329                         _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom");
3330                 }               
3331         } else {
3332                 _editor->temporal_zoom_to_frame (false, _grab_frame);
3333                 /*
3334                 temporal_zoom_step (false);
3335                 center_screen (_grab_frame);
3336                 */
3337         }
3338
3339         _editor->zoom_rect->hide();
3340 }