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