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