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