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