fix dragging of region gain line, affected by coordinate system change caused by...
[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 cx = drag_info.current_pointer_x;
3145         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3146
3147         // calculate zero crossing point. back off by .01 to stay on the
3148         // positive side of zero
3149         double _unused = 0;
3150         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3151         line->parent_group().i2w(_unused, zero_gain_y);
3152
3153         // make sure we hit zero when passing through
3154         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3155                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3156                 cy = zero_gain_y;
3157         }
3158
3159         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3160
3161         cy = max (0.0, cy);
3162         cy = min ((double) line->height(), cy);
3163
3164         double fraction = 1.0 - (cy / line->height());
3165
3166         bool push;
3167
3168         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3169                 push = false;
3170         } else {
3171                 push = true;
3172         }
3173
3174         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3175         
3176         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3177 }
3178
3179 void
3180 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3181 {
3182         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3183         line_drag_motion_callback (item, event);
3184         line->end_drag (0);
3185 }
3186
3187 void
3188 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3189 {
3190         if (selection->regions.empty() || clicked_regionview == 0) {
3191                 return;
3192         }
3193         _region_motion_group->raise_to_top ();
3194         drag_info.copy = false;
3195         drag_info.item = item;
3196         drag_info.data = clicked_regionview;
3197
3198         if (Config->get_edit_mode() == Splice) {
3199                 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3200                 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3201         } else {
3202                 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3203                 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3204         }
3205
3206         start_grab (event);
3207
3208         double speed = 1.0;
3209         TimeAxisView* tvp = clicked_axisview;
3210         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3211
3212         if (tv && tv->is_track()) {
3213                 speed = tv->get_diskstream()->speed();
3214         }
3215         
3216         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3217         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3218         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3219         drag_info.dest_trackview = drag_info.source_trackview;
3220         // we want a move threshold
3221         drag_info.want_move_threshold = true;
3222         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3223
3224         begin_reversible_command (_("move region(s)"));
3225
3226         /* sync the canvas to what we think is its current state */
3227         track_canvas->update_now();
3228 }
3229
3230 void
3231 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3232 {
3233         drag_info.copy = false;
3234         drag_info.item = item;
3235         drag_info.data = clicked_axisview;
3236         drag_info.source_trackview = clicked_axisview;
3237         drag_info.dest_trackview = drag_info.source_trackview;
3238         drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3239         drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3240
3241         start_grab (event);
3242 }
3243
3244 void
3245 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3246 {
3247         if (selection->regions.empty() || clicked_regionview == 0) {
3248                 return;
3249         }
3250         _region_motion_group->raise_to_top ();
3251         drag_info.copy = true;
3252         drag_info.item = item;
3253         drag_info.data = clicked_regionview;    
3254
3255         start_grab(event);
3256
3257         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3258         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3259         double speed = 1.0;
3260
3261         if (rtv && rtv->is_track()) {
3262                 speed = rtv->get_diskstream()->speed();
3263         }
3264         
3265         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3266         drag_info.dest_trackview = drag_info.source_trackview;
3267         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3268         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3269         // we want a move threshold
3270         drag_info.want_move_threshold = true;
3271         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3272         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3273         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3274 }
3275
3276 void
3277 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3278 {
3279         if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3280                 return;
3281         }
3282
3283         drag_info.copy = false;
3284         drag_info.item = item;
3285         drag_info.data = clicked_regionview;
3286         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3287         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3288
3289         start_grab (event);
3290
3291         double speed = 1.0;
3292         TimeAxisView* tvp = clicked_axisview;
3293         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3294
3295         if (tv && tv->is_track()) {
3296                 speed = tv->get_diskstream()->speed();
3297         }
3298         
3299         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3300         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3301         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3302         drag_info.dest_trackview = drag_info.source_trackview;
3303         // we want a move threshold
3304         drag_info.want_move_threshold = true;
3305         drag_info.brushing = true;
3306         
3307         begin_reversible_command (_("Drag region brush"));
3308 }
3309
3310 void
3311 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3312 {
3313         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3314
3315                 drag_info.want_move_threshold = false; // don't copy again
3316
3317                 /* duplicate the regionview(s) and region(s) */
3318
3319                 vector<RegionView*> new_regionviews;
3320                 
3321                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3322                         RegionView* rv;
3323                         RegionView* nrv;
3324
3325                         rv = (*i);
3326                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3327                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3328                         
3329                         const boost::shared_ptr<const Region> original = rv->region();
3330                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3331
3332                         if (arv) {
3333                                 boost::shared_ptr<AudioRegion> audioregion_copy
3334                                         = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3335                                 nrv = new AudioRegionView (*arv, audioregion_copy);
3336                         } else if (mrv) {
3337                                 boost::shared_ptr<MidiRegion> midiregion_copy
3338                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3339                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
3340                         } else {
3341                                 continue;
3342                         }
3343
3344                         nrv->get_canvas_group()->show ();
3345                         new_regionviews.push_back (nrv);
3346                 }
3347
3348                 if (new_regionviews.empty()) {
3349                         return;
3350                 }
3351
3352                 /* reset selection to new regionviews. This will not set selection visual status for 
3353                    these regionviews since they don't belong to a track, so do that by hand too.
3354                  */
3355
3356                 selection->set (new_regionviews);
3357
3358                 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3359                         (*i)->set_selected (true);
3360                 }
3361
3362                 /* reset drag_info data to reflect the fact that we are dragging the copies */
3363                 
3364                 drag_info.data = new_regionviews.front();
3365
3366                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3367                 /* 
3368                    sync the canvas to what we think is its current state 
3369                    without it, the canvas seems to 
3370                    "forget" to update properly after the upcoming reparent() 
3371                    ..only if the mouse is in rapid motion at the time of the grab. 
3372                    something to do with regionview creation raking so long?
3373                  */
3374                 track_canvas->update_now();
3375         }
3376 }
3377
3378 bool
3379 Editor::check_region_drag_possible (RouteTimeAxisView** tv)
3380 {
3381         /* Which trackview is this ? */
3382
3383         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
3384         (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
3385
3386         /* The region motion is only processed if the pointer is over
3387            an audio track.
3388         */
3389         
3390         if (!(*tv) || !(*tv)->is_track()) {
3391                 /* To make sure we hide the verbose canvas cursor when the mouse is 
3392                    not held over and audiotrack. 
3393                 */
3394                 hide_verbose_canvas_cursor ();
3395                 return false;
3396         }
3397
3398         return true;
3399 }
3400
3401 struct RegionSelectionByPosition {
3402     bool operator() (RegionView*a, RegionView* b) {
3403             return a->region()->position () < b->region()->position();
3404     }
3405 };
3406
3407 void
3408 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3409 {
3410         RouteTimeAxisView* tv;
3411         
3412         if (!check_region_drag_possible (&tv)) {
3413                 return;
3414         }
3415
3416         if (!drag_info.move_threshold_passed) {
3417                 return;
3418         }
3419
3420         int dir;
3421
3422         if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3423                 dir = 1;
3424         } else {
3425                 dir = -1;
3426         }
3427
3428         RegionSelection copy (selection->regions);
3429
3430         RegionSelectionByPosition cmp;
3431         copy.sort (cmp);
3432
3433         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3434
3435                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3436
3437                 if (!atv) {
3438                         continue;
3439                 }
3440
3441                 boost::shared_ptr<Playlist> playlist;
3442
3443                 if ((playlist = atv->playlist()) == 0) {
3444                         continue;
3445                 }
3446
3447                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3448                         continue;
3449                 } 
3450
3451                 if (dir > 0) {
3452                         if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3453                                 continue;
3454                         }
3455                 } else {
3456                         if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3457                                 continue;
3458                         }
3459                 }
3460
3461                 
3462                 playlist->shuffle ((*i)->region(), dir);
3463
3464                 drag_info.grab_x = drag_info.current_pointer_x;
3465         }
3466 }
3467
3468 void
3469 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3470 {
3471 }
3472
3473 void
3474 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3475 {
3476         double x_delta;
3477         double y_delta = 0;
3478         RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data); 
3479         nframes64_t pending_region_position = 0;
3480         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
3481         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
3482         bool clamp_y_axis = false;
3483         vector<int32_t>  height_list(512) ;
3484         vector<int32_t>::iterator j;
3485         RouteTimeAxisView* tv;
3486
3487         possibly_copy_regions_during_grab (event);
3488
3489         if (!check_region_drag_possible (&tv)) {
3490                 return;
3491         }
3492
3493         original_pointer_order = drag_info.dest_trackview->order;
3494         
3495         /************************************************************
3496              Y-Delta Computation
3497         ************************************************************/   
3498
3499         if (drag_info.brushing) {
3500                 clamp_y_axis = true;
3501                 pointer_y_span = 0;
3502                 goto y_axis_done;
3503         }
3504
3505         if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
3506
3507                 int32_t children = 0, numtracks = 0;
3508                 // XXX hard coding track limit, oh my, so very very bad
3509                 bitset <1024> tracks (0x00);
3510                 /* get a bitmask representing the visible tracks */
3511
3512                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3513                         TimeAxisView *tracklist_timeview;
3514                         tracklist_timeview = (*i);
3515                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
3516                         TimeAxisView::Children children_list;
3517               
3518                         /* zeroes are audio tracks. ones are other types. */
3519               
3520                         if (!rtv2->hidden()) {
3521                                 
3522                                 if (visible_y_high < rtv2->order) {
3523                                         visible_y_high = rtv2->order;
3524                                 }
3525                                 if (visible_y_low > rtv2->order) {
3526                                         visible_y_low = rtv2->order;
3527                                 }
3528                 
3529                                 if (!rtv2->is_track()) {                                  
3530                                         tracks = tracks |= (0x01 << rtv2->order);
3531                                 }
3532         
3533                                 height_list[rtv2->order] = (*i)->current_height();
3534                                 children = 1;
3535
3536                                 if ((children_list = rtv2->get_child_list()).size() > 0) {
3537                                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
3538                                                 tracks = tracks |= (0x01 << (rtv2->order + children));
3539                                                 height_list[rtv2->order + children] =  (*j)->current_height();
3540                                                 numtracks++;
3541                                                 children++;     
3542                                         }
3543                                 }
3544                                 numtracks++;        
3545                         }
3546                 }
3547                 /* find the actual span according to the canvas */
3548
3549                 canvas_pointer_y_span = pointer_y_span;
3550                 if (drag_info.dest_trackview->order >= tv->order) {
3551                         int32_t y;
3552                         for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
3553                                 if (height_list[y] == 0 ) {
3554                                         canvas_pointer_y_span--;
3555                                 }
3556                         }
3557                 } else {
3558                         int32_t y;
3559                         for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
3560                                 if (    height_list[y] == 0 ) {
3561                                         canvas_pointer_y_span++;
3562                                 }
3563                         }
3564                 }
3565
3566                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3567                         RegionView* rv2 = (*i);
3568                         double ix1, ix2, iy1, iy2;
3569                         int32_t n = 0;
3570
3571                         if (rv2->region()->locked()) {
3572                                 continue;
3573                         }
3574
3575                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3576                         rv2->get_canvas_frame()->i2w (ix1, iy1);
3577                         iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3578
3579                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3580                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3581
3582                         if (rtv2->order != original_pointer_order) {    
3583                                 /* this isn't the pointer track */      
3584
3585                                 if (canvas_pointer_y_span > 0) {
3586
3587                                         /* moving up the canvas */
3588                                         if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
3589         
3590                                                 int32_t visible_tracks = 0;
3591                                                 while (visible_tracks < canvas_pointer_y_span ) {
3592                                                         visible_tracks++;
3593                   
3594                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
3595                                                                 /* we're passing through a hidden track */
3596                                                                 n--;
3597                                                         }                 
3598                                                 }
3599                  
3600                                                 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
3601                                                         clamp_y_axis = true;
3602                                                 }
3603                     
3604                                         } else {
3605                                                 clamp_y_axis = true;
3606                                         }                 
3607                   
3608                                 } else if (canvas_pointer_y_span < 0) {
3609
3610                                         /*moving down the canvas*/
3611
3612                                         if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
3613                     
3614                     
3615                                                 int32_t visible_tracks = 0;
3616                     
3617                                                 while (visible_tracks > canvas_pointer_y_span ) {
3618                                                         visible_tracks--;
3619                       
3620                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
3621                                                                 n++;
3622                                                         }                
3623                                                 }
3624                                                 if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
3625                                                         clamp_y_axis = true;
3626                             
3627                                                 }
3628                                         } else {
3629                           
3630                                                 clamp_y_axis = true;
3631                                         }
3632                                 }               
3633                   
3634                         } else {
3635                       
3636                                 /* this is the pointer's track */
3637                                 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
3638                                         clamp_y_axis = true;
3639                                 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3640                                         clamp_y_axis = true;
3641                                 }
3642                         }             
3643                         if (clamp_y_axis) {
3644                                 break;
3645                         }
3646                 }
3647
3648         } else  if (drag_info.dest_trackview == tv) {
3649                 clamp_y_axis = true;
3650         }         
3651
3652   y_axis_done:
3653         if (!clamp_y_axis) {
3654                 drag_info.dest_trackview = tv;        
3655         }
3656           
3657         /************************************************************
3658             X DELTA COMPUTATION
3659         ************************************************************/
3660
3661         /* compute the amount of pointer motion in frames, and where
3662            the region would be if we moved it by that much.
3663         */
3664         if ( drag_info.move_threshold_passed ) {
3665
3666                 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3667
3668                         nframes64_t sync_frame;
3669                         nframes64_t sync_offset;
3670                         int32_t sync_dir;
3671
3672                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3673
3674                         sync_offset = rv->region()->sync_offset (sync_dir);
3675
3676                         /* we don't handle a sync point that lies before zero.
3677                          */
3678                         if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3679                                 sync_frame = pending_region_position + (sync_dir*sync_offset);
3680
3681                                 /* we snap if the snap modifier is not enabled.
3682                                  */
3683             
3684                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3685                                         snap_to (sync_frame);   
3686                                 }
3687             
3688                                 pending_region_position = rv->region()->adjust_to_sync (sync_frame);
3689
3690                         } else {
3691                                 pending_region_position = drag_info.last_frame_position;
3692                         }
3693             
3694                 } else {
3695                         pending_region_position = 0;
3696                 }
3697           
3698                 if (pending_region_position > max_frames - rv->region()->length()) {
3699                         pending_region_position = drag_info.last_frame_position;
3700                 }
3701
3702                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3703
3704                 bool x_move_allowed;
3705                 
3706                 if (Config->get_edit_mode() == Lock) {
3707                         if (drag_info.copy) {
3708                                 x_move_allowed = !drag_info.x_constrained;
3709                         } else {
3710                                 /* in locked edit mode, reverse the usual meaning of x_constrained */
3711                                 x_move_allowed = drag_info.x_constrained;
3712                         }
3713                 } else {
3714                         x_move_allowed = !drag_info.x_constrained;
3715                 }
3716
3717                 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3718
3719                         /* now compute the canvas unit distance we need to move the regionview
3720                            to make it appear at the new location.
3721                         */
3722
3723                         if (pending_region_position > drag_info.last_frame_position) {
3724                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3725                         } else {
3726                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3727                                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3728
3729                                         RegionView* rv2 = (*i);
3730
3731                                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3732         
3733                                         double ix1, ix2, iy1, iy2;
3734                                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3735                                         rv2->get_canvas_frame()->i2w (ix1, iy1);
3736                         
3737                                         if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
3738                                                 //      do_move = false;
3739                                                 x_delta = 0;
3740                                                 pending_region_position = drag_info.last_frame_position;
3741                                                 break;
3742                                         }
3743                                 }
3744
3745                         }
3746                 
3747                         drag_info.last_frame_position = pending_region_position;
3748
3749                 } else {
3750                         x_delta = 0;
3751                 }
3752
3753         } else {
3754                 /* threshold not passed */
3755
3756                 x_delta = 0;
3757         }
3758         
3759         /*************************************************************
3760             PREPARE TO MOVE
3761         ************************************************************/
3762
3763         if (x_delta == 0 && (pointer_y_span == 0)) {
3764                 /* haven't reached next snap point, and we're not switching
3765                    trackviews. nothing to do.
3766                 */
3767                 return;
3768         }
3769
3770         /*************************************************************
3771             MOTION                                                                    
3772         ************************************************************/
3773         bool do_move = true;
3774         if (drag_info.first_move) {
3775                 if (!drag_info.move_threshold_passed) {
3776                         do_move = false;
3777                 }
3778         }
3779
3780         if (do_move) {
3781
3782                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3783                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3784                 
3785                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3786             
3787                         RegionView* rv = (*i);
3788                         double ix1, ix2, iy1, iy2;
3789                         int32_t temp_pointer_y_span = pointer_y_span;
3790
3791                         if (rv->region()->locked()) {
3792                                 continue;
3793                         }
3794
3795                         /* get item BBox, which will be relative to parent. so we have
3796                            to query on a child, then convert to world coordinates using
3797                            the parent.
3798                         */
3799
3800                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3801                         rv->get_canvas_frame()->i2w (ix1, iy1);
3802                         
3803                         cerr << "adjust y from " << iy1 << " using "
3804                              << vertical_adjustment.get_value() << " - "
3805                              << canvas_timebars_vsize
3806                              << endl;
3807
3808                         iy1 += get_trackview_group_vertical_offset ();;
3809
3810                         if (drag_info.first_move) {
3811
3812                                 // hide any dependent views 
3813         
3814                                 rv->get_time_axis_view().hide_dependent_views (*rv);
3815
3816                                 /* 
3817                                    reparent to a non scrolling group so that we can keep the 
3818                                    region selection above all time axis views.
3819                                    reparenting means we have to move the rv as the two 
3820                                    parent groups have different coordinates.
3821                                 */
3822
3823                                 rv->get_canvas_group()->property_y() =  iy1 - 1;
3824                                 rv->get_canvas_group()->reparent(*_region_motion_group);
3825
3826                                 rv->fake_set_opaque (true);
3827                         }
3828
3829                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3830                         RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3831                         RouteTimeAxisView* temp_rtv;
3832
3833                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3834                                 y_delta = 0;
3835                                 int32_t x = 0;
3836                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3837                                         if (x == canvas_rtv->order) {
3838                                                 /* we found the track the region is on */
3839                                                 if (x != original_pointer_order) {
3840                                                         /*this isn't from the same track we're dragging from */
3841                                                         temp_pointer_y_span = canvas_pointer_y_span;
3842                                                 }                 
3843                                                 while (temp_pointer_y_span > 0) {
3844                                                         /* we're moving up canvas-wise,
3845                                                            so  we need to find the next track height
3846                                                         */
3847                                                         if (j != height_list.begin()) {           
3848                                                                 j--;
3849                                                         }
3850                                                         if (x != original_pointer_order) { 
3851                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3852                                                                 if ((*j) == 0) {
3853                                                                         temp_pointer_y_span++;
3854                                                                 }
3855                                                         }          
3856                                                         y_delta -= (*j);        
3857                                                         temp_pointer_y_span--;  
3858                                                 }
3859
3860                                                 while (temp_pointer_y_span < 0) {                 
3861                                                         y_delta += (*j);
3862                                                         if (x != original_pointer_order) { 
3863                                                                 if ((*j) == 0) {
3864                                                                         temp_pointer_y_span--;
3865                                                                 }
3866                                                         }          
3867                     
3868                                                         if (j != height_list.end()) {                 
3869                                                                 j++;
3870                                                         }
3871                                                         temp_pointer_y_span++;
3872                                                 }
3873                                                 /* find out where we'll be when we move and set height accordingly */
3874                   
3875                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3876                                                 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3877                                                 rv->set_height (temp_rtv->current_height());
3878
3879                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3880                                                      personally, i think this can confuse things, but never mind.
3881                                                 */
3882                                   
3883                                                 //const GdkColor& col (temp_rtv->view->get_region_color());
3884                                                 //rv->set_color (const_cast<GdkColor&>(col));
3885                                                 break;          
3886                                         }
3887                                         x++;
3888                                 }
3889                         }
3890
3891                         /* prevent the regionview from being moved to before 
3892                            the zero position on the canvas.
3893                         */
3894                         /* clamp */
3895                 
3896                         if (x_delta < 0) {
3897                                 if (-x_delta > ix1) {
3898                                         x_delta = -ix1;
3899                                 }
3900                         } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3901                                 x_delta = max_frames - rv->region()->last_frame();
3902                         }
3903
3904                         if (drag_info.brushing) {
3905                                 mouse_brush_insert_region (rv, pending_region_position);
3906                         } else {
3907                                 rv->move (x_delta, y_delta);
3908                         }
3909
3910                 } /* foreach region */
3911
3912         } /* if do_move */
3913
3914         if (drag_info.first_move && drag_info.move_threshold_passed) {
3915                 cursor_group->raise_to_top();
3916                 drag_info.first_move = false;
3917         }
3918
3919         if (x_delta != 0 && !drag_info.brushing) {
3920                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3921         }
3922
3923
3924 void
3925 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3926 {
3927         bool nocommit = true;
3928         vector<RegionView*> copies;
3929         RouteTimeAxisView* source_tv;
3930         boost::shared_ptr<Diskstream> ds;
3931         boost::shared_ptr<Playlist> from_playlist;
3932         vector<RegionView*> new_selection;
3933         typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
3934         PlaylistSet modified_playlists;
3935         PlaylistSet frozen_playlists;
3936         list <sigc::connection> modified_playlist_connections;
3937         pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
3938
3939         /* first_move is set to false if the regionview has been moved in the 
3940            motion handler. 
3941         */
3942
3943         if (drag_info.first_move) {
3944                 /* just a click */
3945                 goto out;
3946         }
3947
3948         nocommit = false;
3949
3950         if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
3951                 selection->set (pre_drag_region_selection);
3952                 pre_drag_region_selection.clear ();
3953         }
3954
3955         if (drag_info.brushing) {
3956                 /* all changes were made during motion event handlers */
3957                 
3958                 if (drag_info.copy) {
3959                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3960                                 copies.push_back (*i);
3961                         }
3962                 }
3963
3964                 goto out;
3965         }
3966
3967         char* op_string;
3968
3969         /* reverse this here so that we have the correct logic to finalize
3970            the drag.
3971         */
3972         
3973         if (Config->get_edit_mode() == Lock && !drag_info.copy) {
3974                 drag_info.x_constrained = !drag_info.x_constrained;
3975         }
3976
3977         if (drag_info.copy) {
3978                 if (drag_info.x_constrained) {
3979                         op_string = _("fixed time region copy");
3980                 } else {
3981                         op_string = _("region copy");
3982                 } 
3983         } else {
3984                 if (drag_info.x_constrained) {
3985                         op_string = _("fixed time region drag");
3986                 } else {
3987                         op_string = _("region drag");
3988                 }
3989         }
3990
3991         begin_reversible_command (op_string);
3992
3993         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3994                         
3995                 RegionView* rv = (*i);              
3996                 double ix1, ix2, iy1, iy2;
3997                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3998                 rv->get_canvas_frame()->i2w (ix1, iy1);
3999                 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4000
4001                 TimeAxisView* dest_tv = trackview_by_y_position (iy1);
4002                 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
4003                 double speed;
4004                 bool changed_tracks, changed_position;
4005                 nframes64_t where;
4006
4007                 if (rv->region()->locked()) {
4008                         ++i;
4009                         continue;
4010                 }
4011
4012                 /* adjust for track speed */
4013
4014                 speed = 1.0;
4015                 
4016                 if (dest_rtv && dest_rtv->get_diskstream()) {
4017                         speed = dest_rtv->get_diskstream()->speed();
4018                 }
4019                 
4020                 changed_position = (drag_info.last_frame_position != (nframes64_t) (rv->region()->position()/speed));
4021                 changed_tracks = (dest_tv != &rv->get_time_axis_view());
4022
4023                 if (changed_position && !drag_info.x_constrained) {
4024                         _master_group->w2i(ix1, iy1);
4025                         where = (nframes64_t) (unit_to_frame (ix1) * speed);
4026                 } else {
4027                         where = rv->region()->position();
4028                 }
4029                         
4030                 boost::shared_ptr<Region> new_region;
4031
4032
4033                 if (drag_info.copy) {
4034                         /* we already made a copy */
4035                         new_region = rv->region();
4036
4037                         /* undo the previous hide_dependent_views so that xfades don't
4038                            disappear on copying regions 
4039                         */
4040                 
4041                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
4042                 
4043                 } else if (changed_tracks && dest_rtv->playlist()) {
4044                         new_region = RegionFactory::create (rv->region());
4045                 }
4046
4047                 if (changed_tracks || drag_info.copy) {
4048
4049                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4050                         if (!to_playlist) {
4051                                 ++i;
4052                                 continue;
4053                         }
4054
4055                         latest_regionviews.clear ();
4056
4057                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4058                         
4059                         insert_result = modified_playlists.insert (to_playlist);
4060                         if (insert_result.second) {
4061                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4062                         }
4063
4064                         to_playlist->add_region (new_region, where);
4065
4066                         c.disconnect ();
4067                                                               
4068                         if (!latest_regionviews.empty()) {
4069                                 // XXX why just the first one ? we only expect one
4070                                 // commented out in nick_m's canvas reworking. is that intended?
4071                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4072                                 new_selection.push_back (latest_regionviews.front());
4073                         }
4074
4075                 } else {
4076                         /* 
4077                            motion on the same track. plonk the previously reparented region 
4078                            back to its original canvas group (its streamview).
4079                            No need to do anything for copies as they are fake regions which will be deleted.
4080                         */
4081
4082                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4083                         rv->get_canvas_group()->property_y() = 0;
4084                   
4085                         /* just change the model */
4086                         
4087                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4088
4089                         insert_result = modified_playlists.insert (playlist);
4090                         if (insert_result.second) {
4091                                 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4092                         }
4093                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
4094                         frozen_insert_result = frozen_playlists.insert(playlist);
4095                         if (frozen_insert_result.second) {
4096                                 playlist->freeze();
4097                         }
4098
4099                         rv->region()->set_position (where, (void*) this);
4100                 }
4101
4102                 if (changed_tracks && !drag_info.copy) {
4103
4104                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
4105                            because we may have copied the region and it has not been attached to a playlist.
4106                         */
4107
4108                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4109                         assert ((ds = source_tv->get_diskstream()));
4110                         assert ((from_playlist = ds->playlist()));
4111
4112                         /* moved to a different audio track, without copying */
4113
4114                         /* the region that used to be in the old playlist is not
4115                            moved to the new one - we use a copy of it. as a result,
4116                            any existing editor for the region should no longer be
4117                            visible.
4118                         */ 
4119             
4120                         rv->hide_region_editor();
4121                         rv->fake_set_opaque (false);
4122                         
4123                         /* remove the region from the old playlist */
4124
4125                         insert_result = modified_playlists.insert (from_playlist);
4126                         if (insert_result.second) {
4127                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4128                         }
4129
4130                         from_playlist->remove_region ((rv->region()));
4131                         
4132                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4133                            was selected in all of them, then removing it from a playlist will have removed all
4134                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
4135                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4136                            corresponding regionview, and the selection is now empty).
4137
4138                            this could have invalidated any and all iterators into the region selection.
4139
4140                            the heuristic we use here is: if the region selection is empty, break out of the loop
4141                            here. if the region selection is not empty, then restart the loop because we know that
4142                            we must have removed at least the region(view) we've just been working on as well as any
4143                            that we processed on previous iterations.
4144
4145                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4146                            we can just iterate.
4147                         */
4148
4149                         if (selection->regions.empty()) {
4150                                 break;
4151                         } else { 
4152                                 i = selection->regions.by_layer().begin();
4153                         }
4154
4155                 } else {
4156                         ++i;
4157                 }
4158                 
4159                 if (drag_info.copy) {
4160                         copies.push_back (rv);
4161                 }
4162         }
4163         
4164         if (new_selection.empty()) {
4165                 if (drag_info.copy) {
4166                         /* the region(view)s that are selected and being dragged around
4167                            are copies and do not belong to any track. remove them
4168                            from the selection right here.
4169                         */
4170                         selection->clear_regions();
4171                 }
4172         } else {
4173                 /* this will clear any existing selection that would have been
4174                    cleared in the other clause above
4175                 */
4176                 selection->set (new_selection);
4177         }
4178
4179         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4180                 (*p)->thaw();
4181         }
4182                         
4183   out:
4184         if (!nocommit) {
4185                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4186                         session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));      
4187                 }
4188                 commit_reversible_command ();
4189         }
4190
4191         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4192                 delete *x;
4193         }
4194
4195 }
4196         
4197 void
4198 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4199 {
4200         if (drag_info.move_threshold_passed) {
4201                 if (drag_info.first_move) {
4202                         // TODO: create region-create-drag region view here
4203                         drag_info.first_move = false;
4204                 }
4205
4206                 // TODO: resize region-create-drag region view here
4207         }
4208
4209
4210 void
4211 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4212 {
4213         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4214         if (!mtv)
4215                 return;
4216
4217         const boost::shared_ptr<MidiDiskstream> diskstream =
4218                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4219         
4220         if (!diskstream) {
4221                 warning << "Cannot create non-MIDI region" << endl;
4222                 return;
4223         }
4224
4225         if (drag_info.first_move) {
4226                 begin_reversible_command (_("create region"));
4227                 XMLNode &before = mtv->playlist()->get_state();
4228
4229                 nframes64_t start = drag_info.grab_frame;
4230                 snap_to (start, -1);
4231                 const Meter& m = session->tempo_map().meter_at(start);
4232                 const Tempo& t = session->tempo_map().tempo_at(start);
4233                 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4234
4235                 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4236                                 
4237                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4238                                              (RegionFactory::create(src, 0, (nframes_t) length, 
4239                                                                     PBD::basename_nosuffix(src->name()))), start);
4240                 XMLNode &after = mtv->playlist()->get_state();
4241                 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4242                 commit_reversible_command();
4243
4244         } else {
4245                 create_region_drag_motion_callback (item, event);
4246                 // TODO: create region-create-drag region here
4247         }
4248 }
4249
4250 void
4251 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4252 {
4253         /* Either add to or set the set the region selection, unless
4254            this is an alignment click (control used)
4255         */
4256         
4257         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4258                 TimeAxisView* tv = &rv.get_time_axis_view();
4259                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4260                 double speed = 1.0;
4261                 if (rtv && rtv->is_track()) {
4262                         speed = rtv->get_diskstream()->speed();
4263                 }
4264
4265                 nframes64_t where = get_preferred_edit_position();
4266
4267                 if (where >= 0) {
4268
4269                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4270                                 
4271                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4272                                 
4273                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4274                                 
4275                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
4276                                 
4277                         } else {
4278                                 
4279                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4280                         }
4281                 }
4282         }
4283 }
4284
4285 void
4286 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
4287 {
4288         char buf[128];
4289         SMPTE::Time smpte;
4290         BBT_Time bbt;
4291         int hours, mins;
4292         nframes64_t frame_rate;
4293         float secs;
4294
4295         if (session == 0) {
4296                 return;
4297         }
4298
4299         AudioClock::Mode m;
4300
4301         if (Profile->get_sae() || Profile->get_small_screen()) {
4302                 m = ARDOUR_UI::instance()->primary_clock.mode();
4303         } else {
4304                 m = ARDOUR_UI::instance()->secondary_clock.mode();
4305         }
4306
4307         switch (m) {
4308         case AudioClock::BBT:
4309                 session->bbt_time (frame, bbt);
4310                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4311                 break;
4312                 
4313         case AudioClock::SMPTE:
4314                 session->smpte_time (frame, smpte);
4315                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4316                 break;
4317
4318         case AudioClock::MinSec:
4319                 /* XXX this is copied from show_verbose_duration_cursor() */
4320                 frame_rate = session->frame_rate();
4321                 hours = frame / (frame_rate * 3600);
4322                 frame = frame % (frame_rate * 3600);
4323                 mins = frame / (frame_rate * 60);
4324                 frame = frame % (frame_rate * 60);
4325                 secs = (float) frame / (float) frame_rate;
4326                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4327                 break;
4328
4329         default:
4330                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4331                 break;
4332         }
4333
4334         if (xpos >= 0 && ypos >=0) {
4335                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4336         }
4337         else {
4338                 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);
4339         }
4340         show_verbose_canvas_cursor ();
4341 }
4342
4343 void
4344 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
4345 {
4346         char buf[128];
4347         SMPTE::Time smpte;
4348         BBT_Time sbbt;
4349         BBT_Time ebbt;
4350         int hours, mins;
4351         nframes64_t distance, frame_rate;
4352         float secs;
4353         Meter meter_at_start(session->tempo_map().meter_at(start));
4354
4355         if (session == 0) {
4356                 return;
4357         }
4358
4359         AudioClock::Mode m;
4360
4361         if (Profile->get_sae() || Profile->get_small_screen()) {
4362                 m = ARDOUR_UI::instance()->primary_clock.mode ();
4363         } else {
4364                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4365         }
4366
4367         switch (m) {
4368         case AudioClock::BBT:
4369                 session->bbt_time (start, sbbt);
4370                 session->bbt_time (end, ebbt);
4371
4372                 /* subtract */
4373                 /* XXX this computation won't work well if the
4374                 user makes a selection that spans any meter changes.
4375                 */
4376
4377                 ebbt.bars -= sbbt.bars;
4378                 if (ebbt.beats >= sbbt.beats) {
4379                         ebbt.beats -= sbbt.beats;
4380                 } else {
4381                         ebbt.bars--;
4382                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4383                 }
4384                 if (ebbt.ticks >= sbbt.ticks) {
4385                         ebbt.ticks -= sbbt.ticks;
4386                 } else {
4387                         ebbt.beats--;
4388                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4389                 }
4390                 
4391                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4392                 break;
4393                 
4394         case AudioClock::SMPTE:
4395                 session->smpte_duration (end - start, smpte);
4396                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4397                 break;
4398
4399         case AudioClock::MinSec:
4400                 /* XXX this stuff should be elsewhere.. */
4401                 distance = end - start;
4402                 frame_rate = session->frame_rate();
4403                 hours = distance / (frame_rate * 3600);
4404                 distance = distance % (frame_rate * 3600);
4405                 mins = distance / (frame_rate * 60);
4406                 distance = distance % (frame_rate * 60);
4407                 secs = (float) distance / (float) frame_rate;
4408                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4409                 break;
4410
4411         default:
4412                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4413                 break;
4414         }
4415
4416         if (xpos >= 0 && ypos >=0) {
4417                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4418         }
4419         else {
4420                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4421         }
4422
4423         show_verbose_canvas_cursor ();
4424 }
4425
4426 void
4427 Editor::collect_new_region_view (RegionView* rv)
4428 {
4429         latest_regionviews.push_back (rv);
4430 }
4431
4432 void
4433 Editor::collect_and_select_new_region_view (RegionView* rv)
4434 {
4435         selection->add(rv);
4436         latest_regionviews.push_back (rv);
4437 }
4438
4439 void
4440 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4441 {
4442         if (clicked_regionview == 0) {
4443                 return;
4444         }
4445
4446         /* lets try to create new Region for the selection */
4447
4448         vector<boost::shared_ptr<Region> > new_regions;
4449         create_region_from_selection (new_regions);
4450
4451         if (new_regions.empty()) {
4452                 return;
4453         }
4454
4455         /* XXX fix me one day to use all new regions */
4456         
4457         boost::shared_ptr<Region> region (new_regions.front());
4458
4459         /* add it to the current stream/playlist.
4460
4461            tricky: the streamview for the track will add a new regionview. we will
4462            catch the signal it sends when it creates the regionview to
4463            set the regionview we want to then drag.
4464         */
4465         
4466         latest_regionviews.clear();
4467         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4468         
4469         /* A selection grab currently creates two undo/redo operations, one for 
4470            creating the new region and another for moving it.
4471         */
4472
4473         begin_reversible_command (_("selection grab"));
4474
4475         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4476
4477         XMLNode *before = &(playlist->get_state());
4478         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4479         XMLNode *after = &(playlist->get_state());
4480         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4481
4482         commit_reversible_command ();
4483         
4484         c.disconnect ();
4485         
4486         if (latest_regionviews.empty()) {
4487                 /* something went wrong */
4488                 return;
4489         }
4490
4491         /* we need to deselect all other regionviews, and select this one
4492            i'm ignoring undo stuff, because the region creation will take care of it 
4493         */
4494         selection->set (latest_regionviews);
4495         
4496         drag_info.item = latest_regionviews.front()->get_canvas_group();
4497         drag_info.data = latest_regionviews.front();
4498         drag_info.motion_callback = &Editor::region_drag_motion_callback;
4499         drag_info.finished_callback = &Editor::region_drag_finished_callback;
4500
4501         start_grab (event);
4502         
4503         drag_info.source_trackview = clicked_routeview;
4504         drag_info.dest_trackview = drag_info.source_trackview;
4505         drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4506         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4507         
4508         show_verbose_time_cursor (drag_info.last_frame_position, 10);
4509 }
4510
4511 void
4512 Editor::cancel_selection ()
4513 {
4514         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4515                 (*i)->hide_selection ();
4516         }
4517         selection->clear ();
4518         clicked_selection = 0;
4519 }       
4520
4521 void
4522 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4523 {
4524         nframes64_t start = 0;
4525         nframes64_t end = 0;
4526
4527         if (session == 0) {
4528                 return;
4529         }
4530
4531         drag_info.item = item;
4532         drag_info.motion_callback = &Editor::drag_selection;
4533         drag_info.finished_callback = &Editor::end_selection_op;
4534
4535         selection_op = op;
4536
4537         switch (op) {
4538         case CreateSelection:
4539                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4540                         drag_info.copy = true;
4541                 } else {
4542                         drag_info.copy = false;
4543                 }
4544                 start_grab (event, selector_cursor);
4545                 break;
4546
4547         case SelectionStartTrim:
4548                 if (clicked_axisview) {
4549                         clicked_axisview->order_selection_trims (item, true);
4550                 } 
4551                 start_grab (event, trimmer_cursor);
4552                 start = selection->time[clicked_selection].start;
4553                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4554                 break;
4555                 
4556         case SelectionEndTrim:
4557                 if (clicked_axisview) {
4558                         clicked_axisview->order_selection_trims (item, false);
4559                 }
4560                 start_grab (event, trimmer_cursor);
4561                 end = selection->time[clicked_selection].end;
4562                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
4563                 break;
4564
4565         case SelectionMove:
4566                 start = selection->time[clicked_selection].start;
4567                 start_grab (event);
4568                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4569                 break;
4570         }
4571
4572         if (selection_op == SelectionMove) {
4573                 show_verbose_time_cursor(start, 10);    
4574         } else {
4575                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4576         }
4577 }
4578
4579 void
4580 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4581 {
4582         nframes64_t start = 0;
4583         nframes64_t end = 0;
4584         nframes64_t length;
4585         nframes64_t pending_position;
4586
4587         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4588                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4589         } else {
4590                 pending_position = 0;
4591         }
4592         
4593         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4594                 snap_to (pending_position);
4595         }
4596
4597         /* only alter selection if the current frame is 
4598            different from the last frame position (adjusted)
4599          */
4600         
4601         if (pending_position == drag_info.last_pointer_frame) return;
4602         
4603         switch (selection_op) {
4604         case CreateSelection:
4605                 
4606                 if (drag_info.first_move) {
4607                         snap_to (drag_info.grab_frame);
4608                 }
4609                 
4610                 if (pending_position < drag_info.grab_frame) {
4611                         start = pending_position;
4612                         end = drag_info.grab_frame;
4613                 } else {
4614                         end = pending_position;
4615                         start = drag_info.grab_frame;
4616                 }
4617                 
4618                 /* first drag: Either add to the selection
4619                    or create a new selection->
4620                 */
4621                 
4622                 if (drag_info.first_move) {
4623                         
4624                         begin_reversible_command (_("range selection"));
4625                         
4626                         if (drag_info.copy) {
4627                                 /* adding to the selection */
4628                                 clicked_selection = selection->add (start, end);
4629                                 drag_info.copy = false;
4630                         } else {
4631                                 /* new selection-> */
4632                                 clicked_selection = selection->set (clicked_axisview, start, end);
4633                         }
4634                 } 
4635                 break;
4636                 
4637         case SelectionStartTrim:
4638                 
4639                 if (drag_info.first_move) {
4640                         begin_reversible_command (_("trim selection start"));
4641                 }
4642                 
4643                 start = selection->time[clicked_selection].start;
4644                 end = selection->time[clicked_selection].end;
4645
4646                 if (pending_position > end) {
4647                         start = end;
4648                 } else {
4649                         start = pending_position;
4650                 }
4651                 break;
4652                 
4653         case SelectionEndTrim:
4654                 
4655                 if (drag_info.first_move) {
4656                         begin_reversible_command (_("trim selection end"));
4657                 }
4658                 
4659                 start = selection->time[clicked_selection].start;
4660                 end = selection->time[clicked_selection].end;
4661
4662                 if (pending_position < start) {
4663                         end = start;
4664                 } else {
4665                         end = pending_position;
4666                 }
4667                 
4668                 break;
4669                 
4670         case SelectionMove:
4671                 
4672                 if (drag_info.first_move) {
4673                         begin_reversible_command (_("move selection"));
4674                 }
4675                 
4676                 start = selection->time[clicked_selection].start;
4677                 end = selection->time[clicked_selection].end;
4678                 
4679                 length = end - start;
4680                 
4681                 start = pending_position;
4682                 snap_to (start);
4683                 
4684                 end = start + length;
4685                 
4686                 break;
4687         }
4688         
4689         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4690                 start_canvas_autoscroll (1, 0);
4691         }
4692
4693         if (start != end) {
4694                 selection->replace (clicked_selection, start, end);
4695         }
4696
4697         drag_info.last_pointer_frame = pending_position;
4698         drag_info.first_move = false;
4699
4700         if (selection_op == SelectionMove) {
4701                 show_verbose_time_cursor(start, 10);    
4702         } else {
4703                 show_verbose_time_cursor(pending_position, 10); 
4704         }
4705 }
4706
4707 void
4708 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
4709 {
4710         if (!drag_info.first_move) {
4711                 drag_selection (item, event);
4712                 /* XXX this is not object-oriented programming at all. ick */
4713                 if (selection->time.consolidate()) {
4714                         selection->TimeChanged ();
4715                 }
4716                 commit_reversible_command ();
4717         } else {
4718                 /* just a click, no pointer movement.*/
4719
4720                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4721
4722                         selection->clear_time();
4723
4724                 } 
4725         }
4726
4727         /* XXX what happens if its a music selection? */
4728         session->set_audio_range (selection->time);
4729         stop_canvas_autoscroll ();
4730 }
4731
4732 void
4733 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
4734 {
4735         double speed = 1.0;
4736         TimeAxisView* tvp = clicked_axisview;
4737         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4738
4739         if (tv && tv->is_track()) {
4740                 speed = tv->get_diskstream()->speed();
4741         }
4742         
4743         nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
4744         nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
4745         nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
4746
4747         //drag_info.item = clicked_regionview->get_name_highlight();
4748         drag_info.item = item;
4749         drag_info.motion_callback = &Editor::trim_motion_callback;
4750         drag_info.finished_callback = &Editor::trim_finished_callback;
4751
4752         start_grab (event, trimmer_cursor);
4753         
4754         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4755                 trim_op = ContentsTrim;
4756         } else {
4757                 /* These will get overridden for a point trim.*/
4758                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
4759                         /* closer to start */
4760                         trim_op = StartTrim;
4761                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
4762                         /* closer to end */
4763                         trim_op = EndTrim;
4764                 }
4765         }
4766
4767         switch (trim_op) {
4768         case StartTrim:
4769                 show_verbose_time_cursor(region_start, 10);     
4770                 break;
4771         case EndTrim:
4772                 show_verbose_time_cursor(region_end, 10);       
4773                 break;
4774         case ContentsTrim:
4775                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4776                 break;
4777         }
4778 }
4779
4780 void
4781 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4782 {
4783         RegionView* rv = clicked_regionview;
4784         nframes64_t frame_delta = 0;
4785         bool left_direction;
4786         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
4787
4788         /* snap modifier works differently here..
4789            its' current state has to be passed to the 
4790            various trim functions in order to work properly 
4791         */ 
4792
4793         double speed = 1.0;
4794         TimeAxisView* tvp = clicked_axisview;
4795         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4796         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4797
4798         if (tv && tv->is_track()) {
4799                 speed = tv->get_diskstream()->speed();
4800         }
4801         
4802         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4803                 left_direction = true;
4804         } else {
4805                 left_direction = false;
4806         }
4807
4808         if (obey_snap) {
4809                 snap_to (drag_info.current_pointer_frame);
4810         }
4811
4812         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4813                 return;
4814         }
4815
4816         if (drag_info.first_move) {
4817         
4818                 string trim_type;
4819
4820                 switch (trim_op) {
4821                 case StartTrim:
4822                         trim_type = "Region start trim";
4823                         break;
4824                 case EndTrim:
4825                         trim_type = "Region end trim";
4826                         break;
4827                 case ContentsTrim:
4828                         trim_type = "Region content trim";
4829                         break;
4830                 }
4831
4832                 begin_reversible_command (trim_type);
4833
4834                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4835                         (*i)->fake_set_opaque(false);
4836                         (*i)->region()->freeze ();
4837                 
4838                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4839                         if (arv)
4840                                 arv->temporarily_hide_envelope ();
4841
4842                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4843                         insert_result = motion_frozen_playlists.insert (pl);
4844                         if (insert_result.second) {
4845                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4846                                 pl->freeze();
4847                         }
4848                 }
4849         }
4850
4851         if (left_direction) {
4852                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4853         } else {
4854                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4855         }
4856
4857         switch (trim_op) {              
4858         case StartTrim:
4859                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4860                         break;
4861                 } else {
4862                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4863                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4864                         }
4865                         break;
4866                 }
4867                 
4868         case EndTrim:
4869                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
4870                         break;
4871                 } else {
4872                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4873                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4874                         }
4875                         break;
4876                 }
4877                 
4878         case ContentsTrim:
4879                 {
4880                         bool swap_direction = false;
4881
4882                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
4883                                 swap_direction = true;
4884                         }
4885                         
4886                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4887                              i != selection->regions.by_layer().end(); ++i)
4888                         {
4889                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4890                         }
4891                 }
4892                 break;
4893         }
4894
4895         switch (trim_op) {
4896         case StartTrim:
4897                 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);   
4898                 break;
4899         case EndTrim:
4900                 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); 
4901                 break;
4902         case ContentsTrim:
4903                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4904                 break;
4905         }
4906
4907         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4908         drag_info.first_move = false;
4909 }
4910
4911 void
4912 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4913 {
4914         boost::shared_ptr<Region> region (rv.region());
4915
4916         if (region->locked()) {
4917                 return;
4918         }
4919
4920         nframes64_t new_bound;
4921
4922         double speed = 1.0;
4923         TimeAxisView* tvp = clicked_axisview;
4924         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4925
4926         if (tv && tv->is_track()) {
4927                 speed = tv->get_diskstream()->speed();
4928         }
4929         
4930         if (left_direction) {
4931                 if (swap_direction) {
4932                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4933                 } else {
4934                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4935                 }
4936         } else {
4937                 if (swap_direction) {
4938                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4939                 } else {
4940                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4941                 }
4942         }
4943
4944         if (obey_snap) {
4945                 snap_to (new_bound);
4946         }
4947         region->trim_start ((nframes64_t) (new_bound * speed), this);   
4948         rv.region_changed (StartChanged);
4949 }
4950
4951 void
4952 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4953 {
4954         boost::shared_ptr<Region> region (rv.region()); 
4955
4956         if (region->locked()) {
4957                 return;
4958         }
4959
4960         nframes64_t new_bound;
4961
4962         double speed = 1.0;
4963         TimeAxisView* tvp = clicked_axisview;
4964         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4965
4966         if (tv && tv->is_track()) {
4967                 speed = tv->get_diskstream()->speed();
4968         }
4969         
4970         if (left_direction) {
4971                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
4972         } else {
4973                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
4974         }
4975
4976         if (obey_snap) {
4977                 snap_to (new_bound, (left_direction ? 0 : 1));  
4978         }
4979
4980         region->trim_front ((nframes64_t) (new_bound * speed), this);
4981
4982         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4983 }
4984
4985 void
4986 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
4987 {
4988         boost::shared_ptr<Region> region (rv.region());
4989
4990         if (region->locked()) {
4991                 return;
4992         }
4993
4994         nframes64_t new_bound;
4995
4996         double speed = 1.0;
4997         TimeAxisView* tvp = clicked_axisview;
4998         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4999
5000         if (tv && tv->is_track()) {
5001                 speed = tv->get_diskstream()->speed();
5002         }
5003         
5004         if (left_direction) {
5005                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5006         } else {
5007                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5008         }
5009
5010         if (obey_snap) {
5011                 snap_to (new_bound);
5012         }
5013         region->trim_end ((nframes64_t) (new_bound * speed), this);
5014         rv.region_changed (LengthChanged);
5015 }
5016         
5017 void
5018 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5019 {
5020         if (!drag_info.first_move) {
5021                 trim_motion_callback (item, event);
5022                 
5023                 if (!selection->selected (clicked_regionview)) {
5024                         thaw_region_after_trim (*clicked_regionview);           
5025                 } else {
5026                         
5027                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5028                              i != selection->regions.by_layer().end(); ++i)
5029                         {
5030                                 thaw_region_after_trim (**i);
5031                                 (*i)->fake_set_opaque (true);
5032                         }
5033                 }
5034                 
5035                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5036                         (*p)->thaw ();
5037                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5038                 }
5039                 
5040                 motion_frozen_playlists.clear ();
5041
5042                 commit_reversible_command();
5043         } else {
5044                 /* no mouse movement */
5045                 point_trim (event);
5046         }
5047 }
5048
5049 void
5050 Editor::point_trim (GdkEvent* event)
5051 {
5052         RegionView* rv = clicked_regionview;
5053         nframes64_t new_bound = drag_info.current_pointer_frame;
5054
5055         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5056                 snap_to (new_bound);
5057         }
5058
5059         /* Choose action dependant on which button was pressed */
5060         switch (event->button.button) {
5061         case 1:
5062                 trim_op = StartTrim;
5063                 begin_reversible_command (_("Start point trim"));
5064
5065                 if (selection->selected (rv)) {
5066
5067                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5068                              i != selection->regions.by_layer().end(); ++i)
5069                         {
5070                                 if (!(*i)->region()->locked()) {
5071                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5072                                         XMLNode &before = pl->get_state();
5073                                         (*i)->region()->trim_front (new_bound, this);   
5074                                         XMLNode &after = pl->get_state();
5075                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5076                                 }
5077                         }
5078
5079                 } else {
5080
5081                         if (!rv->region()->locked()) {
5082                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5083                                 XMLNode &before = pl->get_state();
5084                                 rv->region()->trim_front (new_bound, this);     
5085                                 XMLNode &after = pl->get_state();
5086                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5087                         }
5088                 }
5089
5090                 commit_reversible_command();
5091         
5092                 break;
5093         case 2:
5094                 trim_op = EndTrim;
5095                 begin_reversible_command (_("End point trim"));
5096
5097                 if (selection->selected (rv)) {
5098                         
5099                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5100                         {
5101                                 if (!(*i)->region()->locked()) {
5102                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5103                                         XMLNode &before = pl->get_state();
5104                                         (*i)->region()->trim_end (new_bound, this);
5105                                         XMLNode &after = pl->get_state();
5106                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5107                                 }
5108                         }
5109
5110                 } else {
5111
5112                         if (!rv->region()->locked()) {
5113                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5114                                 XMLNode &before = pl->get_state();
5115                                 rv->region()->trim_end (new_bound, this);
5116                                 XMLNode &after = pl->get_state();
5117                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5118                         }
5119                 }
5120
5121                 commit_reversible_command();
5122         
5123                 break;
5124         default:
5125                 break;
5126         }
5127 }
5128
5129 void
5130 Editor::thaw_region_after_trim (RegionView& rv)
5131 {
5132         boost::shared_ptr<Region> region (rv.region());
5133
5134         if (region->locked()) {
5135                 return;
5136         }
5137
5138         region->thaw (_("trimmed region"));
5139         XMLNode &after = region->playlist()->get_state();
5140         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5141
5142         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5143         if (arv)
5144                 arv->unhide_envelope ();
5145 }
5146
5147 void
5148 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5149 {
5150         Marker* marker;
5151         bool is_start;
5152
5153         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5154                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5155                 /*NOTREACHED*/
5156         }
5157
5158         Location* location = find_location_from_marker (marker, is_start);      
5159         location->set_hidden (true, this);
5160 }
5161
5162
5163 void
5164 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5165 {
5166         if (session == 0) {
5167                 return;
5168         }
5169
5170         drag_info.item = item;
5171         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5172         drag_info.finished_callback = &Editor::end_range_markerbar_op;
5173
5174         range_marker_op = op;
5175
5176         if (!temp_location) {
5177                 temp_location = new Location;
5178         }
5179         
5180         switch (op) {
5181         case CreateRangeMarker:
5182         case CreateTransportMarker:
5183         case CreateCDMarker:
5184         
5185                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5186                         drag_info.copy = true;
5187                 } else {
5188                         drag_info.copy = false;
5189                 }
5190                 start_grab (event, selector_cursor);
5191                 break;
5192         }
5193
5194         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5195         
5196 }
5197
5198 void
5199 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5200 {
5201         nframes64_t start = 0;
5202         nframes64_t end = 0;
5203         ArdourCanvas::SimpleRect *crect;
5204
5205         switch (range_marker_op) {
5206         case CreateRangeMarker:
5207                 crect = range_bar_drag_rect;
5208                 break;
5209         case CreateTransportMarker:
5210                 crect = transport_bar_drag_rect;
5211                 break;
5212         case CreateCDMarker:
5213                 crect = cd_marker_bar_drag_rect;
5214                 break;
5215         default:
5216                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5217                 return;
5218                 break;
5219         }
5220         
5221         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5222                 snap_to (drag_info.current_pointer_frame);
5223         }
5224
5225         /* only alter selection if the current frame is 
5226            different from the last frame position.
5227          */
5228         
5229         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5230         
5231         switch (range_marker_op) {
5232         case CreateRangeMarker:
5233         case CreateTransportMarker:
5234         case CreateCDMarker:
5235                 if (drag_info.first_move) {
5236                         snap_to (drag_info.grab_frame);
5237                 }
5238                 
5239                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5240                         start = drag_info.current_pointer_frame;
5241                         end = drag_info.grab_frame;
5242                 } else {
5243                         end = drag_info.current_pointer_frame;
5244                         start = drag_info.grab_frame;
5245                 }
5246                 
5247                 /* first drag: Either add to the selection
5248                    or create a new selection.
5249                 */
5250                 
5251                 if (drag_info.first_move) {
5252                         
5253                         temp_location->set (start, end);
5254                         
5255                         crect->show ();
5256
5257                         update_marker_drag_item (temp_location);
5258                         range_marker_drag_rect->show();
5259                         //range_marker_drag_rect->raise_to_top();
5260                         
5261                 } 
5262                 break;          
5263         }
5264         
5265         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5266                 start_canvas_autoscroll (1, 0);
5267         }
5268         
5269         if (start != end) {
5270                 temp_location->set (start, end);
5271
5272                 double x1 = frame_to_pixel (start);
5273                 double x2 = frame_to_pixel (end);
5274                 crect->property_x1() = x1;
5275                 crect->property_x2() = x2;
5276
5277                 update_marker_drag_item (temp_location);
5278         }
5279
5280         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5281         drag_info.first_move = false;
5282
5283         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5284         
5285 }
5286
5287 void
5288 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5289 {
5290         Location * newloc = 0;
5291         string rangename;
5292         int flags;
5293         
5294         if (!drag_info.first_move) {
5295                 drag_range_markerbar_op (item, event);
5296
5297                 switch (range_marker_op) {
5298                 case CreateRangeMarker:
5299                 case CreateCDMarker:
5300                     {
5301                         begin_reversible_command (_("new range marker"));
5302                         XMLNode &before = session->locations()->get_state();
5303                         session->locations()->next_available_name(rangename,"unnamed");
5304                         if (range_marker_op == CreateCDMarker) {
5305                                 flags =  Location::IsRangeMarker|Location::IsCDMarker;
5306                                 cd_marker_bar_drag_rect->hide();
5307                         }
5308                         else {
5309                                 flags =  Location::IsRangeMarker;
5310                                 range_bar_drag_rect->hide();
5311                         }
5312                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5313                         session->locations()->add (newloc, true);
5314                         XMLNode &after = session->locations()->get_state();
5315                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5316                         commit_reversible_command ();
5317                         
5318                         range_marker_drag_rect->hide();
5319                         break;
5320                     }
5321
5322                 case CreateTransportMarker:
5323                         // popup menu to pick loop or punch
5324                         new_transport_marker_context_menu (&event->button, item);
5325                         
5326                         break;
5327                 }
5328         } else {
5329                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5330
5331                 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5332
5333                         nframes64_t start;
5334                         nframes64_t end;
5335
5336                         start = session->locations()->first_mark_before (drag_info.grab_frame);
5337                         end = session->locations()->first_mark_after (drag_info.grab_frame);
5338                         
5339                         if (end == max_frames) {
5340                                 end = session->current_end_frame ();
5341                         }
5342
5343                         if (start == 0) {
5344                                 start = session->current_start_frame ();
5345                         }
5346
5347                         switch (mouse_mode) {
5348                         case MouseObject:
5349                                 /* find the two markers on either side and then make the selection from it */
5350                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5351                                 break;
5352
5353                         case MouseRange:
5354                                 /* find the two markers on either side of the click and make the range out of it */
5355                                 selection->set (0, start, end);
5356                                 break;
5357
5358                         default:
5359                                 break;
5360                         }
5361                 } 
5362         }
5363
5364         stop_canvas_autoscroll ();
5365 }
5366
5367
5368
5369 void
5370 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5371 {
5372         drag_info.item = item;
5373         drag_info.motion_callback = &Editor::drag_mouse_zoom;
5374         drag_info.finished_callback = &Editor::end_mouse_zoom;
5375
5376         start_grab (event, zoom_cursor);
5377
5378         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5379 }
5380
5381 void
5382 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5383 {
5384         nframes64_t start;
5385         nframes64_t end;
5386
5387         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5388                 snap_to (drag_info.current_pointer_frame);
5389                 
5390                 if (drag_info.first_move) {
5391                         snap_to (drag_info.grab_frame);
5392                 }
5393         }
5394                 
5395         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5396
5397         /* base start and end on initial click position */
5398         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5399                 start = drag_info.current_pointer_frame;
5400                 end = drag_info.grab_frame;
5401         } else {
5402                 end = drag_info.current_pointer_frame;
5403                 start = drag_info.grab_frame;
5404         }
5405         
5406         if (start != end) {
5407
5408                 if (drag_info.first_move) {
5409                         zoom_rect->show();
5410                         zoom_rect->raise_to_top();
5411                 }
5412
5413                 reposition_zoom_rect(start, end);
5414
5415                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5416                 drag_info.first_move = false;
5417
5418                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5419         }
5420 }
5421
5422 void
5423 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5424 {
5425         if (!drag_info.first_move) {
5426                 drag_mouse_zoom (item, event);
5427                 
5428                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5429                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5430                 } else {
5431                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5432                 }               
5433         } else {
5434                 temporal_zoom_to_frame (false, drag_info.grab_frame);
5435                 /*
5436                 temporal_zoom_step (false);
5437                 center_screen (drag_info.grab_frame);
5438                 */
5439         }
5440
5441         zoom_rect->hide();
5442 }
5443
5444 void
5445 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5446 {
5447         double x1 = frame_to_pixel (start);
5448         double x2 = frame_to_pixel (end);
5449         double y2 = full_canvas_height - 1.0;
5450
5451         zoom_rect->property_x1() = x1;
5452         zoom_rect->property_y1() = 1.0;
5453         zoom_rect->property_x2() = x2;
5454         zoom_rect->property_y2() = y2;
5455 }
5456
5457 void
5458 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5459 {
5460         drag_info.item = item;
5461         drag_info.motion_callback = &Editor::drag_rubberband_select;
5462         drag_info.finished_callback = &Editor::end_rubberband_select;
5463
5464         start_grab (event, cross_hair_cursor);
5465
5466         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5467 }
5468
5469 void
5470 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5471 {
5472         nframes64_t start;
5473         nframes64_t end;
5474         double y1;
5475         double y2;
5476
5477         /* use a bigger drag threshold than the default */
5478
5479         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5480                 return;
5481         }
5482
5483         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5484                 if (drag_info.first_move) {
5485                         snap_to (drag_info.grab_frame);
5486                 } 
5487                 snap_to (drag_info.current_pointer_frame);
5488         }
5489
5490         /* base start and end on initial click position */
5491
5492         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5493                 start = drag_info.current_pointer_frame;
5494                 end = drag_info.grab_frame;
5495         } else {
5496                 end = drag_info.current_pointer_frame;
5497                 start = drag_info.grab_frame;
5498         }
5499
5500         if (drag_info.current_pointer_y < drag_info.grab_y) {
5501                 y1 = drag_info.current_pointer_y;
5502                 y2 = drag_info.grab_y;
5503         } else {
5504                 y2 = drag_info.current_pointer_y;
5505                 y1 = drag_info.grab_y;
5506         }
5507
5508         
5509         if (start != end || y1 != y2) {
5510
5511                 double x1 = frame_to_pixel (start);
5512                 double x2 = frame_to_pixel (end);
5513                 
5514                 rubberband_rect->property_x1() = x1;
5515                 rubberband_rect->property_y1() = y1;
5516                 rubberband_rect->property_x2() = x2;
5517                 rubberband_rect->property_y2() = y2;
5518
5519                 rubberband_rect->show();
5520                 rubberband_rect->raise_to_top();
5521                 
5522                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5523                 drag_info.first_move = false;
5524
5525                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5526         }
5527 }
5528
5529 void
5530 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5531 {
5532         if (!drag_info.first_move) {
5533
5534                 drag_rubberband_select (item, event);
5535
5536                 double y1,y2;
5537                 if (drag_info.current_pointer_y < drag_info.grab_y) {
5538                         y1 = drag_info.current_pointer_y;
5539                         y2 = drag_info.grab_y;
5540                 } else {
5541                         y2 = drag_info.current_pointer_y;
5542                         y1 = drag_info.grab_y;
5543                 }
5544
5545
5546                 Selection::Operation op = Keyboard::selection_type (event->button.state);
5547                 bool commit;
5548
5549                 begin_reversible_command (_("rubberband selection"));
5550
5551                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5552                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5553                 } else {
5554                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5555                 }               
5556
5557                 if (commit) {
5558                         commit_reversible_command ();
5559                 }
5560                 
5561         } else {
5562                 selection->clear_tracks();
5563                 selection->clear_regions();
5564                 selection->clear_points ();
5565                 selection->clear_lines ();
5566         }
5567
5568         rubberband_rect->hide();
5569 }
5570
5571
5572 gint
5573 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5574 {
5575         using namespace Gtkmm2ext;
5576
5577         ArdourPrompter prompter (false);
5578
5579         prompter.set_prompt (_("Name for region:"));
5580         prompter.set_initial_text (clicked_regionview->region()->name());
5581         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5582         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5583         prompter.show_all ();
5584         switch (prompter.run ()) {
5585         case Gtk::RESPONSE_ACCEPT:
5586                 string str;
5587                 prompter.get_result(str);
5588                 if (str.length()) {
5589                         clicked_regionview->region()->set_name (str);
5590                 }
5591                 break;
5592         }
5593         return true;
5594 }
5595
5596 void
5597 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5598 {
5599         drag_info.item = item;
5600         drag_info.motion_callback = &Editor::time_fx_motion;
5601         drag_info.finished_callback = &Editor::end_time_fx;
5602
5603         start_grab (event);
5604
5605         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5606 }
5607
5608 void
5609 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5610 {
5611         RegionView* rv = clicked_regionview;
5612
5613         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5614                 snap_to (drag_info.current_pointer_frame);
5615         }
5616
5617         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5618                 return;
5619         }
5620
5621         if (drag_info.current_pointer_frame > rv->region()->position()) {
5622                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5623         }
5624
5625         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5626         drag_info.first_move = false;
5627
5628         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5629 }
5630
5631 void
5632 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5633 {
5634         clicked_regionview->get_time_axis_view().hide_timestretch ();
5635
5636         if (drag_info.first_move) {
5637                 return;
5638         }
5639
5640         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5641                 /* backwards drag of the left edge - not usable */
5642                 return;
5643         }
5644         
5645         nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5646
5647         float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5648         
5649 #ifndef USE_RUBBERBAND
5650         // Soundtouch uses percentage / 100 instead of normal (/ 1) 
5651         if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5652                 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5653         }
5654 #endif  
5655         
5656         begin_reversible_command (_("timestretch"));
5657         
5658         // XXX how do timeFX on multiple regions ?
5659         
5660         RegionSelection rs;
5661         rs.add (clicked_regionview);
5662
5663         if (time_stretch (rs, percentage) == 0) {
5664                 session->commit_reversible_command ();
5665         }
5666 }
5667
5668 void
5669 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5670 {
5671         /* no brushing without a useful snap setting */
5672
5673         switch (snap_mode) {
5674         case SnapMagnetic:
5675                 return; /* can't work because it allows region to be placed anywhere */
5676         default:
5677                 break; /* OK */
5678         }
5679
5680         switch (snap_type) {
5681         case SnapToMark:
5682                 return;
5683
5684         default:
5685                 break;
5686         }
5687
5688         /* don't brush a copy over the original */
5689         
5690         if (pos == rv->region()->position()) {
5691                 return;
5692         }
5693
5694         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5695
5696         if (rtv == 0 || !rtv->is_track()) {
5697                 return;
5698         }
5699
5700         boost::shared_ptr<Playlist> playlist = rtv->playlist();
5701         double speed = rtv->get_diskstream()->speed();
5702         
5703         XMLNode &before = playlist->get_state();
5704         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
5705         XMLNode &after = playlist->get_state();
5706         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
5707         
5708         // playlist is frozen, so we have to update manually
5709         
5710         playlist->Modified(); /* EMIT SIGNAL */
5711 }
5712
5713 gint
5714 Editor::track_height_step_timeout ()
5715 {
5716         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
5717                 current_stepping_trackview = 0;
5718                 return false;
5719         }
5720         return true;
5721 }
5722