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