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