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