merge 3870-3890 from 2.0-ongoing into 3.X (compiles, runs, no other promises
[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                 if (changed_position && !drag_info.x_constrained) {
4005                         where = rv->region()->position() - drag_delta;
4006                 } else {
4007                         where = rv->region()->position();
4008                 }
4009                         
4010                 boost::shared_ptr<Region> new_region;
4011
4012                 if (drag_info.copy) {
4013                         /* we already made a copy */
4014                         new_region = rv->region();
4015
4016                         /* undo the previous hide_dependent_views so that xfades don't
4017                            disappear on copying regions 
4018                         */
4019                 
4020                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
4021                 
4022                 } else if (changed_tracks && dest_rtv->playlist()) {
4023                         new_region = RegionFactory::create (rv->region());
4024                 }
4025
4026                 if (changed_tracks || drag_info.copy) {
4027
4028                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4029                         if (!to_playlist) {
4030                                 ++i;
4031                                 continue;
4032                         }
4033
4034                         latest_regionviews.clear ();
4035
4036                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4037                         
4038                         insert_result = modified_playlists.insert (to_playlist);
4039                         if (insert_result.second) {
4040                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4041                         }
4042
4043                         to_playlist->add_region (new_region, where);
4044
4045                         c.disconnect ();
4046                                                               
4047                         if (!latest_regionviews.empty()) {
4048                                 // XXX why just the first one ? we only expect one
4049                                 // commented out in nick_m's canvas reworking. is that intended?
4050                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4051                                 new_selection.push_back (latest_regionviews.front());
4052                         }
4053
4054                 } else {
4055                         /* 
4056                            motion on the same track. plonk the previously reparented region 
4057                            back to its original canvas group (its streamview).
4058                            No need to do anything for copies as they are fake regions which will be deleted.
4059                         */
4060
4061                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4062                         rv->get_canvas_group()->property_y() = 0;
4063                   
4064                         /* just change the model */
4065                         
4066                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4067
4068                         insert_result = modified_playlists.insert (playlist);
4069                         if (insert_result.second) {
4070                                 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4071                         }
4072                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
4073                         frozen_insert_result = frozen_playlists.insert(playlist);
4074                         if (frozen_insert_result.second) {
4075                                 playlist->freeze();
4076                         }
4077
4078                         rv->region()->set_position (where, (void*) this);
4079                 }
4080
4081                 if (changed_tracks && !drag_info.copy) {
4082
4083                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
4084                            because we may have copied the region and it has not been attached to a playlist.
4085                         */
4086
4087                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4088                         assert ((ds = source_tv->get_diskstream()));
4089                         assert ((from_playlist = ds->playlist()));
4090
4091                         /* moved to a different audio track, without copying */
4092
4093                         /* the region that used to be in the old playlist is not
4094                            moved to the new one - we use a copy of it. as a result,
4095                            any existing editor for the region should no longer be
4096                            visible.
4097                         */ 
4098             
4099                         rv->hide_region_editor();
4100                         rv->fake_set_opaque (false);
4101                         
4102                         /* remove the region from the old playlist */
4103
4104                         insert_result = modified_playlists.insert (from_playlist);
4105                         if (insert_result.second) {
4106                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4107                         }
4108
4109                         from_playlist->remove_region ((rv->region()));
4110                         
4111                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4112                            was selected in all of them, then removing it from a playlist will have removed all
4113                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
4114                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4115                            corresponding regionview, and the selection is now empty).
4116
4117                            this could have invalidated any and all iterators into the region selection.
4118
4119                            the heuristic we use here is: if the region selection is empty, break out of the loop
4120                            here. if the region selection is not empty, then restart the loop because we know that
4121                            we must have removed at least the region(view) we've just been working on as well as any
4122                            that we processed on previous iterations.
4123
4124                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4125                            we can just iterate.
4126                         */
4127
4128                         if (selection->regions.empty()) {
4129                                 break;
4130                         } else { 
4131                                 i = selection->regions.by_layer().begin();
4132                         }
4133
4134                 } else {
4135                         ++i;
4136                 }
4137                 
4138                 if (drag_info.copy) {
4139                         copies.push_back (rv);
4140                 }
4141         }
4142         
4143         if (new_selection.empty()) {
4144                 if (drag_info.copy) {
4145                         /* the region(view)s that are selected and being dragged around
4146                            are copies and do not belong to any track. remove them
4147                            from the selection right here.
4148                         */
4149                         selection->clear_regions();
4150                 }
4151         } else {
4152                 /* this will clear any existing selection that would have been
4153                    cleared in the other clause above
4154                 */
4155                 selection->set (new_selection);
4156         }
4157
4158         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4159                 (*p)->thaw();
4160         }
4161                         
4162   out:
4163         if (!nocommit) {
4164                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4165                         session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));      
4166                 }
4167                 commit_reversible_command ();
4168         }
4169
4170         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4171                 delete *x;
4172         }
4173
4174 }
4175         
4176 void
4177 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4178 {
4179         if (drag_info.move_threshold_passed) {
4180                 if (drag_info.first_move) {
4181                         // TODO: create region-create-drag region view here
4182                         drag_info.first_move = false;
4183                 }
4184
4185                 // TODO: resize region-create-drag region view here
4186         }
4187
4188
4189 void
4190 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4191 {
4192         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4193         if (!mtv)
4194                 return;
4195
4196         const boost::shared_ptr<MidiDiskstream> diskstream =
4197                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4198         
4199         if (!diskstream) {
4200                 warning << "Cannot create non-MIDI region" << endl;
4201                 return;
4202         }
4203
4204         if (drag_info.first_move) {
4205                 begin_reversible_command (_("create region"));
4206                 XMLNode &before = mtv->playlist()->get_state();
4207
4208                 nframes64_t start = drag_info.grab_frame;
4209                 snap_to (start, -1);
4210                 const Meter& m = session->tempo_map().meter_at(start);
4211                 const Tempo& t = session->tempo_map().tempo_at(start);
4212                 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4213
4214                 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4215                                 
4216                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4217                                              (RegionFactory::create(src, 0, (nframes_t) length, 
4218                                                                     PBD::basename_nosuffix(src->name()))), start);
4219                 XMLNode &after = mtv->playlist()->get_state();
4220                 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4221                 commit_reversible_command();
4222
4223         } else {
4224                 create_region_drag_motion_callback (item, event);
4225                 // TODO: create region-create-drag region here
4226         }
4227 }
4228
4229 void
4230 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4231 {
4232         /* Either add to or set the set the region selection, unless
4233            this is an alignment click (control used)
4234         */
4235         
4236         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4237                 TimeAxisView* tv = &rv.get_time_axis_view();
4238                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4239                 double speed = 1.0;
4240                 if (rtv && rtv->is_track()) {
4241                         speed = rtv->get_diskstream()->speed();
4242                 }
4243
4244                 nframes64_t where = get_preferred_edit_position();
4245
4246                 if (where >= 0) {
4247
4248                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4249                                 
4250                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4251                                 
4252                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4253                                 
4254                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
4255                                 
4256                         } else {
4257                                 
4258                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4259                         }
4260                 }
4261         }
4262 }
4263
4264 void
4265 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
4266 {
4267         char buf[128];
4268         SMPTE::Time smpte;
4269         BBT_Time bbt;
4270         int hours, mins;
4271         nframes64_t frame_rate;
4272         float secs;
4273
4274         if (session == 0) {
4275                 return;
4276         }
4277
4278         AudioClock::Mode m;
4279
4280         if (Profile->get_sae() || Profile->get_small_screen()) {
4281                 m = ARDOUR_UI::instance()->primary_clock.mode();
4282         } else {
4283                 m = ARDOUR_UI::instance()->secondary_clock.mode();
4284         }
4285
4286         switch (m) {
4287         case AudioClock::BBT:
4288                 session->bbt_time (frame, bbt);
4289                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4290                 break;
4291                 
4292         case AudioClock::SMPTE:
4293                 session->smpte_time (frame, smpte);
4294                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4295                 break;
4296
4297         case AudioClock::MinSec:
4298                 /* XXX this is copied from show_verbose_duration_cursor() */
4299                 frame_rate = session->frame_rate();
4300                 hours = frame / (frame_rate * 3600);
4301                 frame = frame % (frame_rate * 3600);
4302                 mins = frame / (frame_rate * 60);
4303                 frame = frame % (frame_rate * 60);
4304                 secs = (float) frame / (float) frame_rate;
4305                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4306                 break;
4307
4308         default:
4309                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4310                 break;
4311         }
4312
4313         if (xpos >= 0 && ypos >=0) {
4314                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4315         }
4316         else {
4317                 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);
4318         }
4319         show_verbose_canvas_cursor ();
4320 }
4321
4322 void
4323 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
4324 {
4325         char buf[128];
4326         SMPTE::Time smpte;
4327         BBT_Time sbbt;
4328         BBT_Time ebbt;
4329         int hours, mins;
4330         nframes64_t distance, frame_rate;
4331         float secs;
4332         Meter meter_at_start(session->tempo_map().meter_at(start));
4333
4334         if (session == 0) {
4335                 return;
4336         }
4337
4338         AudioClock::Mode m;
4339
4340         if (Profile->get_sae() || Profile->get_small_screen()) {
4341                 m = ARDOUR_UI::instance()->primary_clock.mode ();
4342         } else {
4343                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4344         }
4345
4346         switch (m) {
4347         case AudioClock::BBT:
4348                 session->bbt_time (start, sbbt);
4349                 session->bbt_time (end, ebbt);
4350
4351                 /* subtract */
4352                 /* XXX this computation won't work well if the
4353                 user makes a selection that spans any meter changes.
4354                 */
4355
4356                 ebbt.bars -= sbbt.bars;
4357                 if (ebbt.beats >= sbbt.beats) {
4358                         ebbt.beats -= sbbt.beats;
4359                 } else {
4360                         ebbt.bars--;
4361                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4362                 }
4363                 if (ebbt.ticks >= sbbt.ticks) {
4364                         ebbt.ticks -= sbbt.ticks;
4365                 } else {
4366                         ebbt.beats--;
4367                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4368                 }
4369                 
4370                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4371                 break;
4372                 
4373         case AudioClock::SMPTE:
4374                 session->smpte_duration (end - start, smpte);
4375                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4376                 break;
4377
4378         case AudioClock::MinSec:
4379                 /* XXX this stuff should be elsewhere.. */
4380                 distance = end - start;
4381                 frame_rate = session->frame_rate();
4382                 hours = distance / (frame_rate * 3600);
4383                 distance = distance % (frame_rate * 3600);
4384                 mins = distance / (frame_rate * 60);
4385                 distance = distance % (frame_rate * 60);
4386                 secs = (float) distance / (float) frame_rate;
4387                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4388                 break;
4389
4390         default:
4391                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4392                 break;
4393         }
4394
4395         if (xpos >= 0 && ypos >=0) {
4396                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4397         }
4398         else {
4399                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4400         }
4401
4402         show_verbose_canvas_cursor ();
4403 }
4404
4405 void
4406 Editor::collect_new_region_view (RegionView* rv)
4407 {
4408         latest_regionviews.push_back (rv);
4409 }
4410
4411 void
4412 Editor::collect_and_select_new_region_view (RegionView* rv)
4413 {
4414         selection->add(rv);
4415         latest_regionviews.push_back (rv);
4416 }
4417
4418 void
4419 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4420 {
4421         if (clicked_regionview == 0) {
4422                 return;
4423         }
4424
4425         /* lets try to create new Region for the selection */
4426
4427         vector<boost::shared_ptr<Region> > new_regions;
4428         create_region_from_selection (new_regions);
4429
4430         if (new_regions.empty()) {
4431                 return;
4432         }
4433
4434         /* XXX fix me one day to use all new regions */
4435         
4436         boost::shared_ptr<Region> region (new_regions.front());
4437
4438         /* add it to the current stream/playlist.
4439
4440            tricky: the streamview for the track will add a new regionview. we will
4441            catch the signal it sends when it creates the regionview to
4442            set the regionview we want to then drag.
4443         */
4444         
4445         latest_regionviews.clear();
4446         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4447         
4448         /* A selection grab currently creates two undo/redo operations, one for 
4449            creating the new region and another for moving it.
4450         */
4451
4452         begin_reversible_command (_("selection grab"));
4453
4454         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4455
4456         XMLNode *before = &(playlist->get_state());
4457         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4458         XMLNode *after = &(playlist->get_state());
4459         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4460
4461         commit_reversible_command ();
4462         
4463         c.disconnect ();
4464         
4465         if (latest_regionviews.empty()) {
4466                 /* something went wrong */
4467                 return;
4468         }
4469
4470         /* we need to deselect all other regionviews, and select this one
4471            i'm ignoring undo stuff, because the region creation will take care of it 
4472         */
4473         selection->set (latest_regionviews);
4474         
4475         drag_info.item = latest_regionviews.front()->get_canvas_group();
4476         drag_info.data = latest_regionviews.front();
4477         drag_info.motion_callback = &Editor::region_drag_motion_callback;
4478         drag_info.finished_callback = &Editor::region_drag_finished_callback;
4479
4480         start_grab (event);
4481         
4482         drag_info.source_trackview = clicked_routeview;
4483         drag_info.dest_trackview = drag_info.source_trackview;
4484         drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4485         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4486         
4487         show_verbose_time_cursor (drag_info.last_frame_position, 10);
4488 }
4489
4490 void
4491 Editor::cancel_selection ()
4492 {
4493         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4494                 (*i)->hide_selection ();
4495         }
4496         selection->clear ();
4497         clicked_selection = 0;
4498 }       
4499
4500 void
4501 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4502 {
4503         nframes64_t start = 0;
4504         nframes64_t end = 0;
4505
4506         if (session == 0) {
4507                 return;
4508         }
4509
4510         drag_info.item = item;
4511         drag_info.motion_callback = &Editor::drag_selection;
4512         drag_info.finished_callback = &Editor::end_selection_op;
4513
4514         selection_op = op;
4515
4516         switch (op) {
4517         case CreateSelection:
4518                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4519                         drag_info.copy = true;
4520                 } else {
4521                         drag_info.copy = false;
4522                 }
4523                 start_grab (event, selector_cursor);
4524                 break;
4525
4526         case SelectionStartTrim:
4527                 if (clicked_axisview) {
4528                         clicked_axisview->order_selection_trims (item, true);
4529                 } 
4530                 start_grab (event, trimmer_cursor);
4531                 start = selection->time[clicked_selection].start;
4532                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4533                 break;
4534                 
4535         case SelectionEndTrim:
4536                 if (clicked_axisview) {
4537                         clicked_axisview->order_selection_trims (item, false);
4538                 }
4539                 start_grab (event, trimmer_cursor);
4540                 end = selection->time[clicked_selection].end;
4541                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
4542                 break;
4543
4544         case SelectionMove:
4545                 start = selection->time[clicked_selection].start;
4546                 start_grab (event);
4547                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4548                 break;
4549         }
4550
4551         if (selection_op == SelectionMove) {
4552                 show_verbose_time_cursor(start, 10);    
4553         } else {
4554                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4555         }
4556 }
4557
4558 void
4559 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4560 {
4561         nframes64_t start = 0;
4562         nframes64_t end = 0;
4563         nframes64_t length;
4564         nframes64_t pending_position;
4565
4566         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4567                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4568         } else {
4569                 pending_position = 0;
4570         }
4571         
4572         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4573                 snap_to (pending_position);
4574         }
4575
4576         /* only alter selection if the current frame is 
4577            different from the last frame position (adjusted)
4578          */
4579         
4580         if (pending_position == drag_info.last_pointer_frame) return;
4581         
4582         switch (selection_op) {
4583         case CreateSelection:
4584                 
4585                 if (drag_info.first_move) {
4586                         snap_to (drag_info.grab_frame);
4587                 }
4588                 
4589                 if (pending_position < drag_info.grab_frame) {
4590                         start = pending_position;
4591                         end = drag_info.grab_frame;
4592                 } else {
4593                         end = pending_position;
4594                         start = drag_info.grab_frame;
4595                 }
4596                 
4597                 /* first drag: Either add to the selection
4598                    or create a new selection->
4599                 */
4600                 
4601                 if (drag_info.first_move) {
4602                         
4603                         begin_reversible_command (_("range selection"));
4604                         
4605                         if (drag_info.copy) {
4606                                 /* adding to the selection */
4607                                 clicked_selection = selection->add (start, end);
4608                                 drag_info.copy = false;
4609                         } else {
4610                                 /* new selection-> */
4611                                 clicked_selection = selection->set (clicked_axisview, start, end);
4612                         }
4613                 } 
4614                 break;
4615                 
4616         case SelectionStartTrim:
4617                 
4618                 if (drag_info.first_move) {
4619                         begin_reversible_command (_("trim selection start"));
4620                 }
4621                 
4622                 start = selection->time[clicked_selection].start;
4623                 end = selection->time[clicked_selection].end;
4624
4625                 if (pending_position > end) {
4626                         start = end;
4627                 } else {
4628                         start = pending_position;
4629                 }
4630                 break;
4631                 
4632         case SelectionEndTrim:
4633                 
4634                 if (drag_info.first_move) {
4635                         begin_reversible_command (_("trim selection end"));
4636                 }
4637                 
4638                 start = selection->time[clicked_selection].start;
4639                 end = selection->time[clicked_selection].end;
4640
4641                 if (pending_position < start) {
4642                         end = start;
4643                 } else {
4644                         end = pending_position;
4645                 }
4646                 
4647                 break;
4648                 
4649         case SelectionMove:
4650                 
4651                 if (drag_info.first_move) {
4652                         begin_reversible_command (_("move selection"));
4653                 }
4654                 
4655                 start = selection->time[clicked_selection].start;
4656                 end = selection->time[clicked_selection].end;
4657                 
4658                 length = end - start;
4659                 
4660                 start = pending_position;
4661                 snap_to (start);
4662                 
4663                 end = start + length;
4664                 
4665                 break;
4666         }
4667         
4668         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4669                 start_canvas_autoscroll (1, 0);
4670         }
4671
4672         if (start != end) {
4673                 selection->replace (clicked_selection, start, end);
4674         }
4675
4676         drag_info.last_pointer_frame = pending_position;
4677         drag_info.first_move = false;
4678
4679         if (selection_op == SelectionMove) {
4680                 show_verbose_time_cursor(start, 10);    
4681         } else {
4682                 show_verbose_time_cursor(pending_position, 10); 
4683         }
4684 }
4685
4686 void
4687 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4688 {
4689         if (!drag_info.first_move) {
4690                 drag_selection (item, event);
4691                 /* XXX this is not object-oriented programming at all. ick */
4692                 if (selection->time.consolidate()) {
4693                         selection->TimeChanged ();
4694                 }
4695                 commit_reversible_command ();
4696         } else {
4697                 /* just a click, no pointer movement.*/
4698
4699                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4700
4701                         selection->clear_time();
4702
4703                 } 
4704         }
4705
4706         /* XXX what happens if its a music selection? */
4707         session->set_audio_range (selection->time);
4708         stop_canvas_autoscroll ();
4709 }
4710
4711 void
4712 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4713 {
4714         double speed = 1.0;
4715         TimeAxisView* tvp = clicked_axisview;
4716         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4717
4718         if (tv && tv->is_track()) {
4719                 speed = tv->get_diskstream()->speed();
4720         }
4721         
4722         nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4723         nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4724         nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4725
4726         //drag_info.item = clicked_regionview->get_name_highlight();
4727         drag_info.item = item;
4728         drag_info.motion_callback = &Editor::trim_motion_callback;
4729         drag_info.finished_callback = &Editor::trim_finished_callback;
4730
4731         start_grab (event, trimmer_cursor);
4732         
4733         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4734                 trim_op = ContentsTrim;
4735         } else {
4736                 /* These will get overridden for a point trim.*/
4737                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4738                         /* closer to start */
4739                         trim_op = StartTrim;
4740                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4741                         /* closer to end */
4742                         trim_op = EndTrim;
4743                 }
4744         }
4745
4746         switch (trim_op) {
4747         case StartTrim:
4748                 show_verbose_time_cursor(region_start, 10);     
4749                 break;
4750         case EndTrim:
4751                 show_verbose_time_cursor(region_end, 10);       
4752                 break;
4753         case ContentsTrim:
4754                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4755                 break;
4756         }
4757 }
4758
4759 void
4760 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4761 {
4762         RegionView* rv = clicked_regionview;
4763         nframes64_t frame_delta = 0;
4764         bool left_direction;
4765         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4766
4767         /* snap modifier works differently here..
4768            its' current state has to be passed to the 
4769            various trim functions in order to work properly 
4770         */ 
4771
4772         double speed = 1.0;
4773         TimeAxisView* tvp = clicked_axisview;
4774         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4775         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4776
4777         if (tv && tv->is_track()) {
4778                 speed = tv->get_diskstream()->speed();
4779         }
4780         
4781         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4782                 left_direction = true;
4783         } else {
4784                 left_direction = false;
4785         }
4786
4787         if (obey_snap) {
4788                 snap_to (drag_info.current_pointer_frame);
4789         }
4790
4791         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4792                 return;
4793         }
4794
4795         if (drag_info.first_move) {
4796         
4797                 string trim_type;
4798
4799                 switch (trim_op) {
4800                 case StartTrim:
4801                         trim_type = "Region start trim";
4802                         break;
4803                 case EndTrim:
4804                         trim_type = "Region end trim";
4805                         break;
4806                 case ContentsTrim:
4807                         trim_type = "Region content trim";
4808                         break;
4809                 }
4810
4811                 begin_reversible_command (trim_type);
4812
4813                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4814                         (*i)->fake_set_opaque(false);
4815                         (*i)->region()->freeze ();
4816                 
4817                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4818                         if (arv)
4819                                 arv->temporarily_hide_envelope ();
4820
4821                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4822                         insert_result = motion_frozen_playlists.insert (pl);
4823                         if (insert_result.second) {
4824                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4825                                 pl->freeze();
4826                         }
4827                 }
4828         }
4829
4830         if (left_direction) {
4831                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4832         } else {
4833                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4834         }
4835
4836         switch (trim_op) {              
4837         case StartTrim:
4838                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4839                         break;
4840                 } else {
4841                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4842                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4843                         }
4844                         break;
4845                 }
4846                 
4847         case EndTrim:
4848                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4849                         break;
4850                 } else {
4851                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4852                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4853                         }
4854                         break;
4855                 }
4856                 
4857         case ContentsTrim:
4858                 {
4859                         bool swap_direction = false;
4860
4861                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4862                                 swap_direction = true;
4863                         }
4864                         
4865                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4866                              i != selection->regions.by_layer().end(); ++i)
4867                         {
4868                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4869                         }
4870                 }
4871                 break;
4872         }
4873
4874         switch (trim_op) {
4875         case StartTrim:
4876                 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);   
4877                 break;
4878         case EndTrim:
4879                 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); 
4880                 break;
4881         case ContentsTrim:
4882                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4883                 break;
4884         }
4885
4886         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4887         drag_info.first_move = false;
4888 }
4889
4890 void
4891 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4892 {
4893         boost::shared_ptr<Region> region (rv.region());
4894
4895         if (region->locked()) {
4896                 return;
4897         }
4898
4899         nframes64_t new_bound;
4900
4901         double speed = 1.0;
4902         TimeAxisView* tvp = clicked_axisview;
4903         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4904
4905         if (tv && tv->is_track()) {
4906                 speed = tv->get_diskstream()->speed();
4907         }
4908         
4909         if (left_direction) {
4910                 if (swap_direction) {
4911                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4912                 } else {
4913                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4914                 }
4915         } else {
4916                 if (swap_direction) {
4917                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4918                 } else {
4919                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4920                 }
4921         }
4922
4923         if (obey_snap) {
4924                 snap_to (new_bound);
4925         }
4926         region->trim_start ((nframes64_t) (new_bound * speed), this);   
4927         rv.region_changed (StartChanged);
4928 }
4929
4930 void
4931 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4932 {
4933         boost::shared_ptr<Region> region (rv.region()); 
4934
4935         if (region->locked()) {
4936                 return;
4937         }
4938
4939         nframes64_t new_bound;
4940
4941         double speed = 1.0;
4942         TimeAxisView* tvp = clicked_axisview;
4943         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4944
4945         if (tv && tv->is_track()) {
4946                 speed = tv->get_diskstream()->speed();
4947         }
4948         
4949         if (left_direction) {
4950                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4951         } else {
4952                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4953         }
4954
4955         if (obey_snap) {
4956                 snap_to (new_bound, (left_direction ? 0 : 1));  
4957         }
4958
4959         region->trim_front ((nframes64_t) (new_bound * speed), this);
4960
4961         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4962 }
4963
4964 void
4965 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4966 {
4967         boost::shared_ptr<Region> region (rv.region());
4968
4969         if (region->locked()) {
4970                 return;
4971         }
4972
4973         nframes64_t new_bound;
4974
4975         double speed = 1.0;
4976         TimeAxisView* tvp = clicked_axisview;
4977         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4978
4979         if (tv && tv->is_track()) {
4980                 speed = tv->get_diskstream()->speed();
4981         }
4982         
4983         if (left_direction) {
4984                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
4985         } else {
4986                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
4987         }
4988
4989         if (obey_snap) {
4990                 snap_to (new_bound);
4991         }
4992         region->trim_end ((nframes64_t) (new_bound * speed), this);
4993         rv.region_changed (LengthChanged);
4994 }
4995         
4996 void
4997 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4998 {
4999         if (!drag_info.first_move) {
5000                 trim_motion_callback (item, event);
5001                 
5002                 if (!selection->selected (clicked_regionview)) {
5003                         thaw_region_after_trim (*clicked_regionview);           
5004                 } else {
5005                         
5006                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5007                              i != selection->regions.by_layer().end(); ++i)
5008                         {
5009                                 thaw_region_after_trim (**i);
5010                                 (*i)->fake_set_opaque (true);
5011                         }
5012                 }
5013                 
5014                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5015                         (*p)->thaw ();
5016                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5017                 }
5018                 
5019                 motion_frozen_playlists.clear ();
5020
5021                 commit_reversible_command();
5022         } else {
5023                 /* no mouse movement */
5024                 point_trim (event);
5025         }
5026 }
5027
5028 void
5029 Editor::point_trim (GdkEvent* event)
5030 {
5031         RegionView* rv = clicked_regionview;
5032         nframes64_t new_bound = drag_info.current_pointer_frame;
5033
5034         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5035                 snap_to (new_bound);
5036         }
5037
5038         /* Choose action dependant on which button was pressed */
5039         switch (event->button.button) {
5040         case 1:
5041                 trim_op = StartTrim;
5042                 begin_reversible_command (_("Start point trim"));
5043
5044                 if (selection->selected (rv)) {
5045
5046                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5047                              i != selection->regions.by_layer().end(); ++i)
5048                         {
5049                                 if (!(*i)->region()->locked()) {
5050                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5051                                         XMLNode &before = pl->get_state();
5052                                         (*i)->region()->trim_front (new_bound, this);   
5053                                         XMLNode &after = pl->get_state();
5054                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5055                                 }
5056                         }
5057
5058                 } else {
5059
5060                         if (!rv->region()->locked()) {
5061                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5062                                 XMLNode &before = pl->get_state();
5063                                 rv->region()->trim_front (new_bound, this);     
5064                                 XMLNode &after = pl->get_state();
5065                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5066                         }
5067                 }
5068
5069                 commit_reversible_command();
5070         
5071                 break;
5072         case 2:
5073                 trim_op = EndTrim;
5074                 begin_reversible_command (_("End point trim"));
5075
5076                 if (selection->selected (rv)) {
5077                         
5078                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5079                         {
5080                                 if (!(*i)->region()->locked()) {
5081                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5082                                         XMLNode &before = pl->get_state();
5083                                         (*i)->region()->trim_end (new_bound, this);
5084                                         XMLNode &after = pl->get_state();
5085                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5086                                 }
5087                         }
5088
5089                 } else {
5090
5091                         if (!rv->region()->locked()) {
5092                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5093                                 XMLNode &before = pl->get_state();
5094                                 rv->region()->trim_end (new_bound, this);
5095                                 XMLNode &after = pl->get_state();
5096                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5097                         }
5098                 }
5099
5100                 commit_reversible_command();
5101         
5102                 break;
5103         default:
5104                 break;
5105         }
5106 }
5107
5108 void
5109 Editor::thaw_region_after_trim (RegionView& rv)
5110 {
5111         boost::shared_ptr<Region> region (rv.region());
5112
5113         if (region->locked()) {
5114                 return;
5115         }
5116
5117         region->thaw (_("trimmed region"));
5118         XMLNode &after = region->playlist()->get_state();
5119         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5120
5121         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5122         if (arv)
5123                 arv->unhide_envelope ();
5124 }
5125
5126 void
5127 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5128 {
5129         Marker* marker;
5130         bool is_start;
5131
5132         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5133                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5134                 /*NOTREACHED*/
5135         }
5136
5137         Location* location = find_location_from_marker (marker, is_start);      
5138         location->set_hidden (true, this);
5139 }
5140
5141
5142 void
5143 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5144 {
5145         if (session == 0) {
5146                 return;
5147         }
5148
5149         drag_info.item = item;
5150         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5151         drag_info.finished_callback = &Editor::end_range_markerbar_op;
5152
5153         range_marker_op = op;
5154
5155         if (!temp_location) {
5156                 temp_location = new Location;
5157         }
5158         
5159         switch (op) {
5160         case CreateRangeMarker:
5161         case CreateTransportMarker:
5162         case CreateCDMarker:
5163         
5164                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5165                         drag_info.copy = true;
5166                 } else {
5167                         drag_info.copy = false;
5168                 }
5169                 start_grab (event, selector_cursor);
5170                 break;
5171         }
5172
5173         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5174         
5175 }
5176
5177 void
5178 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5179 {
5180         nframes64_t start = 0;
5181         nframes64_t end = 0;
5182         ArdourCanvas::SimpleRect *crect;
5183
5184         switch (range_marker_op) {
5185         case CreateRangeMarker:
5186                 crect = range_bar_drag_rect;
5187                 break;
5188         case CreateTransportMarker:
5189                 crect = transport_bar_drag_rect;
5190                 break;
5191         case CreateCDMarker:
5192                 crect = cd_marker_bar_drag_rect;
5193                 break;
5194         default:
5195                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5196                 return;
5197                 break;
5198         }
5199         
5200         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5201                 snap_to (drag_info.current_pointer_frame);
5202         }
5203
5204         /* only alter selection if the current frame is 
5205            different from the last frame position.
5206          */
5207         
5208         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5209         
5210         switch (range_marker_op) {
5211         case CreateRangeMarker:
5212         case CreateTransportMarker:
5213         case CreateCDMarker:
5214                 if (drag_info.first_move) {
5215                         snap_to (drag_info.grab_frame);
5216                 }
5217                 
5218                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5219                         start = drag_info.current_pointer_frame;
5220                         end = drag_info.grab_frame;
5221                 } else {
5222                         end = drag_info.current_pointer_frame;
5223                         start = drag_info.grab_frame;
5224                 }
5225                 
5226                 /* first drag: Either add to the selection
5227                    or create a new selection.
5228                 */
5229                 
5230                 if (drag_info.first_move) {
5231                         
5232                         temp_location->set (start, end);
5233                         
5234                         crect->show ();
5235
5236                         update_marker_drag_item (temp_location);
5237                         range_marker_drag_rect->show();
5238                         //range_marker_drag_rect->raise_to_top();
5239                         
5240                 } 
5241                 break;          
5242         }
5243         
5244         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5245                 start_canvas_autoscroll (1, 0);
5246         }
5247         
5248         if (start != end) {
5249                 temp_location->set (start, end);
5250
5251                 double x1 = frame_to_pixel (start);
5252                 double x2 = frame_to_pixel (end);
5253                 crect->property_x1() = x1;
5254                 crect->property_x2() = x2;
5255
5256                 update_marker_drag_item (temp_location);
5257         }
5258
5259         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5260         drag_info.first_move = false;
5261
5262         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5263         
5264 }
5265
5266 void
5267 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5268 {
5269         Location * newloc = 0;
5270         string rangename;
5271         int flags;
5272         
5273         if (!drag_info.first_move) {
5274                 drag_range_markerbar_op (item, event);
5275
5276                 switch (range_marker_op) {
5277                 case CreateRangeMarker:
5278                 case CreateCDMarker:
5279                     {
5280                         begin_reversible_command (_("new range marker"));
5281                         XMLNode &before = session->locations()->get_state();
5282                         session->locations()->next_available_name(rangename,"unnamed");
5283                         if (range_marker_op == CreateCDMarker) {
5284                                 flags =  Location::IsRangeMarker|Location::IsCDMarker;
5285                                 cd_marker_bar_drag_rect->hide();
5286                         }
5287                         else {
5288                                 flags =  Location::IsRangeMarker;
5289                                 range_bar_drag_rect->hide();
5290                         }
5291                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5292                         session->locations()->add (newloc, true);
5293                         XMLNode &after = session->locations()->get_state();
5294                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5295                         commit_reversible_command ();
5296                         
5297                         range_marker_drag_rect->hide();
5298                         break;
5299                     }
5300
5301                 case CreateTransportMarker:
5302                         // popup menu to pick loop or punch
5303                         new_transport_marker_context_menu (&event->button, item);
5304                         
5305                         break;
5306                 }
5307         } else {
5308                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5309
5310                 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5311
5312                         nframes64_t start;
5313                         nframes64_t end;
5314
5315                         start = session->locations()->first_mark_before (drag_info.grab_frame);
5316                         end = session->locations()->first_mark_after (drag_info.grab_frame);
5317                         
5318                         if (end == max_frames) {
5319                                 end = session->current_end_frame ();
5320                         }
5321
5322                         if (start == 0) {
5323                                 start = session->current_start_frame ();
5324                         }
5325
5326                         switch (mouse_mode) {
5327                         case MouseObject:
5328                                 /* find the two markers on either side and then make the selection from it */
5329                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5330                                 break;
5331
5332                         case MouseRange:
5333                                 /* find the two markers on either side of the click and make the range out of it */
5334                                 selection->set (0, start, end);
5335                                 break;
5336
5337                         default:
5338                                 break;
5339                         }
5340                 } 
5341         }
5342
5343         stop_canvas_autoscroll ();
5344 }
5345
5346
5347
5348 void
5349 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5350 {
5351         drag_info.item = item;
5352         drag_info.motion_callback = &Editor::drag_mouse_zoom;
5353         drag_info.finished_callback = &Editor::end_mouse_zoom;
5354
5355         start_grab (event, zoom_cursor);
5356
5357         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5358 }
5359
5360 void
5361 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5362 {
5363         nframes64_t start;
5364         nframes64_t end;
5365
5366         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5367                 snap_to (drag_info.current_pointer_frame);
5368                 
5369                 if (drag_info.first_move) {
5370                         snap_to (drag_info.grab_frame);
5371                 }
5372         }
5373                 
5374         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5375
5376         /* base start and end on initial click position */
5377         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5378                 start = drag_info.current_pointer_frame;
5379                 end = drag_info.grab_frame;
5380         } else {
5381                 end = drag_info.current_pointer_frame;
5382                 start = drag_info.grab_frame;
5383         }
5384         
5385         if (start != end) {
5386
5387                 if (drag_info.first_move) {
5388                         zoom_rect->show();
5389                         zoom_rect->raise_to_top();
5390                 }
5391
5392                 reposition_zoom_rect(start, end);
5393
5394                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5395                 drag_info.first_move = false;
5396
5397                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5398         }
5399 }
5400
5401 void
5402 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5403 {
5404         if (!drag_info.first_move) {
5405                 drag_mouse_zoom (item, event);
5406                 
5407                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5408                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5409                 } else {
5410                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5411                 }               
5412         } else {
5413                 temporal_zoom_to_frame (false, drag_info.grab_frame);
5414                 /*
5415                 temporal_zoom_step (false);
5416                 center_screen (drag_info.grab_frame);
5417                 */
5418         }
5419
5420         zoom_rect->hide();
5421 }
5422
5423 void
5424 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5425 {
5426         double x1 = frame_to_pixel (start);
5427         double x2 = frame_to_pixel (end);
5428         double y2 = full_canvas_height - 1.0;
5429
5430         zoom_rect->property_x1() = x1;
5431         zoom_rect->property_y1() = 1.0;
5432         zoom_rect->property_x2() = x2;
5433         zoom_rect->property_y2() = y2;
5434 }
5435
5436 void
5437 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5438 {
5439         drag_info.item = item;
5440         drag_info.motion_callback = &Editor::drag_rubberband_select;
5441         drag_info.finished_callback = &Editor::end_rubberband_select;
5442
5443         start_grab (event, cross_hair_cursor);
5444
5445         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5446 }
5447
5448 void
5449 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5450 {
5451         nframes64_t start;
5452         nframes64_t end;
5453         double y1;
5454         double y2;
5455
5456         /* use a bigger drag threshold than the default */
5457
5458         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5459                 return;
5460         }
5461
5462         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5463                 if (drag_info.first_move) {
5464                         snap_to (drag_info.grab_frame);
5465                 } 
5466                 snap_to (drag_info.current_pointer_frame);
5467         }
5468
5469         /* base start and end on initial click position */
5470
5471         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5472                 start = drag_info.current_pointer_frame;
5473                 end = drag_info.grab_frame;
5474         } else {
5475                 end = drag_info.current_pointer_frame;
5476                 start = drag_info.grab_frame;
5477         }
5478
5479         if (drag_info.current_pointer_y < drag_info.grab_y) {
5480                 y1 = drag_info.current_pointer_y;
5481                 y2 = drag_info.grab_y;
5482         } else {
5483                 y2 = drag_info.current_pointer_y;
5484                 y1 = drag_info.grab_y;
5485         }
5486
5487         
5488         if (start != end || y1 != y2) {
5489
5490                 double x1 = frame_to_pixel (start);
5491                 double x2 = frame_to_pixel (end);
5492                 
5493                 rubberband_rect->property_x1() = x1;
5494                 rubberband_rect->property_y1() = y1;
5495                 rubberband_rect->property_x2() = x2;
5496                 rubberband_rect->property_y2() = y2;
5497
5498                 rubberband_rect->show();
5499                 rubberband_rect->raise_to_top();
5500                 
5501                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5502                 drag_info.first_move = false;
5503
5504                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5505         }
5506 }
5507
5508 void
5509 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5510 {
5511         if (!drag_info.first_move) {
5512
5513                 drag_rubberband_select (item, event);
5514
5515                 double y1,y2;
5516                 if (drag_info.current_pointer_y < drag_info.grab_y) {
5517                         y1 = drag_info.current_pointer_y;
5518                         y2 = drag_info.grab_y;
5519                 } else {
5520                         y2 = drag_info.current_pointer_y;
5521                         y1 = drag_info.grab_y;
5522                 }
5523
5524
5525                 Selection::Operation op = Keyboard::selection_type (event->button.state);
5526                 bool commit;
5527
5528                 begin_reversible_command (_("rubberband selection"));
5529
5530                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5531                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5532                 } else {
5533                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5534                 }               
5535
5536                 if (commit) {
5537                         commit_reversible_command ();
5538                 }
5539                 
5540         } else {
5541                 selection->clear_tracks();
5542                 selection->clear_regions();
5543                 selection->clear_points ();
5544                 selection->clear_lines ();
5545         }
5546
5547         rubberband_rect->hide();
5548 }
5549
5550
5551 gint
5552 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5553 {
5554         using namespace Gtkmm2ext;
5555
5556         ArdourPrompter prompter (false);
5557
5558         prompter.set_prompt (_("Name for region:"));
5559         prompter.set_initial_text (clicked_regionview->region()->name());
5560         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5561         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5562         prompter.show_all ();
5563         switch (prompter.run ()) {
5564         case Gtk::RESPONSE_ACCEPT:
5565                 string str;
5566                 prompter.get_result(str);
5567                 if (str.length()) {
5568                         clicked_regionview->region()->set_name (str);
5569                 }
5570                 break;
5571         }
5572         return true;
5573 }
5574
5575 void
5576 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5577 {
5578         drag_info.item = item;
5579         drag_info.motion_callback = &Editor::time_fx_motion;
5580         drag_info.finished_callback = &Editor::end_time_fx;
5581
5582         start_grab (event);
5583
5584         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5585 }
5586
5587 void
5588 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5589 {
5590         RegionView* rv = clicked_regionview;
5591
5592         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5593                 snap_to (drag_info.current_pointer_frame);
5594         }
5595
5596         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5597                 return;
5598         }
5599
5600         if (drag_info.current_pointer_frame > rv->region()->position()) {
5601                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5602         }
5603
5604         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5605         drag_info.first_move = false;
5606
5607         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5608 }
5609
5610 void
5611 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5612 {
5613         clicked_regionview->get_time_axis_view().hide_timestretch ();
5614
5615         if (drag_info.first_move) {
5616                 return;
5617         }
5618
5619         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5620                 /* backwards drag of the left edge - not usable */
5621                 return;
5622         }
5623         
5624         nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5625
5626         float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5627         
5628 #ifndef USE_RUBBERBAND
5629         // Soundtouch uses percentage / 100 instead of normal (/ 1) 
5630         if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5631                 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5632         }
5633 #endif  
5634         
5635         begin_reversible_command (_("timestretch"));
5636         
5637         // XXX how do timeFX on multiple regions ?
5638         
5639         RegionSelection rs;
5640         rs.add (clicked_regionview);
5641
5642         if (time_stretch (rs, percentage) == 0) {
5643                 session->commit_reversible_command ();
5644         }
5645 }
5646
5647 void
5648 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5649 {
5650         /* no brushing without a useful snap setting */
5651
5652         switch (snap_mode) {
5653         case SnapMagnetic:
5654                 return; /* can't work because it allows region to be placed anywhere */
5655         default:
5656                 break; /* OK */
5657         }
5658
5659         switch (snap_type) {
5660         case SnapToMark:
5661                 return;
5662
5663         default:
5664                 break;
5665         }
5666
5667         /* don't brush a copy over the original */
5668         
5669         if (pos == rv->region()->position()) {
5670                 return;
5671         }
5672
5673         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5674
5675         if (rtv == 0 || !rtv->is_track()) {
5676                 return;
5677         }
5678
5679         boost::shared_ptr<Playlist> playlist = rtv->playlist();
5680         double speed = rtv->get_diskstream()->speed();
5681         
5682         XMLNode &before = playlist->get_state();
5683         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5684         XMLNode &after = playlist->get_state();
5685         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5686         
5687         // playlist is frozen, so we have to update manually
5688         
5689         playlist->Modified(); /* EMIT SIGNAL */
5690 }
5691
5692 gint
5693 Editor::track_height_step_timeout ()
5694 {
5695         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5696                 current_stepping_trackview = 0;
5697                 return false;
5698         }
5699         return true;
5700 }
5701