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