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