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