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