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