5c7c679d726d13fd4559e11fe541aaab6fc64020
[ardour.git] / gtk2_ardour / editor_mouse.cc
1
2 /*
3     Copyright (C) 2000-2001 Paul Davis 
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <cassert>
22 #include <cstdlib>
23 #include <stdint.h>
24 #include <cmath>
25 #include <set>
26 #include <string>
27 #include <algorithm>
28
29 #include <pbd/error.h>
30 #include <gtkmm2ext/utils.h>
31 #include <pbd/memento_command.h>
32 #include <pbd/basename.h>
33
34 #include "ardour_ui.h"
35 #include "editor.h"
36 #include "time_axis_view.h"
37 #include "audio_time_axis.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
40 #include "marker.h"
41 #include "streamview.h"
42 #include "region_gain_line.h"
43 #include "automation_time_axis.h"
44 #include "control_point.h"
45 #include "prompter.h"
46 #include "utils.h"
47 #include "selection.h"
48 #include "keyboard.h"
49 #include "editing.h"
50 #include "rgb_macros.h"
51
52 #include <ardour/types.h>
53 #include <ardour/profile.h>
54 #include <ardour/route.h>
55 #include <ardour/audio_track.h>
56 #include <ardour/audio_diskstream.h>
57 #include <ardour/midi_diskstream.h>
58 #include <ardour/playlist.h>
59 #include <ardour/audioplaylist.h>
60 #include <ardour/audioregion.h>
61 #include <ardour/midi_region.h>
62 #include <ardour/dB.h>
63 #include <ardour/utils.h>
64 #include <ardour/region_factory.h>
65 #include <ardour/source_factory.h>
66
67 #include <bitset>
68
69 #include "i18n.h"
70
71 using namespace std;
72 using namespace ARDOUR;
73 using namespace PBD;
74 using namespace sigc;
75 using namespace Gtk;
76 using namespace Editing;
77
78 const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
79
80 bool
81 Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
82 {
83         int x, y;
84         double wx, wy;
85         Gdk::ModifierType mask;
86         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
87         Glib::RefPtr<const Gdk::Window> pointer_window;
88
89         if (!canvas_window) {
90                 return false;
91         }
92         
93         pointer_window = canvas_window->get_pointer (x, y, mask);
94
95         if (pointer_window == track_canvas->get_bin_window()) {
96                 wx = x;
97                 wy = y;
98                 in_track_canvas = true;
99
100         } else {
101                 in_track_canvas = false;
102                         return false;
103         }
104
105         GdkEvent event;
106         event.type = GDK_BUTTON_RELEASE;
107         event.button.x = wx;
108         event.button.y = wy;
109         
110         where = event_frame (&event, 0, 0);
111         return true;
112 }
113
114 nframes64_t
115 Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
116 {
117         double cx, cy;
118
119         if (pcx == 0) {
120                 pcx = &cx;
121         }
122         if (pcy == 0) {
123                 pcy = &cy;
124         }
125
126         *pcx = 0;
127         *pcy = 0;
128
129         switch (event->type) {
130         case GDK_BUTTON_RELEASE:
131         case GDK_BUTTON_PRESS:
132         case GDK_2BUTTON_PRESS:
133         case GDK_3BUTTON_PRESS:
134
135                 *pcx = event->button.x;
136                 *pcy = event->button.y;
137                 _trackview_group->w2i(*pcx, *pcy);
138                 break;
139         case GDK_MOTION_NOTIFY:
140         
141                 *pcx = event->motion.x;
142                 *pcy = event->motion.y;
143                 _trackview_group->w2i(*pcx, *pcy);
144                 break;
145         case GDK_ENTER_NOTIFY:
146         case GDK_LEAVE_NOTIFY:
147                 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
148                 break;
149         case GDK_KEY_PRESS:
150         case GDK_KEY_RELEASE:
151                 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
152                 break;
153         default:
154                 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
155                 break;
156         }
157
158         /* note that pixel_to_frame() never returns less than zero, so even if the pixel
159            position is negative (as can be the case with motion events in particular),
160            the frame location is always positive.
161         */
162         
163         return pixel_to_frame (*pcx);
164 }
165
166 void
167 Editor::mouse_mode_toggled (MouseMode m)
168 {
169         if (ignore_mouse_mode_toggle) {
170                 return;
171         }
172
173         switch (m) {
174         case MouseRange:
175                 if (mouse_select_button.get_active()) {
176                         set_mouse_mode (m);
177                 }
178                 break;
179
180         case MouseObject:
181                 if (mouse_move_button.get_active()) {
182                         set_mouse_mode (m);
183                 }
184                 break;
185
186         case MouseGain:
187                 if (mouse_gain_button.get_active()) {
188                         set_mouse_mode (m);
189                 }
190                 break;
191
192         case MouseZoom:
193                 if (mouse_zoom_button.get_active()) {
194                         set_mouse_mode (m);
195                 }
196                 break;
197
198         case MouseTimeFX:
199                 if (mouse_timefx_button.get_active()) {
200                         set_mouse_mode (m);
201                 }
202                 break;
203
204         case MouseAudition:
205                 if (mouse_audition_button.get_active()) {
206                         set_mouse_mode (m);
207                 }
208                 break;
209         
210         case MouseNote:
211                 if (mouse_note_button.get_active()) {
212                         set_mouse_mode (m);
213                 }
214                 break;
215
216         default:
217                 break;
218         }
219 }       
220
221 Gdk::Cursor*
222 Editor::which_grabber_cursor ()
223 {
224         switch (_edit_point) {
225         case EditAtMouse:
226                 return grabber_edit_point_cursor;
227                 break;
228         default:
229                 break;
230         }
231         return grabber_cursor;
232 }
233
234 void
235 Editor::set_canvas_cursor ()
236 {
237         switch (mouse_mode) {
238         case MouseRange:
239                 current_canvas_cursor = selector_cursor;
240                 break;
241
242         case MouseObject:
243                 current_canvas_cursor = which_grabber_cursor();
244                 break;
245
246         case MouseGain:
247                 current_canvas_cursor = cross_hair_cursor;
248                 break;
249
250         case MouseZoom:
251                 current_canvas_cursor = zoom_cursor;
252                 break;
253
254         case MouseTimeFX:
255                 current_canvas_cursor = time_fx_cursor; // just use playhead
256                 break;
257
258         case MouseAudition:
259                 current_canvas_cursor = speaker_cursor;
260                 break;
261         
262         case MouseNote:
263                 set_midi_edit_cursor (current_midi_edit_mode());
264                 break;
265         }
266
267         if (is_drawable()) {
268                 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
269         }
270 }
271
272 void
273 Editor::set_mouse_mode (MouseMode m, bool force)
274 {
275         if (drag_info.item) {
276                 return;
277         }
278
279         if (!force && m == mouse_mode) {
280                 return;
281         }
282         
283         mouse_mode = m;
284
285         instant_save ();
286
287         if (mouse_mode != MouseRange) {
288
289                 /* in all modes except range, hide the range selection,
290                    show the object (region) selection.
291                 */
292
293                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
294                         (*i)->set_should_show_selection (true);
295                 }
296                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
297                         (*i)->hide_selection ();
298                 }
299
300         } else {
301
302                 /* 
303                    in range mode,show the range selection.
304                 */
305
306                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
307                         if ((*i)->get_selected()) {
308                                 (*i)->show_selection (selection->time);
309                         }
310                 }
311         }
312
313         /* XXX the hack of unsetting all other buttons should go 
314            away once GTK2 allows us to use regular radio buttons drawn like
315            normal buttons, rather than my silly GroupedButton hack.
316         */
317         
318         ignore_mouse_mode_toggle = true;
319
320         switch (mouse_mode) {
321         case MouseRange:
322                 mouse_select_button.set_active (true);
323                 break;
324
325         case MouseObject:
326                 mouse_move_button.set_active (true);
327                 break;
328
329         case MouseGain:
330                 mouse_gain_button.set_active (true);
331                 break;
332
333         case MouseZoom:
334                 mouse_zoom_button.set_active (true);
335                 break;
336
337         case MouseTimeFX:
338                 mouse_timefx_button.set_active (true);
339                 break;
340
341         case MouseAudition:
342                 mouse_audition_button.set_active (true);
343                 break;
344         
345         case MouseNote:
346                 mouse_note_button.set_active (true);
347                 set_midi_edit_cursor (current_midi_edit_mode());
348                 break;
349         }
350
351         if (mouse_mode == MouseNote)
352                 midi_toolbar_frame.show();
353         else
354                 midi_toolbar_frame.hide();
355
356         ignore_mouse_mode_toggle = false;
357         
358         set_canvas_cursor ();
359 }
360
361 void
362 Editor::step_mouse_mode (bool next)
363 {
364         switch (current_mouse_mode()) {
365         case MouseObject:
366                 if (next) set_mouse_mode (MouseRange);
367                 else set_mouse_mode (MouseTimeFX);
368                 break;
369
370         case MouseRange:
371                 if (next) set_mouse_mode (MouseZoom);
372                 else set_mouse_mode (MouseObject);
373                 break;
374
375         case MouseZoom:
376                 if (next) set_mouse_mode (MouseGain);
377                 else set_mouse_mode (MouseRange);
378                 break;
379         
380         case MouseGain:
381                 if (next) set_mouse_mode (MouseTimeFX);
382                 else set_mouse_mode (MouseZoom);
383                 break;
384         
385         case MouseTimeFX:
386                 if (next) set_mouse_mode (MouseAudition);
387                 else set_mouse_mode (MouseGain);
388                 break;
389
390         case MouseAudition:
391                 if (next) set_mouse_mode (MouseObject);
392                 else set_mouse_mode (MouseTimeFX);
393                 break;
394         
395         case MouseNote:
396                 if (next) set_mouse_mode (MouseObject);
397                 else set_mouse_mode (MouseAudition);
398                 break;
399         }
400 }
401
402 void
403 Editor::midi_edit_mode_toggled (MidiEditMode m)
404 {
405         if (ignore_midi_edit_mode_toggle) {
406                 return;
407         }
408
409         switch (m) {
410         case MidiEditPencil:
411                 if (midi_tool_pencil_button.get_active())
412                         set_midi_edit_mode (m);
413                 break;
414
415         case MidiEditSelect:
416                 if (midi_tool_select_button.get_active())
417                         set_midi_edit_mode (m);
418                 break;
419
420         case MidiEditResize:
421                 if (midi_tool_resize_button.get_active())
422                         set_midi_edit_mode (m);
423                 break;
424
425         case MidiEditErase:
426                 if (midi_tool_erase_button.get_active())
427                         set_midi_edit_mode (m);
428                 break;
429
430         default:
431                 break;
432         }
433
434         set_midi_edit_cursor(m);
435 }       
436
437
438 void
439 Editor::set_midi_edit_mode (MidiEditMode m, bool force)
440 {
441         if (drag_info.item) {
442                 return;
443         }
444
445         if (!force && m == midi_edit_mode) {
446                 return;
447         }
448         
449         midi_edit_mode = m;
450
451         instant_save ();
452         
453         ignore_midi_edit_mode_toggle = true;
454
455         switch (midi_edit_mode) {
456         case MidiEditPencil:
457                 midi_tool_pencil_button.set_active (true);
458                 break;
459
460         case MidiEditSelect:
461                 midi_tool_select_button.set_active (true);
462                 break;
463
464         case MidiEditResize:
465                 midi_tool_resize_button.set_active (true);
466                 break;
467
468         case MidiEditErase:
469                 midi_tool_erase_button.set_active (true);
470                 break;
471         }
472
473         ignore_midi_edit_mode_toggle = false;
474
475         set_midi_edit_cursor (current_midi_edit_mode());
476
477         if (is_drawable()) {
478                 track_canvas->get_window()->set_cursor(*current_canvas_cursor);
479         }
480 }
481
482 void
483 Editor::set_midi_edit_cursor (MidiEditMode m)
484 {
485         switch (midi_edit_mode) {
486         case MidiEditPencil:
487                 current_canvas_cursor = midi_pencil_cursor;
488                 break;
489
490         case MidiEditSelect:
491                 current_canvas_cursor = midi_select_cursor;
492                 break;
493
494         case MidiEditResize:
495                 current_canvas_cursor = midi_resize_cursor;
496                 break;
497
498         case MidiEditErase:
499                 current_canvas_cursor = midi_erase_cursor;
500                 break;
501         }
502 }
503
504 void
505 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
506 {
507         /* in object/audition/timefx mode, any button press sets
508            the selection if the object can be selected. this is a
509            bit of hack, because we want to avoid this if the
510            mouse operation is a region alignment.
511
512            note: not dbl-click or triple-click
513         */
514
515         if (((mouse_mode != MouseObject) &&
516              (mouse_mode != MouseAudition || item_type != RegionItem) &&
517              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
518              (mouse_mode != MouseRange)) ||
519
520             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
521                 
522                 return;
523         }
524
525         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
526
527                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
528                         
529                         /* almost no selection action on modified button-2 or button-3 events */
530                 
531                         if (item_type != RegionItem && event->button.button != 2) {
532                                 return;
533                         }
534                 }
535         }
536             
537         Selection::Operation op = Keyboard::selection_type (event->button.state);
538         bool press = (event->type == GDK_BUTTON_PRESS);
539
540         // begin_reversible_command (_("select on click"));
541         
542         switch (item_type) {
543         case RegionItem:
544                 if (mouse_mode != MouseRange) {
545                         set_selected_regionview_from_click (press, op, true);
546                 } else if (event->type == GDK_BUTTON_PRESS) {
547                         set_selected_track_as_side_effect ();
548                 }
549                 break;
550                 
551         case RegionViewNameHighlight:
552         case RegionViewName:
553                 if (mouse_mode != MouseRange) {
554                         set_selected_regionview_from_click (press, op, true);
555                 } else if (event->type == GDK_BUTTON_PRESS) {
556                         set_selected_track_as_side_effect ();
557                 }
558                 break;
559
560
561         case FadeInHandleItem:
562         case FadeInItem:
563         case FadeOutHandleItem:
564         case FadeOutItem:
565                 if (mouse_mode != MouseRange) {
566                         set_selected_regionview_from_click (press, op, true);
567                 } else if (event->type == GDK_BUTTON_PRESS) {
568                         set_selected_track_as_side_effect ();
569                 }
570                 break;
571
572         case ControlPointItem:
573                 set_selected_track_as_side_effect ();
574                 if (mouse_mode != MouseRange) {
575                         set_selected_control_point_from_click (op, false);
576                 }
577                 break;
578                 
579         case StreamItem:
580                 /* for context click or range selection, select track */
581                 if (event->button.button == 3) {
582                         set_selected_track_as_side_effect ();
583                 } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
584                         set_selected_track_as_side_effect ();
585                 }
586                 break;
587                     
588         case AutomationTrackItem:
589                 set_selected_track_as_side_effect (true);
590                 break;
591                 
592         default:
593                 break;
594         }
595 }
596
597 bool
598 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
599 {
600         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
601         
602         if (canvas_window) {
603                 Glib::RefPtr<const Gdk::Window> pointer_window;
604                 int x, y;
605                 double wx, wy;
606                 Gdk::ModifierType mask;
607
608                 pointer_window = canvas_window->get_pointer (x, y, mask);
609                 
610                 if (pointer_window == track_canvas->get_bin_window()) {
611                         track_canvas->window_to_world (x, y, wx, wy);
612                         allow_vertical_scroll = true;
613                 } else {
614                         allow_vertical_scroll = false;
615                 }
616         }
617
618         track_canvas->grab_focus();
619
620         if (session && session->actively_recording()) {
621                 return true;
622         }
623
624         button_selection (item, event, item_type);
625         
626         if (drag_info.item == 0 &&
627             (Keyboard::is_delete_event (&event->button) ||
628              Keyboard::is_context_menu_event (&event->button) ||
629              Keyboard::is_edit_event (&event->button))) {
630                 
631                 /* handled by button release */
632                 return true;
633         }
634
635         switch (event->button.button) {
636         case 1:
637
638                 if (event->type == GDK_BUTTON_PRESS) {
639
640                         if (drag_info.item) {
641                                 drag_info.item->ungrab (event->button.time);
642                         }
643
644                         /* single mouse clicks on any of these item types operate
645                            independent of mouse mode, mostly because they are
646                            not on the main track canvas or because we want
647                            them to be modeless.
648                         */
649                         
650                         switch (item_type) {
651                         case PlayheadCursorItem:
652                                 start_cursor_grab (item, event);
653                                 return true;
654
655                         case MarkerItem:
656                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
657                                         hide_marker (item, event);
658                                 } else {
659                                         start_marker_grab (item, event);
660                                 }
661                                 return true;
662
663                         case TempoMarkerItem:
664                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
665                                         start_tempo_marker_copy_grab (item, event);
666                                 } else {
667                                         start_tempo_marker_grab (item, event);
668                                 }
669                                 return true;
670
671                         case MeterMarkerItem:
672                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
673                                         start_meter_marker_copy_grab (item, event);
674                                 } else {
675                                         start_meter_marker_grab (item, event);
676                                 }
677                                 return true;
678
679                         case TempoBarItem:
680                                 return true;
681
682                         case MeterBarItem:
683                                 return true;
684                                 
685                         case RangeMarkerBarItem:
686                                 start_range_markerbar_op (item, event, CreateRangeMarker); 
687                                 return true;
688                                 break;
689
690                         case CdMarkerBarItem:
691                                 start_range_markerbar_op (item, event, CreateCDMarker); 
692                                 return true;
693                                 break;
694
695                         case TransportMarkerBarItem:
696                                 start_range_markerbar_op (item, event, CreateTransportMarker); 
697                                 return true;
698                                 break;
699
700                         default:
701                                 break;
702                         }
703                 }
704
705                 switch (mouse_mode) {
706                 case MouseRange:
707                         switch (item_type) {
708                         case StartSelectionTrimItem:
709                                 start_selection_op (item, event, SelectionStartTrim);
710                                 break;
711                                 
712                         case EndSelectionTrimItem:
713                                 start_selection_op (item, event, SelectionEndTrim);
714                                 break;
715
716                         case SelectionItem:
717                                 if (Keyboard::modifier_state_contains 
718                                     (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
719                                         // contains and not equals because I can't use alt as a modifier alone.
720                                         start_selection_grab (item, event);
721                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
722                                         /* grab selection for moving */
723                                         start_selection_op (item, event, SelectionMove);
724                                 } else {
725                                         /* this was debated, but decided the more common action was to
726                                            make a new selection */
727                                         start_selection_op (item, event, CreateSelection);
728                                 }
729                                 break;
730
731                         default:
732                                 start_selection_op (item, event, CreateSelection);
733                         }
734                         return true;
735                         break;
736                         
737                 case MouseObject:
738                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
739                             event->type == GDK_BUTTON_PRESS) {
740                                 
741                                 start_rubberband_select (item, event);
742
743                         } else if (event->type == GDK_BUTTON_PRESS) {
744
745                                 switch (item_type) {
746                                 case FadeInHandleItem:
747                                         start_fade_in_grab (item, event);
748                                         return true;
749                                         
750                                 case FadeOutHandleItem:
751                                         start_fade_out_grab (item, event);
752                                         return true;
753
754                                 case RegionItem:
755                                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
756                                                 start_region_copy_grab (item, event);
757                                         } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
758                                                 start_region_brush_grab (item, event);
759                                         } else {
760                                                 start_region_grab (item, event);
761                                         }
762                                         break;
763                                         
764                                 case RegionViewNameHighlight:
765                                         start_trim (item, event);
766                                         return true;
767                                         break;
768                                         
769                                 case RegionViewName:
770                                         /* rename happens on edit clicks */
771                                         start_trim (clicked_regionview->get_name_highlight(), event);
772                                         return true;
773                                         break;
774
775                                 case ControlPointItem:
776                                         start_control_point_grab (item, event);
777                                         return true;
778                                         break;
779                                         
780                                 case AutomationLineItem:
781                                         start_line_grab_from_line (item, event);
782                                         return true;
783                                         break;
784
785                                 case StreamItem:
786                                 case AutomationTrackItem:
787                                         start_rubberband_select (item, event);
788                                         break;
789                                         
790 #ifdef WITH_CMT
791                                 case ImageFrameHandleStartItem:
792                                         imageframe_start_handle_op(item, event) ;
793                                         return(true) ;
794                                         break ;
795                                 case ImageFrameHandleEndItem:
796                                         imageframe_end_handle_op(item, event) ;
797                                         return(true) ;
798                                         break ;
799                                 case MarkerViewHandleStartItem:
800                                         markerview_item_start_handle_op(item, event) ;
801                                         return(true) ;
802                                         break ;
803                                 case MarkerViewHandleEndItem:
804                                         markerview_item_end_handle_op(item, event) ;
805                                         return(true) ;
806                                         break ;
807                                 case MarkerViewItem:
808                                         start_markerview_grab(item, event) ;
809                                         break ;
810                                 case ImageFrameItem:
811                                         start_imageframe_grab(item, event) ;
812                                         break ;
813 #endif
814
815                                 case MarkerBarItem:
816                                         
817                                         break;
818
819                                 default:
820                                         break;
821                                 }
822                         }
823                         return true;
824                         break;
825                         
826                 case MouseGain:
827                         switch (item_type) {
828                         case RegionItem:
829                                 // start_line_grab_from_regionview (item, event);
830                                 break;
831
832                         case GainLineItem:
833                                 start_line_grab_from_line (item, event);
834                                 return true;
835
836                         case ControlPointItem:
837                                 start_control_point_grab (item, event);
838                                 return true;
839                                 break;
840
841                         default:
842                                 break;
843                         }
844                         return true;
845                         break;
846
847                         switch (item_type) {
848                         case ControlPointItem:
849                                 start_control_point_grab (item, event);
850                                 break;
851
852                         case AutomationLineItem:
853                                 start_line_grab_from_line (item, event);
854                                 break;
855
856                         case RegionItem:
857                                 // XXX need automation mode to identify which
858                                 // line to use
859                                 // start_line_grab_from_regionview (item, event);
860                                 break;
861
862                         default:
863                                 break;
864                         }
865                         return true;
866                         break;
867
868                 case MouseZoom:
869                         if (event->type == GDK_BUTTON_PRESS) {
870                                 start_mouse_zoom (item, event);
871                         }
872
873                         return true;
874                         break;
875
876                 case MouseTimeFX:
877                         if (item_type == RegionItem) {
878                                 start_time_fx (item, event);
879                         }
880                         break;
881
882                 case MouseAudition:
883                         _scrubbing = true;
884                         scrub_reversals = 0;
885                         scrub_reverse_distance = 0;
886                         last_scrub_x = event->button.x;
887                         scrubbing_direction = 0;
888                         track_canvas->get_window()->set_cursor (*transparent_cursor);
889                         /* rest handled in motion & release */
890                         break;
891
892                 case MouseNote:
893                         start_create_region_grab (item, event);
894                         break;
895                 
896                 default:
897                         break;
898                 }
899                 break;
900
901         case 2:
902                 switch (mouse_mode) {
903                 case MouseObject:
904                         if (event->type == GDK_BUTTON_PRESS) {
905                                 switch (item_type) {
906                                 case RegionItem:
907                                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
908                                                 start_region_copy_grab (item, event);
909                                         } else {
910                                                 start_region_grab (item, event);
911                                         }
912                                         return true;
913                                         break;
914                                 case ControlPointItem:
915                                         start_control_point_grab (item, event);
916                                         return true;
917                                         break;
918                                         
919                                 default:
920                                         break;
921                                 }
922                         }
923                         
924                         
925                         switch (item_type) {
926                         case RegionViewNameHighlight:
927                                 start_trim (item, event);
928                                 return true;
929                                 break;
930                                 
931                         case RegionViewName:
932                                 start_trim (clicked_regionview->get_name_highlight(), event);
933                                 return true;
934                                 break;
935                                 
936                         default:
937                                 break;
938                         }
939                         
940                         break;
941
942                 case MouseRange:
943                         if (event->type == GDK_BUTTON_PRESS) {
944                                 /* relax till release */
945                         }
946                         return true;
947                         break;
948                                         
949                                 
950                 case MouseZoom:
951                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
952                                 temporal_zoom_session();
953                         } else {
954                                 temporal_zoom_to_frame (true, event_frame(event));
955                         }
956                         return true;
957                         break;
958
959                 default:
960                         break;
961                 }
962
963                 break;
964
965         case 3:
966                 break;
967
968         default:
969                 break;
970
971         }
972
973         return false;
974 }
975
976 bool
977 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
978 {
979         nframes64_t where = event_frame (event, 0, 0);
980         AutomationTimeAxisView* atv = 0;
981
982         /* no action if we're recording */
983                                                 
984         if (session && session->actively_recording()) {
985                 return true;
986         }
987
988         /* first, see if we're finishing a drag ... */
989
990         if (drag_info.item) {
991                 if (end_grab (item, event)) {
992                         /* grab dragged, so do nothing else */
993                         return true;
994                 }
995         }
996         
997         button_selection (item, event, item_type);
998
999         /* edit events get handled here */
1000         
1001         if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
1002                 switch (item_type) {
1003                 case RegionItem:
1004                         edit_region ();
1005                         break;
1006
1007                 case TempoMarkerItem:
1008                         edit_tempo_marker (item);
1009                         break;
1010                         
1011                 case MeterMarkerItem:
1012                         edit_meter_marker (item);
1013                         break;
1014                         
1015                 case RegionViewName:
1016                         if (clicked_regionview->name_active()) {
1017                                 return mouse_rename_region (item, event);
1018                         }
1019                         break;
1020
1021                 default:
1022                         break;
1023                 }
1024                 return true;
1025         }
1026
1027         /* context menu events get handled here */
1028
1029         if (Keyboard::is_context_menu_event (&event->button)) {
1030
1031                 if (drag_info.item == 0) {
1032
1033                         /* no matter which button pops up the context menu, tell the menu
1034                            widget to use button 1 to drive menu selection.
1035                         */
1036
1037                         switch (item_type) {
1038                         case FadeInItem:
1039                         case FadeInHandleItem:
1040                         case FadeOutItem:
1041                         case FadeOutHandleItem:
1042                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1043                                 break;
1044                         
1045                         case StreamItem:
1046                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1047                                 break;
1048                                 
1049                         case RegionItem:
1050                         case RegionViewNameHighlight:
1051                         case RegionViewName:
1052                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1053                                 break;
1054                                 
1055                         case SelectionItem:
1056                                 popup_track_context_menu (1, event->button.time, item_type, true, where);
1057                                 break;
1058
1059                         case AutomationTrackItem:
1060                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1061                                 break;
1062
1063                         case MarkerBarItem: 
1064                         case RangeMarkerBarItem: 
1065                         case TransportMarkerBarItem:
1066                         case CdMarkerBarItem:
1067                         case TempoBarItem:
1068                         case MeterBarItem:
1069                                 popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
1070                                 break;
1071
1072                         case MarkerItem:
1073                                 marker_context_menu (&event->button, item);
1074                                 break;
1075
1076                         case TempoMarkerItem:
1077                                 tm_marker_context_menu (&event->button, item);
1078                                 break;
1079                                 
1080                         case MeterMarkerItem:
1081                                 tm_marker_context_menu (&event->button, item);
1082                                 break;
1083                         
1084                         case CrossfadeViewItem:
1085                                 popup_track_context_menu (1, event->button.time, item_type, false, where);
1086                                 break;
1087
1088 #ifdef WITH_CMT
1089                         case ImageFrameItem:
1090                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1091                                 break ;
1092                         case ImageFrameTimeAxisItem:
1093                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1094                                 break ;
1095                         case MarkerViewItem:
1096                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1097                                 break ;
1098                         case MarkerTimeAxisItem:
1099                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1100                                 break ;
1101 #endif
1102                                 
1103                         default:
1104                                 break;
1105                         }
1106
1107                         return true;
1108                 }
1109         }
1110
1111         /* delete events get handled here */
1112
1113         if (drag_info.item == 0 && Keyboard::is_delete_event (&event->button)) {
1114
1115                 switch (item_type) {
1116                 case TempoMarkerItem:
1117                         remove_tempo_marker (item);
1118                         break;
1119                         
1120                 case MeterMarkerItem:
1121                         remove_meter_marker (item);
1122                         break;
1123
1124                 case MarkerItem:
1125                         remove_marker (*item, event);
1126                         break;
1127
1128                 case RegionItem:
1129                         if (mouse_mode == MouseObject) {
1130                                 remove_clicked_region ();
1131                         }
1132                         break;
1133                         
1134                 case ControlPointItem:
1135                         if (mouse_mode == MouseGain) {
1136                                 remove_gain_control_point (item, event);
1137                         } else {
1138                                 remove_control_point (item, event);
1139                         }
1140                         break;
1141
1142                 default:
1143                         break;
1144                 }
1145                 return true;
1146         }
1147
1148         switch (event->button.button) {
1149         case 1:
1150
1151                 switch (item_type) {
1152                 /* see comments in button_press_handler */
1153                 case PlayheadCursorItem:
1154                 case MarkerItem:
1155                 case GainLineItem:
1156                 case AutomationLineItem:
1157                 case StartSelectionTrimItem:
1158                 case EndSelectionTrimItem:
1159                         return true;
1160
1161                 case MarkerBarItem:
1162                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1163                                 snap_to (where, 0, true);
1164                         }
1165                         mouse_add_new_marker (where);
1166                         return true;
1167
1168                 case CdMarkerBarItem:
1169                         // if we get here then a dragged range wasn't done
1170                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1171                                 snap_to (where, 0, true);
1172                         }
1173                         mouse_add_new_marker (where, true);
1174                         return true;
1175
1176                 case TempoBarItem:
1177                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1178                                 snap_to (where);
1179                         }
1180                         mouse_add_new_tempo_event (where);
1181                         return true;
1182                         
1183                 case MeterBarItem:
1184                         mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1185                         return true;
1186                         break;
1187
1188                 default:
1189                         break;
1190                 }
1191
1192                 switch (mouse_mode) {
1193                 case MouseObject:
1194                         switch (item_type) {
1195                         case AutomationTrackItem:
1196                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
1197                                 if (atv) {
1198                                         atv->add_automation_event (item, event, where, event->button.y);
1199                                 }
1200                                 return true;
1201                                 
1202                                 break;
1203                                 
1204                         default:
1205                                 break;
1206                         }
1207                         break;
1208
1209                 case MouseGain:
1210                         // Gain only makes sense for audio regions
1211
1212                         if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1213                                 break;
1214                         }
1215
1216                         switch (item_type) {
1217                         case RegionItem:
1218                                 dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1219                                 return true;
1220                                 break;
1221                                 
1222                         case AutomationTrackItem:
1223                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1224                                         add_automation_event (item, event, where, event->button.y);
1225                                 return true;
1226                                 break;
1227                         default:
1228                                 break;
1229                         }
1230                         break;
1231                         
1232                 case MouseAudition:
1233                         _scrubbing = false;
1234                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1235                         if (scrubbing_direction == 0) {
1236                                 /* no drag, just a click */
1237                                 switch (item_type) {
1238                                 case RegionItem:
1239                                         play_selected_region ();
1240                                         break;
1241                                 default:
1242                                         break;
1243                                 }
1244                         } else {
1245                                 /* make sure we stop */
1246                                 session->request_transport_speed (0.0);
1247                         }
1248                         break;
1249                         
1250                 default:
1251                         break;
1252
1253                 }
1254
1255                 return true;
1256                 break;
1257
1258
1259         case 2:
1260                 switch (mouse_mode) {
1261                         
1262                 case MouseObject:
1263                         switch (item_type) {
1264                         case RegionItem:
1265                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1266                                         raise_region ();
1267                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1268                                         lower_region ();
1269                                 } else {
1270                                         // Button2 click is unused
1271                                 }
1272                                 return true;
1273                                 
1274                                 break;
1275                                 
1276                         default:
1277                                 break;
1278                         }
1279                         break;
1280                         
1281                 case MouseRange:
1282                         
1283                         // x_style_paste (where, 1.0);
1284                         return true;
1285                         break;
1286                         
1287                 default:
1288                         break;
1289                 }
1290
1291                 break;
1292         
1293         case 3:
1294                 break;
1295                 
1296         default:
1297                 break;
1298         }
1299         return false;
1300 }
1301
1302 bool
1303 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1304 {
1305         ControlPoint* cp;
1306         Marker * marker;
1307         double fraction;
1308         
1309         if (last_item_entered != item) {
1310                 last_item_entered = item;
1311                 last_item_entered_n = 0;
1312         }
1313
1314         switch (item_type) {
1315         case ControlPointItem:
1316                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1317                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1318                         cp->set_visible (true);
1319
1320                         double at_x, at_y;
1321                         at_x = cp->get_x();
1322                         at_y = cp->get_y ();
1323                         cp->item()->i2w (at_x, at_y);
1324                         at_x += 10.0;
1325                         at_y += 10.0;
1326
1327                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1328
1329                         if (is_drawable() && !_scrubbing) {
1330                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1331                         }
1332
1333                         last_item_entered_n++;
1334                         set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1335                         if (last_item_entered_n < 10) {
1336                                 show_verbose_canvas_cursor ();
1337                         }
1338                 }
1339                 break;
1340
1341         case GainLineItem:
1342                 if (mouse_mode == MouseGain) {
1343                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1344                         if (line)
1345                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1346                         if (is_drawable()) {
1347                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1348                         }
1349                 }
1350                 break;
1351                         
1352         case AutomationLineItem:
1353                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1354                         {
1355                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1356                                 if (line)
1357                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1358                         }
1359                         if (is_drawable()) {
1360                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1361                         }
1362                 }
1363                 break;
1364                 
1365         case RegionViewNameHighlight:
1366                 if (is_drawable() && mouse_mode == MouseObject) {
1367                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1368                 }
1369                 break;
1370
1371         case StartSelectionTrimItem:
1372         case EndSelectionTrimItem:
1373
1374 #ifdef WITH_CMT
1375         case ImageFrameHandleStartItem:
1376         case ImageFrameHandleEndItem:
1377         case MarkerViewHandleStartItem:
1378         case MarkerViewHandleEndItem:
1379 #endif
1380
1381                 if (is_drawable()) {
1382                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1383                 }
1384                 break;
1385
1386         case PlayheadCursorItem:
1387                 if (is_drawable()) {
1388                         switch (_edit_point) {
1389                         case EditAtMouse:
1390                                 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1391                                 break;
1392                         default:
1393                                 track_canvas->get_window()->set_cursor (*grabber_cursor);
1394                                 break;
1395                         }
1396                 }
1397                 break;
1398
1399         case RegionViewName:
1400                 
1401                 /* when the name is not an active item, the entire name highlight is for trimming */
1402
1403                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1404                         if (mouse_mode == MouseObject && is_drawable()) {
1405                                 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1406                         }
1407                 } 
1408                 break;
1409
1410
1411         case AutomationTrackItem:
1412                 if (is_drawable()) {
1413                         Gdk::Cursor *cursor;
1414                         switch (mouse_mode) {
1415                         case MouseRange:
1416                                 cursor = selector_cursor;
1417                                 break;
1418                         case MouseZoom:
1419                                 cursor = zoom_cursor;
1420                                 break;
1421                         default:
1422                                 cursor = cross_hair_cursor;
1423                                 break;
1424                         }
1425
1426                         track_canvas->get_window()->set_cursor (*cursor);
1427
1428                         AutomationTimeAxisView* atv;
1429                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1430                                 clear_entered_track = false;
1431                                 set_entered_track (atv);
1432                         }
1433                 }
1434                 break;
1435
1436         case MarkerBarItem:
1437         case RangeMarkerBarItem:
1438         case TransportMarkerBarItem:
1439         case CdMarkerBarItem:
1440         case MeterBarItem:
1441         case TempoBarItem:
1442                 if (is_drawable()) {
1443                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1444                 }
1445                 break;
1446
1447         case MarkerItem:
1448                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1449                         break;
1450                 }
1451                 entered_marker = marker;
1452                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1453                 // fall through
1454         case MeterMarkerItem:
1455         case TempoMarkerItem:
1456                 if (is_drawable()) {
1457                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1458                 }
1459                 break;
1460         case FadeInHandleItem:
1461         case FadeOutHandleItem:
1462                 if (mouse_mode == MouseObject) {
1463                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1464                         if (rect) {
1465                                 rect->property_fill_color_rgba() = 0;
1466                                 rect->property_outline_pixels() = 1;
1467                         }
1468                 }
1469                 break;
1470
1471         default:
1472                 break;
1473         }
1474
1475         /* second pass to handle entered track status in a comprehensible way.
1476          */
1477
1478         switch (item_type) {
1479         case GainLineItem:
1480         case AutomationLineItem:
1481         case ControlPointItem:
1482                 /* these do not affect the current entered track state */
1483                 clear_entered_track = false;
1484                 break;
1485
1486         case AutomationTrackItem:
1487                 /* handled above already */
1488                 break;
1489
1490         default:
1491                 set_entered_track (0);
1492                 break;
1493         }
1494
1495         return false;
1496 }
1497
1498 bool
1499 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1500 {
1501         AutomationLine* al;
1502         ControlPoint* cp;
1503         Marker *marker;
1504         Location *loc;
1505         RegionView* rv;
1506         bool is_start;
1507
1508         switch (item_type) {
1509         case ControlPointItem:
1510                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1511                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1512                         if (cp->line().npoints() > 1 && !cp->selected()) {
1513                                 cp->set_visible (false);
1514                         }
1515                 }
1516                 
1517                 if (is_drawable()) {
1518                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1519                 }
1520
1521                 hide_verbose_canvas_cursor ();
1522                 break;
1523                 
1524         case RegionViewNameHighlight:
1525         case StartSelectionTrimItem:
1526         case EndSelectionTrimItem:
1527         case PlayheadCursorItem:
1528
1529 #ifdef WITH_CMT
1530         case ImageFrameHandleStartItem:
1531         case ImageFrameHandleEndItem:
1532         case MarkerViewHandleStartItem:
1533         case MarkerViewHandleEndItem:
1534 #endif
1535
1536                 if (is_drawable()) {
1537                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1538                 }
1539                 break;
1540
1541         case GainLineItem:
1542         case AutomationLineItem:
1543                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1544                 {
1545                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1546                         if (line)
1547                                 line->property_fill_color_rgba() = al->get_line_color();
1548                 }
1549                 if (is_drawable()) {
1550                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1551                 }
1552                 break;
1553
1554         case RegionViewName:
1555                 /* see enter_handler() for notes */
1556                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1557                         if (is_drawable() && mouse_mode == MouseObject) {
1558                                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1559                         }
1560                 }
1561                 break;
1562
1563         case RangeMarkerBarItem:
1564         case TransportMarkerBarItem:
1565         case CdMarkerBarItem:
1566         case MeterBarItem:
1567         case TempoBarItem:
1568         case MarkerBarItem:
1569                 if (is_drawable()) {
1570                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1571                 }
1572                 break;
1573                 
1574         case MarkerItem:
1575                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1576                         break;
1577                 }
1578                 entered_marker = 0;
1579                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1580                         location_flags_changed (loc, this);
1581                 }
1582                 // fall through
1583         case MeterMarkerItem:
1584         case TempoMarkerItem:
1585                 
1586                 if (is_drawable()) {
1587                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1588                 }
1589
1590                 break;
1591
1592         case FadeInHandleItem:
1593         case FadeOutHandleItem:
1594                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1595                 {
1596                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1597                         if (rect) {
1598                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1599                                 rect->property_outline_pixels() = 0;
1600                         }
1601                 }
1602                 break;
1603
1604         case AutomationTrackItem:
1605                 if (is_drawable()) {
1606                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1607                         clear_entered_track = true;
1608                         Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1609                 }
1610                 break;
1611                 
1612         default:
1613                 break;
1614         }
1615
1616         return false;
1617 }
1618
1619 gint
1620 Editor::left_automation_track ()
1621 {
1622         if (clear_entered_track) {
1623                 set_entered_track (0);
1624                 clear_entered_track = false;
1625         }
1626         return false;
1627 }
1628
1629 void
1630 Editor::scrub ()
1631 {
1632         double delta;
1633         
1634         if (scrubbing_direction == 0) {
1635                 /* first move */
1636                 session->request_locate (drag_info.current_pointer_frame, false);
1637                 session->request_transport_speed (0.1);
1638                 scrubbing_direction = 1;
1639                 
1640         } else {
1641                 
1642                 if (last_scrub_x > drag_info.current_pointer_x) {
1643                         
1644                         /* pointer moved to the left */
1645                         
1646                         if (scrubbing_direction > 0) {
1647                                 
1648                                 /* we reversed direction to go backwards */
1649                                 
1650                                 scrub_reversals++;
1651                                 scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
1652                                 
1653                         } else {
1654                                 
1655                                 /* still moving to the left (backwards) */
1656                                 
1657                                 scrub_reversals = 0;
1658                                 scrub_reverse_distance = 0;
1659                                 
1660                                 delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
1661                                 session->request_transport_speed (session->transport_speed() - delta);
1662                         }
1663                         
1664                 } else {
1665                         /* pointer moved to the right */
1666                         
1667                         if (scrubbing_direction < 0) {
1668                                 /* we reversed direction to go forward */
1669                                 
1670                                 scrub_reversals++;
1671                                 scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
1672                                 
1673                         } else {
1674                                 /* still moving to the right */
1675                                 
1676                                 scrub_reversals = 0;
1677                                 scrub_reverse_distance = 0;
1678                                 
1679                                 delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
1680                                 session->request_transport_speed (session->transport_speed() + delta);
1681                         }
1682                 }
1683                 
1684                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1685                    back more than 10 pixels, reverse direction
1686                 */
1687                 
1688                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1689                         
1690                         if (scrubbing_direction > 0) {
1691                                 /* was forwards, go backwards */
1692                                 session->request_transport_speed (-0.1);
1693                                 scrubbing_direction = -1;
1694                         } else {
1695                                 /* was backwards, go forwards */
1696                                 session->request_transport_speed (0.1);
1697                                 scrubbing_direction = 1;
1698                         }
1699                         
1700                         scrub_reverse_distance = 0;
1701                         scrub_reversals = 0;
1702                 }
1703         }
1704         
1705         last_scrub_x = drag_info.current_pointer_x;
1706 }
1707
1708 bool
1709 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
1710 {
1711         if (event->motion.is_hint) {
1712                 gint x, y;
1713                 
1714                 /* We call this so that MOTION_NOTIFY events continue to be
1715                    delivered to the canvas. We need to do this because we set
1716                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1717                    the density of the events, at the expense of a round-trip
1718                    to the server. Given that this will mostly occur on cases
1719                    where DISPLAY = :0.0, and given the cost of what the motion
1720                    event might do, its a good tradeoff.  
1721                 */
1722
1723                 track_canvas->get_pointer (x, y);
1724         } 
1725
1726         if (current_stepping_trackview) {
1727                 /* don't keep the persistent stepped trackview if the mouse moves */
1728                 current_stepping_trackview = 0;
1729                 step_timeout.disconnect ();
1730         }
1731
1732         if (session && session->actively_recording()) {
1733                 /* Sorry. no dragging stuff around while we record */
1734                 return true;
1735         }
1736
1737         drag_info.item_type = item_type;
1738         drag_info.last_pointer_x = drag_info.current_pointer_x;
1739         drag_info.last_pointer_y = drag_info.current_pointer_y;
1740         drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
1741                                                        &drag_info.current_pointer_y);
1742
1743         
1744         switch (mouse_mode) {
1745         case MouseAudition:
1746                 if (_scrubbing) {
1747                         scrub ();
1748                 }
1749                 break;
1750
1751         default:
1752                 break;
1753         }
1754
1755
1756         if (!from_autoscroll && drag_info.item) {
1757                 /* item != 0 is the best test i can think of for dragging.
1758                 */
1759                 if (!drag_info.move_threshold_passed) {
1760
1761                         bool x_threshold_passed =  (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1762                         bool y_threshold_passed =  (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
1763                         
1764                         drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
1765                         
1766                         // and change the initial grab loc/frame if this drag info wants us to
1767
1768                         if (drag_info.want_move_threshold && drag_info.move_threshold_passed) {
1769                                 drag_info.grab_frame = drag_info.current_pointer_frame;
1770                                 drag_info.grab_x = drag_info.current_pointer_x;
1771                                 drag_info.grab_y = drag_info.current_pointer_y;
1772                                 drag_info.last_pointer_frame = drag_info.grab_frame;
1773                                 drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
1774                         }
1775                 }
1776         }
1777
1778         switch (item_type) {
1779         case PlayheadCursorItem:
1780         case MarkerItem:
1781         case ControlPointItem:
1782         case RangeMarkerBarItem:
1783         case TransportMarkerBarItem:
1784         case CdMarkerBarItem:
1785         case TempoMarkerItem:
1786         case MeterMarkerItem:
1787         case RegionViewNameHighlight:
1788         case StartSelectionTrimItem:
1789         case EndSelectionTrimItem:
1790         case SelectionItem:
1791         case GainLineItem:
1792         case AutomationLineItem:
1793         case FadeInHandleItem:
1794         case FadeOutHandleItem:
1795
1796 #ifdef WITH_CMT
1797         case ImageFrameHandleStartItem:
1798         case ImageFrameHandleEndItem:
1799         case MarkerViewHandleStartItem:
1800         case MarkerViewHandleEndItem:
1801 #endif
1802
1803           if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
1804                                  (event->motion.state & Gdk::BUTTON2_MASK))) {
1805                   if (!from_autoscroll) {
1806                           maybe_autoscroll_horizontally (&event->motion);
1807                   }
1808                   (this->*(drag_info.motion_callback)) (item, event);
1809                   goto handled;
1810           }
1811           goto not_handled;
1812           break;
1813         default:
1814                 break;
1815         }
1816
1817         switch (mouse_mode) {
1818         case MouseObject:
1819         case MouseRange:
1820         case MouseZoom:
1821         case MouseTimeFX:
1822         case MouseNote:
1823                 if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
1824                                        (event->motion.state & GDK_BUTTON2_MASK))) {
1825                         if (!from_autoscroll) {
1826                                 maybe_autoscroll (&event->motion);
1827                         }
1828                         (this->*(drag_info.motion_callback)) (item, event);
1829                         goto handled;
1830                 }
1831                 goto not_handled;
1832                 break;
1833
1834         default:
1835                 break;
1836         }
1837
1838   handled:
1839         track_canvas_motion (event);
1840         // drag_info.last_pointer_frame = drag_info.current_pointer_frame;
1841         return true;
1842         
1843   not_handled:
1844         return false;
1845 }
1846
1847 void
1848 Editor::break_drag ()
1849 {
1850         stop_canvas_autoscroll ();
1851         hide_verbose_canvas_cursor ();
1852
1853         if (drag_info.item) {
1854                 drag_info.item->ungrab (0);
1855
1856                 /* put it back where it came from */
1857
1858                 double cxw, cyw;
1859                 cxw = 0;
1860                 cyw = 0;
1861                 drag_info.item->i2w (cxw, cyw);
1862                 drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
1863         }
1864
1865         finalize_drag ();
1866 }
1867
1868 void
1869 Editor::finalize_drag ()
1870 {
1871         drag_info.item = 0;
1872         drag_info.copy = false;
1873         drag_info.motion_callback = 0;
1874         drag_info.finished_callback = 0;
1875         drag_info.dest_trackview = 0;
1876         drag_info.source_trackview = 0;
1877         drag_info.last_frame_position = 0;
1878         drag_info.grab_frame = 0;
1879         drag_info.last_pointer_frame = 0;
1880         drag_info.current_pointer_frame = 0;
1881         drag_info.brushing = false;
1882         range_marker_drag_rect->hide();
1883         drag_info.clear_copied_locations ();
1884 }
1885
1886 void
1887 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
1888 {
1889         if (drag_info.item == 0) {
1890                 fatal << _("programming error: start_grab called without drag item") << endmsg;
1891                 /*NOTREACHED*/
1892                 return;
1893         }
1894
1895         if (cursor == 0) {
1896                 cursor = which_grabber_cursor ();
1897         }
1898
1899         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
1900
1901         if (event->button.button == 2) {
1902                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
1903                         drag_info.y_constrained = true;
1904                         drag_info.x_constrained = false;
1905                 } else {
1906                         drag_info.y_constrained = false;
1907                         drag_info.x_constrained = true;
1908                 }
1909         } else {
1910                 drag_info.x_constrained = false;
1911                 drag_info.y_constrained = false;
1912         }
1913
1914         drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
1915         drag_info.last_pointer_frame = drag_info.grab_frame;
1916         drag_info.current_pointer_frame = drag_info.grab_frame;
1917         drag_info.current_pointer_x = drag_info.grab_x;
1918         drag_info.current_pointer_y = drag_info.grab_y;
1919         drag_info.last_pointer_x = drag_info.current_pointer_x;
1920         drag_info.last_pointer_y = drag_info.current_pointer_y;
1921         drag_info.cumulative_x_drag = 0;
1922         drag_info.cumulative_y_drag = 0;
1923         drag_info.first_move = true;
1924         drag_info.move_threshold_passed = false;
1925         drag_info.want_move_threshold = false;
1926         drag_info.pointer_frame_offset = 0;
1927         drag_info.brushing = false;
1928         drag_info.clear_copied_locations ();
1929
1930         drag_info.original_x = 0;
1931         drag_info.original_y = 0;
1932         drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
1933
1934         drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
1935                               *cursor,
1936                               event->button.time);
1937
1938         if (session && session->transport_rolling()) {
1939                 drag_info.was_rolling = true;
1940         } else {
1941                 drag_info.was_rolling = false;
1942         }
1943
1944         switch (snap_type) {
1945         case SnapToRegionStart:
1946         case SnapToRegionEnd:
1947         case SnapToRegionSync:
1948         case SnapToRegionBoundary:
1949                 build_region_boundary_cache ();
1950                 break;
1951         default:
1952                 break;
1953         }
1954 }
1955
1956 void
1957 Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
1958 {
1959         drag_info.item->ungrab (0);
1960         drag_info.item = new_item;
1961
1962         if (cursor == 0) {
1963                 cursor = which_grabber_cursor ();
1964         }
1965
1966         drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
1967 }
1968
1969 bool
1970 Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
1971 {
1972         bool did_drag = false;
1973
1974         stop_canvas_autoscroll ();
1975
1976         if (drag_info.item == 0) {
1977                 return false;
1978         }
1979         
1980         drag_info.item->ungrab (event->button.time);
1981
1982         if (drag_info.finished_callback) {
1983                 drag_info.last_pointer_x = drag_info.current_pointer_x;
1984                 drag_info.last_pointer_y = drag_info.current_pointer_y;
1985                 (this->*(drag_info.finished_callback)) (item, event);
1986         }
1987
1988         did_drag = !drag_info.first_move;
1989
1990         hide_verbose_canvas_cursor();
1991
1992         finalize_drag ();
1993
1994         return did_drag;
1995 }
1996
1997 void
1998 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1999 {
2000         drag_info.item = item;
2001         drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
2002         drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
2003
2004         start_grab (event);
2005
2006         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2007                 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
2008                 /*NOTREACHED*/
2009         }
2010
2011         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2012
2013
2014         drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());       
2015 }
2016
2017 void
2018 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2019 {
2020         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2021         nframes64_t pos;
2022         nframes64_t fade_length;
2023
2024         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2025                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2026         }
2027         else {
2028                 pos = 0;
2029         }
2030
2031         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2032                 snap_to (pos);
2033         }
2034
2035         if (pos < (arv->region()->position() + 64)) {
2036                 fade_length = 64; // this should be a minimum defined somewhere
2037         } else if (pos > arv->region()->last_frame()) {
2038                 fade_length = arv->region()->length();
2039         } else {
2040                 fade_length = pos - arv->region()->position();
2041         }               
2042         /* mapover the region selection */
2043
2044         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2045
2046                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2047                 
2048                 if (!tmp) {
2049                         continue;
2050                 }
2051         
2052                 tmp->reset_fade_in_shape_width (fade_length);
2053         }
2054
2055         show_verbose_duration_cursor (arv->region()->position(),  arv->region()->position() + fade_length, 10);
2056
2057         drag_info.first_move = false;
2058 }
2059
2060 void
2061 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2062 {
2063         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2064         nframes64_t pos;
2065         nframes64_t fade_length;
2066
2067         if (drag_info.first_move) return;
2068
2069         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2070                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2071         } else {
2072                 pos = 0;
2073         }
2074
2075         if (pos < (arv->region()->position() + 64)) {
2076                 fade_length = 64; // this should be a minimum defined somewhere
2077         } else if (pos > arv->region()->last_frame()) {
2078                 fade_length = arv->region()->length();
2079         } else {
2080                 fade_length = pos - arv->region()->position();
2081         }
2082                 
2083         begin_reversible_command (_("change fade in length"));
2084
2085         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2086
2087                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2088                 
2089                 if (!tmp) {
2090                         continue;
2091                 }
2092         
2093                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2094                 XMLNode &before = alist->get_state();
2095
2096                 tmp->audio_region()->set_fade_in_length (fade_length);
2097                 tmp->audio_region()->set_fade_in_active (true);
2098                 
2099                 XMLNode &after = alist->get_state();
2100                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2101         }
2102
2103         commit_reversible_command ();
2104 }
2105
2106 void
2107 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
2108 {
2109         drag_info.item = item;
2110         drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
2111         drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
2112
2113         start_grab (event);
2114
2115         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
2116                 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
2117                 /*NOTREACHED*/
2118         }
2119
2120         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2121
2122         drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());    
2123 }
2124
2125 void
2126 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2127 {
2128         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2129         nframes64_t pos;
2130         nframes64_t fade_length;
2131
2132         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2133                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2134         } else {
2135                 pos = 0;
2136         }
2137
2138         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2139                 snap_to (pos);
2140         }
2141         
2142         if (pos > (arv->region()->last_frame() - 64)) {
2143                 fade_length = 64; // this should really be a minimum fade defined somewhere
2144         }
2145         else if (pos < arv->region()->position()) {
2146                 fade_length = arv->region()->length();
2147         }
2148         else {
2149                 fade_length = arv->region()->last_frame() - pos;
2150         }
2151                 
2152         /* mapover the region selection */
2153
2154         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2155
2156                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2157                 
2158                 if (!tmp) {
2159                         continue;
2160                 }
2161         
2162                 tmp->reset_fade_out_shape_width (fade_length);
2163         }
2164
2165         show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
2166
2167         drag_info.first_move = false;
2168 }
2169
2170 void
2171 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2172 {
2173         if (drag_info.first_move) return;
2174
2175         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
2176         nframes64_t pos;
2177         nframes64_t fade_length;
2178
2179         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2180                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2181         }
2182         else {
2183                 pos = 0;
2184         }
2185
2186         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2187                 snap_to (pos);
2188         }
2189
2190         if (pos > (arv->region()->last_frame() - 64)) {
2191                 fade_length = 64; // this should really be a minimum fade defined somewhere
2192         }
2193         else if (pos < arv->region()->position()) {
2194                 fade_length = arv->region()->length();
2195         }
2196         else {
2197                 fade_length = arv->region()->last_frame() - pos;
2198         }
2199
2200         begin_reversible_command (_("change fade out length"));
2201
2202         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2203
2204                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2205                 
2206                 if (!tmp) {
2207                         continue;
2208                 }
2209         
2210                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2211                 XMLNode &before = alist->get_state();
2212                 
2213                 tmp->audio_region()->set_fade_out_length (fade_length);
2214                 tmp->audio_region()->set_fade_out_active (true);
2215
2216                 XMLNode &after = alist->get_state();
2217                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2218         }
2219
2220         commit_reversible_command ();
2221 }
2222
2223 void
2224 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
2225 {
2226         drag_info.item = item;
2227         drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
2228         drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
2229
2230         start_grab (event);
2231
2232         if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
2233                 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
2234                 /*NOTREACHED*/
2235         }
2236
2237         Cursor* cursor = (Cursor *) drag_info.data;
2238
2239         if (cursor == playhead_cursor) {
2240                 _dragging_playhead = true;
2241                 
2242                 if (session && drag_info.was_rolling) {
2243                         session->request_stop ();
2244                 }
2245
2246                 if (session && session->is_auditioning()) {
2247                         session->cancel_audition ();
2248                 }
2249         }
2250
2251         drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;  
2252         
2253         show_verbose_time_cursor (cursor->current_frame, 10);
2254 }
2255
2256 void
2257 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2258 {
2259         Cursor* cursor = (Cursor *) drag_info.data;
2260         nframes64_t adjusted_frame;
2261         
2262         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2263                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2264         }
2265         else {
2266                 adjusted_frame = 0;
2267         }
2268         
2269         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2270                 if (cursor == playhead_cursor) {
2271                         snap_to (adjusted_frame);
2272                 }
2273         }
2274         
2275         if (adjusted_frame == drag_info.last_pointer_frame) return;
2276
2277         cursor->set_position (adjusted_frame);
2278
2279         show_verbose_time_cursor (cursor->current_frame, 10);
2280
2281 #ifdef GTKOSX
2282         track_canvas->update_now ();
2283 #endif
2284         UpdateAllTransportClocks (cursor->current_frame);
2285
2286         drag_info.last_pointer_frame = adjusted_frame;
2287         drag_info.first_move = false;
2288 }
2289
2290 void
2291 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2292 {
2293         if (drag_info.first_move) return;
2294         
2295         cursor_drag_motion_callback (item, event);
2296
2297         _dragging_playhead = false;
2298         
2299         if (item == &playhead_cursor->canvas_item) {
2300                 if (session) {
2301                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2302                 }
2303         } 
2304 }
2305
2306 void
2307 Editor::update_marker_drag_item (Location *location)
2308 {
2309         double x1 = frame_to_pixel (location->start());
2310         double x2 = frame_to_pixel (location->end());
2311
2312         if (location->is_mark()) {
2313                 marker_drag_line_points.front().set_x(x1);
2314                 marker_drag_line_points.back().set_x(x1);
2315                 marker_drag_line->property_points() = marker_drag_line_points;
2316         } else {
2317                 range_marker_drag_rect->property_x1() = x1;
2318                 range_marker_drag_rect->property_x2() = x2;
2319         }
2320 }
2321
2322
2323 void
2324 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2325 {
2326         Marker* marker;
2327
2328         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2329                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2330                 /*NOTREACHED*/
2331         }
2332
2333         bool is_start;
2334
2335         Location  *location = find_location_from_marker (marker, is_start);
2336
2337         drag_info.item = item;
2338         drag_info.data = marker;
2339         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2340         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2341
2342         start_grab (event);
2343
2344         _dragging_edit_point = true;
2345
2346         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
2347
2348         update_marker_drag_item (location);
2349
2350         if (location->is_mark()) {
2351                 // marker_drag_line->show();
2352                 // marker_drag_line->raise_to_top();
2353         } else {
2354                 range_marker_drag_rect->show();
2355                 //range_marker_drag_rect->raise_to_top();
2356         }
2357
2358         if (is_start) {
2359                 show_verbose_time_cursor (location->start(), 10);
2360         } else {
2361                 show_verbose_time_cursor (location->end(), 10);
2362         }
2363
2364         Selection::Operation op = Keyboard::selection_type (event->button.state);
2365
2366         switch (op) {
2367         case Selection::Toggle:
2368                 selection->toggle (marker);
2369                 break;
2370         case Selection::Set:
2371                 if (!selection->selected (marker)) {
2372                         selection->set (marker);
2373                 }
2374                 break;
2375         case Selection::Extend:
2376         {
2377                 Locations::LocationList ll;
2378                 list<Marker*> to_add;
2379                 nframes64_t s, e;
2380                 selection->markers.range (s, e);
2381                 s = min (marker->position(), s);
2382                 e = max (marker->position(), e);
2383                 s = min (s, e);
2384                 e = max (s, e);
2385                 if (e < max_frames) {
2386                         ++e;
2387                 }
2388                 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2389                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2390                         LocationMarkers* lm = find_location_markers (*i);
2391                         if (lm) {
2392                                 if (lm->start) {
2393                                         to_add.push_back (lm->start);
2394                                 }
2395                                 if (lm->end) {
2396                                         to_add.push_back (lm->end);
2397                                 }
2398                         }
2399                 }
2400                 if (!to_add.empty()) {
2401                         selection->add (to_add);
2402                 }
2403                 break;
2404         }
2405         case Selection::Add:
2406                 selection->add (marker);
2407                 break;
2408         }
2409
2410         /* set up copies for us to manipulate during the drag */
2411
2412         drag_info.clear_copied_locations ();
2413
2414         for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2415                 Location  *l = find_location_from_marker (*i, is_start);
2416                 drag_info.copied_locations.push_back (new Location (*l));
2417         }
2418 }
2419
2420 void
2421 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2422 {
2423         nframes64_t f_delta = 0;
2424         nframes64_t newframe;
2425         bool is_start;
2426         bool move_both = false;
2427         Marker* dragged_marker = (Marker*) drag_info.data;
2428         Marker* marker;
2429         Location  *real_location;
2430         Location  *copy_location;
2431
2432         if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2433                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2434         } else {
2435                 newframe = 0;
2436         }
2437
2438         nframes64_t next = newframe;
2439
2440         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2441                 snap_to (newframe, 0, true);
2442         }
2443         
2444         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { 
2445                 return;
2446         }
2447
2448         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2449                 move_both = true;
2450         }
2451
2452         MarkerSelection::iterator i;
2453         list<Location*>::iterator x;
2454
2455         /* find the marker we're dragging, and compute the delta */
2456
2457         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2458              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2459              ++i, ++x) {
2460
2461                 copy_location = *x;
2462                 marker = *i;
2463
2464                 if (marker == dragged_marker) {
2465
2466                         if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2467                                 /* que pasa ?? */
2468                                 return;
2469                         }
2470
2471                         if (real_location->is_mark()) {
2472                                 f_delta = newframe - copy_location->start();
2473                         } else {
2474
2475
2476                                 switch (marker->type()) {
2477                                 case Marker::Start:
2478                                 case Marker::LoopStart:
2479                                 case Marker::PunchIn:
2480                                         f_delta = newframe - copy_location->start();
2481                                         break;
2482
2483                                 case Marker::End:
2484                                 case Marker::LoopEnd:
2485                                 case Marker::PunchOut:
2486                                         f_delta = newframe - copy_location->end();
2487                                         break;
2488                                 default:
2489                                         /* what kind of marker is this ? */
2490                                         return;
2491                                 }
2492                         }
2493                         break;
2494                 }
2495         }
2496
2497         if (i == selection->markers.end()) {
2498                 /* hmm, impossible - we didn't find the dragged marker */
2499                 return;
2500         }
2501
2502         /* now move them all */
2503
2504         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2505              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2506              ++i, ++x) {
2507
2508                 copy_location = *x;
2509                 marker = *i;
2510
2511                 /* call this to find out if its the start or end */
2512                 
2513                 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2514                         continue;
2515                 }
2516                 
2517                 if (real_location->locked()) {
2518                         continue;
2519                 }
2520
2521                 if (copy_location->is_mark()) {
2522
2523                         /* just move it */
2524                         
2525                         copy_location->set_start (copy_location->start() + f_delta);
2526
2527                 } else {
2528                         
2529                         nframes64_t new_start = copy_location->start() + f_delta;
2530                         nframes64_t new_end = copy_location->end() + f_delta;
2531                         
2532                         if (is_start) { // start-of-range marker
2533                                 
2534                                 if (move_both) {
2535                                         copy_location->set_start (new_start);
2536                                         copy_location->set_end (new_end);
2537                                 } else  if (new_start < copy_location->end()) {
2538                                         copy_location->set_start (new_start);
2539                                 } else { 
2540                                         snap_to (next, 1, true);
2541                                         copy_location->set_end (next);
2542                                         copy_location->set_start (newframe);
2543                                 }
2544                                 
2545                         } else { // end marker
2546                                 
2547                                 if (move_both) {
2548                                         copy_location->set_end (new_end);
2549                                         copy_location->set_start (new_start);
2550                                 } else if (new_end > copy_location->start()) {
2551                                         copy_location->set_end (new_end);
2552                                 } else if (newframe > 0) {
2553                                         snap_to (next, -1, true);
2554                                         copy_location->set_start (next);
2555                                         copy_location->set_end (newframe);
2556                                 }
2557                         }
2558                 }
2559                 update_marker_drag_item (copy_location);
2560
2561                 LocationMarkers* lm = find_location_markers (real_location);
2562
2563                 if (lm) {
2564                         lm->set_position (copy_location->start(), copy_location->end());
2565                 }
2566         }
2567
2568         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2569         drag_info.first_move = false;
2570
2571         if (drag_info.copied_locations.empty()) {
2572                 abort();
2573         }
2574
2575         edit_point_clock.set (drag_info.copied_locations.front()->start());
2576         show_verbose_time_cursor (newframe, 10);
2577
2578 #ifdef GTKOSX
2579         track_canvas->update_now ();
2580 #endif
2581         edit_point_clock.set (copy_location->start());
2582 }
2583
2584 void
2585 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2586 {
2587         if (drag_info.first_move) {
2588
2589                 /* just a click, do nothing but finish
2590                    off the selection process
2591                 */
2592
2593                 Selection::Operation op = Keyboard::selection_type (event->button.state);
2594                 Marker* marker = (Marker *) drag_info.data;
2595
2596                 switch (op) {
2597                 case Selection::Set:
2598                         if (selection->selected (marker) && selection->markers.size() > 1) {
2599                                 selection->set (marker);
2600                         }
2601                         break;
2602
2603                 case Selection::Toggle:
2604                 case Selection::Extend:
2605                 case Selection::Add:
2606                         break;
2607                 }
2608                 
2609                 return;
2610         }
2611
2612         _dragging_edit_point = false;
2613         
2614
2615         begin_reversible_command ( _("move marker") );
2616         XMLNode &before = session->locations()->get_state();
2617
2618         MarkerSelection::iterator i;
2619         list<Location*>::iterator x;
2620         bool is_start;
2621
2622         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2623              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2624              ++i, ++x) {
2625         
2626                 Location * location = find_location_from_marker ((*i), is_start);
2627                 
2628                 if (location) {
2629                         
2630                         if (location->locked()) {
2631                                 return;
2632                         }
2633                         
2634                         if (location->is_mark()) {
2635                                 location->set_start ((*x)->start());
2636                         } else {
2637                                 location->set ((*x)->start(), (*x)->end());
2638                         }
2639                 }
2640         }
2641
2642         XMLNode &after = session->locations()->get_state();
2643         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2644         commit_reversible_command ();
2645         
2646         marker_drag_line->hide();
2647         range_marker_drag_rect->hide();
2648 }
2649
2650 void
2651 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2652 {
2653         Marker* marker;
2654         MeterMarker* meter_marker;
2655
2656         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2657                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2658                 /*NOTREACHED*/
2659         }
2660
2661         meter_marker = dynamic_cast<MeterMarker*> (marker);
2662
2663         MetricSection& section (meter_marker->meter());
2664
2665         if (!section.movable()) {
2666                 return;
2667         }
2668
2669         drag_info.item = item;
2670         drag_info.copy = false;
2671         drag_info.data = marker;
2672         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2673         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2674
2675         start_grab (event);
2676
2677         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2678
2679         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2680 }
2681
2682 void
2683 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2684 {
2685         Marker* marker;
2686         MeterMarker* meter_marker;
2687
2688         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2689                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2690                 /*NOTREACHED*/
2691         }
2692
2693         meter_marker = dynamic_cast<MeterMarker*> (marker);
2694         
2695         // create a dummy marker for visual representation of moving the copy.
2696         // The actual copying is not done before we reach the finish callback.
2697         char name[64];
2698         snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2699         MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, 
2700                                                   *new MeterSection(meter_marker->meter()));
2701
2702         drag_info.item = &new_marker->the_item();
2703         drag_info.copy = true;
2704         drag_info.data = new_marker;
2705         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2706         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2707
2708         start_grab (event);
2709
2710         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2711
2712         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2713 }
2714
2715 void
2716 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2717 {
2718         MeterMarker* marker = (MeterMarker *) drag_info.data;
2719         nframes64_t adjusted_frame;
2720
2721         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2722                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2723         }
2724         else {
2725                 adjusted_frame = 0;
2726         }
2727         
2728         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2729                 snap_to (adjusted_frame);
2730         }
2731         
2732         if (adjusted_frame == drag_info.last_pointer_frame) return;
2733
2734         marker->set_position (adjusted_frame);
2735         
2736         
2737         drag_info.last_pointer_frame = adjusted_frame;
2738         drag_info.first_move = false;
2739
2740         show_verbose_time_cursor (adjusted_frame, 10);
2741 }
2742
2743 void
2744 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2745 {
2746         if (drag_info.first_move) return;
2747
2748         meter_marker_drag_motion_callback (drag_info.item, event);
2749         
2750         MeterMarker* marker = (MeterMarker *) drag_info.data;
2751         BBT_Time when;
2752         
2753         TempoMap& map (session->tempo_map());
2754         map.bbt_time (drag_info.last_pointer_frame, when);
2755         
2756         if (drag_info.copy == true) {
2757                 begin_reversible_command (_("copy meter mark"));
2758                 XMLNode &before = map.get_state();
2759                 map.add_meter (marker->meter(), when);
2760                 XMLNode &after = map.get_state();
2761                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2762                 commit_reversible_command ();
2763
2764                 // delete the dummy marker we used for visual representation of copying.
2765                 // a new visual marker will show up automatically.
2766                 delete marker;
2767         } else {
2768                 begin_reversible_command (_("move meter mark"));
2769                 XMLNode &before = map.get_state();
2770                 map.move_meter (marker->meter(), when);
2771                 XMLNode &after = map.get_state();
2772                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2773                 commit_reversible_command ();
2774         }
2775 }
2776
2777 void
2778 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2779 {
2780         Marker* marker;
2781         TempoMarker* tempo_marker;
2782
2783         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2784                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2785                 /*NOTREACHED*/
2786         }
2787
2788         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2789                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2790                 /*NOTREACHED*/
2791         }
2792
2793         MetricSection& section (tempo_marker->tempo());
2794
2795         if (!section.movable()) {
2796                 return;
2797         }
2798
2799         drag_info.item = item;
2800         drag_info.copy = false;
2801         drag_info.data = marker;
2802         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2803         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2804
2805         start_grab (event);
2806
2807         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2808         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2809 }
2810
2811 void
2812 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2813 {
2814         Marker* marker;
2815         TempoMarker* tempo_marker;
2816
2817         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2818                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2819                 /*NOTREACHED*/
2820         }
2821
2822         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2823                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2824                 /*NOTREACHED*/
2825         }
2826
2827         // create a dummy marker for visual representation of moving the copy.
2828         // The actual copying is not done before we reach the finish callback.
2829         char name[64];
2830         snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2831         TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, 
2832                                                   *new TempoSection(tempo_marker->tempo()));
2833
2834         drag_info.item = &new_marker->the_item();
2835         drag_info.copy = true;
2836         drag_info.data = new_marker;
2837         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2838         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2839
2840         start_grab (event);
2841
2842         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2843
2844         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2845 }
2846
2847 void
2848 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2849 {
2850         TempoMarker* marker = (TempoMarker *) drag_info.data;
2851         nframes64_t adjusted_frame;
2852         
2853         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2854                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2855         }
2856         else {
2857                 adjusted_frame = 0;
2858         }
2859
2860         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2861                 snap_to (adjusted_frame);
2862         }
2863         
2864         if (adjusted_frame == drag_info.last_pointer_frame) return;
2865
2866         /* OK, we've moved far enough to make it worth actually move the thing. */
2867                 
2868         marker->set_position (adjusted_frame);
2869         
2870         show_verbose_time_cursor (adjusted_frame, 10);
2871
2872         drag_info.last_pointer_frame = adjusted_frame;
2873         drag_info.first_move = false;
2874 }
2875
2876 void
2877 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2878 {
2879         if (drag_info.first_move) return;
2880         
2881         tempo_marker_drag_motion_callback (drag_info.item, event);
2882         
2883         TempoMarker* marker = (TempoMarker *) drag_info.data;
2884         BBT_Time when;
2885         
2886         TempoMap& map (session->tempo_map());
2887         map.bbt_time (drag_info.last_pointer_frame, when);
2888
2889         if (drag_info.copy == true) {
2890                 begin_reversible_command (_("copy tempo mark"));
2891                 XMLNode &before = map.get_state();
2892                 map.add_tempo (marker->tempo(), when);
2893                 XMLNode &after = map.get_state();
2894                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2895                 commit_reversible_command ();
2896                 
2897                 // delete the dummy marker we used for visual representation of copying.
2898                 // a new visual marker will show up automatically.
2899                 delete marker;
2900         } else {
2901                 begin_reversible_command (_("move tempo mark"));
2902                 XMLNode &before = map.get_state();
2903                 map.move_tempo (marker->tempo(), when);
2904                 XMLNode &after = map.get_state();
2905                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2906                 commit_reversible_command ();
2907         }
2908 }
2909
2910 void
2911 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2912 {
2913         ControlPoint* control_point;
2914
2915         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2916                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2917                 /*NOTREACHED*/
2918         }
2919
2920         // We shouldn't remove the first or last gain point
2921         if (control_point->line().is_last_point(*control_point) ||
2922                 control_point->line().is_first_point(*control_point)) { 
2923                 return;
2924         }
2925
2926         control_point->line().remove_point (*control_point);
2927 }
2928
2929 void
2930 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2931 {
2932         ControlPoint* control_point;
2933
2934         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2935                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2936                 /*NOTREACHED*/
2937         }
2938
2939         control_point->line().remove_point (*control_point);
2940 }
2941
2942 void
2943 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2944 {
2945         ControlPoint* control_point;
2946
2947         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2948                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2949                 /*NOTREACHED*/
2950         }
2951
2952         drag_info.item = item;
2953         drag_info.data = control_point;
2954         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2955         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2956
2957         start_grab (event, fader_cursor);
2958
2959         // start the grab at the center of the control point so
2960         // the point doesn't 'jump' to the mouse after the first drag
2961         drag_info.grab_x = control_point->get_x();
2962         drag_info.grab_y = control_point->get_y();
2963
2964         control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2965         track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2966
2967         drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2968
2969         control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2970
2971         float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
2972         set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction), 
2973                                    drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
2974
2975         show_verbose_canvas_cursor ();
2976 }
2977
2978 void
2979 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2980 {
2981         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2982
2983         double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2984         double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2985
2986         if (event->button.state & Keyboard::SecondaryModifier) {
2987                 dx *= 0.1;
2988                 dy *= 0.1;
2989         }
2990
2991         double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2992         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2993
2994         // calculate zero crossing point. back off by .01 to stay on the
2995         // positive side of zero
2996         double _unused = 0;
2997         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2998         cp->line().parent_group().i2w(_unused, zero_gain_y);
2999
3000         // make sure we hit zero when passing through
3001         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3002                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3003                 cy = zero_gain_y;
3004         }
3005
3006         if (drag_info.x_constrained) {
3007                 cx = drag_info.grab_x;
3008         }
3009         if (drag_info.y_constrained) {
3010                 cy = drag_info.grab_y;
3011         }
3012
3013         drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3014         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3015
3016         cp->line().parent_group().w2i (cx, cy);
3017
3018         cx = max (0.0, cx);
3019         cy = max (0.0, cy);
3020         cy = min ((double) cp->line().height(), cy);
3021
3022         //translate cx to frames
3023         nframes64_t cx_frames = unit_to_frame (cx);
3024
3025         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3026                 snap_to (cx_frames);
3027         }
3028
3029         float fraction = 1.0 - (cy / cp->line().height());
3030
3031         bool push;
3032
3033         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3034                 push = true;
3035         } else {
3036                 push = false;
3037         }
3038
3039         cp->line().point_drag (*cp, cx_frames , fraction, push);
3040         
3041         set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3042
3043         drag_info.first_move = false;
3044 }
3045
3046 void
3047 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3048 {
3049         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3050
3051         if (drag_info.first_move) {
3052
3053                 /* just a click */
3054                 
3055                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3056                         reset_point_selection ();
3057                 }
3058
3059         } else {
3060                 control_point_drag_motion_callback (item, event);
3061         }
3062         cp->line().end_drag (cp);
3063 }
3064
3065 void
3066 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3067 {
3068         switch (mouse_mode) {
3069         case MouseGain:
3070                 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3071                 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3072                 break;
3073         default:
3074                 break;
3075         }
3076 }
3077
3078 void
3079 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3080 {
3081         AutomationLine* al;
3082         
3083         if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3084                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3085                 /*NOTREACHED*/
3086         }
3087
3088         start_line_grab (al, event);
3089 }
3090
3091 void
3092 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3093 {
3094         double cx;
3095         double cy;
3096         nframes64_t frame_within_region;
3097
3098         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3099            origin.
3100         */
3101
3102         cx = event->button.x;
3103         cy = event->button.y;
3104         line->parent_group().w2i (cx, cy);
3105         frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3106
3107         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
3108                                             current_line_drag_info.after)) {
3109                 /* no adjacent points */
3110                 return;
3111         }
3112
3113         drag_info.item = &line->grab_item();
3114         drag_info.data = line;
3115         drag_info.motion_callback = &Editor::line_drag_motion_callback;
3116         drag_info.finished_callback = &Editor::line_drag_finished_callback;
3117
3118         start_grab (event, fader_cursor);
3119
3120         double fraction = 1.0 - (cy / line->height());
3121
3122         line->start_drag (0, drag_info.grab_frame, fraction);
3123         
3124         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3125                                    drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3126         show_verbose_canvas_cursor ();
3127 }
3128
3129 void
3130 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3131 {
3132         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3133
3134         double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3135
3136         if (event->button.state & Keyboard::SecondaryModifier) {
3137                 dy *= 0.1;
3138         }
3139
3140         double cx = drag_info.current_pointer_x;
3141         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3142
3143         // calculate zero crossing point. back off by .01 to stay on the
3144         // positive side of zero
3145         double _unused = 0;
3146         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3147         line->parent_group().i2w(_unused, zero_gain_y);
3148
3149         // make sure we hit zero when passing through
3150         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3151                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3152                 cy = zero_gain_y;
3153         }
3154
3155         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3156
3157         line->parent_group().w2i (cx, cy);
3158
3159         cy = max (0.0, cy);
3160         cy = min ((double) line->height(), cy);
3161
3162         double fraction = 1.0 - (cy / line->height());
3163
3164         bool push;
3165
3166         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3167                 push = false;
3168         } else {
3169                 push = true;
3170         }
3171
3172         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3173         
3174         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3175 }
3176
3177 void
3178 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3179 {
3180         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3181         line_drag_motion_callback (item, event);
3182         line->end_drag (0);
3183 }
3184
3185 void
3186 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3187 {
3188         if (selection->regions.empty() || clicked_regionview == 0) {
3189                 return;
3190         }
3191         _region_motion_group->raise_to_top ();
3192         drag_info.copy = false;
3193         drag_info.item = item;
3194         drag_info.data = clicked_regionview;
3195
3196         if (Config->get_edit_mode() == Splice) {
3197                 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3198                 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3199         } else {
3200                 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3201                 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3202         }
3203
3204         start_grab (event);
3205
3206         double speed = 1.0;
3207         TimeAxisView* tvp = clicked_axisview;
3208         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3209
3210         if (tv && tv->is_track()) {
3211                 speed = tv->get_diskstream()->speed();
3212         }
3213         
3214         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3215         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3216         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3217         drag_info.dest_trackview = drag_info.source_trackview;
3218         // we want a move threshold
3219         drag_info.want_move_threshold = true;
3220         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3221
3222         begin_reversible_command (_("move region(s)"));
3223
3224         /* sync the canvas to what we think is its current state */
3225         track_canvas->update_now();
3226 }
3227
3228 void
3229 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3230 {
3231         drag_info.copy = false;
3232         drag_info.item = item;
3233         drag_info.data = clicked_axisview;
3234         drag_info.source_trackview = clicked_axisview;
3235         drag_info.dest_trackview = drag_info.source_trackview;
3236         drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3237         drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3238
3239         start_grab (event);
3240 }
3241
3242 void
3243 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3244 {
3245         if (selection->regions.empty() || clicked_regionview == 0) {
3246                 return;
3247         }
3248         _region_motion_group->raise_to_top ();
3249         drag_info.copy = true;
3250         drag_info.item = item;
3251         drag_info.data = clicked_regionview;    
3252
3253         start_grab(event);
3254
3255         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3256         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3257         double speed = 1.0;
3258
3259         if (rtv && rtv->is_track()) {
3260                 speed = rtv->get_diskstream()->speed();
3261         }
3262         
3263         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3264         drag_info.dest_trackview = drag_info.source_trackview;
3265         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3266         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3267         // we want a move threshold
3268         drag_info.want_move_threshold = true;
3269         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3270         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3271         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3272 }
3273
3274 void
3275 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3276 {
3277         if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3278                 return;
3279         }
3280
3281         drag_info.copy = false;
3282         drag_info.item = item;
3283         drag_info.data = clicked_regionview;
3284         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3285         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3286
3287         start_grab (event);
3288
3289         double speed = 1.0;
3290         TimeAxisView* tvp = clicked_axisview;
3291         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3292
3293         if (tv && tv->is_track()) {
3294                 speed = tv->get_diskstream()->speed();
3295         }
3296         
3297         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3298         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3299         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3300         drag_info.dest_trackview = drag_info.source_trackview;
3301         // we want a move threshold
3302         drag_info.want_move_threshold = true;
3303         drag_info.brushing = true;
3304         
3305         begin_reversible_command (_("Drag region brush"));
3306 }
3307
3308 void
3309 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3310 {
3311         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3312
3313                 drag_info.want_move_threshold = false; // don't copy again
3314
3315                 /* duplicate the regionview(s) and region(s) */
3316
3317                 vector<RegionView*> new_regionviews;
3318                 
3319                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3320                         RegionView* rv;
3321                         RegionView* nrv;
3322
3323                         rv = (*i);
3324                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3325                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3326                         
3327                         const boost::shared_ptr<const Region> original = rv->region();
3328                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3329
3330                         if (arv) {
3331                                 boost::shared_ptr<AudioRegion> audioregion_copy
3332                                         = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3333                                 nrv = new AudioRegionView (*arv, audioregion_copy);
3334                         } else if (mrv) {
3335                                 boost::shared_ptr<MidiRegion> midiregion_copy
3336                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3337                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
3338                         } else {
3339                                 continue;
3340                         }
3341
3342                         nrv->get_canvas_group()->show ();
3343                         new_regionviews.push_back (nrv);
3344                 }
3345
3346                 if (new_regionviews.empty()) {
3347                         return;
3348                 }
3349
3350                 /* reset selection to new regionviews. This will not set selection visual status for 
3351                    these regionviews since they don't belong to a track, so do that by hand too.
3352                  */
3353
3354                 selection->set (new_regionviews);
3355
3356                 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3357                         (*i)->set_selected (true);
3358                 }
3359
3360                 /* reset drag_info data to reflect the fact that we are dragging the copies */
3361                 
3362                 drag_info.data = new_regionviews.front();
3363
3364                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3365                 /* 
3366                    sync the canvas to what we think is its current state 
3367                    without it, the canvas seems to 
3368                    "forget" to update properly after the upcoming reparent() 
3369                    ..only if the mouse is in rapid motion at the time of the grab. 
3370                    something to do with regionview creation raking so long?
3371                  */
3372                 track_canvas->update_now();
3373         }
3374 }
3375
3376 bool
3377 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3378 {
3379         /* Which trackview is this ? */
3380
3381         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3382         (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3383
3384         /* The region motion is only processed if the pointer is over
3385            an audio track.
3386         */
3387         
3388         if (!(*tv) || !(*tv)->is_track()) {
3389                 /* To make sure we hide the verbose canvas cursor when the mouse is 
3390                    not held over and audiotrack. 
3391                 */
3392                 hide_verbose_canvas_cursor ();
3393                 return false;
3394         }
3395
3396         return true;
3397 }
3398
3399 struct RegionSelectionByPosition {
3400     bool operator() (RegionView*a, RegionView* b) {
3401             return a->region()->position () < b->region()->position();
3402     }
3403 };
3404
3405 void
3406 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3407 {
3408         RouteTimeAxisView* tv;
3409         
3410         if (!check_region_drag_possible (&tv)) {
3411                 return;
3412         }
3413
3414         if (!drag_info.move_threshold_passed) {
3415                 return;
3416         }
3417
3418         int dir;
3419
3420         if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3421                 dir = 1;
3422         } else {
3423                 dir = -1;
3424         }
3425
3426         RegionSelection copy (selection->regions);
3427
3428         RegionSelectionByPosition cmp;
3429         copy.sort (cmp);
3430
3431         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3432
3433                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3434
3435                 if (!atv) {
3436                         continue;
3437                 }
3438
3439                 boost::shared_ptr<Playlist> playlist;
3440
3441                 if ((playlist = atv->playlist()) == 0) {
3442                         continue;
3443                 }
3444
3445                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3446                         continue;
3447                 } 
3448
3449                 if (dir > 0) {
3450                         if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3451                                 continue;
3452                         }
3453                 } else {
3454                         if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3455                                 continue;
3456                         }
3457                 }
3458
3459                 
3460                 playlist->shuffle ((*i)->region(), dir);
3461
3462                 drag_info.grab_x = drag_info.current_pointer_x;
3463         }
3464 }
3465
3466 void
3467 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3468 {
3469 }
3470
3471 void
3472 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3473 {
3474         double x_delta;
3475         double y_delta = 0;
3476         RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data); 
3477         nframes64_t pending_region_position = 0;
3478         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3479         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
3480         bool clamp_y_axis = false;
3481         vector<int32_t>  height_list(512) ;
3482         vector<int32_t>::iterator j;
3483         RouteTimeAxisView* tv;
3484
3485         possibly_copy_regions_during_grab (event);
3486
3487         if (!check_region_drag_possible (&tv)) {
3488                 return;
3489         }
3490
3491         original_pointer_order = drag_info.dest_trackview->order;
3492         
3493         /************************************************************
3494              Y-Delta Computation
3495         ************************************************************/   
3496
3497         if (drag_info.brushing) {
3498                 clamp_y_axis = true;
3499                 pointer_y_span = 0;
3500                 goto y_axis_done;
3501         }
3502
3503         if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3504
3505                 int32_t children = 0, numtracks = 0;
3506                 // XXX hard coding track limit, oh my, so very very bad
3507                 bitset <1024> tracks (0x00);
3508                 /* get a bitmask representing the visible tracks */
3509
3510                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3511                         TimeAxisView *tracklist_timeview;
3512                         tracklist_timeview = (*i);
3513                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3514                         TimeAxisView::Children children_list;
3515               
3516                         /* zeroes are audio tracks. ones are other types. */
3517               
3518                         if (!rtv2->hidden()) {
3519                                 
3520                                 if (visible_y_high < rtv2->order) {
3521                                         visible_y_high = rtv2->order;
3522                                 }
3523                                 if (visible_y_low > rtv2->order) {
3524                                         visible_y_low = rtv2->order;
3525                                 }
3526                 
3527                                 if (!rtv2->is_track()) {                                  
3528                                         tracks = tracks |= (0x01 << rtv2->order);
3529                                 }
3530         
3531                                 height_list[rtv2->order] = (*i)->current_height();
3532                                 children = 1;
3533
3534                                 if ((children_list = rtv2->get_child_list()).size() > 0) {
3535                                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
3536                                                 tracks = tracks |= (0x01 << (rtv2->order + children));
3537                                                 height_list[rtv2->order + children] =  (*j)->current_height();
3538                                                 numtracks++;
3539                                                 children++;     
3540                                         }
3541                                 }
3542                                 numtracks++;        
3543                         }
3544                 }
3545                 /* find the actual span according to the canvas */
3546
3547                 canvas_pointer_y_span = pointer_y_span;
3548                 if (drag_info.dest_trackview->order >= tv->order) {
3549                         int32_t y;
3550                         for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3551                                 if (height_list[y] == 0 ) {
3552                                         canvas_pointer_y_span--;
3553                                 }
3554                         }
3555                 } else {
3556                         int32_t y;
3557                         for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3558                                 if (    height_list[y] == 0 ) {
3559                                         canvas_pointer_y_span++;
3560                                 }
3561                         }
3562                 }
3563
3564                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3565                         RegionView* rv2 = (*i);
3566                         double ix1, ix2, iy1, iy2;
3567                         int32_t n = 0;
3568
3569                         if (rv2->region()->locked()) {
3570                                 continue;
3571                         }
3572
3573                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3574                         rv2->get_canvas_frame()->i2w (ix1, iy1);
3575                         iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3576
3577                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3578                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3579
3580                         if (rtv2->order != original_pointer_order) {    
3581                                 /* this isn't the pointer track */      
3582
3583                                 if (canvas_pointer_y_span > 0) {
3584
3585                                         /* moving up the canvas */
3586                                         if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3587         
3588                                                 int32_t visible_tracks = 0;
3589                                                 while (visible_tracks < canvas_pointer_y_span ) {
3590                                                         visible_tracks++;
3591                   
3592                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3593                                                                 /* we're passing through a hidden track */
3594                                                                 n--;
3595                                                         }                 
3596                                                 }
3597                  
3598                                                 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
3599                                                         clamp_y_axis = true;
3600                                                 }
3601                     
3602                                         } else {
3603                                                 clamp_y_axis = true;
3604                                         }                 
3605                   
3606                                 } else if (canvas_pointer_y_span < 0) {
3607
3608                                         /*moving down the canvas*/
3609
3610                                         if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3611                     
3612                     
3613                                                 int32_t visible_tracks = 0;
3614                     
3615                                                 while (visible_tracks > canvas_pointer_y_span ) {
3616                                                         visible_tracks--;
3617                       
3618                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
3619                                                                 n++;
3620                                                         }                
3621                                                 }
3622                                                 if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3623                                                         clamp_y_axis = true;
3624                             
3625                                                 }
3626                                         } else {
3627                           
3628                                                 clamp_y_axis = true;
3629                                         }
3630                                 }               
3631                   
3632                         } else {
3633                       
3634                                 /* this is the pointer's track */
3635                                 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
3636                                         clamp_y_axis = true;
3637                                 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3638                                         clamp_y_axis = true;
3639                                 }
3640                         }             
3641                         if (clamp_y_axis) {
3642                                 break;
3643                         }
3644                 }
3645
3646         } else  if (drag_info.dest_trackview == tv) {
3647                 clamp_y_axis = true;
3648         }         
3649
3650   y_axis_done:
3651         if (!clamp_y_axis) {
3652                 drag_info.dest_trackview = tv;        
3653         }
3654           
3655         /************************************************************
3656             X DELTA COMPUTATION
3657         ************************************************************/
3658
3659         /* compute the amount of pointer motion in frames, and where
3660            the region would be if we moved it by that much.
3661         */
3662         if ( drag_info.move_threshold_passed ) {
3663
3664                 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3665
3666                         nframes64_t sync_frame;
3667                         nframes64_t sync_offset;
3668                         int32_t sync_dir;
3669
3670                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3671
3672                         sync_offset = rv->region()->sync_offset (sync_dir);
3673
3674                         /* we don't handle a sync point that lies before zero.
3675                          */
3676                         if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3677                                 sync_frame = pending_region_position + (sync_dir*sync_offset);
3678
3679                                 /* we snap if the snap modifier is not enabled.
3680                                  */
3681             
3682                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3683                                         snap_to (sync_frame);   
3684                                 }
3685             
3686                                 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3687
3688                         } else {
3689                                 pending_region_position = drag_info.last_frame_position;
3690                         }
3691             
3692                 } else {
3693                         pending_region_position = 0;
3694                 }
3695           
3696                 if (pending_region_position > max_frames - rv->region()->length()) {
3697                         pending_region_position = drag_info.last_frame_position;
3698                 }
3699
3700                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3701
3702                 bool x_move_allowed;
3703                 
3704                 if (Config->get_edit_mode() == Lock) {
3705                         if (drag_info.copy) {
3706                                 x_move_allowed = !drag_info.x_constrained;
3707                         } else {
3708                                 /* in locked edit mode, reverse the usual meaning of x_constrained */
3709                                 x_move_allowed = drag_info.x_constrained;
3710                         }
3711                 } else {
3712                         x_move_allowed = !drag_info.x_constrained;
3713                 }
3714
3715                 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3716
3717                         /* now compute the canvas unit distance we need to move the regionview
3718                            to make it appear at the new location.
3719                         */
3720
3721                         if (pending_region_position > drag_info.last_frame_position) {
3722                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3723                         } else {
3724                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3725                                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3726
3727                                         RegionView* rv2 = (*i);
3728
3729                                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3730         
3731                                         double ix1, ix2, iy1, iy2;
3732                                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3733                                         rv2->get_canvas_frame()->i2w (ix1, iy1);
3734                         
3735                                         if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3736                                                 //      do_move = false;
3737                                                 x_delta = 0;
3738                                                 pending_region_position = drag_info.last_frame_position;
3739                                                 break;
3740                                         }
3741                                 }
3742
3743                         }
3744                 
3745                         drag_info.last_frame_position = pending_region_position;
3746
3747                 } else {
3748                         x_delta = 0;
3749                 }
3750
3751         } else {
3752                 /* threshold not passed */
3753
3754                 x_delta = 0;
3755         }
3756         
3757         /*************************************************************
3758             PREPARE TO MOVE
3759         ************************************************************/
3760
3761         if (x_delta == 0 && (pointer_y_span == 0)) {
3762                 /* haven't reached next snap point, and we're not switching
3763                    trackviews. nothing to do.
3764                 */
3765                 return;
3766         }
3767
3768         /*************************************************************
3769             MOTION                                                                    
3770         ************************************************************/
3771         bool do_move = true;
3772         if (drag_info.first_move) {
3773                 if (!drag_info.move_threshold_passed) {
3774                         do_move = false;
3775                 }
3776         }
3777
3778         if (do_move) {
3779
3780                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3781                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3782                 
3783                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3784             
3785                         RegionView* rv = (*i);
3786                         double ix1, ix2, iy1, iy2;
3787                         int32_t temp_pointer_y_span = pointer_y_span;
3788
3789                         if (rv->region()->locked()) {
3790                                 continue;
3791                         }
3792
3793                         /* get item BBox, which will be relative to parent. so we have
3794                            to query on a child, then convert to world coordinates using
3795                            the parent.
3796                         */
3797
3798                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3799                         rv->get_canvas_frame()->i2w (ix1, iy1);
3800                         
3801                         cerr << "adjust y from " << iy1 << " using "
3802                              << vertical_adjustment.get_value() << " - "
3803                              << canvas_timebars_vsize
3804                              << endl;
3805
3806                         iy1 += get_trackview_group_vertical_offset ();;
3807
3808                         if (drag_info.first_move) {
3809
3810                                 // hide any dependent views 
3811         
3812                                 rv->get_time_axis_view().hide_dependent_views (*rv);
3813
3814                                 /* 
3815                                    reparent to a non scrolling group so that we can keep the 
3816                                    region selection above all time axis views.
3817                                    reparenting means we have to move the rv as the two 
3818                                    parent groups have different coordinates.
3819                                 */
3820
3821                                 rv->get_canvas_group()->property_y() =  iy1 - 1;
3822                                 rv->get_canvas_group()->reparent(*_region_motion_group);
3823
3824                                 rv->fake_set_opaque (true);
3825                         }
3826
3827                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3828                         RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3829                         RouteTimeAxisView* temp_rtv;
3830
3831                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3832                                 y_delta = 0;
3833                                 int32_t x = 0;
3834                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3835                                         if (x == canvas_rtv->order) {
3836                                                 /* we found the track the region is on */
3837                                                 if (x != original_pointer_order) {
3838                                                         /*this isn't from the same track we're dragging from */
3839                                                         temp_pointer_y_span = canvas_pointer_y_span;
3840                                                 }                 
3841                                                 while (temp_pointer_y_span > 0) {
3842                                                         /* we're moving up canvas-wise,
3843                                                            so  we need to find the next track height
3844                                                         */
3845                                                         if (j != height_list.begin()) {           
3846                                                                 j--;
3847                                                         }
3848                                                         if (x != original_pointer_order) { 
3849                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3850                                                                 if ((*j) == 0) {
3851                                                                         temp_pointer_y_span++;
3852                                                                 }
3853                                                         }          
3854                                                         y_delta -= (*j);        
3855                                                         temp_pointer_y_span--;  
3856                                                 }
3857
3858                                                 while (temp_pointer_y_span < 0) {                 
3859                                                         y_delta += (*j);
3860                                                         if (x != original_pointer_order) { 
3861                                                                 if ((*j) == 0) {
3862                                                                         temp_pointer_y_span--;
3863                                                                 }
3864                                                         }          
3865                     
3866                                                         if (j != height_list.end()) {                 
3867                                                                 j++;
3868                                                         }
3869                                                         temp_pointer_y_span++;
3870                                                 }
3871                                                 /* find out where we'll be when we move and set height accordingly */
3872                   
3873                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3874                                                 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3875                                                 rv->set_height (temp_rtv->current_height());
3876
3877                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3878                                                      personally, i think this can confuse things, but never mind.
3879                                                 */
3880                                   
3881                                                 //const GdkColor& col (temp_rtv->view->get_region_color());
3882                                                 //rv->set_color (const_cast<GdkColor&>(col));
3883                                                 break;          
3884                                         }
3885                                         x++;
3886                                 }
3887                         }
3888
3889                         /* prevent the regionview from being moved to before 
3890                            the zero position on the canvas.
3891                         */
3892                         /* clamp */
3893                 
3894                         if (x_delta < 0) {
3895                                 if (-x_delta > ix1) {
3896                                         x_delta = -ix1;
3897                                 }
3898                         } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3899                                 x_delta = max_frames - rv->region()->last_frame();
3900                         }
3901
3902                         if (drag_info.brushing) {
3903                                 mouse_brush_insert_region (rv, pending_region_position);
3904                         } else {
3905                                 rv->move (x_delta, y_delta);
3906                         }
3907
3908                 } /* foreach region */
3909
3910         } /* if do_move */
3911
3912         if (drag_info.first_move && drag_info.move_threshold_passed) {
3913                 cursor_group->raise_to_top();
3914                 drag_info.first_move = false;
3915         }
3916
3917         if (x_delta != 0 && !drag_info.brushing) {
3918                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3919         }
3920
3921
3922 void
3923 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3924 {
3925         bool nocommit = true;
3926         vector<RegionView*> copies;
3927         RouteTimeAxisView* source_tv;
3928         boost::shared_ptr<Diskstream> ds;
3929         boost::shared_ptr<Playlist> from_playlist;
3930         vector<RegionView*> new_selection;
3931         typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3932         PlaylistSet modified_playlists;
3933         PlaylistSet frozen_playlists;
3934         list <sigc::connection> modified_playlist_connections;
3935         pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3936
3937         /* first_move is set to false if the regionview has been moved in the 
3938            motion handler. 
3939         */
3940
3941         if (drag_info.first_move) {
3942                 /* just a click */
3943                 goto out;
3944         }
3945
3946         nocommit = false;
3947
3948         if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3949                 selection->set (pre_drag_region_selection);
3950                 pre_drag_region_selection.clear ();
3951         }
3952
3953         if (drag_info.brushing) {
3954                 /* all changes were made during motion event handlers */
3955                 
3956                 if (drag_info.copy) {
3957                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3958                                 copies.push_back (*i);
3959                         }
3960                 }
3961
3962                 goto out;
3963         }
3964
3965         char* op_string;
3966
3967         /* reverse this here so that we have the correct logic to finalize
3968            the drag.
3969         */
3970         
3971         if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3972                 drag_info.x_constrained = !drag_info.x_constrained;
3973         }
3974
3975         if (drag_info.copy) {
3976                 if (drag_info.x_constrained) {
3977                         op_string = _("fixed time region copy");
3978                 } else {
3979                         op_string = _("region copy");
3980                 } 
3981         } else {
3982                 if (drag_info.x_constrained) {
3983                         op_string = _("fixed time region drag");
3984                 } else {
3985                         op_string = _("region drag");
3986                 }
3987         }
3988
3989         begin_reversible_command (op_string);
3990
3991         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3992                         
3993                 RegionView* rv = (*i);              
3994                 double ix1, ix2, iy1, iy2;
3995                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3996                 rv->get_canvas_frame()->i2w (ix1, iy1);
3997                 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3998
3999                 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
4000                 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
4001                 double speed;
4002                 bool changed_tracks, changed_position;
4003                 nframes64_t where;
4004
4005                 if (rv->region()->locked()) {
4006                         ++i;
4007                         continue;
4008                 }
4009
4010                 /* adjust for track speed */
4011
4012                 speed = 1.0;
4013                 
4014                 if (dest_rtv && dest_rtv->get_diskstream()) {
4015                         speed = dest_rtv->get_diskstream()->speed();
4016                 }
4017                 
4018                 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
4019                 changed_tracks = (dest_tv != &rv->get_time_axis_view());
4020
4021                 if (changed_position && !drag_info.x_constrained) {
4022                         _master_group->w2i(ix1, iy1);
4023                         where = (nframes64_t) (unit_to_frame (ix1) * speed);
4024                 } else {
4025                         where = rv->region()->position();
4026                 }
4027                         
4028                 boost::shared_ptr<Region> new_region;
4029
4030
4031                 if (drag_info.copy) {
4032                         /* we already made a copy */
4033                         new_region = rv->region();
4034
4035                         /* undo the previous hide_dependent_views so that xfades don't
4036                            disappear on copying regions 
4037                         */
4038                 
4039                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
4040                 
4041                 } else if (changed_tracks && dest_rtv->playlist()) {
4042                         new_region = RegionFactory::create (rv->region());
4043                 }
4044
4045                 if (changed_tracks || drag_info.copy) {
4046
4047                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4048                         if (!to_playlist) {
4049                                 ++i;
4050                                 continue;
4051                         }
4052
4053                         latest_regionviews.clear ();
4054
4055                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4056                         
4057                         insert_result = modified_playlists.insert (to_playlist);
4058                         if (insert_result.second) {
4059                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4060                         }
4061
4062                         to_playlist->add_region (new_region, where);
4063
4064                         c.disconnect ();
4065                                                               
4066                         if (!latest_regionviews.empty()) {
4067                                 // XXX why just the first one ? we only expect one
4068                                 // commented out in nick_m's canvas reworking. is that intended?
4069                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4070                                 new_selection.push_back (latest_regionviews.front());
4071                         }
4072
4073                 } else {
4074                         /* 
4075                            motion on the same track. plonk the previously reparented region 
4076                            back to its original canvas group (its streamview).
4077                            No need to do anything for copies as they are fake regions which will be deleted.
4078                         */
4079
4080                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4081                         rv->get_canvas_group()->property_y() = 0;
4082                   
4083                         /* just change the model */
4084                         
4085                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4086
4087                         insert_result = modified_playlists.insert (playlist);
4088                         if (insert_result.second) {
4089                                 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4090                         }
4091                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
4092                         frozen_insert_result = frozen_playlists.insert(playlist);
4093                         if (frozen_insert_result.second) {
4094                                 playlist->freeze();
4095                         }
4096
4097                         rv->region()->set_position (where, (void*) this);
4098                 }
4099
4100                 if (changed_tracks && !drag_info.copy) {
4101
4102                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
4103                            because we may have copied the region and it has not been attached to a playlist.
4104                         */
4105
4106                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4107                         assert ((ds = source_tv->get_diskstream()));
4108                         assert ((from_playlist = ds->playlist()));
4109
4110                         /* moved to a different audio track, without copying */
4111
4112                         /* the region that used to be in the old playlist is not
4113                            moved to the new one - we use a copy of it. as a result,
4114                            any existing editor for the region should no longer be
4115                            visible.
4116                         */ 
4117             
4118                         rv->hide_region_editor();
4119                         rv->fake_set_opaque (false);
4120                         
4121                         /* remove the region from the old playlist */
4122
4123                         insert_result = modified_playlists.insert (from_playlist);
4124                         if (insert_result.second) {
4125                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4126                         }
4127
4128                         from_playlist->remove_region ((rv->region()));
4129                         
4130                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4131                            was selected in all of them, then removing it from a playlist will have removed all
4132                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
4133                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4134                            corresponding regionview, and the selection is now empty).
4135
4136                            this could have invalidated any and all iterators into the region selection.
4137
4138                            the heuristic we use here is: if the region selection is empty, break out of the loop
4139                            here. if the region selection is not empty, then restart the loop because we know that
4140                            we must have removed at least the region(view) we've just been working on as well as any
4141                            that we processed on previous iterations.
4142
4143                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4144                            we can just iterate.
4145                         */
4146
4147                         if (selection->regions.empty()) {
4148                                 break;
4149                         } else { 
4150                                 i = selection->regions.by_layer().begin();
4151                         }
4152
4153                 } else {
4154                         ++i;
4155                 }
4156                 
4157                 if (drag_info.copy) {
4158                         copies.push_back (rv);
4159                 }
4160         }
4161         
4162         if (new_selection.empty()) {
4163                 if (drag_info.copy) {
4164                         /* the region(view)s that are selected and being dragged around
4165                            are copies and do not belong to any track. remove them
4166                            from the selection right here.
4167                         */
4168                         selection->clear_regions();
4169                 }
4170         } else {
4171                 /* this will clear any existing selection that would have been
4172                    cleared in the other clause above
4173                 */
4174                 selection->set (new_selection);
4175         }
4176
4177         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4178                 (*p)->thaw();
4179         }
4180                         
4181   out:
4182         if (!nocommit) {
4183                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4184                         session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));      
4185                 }
4186                 commit_reversible_command ();
4187         }
4188
4189         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4190                 delete *x;
4191         }
4192
4193 }
4194         
4195 void
4196 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4197 {
4198         if (drag_info.move_threshold_passed) {
4199                 if (drag_info.first_move) {
4200                         // TODO: create region-create-drag region view here
4201                         drag_info.first_move = false;
4202                 }
4203
4204                 // TODO: resize region-create-drag region view here
4205         }
4206
4207
4208 void
4209 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4210 {
4211         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4212         if (!mtv)
4213                 return;
4214
4215         const boost::shared_ptr<MidiDiskstream> diskstream =
4216                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4217         
4218         if (!diskstream) {
4219                 warning << "Cannot create non-MIDI region" << endl;
4220                 return;
4221         }
4222
4223         if (drag_info.first_move) {
4224                 begin_reversible_command (_("create region"));
4225                 XMLNode &before = mtv->playlist()->get_state();
4226
4227                 nframes64_t start = drag_info.grab_frame;
4228                 snap_to (start, -1);
4229                 const Meter& m = session->tempo_map().meter_at(start);
4230                 const Tempo& t = session->tempo_map().tempo_at(start);
4231                 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4232
4233                 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4234                                 
4235                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4236                                              (RegionFactory::create(src, 0, (nframes_t) length, 
4237                                                                     PBD::basename_nosuffix(src->name()))), start);
4238                 XMLNode &after = mtv->playlist()->get_state();
4239                 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4240                 commit_reversible_command();
4241
4242         } else {
4243                 create_region_drag_motion_callback (item, event);
4244                 // TODO: create region-create-drag region here
4245         }
4246 }
4247
4248 void
4249 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4250 {
4251         /* Either add to or set the set the region selection, unless
4252            this is an alignment click (control used)
4253         */
4254         
4255         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4256                 TimeAxisView* tv = &rv.get_time_axis_view();
4257                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4258                 double speed = 1.0;
4259                 if (rtv && rtv->is_track()) {
4260                         speed = rtv->get_diskstream()->speed();
4261                 }
4262
4263                 nframes64_t where = get_preferred_edit_position();
4264
4265                 if (where >= 0) {
4266
4267                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4268                                 
4269                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4270                                 
4271                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4272                                 
4273                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
4274                                 
4275                         } else {
4276                                 
4277                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4278                         }
4279                 }
4280         }
4281 }
4282
4283 void
4284 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
4285 {
4286         char buf[128];
4287         SMPTE::Time smpte;
4288         BBT_Time bbt;
4289         int hours, mins;
4290         nframes64_t frame_rate;
4291         float secs;
4292
4293         if (session == 0) {
4294                 return;
4295         }
4296
4297         AudioClock::Mode m;
4298
4299         if (Profile->get_sae() || Profile->get_small_screen()) {
4300                 m = ARDOUR_UI::instance()->primary_clock.mode();
4301         } else {
4302                 m = ARDOUR_UI::instance()->secondary_clock.mode();
4303         }
4304
4305         switch (m) {
4306         case AudioClock::BBT:
4307                 session->bbt_time (frame, bbt);
4308                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4309                 break;
4310                 
4311         case AudioClock::SMPTE:
4312                 session->smpte_time (frame, smpte);
4313                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4314                 break;
4315
4316         case AudioClock::MinSec:
4317                 /* XXX this is copied from show_verbose_duration_cursor() */
4318                 frame_rate = session->frame_rate();
4319                 hours = frame / (frame_rate * 3600);
4320                 frame = frame % (frame_rate * 3600);
4321                 mins = frame / (frame_rate * 60);
4322                 frame = frame % (frame_rate * 60);
4323                 secs = (float) frame / (float) frame_rate;
4324                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4325                 break;
4326
4327         default:
4328                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4329                 break;
4330         }
4331
4332         if (xpos >= 0 && ypos >=0) {
4333                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4334         }
4335         else {
4336                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset - horizontal_adjustment.get_value(), drag_info.current_pointer_y + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
4337         }
4338         show_verbose_canvas_cursor ();
4339 }
4340
4341 void
4342 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
4343 {
4344         char buf[128];
4345         SMPTE::Time smpte;
4346         BBT_Time sbbt;
4347         BBT_Time ebbt;
4348         int hours, mins;
4349         nframes64_t distance, frame_rate;
4350         float secs;
4351         Meter meter_at_start(session->tempo_map().meter_at(start));
4352
4353         if (session == 0) {
4354                 return;
4355         }
4356
4357         AudioClock::Mode m;
4358
4359         if (Profile->get_sae() || Profile->get_small_screen()) {
4360                 m = ARDOUR_UI::instance()->primary_clock.mode ();
4361         } else {
4362                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4363         }
4364
4365         switch (m) {
4366         case AudioClock::BBT:
4367                 session->bbt_time (start, sbbt);
4368                 session->bbt_time (end, ebbt);
4369
4370                 /* subtract */
4371                 /* XXX this computation won't work well if the
4372                 user makes a selection that spans any meter changes.
4373                 */
4374
4375                 ebbt.bars -= sbbt.bars;
4376                 if (ebbt.beats >= sbbt.beats) {
4377                         ebbt.beats -= sbbt.beats;
4378                 } else {
4379                         ebbt.bars--;
4380                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4381                 }
4382                 if (ebbt.ticks >= sbbt.ticks) {
4383                         ebbt.ticks -= sbbt.ticks;
4384                 } else {
4385                         ebbt.beats--;
4386                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4387                 }
4388                 
4389                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4390                 break;
4391                 
4392         case AudioClock::SMPTE:
4393                 session->smpte_duration (end - start, smpte);
4394                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4395                 break;
4396
4397         case AudioClock::MinSec:
4398                 /* XXX this stuff should be elsewhere.. */
4399                 distance = end - start;
4400                 frame_rate = session->frame_rate();
4401                 hours = distance / (frame_rate * 3600);
4402                 distance = distance % (frame_rate * 3600);
4403                 mins = distance / (frame_rate * 60);
4404                 distance = distance % (frame_rate * 60);
4405                 secs = (float) distance / (float) frame_rate;
4406                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4407                 break;
4408
4409         default:
4410                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4411                 break;
4412         }
4413
4414         if (xpos >= 0 && ypos >=0) {
4415                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4416         }
4417         else {
4418                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4419         }
4420
4421         show_verbose_canvas_cursor ();
4422 }
4423
4424 void
4425 Editor::collect_new_region_view (RegionView* rv)
4426 {
4427         latest_regionviews.push_back (rv);
4428 }
4429
4430 void
4431 Editor::collect_and_select_new_region_view (RegionView* rv)
4432 {
4433         selection->add(rv);
4434         latest_regionviews.push_back (rv);
4435 }
4436
4437 void
4438 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4439 {
4440         if (clicked_regionview == 0) {
4441                 return;
4442         }
4443
4444         /* lets try to create new Region for the selection */
4445
4446         vector<boost::shared_ptr<Region> > new_regions;
4447         create_region_from_selection (new_regions);
4448
4449         if (new_regions.empty()) {
4450                 return;
4451         }
4452
4453         /* XXX fix me one day to use all new regions */
4454         
4455         boost::shared_ptr<Region> region (new_regions.front());
4456
4457         /* add it to the current stream/playlist.
4458
4459            tricky: the streamview for the track will add a new regionview. we will
4460            catch the signal it sends when it creates the regionview to
4461            set the regionview we want to then drag.
4462         */
4463         
4464         latest_regionviews.clear();
4465         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4466         
4467         /* A selection grab currently creates two undo/redo operations, one for 
4468            creating the new region and another for moving it.
4469         */
4470
4471         begin_reversible_command (_("selection grab"));
4472
4473         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4474
4475         XMLNode *before = &(playlist->get_state());
4476         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4477         XMLNode *after = &(playlist->get_state());
4478         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4479
4480         commit_reversible_command ();
4481         
4482         c.disconnect ();
4483         
4484         if (latest_regionviews.empty()) {
4485                 /* something went wrong */
4486                 return;
4487         }
4488
4489         /* we need to deselect all other regionviews, and select this one
4490            i'm ignoring undo stuff, because the region creation will take care of it 
4491         */
4492         selection->set (latest_regionviews);
4493         
4494         drag_info.item = latest_regionviews.front()->get_canvas_group();
4495         drag_info.data = latest_regionviews.front();
4496         drag_info.motion_callback = &Editor::region_drag_motion_callback;
4497         drag_info.finished_callback = &Editor::region_drag_finished_callback;
4498
4499         start_grab (event);
4500         
4501         drag_info.source_trackview = clicked_routeview;
4502         drag_info.dest_trackview = drag_info.source_trackview;
4503         drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4504         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4505         
4506         show_verbose_time_cursor (drag_info.last_frame_position, 10);
4507 }
4508
4509 void
4510 Editor::cancel_selection ()
4511 {
4512         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4513                 (*i)->hide_selection ();
4514         }
4515         selection->clear ();
4516         clicked_selection = 0;
4517 }       
4518
4519 void
4520 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4521 {
4522         nframes64_t start = 0;
4523         nframes64_t end = 0;
4524
4525         if (session == 0) {
4526                 return;
4527         }
4528
4529         drag_info.item = item;
4530         drag_info.motion_callback = &Editor::drag_selection;
4531         drag_info.finished_callback = &Editor::end_selection_op;
4532
4533         selection_op = op;
4534
4535         switch (op) {
4536         case CreateSelection:
4537                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4538                         drag_info.copy = true;
4539                 } else {
4540                         drag_info.copy = false;
4541                 }
4542                 start_grab (event, selector_cursor);
4543                 break;
4544
4545         case SelectionStartTrim:
4546                 if (clicked_axisview) {
4547                         clicked_axisview->order_selection_trims (item, true);
4548                 } 
4549                 start_grab (event, trimmer_cursor);
4550                 start = selection->time[clicked_selection].start;
4551                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4552                 break;
4553                 
4554         case SelectionEndTrim:
4555                 if (clicked_axisview) {
4556                         clicked_axisview->order_selection_trims (item, false);
4557                 }
4558                 start_grab (event, trimmer_cursor);
4559                 end = selection->time[clicked_selection].end;
4560                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
4561                 break;
4562
4563         case SelectionMove:
4564                 start = selection->time[clicked_selection].start;
4565                 start_grab (event);
4566                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4567                 break;
4568         }
4569
4570         if (selection_op == SelectionMove) {
4571                 show_verbose_time_cursor(start, 10);    
4572         } else {
4573                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4574         }
4575 }
4576
4577 void
4578 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4579 {
4580         nframes64_t start = 0;
4581         nframes64_t end = 0;
4582         nframes64_t length;
4583         nframes64_t pending_position;
4584
4585         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4586                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4587         } else {
4588                 pending_position = 0;
4589         }
4590         
4591         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4592                 snap_to (pending_position);
4593         }
4594
4595         /* only alter selection if the current frame is 
4596            different from the last frame position (adjusted)
4597          */
4598         
4599         if (pending_position == drag_info.last_pointer_frame) return;
4600         
4601         switch (selection_op) {
4602         case CreateSelection:
4603                 
4604                 if (drag_info.first_move) {
4605                         snap_to (drag_info.grab_frame);
4606                 }
4607                 
4608                 if (pending_position < drag_info.grab_frame) {
4609                         start = pending_position;
4610                         end = drag_info.grab_frame;
4611                 } else {
4612                         end = pending_position;
4613                         start = drag_info.grab_frame;
4614                 }
4615                 
4616                 /* first drag: Either add to the selection
4617                    or create a new selection->
4618                 */
4619                 
4620                 if (drag_info.first_move) {
4621                         
4622                         begin_reversible_command (_("range selection"));
4623                         
4624                         if (drag_info.copy) {
4625                                 /* adding to the selection */
4626                                 clicked_selection = selection->add (start, end);
4627                                 drag_info.copy = false;
4628                         } else {
4629                                 /* new selection-> */
4630                                 clicked_selection = selection->set (clicked_axisview, start, end);
4631                         }
4632                 } 
4633                 break;
4634                 
4635         case SelectionStartTrim:
4636                 
4637                 if (drag_info.first_move) {
4638                         begin_reversible_command (_("trim selection start"));
4639                 }
4640                 
4641                 start = selection->time[clicked_selection].start;
4642                 end = selection->time[clicked_selection].end;
4643
4644                 if (pending_position > end) {
4645                         start = end;
4646                 } else {
4647                         start = pending_position;
4648                 }
4649                 break;
4650                 
4651         case SelectionEndTrim:
4652                 
4653                 if (drag_info.first_move) {
4654                         begin_reversible_command (_("trim selection end"));
4655                 }
4656                 
4657                 start = selection->time[clicked_selection].start;
4658                 end = selection->time[clicked_selection].end;
4659
4660                 if (pending_position < start) {
4661                         end = start;
4662                 } else {
4663                         end = pending_position;
4664                 }
4665                 
4666                 break;
4667                 
4668         case SelectionMove:
4669                 
4670                 if (drag_info.first_move) {
4671                         begin_reversible_command (_("move selection"));
4672                 }
4673                 
4674                 start = selection->time[clicked_selection].start;
4675                 end = selection->time[clicked_selection].end;
4676                 
4677                 length = end - start;
4678                 
4679                 start = pending_position;
4680                 snap_to (start);
4681                 
4682                 end = start + length;
4683                 
4684                 break;
4685         }
4686         
4687         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4688                 start_canvas_autoscroll (1, 0);
4689         }
4690
4691         if (start != end) {
4692                 selection->replace (clicked_selection, start, end);
4693         }
4694
4695         drag_info.last_pointer_frame = pending_position;
4696         drag_info.first_move = false;
4697
4698         if (selection_op == SelectionMove) {
4699                 show_verbose_time_cursor(start, 10);    
4700         } else {
4701                 show_verbose_time_cursor(pending_position, 10); 
4702         }
4703 }
4704
4705 void
4706 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4707 {
4708         if (!drag_info.first_move) {
4709                 drag_selection (item, event);
4710                 /* XXX this is not object-oriented programming at all. ick */
4711                 if (selection->time.consolidate()) {
4712                         selection->TimeChanged ();
4713                 }
4714                 commit_reversible_command ();
4715         } else {
4716                 /* just a click, no pointer movement.*/
4717
4718                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4719
4720                         selection->clear_time();
4721
4722                 } 
4723         }
4724
4725         /* XXX what happens if its a music selection? */
4726         session->set_audio_range (selection->time);
4727         stop_canvas_autoscroll ();
4728 }
4729
4730 void
4731 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4732 {
4733         double speed = 1.0;
4734         TimeAxisView* tvp = clicked_axisview;
4735         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4736
4737         if (tv && tv->is_track()) {
4738                 speed = tv->get_diskstream()->speed();
4739         }
4740         
4741         nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4742         nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4743         nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4744
4745         //drag_info.item = clicked_regionview->get_name_highlight();
4746         drag_info.item = item;
4747         drag_info.motion_callback = &Editor::trim_motion_callback;
4748         drag_info.finished_callback = &Editor::trim_finished_callback;
4749
4750         start_grab (event, trimmer_cursor);
4751         
4752         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4753                 trim_op = ContentsTrim;
4754         } else {
4755                 /* These will get overridden for a point trim.*/
4756                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4757                         /* closer to start */
4758                         trim_op = StartTrim;
4759                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4760                         /* closer to end */
4761                         trim_op = EndTrim;
4762                 }
4763         }
4764
4765         switch (trim_op) {
4766         case StartTrim:
4767                 show_verbose_time_cursor(region_start, 10);     
4768                 break;
4769         case EndTrim:
4770                 show_verbose_time_cursor(region_end, 10);       
4771                 break;
4772         case ContentsTrim:
4773                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4774                 break;
4775         }
4776 }
4777
4778 void
4779 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4780 {
4781         RegionView* rv = clicked_regionview;
4782         nframes64_t frame_delta = 0;
4783         bool left_direction;
4784         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4785
4786         /* snap modifier works differently here..
4787            its' current state has to be passed to the 
4788            various trim functions in order to work properly 
4789         */ 
4790
4791         double speed = 1.0;
4792         TimeAxisView* tvp = clicked_axisview;
4793         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4794         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4795
4796         if (tv && tv->is_track()) {
4797                 speed = tv->get_diskstream()->speed();
4798         }
4799         
4800         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4801                 left_direction = true;
4802         } else {
4803                 left_direction = false;
4804         }
4805
4806         if (obey_snap) {
4807                 snap_to (drag_info.current_pointer_frame);
4808         }
4809
4810         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4811                 return;
4812         }
4813
4814         if (drag_info.first_move) {
4815         
4816                 string trim_type;
4817
4818                 switch (trim_op) {
4819                 case StartTrim:
4820                         trim_type = "Region start trim";
4821                         break;
4822                 case EndTrim:
4823                         trim_type = "Region end trim";
4824                         break;
4825                 case ContentsTrim:
4826                         trim_type = "Region content trim";
4827                         break;
4828                 }
4829
4830                 begin_reversible_command (trim_type);
4831
4832                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4833                         (*i)->fake_set_opaque(false);
4834                         (*i)->region()->freeze ();
4835                 
4836                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4837                         if (arv)
4838                                 arv->temporarily_hide_envelope ();
4839
4840                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4841                         insert_result = motion_frozen_playlists.insert (pl);
4842                         if (insert_result.second) {
4843                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4844                                 pl->freeze();
4845                         }
4846                 }
4847         }
4848
4849         if (left_direction) {
4850                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4851         } else {
4852                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4853         }
4854
4855         switch (trim_op) {              
4856         case StartTrim:
4857                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4858                         break;
4859                 } else {
4860                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4861                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4862                         }
4863                         break;
4864                 }
4865                 
4866         case EndTrim:
4867                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4868                         break;
4869                 } else {
4870                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4871                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4872                         }
4873                         break;
4874                 }
4875                 
4876         case ContentsTrim:
4877                 {
4878                         bool swap_direction = false;
4879
4880                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4881                                 swap_direction = true;
4882                         }
4883                         
4884                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4885                              i != selection->regions.by_layer().end(); ++i)
4886                         {
4887                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4888                         }
4889                 }
4890                 break;
4891         }
4892
4893         switch (trim_op) {
4894         case StartTrim:
4895                 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);   
4896                 break;
4897         case EndTrim:
4898                 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); 
4899                 break;
4900         case ContentsTrim:
4901                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4902                 break;
4903         }
4904
4905         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4906         drag_info.first_move = false;
4907 }
4908
4909 void
4910 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4911 {
4912         boost::shared_ptr<Region> region (rv.region());
4913
4914         if (region->locked()) {
4915                 return;
4916         }
4917
4918         nframes64_t new_bound;
4919
4920         double speed = 1.0;
4921         TimeAxisView* tvp = clicked_axisview;
4922         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4923
4924         if (tv && tv->is_track()) {
4925                 speed = tv->get_diskstream()->speed();
4926         }
4927         
4928         if (left_direction) {
4929                 if (swap_direction) {
4930                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4931                 } else {
4932                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4933                 }
4934         } else {
4935                 if (swap_direction) {
4936                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4937                 } else {
4938                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4939                 }
4940         }
4941
4942         if (obey_snap) {
4943                 snap_to (new_bound);
4944         }
4945         region->trim_start ((nframes64_t) (new_bound * speed), this);   
4946         rv.region_changed (StartChanged);
4947 }
4948
4949 void
4950 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4951 {
4952         boost::shared_ptr<Region> region (rv.region()); 
4953
4954         if (region->locked()) {
4955                 return;
4956         }
4957
4958         nframes64_t new_bound;
4959
4960         double speed = 1.0;
4961         TimeAxisView* tvp = clicked_axisview;
4962         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4963
4964         if (tv && tv->is_track()) {
4965                 speed = tv->get_diskstream()->speed();
4966         }
4967         
4968         if (left_direction) {
4969                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4970         } else {
4971                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4972         }
4973
4974         if (obey_snap) {
4975                 snap_to (new_bound, (left_direction ? 0 : 1));  
4976         }
4977
4978         region->trim_front ((nframes64_t) (new_bound * speed), this);
4979
4980         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4981 }
4982
4983 void
4984 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4985 {
4986         boost::shared_ptr<Region> region (rv.region());
4987
4988         if (region->locked()) {
4989                 return;
4990         }
4991
4992         nframes64_t new_bound;
4993
4994         double speed = 1.0;
4995         TimeAxisView* tvp = clicked_axisview;
4996         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4997
4998         if (tv && tv->is_track()) {
4999                 speed = tv->get_diskstream()->speed();
5000         }
5001         
5002         if (left_direction) {
5003                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5004         } else {
5005                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5006         }
5007
5008         if (obey_snap) {
5009                 snap_to (new_bound);
5010         }
5011         region->trim_end ((nframes64_t) (new_bound * speed), this);
5012         rv.region_changed (LengthChanged);
5013 }
5014         
5015 void
5016 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5017 {
5018         if (!drag_info.first_move) {
5019                 trim_motion_callback (item, event);
5020                 
5021                 if (!selection->selected (clicked_regionview)) {
5022                         thaw_region_after_trim (*clicked_regionview);           
5023                 } else {
5024                         
5025                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5026                              i != selection->regions.by_layer().end(); ++i)
5027                         {
5028                                 thaw_region_after_trim (**i);
5029                                 (*i)->fake_set_opaque (true);
5030                         }
5031                 }
5032                 
5033                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5034                         (*p)->thaw ();
5035                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5036                 }
5037                 
5038                 motion_frozen_playlists.clear ();
5039
5040                 commit_reversible_command();
5041         } else {
5042                 /* no mouse movement */
5043                 point_trim (event);
5044         }
5045 }
5046
5047 void
5048 Editor::point_trim (GdkEvent* event)
5049 {
5050         RegionView* rv = clicked_regionview;
5051         nframes64_t new_bound = drag_info.current_pointer_frame;
5052
5053         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5054                 snap_to (new_bound);
5055         }
5056
5057         /* Choose action dependant on which button was pressed */
5058         switch (event->button.button) {
5059         case 1:
5060                 trim_op = StartTrim;
5061                 begin_reversible_command (_("Start point trim"));
5062
5063                 if (selection->selected (rv)) {
5064
5065                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5066                              i != selection->regions.by_layer().end(); ++i)
5067                         {
5068                                 if (!(*i)->region()->locked()) {
5069                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5070                                         XMLNode &before = pl->get_state();
5071                                         (*i)->region()->trim_front (new_bound, this);   
5072                                         XMLNode &after = pl->get_state();
5073                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5074                                 }
5075                         }
5076
5077                 } else {
5078
5079                         if (!rv->region()->locked()) {
5080                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5081                                 XMLNode &before = pl->get_state();
5082                                 rv->region()->trim_front (new_bound, this);     
5083                                 XMLNode &after = pl->get_state();
5084                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5085                         }
5086                 }
5087
5088                 commit_reversible_command();
5089         
5090                 break;
5091         case 2:
5092                 trim_op = EndTrim;
5093                 begin_reversible_command (_("End point trim"));
5094
5095                 if (selection->selected (rv)) {
5096                         
5097                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5098                         {
5099                                 if (!(*i)->region()->locked()) {
5100                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5101                                         XMLNode &before = pl->get_state();
5102                                         (*i)->region()->trim_end (new_bound, this);
5103                                         XMLNode &after = pl->get_state();
5104                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5105                                 }
5106                         }
5107
5108                 } else {
5109
5110                         if (!rv->region()->locked()) {
5111                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5112                                 XMLNode &before = pl->get_state();
5113                                 rv->region()->trim_end (new_bound, this);
5114                                 XMLNode &after = pl->get_state();
5115                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5116                         }
5117                 }
5118
5119                 commit_reversible_command();
5120         
5121                 break;
5122         default:
5123                 break;
5124         }
5125 }
5126
5127 void
5128 Editor::thaw_region_after_trim (RegionView& rv)
5129 {
5130         boost::shared_ptr<Region> region (rv.region());
5131
5132         if (region->locked()) {
5133                 return;
5134         }
5135
5136         region->thaw (_("trimmed region"));
5137         XMLNode &after = region->playlist()->get_state();
5138         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5139
5140         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5141         if (arv)
5142                 arv->unhide_envelope ();
5143 }
5144
5145 void
5146 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5147 {
5148         Marker* marker;
5149         bool is_start;
5150
5151         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5152                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5153                 /*NOTREACHED*/
5154         }
5155
5156         Location* location = find_location_from_marker (marker, is_start);      
5157         location->set_hidden (true, this);
5158 }
5159
5160
5161 void
5162 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5163 {
5164         if (session == 0) {
5165                 return;
5166         }
5167
5168         drag_info.item = item;
5169         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5170         drag_info.finished_callback = &Editor::end_range_markerbar_op;
5171
5172         range_marker_op = op;
5173
5174         if (!temp_location) {
5175                 temp_location = new Location;
5176         }
5177         
5178         switch (op) {
5179         case CreateRangeMarker:
5180         case CreateTransportMarker:
5181         case CreateCDMarker:
5182         
5183                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5184                         drag_info.copy = true;
5185                 } else {
5186                         drag_info.copy = false;
5187                 }
5188                 start_grab (event, selector_cursor);
5189                 break;
5190         }
5191
5192         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5193         
5194 }
5195
5196 void
5197 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5198 {
5199         nframes64_t start = 0;
5200         nframes64_t end = 0;
5201         ArdourCanvas::SimpleRect *crect;
5202
5203         switch (range_marker_op) {
5204         case CreateRangeMarker:
5205                 crect = range_bar_drag_rect;
5206                 break;
5207         case CreateTransportMarker:
5208                 crect = transport_bar_drag_rect;
5209                 break;
5210         case CreateCDMarker:
5211                 crect = cd_marker_bar_drag_rect;
5212                 break;
5213         default:
5214                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5215                 return;
5216                 break;
5217         }
5218         
5219         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5220                 snap_to (drag_info.current_pointer_frame);
5221         }
5222
5223         /* only alter selection if the current frame is 
5224            different from the last frame position.
5225          */
5226         
5227         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5228         
5229         switch (range_marker_op) {
5230         case CreateRangeMarker:
5231         case CreateTransportMarker:
5232         case CreateCDMarker:
5233                 if (drag_info.first_move) {
5234                         snap_to (drag_info.grab_frame);
5235                 }
5236                 
5237                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5238                         start = drag_info.current_pointer_frame;
5239                         end = drag_info.grab_frame;
5240                 } else {
5241                         end = drag_info.current_pointer_frame;
5242                         start = drag_info.grab_frame;
5243                 }
5244                 
5245                 /* first drag: Either add to the selection
5246                    or create a new selection.
5247                 */
5248                 
5249                 if (drag_info.first_move) {
5250                         
5251                         temp_location->set (start, end);
5252                         
5253                         crect->show ();
5254
5255                         update_marker_drag_item (temp_location);
5256                         range_marker_drag_rect->show();
5257                         //range_marker_drag_rect->raise_to_top();
5258                         
5259                 } 
5260                 break;          
5261         }
5262         
5263         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5264                 start_canvas_autoscroll (1, 0);
5265         }
5266         
5267         if (start != end) {
5268                 temp_location->set (start, end);
5269
5270                 double x1 = frame_to_pixel (start);
5271                 double x2 = frame_to_pixel (end);
5272                 crect->property_x1() = x1;
5273                 crect->property_x2() = x2;
5274
5275                 update_marker_drag_item (temp_location);
5276         }
5277
5278         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5279         drag_info.first_move = false;
5280
5281         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5282         
5283 }
5284
5285 void
5286 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5287 {
5288         Location * newloc = 0;
5289         string rangename;
5290         int flags;
5291         
5292         if (!drag_info.first_move) {
5293                 drag_range_markerbar_op (item, event);
5294
5295                 switch (range_marker_op) {
5296                 case CreateRangeMarker:
5297                 case CreateCDMarker:
5298                     {
5299                         begin_reversible_command (_("new range marker"));
5300                         XMLNode &before = session->locations()->get_state();
5301                         session->locations()->next_available_name(rangename,"unnamed");
5302                         if (range_marker_op == CreateCDMarker) {
5303                                 flags =  Location::IsRangeMarker|Location::IsCDMarker;
5304                                 cd_marker_bar_drag_rect->hide();
5305                         }
5306                         else {
5307                                 flags =  Location::IsRangeMarker;
5308                                 range_bar_drag_rect->hide();
5309                         }
5310                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5311                         session->locations()->add (newloc, true);
5312                         XMLNode &after = session->locations()->get_state();
5313                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5314                         commit_reversible_command ();
5315                         
5316                         range_marker_drag_rect->hide();
5317                         break;
5318                     }
5319
5320                 case CreateTransportMarker:
5321                         // popup menu to pick loop or punch
5322                         new_transport_marker_context_menu (&event->button, item);
5323                         
5324                         break;
5325                 }
5326         } else {
5327                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5328
5329                 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5330
5331                         nframes64_t start;
5332                         nframes64_t end;
5333
5334                         start = session->locations()->first_mark_before (drag_info.grab_frame);
5335                         end = session->locations()->first_mark_after (drag_info.grab_frame);
5336                         
5337                         if (end == max_frames) {
5338                                 end = session->current_end_frame ();
5339                         }
5340
5341                         if (start == 0) {
5342                                 start = session->current_start_frame ();
5343                         }
5344
5345                         switch (mouse_mode) {
5346                         case MouseObject:
5347                                 /* find the two markers on either side and then make the selection from it */
5348                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5349                                 break;
5350
5351                         case MouseRange:
5352                                 /* find the two markers on either side of the click and make the range out of it */
5353                                 selection->set (0, start, end);
5354                                 break;
5355
5356                         default:
5357                                 break;
5358                         }
5359                 } 
5360         }
5361
5362         stop_canvas_autoscroll ();
5363 }
5364
5365
5366
5367 void
5368 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5369 {
5370         drag_info.item = item;
5371         drag_info.motion_callback = &Editor::drag_mouse_zoom;
5372         drag_info.finished_callback = &Editor::end_mouse_zoom;
5373
5374         start_grab (event, zoom_cursor);
5375
5376         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5377 }
5378
5379 void
5380 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5381 {
5382         nframes64_t start;
5383         nframes64_t end;
5384
5385         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5386                 snap_to (drag_info.current_pointer_frame);
5387                 
5388                 if (drag_info.first_move) {
5389                         snap_to (drag_info.grab_frame);
5390                 }
5391         }
5392                 
5393         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5394
5395         /* base start and end on initial click position */
5396         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5397                 start = drag_info.current_pointer_frame;
5398                 end = drag_info.grab_frame;
5399         } else {
5400                 end = drag_info.current_pointer_frame;
5401                 start = drag_info.grab_frame;
5402         }
5403         
5404         if (start != end) {
5405
5406                 if (drag_info.first_move) {
5407                         zoom_rect->show();
5408                         zoom_rect->raise_to_top();
5409                 }
5410
5411                 reposition_zoom_rect(start, end);
5412
5413                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5414                 drag_info.first_move = false;
5415
5416                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5417         }
5418 }
5419
5420 void
5421 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5422 {
5423         if (!drag_info.first_move) {
5424                 drag_mouse_zoom (item, event);
5425                 
5426                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5427                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5428                 } else {
5429                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5430                 }               
5431         } else {
5432                 temporal_zoom_to_frame (false, drag_info.grab_frame);
5433                 /*
5434                 temporal_zoom_step (false);
5435                 center_screen (drag_info.grab_frame);
5436                 */
5437         }
5438
5439         zoom_rect->hide();
5440 }
5441
5442 void
5443 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5444 {
5445         double x1 = frame_to_pixel (start);
5446         double x2 = frame_to_pixel (end);
5447         double y2 = full_canvas_height - 1.0;
5448
5449         zoom_rect->property_x1() = x1;
5450         zoom_rect->property_y1() = 1.0;
5451         zoom_rect->property_x2() = x2;
5452         zoom_rect->property_y2() = y2;
5453 }
5454
5455 void
5456 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5457 {
5458         drag_info.item = item;
5459         drag_info.motion_callback = &Editor::drag_rubberband_select;
5460         drag_info.finished_callback = &Editor::end_rubberband_select;
5461
5462         start_grab (event, cross_hair_cursor);
5463
5464         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5465 }
5466
5467 void
5468 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5469 {
5470         nframes64_t start;
5471         nframes64_t end;
5472         double y1;
5473         double y2;
5474
5475         /* use a bigger drag threshold than the default */
5476
5477         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5478                 return;
5479         }
5480
5481         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5482                 if (drag_info.first_move) {
5483                         snap_to (drag_info.grab_frame);
5484                 } 
5485                 snap_to (drag_info.current_pointer_frame);
5486         }
5487
5488         /* base start and end on initial click position */
5489
5490         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5491                 start = drag_info.current_pointer_frame;
5492                 end = drag_info.grab_frame;
5493         } else {
5494                 end = drag_info.current_pointer_frame;
5495                 start = drag_info.grab_frame;
5496         }
5497
5498         if (drag_info.current_pointer_y < drag_info.grab_y) {
5499                 y1 = drag_info.current_pointer_y;
5500                 y2 = drag_info.grab_y;
5501         } else {
5502                 y2 = drag_info.current_pointer_y;
5503                 y1 = drag_info.grab_y;
5504         }
5505
5506         
5507         if (start != end || y1 != y2) {
5508
5509                 double x1 = frame_to_pixel (start);
5510                 double x2 = frame_to_pixel (end);
5511                 
5512                 rubberband_rect->property_x1() = x1;
5513                 rubberband_rect->property_y1() = y1;
5514                 rubberband_rect->property_x2() = x2;
5515                 rubberband_rect->property_y2() = y2;
5516
5517                 rubberband_rect->show();
5518                 rubberband_rect->raise_to_top();
5519                 
5520                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5521                 drag_info.first_move = false;
5522
5523                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5524         }
5525 }
5526
5527 void
5528 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5529 {
5530         if (!drag_info.first_move) {
5531
5532                 drag_rubberband_select (item, event);
5533
5534                 double y1,y2;
5535                 if (drag_info.current_pointer_y < drag_info.grab_y) {
5536                         y1 = drag_info.current_pointer_y;
5537                         y2 = drag_info.grab_y;
5538                 } else {
5539                         y2 = drag_info.current_pointer_y;
5540                         y1 = drag_info.grab_y;
5541                 }
5542
5543
5544                 Selection::Operation op = Keyboard::selection_type (event->button.state);
5545                 bool commit;
5546
5547                 begin_reversible_command (_("rubberband selection"));
5548
5549                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5550                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5551                 } else {
5552                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5553                 }               
5554
5555                 if (commit) {
5556                         commit_reversible_command ();
5557                 }
5558                 
5559         } else {
5560                 selection->clear_tracks();
5561                 selection->clear_regions();
5562                 selection->clear_points ();
5563                 selection->clear_lines ();
5564         }
5565
5566         rubberband_rect->hide();
5567 }
5568
5569
5570 gint
5571 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5572 {
5573         using namespace Gtkmm2ext;
5574
5575         ArdourPrompter prompter (false);
5576
5577         prompter.set_prompt (_("Name for region:"));
5578         prompter.set_initial_text (clicked_regionview->region()->name());
5579         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5580         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5581         prompter.show_all ();
5582         switch (prompter.run ()) {
5583         case Gtk::RESPONSE_ACCEPT:
5584                 string str;
5585                 prompter.get_result(str);
5586                 if (str.length()) {
5587                         clicked_regionview->region()->set_name (str);
5588                 }
5589                 break;
5590         }
5591         return true;
5592 }
5593
5594 void
5595 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5596 {
5597         drag_info.item = item;
5598         drag_info.motion_callback = &Editor::time_fx_motion;
5599         drag_info.finished_callback = &Editor::end_time_fx;
5600
5601         start_grab (event);
5602
5603         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5604 }
5605
5606 void
5607 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5608 {
5609         RegionView* rv = clicked_regionview;
5610
5611         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5612                 snap_to (drag_info.current_pointer_frame);
5613         }
5614
5615         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5616                 return;
5617         }
5618
5619         if (drag_info.current_pointer_frame > rv->region()->position()) {
5620                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5621         }
5622
5623         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5624         drag_info.first_move = false;
5625
5626         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5627 }
5628
5629 void
5630 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5631 {
5632         clicked_regionview->get_time_axis_view().hide_timestretch ();
5633
5634         if (drag_info.first_move) {
5635                 return;
5636         }
5637
5638         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5639                 /* backwards drag of the left edge - not usable */
5640                 return;
5641         }
5642         
5643         nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5644
5645         float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5646         
5647 #ifndef USE_RUBBERBAND
5648         // Soundtouch uses percentage / 100 instead of normal (/ 1) 
5649         if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5650                 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5651         }
5652 #endif  
5653         
5654         begin_reversible_command (_("timestretch"));
5655         
5656         // XXX how do timeFX on multiple regions ?
5657         
5658         RegionSelection rs;
5659         rs.add (clicked_regionview);
5660
5661         if (time_stretch (rs, percentage) == 0) {
5662                 session->commit_reversible_command ();
5663         }
5664 }
5665
5666 void
5667 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5668 {
5669         /* no brushing without a useful snap setting */
5670
5671         switch (snap_mode) {
5672         case SnapMagnetic:
5673                 return; /* can't work because it allows region to be placed anywhere */
5674         default:
5675                 break; /* OK */
5676         }
5677
5678         switch (snap_type) {
5679         case SnapToMark:
5680                 return;
5681
5682         default:
5683                 break;
5684         }
5685
5686         /* don't brush a copy over the original */
5687         
5688         if (pos == rv->region()->position()) {
5689                 return;
5690         }
5691
5692         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5693
5694         if (rtv == 0 || !rtv->is_track()) {
5695                 return;
5696         }
5697
5698         boost::shared_ptr<Playlist> playlist = rtv->playlist();
5699         double speed = rtv->get_diskstream()->speed();
5700         
5701         XMLNode &before = playlist->get_state();
5702         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5703         XMLNode &after = playlist->get_state();
5704         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5705         
5706         // playlist is frozen, so we have to update manually
5707         
5708         playlist->Modified(); /* EMIT SIGNAL */
5709 }
5710
5711 gint
5712 Editor::track_height_step_timeout ()
5713 {
5714         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5715                 current_stepping_trackview = 0;
5716                 return false;
5717         }
5718         return true;
5719 }
5720