merged with 1697 revision of trunk (which is post-rc1 but pre-rc2
[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 ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
1453                         bool y_threshold_passed =  (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
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                 cerr << "end grab with no item\n";
1624                 return false;
1625         }
1626         
1627         drag_info.item->ungrab (event->button.time);
1628
1629         if (drag_info.finished_callback) {
1630                 (this->*(drag_info.finished_callback)) (item, event);
1631         }
1632
1633         did_drag = !drag_info.first_move;
1634
1635         hide_verbose_canvas_cursor();
1636
1637         drag_info.item = 0;
1638         drag_info.copy = false;
1639         drag_info.motion_callback = 0;
1640         drag_info.finished_callback = 0;
1641         drag_info.last_trackview = 0;
1642         drag_info.last_frame_position = 0;
1643         drag_info.grab_frame = 0;
1644         drag_info.last_pointer_frame = 0;
1645         drag_info.current_pointer_frame = 0;
1646         drag_info.brushing = false;
1647
1648         if (drag_info.copied_location) {
1649                 delete drag_info.copied_location;
1650                 drag_info.copied_location = 0;
1651         }
1652
1653         return did_drag;
1654 }
1655
1656 void
1657 Editor::set_edit_cursor (GdkEvent* event)
1658 {
1659         nframes_t pointer_frame = event_frame (event);
1660
1661         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1662                 if (snap_type != SnapToEditCursor) {
1663                         snap_to (pointer_frame);
1664                 }
1665         }
1666
1667         edit_cursor->set_position (pointer_frame);
1668         edit_cursor_clock.set (pointer_frame);
1669 }
1670
1671 void
1672 Editor::set_playhead_cursor (GdkEvent* event)
1673 {
1674         nframes_t pointer_frame = event_frame (event);
1675
1676         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1677                 snap_to (pointer_frame);
1678         }
1679
1680         if (session) {
1681                 session->request_locate (pointer_frame, session->transport_rolling());
1682         }
1683 }
1684
1685 void
1686 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
1687 {
1688         drag_info.item = item;
1689         drag_info.motion_callback = &Editor::fade_in_drag_motion_callback;
1690         drag_info.finished_callback = &Editor::fade_in_drag_finished_callback;
1691
1692         start_grab (event);
1693
1694         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1695                 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1696                 /*NOTREACHED*/
1697         }
1698
1699         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1700
1701         drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());  
1702 }
1703
1704 void
1705 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1706 {
1707         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1708         nframes_t pos;
1709         nframes_t fade_length;
1710
1711         if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1712                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1713         }
1714         else {
1715                 pos = 0;
1716         }
1717
1718         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1719                 snap_to (pos);
1720         }
1721
1722         if (pos < (arv->region()->position() + 64)) {
1723                 fade_length = 64; // this should be a minimum defined somewhere
1724         } else if (pos > arv->region()->last_frame()) {
1725                 fade_length = arv->region()->length();
1726         } else {
1727                 fade_length = pos - arv->region()->position();
1728         }               
1729         /* mapover the region selection */
1730
1731         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1732
1733                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1734                 
1735                 if (!tmp) {
1736                         continue;
1737                 }
1738         
1739                 tmp->reset_fade_in_shape_width (fade_length);
1740         }
1741
1742         show_verbose_duration_cursor (arv->region()->position(),  arv->region()->position() + fade_length, 10);
1743
1744         drag_info.first_move = false;
1745 }
1746
1747 void
1748 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1749 {
1750         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1751         nframes_t pos;
1752         nframes_t fade_length;
1753
1754         if (drag_info.first_move) return;
1755
1756         if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1757                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1758         } else {
1759                 pos = 0;
1760         }
1761
1762         if (pos < (arv->region()->position() + 64)) {
1763                 fade_length = 64; // this should be a minimum defined somewhere
1764         } else if (pos > arv->region()->last_frame()) {
1765                 fade_length = arv->region()->length();
1766         } else {
1767                 fade_length = pos - arv->region()->position();
1768         }
1769                 
1770         begin_reversible_command (_("change fade in length"));
1771
1772         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1773
1774                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1775                 
1776                 if (!tmp) {
1777                         continue;
1778                 }
1779         
1780                 AutomationList& alist = tmp->audio_region()->fade_in();
1781                 XMLNode &before = alist.get_state();
1782
1783                 tmp->audio_region()->set_fade_in_length (fade_length);
1784                 
1785                 XMLNode &after = alist.get_state();
1786                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1787         }
1788
1789         commit_reversible_command ();
1790 }
1791
1792 void
1793 Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
1794 {
1795         drag_info.item = item;
1796         drag_info.motion_callback = &Editor::fade_out_drag_motion_callback;
1797         drag_info.finished_callback = &Editor::fade_out_drag_finished_callback;
1798
1799         start_grab (event);
1800
1801         if ((drag_info.data = (item->get_data ("regionview"))) == 0) {
1802                 fatal << _("programming error: fade out canvas item has no regionview data pointer!") << endmsg;
1803                 /*NOTREACHED*/
1804         }
1805
1806         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1807
1808         drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());       
1809 }
1810
1811 void
1812 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1813 {
1814         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1815         nframes_t pos;
1816         nframes_t fade_length;
1817
1818         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1819                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1820         }
1821         else {
1822                 pos = 0;
1823         }
1824
1825         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1826                 snap_to (pos);
1827         }
1828         
1829         if (pos > (arv->region()->last_frame() - 64)) {
1830                 fade_length = 64; // this should really be a minimum fade defined somewhere
1831         }
1832         else if (pos < arv->region()->position()) {
1833                 fade_length = arv->region()->length();
1834         }
1835         else {
1836                 fade_length = arv->region()->last_frame() - pos;
1837         }
1838                 
1839         /* mapover the region selection */
1840
1841         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1842
1843                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1844                 
1845                 if (!tmp) {
1846                         continue;
1847                 }
1848         
1849                 tmp->reset_fade_out_shape_width (fade_length);
1850         }
1851
1852         show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1853
1854         drag_info.first_move = false;
1855 }
1856
1857 void
1858 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1859 {
1860         if (drag_info.first_move) return;
1861
1862         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1863         nframes_t pos;
1864         nframes_t fade_length;
1865
1866         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1867                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1868         }
1869         else {
1870                 pos = 0;
1871         }
1872
1873         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1874                 snap_to (pos);
1875         }
1876
1877         if (pos > (arv->region()->last_frame() - 64)) {
1878                 fade_length = 64; // this should really be a minimum fade defined somewhere
1879         }
1880         else if (pos < arv->region()->position()) {
1881                 fade_length = arv->region()->length();
1882         }
1883         else {
1884                 fade_length = arv->region()->last_frame() - pos;
1885         }
1886
1887         begin_reversible_command (_("change fade out length"));
1888
1889         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1890
1891                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1892                 
1893                 if (!tmp) {
1894                         continue;
1895                 }
1896         
1897                 AutomationList& alist = tmp->audio_region()->fade_out();
1898                 XMLNode &before = alist.get_state();
1899                 
1900                 tmp->audio_region()->set_fade_out_length (fade_length);
1901
1902                 XMLNode &after = alist.get_state();
1903                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1904         }
1905
1906         commit_reversible_command ();
1907 }
1908
1909 void
1910 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1911 {
1912         drag_info.item = item;
1913         drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1914         drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1915
1916         start_grab (event);
1917
1918         if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1919                 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1920                 /*NOTREACHED*/
1921         }
1922
1923         Cursor* cursor = (Cursor *) drag_info.data;
1924
1925         if (cursor == playhead_cursor) {
1926                 _dragging_playhead = true;
1927                 
1928                 if (session && drag_info.was_rolling) {
1929                         session->request_stop ();
1930                 }
1931
1932                 if (session && session->is_auditioning()) {
1933                         session->cancel_audition ();
1934                 }
1935         }
1936
1937         drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;  
1938         
1939         show_verbose_time_cursor (cursor->current_frame, 10);
1940 }
1941
1942 void
1943 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1944 {
1945         Cursor* cursor = (Cursor *) drag_info.data;
1946         nframes_t adjusted_frame;
1947         
1948         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1949                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1950         }
1951         else {
1952                 adjusted_frame = 0;
1953         }
1954         
1955         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1956                 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1957                         snap_to (adjusted_frame);
1958                 }
1959         }
1960         
1961         if (adjusted_frame == drag_info.last_pointer_frame) return;
1962
1963         cursor->set_position (adjusted_frame);
1964         
1965         if (cursor == edit_cursor) {
1966                 edit_cursor_clock.set (cursor->current_frame);
1967         } else {
1968                 UpdateAllTransportClocks (cursor->current_frame);
1969         }
1970
1971         show_verbose_time_cursor (cursor->current_frame, 10);
1972
1973         drag_info.last_pointer_frame = adjusted_frame;
1974         drag_info.first_move = false;
1975 }
1976
1977 void
1978 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1979 {
1980         if (drag_info.first_move) return;
1981         
1982         cursor_drag_motion_callback (item, event);
1983
1984         _dragging_playhead = false;
1985         
1986         if (item == &playhead_cursor->canvas_item) {
1987                 if (session) {
1988                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1989                 }
1990         } else if (item == &edit_cursor->canvas_item) {
1991                 edit_cursor->set_position (edit_cursor->current_frame);
1992                 edit_cursor_clock.set (edit_cursor->current_frame);
1993         } 
1994 }
1995
1996 void
1997 Editor::update_marker_drag_item (Location *location)
1998 {
1999         double x1 = frame_to_pixel (location->start());
2000         double x2 = frame_to_pixel (location->end());
2001
2002         if (location->is_mark()) {
2003                 marker_drag_line_points.front().set_x(x1);
2004                 marker_drag_line_points.back().set_x(x1);
2005                 marker_drag_line->property_points() = marker_drag_line_points;
2006         }
2007         else {
2008                 range_marker_drag_rect->property_x1() = x1;
2009                 range_marker_drag_rect->property_x2() = x2;
2010         }
2011 }
2012
2013 void
2014 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2015 {
2016         Marker* marker;
2017
2018         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2019                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2020                 /*NOTREACHED*/
2021         }
2022
2023         bool is_start;
2024
2025         Location  *location = find_location_from_marker (marker, is_start);
2026
2027         drag_info.item = item;
2028         drag_info.data = marker;
2029         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2030         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2031
2032         start_grab (event);
2033
2034         drag_info.copied_location = new Location (*location);
2035         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
2036
2037         update_marker_drag_item (location);
2038
2039         if (location->is_mark()) {
2040                 marker_drag_line->show();
2041                 marker_drag_line->raise_to_top();
2042         }
2043         else {
2044                 range_marker_drag_rect->show();
2045                 range_marker_drag_rect->raise_to_top();
2046         }
2047         
2048         if (is_start) show_verbose_time_cursor (location->start(), 10);
2049         else show_verbose_time_cursor (location->end(), 10);
2050 }
2051
2052 void
2053 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2054 {
2055         nframes_t f_delta;      
2056         Marker* marker = (Marker *) drag_info.data;
2057         Location  *real_location;
2058         Location  *copy_location;
2059         bool is_start;
2060         bool move_both = false;
2061
2062
2063         nframes_t newframe;
2064         if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
2065                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2066         }
2067         else {
2068                 newframe = 0;
2069         }
2070
2071         nframes_t next = newframe;
2072
2073         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2074                 snap_to (newframe, 0, true);
2075         }
2076         
2077         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { 
2078                 return;
2079         }
2080
2081         /* call this to find out if its the start or end */
2082         
2083         real_location = find_location_from_marker (marker, is_start);
2084
2085         /* use the copy that we're "dragging" around */
2086         
2087         copy_location = drag_info.copied_location;
2088
2089         f_delta = copy_location->end() - copy_location->start();
2090         
2091         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2092                 move_both = true;
2093         }
2094
2095         if (copy_location->is_mark()) {
2096                 /* just move it */
2097
2098                 copy_location->set_start (newframe);
2099
2100         } else {
2101
2102                 if (is_start) { // start-of-range marker
2103                         
2104                         if (move_both) {
2105                                 copy_location->set_start (newframe);
2106                                 copy_location->set_end (newframe + f_delta);
2107                         } else  if (newframe < copy_location->end()) {
2108                                 copy_location->set_start (newframe);
2109                         } else { 
2110                                 snap_to (next, 1, true);
2111                                 copy_location->set_end (next);
2112                                 copy_location->set_start (newframe);
2113                         }
2114                         
2115                 } else { // end marker
2116                         
2117                         if (move_both) {
2118                                 copy_location->set_end (newframe);
2119                                 copy_location->set_start (newframe - f_delta);
2120                         } else if (newframe > copy_location->start()) {
2121                                 copy_location->set_end (newframe);
2122                                 
2123                         } else if (newframe > 0) {
2124                                 snap_to (next, -1, true);
2125                                 copy_location->set_start (next);
2126                                 copy_location->set_end (newframe);
2127                         }
2128                 }
2129         }
2130
2131         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2132         drag_info.first_move = false;
2133
2134         update_marker_drag_item (copy_location);
2135
2136         LocationMarkers* lm = find_location_markers (real_location);
2137         lm->set_position (copy_location->start(), copy_location->end());
2138         
2139         show_verbose_time_cursor (newframe, 10);
2140 }
2141
2142 void
2143 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2144 {
2145         if (drag_info.first_move) {
2146                 marker_drag_motion_callback (item, event);
2147
2148         }
2149         
2150         Marker* marker = (Marker *) drag_info.data;
2151         bool is_start;
2152
2153
2154         begin_reversible_command ( _("move marker") );
2155         XMLNode &before = session->locations()->get_state();
2156         
2157         Location * location = find_location_from_marker (marker, is_start);
2158         
2159         if (location) {
2160                 if (location->is_mark()) {
2161                         location->set_start (drag_info.copied_location->start());
2162                 } else {
2163                         location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2164                 }
2165         }
2166
2167         XMLNode &after = session->locations()->get_state();
2168         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2169         commit_reversible_command ();
2170         
2171         marker_drag_line->hide();
2172         range_marker_drag_rect->hide();
2173 }
2174
2175 void
2176 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2177 {
2178         Marker* marker;
2179         MeterMarker* meter_marker;
2180
2181         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2182                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2183                 /*NOTREACHED*/
2184         }
2185
2186         meter_marker = dynamic_cast<MeterMarker*> (marker);
2187
2188         MetricSection& section (meter_marker->meter());
2189
2190         if (!section.movable()) {
2191                 return;
2192         }
2193
2194         drag_info.item = item;
2195         drag_info.data = marker;
2196         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2197         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2198
2199         start_grab (event);
2200
2201         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2202
2203         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2204 }
2205
2206 void
2207 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2208 {
2209         Marker* marker;
2210         MeterMarker* meter_marker;
2211
2212         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2213                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2214                 /*NOTREACHED*/
2215         }
2216
2217         meter_marker = dynamic_cast<MeterMarker*> (marker);
2218         
2219         // create a dummy marker for visual representation of moving the copy.
2220         // The actual copying is not done before we reach the finish callback.
2221         char name[64];
2222         snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2223         MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name, 
2224                                                   *new MeterSection(meter_marker->meter()));
2225
2226         drag_info.item = &new_marker->the_item();
2227         drag_info.copy = true;
2228         drag_info.data = new_marker;
2229         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2230         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2231
2232         start_grab (event);
2233
2234         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2235
2236         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2237 }
2238
2239 void
2240 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2241 {
2242         MeterMarker* marker = (MeterMarker *) drag_info.data;
2243         nframes_t adjusted_frame;
2244
2245         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2246                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2247         }
2248         else {
2249                 adjusted_frame = 0;
2250         }
2251         
2252         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2253                 snap_to (adjusted_frame);
2254         }
2255         
2256         if (adjusted_frame == drag_info.last_pointer_frame) return;
2257
2258         marker->set_position (adjusted_frame);
2259         
2260         
2261         drag_info.last_pointer_frame = adjusted_frame;
2262         drag_info.first_move = false;
2263
2264         show_verbose_time_cursor (adjusted_frame, 10);
2265 }
2266
2267 void
2268 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2269 {
2270         if (drag_info.first_move) return;
2271
2272         meter_marker_drag_motion_callback (drag_info.item, event);
2273         
2274         MeterMarker* marker = (MeterMarker *) drag_info.data;
2275         BBT_Time when;
2276         
2277         TempoMap& map (session->tempo_map());
2278         map.bbt_time (drag_info.last_pointer_frame, when);
2279         
2280         if (drag_info.copy == true) {
2281                 begin_reversible_command (_("copy meter mark"));
2282                 XMLNode &before = map.get_state();
2283                 map.add_meter (marker->meter(), when);
2284                 XMLNode &after = map.get_state();
2285                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2286                 commit_reversible_command ();
2287                 
2288                 // delete the dummy marker we used for visual representation of copying.
2289                 // a new visual marker will show up automatically.
2290                 delete marker;
2291         } else {
2292                 begin_reversible_command (_("move meter mark"));
2293                 XMLNode &before = map.get_state();
2294                 map.move_meter (marker->meter(), when);
2295                 XMLNode &after = map.get_state();
2296                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2297                 commit_reversible_command ();
2298         }
2299 }
2300
2301 void
2302 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2303 {
2304         Marker* marker;
2305         TempoMarker* tempo_marker;
2306
2307         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2308                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2309                 /*NOTREACHED*/
2310         }
2311
2312         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2313                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2314                 /*NOTREACHED*/
2315         }
2316
2317         MetricSection& section (tempo_marker->tempo());
2318
2319         if (!section.movable()) {
2320                 return;
2321         }
2322
2323         drag_info.item = item;
2324         drag_info.data = marker;
2325         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2326         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2327
2328         start_grab (event);
2329
2330         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2331         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2332 }
2333
2334 void
2335 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2336 {
2337         Marker* marker;
2338         TempoMarker* tempo_marker;
2339
2340         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2341                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2342                 /*NOTREACHED*/
2343         }
2344
2345         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2346                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2347                 /*NOTREACHED*/
2348         }
2349
2350         // create a dummy marker for visual representation of moving the copy.
2351         // The actual copying is not done before we reach the finish callback.
2352         char name[64];
2353         snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2354         TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name, 
2355                                                   *new TempoSection(tempo_marker->tempo()));
2356
2357         drag_info.item = &new_marker->the_item();
2358         drag_info.copy = true;
2359         drag_info.data = new_marker;
2360         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2361         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2362
2363         start_grab (event);
2364
2365         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2366
2367         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2368 }
2369
2370 void
2371 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2372 {
2373         TempoMarker* marker = (TempoMarker *) drag_info.data;
2374         nframes_t adjusted_frame;
2375         
2376         if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2377                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2378         }
2379         else {
2380                 adjusted_frame = 0;
2381         }
2382
2383         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2384                 snap_to (adjusted_frame);
2385         }
2386         
2387         if (adjusted_frame == drag_info.last_pointer_frame) return;
2388
2389         /* OK, we've moved far enough to make it worth actually move the thing. */
2390                 
2391         marker->set_position (adjusted_frame);
2392         
2393         show_verbose_time_cursor (adjusted_frame, 10);
2394
2395         drag_info.last_pointer_frame = adjusted_frame;
2396         drag_info.first_move = false;
2397 }
2398
2399 void
2400 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2401 {
2402         if (drag_info.first_move) return;
2403         
2404         tempo_marker_drag_motion_callback (drag_info.item, event);
2405         
2406         TempoMarker* marker = (TempoMarker *) drag_info.data;
2407         BBT_Time when;
2408         
2409         TempoMap& map (session->tempo_map());
2410         map.bbt_time (drag_info.last_pointer_frame, when);
2411
2412         if (drag_info.copy == true) {
2413                 begin_reversible_command (_("copy tempo mark"));
2414                 XMLNode &before = map.get_state();
2415                 map.add_tempo (marker->tempo(), when);
2416                 XMLNode &after = map.get_state();
2417                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2418                 commit_reversible_command ();
2419                 
2420                 // delete the dummy marker we used for visual representation of copying.
2421                 // a new visual marker will show up automatically.
2422                 delete marker;
2423         } else {
2424                 begin_reversible_command (_("move tempo mark"));
2425                 XMLNode &before = map.get_state();
2426                 map.move_tempo (marker->tempo(), when);
2427                 XMLNode &after = map.get_state();
2428                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2429                 commit_reversible_command ();
2430         }
2431 }
2432
2433 void
2434 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2435 {
2436         ControlPoint* control_point;
2437
2438         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2439                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2440                 /*NOTREACHED*/
2441         }
2442
2443         // We shouldn't remove the first or last gain point
2444         if (control_point->line.is_last_point(*control_point) ||
2445                 control_point->line.is_first_point(*control_point)) {   
2446                 return;
2447         }
2448
2449         control_point->line.remove_point (*control_point);
2450 }
2451
2452 void
2453 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2454 {
2455         ControlPoint* control_point;
2456
2457         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2458                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2459                 /*NOTREACHED*/
2460         }
2461
2462         control_point->line.remove_point (*control_point);
2463 }
2464
2465 void
2466 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2467 {
2468         ControlPoint* control_point;
2469         
2470         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2471                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2472                 /*NOTREACHED*/
2473         }
2474
2475         drag_info.item = item;
2476         drag_info.data = control_point;
2477         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2478         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2479
2480         start_grab (event, fader_cursor);
2481
2482         control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2483
2484         float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
2485         set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction), 
2486                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2487
2488         show_verbose_canvas_cursor ();
2489 }
2490
2491 void
2492 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2493 {
2494         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2495
2496         double cx = drag_info.current_pointer_x;
2497         double cy = drag_info.current_pointer_y;
2498
2499         drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2500         drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2501
2502         if (drag_info.x_constrained) {
2503                 cx = drag_info.grab_x;
2504         }
2505         if (drag_info.y_constrained) {
2506                 cy = drag_info.grab_y;
2507         }
2508
2509         cp->line.parent_group().w2i (cx, cy);
2510
2511         cx = max (0.0, cx);
2512         cy = max (0.0, cy);
2513         cy = min ((double) cp->line.height(), cy);
2514
2515         //translate cx to frames
2516         nframes_t cx_frames = unit_to_frame (cx);
2517
2518         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2519                 snap_to (cx_frames);
2520         }
2521
2522         float fraction = 1.0 - (cy / cp->line.height());
2523         
2524         bool push;
2525
2526         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2527                 push = true;
2528         } else {
2529                 push = false;
2530         }
2531
2532         cp->line.point_drag (*cp, cx_frames , fraction, push);
2533         
2534         set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2535
2536         drag_info.first_move = false;
2537 }
2538
2539 void
2540 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2541 {
2542         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2543
2544         if (drag_info.first_move) {
2545
2546                 /* just a click */
2547                 
2548                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2549                         reset_point_selection ();
2550                 }
2551
2552         } else {
2553                 control_point_drag_motion_callback (item, event);
2554         }
2555         cp->line.end_drag (cp);
2556 }
2557
2558 void
2559 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2560 {
2561         switch (mouse_mode) {
2562         case MouseGain:
2563                 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2564                 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2565                 break;
2566         default:
2567                 break;
2568         }
2569 }
2570
2571 void
2572 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2573 {
2574         AutomationLine* al;
2575         
2576         if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2577                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2578                 /*NOTREACHED*/
2579         }
2580
2581         start_line_grab (al, event);
2582 }
2583
2584 void
2585 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2586 {
2587         double cx;
2588         double cy;
2589         nframes_t frame_within_region;
2590
2591         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2592            origin.
2593         */
2594
2595         cx = event->button.x;
2596         cy = event->button.y;
2597         line->parent_group().w2i (cx, cy);
2598         frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2599
2600         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
2601                                             current_line_drag_info.after)) {
2602                 /* no adjacent points */
2603                 return;
2604         }
2605
2606         drag_info.item = &line->grab_item();
2607         drag_info.data = line;
2608         drag_info.motion_callback = &Editor::line_drag_motion_callback;
2609         drag_info.finished_callback = &Editor::line_drag_finished_callback;
2610
2611         start_grab (event, fader_cursor);
2612
2613         double fraction = 1.0 - (cy / line->height());
2614
2615         line->start_drag (0, drag_info.grab_frame, fraction);
2616         
2617         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2618                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2619         show_verbose_canvas_cursor ();
2620 }
2621
2622 void
2623 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2624 {
2625         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2626         double cx = drag_info.current_pointer_x;
2627         double cy = drag_info.current_pointer_y;
2628
2629         line->parent_group().w2i (cx, cy);
2630         
2631         double fraction;
2632         fraction = 1.0 - (cy / line->height());
2633
2634         bool push;
2635
2636         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2637                 push = false;
2638         } else {
2639                 push = true;
2640         }
2641
2642         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2643         
2644         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2645 }
2646
2647 void
2648 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2649 {
2650         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2651         line_drag_motion_callback (item, event);
2652         line->end_drag (0);
2653 }
2654
2655 void
2656 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2657 {
2658         if (selection->regions.empty() || clicked_regionview == 0) {
2659                 return;
2660         }
2661
2662         drag_info.copy = false;
2663         drag_info.item = item;
2664         drag_info.data = clicked_regionview;
2665         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2666         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2667
2668         start_grab (event);
2669
2670         double speed = 1.0;
2671         TimeAxisView* tvp = clicked_trackview;
2672         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2673
2674         if (tv && tv->is_audio_track()) {
2675                 speed = tv->get_diskstream()->speed();
2676         }
2677         
2678         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2679         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2680         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2681         // we want a move threshold
2682         drag_info.want_move_threshold = true;
2683         
2684         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2685
2686         begin_reversible_command (_("move region(s)"));
2687 }
2688
2689 void
2690 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2691 {
2692         if (selection->regions.empty() || clicked_regionview == 0) {
2693                 return;
2694         }
2695
2696         drag_info.copy = true;
2697         drag_info.item = item;
2698         drag_info.data = clicked_regionview;    
2699
2700         start_grab(event);
2701
2702         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2703         RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
2704         double speed = 1.0;
2705
2706         if (atv && atv->is_audio_track()) {
2707                 speed = atv->get_diskstream()->speed();
2708         }
2709         
2710         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2711         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2712         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2713         // we want a move threshold
2714         drag_info.want_move_threshold = true;
2715         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2716         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2717         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2718 }
2719
2720 void
2721 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2722 {
2723         if (selection->regions.empty() || clicked_regionview == 0) {
2724                 return;
2725         }
2726
2727         drag_info.copy = false;
2728         drag_info.item = item;
2729         drag_info.data = clicked_regionview;
2730         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2731         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2732
2733         start_grab (event);
2734
2735         double speed = 1.0;
2736         TimeAxisView* tvp = clicked_trackview;
2737         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2738
2739         if (tv && tv->is_audio_track()) {
2740                 speed = tv->get_diskstream()->speed();
2741         }
2742         
2743         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2744         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2745         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2746         // we want a move threshold
2747         drag_info.want_move_threshold = true;
2748         drag_info.brushing = true;
2749         
2750         begin_reversible_command (_("Drag region brush"));
2751 }
2752
2753 void
2754 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2755 {
2756         double x_delta;
2757         double y_delta = 0;
2758         RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data); 
2759         nframes_t pending_region_position = 0;
2760         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2761         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
2762         bool clamp_y_axis = false;
2763         vector<int32_t>  height_list(512) ;
2764         vector<int32_t>::iterator j;
2765
2766         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2767
2768                 drag_info.want_move_threshold = false; // don't copy again
2769
2770                 /* duplicate the region(s) */
2771                 
2772                 vector<RegionView*> new_regionviews;
2773                 
2774                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2775                         RegionView* rv;
2776                         RegionView* nrv;
2777                         AudioRegionView* arv;
2778
2779                         rv = (*i);
2780
2781                         
2782                         if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
2783                                 /* XXX handle MIDI here */
2784                                 continue;
2785                         }
2786
2787                         nrv = new AudioRegionView (*arv);
2788                         nrv->get_canvas_group()->show ();
2789
2790                         new_regionviews.push_back (nrv);
2791                 }
2792
2793                 if (new_regionviews.empty()) {
2794                         return;
2795                 }
2796
2797                 /* reset selection to new regionviews */
2798                 
2799                 selection->set (new_regionviews);
2800                 
2801                 /* reset drag_info data to reflect the fact that we are dragging the copies */
2802                 
2803                 drag_info.data = new_regionviews.front();
2804                 
2805                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2806         }
2807
2808         /* Which trackview is this ? */
2809
2810         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2811         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
2812
2813         /* The region motion is only processed if the pointer is over
2814            an audio track.
2815         */
2816         
2817         if (!tv || !tv->is_audio_track()) {
2818                 /* To make sure we hide the verbose canvas cursor when the mouse is 
2819                    not held over and audiotrack. 
2820                 */
2821                 hide_verbose_canvas_cursor ();
2822                 return;
2823         }
2824         
2825         original_pointer_order = drag_info.last_trackview->order;
2826                 
2827         /************************************************************
2828                  Y-Delta Computation
2829         ************************************************************/   
2830
2831         if (drag_info.brushing) {
2832                 clamp_y_axis = true;
2833                 pointer_y_span = 0;
2834                 goto y_axis_done;
2835         }
2836         
2837         if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2838
2839                 int32_t children = 0, numtracks = 0;
2840                 // XXX hard coding track limit, oh my, so very very bad
2841                 bitset <1024> tracks (0x00);
2842                 /* get a bitmask representing the visible tracks */
2843
2844                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2845                         TimeAxisView *tracklist_timeview;
2846                         tracklist_timeview = (*i);
2847                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
2848                         list<TimeAxisView*> children_list;
2849               
2850                         /* zeroes are audio tracks. ones are other types. */
2851               
2852                         if (!atv2->hidden()) {
2853                                 
2854                                 if (visible_y_high < atv2->order) {
2855                                         visible_y_high = atv2->order;
2856                                 }
2857                                 if (visible_y_low > atv2->order) {
2858                                         visible_y_low = atv2->order;
2859                                 }
2860                 
2861                                 if (!atv2->is_audio_track()) {                            
2862                                         tracks = tracks |= (0x01 << atv2->order);
2863                                 }
2864         
2865                                 height_list[atv2->order] = (*i)->height;
2866                                 children = 1;
2867                                 if ((children_list = atv2->get_child_list()).size() > 0) {
2868                                         for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
2869                                                 tracks = tracks |= (0x01 << (atv2->order + children));
2870                                                 height_list[atv2->order + children] =  (*j)->height;                
2871                                                 numtracks++;
2872                                                 children++;     
2873                                         }
2874                                 }
2875                                 numtracks++;        
2876                         }
2877                 }
2878                 /* find the actual span according to the canvas */
2879
2880                 canvas_pointer_y_span = pointer_y_span;
2881                 if (drag_info.last_trackview->order >= tv->order) {
2882                         int32_t y;
2883                         for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2884                                 if (height_list[y] == 0 ) {
2885                                         canvas_pointer_y_span--;
2886                                 }
2887                         }
2888                 } else {
2889                         int32_t y;
2890                         for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2891                                 if (    height_list[y] == 0 ) {
2892                                         canvas_pointer_y_span++;
2893                                 }
2894                         }
2895                 }
2896
2897                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2898                         RegionView* rv2 = (*i);
2899                         double ix1, ix2, iy1, iy2;
2900                         int32_t n = 0;
2901
2902                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2903                         rv2->get_canvas_group()->i2w (ix1, iy1);
2904                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2905                         RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2906
2907                         if (atv2->order != original_pointer_order) {    
2908                                 /* this isn't the pointer track */      
2909
2910                                 if (canvas_pointer_y_span > 0) {
2911
2912                                         /* moving up the canvas */
2913                                         if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
2914         
2915                                                 int32_t visible_tracks = 0;
2916                                                 while (visible_tracks < canvas_pointer_y_span ) {
2917                                                         visible_tracks++;
2918                   
2919                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {
2920                                                                 /* we're passing through a hidden track */
2921                                                                 n--;
2922                                                         }                 
2923                                                 }
2924                  
2925                                                 if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
2926                                                         clamp_y_axis = true;
2927                                                 }
2928                     
2929                                         } else {
2930                                                 clamp_y_axis = true;
2931                                         }                 
2932                   
2933                                 } else if (canvas_pointer_y_span < 0) {
2934
2935                                         /*moving down the canvas*/
2936
2937                                         if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2938                     
2939                     
2940                                                 int32_t visible_tracks = 0;
2941                     
2942                                                 while (visible_tracks > canvas_pointer_y_span ) {
2943                                                         visible_tracks--;
2944                       
2945                                                         while (height_list[atv2->order - (visible_tracks - n)] == 0) {             
2946                                                                 n++;
2947                                                         }                
2948                                                 }
2949                                                 if (  tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2950                                                         clamp_y_axis = true;
2951                             
2952                                                 }
2953                                         } else {
2954                           
2955                                                 clamp_y_axis = true;
2956                                         }
2957                                 }               
2958                   
2959                         } else {
2960                       
2961                                 /* this is the pointer's track */
2962                                 if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
2963                                         clamp_y_axis = true;
2964                                 } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2965                                         clamp_y_axis = true;
2966                                 }
2967                         }             
2968                         if (clamp_y_axis) {
2969                                 break;
2970                         }
2971                 }
2972
2973         } else  if (drag_info.last_trackview == tv) {
2974                 clamp_y_axis = true;
2975         }         
2976
2977   y_axis_done:
2978         if (!clamp_y_axis) {
2979                 drag_info.last_trackview = tv;        
2980         }
2981           
2982         /************************************************************
2983                         X DELTA COMPUTATION
2984         ************************************************************/
2985
2986         /* compute the amount of pointer motion in frames, and where
2987            the region would be if we moved it by that much.
2988         */
2989
2990         if (drag_info.move_threshold_passed) {
2991
2992                 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2993
2994                         nframes_t sync_frame;
2995                         nframes_t sync_offset;
2996                         int32_t sync_dir;
2997             
2998                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2999             
3000                         sync_offset = rv->region()->sync_offset (sync_dir);
3001                         sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3002
3003                         /* we snap if the snap modifier is not enabled.
3004                          */
3005             
3006                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3007                                 snap_to (sync_frame);   
3008                         }
3009             
3010                         if (sync_frame - sync_offset <= sync_frame) {
3011                                 pending_region_position = sync_frame - (sync_dir*sync_offset);
3012                         } else {
3013                                 pending_region_position = 0;
3014                         }
3015             
3016                 } else {
3017                         pending_region_position = 0;
3018                 }
3019           
3020                 if (pending_region_position > max_frames - rv->region()->length()) {
3021                         pending_region_position = drag_info.last_frame_position;
3022                 }
3023           
3024                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3025           
3026                 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3027
3028                         /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3029                            to make it appear at the new location.
3030                         */
3031             
3032                         if (pending_region_position > drag_info.last_frame_position) {
3033                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3034                         } else {
3035                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3036                         }
3037             
3038                         drag_info.last_frame_position = pending_region_position;
3039             
3040                 } else {
3041                         x_delta = 0;
3042                 }
3043
3044         } else {
3045                 /* threshold not passed */
3046
3047                 x_delta = 0;
3048         }
3049
3050         /*************************************************************
3051                         PREPARE TO MOVE
3052         ************************************************************/
3053
3054         if (x_delta == 0 && (pointer_y_span == 0)) {
3055                 /* haven't reached next snap point, and we're not switching
3056                    trackviews. nothing to do.
3057                 */
3058                 return;
3059         } 
3060
3061         if (x_delta < 0) {
3062                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3063
3064                         RegionView* rv2 = (*i);
3065
3066                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3067                         
3068                         double ix1, ix2, iy1, iy2;
3069                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3070                         rv2->get_canvas_group()->i2w (ix1, iy1);
3071
3072                         if (ix1 <= 1) {
3073                                 x_delta = 0;
3074                                 break;
3075                         }
3076                 }
3077         }
3078
3079         /*************************************************************
3080                         MOTION                                                                
3081         ************************************************************/
3082
3083         bool do_move;
3084
3085         if (drag_info.first_move) {
3086                 if (drag_info.move_threshold_passed) {
3087                         do_move = true;
3088                 } else {
3089                         do_move = false;
3090                 }
3091         } else {
3092                 do_move = true;
3093         }
3094
3095         if (do_move) {
3096
3097                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3098                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3099
3100                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3101             
3102                         RegionView* rv = (*i);
3103                         double ix1, ix2, iy1, iy2;
3104                         int32_t temp_pointer_y_span = pointer_y_span;
3105
3106                         /* get item BBox, which will be relative to parent. so we have
3107                            to query on a child, then convert to world coordinates using
3108                            the parent.
3109                         */
3110
3111                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3112                         rv->get_canvas_group()->i2w (ix1, iy1);
3113                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3114                         AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3115                         AudioTimeAxisView* temp_atv;
3116
3117                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3118                                 y_delta = 0;
3119                                 int32_t x = 0;
3120                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3121                                         if (x == canvas_atv->order) {
3122                                                 /* we found the track the region is on */
3123                                                 if (x != original_pointer_order) {
3124                                                         /*this isn't from the same track we're dragging from */
3125                                                         temp_pointer_y_span = canvas_pointer_y_span;
3126                                                 }                 
3127                                                 while (temp_pointer_y_span > 0) {
3128                                                         /* we're moving up canvas-wise,
3129                                                            so  we need to find the next track height
3130                                                         */
3131                                                         if (j != height_list.begin()) {           
3132                                                                 j--;
3133                                                         }
3134                                                         if (x != original_pointer_order) { 
3135                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3136                                                                 if ((*j) == 0) {
3137                                                                         temp_pointer_y_span++;
3138                                                                 }
3139                                                         }          
3140                                                         y_delta -= (*j);        
3141                                                         temp_pointer_y_span--;  
3142                                                 }
3143                                                 while (temp_pointer_y_span < 0) {                 
3144                                                         y_delta += (*j);
3145                                                         if (x != original_pointer_order) { 
3146                                                                 if ((*j) == 0) {
3147                                                                         temp_pointer_y_span--;
3148                                                                 }
3149                                                         }          
3150                     
3151                                                         if (j != height_list.end()) {                 
3152                                                                 j++;
3153                                                         }
3154                                                         temp_pointer_y_span++;
3155                                                 }
3156                                                 /* find out where we'll be when we move and set height accordingly */
3157                   
3158                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3159                                                 temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
3160                                                 rv->set_height (temp_atv->height);
3161         
3162                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3163                                                      personally, i think this can confuse things, but never mind.
3164                                                 */
3165                                   
3166                                                 //const GdkColor& col (temp_atv->view->get_region_color());
3167                                                 //rv->set_color (const_cast<GdkColor&>(col));
3168                                                 break;          
3169                                         }
3170                                         x++;
3171                                 }
3172                         }
3173           
3174                         /* prevent the regionview from being moved to before 
3175                            the zero position on the canvas.
3176                         */
3177                         /* clamp */
3178                 
3179                         if (x_delta < 0) {
3180                                 if (-x_delta > ix1) {
3181                                         x_delta = -ix1;
3182                                 }
3183                         } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3184                                 x_delta = max_frames - rv->region()->last_frame();
3185                         }
3186
3187                         if (drag_info.first_move) {
3188
3189                                 /* hide any dependent views */
3190                         
3191                                 rv->get_time_axis_view().hide_dependent_views (*rv);
3192                         
3193                                 /* this is subtle. raising the regionview itself won't help,
3194                                    because raise_to_top() just puts the item on the top of
3195                                    its parent's stack. so, we need to put the trackview canvas_display group
3196                                    on the top, since its parent is the whole canvas.
3197                                 */
3198                         
3199                                 rv->get_canvas_group()->raise_to_top();
3200                                 rv->get_time_axis_view().canvas_display->raise_to_top();
3201                                 cursor_group->raise_to_top();
3202
3203                                 rv->fake_set_opaque (true);
3204                         }
3205                         
3206                         if (drag_info.brushing) {
3207                                 mouse_brush_insert_region (rv, pending_region_position);
3208                         } else {
3209                                 rv->move (x_delta, y_delta);                    
3210                         }
3211
3212                 } /* foreach region */
3213
3214         } /* if do_move */
3215
3216         if (drag_info.first_move && drag_info.move_threshold_passed) {
3217                 cursor_group->raise_to_top();
3218                 drag_info.first_move = false;
3219         }
3220
3221         if (x_delta != 0 && !drag_info.brushing) {
3222                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3223         }
3224
3225
3226 void
3227 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3228 {
3229         nframes_t where;
3230         RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3231         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3232         bool nocommit = true;
3233         double speed;
3234         RouteTimeAxisView* atv;
3235         bool regionview_y_movement;
3236         bool regionview_x_movement;
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                 goto out;
3258         }
3259
3260         /* adjust for track speed */
3261         speed = 1.0;
3262
3263         atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
3264         if (atv && atv->get_diskstream()) {
3265                 speed = atv->get_diskstream()->speed();
3266         }
3267         
3268         regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3269         regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3270
3271         //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
3272         //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
3273         
3274         char* op_string;
3275
3276         if (drag_info.copy) {
3277                 if (drag_info.x_constrained) {
3278                         op_string = _("fixed time region copy");
3279                 } else {
3280                         op_string = _("region copy");
3281                 } 
3282         } else {
3283                 if (drag_info.x_constrained) {
3284                         op_string = _("fixed time region drag");
3285                 } else {
3286                         op_string = _("region drag");
3287                 }
3288         }
3289
3290         begin_reversible_command (op_string);
3291
3292         if (regionview_y_movement) {
3293
3294                 /* moved to a different audio track. */
3295                 
3296                 vector<RegionView*> new_selection;
3297
3298                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3299             
3300                         RegionView* rv = (*i);              
3301
3302                         double ix1, ix2, iy1, iy2;
3303                         
3304                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3305                         rv->get_canvas_group()->i2w (ix1, iy1);
3306                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3307                         AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
3308
3309                         boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
3310                         boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
3311             
3312                         where = (nframes_t) (unit_to_frame (ix1) * speed);
3313                         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
3314
3315                         /* undo the previous hide_dependent_views so that xfades don't
3316                            disappear on copying regions 
3317                         */
3318
3319                         rv->get_time_axis_view().reveal_dependent_views (*rv);
3320
3321                         if (!drag_info.copy) {
3322                                 
3323                                 /* the region that used to be in the old playlist is not
3324                                    moved to the new one - we make a copy of it. as a result,
3325                                    any existing editor for the region should no longer be
3326                                    visible.
3327                                 */ 
3328             
3329                                 rv->hide_region_editor();
3330                                 rv->fake_set_opaque (false);
3331
3332                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));    
3333                                 from_playlist->remove_region ((rv->region()));
3334                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));    
3335                         }
3336
3337                         latest_regionview = 0;
3338                         
3339                         sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3340                         session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
3341                         to_playlist->add_region (new_region, where);
3342                         session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));        
3343                         c.disconnect ();
3344                                                               
3345                         if (latest_regionview) {
3346                                 new_selection.push_back (latest_regionview);
3347                         }
3348
3349                         if (drag_info.copy) {
3350                                 // get rid of the copy
3351                                 delete rv;
3352                         } 
3353
3354                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
3355                            was selected in all of them, then removing it from the playlist will have removed all
3356                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
3357                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
3358                            corresponding regionview, and the selection is now empty).
3359
3360                            this could have invalidated any and all iterators into the region selection.
3361
3362                            the heuristic we use here is: if the region selection is empty, break out of the loop
3363                            here. if the region selection is not empty, then restart the loop because we know that
3364                            we must have removed at least the region(view) we've just been working on as well as any
3365                            that we processed on previous iterations.
3366                         */
3367
3368                         if (selection->regions.empty()) {
3369                                 break;
3370                         } else { 
3371                                 i = selection->regions.by_layer().begin();
3372                         }
3373                 } 
3374
3375                 selection->set (new_selection);
3376
3377         } else {
3378
3379                 /* motion within a single track */
3380
3381                 list<RegionView*> regions = selection->regions.by_layer();
3382
3383                 if (drag_info.copy) {
3384                         selection->clear_regions();
3385                 }
3386                 
3387                 for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
3388
3389                         rv = (*i);
3390
3391                         if (rv->region()->locked()) {
3392                                 continue;
3393                         }
3394                         
3395
3396                         if (regionview_x_movement) {
3397                                 double ownspeed = 1.0;
3398                                 atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
3399
3400                                 if (atv && atv->get_diskstream()) {
3401                                         ownspeed = atv->get_diskstream()->speed();
3402                                 }
3403                                 
3404                                 /* base the new region position on the current position of the regionview.*/
3405                                 
3406                                 double ix1, ix2, iy1, iy2;
3407                                 
3408                                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3409                                 rv->get_canvas_group()->i2w (ix1, iy1);
3410                                 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3411                                 
3412                         } else {
3413                                 
3414                                 where = rv->region()->position();
3415                         }
3416
3417                         boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
3418
3419                         assert (to_playlist);
3420
3421                         /* add the undo */
3422
3423                         session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
3424
3425                         if (drag_info.copy) {
3426
3427                                 boost::shared_ptr<Region> newregion;
3428                                 boost::shared_ptr<Region> ar;
3429
3430                                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3431                                         newregion = RegionFactory::create (ar);
3432                                 } else {
3433                                         /* XXX MIDI HERE drobilla */
3434                                         continue;
3435                                 }
3436
3437                                 /* add it */
3438                                 
3439                                 latest_regionview = 0;
3440                                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3441                                 to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
3442                                 c.disconnect ();
3443                                 
3444                                 if (latest_regionview) {
3445                                         atv->reveal_dependent_views (*latest_regionview);
3446                                         selection->add (latest_regionview);
3447                                 }
3448                                 
3449                                 /* if the original region was locked, we don't care for the new one */
3450                                 
3451                                 newregion->set_locked (false);                  
3452
3453                         } else {
3454
3455                                 /* just change the model */
3456
3457                                 rv->region()->set_position (where, (void*) this);
3458
3459                         }
3460
3461                         /* add the redo */
3462
3463                         session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
3464
3465                         /* get rid of the copy */
3466
3467                         if (drag_info.copy) {
3468                                 delete rv;
3469                         }
3470                 }
3471         }
3472
3473   out:
3474         
3475         if (!nocommit) {
3476                 commit_reversible_command ();
3477         }
3478 }
3479
3480 void
3481 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3482 {
3483         /* Either add to or set the set the region selection, unless
3484            this is an alignment click (control used)
3485         */
3486         
3487         if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3488                 TimeAxisView* tv = &rv.get_time_axis_view();
3489                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
3490                 double speed = 1.0;
3491                 if (atv && atv->is_audio_track()) {
3492                         speed = atv->get_diskstream()->speed();
3493                 }
3494
3495                 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3496
3497                         align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3498
3499                 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3500
3501                         align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3502
3503                 } else {
3504
3505                         align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3506                 }
3507         }
3508 }
3509
3510 void
3511 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos) 
3512 {
3513         char buf[128];
3514         SMPTE::Time smpte;
3515         BBT_Time bbt;
3516         int hours, mins;
3517         nframes_t frame_rate;
3518         float secs;
3519
3520         if (session == 0) {
3521                 return;
3522         }
3523
3524         switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
3525         case AudioClock::BBT:
3526                 session->bbt_time (frame, bbt);
3527                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3528                 break;
3529                 
3530         case AudioClock::SMPTE:
3531                 session->smpte_time (frame, smpte);
3532                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3533                 break;
3534
3535         case AudioClock::MinSec:
3536                 /* XXX this is copied from show_verbose_duration_cursor() */
3537                 frame_rate = session->frame_rate();
3538                 hours = frame / (frame_rate * 3600);
3539                 frame = frame % (frame_rate * 3600);
3540                 mins = frame / (frame_rate * 60);
3541                 frame = frame % (frame_rate * 60);
3542                 secs = (float) frame / (float) frame_rate;
3543                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3544                 break;
3545
3546         default:
3547                 snprintf (buf, sizeof(buf), "%u", frame);
3548                 break;
3549         }
3550
3551         if (xpos >= 0 && ypos >=0) {
3552                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3553         }
3554         else {
3555                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3556         }
3557         show_verbose_canvas_cursor ();
3558 }
3559
3560 void
3561 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos) 
3562 {
3563         char buf[128];
3564         SMPTE::Time smpte;
3565         BBT_Time sbbt;
3566         BBT_Time ebbt;
3567         int hours, mins;
3568         nframes_t distance, frame_rate;
3569         float secs;
3570         Meter meter_at_start(session->tempo_map().meter_at(start));
3571
3572         if (session == 0) {
3573                 return;
3574         }
3575
3576         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3577         case AudioClock::BBT:
3578                 session->bbt_time (start, sbbt);
3579                 session->bbt_time (end, ebbt);
3580
3581                 /* subtract */
3582                 /* XXX this computation won't work well if the
3583                 user makes a selection that spans any meter changes.
3584                 */
3585
3586                 ebbt.bars -= sbbt.bars;
3587                 if (ebbt.beats >= sbbt.beats) {
3588                         ebbt.beats -= sbbt.beats;
3589                 } else {
3590                         ebbt.bars--;
3591                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3592                 }
3593                 if (ebbt.ticks >= sbbt.ticks) {
3594                         ebbt.ticks -= sbbt.ticks;
3595                 } else {
3596                         ebbt.beats--;
3597                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3598                 }
3599                 
3600                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3601                 break;
3602                 
3603         case AudioClock::SMPTE:
3604                 session->smpte_duration (end - start, smpte);
3605                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3606                 break;
3607
3608         case AudioClock::MinSec:
3609                 /* XXX this stuff should be elsewhere.. */
3610                 distance = end - start;
3611                 frame_rate = session->frame_rate();
3612                 hours = distance / (frame_rate * 3600);
3613                 distance = distance % (frame_rate * 3600);
3614                 mins = distance / (frame_rate * 60);
3615                 distance = distance % (frame_rate * 60);
3616                 secs = (float) distance / (float) frame_rate;
3617                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3618                 break;
3619
3620         default:
3621                 snprintf (buf, sizeof(buf), "%u", end - start);
3622                 break;
3623         }
3624
3625         if (xpos >= 0 && ypos >=0) {
3626                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3627         }
3628         else {
3629                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3630         }
3631         show_verbose_canvas_cursor ();
3632 }
3633
3634 void
3635 Editor::collect_new_region_view (RegionView* rv)
3636 {
3637         latest_regionview = rv;
3638 }
3639
3640 void
3641 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3642 {
3643         if (clicked_regionview == 0) {
3644                 return;
3645         }
3646
3647         /* lets try to create new Region for the selection */
3648
3649         vector<boost::shared_ptr<AudioRegion> > new_regions;
3650         create_region_from_selection (new_regions);
3651
3652         if (new_regions.empty()) {
3653                 return;
3654         }
3655
3656         /* XXX fix me one day to use all new regions */
3657         
3658         boost::shared_ptr<Region> region (new_regions.front());
3659
3660         /* add it to the current stream/playlist.
3661
3662            tricky: the streamview for the track will add a new regionview. we will
3663            catch the signal it sends when it creates the regionview to
3664            set the regionview we want to then drag.
3665         */
3666         
3667         latest_regionview = 0;
3668         sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3669         
3670         /* A selection grab currently creates two undo/redo operations, one for 
3671            creating the new region and another for moving it.
3672         */
3673
3674         begin_reversible_command (_("selection grab"));
3675
3676         boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
3677
3678         XMLNode *before = &(playlist->get_state());
3679         clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
3680         XMLNode *after = &(playlist->get_state());
3681         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3682
3683         commit_reversible_command ();
3684         
3685         c.disconnect ();
3686         
3687         if (latest_regionview == 0) {
3688                 /* something went wrong */
3689                 return;
3690         }
3691
3692         /* we need to deselect all other regionviews, and select this one
3693            i'm ignoring undo stuff, because the region creation will take care of it */
3694         selection->set (latest_regionview);
3695         
3696         drag_info.item = latest_regionview->get_canvas_group();
3697         drag_info.data = latest_regionview;
3698         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3699         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3700
3701         start_grab (event);
3702         
3703         drag_info.last_trackview = clicked_trackview;
3704         drag_info.last_frame_position = latest_regionview->region()->position();
3705         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3706         
3707         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3708 }
3709
3710 void
3711 Editor::cancel_selection ()
3712 {
3713         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3714                 (*i)->hide_selection ();
3715         }
3716         begin_reversible_command (_("cancel selection"));
3717         selection->clear ();
3718         clicked_selection = 0;
3719         commit_reversible_command ();
3720 }       
3721
3722 void
3723 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3724 {
3725         nframes_t start = 0;
3726         nframes_t end = 0;
3727
3728         if (session == 0) {
3729                 return;
3730         }
3731
3732         drag_info.item = item;
3733         drag_info.motion_callback = &Editor::drag_selection;
3734         drag_info.finished_callback = &Editor::end_selection_op;
3735
3736         selection_op = op;
3737
3738         switch (op) {
3739         case CreateSelection:
3740                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3741                         drag_info.copy = true;
3742                 } else {
3743                         drag_info.copy = false;
3744                 }
3745                 start_grab (event, selector_cursor);
3746                 break;
3747
3748         case SelectionStartTrim:
3749                 if (clicked_trackview) {
3750                         clicked_trackview->order_selection_trims (item, true);
3751                 } 
3752                 start_grab (event, trimmer_cursor);
3753                 start = selection->time[clicked_selection].start;
3754                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3755                 break;
3756                 
3757         case SelectionEndTrim:
3758                 if (clicked_trackview) {
3759                         clicked_trackview->order_selection_trims (item, false);
3760                 }
3761                 start_grab (event, trimmer_cursor);
3762                 end = selection->time[clicked_selection].end;
3763                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
3764                 break;
3765
3766         case SelectionMove:
3767                 start = selection->time[clicked_selection].start;
3768                 start_grab (event);
3769                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3770                 break;
3771         }
3772
3773         if (selection_op == SelectionMove) {
3774                 show_verbose_time_cursor(start, 10);    
3775         } else {
3776                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3777         }
3778 }
3779
3780 void
3781 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3782 {
3783         nframes_t start = 0;
3784         nframes_t end = 0;
3785         nframes_t length;
3786         nframes_t pending_position;
3787
3788         if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3789                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3790         }
3791         else {
3792                 pending_position = 0;
3793         }
3794         
3795         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3796                 snap_to (pending_position);
3797         }
3798
3799         /* only alter selection if the current frame is 
3800            different from the last frame position (adjusted)
3801          */
3802         
3803         if (pending_position == drag_info.last_pointer_frame) return;
3804         
3805         switch (selection_op) {
3806         case CreateSelection:
3807                 
3808                 if (drag_info.first_move) {
3809                         snap_to (drag_info.grab_frame);
3810                 }
3811                 
3812                 if (pending_position < drag_info.grab_frame) {
3813                         start = pending_position;
3814                         end = drag_info.grab_frame;
3815                 } else {
3816                         end = pending_position;
3817                         start = drag_info.grab_frame;
3818                 }
3819                 
3820                 /* first drag: Either add to the selection
3821                    or create a new selection->
3822                 */
3823                 
3824                 if (drag_info.first_move) {
3825                         
3826                         begin_reversible_command (_("range selection"));
3827                         
3828                         if (drag_info.copy) {
3829                                 /* adding to the selection */
3830                                 clicked_selection = selection->add (start, end);
3831                                 drag_info.copy = false;
3832                         } else {
3833                                 /* new selection-> */
3834                                 clicked_selection = selection->set (clicked_trackview, start, end);
3835                         }
3836                 } 
3837                 break;
3838                 
3839         case SelectionStartTrim:
3840                 
3841                 if (drag_info.first_move) {
3842                         begin_reversible_command (_("trim selection start"));
3843                 }
3844                 
3845                 start = selection->time[clicked_selection].start;
3846                 end = selection->time[clicked_selection].end;
3847
3848                 if (pending_position > end) {
3849                         start = end;
3850                 } else {
3851                         start = pending_position;
3852                 }
3853                 break;
3854                 
3855         case SelectionEndTrim:
3856                 
3857                 if (drag_info.first_move) {
3858                         begin_reversible_command (_("trim selection end"));
3859                 }
3860                 
3861                 start = selection->time[clicked_selection].start;
3862                 end = selection->time[clicked_selection].end;
3863
3864                 if (pending_position < start) {
3865                         end = start;
3866                 } else {
3867                         end = pending_position;
3868                 }
3869                 
3870                 break;
3871                 
3872         case SelectionMove:
3873                 
3874                 if (drag_info.first_move) {
3875                         begin_reversible_command (_("move selection"));
3876                 }
3877                 
3878                 start = selection->time[clicked_selection].start;
3879                 end = selection->time[clicked_selection].end;
3880                 
3881                 length = end - start;
3882                 
3883                 start = pending_position;
3884                 snap_to (start);
3885                 
3886                 end = start + length;
3887                 
3888                 break;
3889         }
3890         
3891         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3892                 start_canvas_autoscroll (1);
3893         }
3894
3895         if (start != end) {
3896                 selection->replace (clicked_selection, start, end);
3897         }
3898
3899         drag_info.last_pointer_frame = pending_position;
3900         drag_info.first_move = false;
3901
3902         if (selection_op == SelectionMove) {
3903                 show_verbose_time_cursor(start, 10);    
3904         } else {
3905                 show_verbose_time_cursor(pending_position, 10); 
3906         }
3907 }
3908
3909 void
3910 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3911 {
3912         if (!drag_info.first_move) {
3913                 drag_selection (item, event);
3914                 /* XXX this is not object-oriented programming at all. ick */
3915                 if (selection->time.consolidate()) {
3916                         selection->TimeChanged ();
3917                 }
3918                 commit_reversible_command ();
3919         } else {
3920                 /* just a click, no pointer movement.*/
3921
3922                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3923
3924                         selection->clear_time();
3925
3926                 } 
3927         }
3928
3929         /* XXX what happens if its a music selection? */
3930         session->set_audio_range (selection->time);
3931         stop_canvas_autoscroll ();
3932 }
3933
3934 void
3935 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3936 {
3937         double speed = 1.0;
3938         TimeAxisView* tvp = clicked_trackview;
3939         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
3940
3941         if (tv && tv->is_audio_track()) {
3942                 speed = tv->get_diskstream()->speed();
3943         }
3944         
3945         nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3946         nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3947         nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3948
3949         //drag_info.item = clicked_regionview->get_name_highlight();
3950         drag_info.item = item;
3951         drag_info.motion_callback = &Editor::trim_motion_callback;
3952         drag_info.finished_callback = &Editor::trim_finished_callback;
3953
3954         start_grab (event, trimmer_cursor);
3955         
3956         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3957                 trim_op = ContentsTrim;
3958         } else {
3959                 /* These will get overridden for a point trim.*/
3960                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3961                         /* closer to start */
3962                         trim_op = StartTrim;
3963                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3964                         /* closer to end */
3965                         trim_op = EndTrim;
3966                 }
3967         }
3968
3969         switch (trim_op) {
3970         case StartTrim:
3971                 show_verbose_time_cursor(region_start, 10);     
3972                 break;
3973         case EndTrim:
3974                 show_verbose_time_cursor(region_end, 10);       
3975                 break;
3976         case ContentsTrim:
3977                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3978                 break;
3979         }
3980 }
3981
3982 void
3983 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3984 {
3985         RegionView* rv = clicked_regionview;
3986         nframes_t frame_delta = 0;
3987         bool left_direction;
3988         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3989
3990         /* snap modifier works differently here..
3991            its' current state has to be passed to the 
3992            various trim functions in order to work properly 
3993         */ 
3994
3995         double speed = 1.0;
3996         TimeAxisView* tvp = clicked_trackview;
3997         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3998         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3999
4000         if (tv && tv->is_audio_track()) {
4001                 speed = tv->get_diskstream()->speed();
4002         }
4003         
4004         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4005                 left_direction = true;
4006         } else {
4007                 left_direction = false;
4008         }
4009
4010         if (obey_snap) {
4011                 snap_to (drag_info.current_pointer_frame);
4012         }
4013
4014         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4015                 return;
4016         }
4017
4018         if (drag_info.first_move) {
4019         
4020                 string trim_type;
4021
4022                 switch (trim_op) {
4023                 case StartTrim:
4024                         trim_type = "Region start trim";
4025                         break;
4026                 case EndTrim:
4027                         trim_type = "Region end trim";
4028                         break;
4029                 case ContentsTrim:
4030                         trim_type = "Region content trim";
4031                         break;
4032                 }
4033
4034                 begin_reversible_command (trim_type);
4035
4036                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4037                         (*i)->fake_set_opaque(false);
4038                         (*i)->region()->freeze ();
4039                 
4040                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4041                         if (arv)
4042                                 arv->temporarily_hide_envelope ();
4043
4044                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4045                         insert_result = motion_frozen_playlists.insert (pl);
4046                         if (insert_result.second) {
4047                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4048                         }
4049                 }
4050         }
4051
4052         if (left_direction) {
4053                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4054         } else {
4055                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4056         }
4057
4058         switch (trim_op) {              
4059         case StartTrim:
4060                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4061                         break;
4062                 } else {
4063                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4064                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4065                         }
4066                         break;
4067                 }
4068                 
4069         case EndTrim:
4070                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4071                         break;
4072                 } else {
4073                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4074                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4075                         }
4076                         break;
4077                 }
4078                 
4079         case ContentsTrim:
4080                 {
4081                         bool swap_direction = false;
4082
4083                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4084                                 swap_direction = true;
4085                         }
4086                         
4087                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4088                              i != selection->regions.by_layer().end(); ++i)
4089                         {
4090                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4091                         }
4092                 }
4093                 break;
4094         }
4095
4096         switch (trim_op) {
4097         case StartTrim:
4098                 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);     
4099                 break;
4100         case EndTrim:
4101                 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);   
4102                 break;
4103         case ContentsTrim:
4104                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4105                 break;
4106         }
4107
4108         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4109         drag_info.first_move = false;
4110 }
4111
4112 void
4113 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4114 {
4115         boost::shared_ptr<Region> region (rv.region());
4116
4117         if (region->locked()) {
4118                 return;
4119         }
4120
4121         nframes_t new_bound;
4122
4123         double speed = 1.0;
4124         TimeAxisView* tvp = clicked_trackview;
4125         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4126
4127         if (tv && tv->is_audio_track()) {
4128                 speed = tv->get_diskstream()->speed();
4129         }
4130         
4131         if (left_direction) {
4132                 if (swap_direction) {
4133                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4134                 } else {
4135                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4136                 }
4137         } else {
4138                 if (swap_direction) {
4139                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4140                 } else {
4141                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4142                 }
4143         }
4144
4145         if (obey_snap) {
4146                 snap_to (new_bound);
4147         }
4148         region->trim_start ((nframes_t) (new_bound * speed), this);     
4149         rv.region_changed (StartChanged);
4150 }
4151
4152 void
4153 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4154 {
4155         boost::shared_ptr<Region> region (rv.region()); 
4156
4157         if (region->locked()) {
4158                 return;
4159         }
4160
4161         nframes_t new_bound;
4162
4163         double speed = 1.0;
4164         TimeAxisView* tvp = clicked_trackview;
4165         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4166
4167         if (tv && tv->is_audio_track()) {
4168                 speed = tv->get_diskstream()->speed();
4169         }
4170         
4171         if (left_direction) {
4172                 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4173         } else {
4174                 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4175         }
4176
4177         if (obey_snap) {
4178                 snap_to (new_bound, (left_direction ? 0 : 1));  
4179         }
4180
4181         region->trim_front ((nframes_t) (new_bound * speed), this);
4182
4183         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4184 }
4185
4186 void
4187 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4188 {
4189         boost::shared_ptr<Region> region (rv.region());
4190
4191         if (region->locked()) {
4192                 return;
4193         }
4194
4195         nframes_t new_bound;
4196
4197         double speed = 1.0;
4198         TimeAxisView* tvp = clicked_trackview;
4199         AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
4200
4201         if (tv && tv->is_audio_track()) {
4202                 speed = tv->get_diskstream()->speed();
4203         }
4204         
4205         if (left_direction) {
4206                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4207         } else {
4208                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4209         }
4210
4211         if (obey_snap) {
4212                 snap_to (new_bound);
4213         }
4214         region->trim_end ((nframes_t) (new_bound * speed), this);
4215         rv.region_changed (LengthChanged);
4216 }
4217         
4218 void
4219 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4220 {
4221         if (!drag_info.first_move) {
4222                 trim_motion_callback (item, event);
4223                 
4224                 if (!clicked_regionview->get_selected()) {
4225                         thaw_region_after_trim (*clicked_regionview);           
4226                 } else {
4227                         
4228                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4229                              i != selection->regions.by_layer().end(); ++i)
4230                         {
4231                                 thaw_region_after_trim (**i);
4232                                 (*i)->fake_set_opaque (true);
4233                         }
4234                 }
4235                 
4236                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4237                         //(*p)->thaw ();
4238                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4239                 }
4240                 
4241                 motion_frozen_playlists.clear ();
4242
4243                 commit_reversible_command();
4244         } else {
4245                 /* no mouse movement */
4246                 point_trim (event);
4247         }
4248 }
4249
4250 void
4251 Editor::point_trim (GdkEvent* event)
4252 {
4253         RegionView* rv = clicked_regionview;
4254         nframes_t new_bound = drag_info.current_pointer_frame;
4255
4256         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4257                 snap_to (new_bound);
4258         }
4259
4260         /* Choose action dependant on which button was pressed */
4261         switch (event->button.button) {
4262         case 1:
4263                 trim_op = StartTrim;
4264                 begin_reversible_command (_("Start point trim"));
4265
4266                 if (rv->get_selected()) {
4267
4268                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4269                              i != selection->regions.by_layer().end(); ++i)
4270                         {
4271                                 if (!(*i)->region()->locked()) {
4272                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4273                                         XMLNode &before = pl->get_state();
4274                                         (*i)->region()->trim_front (new_bound, this);   
4275                                         XMLNode &after = pl->get_state();
4276                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4277                                 }
4278                         }
4279
4280                 } else {
4281
4282                         if (!rv->region()->locked()) {
4283                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4284                                 XMLNode &before = pl->get_state();
4285                                 rv->region()->trim_front (new_bound, this);     
4286                                 XMLNode &after = pl->get_state();
4287                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4288                         }
4289                 }
4290
4291                 commit_reversible_command();
4292         
4293                 break;
4294         case 2:
4295                 trim_op = EndTrim;
4296                 begin_reversible_command (_("End point trim"));
4297
4298                 if (rv->get_selected()) {
4299                         
4300                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4301                         {
4302                                 if (!(*i)->region()->locked()) {
4303                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4304                                         XMLNode &before = pl->get_state();
4305                                         (*i)->region()->trim_end (new_bound, this);
4306                                         XMLNode &after = pl->get_state();
4307                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4308                                 }
4309                         }
4310
4311                 } else {
4312
4313                         if (!rv->region()->locked()) {
4314                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4315                                 XMLNode &before = pl->get_state();
4316                                 rv->region()->trim_end (new_bound, this);
4317                                 XMLNode &after = pl->get_state();
4318                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4319                         }
4320                 }
4321
4322                 commit_reversible_command();
4323         
4324                 break;
4325         default:
4326                 break;
4327         }
4328 }
4329
4330 void
4331 Editor::thaw_region_after_trim (RegionView& rv)
4332 {
4333         boost::shared_ptr<Region> region (rv.region());
4334
4335         if (region->locked()) {
4336                 return;
4337         }
4338
4339         region->thaw (_("trimmed region"));
4340         XMLNode &after = region->playlist()->get_state();
4341         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4342
4343         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4344         if (arv)
4345                 arv->unhide_envelope ();
4346 }
4347
4348 void
4349 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4350 {
4351         Marker* marker;
4352         bool is_start;
4353
4354         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4355                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4356                 /*NOTREACHED*/
4357         }
4358
4359         Location* location = find_location_from_marker (marker, is_start);      
4360         location->set_hidden (true, this);
4361 }
4362
4363
4364 void
4365 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4366 {
4367         if (session == 0) {
4368                 return;
4369         }
4370
4371         drag_info.item = item;
4372         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4373         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4374
4375         range_marker_op = op;
4376
4377         if (!temp_location) {
4378                 temp_location = new Location;
4379         }
4380         
4381         switch (op) {
4382         case CreateRangeMarker:
4383         case CreateTransportMarker:
4384                 
4385                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4386                         drag_info.copy = true;
4387                 } else {
4388                         drag_info.copy = false;
4389                 }
4390                 start_grab (event, selector_cursor);
4391                 break;
4392         }
4393
4394         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4395         
4396 }
4397
4398 void
4399 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4400 {
4401         nframes_t start = 0;
4402         nframes_t end = 0;
4403         ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4404         
4405         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4406                 snap_to (drag_info.current_pointer_frame);
4407         }
4408
4409         /* only alter selection if the current frame is 
4410            different from the last frame position.
4411          */
4412         
4413         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4414         
4415         switch (range_marker_op) {
4416         case CreateRangeMarker:
4417         case CreateTransportMarker:
4418                 if (drag_info.first_move) {
4419                         snap_to (drag_info.grab_frame);
4420                 }
4421                 
4422                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4423                         start = drag_info.current_pointer_frame;
4424                         end = drag_info.grab_frame;
4425                 } else {
4426                         end = drag_info.current_pointer_frame;
4427                         start = drag_info.grab_frame;
4428                 }
4429                 
4430                 /* first drag: Either add to the selection
4431                    or create a new selection.
4432                 */
4433                 
4434                 if (drag_info.first_move) {
4435                         
4436                         temp_location->set (start, end);
4437                         
4438                         crect->show ();
4439
4440                         update_marker_drag_item (temp_location);
4441                         range_marker_drag_rect->show();
4442                         range_marker_drag_rect->raise_to_top();
4443                         
4444                 } 
4445                 break;          
4446         }
4447         
4448         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4449                 start_canvas_autoscroll (1);
4450         }
4451         
4452         if (start != end) {
4453                 temp_location->set (start, end);
4454
4455                 double x1 = frame_to_pixel (start);
4456                 double x2 = frame_to_pixel (end);
4457                 crect->property_x1() = x1;
4458                 crect->property_x2() = x2;
4459
4460                 update_marker_drag_item (temp_location);
4461         }
4462
4463         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4464         drag_info.first_move = false;
4465
4466         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4467         
4468 }
4469
4470 void
4471 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4472 {
4473         Location * newloc = 0;
4474         string rangename;
4475         
4476         if (!drag_info.first_move) {
4477                 drag_range_markerbar_op (item, event);
4478
4479                 switch (range_marker_op) {
4480                 case CreateRangeMarker:
4481                     {
4482                         begin_reversible_command (_("new range marker"));
4483                         XMLNode &before = session->locations()->get_state();
4484                         session->locations()->next_available_name(rangename,"unnamed");
4485                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4486                         session->locations()->add (newloc, true);
4487                         XMLNode &after = session->locations()->get_state();
4488                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4489                         commit_reversible_command ();
4490                         
4491                         range_bar_drag_rect->hide();
4492                         range_marker_drag_rect->hide();
4493                         break;
4494                     }
4495
4496                 case CreateTransportMarker:
4497                         // popup menu to pick loop or punch
4498                         new_transport_marker_context_menu (&event->button, item);
4499                         
4500                         break;
4501                 }
4502         } else {
4503                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4504
4505                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4506
4507                         nframes_t start;
4508                         nframes_t end;
4509
4510                         start = session->locations()->first_mark_before (drag_info.grab_frame);
4511                         end = session->locations()->first_mark_after (drag_info.grab_frame);
4512                         
4513                         if (end == max_frames) {
4514                                 end = session->current_end_frame ();
4515                         }
4516
4517                         if (start == 0) {
4518                                 start = session->current_start_frame ();
4519                         }
4520
4521                         switch (mouse_mode) {
4522                         case MouseObject:
4523                                 /* find the two markers on either side and then make the selection from it */
4524                                 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4525                                 break;
4526
4527                         case MouseRange:
4528                                 /* find the two markers on either side of the click and make the range out of it */
4529                                 selection->set (0, start, end);
4530                                 break;
4531
4532                         default:
4533                                 break;
4534                         }
4535                 } 
4536         }
4537
4538         stop_canvas_autoscroll ();
4539 }
4540
4541
4542
4543 void
4544 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4545 {
4546         drag_info.item = item;
4547         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4548         drag_info.finished_callback = &Editor::end_mouse_zoom;
4549
4550         start_grab (event, zoom_cursor);
4551
4552         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4553 }
4554
4555 void
4556 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4557 {
4558         nframes_t start;
4559         nframes_t end;
4560
4561         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4562                 snap_to (drag_info.current_pointer_frame);
4563                 
4564                 if (drag_info.first_move) {
4565                         snap_to (drag_info.grab_frame);
4566                 }
4567         }
4568                 
4569         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4570
4571         /* base start and end on initial click position */
4572         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4573                 start = drag_info.current_pointer_frame;
4574                 end = drag_info.grab_frame;
4575         } else {
4576                 end = drag_info.current_pointer_frame;
4577                 start = drag_info.grab_frame;
4578         }
4579         
4580         if (start != end) {
4581
4582                 if (drag_info.first_move) {
4583                         zoom_rect->show();
4584                         zoom_rect->raise_to_top();
4585                 }
4586
4587                 reposition_zoom_rect(start, end);
4588
4589                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4590                 drag_info.first_move = false;
4591
4592                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4593         }
4594 }
4595
4596 void
4597 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4598 {
4599         if (!drag_info.first_move) {
4600                 drag_mouse_zoom (item, event);
4601                 
4602                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4603                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4604                 } else {
4605                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4606                 }               
4607         } else {
4608                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4609                 /*
4610                 temporal_zoom_step (false);
4611                 center_screen (drag_info.grab_frame);
4612                 */
4613         }
4614
4615         zoom_rect->hide();
4616 }
4617
4618 void
4619 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4620 {
4621         double x1 = frame_to_pixel (start);
4622         double x2 = frame_to_pixel (end);
4623         double y2 = full_canvas_height - 1.0;
4624
4625         zoom_rect->property_x1() = x1;
4626         zoom_rect->property_y1() = 1.0;
4627         zoom_rect->property_x2() = x2;
4628         zoom_rect->property_y2() = y2;
4629 }
4630
4631 void
4632 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4633 {
4634         drag_info.item = item;
4635         drag_info.motion_callback = &Editor::drag_rubberband_select;
4636         drag_info.finished_callback = &Editor::end_rubberband_select;
4637
4638         start_grab (event, cross_hair_cursor);
4639
4640         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4641 }
4642
4643 void
4644 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4645 {
4646         nframes_t start;
4647         nframes_t end;
4648         double y1;
4649         double y2;
4650
4651         /* use a bigger drag threshold than the default */
4652
4653         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4654                 return;
4655         }
4656
4657         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4658                 if (drag_info.first_move) {
4659                         snap_to (drag_info.grab_frame);
4660                 } 
4661                 snap_to (drag_info.current_pointer_frame);
4662         }
4663
4664         /* base start and end on initial click position */
4665
4666         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4667                 start = drag_info.current_pointer_frame;
4668                 end = drag_info.grab_frame;
4669         } else {
4670                 end = drag_info.current_pointer_frame;
4671                 start = drag_info.grab_frame;
4672         }
4673
4674         if (drag_info.current_pointer_y < drag_info.grab_y) {
4675                 y1 = drag_info.current_pointer_y;
4676                 y2 = drag_info.grab_y;
4677         } else {
4678                 y2 = drag_info.current_pointer_y;
4679                 y1 = drag_info.grab_y;
4680         }
4681
4682         
4683         if (start != end || y1 != y2) {
4684
4685                 double x1 = frame_to_pixel (start);
4686                 double x2 = frame_to_pixel (end);
4687                 
4688                 rubberband_rect->property_x1() = x1;
4689                 rubberband_rect->property_y1() = y1;
4690                 rubberband_rect->property_x2() = x2;
4691                 rubberband_rect->property_y2() = y2;
4692
4693                 rubberband_rect->show();
4694                 rubberband_rect->raise_to_top();
4695                 
4696                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4697                 drag_info.first_move = false;
4698
4699                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4700         }
4701 }
4702
4703 void
4704 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4705 {
4706         if (!drag_info.first_move) {
4707
4708                 drag_rubberband_select (item, event);
4709
4710                 double y1,y2;
4711                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4712                         y1 = drag_info.current_pointer_y;
4713                         y2 = drag_info.grab_y;
4714                 }
4715                 else {
4716                         y2 = drag_info.current_pointer_y;
4717                         y1 = drag_info.grab_y;
4718                 }
4719
4720
4721                 Selection::Operation op = Keyboard::selection_type (event->button.state);
4722                 bool commit;
4723
4724                 begin_reversible_command (_("rubberband selection"));
4725
4726                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4727                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4728                 } else {
4729                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4730                 }               
4731
4732                 if (commit) {
4733                         commit_reversible_command ();
4734                 }
4735                 
4736         } else {
4737                 selection->clear_tracks();
4738                 selection->clear_regions();
4739                 selection->clear_points ();
4740                 selection->clear_lines ();
4741         }
4742
4743         rubberband_rect->hide();
4744 }
4745
4746
4747 gint
4748 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4749 {
4750         using namespace Gtkmm2ext;
4751
4752         ArdourPrompter prompter (false);
4753
4754         prompter.set_prompt (_("Name for region:"));
4755         prompter.set_initial_text (clicked_regionview->region()->name());
4756         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4757         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4758         prompter.show_all ();
4759         switch (prompter.run ()) {
4760         case Gtk::RESPONSE_ACCEPT:
4761         string str;
4762                 prompter.get_result(str);
4763                 if (str.length()) {
4764                 clicked_regionview->region()->set_name (str);
4765                 }
4766                 break;
4767         }
4768         return true;
4769 }
4770
4771 void
4772 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4773 {
4774         drag_info.item = item;
4775         drag_info.motion_callback = &Editor::time_fx_motion;
4776         drag_info.finished_callback = &Editor::end_time_fx;
4777
4778         start_grab (event);
4779
4780         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4781 }
4782
4783 void
4784 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4785 {
4786         RegionView* rv = clicked_regionview;
4787
4788         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4789                 snap_to (drag_info.current_pointer_frame);
4790         }
4791
4792         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4793                 return;
4794         }
4795
4796         if (drag_info.current_pointer_frame > rv->region()->position()) {
4797                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4798         }
4799
4800         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4801         drag_info.first_move = false;
4802
4803         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4804 }
4805
4806 void
4807 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4808 {
4809         clicked_regionview->get_time_axis_view().hide_timestretch ();
4810
4811         if (drag_info.first_move) {
4812                 return;
4813         }
4814         
4815         nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4816         float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4817         
4818         begin_reversible_command (_("timestretch"));
4819
4820         if (run_timestretch (selection->regions, percentage) == 0) {
4821                 session->commit_reversible_command ();
4822         }
4823 }
4824
4825 void
4826 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4827 {
4828         /* no brushing without a useful snap setting */
4829
4830         // FIXME
4831         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4832         assert(arv);
4833
4834         switch (snap_mode) {
4835         case SnapMagnetic:
4836                 return; /* can't work because it allows region to be placed anywhere */
4837         default:
4838                 break; /* OK */
4839         }
4840
4841         switch (snap_type) {
4842         case SnapToFrame:
4843         case SnapToMark:
4844         case SnapToEditCursor:
4845                 return;
4846
4847         default:
4848                 break;
4849         }
4850
4851         /* don't brush a copy over the original */
4852         
4853         if (pos == rv->region()->position()) {
4854                 return;
4855         }
4856
4857         RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4858
4859         if (atv == 0 || !atv->is_audio_track()) {
4860                 return;
4861         }
4862
4863         boost::shared_ptr<Playlist> playlist = atv->playlist();
4864         double speed = atv->get_diskstream()->speed();
4865         
4866         XMLNode &before = playlist->get_state();
4867         playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4868         XMLNode &after = playlist->get_state();
4869         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4870         
4871         // playlist is frozen, so we have to update manually
4872         
4873         playlist->Modified(); /* EMIT SIGNAL */
4874 }
4875
4876 gint
4877 Editor::track_height_step_timeout ()
4878 {
4879         struct timeval now;
4880         struct timeval delta;
4881         
4882         gettimeofday (&now, 0);
4883         timersub (&now, &last_track_height_step_timestamp, &delta);
4884         
4885         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4886                 current_stepping_trackview = 0;
4887                 return false;
4888         }
4889         return true;
4890 }