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