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