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