Relayer punch/loop range rects, fix display of punch-in with no punch-out, ensure...
[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 += 20.0;
1325                         at_y += 20.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         UpdateAllTransportClocks (cursor->current_frame);
2280
2281         show_verbose_time_cursor (cursor->current_frame, 10);
2282
2283         drag_info.last_pointer_frame = adjusted_frame;
2284         drag_info.first_move = false;
2285 }
2286
2287 void
2288 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2289 {
2290         if (drag_info.first_move) return;
2291         
2292         cursor_drag_motion_callback (item, event);
2293
2294         _dragging_playhead = false;
2295         
2296         if (item == &playhead_cursor->canvas_item) {
2297                 if (session) {
2298                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2299                 }
2300         } 
2301 }
2302
2303 void
2304 Editor::update_marker_drag_item (Location *location)
2305 {
2306         double x1 = frame_to_pixel (location->start());
2307         double x2 = frame_to_pixel (location->end());
2308
2309         if (location->is_mark()) {
2310                 marker_drag_line_points.front().set_x(x1);
2311                 marker_drag_line_points.back().set_x(x1);
2312                 marker_drag_line->property_points() = marker_drag_line_points;
2313         }
2314         else {
2315                 range_marker_drag_rect->property_x1() = x1;
2316                 range_marker_drag_rect->property_x2() = x2;
2317         }
2318 }
2319
2320
2321 void
2322 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2323 {
2324         Marker* marker;
2325
2326         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2327                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2328                 /*NOTREACHED*/
2329         }
2330
2331         bool is_start;
2332
2333         Location  *location = find_location_from_marker (marker, is_start);
2334
2335         drag_info.item = item;
2336         drag_info.data = marker;
2337         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2338         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2339
2340         start_grab (event);
2341
2342         _dragging_edit_point = true;
2343
2344         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
2345
2346         update_marker_drag_item (location);
2347
2348         if (location->is_mark()) {
2349                 // marker_drag_line->show();
2350                 // marker_drag_line->raise_to_top();
2351         } else {
2352                 range_marker_drag_rect->show();
2353                 //range_marker_drag_rect->raise_to_top();
2354         }
2355
2356         if (is_start) {
2357                 show_verbose_time_cursor (location->start(), 10);
2358         } else {
2359                 show_verbose_time_cursor (location->end(), 10);
2360         }
2361
2362         Selection::Operation op = Keyboard::selection_type (event->button.state);
2363
2364         switch (op) {
2365         case Selection::Toggle:
2366                 selection->toggle (marker);
2367                 break;
2368         case Selection::Set:
2369                 if (!selection->selected (marker)) {
2370                         selection->set (marker);
2371                 }
2372                 break;
2373         case Selection::Extend:
2374         {
2375                 Locations::LocationList ll;
2376                 list<Marker*> to_add;
2377                 nframes64_t s, e;
2378                 selection->markers.range (s, e);
2379                 s = min (marker->position(), s);
2380                 e = max (marker->position(), e);
2381                 s = min (s, e);
2382                 e = max (s, e);
2383                 if (e < max_frames) {
2384                         ++e;
2385                 }
2386                 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2387                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2388                         LocationMarkers* lm = find_location_markers (*i);
2389                         if (lm) {
2390                                 if (lm->start) {
2391                                         to_add.push_back (lm->start);
2392                                 }
2393                                 if (lm->end) {
2394                                         to_add.push_back (lm->end);
2395                                 }
2396                         }
2397                 }
2398                 if (!to_add.empty()) {
2399                         selection->add (to_add);
2400                 }
2401                 break;
2402         }
2403         case Selection::Add:
2404                 selection->add (marker);
2405                 break;
2406         }
2407
2408         /* set up copies for us to manipulate during the drag */
2409
2410         drag_info.clear_copied_locations ();
2411
2412         for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2413                 Location  *l = find_location_from_marker (*i, is_start);
2414                 drag_info.copied_locations.push_back (new Location (*l));
2415         }
2416 }
2417
2418 void
2419 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2420 {
2421         nframes64_t f_delta;    
2422         nframes64_t newframe;
2423         bool is_start;
2424         bool move_both = false;
2425         Marker* dragged_marker = (Marker*) drag_info.data;
2426         Marker* marker;
2427         Location  *real_location;
2428         Location  *copy_location;
2429
2430         if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2431                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2432         } else {
2433                 newframe = 0;
2434         }
2435
2436         nframes64_t next = newframe;
2437
2438         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2439                 snap_to (newframe, 0, true);
2440         }
2441         
2442         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { 
2443                 return;
2444         }
2445
2446         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2447                 move_both = true;
2448         }
2449
2450         MarkerSelection::iterator i;
2451         list<Location*>::iterator x;
2452
2453         /* find the marker we're dragging, and compute the delta */
2454
2455         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2456              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2457              ++i, ++x) {
2458
2459                 copy_location = *x;
2460                 marker = *i;
2461
2462                 if (marker == dragged_marker) {
2463
2464                         if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2465                                 /* que pasa ?? */
2466                                 return;
2467                         }
2468
2469                         if (real_location->is_mark()) {
2470                                 f_delta = newframe - copy_location->start();
2471                         } else {
2472
2473
2474                                 switch (marker->type()) {
2475                                 case Marker::Start:
2476                                 case Marker::LoopStart:
2477                                 case Marker::PunchIn:
2478                                         f_delta = newframe - copy_location->start();
2479                                         break;
2480
2481                                 case Marker::End:
2482                                 case Marker::LoopEnd:
2483                                 case Marker::PunchOut:
2484                                         f_delta = newframe - copy_location->end();
2485                                         break;
2486                                 default:
2487                                         /* what kind of marker is this ? */
2488                                         return;
2489                                 }
2490                         }
2491                         break;
2492                 }
2493         }
2494
2495         if (i == selection->markers.end()) {
2496                 /* hmm, impossible - we didn't find the dragged marker */
2497                 return;
2498         }
2499
2500         /* now move them all */
2501
2502         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2503              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2504              ++i, ++x) {
2505
2506                 copy_location = *x;
2507                 marker = *i;
2508
2509                 /* call this to find out if its the start or end */
2510                 
2511                 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2512                         continue;
2513                 }
2514                 
2515                 if (real_location->locked()) {
2516                         continue;
2517                 }
2518
2519                 if (copy_location->is_mark()) {
2520
2521                         /* just move it */
2522                         
2523                         copy_location->set_start (copy_location->start() + f_delta);
2524
2525                 } else {
2526                         
2527                         nframes64_t new_start = copy_location->start() + f_delta;
2528                         nframes64_t new_end = copy_location->end() + f_delta;
2529                         
2530                         if (is_start) { // start-of-range marker
2531                                 
2532                                 if (move_both) {
2533                                         copy_location->set_start (new_start);
2534                                         copy_location->set_end (new_end);
2535                                 } else  if (new_start < copy_location->end()) {
2536                                         copy_location->set_start (new_start);
2537                                 } else { 
2538                                         snap_to (next, 1, true);
2539                                         copy_location->set_end (next);
2540                                         copy_location->set_start (newframe);
2541                                 }
2542                                 
2543                         } else { // end marker
2544                                 
2545                                 if (move_both) {
2546                                         copy_location->set_end (new_end);
2547                                         copy_location->set_start (new_start);
2548                                 } else if (new_end > copy_location->start()) {
2549                                         copy_location->set_end (new_end);
2550                                 } else if (newframe > 0) {
2551                                         snap_to (next, -1, true);
2552                                         copy_location->set_start (next);
2553                                         copy_location->set_end (newframe);
2554                                 }
2555                         }
2556                 }
2557                 update_marker_drag_item (copy_location);
2558
2559                 LocationMarkers* lm = find_location_markers (real_location);
2560
2561                 if (lm) {
2562                         lm->set_position (copy_location->start(), copy_location->end());
2563                 }
2564         }
2565
2566         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2567         drag_info.first_move = false;
2568
2569         if (drag_info.copied_locations.empty()) {
2570                 abort();
2571         }
2572
2573         edit_point_clock.set (drag_info.copied_locations.front()->start());
2574         show_verbose_time_cursor (newframe, 10);
2575
2576 #ifdef GTKOSX
2577         track_canvas->update_now ();
2578 #endif
2579 }
2580
2581 void
2582 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2583 {
2584         if (drag_info.first_move) {
2585
2586                 /* just a click, do nothing but finish
2587                    off the selection process
2588                 */
2589
2590                 Selection::Operation op = Keyboard::selection_type (event->button.state);
2591                 Marker* marker = (Marker *) drag_info.data;
2592
2593                 switch (op) {
2594                 case Selection::Set:
2595                         if (selection->selected (marker) && selection->markers.size() > 1) {
2596                                 selection->set (marker);
2597                         }
2598                         break;
2599
2600                 case Selection::Toggle:
2601                 case Selection::Extend:
2602                 case Selection::Add:
2603                         break;
2604                 }
2605                 
2606                 return;
2607         }
2608
2609         _dragging_edit_point = false;
2610         
2611
2612         begin_reversible_command ( _("move marker") );
2613         XMLNode &before = session->locations()->get_state();
2614
2615         MarkerSelection::iterator i;
2616         list<Location*>::iterator x;
2617         bool is_start;
2618
2619         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2620              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2621              ++i, ++x) {
2622         
2623                 Location * location = find_location_from_marker ((*i), is_start);
2624                 
2625                 if (location) {
2626                         
2627                         if (location->locked()) {
2628                                 return;
2629                         }
2630                         
2631                         if (location->is_mark()) {
2632                                 location->set_start ((*x)->start());
2633                         } else {
2634                                 location->set ((*x)->start(), (*x)->end());
2635                         }
2636                 }
2637         }
2638
2639         XMLNode &after = session->locations()->get_state();
2640         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2641         commit_reversible_command ();
2642         
2643         marker_drag_line->hide();
2644         range_marker_drag_rect->hide();
2645 }
2646
2647 void
2648 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2649 {
2650         Marker* marker;
2651         MeterMarker* meter_marker;
2652
2653         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2654                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2655                 /*NOTREACHED*/
2656         }
2657
2658         meter_marker = dynamic_cast<MeterMarker*> (marker);
2659
2660         MetricSection& section (meter_marker->meter());
2661
2662         if (!section.movable()) {
2663                 return;
2664         }
2665
2666         drag_info.item = item;
2667         drag_info.copy = false;
2668         drag_info.data = marker;
2669         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2670         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2671
2672         start_grab (event);
2673
2674         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2675
2676         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2677 }
2678
2679 void
2680 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2681 {
2682         Marker* marker;
2683         MeterMarker* meter_marker;
2684
2685         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2686                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2687                 /*NOTREACHED*/
2688         }
2689
2690         meter_marker = dynamic_cast<MeterMarker*> (marker);
2691         
2692         // create a dummy marker for visual representation of moving the copy.
2693         // The actual copying is not done before we reach the finish callback.
2694         char name[64];
2695         snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2696         MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, 
2697                                                   *new MeterSection(meter_marker->meter()));
2698
2699         drag_info.item = &new_marker->the_item();
2700         drag_info.copy = true;
2701         drag_info.data = new_marker;
2702         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2703         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2704
2705         start_grab (event);
2706
2707         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2708
2709         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2710 }
2711
2712 void
2713 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2714 {
2715         MeterMarker* marker = (MeterMarker *) drag_info.data;
2716         nframes64_t adjusted_frame;
2717
2718         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2719                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2720         }
2721         else {
2722                 adjusted_frame = 0;
2723         }
2724         
2725         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2726                 snap_to (adjusted_frame);
2727         }
2728         
2729         if (adjusted_frame == drag_info.last_pointer_frame) return;
2730
2731         marker->set_position (adjusted_frame);
2732         
2733         
2734         drag_info.last_pointer_frame = adjusted_frame;
2735         drag_info.first_move = false;
2736
2737         show_verbose_time_cursor (adjusted_frame, 10);
2738 }
2739
2740 void
2741 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2742 {
2743         if (drag_info.first_move) return;
2744
2745         meter_marker_drag_motion_callback (drag_info.item, event);
2746         
2747         MeterMarker* marker = (MeterMarker *) drag_info.data;
2748         BBT_Time when;
2749         
2750         TempoMap& map (session->tempo_map());
2751         map.bbt_time (drag_info.last_pointer_frame, when);
2752         
2753         if (drag_info.copy == true) {
2754                 begin_reversible_command (_("copy meter mark"));
2755                 XMLNode &before = map.get_state();
2756                 map.add_meter (marker->meter(), when);
2757                 XMLNode &after = map.get_state();
2758                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2759                 commit_reversible_command ();
2760
2761                 // delete the dummy marker we used for visual representation of copying.
2762                 // a new visual marker will show up automatically.
2763                 delete marker;
2764         } else {
2765                 begin_reversible_command (_("move meter mark"));
2766                 XMLNode &before = map.get_state();
2767                 map.move_meter (marker->meter(), when);
2768                 XMLNode &after = map.get_state();
2769                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2770                 commit_reversible_command ();
2771         }
2772 }
2773
2774 void
2775 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2776 {
2777         Marker* marker;
2778         TempoMarker* tempo_marker;
2779
2780         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2781                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2782                 /*NOTREACHED*/
2783         }
2784
2785         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2786                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2787                 /*NOTREACHED*/
2788         }
2789
2790         MetricSection& section (tempo_marker->tempo());
2791
2792         if (!section.movable()) {
2793                 return;
2794         }
2795
2796         drag_info.item = item;
2797         drag_info.copy = false;
2798         drag_info.data = marker;
2799         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2800         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2801
2802         start_grab (event);
2803
2804         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2805         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2806 }
2807
2808 void
2809 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2810 {
2811         Marker* marker;
2812         TempoMarker* tempo_marker;
2813
2814         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2815                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2816                 /*NOTREACHED*/
2817         }
2818
2819         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2820                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2821                 /*NOTREACHED*/
2822         }
2823
2824         // create a dummy marker for visual representation of moving the copy.
2825         // The actual copying is not done before we reach the finish callback.
2826         char name[64];
2827         snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2828         TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, 
2829                                                   *new TempoSection(tempo_marker->tempo()));
2830
2831         drag_info.item = &new_marker->the_item();
2832         drag_info.copy = true;
2833         drag_info.data = new_marker;
2834         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2835         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2836
2837         start_grab (event);
2838
2839         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2840
2841         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2842 }
2843
2844 void
2845 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2846 {
2847         TempoMarker* marker = (TempoMarker *) drag_info.data;
2848         nframes64_t adjusted_frame;
2849         
2850         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2851                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2852         }
2853         else {
2854                 adjusted_frame = 0;
2855         }
2856
2857         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2858                 snap_to (adjusted_frame);
2859         }
2860         
2861         if (adjusted_frame == drag_info.last_pointer_frame) return;
2862
2863         /* OK, we've moved far enough to make it worth actually move the thing. */
2864                 
2865         marker->set_position (adjusted_frame);
2866         
2867         show_verbose_time_cursor (adjusted_frame, 10);
2868
2869         drag_info.last_pointer_frame = adjusted_frame;
2870         drag_info.first_move = false;
2871 }
2872
2873 void
2874 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2875 {
2876         if (drag_info.first_move) return;
2877         
2878         tempo_marker_drag_motion_callback (drag_info.item, event);
2879         
2880         TempoMarker* marker = (TempoMarker *) drag_info.data;
2881         BBT_Time when;
2882         
2883         TempoMap& map (session->tempo_map());
2884         map.bbt_time (drag_info.last_pointer_frame, when);
2885
2886         if (drag_info.copy == true) {
2887                 begin_reversible_command (_("copy tempo mark"));
2888                 XMLNode &before = map.get_state();
2889                 map.add_tempo (marker->tempo(), when);
2890                 XMLNode &after = map.get_state();
2891                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2892                 commit_reversible_command ();
2893                 
2894                 // delete the dummy marker we used for visual representation of copying.
2895                 // a new visual marker will show up automatically.
2896                 delete marker;
2897         } else {
2898                 begin_reversible_command (_("move tempo mark"));
2899                 XMLNode &before = map.get_state();
2900                 map.move_tempo (marker->tempo(), when);
2901                 XMLNode &after = map.get_state();
2902                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2903                 commit_reversible_command ();
2904         }
2905 }
2906
2907 void
2908 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2909 {
2910         ControlPoint* control_point;
2911
2912         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2913                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2914                 /*NOTREACHED*/
2915         }
2916
2917         // We shouldn't remove the first or last gain point
2918         if (control_point->line().is_last_point(*control_point) ||
2919                 control_point->line().is_first_point(*control_point)) { 
2920                 return;
2921         }
2922
2923         control_point->line().remove_point (*control_point);
2924 }
2925
2926 void
2927 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
2928 {
2929         ControlPoint* control_point;
2930
2931         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2932                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2933                 /*NOTREACHED*/
2934         }
2935
2936         control_point->line().remove_point (*control_point);
2937 }
2938
2939 void
2940 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2941 {
2942         ControlPoint* control_point;
2943
2944         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2945                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2946                 /*NOTREACHED*/
2947         }
2948
2949         drag_info.item = item;
2950         drag_info.data = control_point;
2951         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2952         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2953
2954         start_grab (event, fader_cursor);
2955
2956         // start the grab at the center of the control point so
2957         // the point doesn't 'jump' to the mouse after the first drag
2958         drag_info.grab_x = control_point->get_x();
2959         drag_info.grab_y = control_point->get_y();
2960
2961         control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
2962         track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
2963
2964         drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
2965
2966         control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
2967
2968         float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
2969         set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction), 
2970                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2971
2972         show_verbose_canvas_cursor ();
2973 }
2974
2975 void
2976 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2977 {
2978         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2979
2980         double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
2981         double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
2982
2983         if (event->button.state & Keyboard::SecondaryModifier) {
2984                 dx *= 0.1;
2985                 dy *= 0.1;
2986         }
2987
2988         double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
2989         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
2990
2991         // calculate zero crossing point. back off by .01 to stay on the
2992         // positive side of zero
2993         double _unused = 0;
2994         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
2995         cp->line().parent_group().i2w(_unused, zero_gain_y);
2996
2997         // make sure we hit zero when passing through
2998         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2999                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3000                 cy = zero_gain_y;
3001         }
3002
3003         if (drag_info.x_constrained) {
3004                 cx = drag_info.grab_x;
3005         }
3006         if (drag_info.y_constrained) {
3007                 cy = drag_info.grab_y;
3008         }
3009
3010         drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3011         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3012
3013         cp->line().parent_group().w2i (cx, cy);
3014
3015         cx = max (0.0, cx);
3016         cy = max (0.0, cy);
3017         cy = min ((double) cp->line().height(), cy);
3018
3019         //translate cx to frames
3020         nframes64_t cx_frames = unit_to_frame (cx);
3021
3022         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3023                 snap_to (cx_frames);
3024         }
3025
3026         float fraction = 1.0 - (cy / cp->line().height());
3027
3028         bool push;
3029
3030         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3031                 push = true;
3032         } else {
3033                 push = false;
3034         }
3035
3036         cp->line().point_drag (*cp, cx_frames , fraction, push);
3037         
3038         set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3039
3040         drag_info.first_move = false;
3041 }
3042
3043 void
3044 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3045 {
3046         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3047
3048         if (drag_info.first_move) {
3049
3050                 /* just a click */
3051                 
3052                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3053                         reset_point_selection ();
3054                 }
3055
3056         } else {
3057                 control_point_drag_motion_callback (item, event);
3058         }
3059         cp->line().end_drag (cp);
3060 }
3061
3062 void
3063 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3064 {
3065         switch (mouse_mode) {
3066         case MouseGain:
3067                 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3068                 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3069                 break;
3070         default:
3071                 break;
3072         }
3073 }
3074
3075 void
3076 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3077 {
3078         AutomationLine* al;
3079         
3080         if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3081                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3082                 /*NOTREACHED*/
3083         }
3084
3085         start_line_grab (al, event);
3086 }
3087
3088 void
3089 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3090 {
3091         double cx;
3092         double cy;
3093         nframes64_t frame_within_region;
3094
3095         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3096            origin.
3097         */
3098
3099         cx = event->button.x;
3100         cy = event->button.y;
3101         line->parent_group().w2i (cx, cy);
3102         frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3103
3104         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
3105                                             current_line_drag_info.after)) {
3106                 /* no adjacent points */
3107                 return;
3108         }
3109
3110         drag_info.item = &line->grab_item();
3111         drag_info.data = line;
3112         drag_info.motion_callback = &Editor::line_drag_motion_callback;
3113         drag_info.finished_callback = &Editor::line_drag_finished_callback;
3114
3115         start_grab (event, fader_cursor);
3116
3117         double fraction = 1.0 - (cy / line->height());
3118
3119         line->start_drag (0, drag_info.grab_frame, fraction);
3120         
3121         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3122                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
3123         show_verbose_canvas_cursor ();
3124 }
3125
3126 void
3127 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3128 {
3129         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3130
3131         double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3132
3133         if (event->button.state & Keyboard::SecondaryModifier) {
3134                 dy *= 0.1;
3135         }
3136
3137         double cx = drag_info.current_pointer_x;
3138         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3139
3140         // calculate zero crossing point. back off by .01 to stay on the
3141         // positive side of zero
3142         double _unused = 0;
3143         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3144         line->parent_group().i2w(_unused, zero_gain_y);
3145
3146         // make sure we hit zero when passing through
3147         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3148                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3149                 cy = zero_gain_y;
3150         }
3151
3152         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3153
3154         line->parent_group().w2i (cx, cy);
3155
3156         cy = max (0.0, cy);
3157         cy = min ((double) line->height(), cy);
3158
3159         double fraction = 1.0 - (cy / line->height());
3160
3161         bool push;
3162
3163         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3164                 push = false;
3165         } else {
3166                 push = true;
3167         }
3168
3169         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3170         
3171         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3172 }
3173
3174 void
3175 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3176 {
3177         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3178         line_drag_motion_callback (item, event);
3179         line->end_drag (0);
3180 }
3181
3182 void
3183 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3184 {
3185         if (selection->regions.empty() || clicked_regionview == 0) {
3186                 return;
3187         }
3188         _region_motion_group->raise_to_top ();
3189         drag_info.copy = false;
3190         drag_info.item = item;
3191         drag_info.data = clicked_regionview;
3192
3193         if (Config->get_edit_mode() == Splice) {
3194                 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3195                 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3196         } else {
3197                 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3198                 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3199         }
3200
3201         start_grab (event);
3202
3203         double speed = 1.0;
3204         TimeAxisView* tvp = clicked_axisview;
3205         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3206
3207         if (tv && tv->is_track()) {
3208                 speed = tv->get_diskstream()->speed();
3209         }
3210         
3211         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3212         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3213         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3214         drag_info.dest_trackview = drag_info.source_trackview;
3215         // we want a move threshold
3216         drag_info.want_move_threshold = true;
3217         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3218
3219         begin_reversible_command (_("move region(s)"));
3220
3221         /* sync the canvas to what we think is its current state */
3222         track_canvas->update_now();
3223 }
3224
3225 void
3226 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3227 {
3228         drag_info.copy = false;
3229         drag_info.item = item;
3230         drag_info.data = clicked_axisview;
3231         drag_info.source_trackview = clicked_axisview;
3232         drag_info.dest_trackview = drag_info.source_trackview;
3233         drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3234         drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3235
3236         start_grab (event);
3237 }
3238
3239 void
3240 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3241 {
3242         if (selection->regions.empty() || clicked_regionview == 0) {
3243                 return;
3244         }
3245         _region_motion_group->raise_to_top ();
3246         drag_info.copy = true;
3247         drag_info.item = item;
3248         drag_info.data = clicked_regionview;    
3249
3250         start_grab(event);
3251
3252         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3253         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3254         double speed = 1.0;
3255
3256         if (rtv && rtv->is_track()) {
3257                 speed = rtv->get_diskstream()->speed();
3258         }
3259         
3260         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3261         drag_info.dest_trackview = drag_info.source_trackview;
3262         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3263         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3264         // we want a move threshold
3265         drag_info.want_move_threshold = true;
3266         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3267         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3268         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3269 }
3270
3271 void
3272 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3273 {
3274         if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3275                 return;
3276         }
3277
3278         drag_info.copy = false;
3279         drag_info.item = item;
3280         drag_info.data = clicked_regionview;
3281         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3282         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3283
3284         start_grab (event);
3285
3286         double speed = 1.0;
3287         TimeAxisView* tvp = clicked_axisview;
3288         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3289
3290         if (tv && tv->is_track()) {
3291                 speed = tv->get_diskstream()->speed();
3292         }
3293         
3294         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3295         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3296         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3297         drag_info.dest_trackview = drag_info.source_trackview;
3298         // we want a move threshold
3299         drag_info.want_move_threshold = true;
3300         drag_info.brushing = true;
3301         
3302         begin_reversible_command (_("Drag region brush"));
3303 }
3304
3305 void
3306 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3307 {
3308         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3309
3310                 drag_info.want_move_threshold = false; // don't copy again
3311
3312                 /* duplicate the regionview(s) and region(s) */
3313
3314                 vector<RegionView*> new_regionviews;
3315                 
3316                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3317                         RegionView* rv;
3318                         RegionView* nrv;
3319
3320                         rv = (*i);
3321                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3322                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3323                         
3324                         const boost::shared_ptr<const Region> original = rv->region();
3325                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3326
3327                         if (arv) {
3328                                 boost::shared_ptr<AudioRegion> audioregion_copy
3329                                         = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3330                                 nrv = new AudioRegionView (*arv, audioregion_copy);
3331                         } else if (mrv) {
3332                                 boost::shared_ptr<MidiRegion> midiregion_copy
3333                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3334                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
3335                         } else {
3336                                 continue;
3337                         }
3338
3339                         nrv->get_canvas_group()->show ();
3340                         new_regionviews.push_back (nrv);
3341                 }
3342
3343                 if (new_regionviews.empty()) {
3344                         return;
3345                 }
3346
3347                 /* reset selection to new regionviews. This will not set selection visual status for 
3348                    these regionviews since they don't belong to a track, so do that by hand too.
3349                  */
3350
3351                 selection->set (new_regionviews);
3352
3353                 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3354                         (*i)->set_selected (true);
3355                 }
3356
3357                 /* reset drag_info data to reflect the fact that we are dragging the copies */
3358                 
3359                 drag_info.data = new_regionviews.front();
3360
3361                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3362                 /* 
3363                    sync the canvas to what we think is its current state 
3364                    without it, the canvas seems to 
3365                    "forget" to update properly after the upcoming reparent() 
3366                    ..only if the mouse is in rapid motion at the time of the grab. 
3367                    something to do with regionview creation raking so long?
3368                  */
3369                 track_canvas->update_now();
3370         }
3371 }
3372
3373 bool
3374 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3375 {
3376         /* Which trackview is this ? */
3377
3378         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3379         (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3380
3381         /* The region motion is only processed if the pointer is over
3382            an audio track.
3383         */
3384         
3385         if (!(*tv) || !(*tv)->is_track()) {
3386                 /* To make sure we hide the verbose canvas cursor when the mouse is 
3387                    not held over and audiotrack. 
3388                 */
3389                 hide_verbose_canvas_cursor ();
3390                 return false;
3391         }
3392
3393         return true;
3394 }
3395
3396 struct RegionSelectionByPosition {
3397     bool operator() (RegionView*a, RegionView* b) {
3398             return a->region()->position () < b->region()->position();
3399     }
3400 };
3401
3402 void
3403 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3404 {
3405         RouteTimeAxisView* tv;
3406         
3407         if (!check_region_drag_possible (&tv)) {
3408                 return;
3409         }
3410
3411         if (!drag_info.move_threshold_passed) {
3412                 return;
3413         }
3414
3415         int dir;
3416
3417         if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3418                 dir = 1;
3419         } else {
3420                 dir = -1;
3421         }
3422
3423         RegionSelection copy (selection->regions);
3424
3425         RegionSelectionByPosition cmp;
3426         copy.sort (cmp);
3427
3428         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3429
3430                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3431
3432                 if (!atv) {
3433                         continue;
3434                 }
3435
3436                 boost::shared_ptr<Playlist> playlist;
3437
3438                 if ((playlist = atv->playlist()) == 0) {
3439                         continue;
3440                 }
3441
3442                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3443                         continue;
3444                 } 
3445
3446                 if (dir > 0) {
3447                         if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3448                                 continue;
3449                         }
3450                 } else {
3451                         if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3452                                 continue;
3453                         }
3454                 }
3455
3456                 
3457                 playlist->shuffle ((*i)->region(), dir);
3458
3459                 drag_info.grab_x = drag_info.current_pointer_x;
3460         }
3461 }
3462
3463 void
3464 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3465 {
3466 }
3467
3468 void
3469 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3470 {
3471         double x_delta;
3472         double y_delta = 0;
3473         RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data); 
3474         nframes64_t pending_region_position = 0;
3475         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3476         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
3477         bool clamp_y_axis = false;
3478         vector<int32_t>  height_list(512) ;
3479         vector<int32_t>::iterator j;
3480         RouteTimeAxisView* tv;
3481
3482         possibly_copy_regions_during_grab (event);
3483
3484         if (!check_region_drag_possible (&tv)) {
3485                 return;
3486         }
3487
3488         original_pointer_order = drag_info.dest_trackview->order;
3489         
3490         /************************************************************
3491              Y-Delta Computation
3492         ************************************************************/   
3493
3494         if (drag_info.brushing) {
3495                 clamp_y_axis = true;
3496                 pointer_y_span = 0;
3497                 goto y_axis_done;
3498         }
3499
3500         if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3501
3502                 int32_t children = 0, numtracks = 0;
3503                 // XXX hard coding track limit, oh my, so very very bad
3504                 bitset <1024> tracks (0x00);
3505                 /* get a bitmask representing the visible tracks */
3506
3507                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3508                         TimeAxisView *tracklist_timeview;
3509                         tracklist_timeview = (*i);
3510                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3511                         TimeAxisView::Children children_list;
3512               
3513                         /* zeroes are audio tracks. ones are other types. */
3514               
3515                         if (!rtv2->hidden()) {
3516                                 
3517                                 if (visible_y_high < rtv2->order) {
3518                                         visible_y_high = rtv2->order;
3519                                 }
3520                                 if (visible_y_low > rtv2->order) {
3521                                         visible_y_low = rtv2->order;
3522                                 }
3523                 
3524                                 if (!rtv2->is_track()) {                                  
3525                                         tracks = tracks |= (0x01 << rtv2->order);
3526                                 }
3527         
3528                                 height_list[rtv2->order] = (*i)->current_height();
3529                                 children = 1;
3530
3531                                 if ((children_list = rtv2->get_child_list()).size() > 0) {
3532                                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
3533                                                 tracks = tracks |= (0x01 << (rtv2->order + children));
3534                                                 height_list[rtv2->order + children] =  (*j)->current_height();
3535                                                 numtracks++;
3536                                                 children++;     
3537                                         }
3538                                 }
3539                                 numtracks++;        
3540                         }
3541                 }
3542                 /* find the actual span according to the canvas */
3543
3544                 canvas_pointer_y_span = pointer_y_span;
3545                 if (drag_info.dest_trackview->order >= tv->order) {
3546                         int32_t y;
3547                         for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3548                                 if (height_list[y] == 0 ) {
3549                                         canvas_pointer_y_span--;
3550                                 }
3551                         }
3552                 } else {
3553                         int32_t y;
3554                         for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3555                                 if (    height_list[y] == 0 ) {
3556                                         canvas_pointer_y_span++;
3557                                 }
3558                         }
3559                 }
3560
3561                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3562                         RegionView* rv2 = (*i);
3563                         double ix1, ix2, iy1, iy2;
3564                         int32_t n = 0;
3565
3566                         if (rv2->region()->locked()) {
3567                                 continue;
3568                         }
3569
3570                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3571                         rv2->get_canvas_frame()->i2w (ix1, iy1);
3572                         iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3573
3574                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3575                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3576
3577                         if (rtv2->order != original_pointer_order) {    
3578                                 /* this isn't the pointer track */      
3579
3580                                 if (canvas_pointer_y_span > 0) {
3581
3582                                         /* moving up the canvas */
3583                                         if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3584         
3585                                                 int32_t visible_tracks = 0;
3586                                                 while (visible_tracks < canvas_pointer_y_span ) {
3587                                                         visible_tracks++;
3588                   
3589                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3590                                                                 /* we're passing through a hidden track */
3591                                                                 n--;
3592                                                         }                 
3593                                                 }
3594                  
3595                                                 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
3596                                                         clamp_y_axis = true;
3597                                                 }
3598                     
3599                                         } else {
3600                                                 clamp_y_axis = true;
3601                                         }                 
3602                   
3603                                 } else if (canvas_pointer_y_span < 0) {
3604
3605                                         /*moving down the canvas*/
3606
3607                                         if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3608                     
3609                     
3610                                                 int32_t visible_tracks = 0;
3611                     
3612                                                 while (visible_tracks > canvas_pointer_y_span ) {
3613                                                         visible_tracks--;
3614                       
3615                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
3616                                                                 n++;
3617                                                         }                
3618                                                 }
3619                                                 if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3620                                                         clamp_y_axis = true;
3621                             
3622                                                 }
3623                                         } else {
3624                           
3625                                                 clamp_y_axis = true;
3626                                         }
3627                                 }               
3628                   
3629                         } else {
3630                       
3631                                 /* this is the pointer's track */
3632                                 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
3633                                         clamp_y_axis = true;
3634                                 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3635                                         clamp_y_axis = true;
3636                                 }
3637                         }             
3638                         if (clamp_y_axis) {
3639                                 break;
3640                         }
3641                 }
3642
3643         } else  if (drag_info.dest_trackview == tv) {
3644                 clamp_y_axis = true;
3645         }         
3646
3647   y_axis_done:
3648         if (!clamp_y_axis) {
3649                 drag_info.dest_trackview = tv;        
3650         }
3651           
3652         /************************************************************
3653             X DELTA COMPUTATION
3654         ************************************************************/
3655
3656         /* compute the amount of pointer motion in frames, and where
3657            the region would be if we moved it by that much.
3658         */
3659         if ( drag_info.move_threshold_passed ) {
3660
3661                 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3662
3663                         nframes64_t sync_frame;
3664                         nframes64_t sync_offset;
3665                         int32_t sync_dir;
3666
3667                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3668
3669                         sync_offset = rv->region()->sync_offset (sync_dir);
3670
3671                         /* we don't handle a sync point that lies before zero.
3672                          */
3673                         if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3674                                 sync_frame = pending_region_position + (sync_dir*sync_offset);
3675
3676                                 /* we snap if the snap modifier is not enabled.
3677                                  */
3678             
3679                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3680                                         snap_to (sync_frame);   
3681                                 }
3682             
3683                                 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3684
3685                         } else {
3686                                 pending_region_position = drag_info.last_frame_position;
3687                         }
3688             
3689                 } else {
3690                         pending_region_position = 0;
3691                 }
3692           
3693                 if (pending_region_position > max_frames - rv->region()->length()) {
3694                         pending_region_position = drag_info.last_frame_position;
3695                 }
3696
3697                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3698
3699                 bool x_move_allowed;
3700                 
3701                 if (Config->get_edit_mode() == Lock) {
3702                         if (drag_info.copy) {
3703                                 x_move_allowed = !drag_info.x_constrained;
3704                         } else {
3705                                 /* in locked edit mode, reverse the usual meaning of x_constrained */
3706                                 x_move_allowed = drag_info.x_constrained;
3707                         }
3708                 } else {
3709                         x_move_allowed = !drag_info.x_constrained;
3710                 }
3711
3712                 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3713
3714                         /* now compute the canvas unit distance we need to move the regionview
3715                            to make it appear at the new location.
3716                         */
3717
3718                         if (pending_region_position > drag_info.last_frame_position) {
3719                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3720                         } else {
3721                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3722                                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3723
3724                                         RegionView* rv2 = (*i);
3725
3726                                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3727         
3728                                         double ix1, ix2, iy1, iy2;
3729                                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3730                                         rv2->get_canvas_frame()->i2w (ix1, iy1);
3731                         
3732                                         if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3733                                                 //      do_move = false;
3734                                                 x_delta = 0;
3735                                                 pending_region_position = drag_info.last_frame_position;
3736                                                 break;
3737                                         }
3738                                 }
3739
3740                         }
3741                 
3742                         drag_info.last_frame_position = pending_region_position;
3743
3744                 } else {
3745                         x_delta = 0;
3746                 }
3747
3748         } else {
3749                 /* threshold not passed */
3750
3751                 x_delta = 0;
3752         }
3753         
3754         /*************************************************************
3755             PREPARE TO MOVE
3756         ************************************************************/
3757
3758         if (x_delta == 0 && (pointer_y_span == 0)) {
3759                 /* haven't reached next snap point, and we're not switching
3760                    trackviews. nothing to do.
3761                 */
3762                 return;
3763         }
3764
3765         /*************************************************************
3766             MOTION                                                                    
3767         ************************************************************/
3768         bool do_move = true;
3769         if (drag_info.first_move) {
3770                 if (!drag_info.move_threshold_passed) {
3771                         do_move = false;
3772                 }
3773         }
3774
3775         if (do_move) {
3776
3777                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3778                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3779                 
3780                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3781             
3782                         RegionView* rv = (*i);
3783                         double ix1, ix2, iy1, iy2;
3784                         int32_t temp_pointer_y_span = pointer_y_span;
3785
3786                         if (rv->region()->locked()) {
3787                                 continue;
3788                         }
3789
3790                         /* get item BBox, which will be relative to parent. so we have
3791                            to query on a child, then convert to world coordinates using
3792                            the parent.
3793                         */
3794
3795                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3796                         rv->get_canvas_frame()->i2w (ix1, iy1);
3797                         
3798                         cerr << "adjust y from " << iy1 << " using "
3799                              << vertical_adjustment.get_value() << " - "
3800                              << canvas_timebars_vsize
3801                              << endl;
3802
3803                         iy1 += get_trackview_group_vertical_offset ();;
3804
3805                         if (drag_info.first_move) {
3806
3807                                 // hide any dependent views 
3808         
3809                                 rv->get_time_axis_view().hide_dependent_views (*rv);
3810
3811                                 /* 
3812                                    reparent to a non scrolling group so that we can keep the 
3813                                    region selection above all time axis views.
3814                                    reparenting means we have to move the rv as the two 
3815                                    parent groups have different coordinates.
3816                                 */
3817
3818                                 rv->get_canvas_group()->property_y() =  iy1 - 1;
3819                                 rv->get_canvas_group()->reparent(*_region_motion_group);
3820
3821                                 rv->fake_set_opaque (true);
3822                         }
3823
3824                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3825                         RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3826                         RouteTimeAxisView* temp_rtv;
3827
3828                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3829                                 y_delta = 0;
3830                                 int32_t x = 0;
3831                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3832                                         if (x == canvas_rtv->order) {
3833                                                 /* we found the track the region is on */
3834                                                 if (x != original_pointer_order) {
3835                                                         /*this isn't from the same track we're dragging from */
3836                                                         temp_pointer_y_span = canvas_pointer_y_span;
3837                                                 }                 
3838                                                 while (temp_pointer_y_span > 0) {
3839                                                         /* we're moving up canvas-wise,
3840                                                            so  we need to find the next track height
3841                                                         */
3842                                                         if (j != height_list.begin()) {           
3843                                                                 j--;
3844                                                         }
3845                                                         if (x != original_pointer_order) { 
3846                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3847                                                                 if ((*j) == 0) {
3848                                                                         temp_pointer_y_span++;
3849                                                                 }
3850                                                         }          
3851                                                         y_delta -= (*j);        
3852                                                         temp_pointer_y_span--;  
3853                                                 }
3854
3855                                                 while (temp_pointer_y_span < 0) {                 
3856                                                         y_delta += (*j);
3857                                                         if (x != original_pointer_order) { 
3858                                                                 if ((*j) == 0) {
3859                                                                         temp_pointer_y_span--;
3860                                                                 }
3861                                                         }          
3862                     
3863                                                         if (j != height_list.end()) {                 
3864                                                                 j++;
3865                                                         }
3866                                                         temp_pointer_y_span++;
3867                                                 }
3868                                                 /* find out where we'll be when we move and set height accordingly */
3869                   
3870                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3871                                                 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3872                                                 rv->set_height (temp_rtv->current_height());
3873
3874                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3875                                                      personally, i think this can confuse things, but never mind.
3876                                                 */
3877                                   
3878                                                 //const GdkColor& col (temp_rtv->view->get_region_color());
3879                                                 //rv->set_color (const_cast<GdkColor&>(col));
3880                                                 break;          
3881                                         }
3882                                         x++;
3883                                 }
3884                         }
3885
3886                         /* prevent the regionview from being moved to before 
3887                            the zero position on the canvas.
3888                         */
3889                         /* clamp */
3890                 
3891                         if (x_delta < 0) {
3892                                 if (-x_delta > ix1) {
3893                                         x_delta = -ix1;
3894                                 }
3895                         } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3896                                 x_delta = max_frames - rv->region()->last_frame();
3897                         }
3898
3899                         if (drag_info.brushing) {
3900                                 mouse_brush_insert_region (rv, pending_region_position);
3901                         } else {
3902                                 rv->move (x_delta, y_delta);
3903                         }
3904
3905                 } /* foreach region */
3906
3907         } /* if do_move */
3908
3909         if (drag_info.first_move && drag_info.move_threshold_passed) {
3910                 cursor_group->raise_to_top();
3911                 drag_info.first_move = false;
3912         }
3913
3914         if (x_delta != 0 && !drag_info.brushing) {
3915                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3916         }
3917
3918
3919 void
3920 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3921 {
3922         bool nocommit = true;
3923         vector<RegionView*> copies;
3924         RouteTimeAxisView* source_tv;
3925         boost::shared_ptr<Diskstream> ds;
3926         boost::shared_ptr<Playlist> from_playlist;
3927         vector<RegionView*> new_selection;
3928         typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3929         PlaylistSet modified_playlists;
3930         PlaylistSet frozen_playlists;
3931         list <sigc::connection> modified_playlist_connections;
3932         pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3933
3934         /* first_move is set to false if the regionview has been moved in the 
3935            motion handler. 
3936         */
3937
3938         if (drag_info.first_move) {
3939                 /* just a click */
3940                 goto out;
3941         }
3942
3943         nocommit = false;
3944
3945         if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3946                 selection->set (pre_drag_region_selection);
3947                 pre_drag_region_selection.clear ();
3948         }
3949
3950         if (drag_info.brushing) {
3951                 /* all changes were made during motion event handlers */
3952                 
3953                 if (drag_info.copy) {
3954                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3955                                 copies.push_back (*i);
3956                         }
3957                 }
3958
3959                 goto out;
3960         }
3961
3962         char* op_string;
3963
3964         /* reverse this here so that we have the correct logic to finalize
3965            the drag.
3966         */
3967         
3968         if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3969                 drag_info.x_constrained = !drag_info.x_constrained;
3970         }
3971
3972         if (drag_info.copy) {
3973                 if (drag_info.x_constrained) {
3974                         op_string = _("fixed time region copy");
3975                 } else {
3976                         op_string = _("region copy");
3977                 } 
3978         } else {
3979                 if (drag_info.x_constrained) {
3980                         op_string = _("fixed time region drag");
3981                 } else {
3982                         op_string = _("region drag");
3983                 }
3984         }
3985
3986         begin_reversible_command (op_string);
3987
3988         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3989                         
3990                 RegionView* rv = (*i);              
3991                 double ix1, ix2, iy1, iy2;
3992                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3993                 rv->get_canvas_frame()->i2w (ix1, iy1);
3994                 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3995
3996                 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
3997                 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
3998                 double speed;
3999                 bool changed_tracks, changed_position;
4000                 nframes64_t where;
4001
4002                 if (rv->region()->locked()) {
4003                         ++i;
4004                         continue;
4005                 }
4006
4007                 /* adjust for track speed */
4008
4009                 speed = 1.0;
4010                 
4011                 if (dest_rtv && dest_rtv->get_diskstream()) {
4012                         speed = dest_rtv->get_diskstream()->speed();
4013                 }
4014                 
4015                 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
4016                 changed_tracks = (dest_tv != &rv->get_time_axis_view());
4017
4018                 if (changed_position && !drag_info.x_constrained) {
4019                         _master_group->w2i(ix1, iy1);
4020                         where = (nframes64_t) (unit_to_frame (ix1) * speed);
4021                 } else {
4022                         where = rv->region()->position();
4023                 }
4024                         
4025                 boost::shared_ptr<Region> new_region;
4026
4027
4028                 if (drag_info.copy) {
4029                         /* we already made a copy */
4030                         new_region = rv->region();
4031
4032                         /* undo the previous hide_dependent_views so that xfades don't
4033                            disappear on copying regions 
4034                         */
4035                 
4036                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
4037                 
4038                 } else if (changed_tracks && dest_rtv->playlist()) {
4039                         new_region = RegionFactory::create (rv->region());
4040                 }
4041
4042                 if (changed_tracks || drag_info.copy) {
4043
4044                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4045                         if (!to_playlist) {
4046                                 ++i;
4047                                 continue;
4048                         }
4049
4050                         latest_regionviews.clear ();
4051
4052                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4053                         
4054                         insert_result = modified_playlists.insert (to_playlist);
4055                         if (insert_result.second) {
4056                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4057                         }
4058
4059                         to_playlist->add_region (new_region, where);
4060
4061                         c.disconnect ();
4062                                                               
4063                         if (!latest_regionviews.empty()) {
4064                                 // XXX why just the first one ? we only expect one
4065                                 // commented out in nick_m's canvas reworking. is that intended?
4066                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4067                                 new_selection.push_back (latest_regionviews.front());
4068                         }
4069
4070                 } else {
4071                         /* 
4072                            motion on the same track. plonk the previously reparented region 
4073                            back to its original canvas group (its streamview).
4074                            No need to do anything for copies as they are fake regions which will be deleted.
4075                         */
4076
4077                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4078                         rv->get_canvas_group()->property_y() = 0;
4079                   
4080                         /* just change the model */
4081                         
4082                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4083
4084                         insert_result = modified_playlists.insert (playlist);
4085                         if (insert_result.second) {
4086                                 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4087                         }
4088                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
4089                         frozen_insert_result = frozen_playlists.insert(playlist);
4090                         if (frozen_insert_result.second) {
4091                                 playlist->freeze();
4092                         }
4093
4094                         rv->region()->set_position (where, (void*) this);
4095                 }
4096
4097                 if (changed_tracks && !drag_info.copy) {
4098
4099                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
4100                            because we may have copied the region and it has not been attached to a playlist.
4101                         */
4102
4103                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4104                         assert ((ds = source_tv->get_diskstream()));
4105                         assert ((from_playlist = ds->playlist()));
4106
4107                         /* moved to a different audio track, without copying */
4108
4109                         /* the region that used to be in the old playlist is not
4110                            moved to the new one - we use a copy of it. as a result,
4111                            any existing editor for the region should no longer be
4112                            visible.
4113                         */ 
4114             
4115                         rv->hide_region_editor();
4116                         rv->fake_set_opaque (false);
4117                         
4118                         /* remove the region from the old playlist */
4119
4120                         insert_result = modified_playlists.insert (from_playlist);
4121                         if (insert_result.second) {
4122                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4123                         }
4124
4125                         from_playlist->remove_region ((rv->region()));
4126                         
4127                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4128                            was selected in all of them, then removing it from a playlist will have removed all
4129                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
4130                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4131                            corresponding regionview, and the selection is now empty).
4132
4133                            this could have invalidated any and all iterators into the region selection.
4134
4135                            the heuristic we use here is: if the region selection is empty, break out of the loop
4136                            here. if the region selection is not empty, then restart the loop because we know that
4137                            we must have removed at least the region(view) we've just been working on as well as any
4138                            that we processed on previous iterations.
4139
4140                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4141                            we can just iterate.
4142                         */
4143
4144                         if (selection->regions.empty()) {
4145                                 break;
4146                         } else { 
4147                                 i = selection->regions.by_layer().begin();
4148                         }
4149
4150                 } else {
4151                         ++i;
4152                 }
4153                 
4154                 if (drag_info.copy) {
4155                         copies.push_back (rv);
4156                 }
4157         }
4158         
4159         if (new_selection.empty()) {
4160                 if (drag_info.copy) {
4161                         /* the region(view)s that are selected and being dragged around
4162                            are copies and do not belong to any track. remove them
4163                            from the selection right here.
4164                         */
4165                         selection->clear_regions();
4166                 }
4167         } else {
4168                 /* this will clear any existing selection that would have been
4169                    cleared in the other clause above
4170                 */
4171                 selection->set (new_selection);
4172         }
4173
4174         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4175                 (*p)->thaw();
4176         }
4177                         
4178   out:
4179         if (!nocommit) {
4180                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4181                         session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));      
4182                 }
4183                 commit_reversible_command ();
4184         }
4185
4186         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4187                 delete *x;
4188         }
4189
4190 }
4191         
4192 void
4193 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4194 {
4195         if (drag_info.move_threshold_passed) {
4196                 if (drag_info.first_move) {
4197                         // TODO: create region-create-drag region view here
4198                         drag_info.first_move = false;
4199                 }
4200
4201                 // TODO: resize region-create-drag region view here
4202         }
4203
4204
4205 void
4206 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4207 {
4208         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4209         if (!mtv)
4210                 return;
4211
4212         const boost::shared_ptr<MidiDiskstream> diskstream =
4213                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4214         
4215         if (!diskstream) {
4216                 warning << "Cannot create non-MIDI region" << endl;
4217                 return;
4218         }
4219
4220         if (drag_info.first_move) {
4221                 begin_reversible_command (_("create region"));
4222                 XMLNode &before = mtv->playlist()->get_state();
4223
4224                 nframes64_t start = drag_info.grab_frame;
4225                 snap_to (start, -1);
4226                 const Meter& m = session->tempo_map().meter_at(start);
4227                 const Tempo& t = session->tempo_map().tempo_at(start);
4228                 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4229
4230                 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4231                                 
4232                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4233                                              (RegionFactory::create(src, 0, (nframes_t) length, 
4234                                                                     PBD::basename_nosuffix(src->name()))), start);
4235                 XMLNode &after = mtv->playlist()->get_state();
4236                 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4237                 commit_reversible_command();
4238
4239         } else {
4240                 create_region_drag_motion_callback (item, event);
4241                 // TODO: create region-create-drag region here
4242         }
4243 }
4244
4245 void
4246 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4247 {
4248         /* Either add to or set the set the region selection, unless
4249            this is an alignment click (control used)
4250         */
4251         
4252         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4253                 TimeAxisView* tv = &rv.get_time_axis_view();
4254                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4255                 double speed = 1.0;
4256                 if (rtv && rtv->is_track()) {
4257                         speed = rtv->get_diskstream()->speed();
4258                 }
4259
4260                 nframes64_t where = get_preferred_edit_position();
4261
4262                 if (where >= 0) {
4263
4264                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4265                                 
4266                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4267                                 
4268                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4269                                 
4270                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
4271                                 
4272                         } else {
4273                                 
4274                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4275                         }
4276                 }
4277         }
4278 }
4279
4280 void
4281 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
4282 {
4283         char buf[128];
4284         SMPTE::Time smpte;
4285         BBT_Time bbt;
4286         int hours, mins;
4287         nframes64_t frame_rate;
4288         float secs;
4289
4290         if (session == 0) {
4291                 return;
4292         }
4293
4294         AudioClock::Mode m;
4295
4296         if (Profile->get_sae() || Profile->get_small_screen()) {
4297                 m = ARDOUR_UI::instance()->primary_clock.mode();
4298         } else {
4299                 m = ARDOUR_UI::instance()->secondary_clock.mode();
4300         }
4301
4302         switch (m) {
4303         case AudioClock::BBT:
4304                 session->bbt_time (frame, bbt);
4305                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4306                 break;
4307                 
4308         case AudioClock::SMPTE:
4309                 session->smpte_time (frame, smpte);
4310                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4311                 break;
4312
4313         case AudioClock::MinSec:
4314                 /* XXX this is copied from show_verbose_duration_cursor() */
4315                 frame_rate = session->frame_rate();
4316                 hours = frame / (frame_rate * 3600);
4317                 frame = frame % (frame_rate * 3600);
4318                 mins = frame / (frame_rate * 60);
4319                 frame = frame % (frame_rate * 60);
4320                 secs = (float) frame / (float) frame_rate;
4321                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4322                 break;
4323
4324         default:
4325                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4326                 break;
4327         }
4328
4329         if (xpos >= 0 && ypos >=0) {
4330                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4331         }
4332         else {
4333                 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);
4334         }
4335         show_verbose_canvas_cursor ();
4336 }
4337
4338 void
4339 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
4340 {
4341         char buf[128];
4342         SMPTE::Time smpte;
4343         BBT_Time sbbt;
4344         BBT_Time ebbt;
4345         int hours, mins;
4346         nframes64_t distance, frame_rate;
4347         float secs;
4348         Meter meter_at_start(session->tempo_map().meter_at(start));
4349
4350         if (session == 0) {
4351                 return;
4352         }
4353
4354         AudioClock::Mode m;
4355
4356         if (Profile->get_sae() || Profile->get_small_screen()) {
4357                 m = ARDOUR_UI::instance()->primary_clock.mode ();
4358         } else {
4359                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4360         }
4361
4362         switch (m) {
4363         case AudioClock::BBT:
4364                 session->bbt_time (start, sbbt);
4365                 session->bbt_time (end, ebbt);
4366
4367                 /* subtract */
4368                 /* XXX this computation won't work well if the
4369                 user makes a selection that spans any meter changes.
4370                 */
4371
4372                 ebbt.bars -= sbbt.bars;
4373                 if (ebbt.beats >= sbbt.beats) {
4374                         ebbt.beats -= sbbt.beats;
4375                 } else {
4376                         ebbt.bars--;
4377                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4378                 }
4379                 if (ebbt.ticks >= sbbt.ticks) {
4380                         ebbt.ticks -= sbbt.ticks;
4381                 } else {
4382                         ebbt.beats--;
4383                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4384                 }
4385                 
4386                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4387                 break;
4388                 
4389         case AudioClock::SMPTE:
4390                 session->smpte_duration (end - start, smpte);
4391                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4392                 break;
4393
4394         case AudioClock::MinSec:
4395                 /* XXX this stuff should be elsewhere.. */
4396                 distance = end - start;
4397                 frame_rate = session->frame_rate();
4398                 hours = distance / (frame_rate * 3600);
4399                 distance = distance % (frame_rate * 3600);
4400                 mins = distance / (frame_rate * 60);
4401                 distance = distance % (frame_rate * 60);
4402                 secs = (float) distance / (float) frame_rate;
4403                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4404                 break;
4405
4406         default:
4407                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4408                 break;
4409         }
4410
4411         if (xpos >= 0 && ypos >=0) {
4412                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4413         }
4414         else {
4415                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4416         }
4417
4418         show_verbose_canvas_cursor ();
4419 }
4420
4421 void
4422 Editor::collect_new_region_view (RegionView* rv)
4423 {
4424         latest_regionviews.push_back (rv);
4425 }
4426
4427 void
4428 Editor::collect_and_select_new_region_view (RegionView* rv)
4429 {
4430         selection->add(rv);
4431         latest_regionviews.push_back (rv);
4432 }
4433
4434 void
4435 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4436 {
4437         if (clicked_regionview == 0) {
4438                 return;
4439         }
4440
4441         /* lets try to create new Region for the selection */
4442
4443         vector<boost::shared_ptr<Region> > new_regions;
4444         create_region_from_selection (new_regions);
4445
4446         if (new_regions.empty()) {
4447                 return;
4448         }
4449
4450         /* XXX fix me one day to use all new regions */
4451         
4452         boost::shared_ptr<Region> region (new_regions.front());
4453
4454         /* add it to the current stream/playlist.
4455
4456            tricky: the streamview for the track will add a new regionview. we will
4457            catch the signal it sends when it creates the regionview to
4458            set the regionview we want to then drag.
4459         */
4460         
4461         latest_regionviews.clear();
4462         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4463         
4464         /* A selection grab currently creates two undo/redo operations, one for 
4465            creating the new region and another for moving it.
4466         */
4467
4468         begin_reversible_command (_("selection grab"));
4469
4470         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4471
4472         XMLNode *before = &(playlist->get_state());
4473         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4474         XMLNode *after = &(playlist->get_state());
4475         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4476
4477         commit_reversible_command ();
4478         
4479         c.disconnect ();
4480         
4481         if (latest_regionviews.empty()) {
4482                 /* something went wrong */
4483                 return;
4484         }
4485
4486         /* we need to deselect all other regionviews, and select this one
4487            i'm ignoring undo stuff, because the region creation will take care of it 
4488         */
4489         selection->set (latest_regionviews);
4490         
4491         drag_info.item = latest_regionviews.front()->get_canvas_group();
4492         drag_info.data = latest_regionviews.front();
4493         drag_info.motion_callback = &Editor::region_drag_motion_callback;
4494         drag_info.finished_callback = &Editor::region_drag_finished_callback;
4495
4496         start_grab (event);
4497         
4498         drag_info.source_trackview = clicked_routeview;
4499         drag_info.dest_trackview = drag_info.source_trackview;
4500         drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4501         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4502         
4503         show_verbose_time_cursor (drag_info.last_frame_position, 10);
4504 }
4505
4506 void
4507 Editor::cancel_selection ()
4508 {
4509         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4510                 (*i)->hide_selection ();
4511         }
4512         selection->clear ();
4513         clicked_selection = 0;
4514 }       
4515
4516 void
4517 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4518 {
4519         nframes64_t start = 0;
4520         nframes64_t end = 0;
4521
4522         if (session == 0) {
4523                 return;
4524         }
4525
4526         drag_info.item = item;
4527         drag_info.motion_callback = &Editor::drag_selection;
4528         drag_info.finished_callback = &Editor::end_selection_op;
4529
4530         selection_op = op;
4531
4532         switch (op) {
4533         case CreateSelection:
4534                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4535                         drag_info.copy = true;
4536                 } else {
4537                         drag_info.copy = false;
4538                 }
4539                 start_grab (event, selector_cursor);
4540                 break;
4541
4542         case SelectionStartTrim:
4543                 if (clicked_axisview) {
4544                         clicked_axisview->order_selection_trims (item, true);
4545                 } 
4546                 start_grab (event, trimmer_cursor);
4547                 start = selection->time[clicked_selection].start;
4548                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4549                 break;
4550                 
4551         case SelectionEndTrim:
4552                 if (clicked_axisview) {
4553                         clicked_axisview->order_selection_trims (item, false);
4554                 }
4555                 start_grab (event, trimmer_cursor);
4556                 end = selection->time[clicked_selection].end;
4557                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
4558                 break;
4559
4560         case SelectionMove:
4561                 start = selection->time[clicked_selection].start;
4562                 start_grab (event);
4563                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4564                 break;
4565         }
4566
4567         if (selection_op == SelectionMove) {
4568                 show_verbose_time_cursor(start, 10);    
4569         } else {
4570                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4571         }
4572 }
4573
4574 void
4575 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4576 {
4577         nframes64_t start = 0;
4578         nframes64_t end = 0;
4579         nframes64_t length;
4580         nframes64_t pending_position;
4581
4582         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4583                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4584         } else {
4585                 pending_position = 0;
4586         }
4587         
4588         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4589                 snap_to (pending_position);
4590         }
4591
4592         /* only alter selection if the current frame is 
4593            different from the last frame position (adjusted)
4594          */
4595         
4596         if (pending_position == drag_info.last_pointer_frame) return;
4597         
4598         switch (selection_op) {
4599         case CreateSelection:
4600                 
4601                 if (drag_info.first_move) {
4602                         snap_to (drag_info.grab_frame);
4603                 }
4604                 
4605                 if (pending_position < drag_info.grab_frame) {
4606                         start = pending_position;
4607                         end = drag_info.grab_frame;
4608                 } else {
4609                         end = pending_position;
4610                         start = drag_info.grab_frame;
4611                 }
4612                 
4613                 /* first drag: Either add to the selection
4614                    or create a new selection->
4615                 */
4616                 
4617                 if (drag_info.first_move) {
4618                         
4619                         begin_reversible_command (_("range selection"));
4620                         
4621                         if (drag_info.copy) {
4622                                 /* adding to the selection */
4623                                 clicked_selection = selection->add (start, end);
4624                                 drag_info.copy = false;
4625                         } else {
4626                                 /* new selection-> */
4627                                 clicked_selection = selection->set (clicked_axisview, start, end);
4628                         }
4629                 } 
4630                 break;
4631                 
4632         case SelectionStartTrim:
4633                 
4634                 if (drag_info.first_move) {
4635                         begin_reversible_command (_("trim selection start"));
4636                 }
4637                 
4638                 start = selection->time[clicked_selection].start;
4639                 end = selection->time[clicked_selection].end;
4640
4641                 if (pending_position > end) {
4642                         start = end;
4643                 } else {
4644                         start = pending_position;
4645                 }
4646                 break;
4647                 
4648         case SelectionEndTrim:
4649                 
4650                 if (drag_info.first_move) {
4651                         begin_reversible_command (_("trim selection end"));
4652                 }
4653                 
4654                 start = selection->time[clicked_selection].start;
4655                 end = selection->time[clicked_selection].end;
4656
4657                 if (pending_position < start) {
4658                         end = start;
4659                 } else {
4660                         end = pending_position;
4661                 }
4662                 
4663                 break;
4664                 
4665         case SelectionMove:
4666                 
4667                 if (drag_info.first_move) {
4668                         begin_reversible_command (_("move selection"));
4669                 }
4670                 
4671                 start = selection->time[clicked_selection].start;
4672                 end = selection->time[clicked_selection].end;
4673                 
4674                 length = end - start;
4675                 
4676                 start = pending_position;
4677                 snap_to (start);
4678                 
4679                 end = start + length;
4680                 
4681                 break;
4682         }
4683         
4684         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4685                 start_canvas_autoscroll (1, 0);
4686         }
4687
4688         if (start != end) {
4689                 selection->replace (clicked_selection, start, end);
4690         }
4691
4692         drag_info.last_pointer_frame = pending_position;
4693         drag_info.first_move = false;
4694
4695         if (selection_op == SelectionMove) {
4696                 show_verbose_time_cursor(start, 10);    
4697         } else {
4698                 show_verbose_time_cursor(pending_position, 10); 
4699         }
4700 }
4701
4702 void
4703 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4704 {
4705         if (!drag_info.first_move) {
4706                 drag_selection (item, event);
4707                 /* XXX this is not object-oriented programming at all. ick */
4708                 if (selection->time.consolidate()) {
4709                         selection->TimeChanged ();
4710                 }
4711                 commit_reversible_command ();
4712         } else {
4713                 /* just a click, no pointer movement.*/
4714
4715                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4716
4717                         selection->clear_time();
4718
4719                 } 
4720         }
4721
4722         /* XXX what happens if its a music selection? */
4723         session->set_audio_range (selection->time);
4724         stop_canvas_autoscroll ();
4725 }
4726
4727 void
4728 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4729 {
4730         double speed = 1.0;
4731         TimeAxisView* tvp = clicked_axisview;
4732         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4733
4734         if (tv && tv->is_track()) {
4735                 speed = tv->get_diskstream()->speed();
4736         }
4737         
4738         nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4739         nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4740         nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4741
4742         //drag_info.item = clicked_regionview->get_name_highlight();
4743         drag_info.item = item;
4744         drag_info.motion_callback = &Editor::trim_motion_callback;
4745         drag_info.finished_callback = &Editor::trim_finished_callback;
4746
4747         start_grab (event, trimmer_cursor);
4748         
4749         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4750                 trim_op = ContentsTrim;
4751         } else {
4752                 /* These will get overridden for a point trim.*/
4753                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4754                         /* closer to start */
4755                         trim_op = StartTrim;
4756                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4757                         /* closer to end */
4758                         trim_op = EndTrim;
4759                 }
4760         }
4761
4762         switch (trim_op) {
4763         case StartTrim:
4764                 show_verbose_time_cursor(region_start, 10);     
4765                 break;
4766         case EndTrim:
4767                 show_verbose_time_cursor(region_end, 10);       
4768                 break;
4769         case ContentsTrim:
4770                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4771                 break;
4772         }
4773 }
4774
4775 void
4776 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4777 {
4778         RegionView* rv = clicked_regionview;
4779         nframes64_t frame_delta = 0;
4780         bool left_direction;
4781         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4782
4783         /* snap modifier works differently here..
4784            its' current state has to be passed to the 
4785            various trim functions in order to work properly 
4786         */ 
4787
4788         double speed = 1.0;
4789         TimeAxisView* tvp = clicked_axisview;
4790         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4791         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4792
4793         if (tv && tv->is_track()) {
4794                 speed = tv->get_diskstream()->speed();
4795         }
4796         
4797         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4798                 left_direction = true;
4799         } else {
4800                 left_direction = false;
4801         }
4802
4803         if (obey_snap) {
4804                 snap_to (drag_info.current_pointer_frame);
4805         }
4806
4807         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4808                 return;
4809         }
4810
4811         if (drag_info.first_move) {
4812         
4813                 string trim_type;
4814
4815                 switch (trim_op) {
4816                 case StartTrim:
4817                         trim_type = "Region start trim";
4818                         break;
4819                 case EndTrim:
4820                         trim_type = "Region end trim";
4821                         break;
4822                 case ContentsTrim:
4823                         trim_type = "Region content trim";
4824                         break;
4825                 }
4826
4827                 begin_reversible_command (trim_type);
4828
4829                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4830                         (*i)->fake_set_opaque(false);
4831                         (*i)->region()->freeze ();
4832                 
4833                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4834                         if (arv)
4835                                 arv->temporarily_hide_envelope ();
4836
4837                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4838                         insert_result = motion_frozen_playlists.insert (pl);
4839                         if (insert_result.second) {
4840                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4841                                 pl->freeze();
4842                         }
4843                 }
4844         }
4845
4846         if (left_direction) {
4847                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4848         } else {
4849                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4850         }
4851
4852         switch (trim_op) {              
4853         case StartTrim:
4854                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4855                         break;
4856                 } else {
4857                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4858                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4859                         }
4860                         break;
4861                 }
4862                 
4863         case EndTrim:
4864                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4865                         break;
4866                 } else {
4867                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4868                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4869                         }
4870                         break;
4871                 }
4872                 
4873         case ContentsTrim:
4874                 {
4875                         bool swap_direction = false;
4876
4877                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4878                                 swap_direction = true;
4879                         }
4880                         
4881                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4882                              i != selection->regions.by_layer().end(); ++i)
4883                         {
4884                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4885                         }
4886                 }
4887                 break;
4888         }
4889
4890         switch (trim_op) {
4891         case StartTrim:
4892                 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);   
4893                 break;
4894         case EndTrim:
4895                 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); 
4896                 break;
4897         case ContentsTrim:
4898                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4899                 break;
4900         }
4901
4902         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4903         drag_info.first_move = false;
4904 }
4905
4906 void
4907 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4908 {
4909         boost::shared_ptr<Region> region (rv.region());
4910
4911         if (region->locked()) {
4912                 return;
4913         }
4914
4915         nframes64_t new_bound;
4916
4917         double speed = 1.0;
4918         TimeAxisView* tvp = clicked_axisview;
4919         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4920
4921         if (tv && tv->is_track()) {
4922                 speed = tv->get_diskstream()->speed();
4923         }
4924         
4925         if (left_direction) {
4926                 if (swap_direction) {
4927                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4928                 } else {
4929                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4930                 }
4931         } else {
4932                 if (swap_direction) {
4933                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4934                 } else {
4935                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4936                 }
4937         }
4938
4939         if (obey_snap) {
4940                 snap_to (new_bound);
4941         }
4942         region->trim_start ((nframes64_t) (new_bound * speed), this);   
4943         rv.region_changed (StartChanged);
4944 }
4945
4946 void
4947 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4948 {
4949         boost::shared_ptr<Region> region (rv.region()); 
4950
4951         if (region->locked()) {
4952                 return;
4953         }
4954
4955         nframes64_t new_bound;
4956
4957         double speed = 1.0;
4958         TimeAxisView* tvp = clicked_axisview;
4959         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4960
4961         if (tv && tv->is_track()) {
4962                 speed = tv->get_diskstream()->speed();
4963         }
4964         
4965         if (left_direction) {
4966                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4967         } else {
4968                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4969         }
4970
4971         if (obey_snap) {
4972                 snap_to (new_bound, (left_direction ? 0 : 1));  
4973         }
4974
4975         region->trim_front ((nframes64_t) (new_bound * speed), this);
4976
4977         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4978 }
4979
4980 void
4981 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4982 {
4983         boost::shared_ptr<Region> region (rv.region());
4984
4985         if (region->locked()) {
4986                 return;
4987         }
4988
4989         nframes64_t new_bound;
4990
4991         double speed = 1.0;
4992         TimeAxisView* tvp = clicked_axisview;
4993         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4994
4995         if (tv && tv->is_track()) {
4996                 speed = tv->get_diskstream()->speed();
4997         }
4998         
4999         if (left_direction) {
5000                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5001         } else {
5002                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5003         }
5004
5005         if (obey_snap) {
5006                 snap_to (new_bound);
5007         }
5008         region->trim_end ((nframes64_t) (new_bound * speed), this);
5009         rv.region_changed (LengthChanged);
5010 }
5011         
5012 void
5013 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5014 {
5015         if (!drag_info.first_move) {
5016                 trim_motion_callback (item, event);
5017                 
5018                 if (!selection->selected (clicked_regionview)) {
5019                         thaw_region_after_trim (*clicked_regionview);           
5020                 } else {
5021                         
5022                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5023                              i != selection->regions.by_layer().end(); ++i)
5024                         {
5025                                 thaw_region_after_trim (**i);
5026                                 (*i)->fake_set_opaque (true);
5027                         }
5028                 }
5029                 
5030                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5031                         (*p)->thaw ();
5032                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5033                 }
5034                 
5035                 motion_frozen_playlists.clear ();
5036
5037                 commit_reversible_command();
5038         } else {
5039                 /* no mouse movement */
5040                 point_trim (event);
5041         }
5042 }
5043
5044 void
5045 Editor::point_trim (GdkEvent* event)
5046 {
5047         RegionView* rv = clicked_regionview;
5048         nframes64_t new_bound = drag_info.current_pointer_frame;
5049
5050         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5051                 snap_to (new_bound);
5052         }
5053
5054         /* Choose action dependant on which button was pressed */
5055         switch (event->button.button) {
5056         case 1:
5057                 trim_op = StartTrim;
5058                 begin_reversible_command (_("Start point trim"));
5059
5060                 if (selection->selected (rv)) {
5061
5062                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5063                              i != selection->regions.by_layer().end(); ++i)
5064                         {
5065                                 if (!(*i)->region()->locked()) {
5066                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5067                                         XMLNode &before = pl->get_state();
5068                                         (*i)->region()->trim_front (new_bound, this);   
5069                                         XMLNode &after = pl->get_state();
5070                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5071                                 }
5072                         }
5073
5074                 } else {
5075
5076                         if (!rv->region()->locked()) {
5077                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5078                                 XMLNode &before = pl->get_state();
5079                                 rv->region()->trim_front (new_bound, this);     
5080                                 XMLNode &after = pl->get_state();
5081                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5082                         }
5083                 }
5084
5085                 commit_reversible_command();
5086         
5087                 break;
5088         case 2:
5089                 trim_op = EndTrim;
5090                 begin_reversible_command (_("End point trim"));
5091
5092                 if (selection->selected (rv)) {
5093                         
5094                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5095                         {
5096                                 if (!(*i)->region()->locked()) {
5097                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5098                                         XMLNode &before = pl->get_state();
5099                                         (*i)->region()->trim_end (new_bound, this);
5100                                         XMLNode &after = pl->get_state();
5101                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5102                                 }
5103                         }
5104
5105                 } else {
5106
5107                         if (!rv->region()->locked()) {
5108                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5109                                 XMLNode &before = pl->get_state();
5110                                 rv->region()->trim_end (new_bound, this);
5111                                 XMLNode &after = pl->get_state();
5112                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5113                         }
5114                 }
5115
5116                 commit_reversible_command();
5117         
5118                 break;
5119         default:
5120                 break;
5121         }
5122 }
5123
5124 void
5125 Editor::thaw_region_after_trim (RegionView& rv)
5126 {
5127         boost::shared_ptr<Region> region (rv.region());
5128
5129         if (region->locked()) {
5130                 return;
5131         }
5132
5133         region->thaw (_("trimmed region"));
5134         XMLNode &after = region->playlist()->get_state();
5135         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5136
5137         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5138         if (arv)
5139                 arv->unhide_envelope ();
5140 }
5141
5142 void
5143 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5144 {
5145         Marker* marker;
5146         bool is_start;
5147
5148         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5149                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5150                 /*NOTREACHED*/
5151         }
5152
5153         Location* location = find_location_from_marker (marker, is_start);      
5154         location->set_hidden (true, this);
5155 }
5156
5157
5158 void
5159 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5160 {
5161         if (session == 0) {
5162                 return;
5163         }
5164
5165         drag_info.item = item;
5166         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5167         drag_info.finished_callback = &Editor::end_range_markerbar_op;
5168
5169         range_marker_op = op;
5170
5171         if (!temp_location) {
5172                 temp_location = new Location;
5173         }
5174         
5175         switch (op) {
5176         case CreateRangeMarker:
5177         case CreateTransportMarker:
5178         case CreateCDMarker:
5179         
5180                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5181                         drag_info.copy = true;
5182                 } else {
5183                         drag_info.copy = false;
5184                 }
5185                 start_grab (event, selector_cursor);
5186                 break;
5187         }
5188
5189         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5190         
5191 }
5192
5193 void
5194 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5195 {
5196         nframes64_t start = 0;
5197         nframes64_t end = 0;
5198         ArdourCanvas::SimpleRect *crect;
5199
5200         switch (range_marker_op) {
5201         case CreateRangeMarker:
5202                 crect = range_bar_drag_rect;
5203                 break;
5204         case CreateTransportMarker:
5205                 crect = transport_bar_drag_rect;
5206                 break;
5207         case CreateCDMarker:
5208                 crect = cd_marker_bar_drag_rect;
5209                 break;
5210         default:
5211                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5212                 return;
5213                 break;
5214         }
5215         
5216         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5217                 snap_to (drag_info.current_pointer_frame);
5218         }
5219
5220         /* only alter selection if the current frame is 
5221            different from the last frame position.
5222          */
5223         
5224         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5225         
5226         switch (range_marker_op) {
5227         case CreateRangeMarker:
5228         case CreateTransportMarker:
5229         case CreateCDMarker:
5230                 if (drag_info.first_move) {
5231                         snap_to (drag_info.grab_frame);
5232                 }
5233                 
5234                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5235                         start = drag_info.current_pointer_frame;
5236                         end = drag_info.grab_frame;
5237                 } else {
5238                         end = drag_info.current_pointer_frame;
5239                         start = drag_info.grab_frame;
5240                 }
5241                 
5242                 /* first drag: Either add to the selection
5243                    or create a new selection.
5244                 */
5245                 
5246                 if (drag_info.first_move) {
5247                         
5248                         temp_location->set (start, end);
5249                         
5250                         crect->show ();
5251
5252                         update_marker_drag_item (temp_location);
5253                         range_marker_drag_rect->show();
5254                         //range_marker_drag_rect->raise_to_top();
5255                         
5256                 } 
5257                 break;          
5258         }
5259         
5260         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5261                 start_canvas_autoscroll (1, 0);
5262         }
5263         
5264         if (start != end) {
5265                 temp_location->set (start, end);
5266
5267                 double x1 = frame_to_pixel (start);
5268                 double x2 = frame_to_pixel (end);
5269                 crect->property_x1() = x1;
5270                 crect->property_x2() = x2;
5271
5272                 update_marker_drag_item (temp_location);
5273         }
5274
5275         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5276         drag_info.first_move = false;
5277
5278         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5279         
5280 }
5281
5282 void
5283 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5284 {
5285         Location * newloc = 0;
5286         string rangename;
5287         int flags;
5288         
5289         if (!drag_info.first_move) {
5290                 drag_range_markerbar_op (item, event);
5291
5292                 switch (range_marker_op) {
5293                 case CreateRangeMarker:
5294                 case CreateCDMarker:
5295                     {
5296                         begin_reversible_command (_("new range marker"));
5297                         XMLNode &before = session->locations()->get_state();
5298                         session->locations()->next_available_name(rangename,"unnamed");
5299                         if (range_marker_op == CreateCDMarker) {
5300                                 flags =  Location::IsRangeMarker|Location::IsCDMarker;
5301                                 cd_marker_bar_drag_rect->hide();
5302                         }
5303                         else {
5304                                 flags =  Location::IsRangeMarker;
5305                                 range_bar_drag_rect->hide();
5306                         }
5307                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5308                         session->locations()->add (newloc, true);
5309                         XMLNode &after = session->locations()->get_state();
5310                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5311                         commit_reversible_command ();
5312                         
5313                         range_marker_drag_rect->hide();
5314                         break;
5315                     }
5316
5317                 case CreateTransportMarker:
5318                         // popup menu to pick loop or punch
5319                         new_transport_marker_context_menu (&event->button, item);
5320                         
5321                         break;
5322                 }
5323         } else {
5324                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5325
5326                 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5327
5328                         nframes64_t start;
5329                         nframes64_t end;
5330
5331                         start = session->locations()->first_mark_before (drag_info.grab_frame);
5332                         end = session->locations()->first_mark_after (drag_info.grab_frame);
5333                         
5334                         if (end == max_frames) {
5335                                 end = session->current_end_frame ();
5336                         }
5337
5338                         if (start == 0) {
5339                                 start = session->current_start_frame ();
5340                         }
5341
5342                         switch (mouse_mode) {
5343                         case MouseObject:
5344                                 /* find the two markers on either side and then make the selection from it */
5345                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5346                                 break;
5347
5348                         case MouseRange:
5349                                 /* find the two markers on either side of the click and make the range out of it */
5350                                 selection->set (0, start, end);
5351                                 break;
5352
5353                         default:
5354                                 break;
5355                         }
5356                 } 
5357         }
5358
5359         stop_canvas_autoscroll ();
5360 }
5361
5362
5363
5364 void
5365 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5366 {
5367         drag_info.item = item;
5368         drag_info.motion_callback = &Editor::drag_mouse_zoom;
5369         drag_info.finished_callback = &Editor::end_mouse_zoom;
5370
5371         start_grab (event, zoom_cursor);
5372
5373         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5374 }
5375
5376 void
5377 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5378 {
5379         nframes64_t start;
5380         nframes64_t end;
5381
5382         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5383                 snap_to (drag_info.current_pointer_frame);
5384                 
5385                 if (drag_info.first_move) {
5386                         snap_to (drag_info.grab_frame);
5387                 }
5388         }
5389                 
5390         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5391
5392         /* base start and end on initial click position */
5393         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5394                 start = drag_info.current_pointer_frame;
5395                 end = drag_info.grab_frame;
5396         } else {
5397                 end = drag_info.current_pointer_frame;
5398                 start = drag_info.grab_frame;
5399         }
5400         
5401         if (start != end) {
5402
5403                 if (drag_info.first_move) {
5404                         zoom_rect->show();
5405                         zoom_rect->raise_to_top();
5406                 }
5407
5408                 reposition_zoom_rect(start, end);
5409
5410                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5411                 drag_info.first_move = false;
5412
5413                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5414         }
5415 }
5416
5417 void
5418 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5419 {
5420         if (!drag_info.first_move) {
5421                 drag_mouse_zoom (item, event);
5422                 
5423                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5424                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5425                 } else {
5426                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5427                 }               
5428         } else {
5429                 temporal_zoom_to_frame (false, drag_info.grab_frame);
5430                 /*
5431                 temporal_zoom_step (false);
5432                 center_screen (drag_info.grab_frame);
5433                 */
5434         }
5435
5436         zoom_rect->hide();
5437 }
5438
5439 void
5440 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5441 {
5442         double x1 = frame_to_pixel (start);
5443         double x2 = frame_to_pixel (end);
5444         double y2 = full_canvas_height - 1.0;
5445
5446         zoom_rect->property_x1() = x1;
5447         zoom_rect->property_y1() = 1.0;
5448         zoom_rect->property_x2() = x2;
5449         zoom_rect->property_y2() = y2;
5450 }
5451
5452 void
5453 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5454 {
5455         drag_info.item = item;
5456         drag_info.motion_callback = &Editor::drag_rubberband_select;
5457         drag_info.finished_callback = &Editor::end_rubberband_select;
5458
5459         start_grab (event, cross_hair_cursor);
5460
5461         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5462 }
5463
5464 void
5465 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5466 {
5467         nframes64_t start;
5468         nframes64_t end;
5469         double y1;
5470         double y2;
5471
5472         /* use a bigger drag threshold than the default */
5473
5474         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5475                 return;
5476         }
5477
5478         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5479                 if (drag_info.first_move) {
5480                         snap_to (drag_info.grab_frame);
5481                 } 
5482                 snap_to (drag_info.current_pointer_frame);
5483         }
5484
5485         /* base start and end on initial click position */
5486
5487         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5488                 start = drag_info.current_pointer_frame;
5489                 end = drag_info.grab_frame;
5490         } else {
5491                 end = drag_info.current_pointer_frame;
5492                 start = drag_info.grab_frame;
5493         }
5494
5495         if (drag_info.current_pointer_y < drag_info.grab_y) {
5496                 y1 = drag_info.current_pointer_y;
5497                 y2 = drag_info.grab_y;
5498         } else {
5499                 y2 = drag_info.current_pointer_y;
5500                 y1 = drag_info.grab_y;
5501         }
5502
5503         
5504         if (start != end || y1 != y2) {
5505
5506                 double x1 = frame_to_pixel (start);
5507                 double x2 = frame_to_pixel (end);
5508                 
5509                 rubberband_rect->property_x1() = x1;
5510                 rubberband_rect->property_y1() = y1;
5511                 rubberband_rect->property_x2() = x2;
5512                 rubberband_rect->property_y2() = y2;
5513
5514                 rubberband_rect->show();
5515                 rubberband_rect->raise_to_top();
5516                 
5517                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5518                 drag_info.first_move = false;
5519
5520                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5521         }
5522 }
5523
5524 void
5525 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5526 {
5527         if (!drag_info.first_move) {
5528
5529                 drag_rubberband_select (item, event);
5530
5531                 double y1,y2;
5532                 if (drag_info.current_pointer_y < drag_info.grab_y) {
5533                         y1 = drag_info.current_pointer_y;
5534                         y2 = drag_info.grab_y;
5535                 } else {
5536                         y2 = drag_info.current_pointer_y;
5537                         y1 = drag_info.grab_y;
5538                 }
5539
5540
5541                 Selection::Operation op = Keyboard::selection_type (event->button.state);
5542                 bool commit;
5543
5544                 begin_reversible_command (_("rubberband selection"));
5545
5546                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5547                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5548                 } else {
5549                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5550                 }               
5551
5552                 if (commit) {
5553                         commit_reversible_command ();
5554                 }
5555                 
5556         } else {
5557                 selection->clear_tracks();
5558                 selection->clear_regions();
5559                 selection->clear_points ();
5560                 selection->clear_lines ();
5561         }
5562
5563         rubberband_rect->hide();
5564 }
5565
5566
5567 gint
5568 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5569 {
5570         using namespace Gtkmm2ext;
5571
5572         ArdourPrompter prompter (false);
5573
5574         prompter.set_prompt (_("Name for region:"));
5575         prompter.set_initial_text (clicked_regionview->region()->name());
5576         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5577         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5578         prompter.show_all ();
5579         switch (prompter.run ()) {
5580         case Gtk::RESPONSE_ACCEPT:
5581                 string str;
5582                 prompter.get_result(str);
5583                 if (str.length()) {
5584                         clicked_regionview->region()->set_name (str);
5585                 }
5586                 break;
5587         }
5588         return true;
5589 }
5590
5591 void
5592 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5593 {
5594         drag_info.item = item;
5595         drag_info.motion_callback = &Editor::time_fx_motion;
5596         drag_info.finished_callback = &Editor::end_time_fx;
5597
5598         start_grab (event);
5599
5600         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5601 }
5602
5603 void
5604 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5605 {
5606         RegionView* rv = clicked_regionview;
5607
5608         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5609                 snap_to (drag_info.current_pointer_frame);
5610         }
5611
5612         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5613                 return;
5614         }
5615
5616         if (drag_info.current_pointer_frame > rv->region()->position()) {
5617                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5618         }
5619
5620         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5621         drag_info.first_move = false;
5622
5623         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5624 }
5625
5626 void
5627 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5628 {
5629         clicked_regionview->get_time_axis_view().hide_timestretch ();
5630
5631         if (drag_info.first_move) {
5632                 return;
5633         }
5634
5635         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5636                 /* backwards drag of the left edge - not usable */
5637                 return;
5638         }
5639         
5640         nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5641
5642         float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5643         
5644 #ifndef USE_RUBBERBAND
5645         // Soundtouch uses percentage / 100 instead of normal (/ 1) 
5646         if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5647                 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5648         }
5649 #endif  
5650         
5651         begin_reversible_command (_("timestretch"));
5652         
5653         // XXX how do timeFX on multiple regions ?
5654         
5655         RegionSelection rs;
5656         rs.add (clicked_regionview);
5657
5658         if (time_stretch (rs, percentage) == 0) {
5659                 session->commit_reversible_command ();
5660         }
5661 }
5662
5663 void
5664 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5665 {
5666         /* no brushing without a useful snap setting */
5667
5668         switch (snap_mode) {
5669         case SnapMagnetic:
5670                 return; /* can't work because it allows region to be placed anywhere */
5671         default:
5672                 break; /* OK */
5673         }
5674
5675         switch (snap_type) {
5676         case SnapToMark:
5677                 return;
5678
5679         default:
5680                 break;
5681         }
5682
5683         /* don't brush a copy over the original */
5684         
5685         if (pos == rv->region()->position()) {
5686                 return;
5687         }
5688
5689         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5690
5691         if (rtv == 0 || !rtv->is_track()) {
5692                 return;
5693         }
5694
5695         boost::shared_ptr<Playlist> playlist = rtv->playlist();
5696         double speed = rtv->get_diskstream()->speed();
5697         
5698         XMLNode &before = playlist->get_state();
5699         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5700         XMLNode &after = playlist->get_state();
5701         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5702         
5703         // playlist is frozen, so we have to update manually
5704         
5705         playlist->Modified(); /* EMIT SIGNAL */
5706 }
5707
5708 gint
5709 Editor::track_height_step_timeout ()
5710 {
5711         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5712                 current_stepping_trackview = 0;
5713                 return false;
5714         }
5715         return true;
5716 }
5717