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