Show selected MIDI track in editor mixer.
[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 ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
1413                         bool y_threshold_passed =  (abs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
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 (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 (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 (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1780                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1781         } else {
1782                 pos = 0;
1783         }
1784
1785         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1786                 snap_to (pos);
1787         }
1788         
1789         if (pos > (arv->region()->last_frame() - 64)) {
1790                 fade_length = 64; // this should really be a minimum fade defined somewhere
1791         }
1792         else if (pos < arv->region()->position()) {
1793                 fade_length = arv->region()->length();
1794         }
1795         else {
1796                 fade_length = arv->region()->last_frame() - pos;
1797         }
1798                 
1799         /* mapover the region selection */
1800
1801         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1802
1803                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1804                 
1805                 if (!tmp) {
1806                         continue;
1807                 }
1808         
1809                 tmp->reset_fade_out_shape_width (fade_length);
1810         }
1811
1812         show_verbose_duration_cursor (arv->region()->last_frame() - fade_length, arv->region()->last_frame(), 10);
1813
1814         drag_info.first_move = false;
1815 }
1816
1817 void
1818 Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1819 {
1820         if (drag_info.first_move) return;
1821
1822         AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
1823         nframes_t pos;
1824         nframes_t fade_length;
1825
1826         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1827                 pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1828         }
1829         else {
1830                 pos = 0;
1831         }
1832
1833         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1834                 snap_to (pos);
1835         }
1836
1837         if (pos > (arv->region()->last_frame() - 64)) {
1838                 fade_length = 64; // this should really be a minimum fade defined somewhere
1839         }
1840         else if (pos < arv->region()->position()) {
1841                 fade_length = arv->region()->length();
1842         }
1843         else {
1844                 fade_length = arv->region()->last_frame() - pos;
1845         }
1846
1847         begin_reversible_command (_("change fade out length"));
1848
1849         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1850
1851                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1852                 
1853                 if (!tmp) {
1854                         continue;
1855                 }
1856         
1857                 AutomationList& alist = tmp->audio_region()->fade_out();
1858                 XMLNode &before = alist.get_state();
1859                 
1860                 tmp->audio_region()->set_fade_out_length (fade_length);
1861
1862                 XMLNode &after = alist.get_state();
1863                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
1864         }
1865
1866         commit_reversible_command ();
1867 }
1868
1869 void
1870 Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
1871 {
1872         drag_info.item = item;
1873         drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
1874         drag_info.finished_callback = &Editor::cursor_drag_finished_callback;
1875
1876         start_grab (event);
1877
1878         if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
1879                 fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
1880                 /*NOTREACHED*/
1881         }
1882
1883         Cursor* cursor = (Cursor *) drag_info.data;
1884
1885         if (cursor == playhead_cursor) {
1886                 _dragging_playhead = true;
1887                 
1888                 if (session && drag_info.was_rolling) {
1889                         session->request_stop ();
1890                 }
1891
1892                 if (session && session->is_auditioning()) {
1893                         session->cancel_audition ();
1894                 }
1895         }
1896
1897         drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;  
1898         
1899         show_verbose_time_cursor (cursor->current_frame, 10);
1900 }
1901
1902 void
1903 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
1904 {
1905         Cursor* cursor = (Cursor *) drag_info.data;
1906         nframes_t adjusted_frame;
1907         
1908         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
1909                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
1910         }
1911         else {
1912                 adjusted_frame = 0;
1913         }
1914         
1915         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
1916                 if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
1917                         snap_to (adjusted_frame);
1918                 }
1919         }
1920         
1921         if (adjusted_frame == drag_info.last_pointer_frame) return;
1922
1923         cursor->set_position (adjusted_frame);
1924         
1925         if (cursor == edit_cursor) {
1926                 edit_cursor_clock.set (cursor->current_frame);
1927         } else {
1928                 UpdateAllTransportClocks (cursor->current_frame);
1929         }
1930
1931         show_verbose_time_cursor (cursor->current_frame, 10);
1932
1933         drag_info.last_pointer_frame = adjusted_frame;
1934         drag_info.first_move = false;
1935 }
1936
1937 void
1938 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
1939 {
1940         if (drag_info.first_move) return;
1941         
1942         cursor_drag_motion_callback (item, event);
1943
1944         _dragging_playhead = false;
1945         
1946         if (item == &playhead_cursor->canvas_item) {
1947                 if (session) {
1948                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
1949                 }
1950         } else if (item == &edit_cursor->canvas_item) {
1951                 edit_cursor->set_position (edit_cursor->current_frame);
1952                 edit_cursor_clock.set (edit_cursor->current_frame);
1953         } 
1954 }
1955
1956 void
1957 Editor::update_marker_drag_item (Location *location)
1958 {
1959         double x1 = frame_to_pixel (location->start());
1960         double x2 = frame_to_pixel (location->end());
1961
1962         if (location->is_mark()) {
1963                 marker_drag_line_points.front().set_x(x1);
1964                 marker_drag_line_points.back().set_x(x1);
1965                 marker_drag_line->property_points() = marker_drag_line_points;
1966         }
1967         else {
1968                 range_marker_drag_rect->property_x1() = x1;
1969                 range_marker_drag_rect->property_x2() = x2;
1970         }
1971 }
1972
1973 void
1974 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
1975 {
1976         Marker* marker;
1977
1978         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1979                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1980                 /*NOTREACHED*/
1981         }
1982
1983         bool is_start;
1984
1985         Location  *location = find_location_from_marker (marker, is_start);
1986
1987         drag_info.item = item;
1988         drag_info.data = marker;
1989         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
1990         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
1991
1992         start_grab (event);
1993
1994         drag_info.copied_location = new Location (*location);
1995         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
1996
1997         update_marker_drag_item (location);
1998
1999         if (location->is_mark()) {
2000                 marker_drag_line->show();
2001                 marker_drag_line->raise_to_top();
2002         }
2003         else {
2004                 range_marker_drag_rect->show();
2005                 range_marker_drag_rect->raise_to_top();
2006         }
2007         
2008         if (is_start) show_verbose_time_cursor (location->start(), 10);
2009         else show_verbose_time_cursor (location->end(), 10);
2010 }
2011
2012 void
2013 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2014 {
2015         nframes_t f_delta;      
2016         Marker* marker = (Marker *) drag_info.data;
2017         Location  *real_location;
2018         Location  *copy_location;
2019         bool is_start;
2020         bool move_both = false;
2021
2022
2023         nframes_t newframe;
2024         if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2025                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2026         } else {
2027                 newframe = 0;
2028         }
2029
2030         nframes_t next = newframe;
2031
2032         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2033                 snap_to (newframe, 0, true);
2034         }
2035         
2036         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { 
2037                 return;
2038         }
2039
2040         /* call this to find out if its the start or end */
2041         
2042         real_location = find_location_from_marker (marker, is_start);
2043
2044         /* use the copy that we're "dragging" around */
2045         
2046         copy_location = drag_info.copied_location;
2047
2048         f_delta = copy_location->end() - copy_location->start();
2049         
2050         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
2051                 move_both = true;
2052         }
2053
2054         if (copy_location->is_mark()) {
2055                 /* just move it */
2056
2057                 copy_location->set_start (newframe);
2058
2059         } else {
2060
2061                 if (is_start) { // start-of-range marker
2062                         
2063                         if (move_both) {
2064                                 copy_location->set_start (newframe);
2065                                 copy_location->set_end (newframe + f_delta);
2066                         } else  if (newframe < copy_location->end()) {
2067                                 copy_location->set_start (newframe);
2068                         } else { 
2069                                 snap_to (next, 1, true);
2070                                 copy_location->set_end (next);
2071                                 copy_location->set_start (newframe);
2072                         }
2073                         
2074                 } else { // end marker
2075                         
2076                         if (move_both) {
2077                                 copy_location->set_end (newframe);
2078                                 copy_location->set_start (newframe - f_delta);
2079                         } else if (newframe > copy_location->start()) {
2080                                 copy_location->set_end (newframe);
2081                                 
2082                         } else if (newframe > 0) {
2083                                 snap_to (next, -1, true);
2084                                 copy_location->set_start (next);
2085                                 copy_location->set_end (newframe);
2086                         }
2087                 }
2088         }
2089
2090         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2091         drag_info.first_move = false;
2092
2093         update_marker_drag_item (copy_location);
2094
2095         LocationMarkers* lm = find_location_markers (real_location);
2096         lm->set_position (copy_location->start(), copy_location->end());
2097         
2098         show_verbose_time_cursor (newframe, 10);
2099 }
2100
2101 void
2102 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2103 {
2104         if (drag_info.first_move) {
2105                 marker_drag_motion_callback (item, event);
2106
2107         }
2108         
2109         Marker* marker = (Marker *) drag_info.data;
2110         bool is_start;
2111
2112
2113         begin_reversible_command ( _("move marker") );
2114         XMLNode &before = session->locations()->get_state();
2115         
2116         Location * location = find_location_from_marker (marker, is_start);
2117         
2118         if (location) {
2119                 if (location->is_mark()) {
2120                         location->set_start (drag_info.copied_location->start());
2121                 } else {
2122                         location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
2123                 }
2124         }
2125
2126         XMLNode &after = session->locations()->get_state();
2127         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2128         commit_reversible_command ();
2129         
2130         marker_drag_line->hide();
2131         range_marker_drag_rect->hide();
2132 }
2133
2134 void
2135 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2136 {
2137         Marker* marker;
2138         MeterMarker* meter_marker;
2139
2140         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2141                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2142                 /*NOTREACHED*/
2143         }
2144
2145         meter_marker = dynamic_cast<MeterMarker*> (marker);
2146
2147         MetricSection& section (meter_marker->meter());
2148
2149         if (!section.movable()) {
2150                 return;
2151         }
2152
2153         drag_info.item = item;
2154         drag_info.data = marker;
2155         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2156         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2157
2158         start_grab (event);
2159
2160         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2161
2162         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2163 }
2164
2165 void
2166 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2167 {
2168         Marker* marker;
2169         MeterMarker* meter_marker;
2170
2171         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2172                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2173                 /*NOTREACHED*/
2174         }
2175
2176         meter_marker = dynamic_cast<MeterMarker*> (marker);
2177         
2178         // create a dummy marker for visual representation of moving the copy.
2179         // The actual copying is not done before we reach the finish callback.
2180         char name[64];
2181         snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2182         MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name, 
2183                                                   *new MeterSection(meter_marker->meter()));
2184
2185         drag_info.item = &new_marker->the_item();
2186         drag_info.copy = true;
2187         drag_info.data = new_marker;
2188         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2189         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2190
2191         start_grab (event);
2192
2193         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2194
2195         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2196 }
2197
2198 void
2199 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2200 {
2201         MeterMarker* marker = (MeterMarker *) drag_info.data;
2202         nframes_t adjusted_frame;
2203
2204         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2205                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2206         }
2207         else {
2208                 adjusted_frame = 0;
2209         }
2210         
2211         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2212                 snap_to (adjusted_frame);
2213         }
2214         
2215         if (adjusted_frame == drag_info.last_pointer_frame) return;
2216
2217         marker->set_position (adjusted_frame);
2218         
2219         
2220         drag_info.last_pointer_frame = adjusted_frame;
2221         drag_info.first_move = false;
2222
2223         show_verbose_time_cursor (adjusted_frame, 10);
2224 }
2225
2226 void
2227 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2228 {
2229         if (drag_info.first_move) return;
2230
2231         meter_marker_drag_motion_callback (drag_info.item, event);
2232         
2233         MeterMarker* marker = (MeterMarker *) drag_info.data;
2234         BBT_Time when;
2235         
2236         TempoMap& map (session->tempo_map());
2237         map.bbt_time (drag_info.last_pointer_frame, when);
2238         
2239         if (drag_info.copy == true) {
2240                 begin_reversible_command (_("copy meter mark"));
2241                 XMLNode &before = map.get_state();
2242                 map.add_meter (marker->meter(), when);
2243                 XMLNode &after = map.get_state();
2244                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2245                 commit_reversible_command ();
2246                 
2247                 // delete the dummy marker we used for visual representation of copying.
2248                 // a new visual marker will show up automatically.
2249                 delete marker;
2250         } else {
2251                 begin_reversible_command (_("move meter mark"));
2252                 XMLNode &before = map.get_state();
2253                 map.move_meter (marker->meter(), when);
2254                 XMLNode &after = map.get_state();
2255                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2256                 commit_reversible_command ();
2257         }
2258 }
2259
2260 void
2261 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2262 {
2263         Marker* marker;
2264         TempoMarker* tempo_marker;
2265
2266         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2267                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2268                 /*NOTREACHED*/
2269         }
2270
2271         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2272                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2273                 /*NOTREACHED*/
2274         }
2275
2276         MetricSection& section (tempo_marker->tempo());
2277
2278         if (!section.movable()) {
2279                 return;
2280         }
2281
2282         drag_info.item = item;
2283         drag_info.data = marker;
2284         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2285         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2286
2287         start_grab (event);
2288
2289         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2290         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2291 }
2292
2293 void
2294 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2295 {
2296         Marker* marker;
2297         TempoMarker* tempo_marker;
2298
2299         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2300                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2301                 /*NOTREACHED*/
2302         }
2303
2304         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2305                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2306                 /*NOTREACHED*/
2307         }
2308
2309         // create a dummy marker for visual representation of moving the copy.
2310         // The actual copying is not done before we reach the finish callback.
2311         char name[64];
2312         snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2313         TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name, 
2314                                                   *new TempoSection(tempo_marker->tempo()));
2315
2316         drag_info.item = &new_marker->the_item();
2317         drag_info.copy = true;
2318         drag_info.data = new_marker;
2319         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2320         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2321
2322         start_grab (event);
2323
2324         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2325
2326         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2327 }
2328
2329 void
2330 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2331 {
2332         TempoMarker* marker = (TempoMarker *) drag_info.data;
2333         nframes_t adjusted_frame;
2334         
2335         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2336                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2337         }
2338         else {
2339                 adjusted_frame = 0;
2340         }
2341
2342         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2343                 snap_to (adjusted_frame);
2344         }
2345         
2346         if (adjusted_frame == drag_info.last_pointer_frame) return;
2347
2348         /* OK, we've moved far enough to make it worth actually move the thing. */
2349                 
2350         marker->set_position (adjusted_frame);
2351         
2352         show_verbose_time_cursor (adjusted_frame, 10);
2353
2354         drag_info.last_pointer_frame = adjusted_frame;
2355         drag_info.first_move = false;
2356 }
2357
2358 void
2359 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2360 {
2361         if (drag_info.first_move) return;
2362         
2363         tempo_marker_drag_motion_callback (drag_info.item, event);
2364         
2365         TempoMarker* marker = (TempoMarker *) drag_info.data;
2366         BBT_Time when;
2367         
2368         TempoMap& map (session->tempo_map());
2369         map.bbt_time (drag_info.last_pointer_frame, when);
2370
2371         if (drag_info.copy == true) {
2372                 begin_reversible_command (_("copy tempo mark"));
2373                 XMLNode &before = map.get_state();
2374                 map.add_tempo (marker->tempo(), when);
2375                 XMLNode &after = map.get_state();
2376                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2377                 commit_reversible_command ();
2378                 
2379                 // delete the dummy marker we used for visual representation of copying.
2380                 // a new visual marker will show up automatically.
2381                 delete marker;
2382         } else {
2383                 begin_reversible_command (_("move tempo mark"));
2384                 XMLNode &before = map.get_state();
2385                 map.move_tempo (marker->tempo(), when);
2386                 XMLNode &after = map.get_state();
2387                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2388                 commit_reversible_command ();
2389         }
2390 }
2391
2392 void
2393 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2394 {
2395         ControlPoint* control_point;
2396
2397         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2398                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2399                 /*NOTREACHED*/
2400         }
2401
2402         // We shouldn't remove the first or last gain point
2403         if (control_point->line.is_last_point(*control_point) ||
2404                 control_point->line.is_first_point(*control_point)) {   
2405                 return;
2406         }
2407
2408         control_point->line.remove_point (*control_point);
2409 }
2410
2411 void
2412 Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
2413 {
2414         ControlPoint* control_point;
2415
2416         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2417                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2418                 /*NOTREACHED*/
2419         }
2420
2421         control_point->line.remove_point (*control_point);
2422 }
2423
2424 void
2425 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
2426 {
2427         ControlPoint* control_point;
2428
2429         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2430                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2431                 /*NOTREACHED*/
2432         }
2433
2434         drag_info.item = item;
2435         drag_info.data = control_point;
2436         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
2437         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
2438
2439         start_grab (event, fader_cursor);
2440
2441         control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
2442
2443         float fraction = 1.0 - ((control_point->get_y() - control_point->line.y_position()) / control_point->line.height());
2444         set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction), 
2445                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2446
2447         show_verbose_canvas_cursor ();
2448 }
2449
2450 void
2451 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2452 {
2453         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2454
2455         double cx = drag_info.current_pointer_x;
2456         double cy = drag_info.current_pointer_y;
2457
2458         drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
2459         drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
2460
2461         if (drag_info.x_constrained) {
2462                 cx = drag_info.grab_x;
2463         }
2464         if (drag_info.y_constrained) {
2465                 cy = drag_info.grab_y;
2466         }
2467
2468         cp->line.parent_group().w2i (cx, cy);
2469
2470         cx = max (0.0, cx);
2471         cy = max (0.0, cy);
2472         cy = min ((double) (cp->line.y_position() + cp->line.height()), cy);
2473
2474         //translate cx to frames
2475         nframes_t cx_frames = unit_to_frame (cx);
2476
2477         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
2478                 snap_to (cx_frames);
2479         }
2480
2481         float const fraction = 1.0 - ((cy - cp->line.y_position()) / cp->line.height());
2482
2483         bool push;
2484
2485         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2486                 push = true;
2487         } else {
2488                 push = false;
2489         }
2490
2491         cp->line.point_drag (*cp, cx_frames , fraction, push);
2492         
2493         set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
2494
2495         drag_info.first_move = false;
2496 }
2497
2498 void
2499 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2500 {
2501         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
2502
2503         if (drag_info.first_move) {
2504
2505                 /* just a click */
2506                 
2507                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
2508                         reset_point_selection ();
2509                 }
2510
2511         } else {
2512                 control_point_drag_motion_callback (item, event);
2513         }
2514         cp->line.end_drag (cp);
2515 }
2516
2517 void
2518 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
2519 {
2520         switch (mouse_mode) {
2521         case MouseGain:
2522                 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
2523                 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
2524                 break;
2525         default:
2526                 break;
2527         }
2528 }
2529
2530 void
2531 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
2532 {
2533         AutomationLine* al;
2534         
2535         if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
2536                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
2537                 /*NOTREACHED*/
2538         }
2539
2540         start_line_grab (al, event);
2541 }
2542
2543 void
2544 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
2545 {
2546         double cx;
2547         double cy;
2548         nframes_t frame_within_region;
2549
2550         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2551            origin.
2552         */
2553
2554         cx = event->button.x;
2555         cy = event->button.y;
2556         line->parent_group().w2i (cx, cy);
2557         frame_within_region = (nframes_t) floor (cx * frames_per_unit);
2558
2559         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
2560                                             current_line_drag_info.after)) {
2561                 /* no adjacent points */
2562                 return;
2563         }
2564
2565         drag_info.item = &line->grab_item();
2566         drag_info.data = line;
2567         drag_info.motion_callback = &Editor::line_drag_motion_callback;
2568         drag_info.finished_callback = &Editor::line_drag_finished_callback;
2569
2570         start_grab (event, fader_cursor);
2571
2572         double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2573
2574         line->start_drag (0, drag_info.grab_frame, fraction);
2575         
2576         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
2577                                    drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
2578         show_verbose_canvas_cursor ();
2579 }
2580
2581 void
2582 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2583 {
2584         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2585         double cx = drag_info.current_pointer_x;
2586         double cy = drag_info.current_pointer_y;
2587
2588         line->parent_group().w2i (cx, cy);
2589         
2590         double const fraction = 1.0 - ((cy - line->y_position()) / line->height());
2591
2592         bool push;
2593
2594         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
2595                 push = false;
2596         } else {
2597                 push = true;
2598         }
2599
2600         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
2601         
2602         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
2603 }
2604
2605 void
2606 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2607 {
2608         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
2609         line_drag_motion_callback (item, event);
2610         line->end_drag (0);
2611 }
2612
2613 void
2614 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
2615 {
2616         if (selection->regions.empty() || clicked_regionview == 0) {
2617                 return;
2618         }
2619
2620         drag_info.copy = false;
2621         drag_info.item = item;
2622         drag_info.data = clicked_regionview;
2623         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2624         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2625
2626         start_grab (event);
2627
2628         double speed = 1.0;
2629         TimeAxisView* tvp = clicked_axisview;
2630         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2631
2632         if (tv && tv->is_track()) {
2633                 speed = tv->get_diskstream()->speed();
2634         }
2635         
2636         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2637         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2638         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2639         // we want a move threshold
2640         drag_info.want_move_threshold = true;
2641         
2642         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2643
2644         begin_reversible_command (_("move region(s)"));
2645 }
2646
2647 void
2648 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2649 {
2650         if (selection->regions.empty() || clicked_regionview == 0) {
2651                 return;
2652         }
2653
2654         drag_info.copy = true;
2655         drag_info.item = item;
2656         drag_info.data = clicked_regionview;    
2657
2658         start_grab(event);
2659
2660         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
2661         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2662         double speed = 1.0;
2663
2664         if (rtv && rtv->is_track()) {
2665                 speed = rtv->get_diskstream()->speed();
2666         }
2667         
2668         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2669         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2670         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2671         // we want a move threshold
2672         drag_info.want_move_threshold = true;
2673         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2674         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2675         show_verbose_time_cursor (drag_info.last_frame_position, 10);
2676 }
2677
2678 void
2679 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
2680 {
2681         if (selection->regions.empty() || clicked_regionview == 0) {
2682                 return;
2683         }
2684
2685         drag_info.copy = false;
2686         drag_info.item = item;
2687         drag_info.data = clicked_regionview;
2688         drag_info.motion_callback = &Editor::region_drag_motion_callback;
2689         drag_info.finished_callback = &Editor::region_drag_finished_callback;
2690
2691         start_grab (event);
2692
2693         double speed = 1.0;
2694         TimeAxisView* tvp = clicked_axisview;
2695         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2696
2697         if (tv && tv->is_track()) {
2698                 speed = tv->get_diskstream()->speed();
2699         }
2700         
2701         drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
2702         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
2703         drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
2704         // we want a move threshold
2705         drag_info.want_move_threshold = true;
2706         drag_info.brushing = true;
2707         
2708         begin_reversible_command (_("Drag region brush"));
2709 }
2710
2711 void
2712 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2713 {
2714         double x_delta;
2715         double y_delta = 0;
2716         RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data); 
2717         nframes_t pending_region_position = 0;
2718         int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
2719         int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
2720         bool clamp_y_axis = false;
2721         vector<int32_t>  height_list(512) ;
2722         vector<int32_t>::iterator j;
2723
2724         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
2725
2726                 drag_info.want_move_threshold = false; // don't copy again
2727
2728                 /* duplicate the region(s) */
2729
2730                 vector<RegionView*> new_regionviews;
2731                 
2732                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2733                         RegionView* rv;
2734                         RegionView* nrv;
2735
2736                         rv = (*i);
2737
2738                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
2739                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
2740
2741                         if (arv) {
2742                                 nrv = new AudioRegionView (*arv);
2743                         } else if (mrv) {
2744                                 nrv = new MidiRegionView (*mrv);
2745                         } else {
2746                                 continue;
2747                         }
2748
2749                         nrv->get_canvas_group()->show ();
2750
2751                         new_regionviews.push_back (nrv);
2752                 }
2753
2754                 if (new_regionviews.empty()) {
2755                         return;
2756                 }
2757
2758                 /* reset selection to new regionviews */
2759                 
2760                 selection->set (new_regionviews);
2761                 
2762                 /* reset drag_info data to reflect the fact that we are dragging the copies */
2763                 
2764                 drag_info.data = new_regionviews.front();
2765
2766                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2767         }
2768
2769         /* Which trackview is this ? */
2770
2771         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2772         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2773
2774         /* The region motion is only processed if the pointer is over
2775            an audio track.
2776         */
2777         
2778         if (!tv || !tv->is_track()) {
2779                 /* To make sure we hide the verbose canvas cursor when the mouse is 
2780                    not held over a track. 
2781                 */
2782                 hide_verbose_canvas_cursor ();
2783                 return;
2784         }
2785         
2786         original_pointer_order = drag_info.last_trackview->order;
2787                 
2788         /************************************************************
2789                  Y-Delta Computation
2790         ************************************************************/   
2791
2792         if (drag_info.brushing) {
2793                 clamp_y_axis = true;
2794                 pointer_y_span = 0;
2795                 goto y_axis_done;
2796         }
2797         
2798         if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2799
2800                 int32_t children = 0, numtracks = 0;
2801                 // XXX hard coding track limit, oh my, so very very bad
2802                 bitset <1024> tracks (0x00);
2803                 /* get a bitmask representing the visible tracks */
2804
2805                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2806                         TimeAxisView *tracklist_timeview;
2807                         tracklist_timeview = (*i);
2808                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2809                         list<TimeAxisView*> children_list;
2810               
2811                         /* zeroes are audio tracks. ones are other types. */
2812               
2813                         if (!rtv2->hidden()) {
2814                                 
2815                                 if (visible_y_high < rtv2->order) {
2816                                         visible_y_high = rtv2->order;
2817                                 }
2818                                 if (visible_y_low > rtv2->order) {
2819                                         visible_y_low = rtv2->order;
2820                                 }
2821                 
2822                                 if (!rtv2->is_track()) {                                  
2823                                         tracks = tracks |= (0x01 << rtv2->order);
2824                                 }
2825         
2826                                 height_list[rtv2->order] = (*i)->height;
2827                                 children = 1;
2828                                 if ((children_list = rtv2->get_child_list()).size() > 0) {
2829                                         for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
2830                                                 tracks = tracks |= (0x01 << (rtv2->order + children));
2831                                                 height_list[rtv2->order + children] =  (*j)->height;                
2832                                                 numtracks++;
2833                                                 children++;     
2834                                         }
2835                                 }
2836                                 numtracks++;        
2837                         }
2838                 }
2839                 /* find the actual span according to the canvas */
2840
2841                 canvas_pointer_y_span = pointer_y_span;
2842                 if (drag_info.last_trackview->order >= tv->order) {
2843                         int32_t y;
2844                         for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2845                                 if (height_list[y] == 0 ) {
2846                                         canvas_pointer_y_span--;
2847                                 }
2848                         }
2849                 } else {
2850                         int32_t y;
2851                         for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2852                                 if (    height_list[y] == 0 ) {
2853                                         canvas_pointer_y_span++;
2854                                 }
2855                         }
2856                 }
2857
2858                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2859                         RegionView* rv2 = (*i);
2860                         double ix1, ix2, iy1, iy2;
2861                         int32_t n = 0;
2862
2863                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2864                         rv2->get_canvas_group()->i2w (ix1, iy1);
2865                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2866                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2867
2868                         if (rtv2->order != original_pointer_order) {    
2869                                 /* this isn't the pointer track */      
2870
2871                                 if (canvas_pointer_y_span > 0) {
2872
2873                                         /* moving up the canvas */
2874                                         if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2875         
2876                                                 int32_t visible_tracks = 0;
2877                                                 while (visible_tracks < canvas_pointer_y_span ) {
2878                                                         visible_tracks++;
2879                   
2880                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2881                                                                 /* we're passing through a hidden track */
2882                                                                 n--;
2883                                                         }                 
2884                                                 }
2885                  
2886                                                 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
2887                                                         clamp_y_axis = true;
2888                                                 }
2889                     
2890                                         } else {
2891                                                 clamp_y_axis = true;
2892                                         }                 
2893                   
2894                                 } else if (canvas_pointer_y_span < 0) {
2895
2896                                         /*moving down the canvas*/
2897
2898                                         if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2899                     
2900                     
2901                                                 int32_t visible_tracks = 0;
2902                     
2903                                                 while (visible_tracks > canvas_pointer_y_span ) {
2904                                                         visible_tracks--;
2905                       
2906                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
2907                                                                 n++;
2908                                                         }                
2909                                                 }
2910                                                 if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2911                                                         clamp_y_axis = true;
2912                             
2913                                                 }
2914                                         } else {
2915                           
2916                                                 clamp_y_axis = true;
2917                                         }
2918                                 }               
2919                   
2920                         } else {
2921                       
2922                                 /* this is the pointer's track */
2923                                 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
2924                                         clamp_y_axis = true;
2925                                 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
2926                                         clamp_y_axis = true;
2927                                 }
2928                         }             
2929                         if (clamp_y_axis) {
2930                                 break;
2931                         }
2932                 }
2933
2934         } else  if (drag_info.last_trackview == tv) {
2935                 clamp_y_axis = true;
2936         }         
2937
2938   y_axis_done:
2939         if (!clamp_y_axis) {
2940                 drag_info.last_trackview = tv;        
2941         }
2942           
2943         /************************************************************
2944                         X DELTA COMPUTATION
2945         ************************************************************/
2946
2947         /* compute the amount of pointer motion in frames, and where
2948            the region would be if we moved it by that much.
2949         */
2950
2951         if (drag_info.move_threshold_passed) {
2952
2953                 if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2954
2955                         nframes_t sync_frame;
2956                         nframes_t sync_offset;
2957                         int32_t sync_dir;
2958             
2959                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2960
2961                         sync_offset = rv->region()->sync_offset (sync_dir);
2962                         sync_frame = rv->region()->adjust_to_sync (pending_region_position);
2963
2964                         /* we snap if the snap modifier is not enabled.
2965                          */
2966             
2967                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2968                                 snap_to (sync_frame);   
2969                         }
2970             
2971                         if (sync_frame - sync_offset <= sync_frame) {
2972                                 pending_region_position = sync_frame - (sync_dir*sync_offset);
2973                         } else {
2974                                 pending_region_position = 0;
2975                         }
2976             
2977                 } else {
2978                         pending_region_position = 0;
2979                 }
2980           
2981                 if (pending_region_position > max_frames - rv->region()->length()) {
2982                         pending_region_position = drag_info.last_frame_position;
2983                 }
2984           
2985                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
2986           
2987                 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
2988
2989                         /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
2990                            to make it appear at the new location.
2991                         */
2992
2993                         if (pending_region_position > drag_info.last_frame_position) {
2994                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
2995                         } else {
2996                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
2997                         }
2998
2999                         drag_info.last_frame_position = pending_region_position;
3000             
3001                 } else {
3002                         x_delta = 0;
3003                 }
3004
3005         } else {
3006                 /* threshold not passed */
3007
3008                 x_delta = 0;
3009         }
3010
3011         /*************************************************************
3012                         PREPARE TO MOVE
3013         ************************************************************/
3014
3015         if (x_delta == 0 && (pointer_y_span == 0)) {
3016                 /* haven't reached next snap point, and we're not switching
3017                    trackviews. nothing to do.
3018                 */
3019                 return;
3020         } 
3021
3022
3023         if (x_delta < 0) {
3024                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3025
3026                         RegionView* rv2 = (*i);
3027
3028                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3029                         
3030                         double ix1, ix2, iy1, iy2;
3031                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3032                         rv2->get_canvas_group()->i2w (ix1, iy1);
3033
3034                         if (ix1 <= 1) {
3035                                 x_delta = 0;
3036                                 break;
3037                         }
3038                 }
3039         }
3040
3041         /*************************************************************
3042                         MOTION                                                                
3043         ************************************************************/
3044
3045         bool do_move;
3046
3047         if (drag_info.first_move) {
3048                 if (drag_info.move_threshold_passed) {
3049                         do_move = true;
3050                 } else {
3051                         do_move = false;
3052                 }
3053         } else {
3054                 do_move = true;
3055         }
3056
3057         if (do_move) {
3058
3059                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3060                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
3061
3062                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3063             
3064                         RegionView* rv = (*i);
3065                         double ix1, ix2, iy1, iy2;
3066                         int32_t temp_pointer_y_span = pointer_y_span;
3067
3068                         /* get item BBox, which will be relative to parent. so we have
3069                            to query on a child, then convert to world coordinates using
3070                            the parent.
3071                         */
3072
3073                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3074                         rv->get_canvas_group()->i2w (ix1, iy1);
3075                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3076                         RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3077                         RouteTimeAxisView* temp_rtv;
3078
3079                         if ((pointer_y_span != 0) && !clamp_y_axis) {
3080                                 y_delta = 0;
3081                                 int32_t x = 0;
3082                                 for (j = height_list.begin(); j!= height_list.end(); j++) {     
3083                                         if (x == canvas_rtv->order) {
3084                                                 /* we found the track the region is on */
3085                                                 if (x != original_pointer_order) {
3086                                                         /*this isn't from the same track we're dragging from */
3087                                                         temp_pointer_y_span = canvas_pointer_y_span;
3088                                                 }                 
3089                                                 while (temp_pointer_y_span > 0) {
3090                                                         /* we're moving up canvas-wise,
3091                                                            so  we need to find the next track height
3092                                                         */
3093                                                         if (j != height_list.begin()) {           
3094                                                                 j--;
3095                                                         }
3096                                                         if (x != original_pointer_order) { 
3097                                                                 /* we're not from the dragged track, so ignore hidden tracks. */              
3098                                                                 if ((*j) == 0) {
3099                                                                         temp_pointer_y_span++;
3100                                                                 }
3101                                                         }          
3102                                                         y_delta -= (*j);        
3103                                                         temp_pointer_y_span--;  
3104                                                 }
3105                                                 while (temp_pointer_y_span < 0) {                 
3106                                                         y_delta += (*j);
3107                                                         if (x != original_pointer_order) { 
3108                                                                 if ((*j) == 0) {
3109                                                                         temp_pointer_y_span--;
3110                                                                 }
3111                                                         }          
3112                     
3113                                                         if (j != height_list.end()) {                 
3114                                                                 j++;
3115                                                         }
3116                                                         temp_pointer_y_span++;
3117                                                 }
3118                                                 /* find out where we'll be when we move and set height accordingly */
3119                   
3120                                                 tvp2 = trackview_by_y_position (iy1 + y_delta);
3121                                                 temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3122                                                 rv->set_y_position_and_height (0, temp_rtv->height);
3123         
3124                                                 /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3125                                                      personally, i think this can confuse things, but never mind.
3126                                                 */
3127                                   
3128                                                 //const GdkColor& col (temp_rtv->view->get_region_color());
3129                                                 //rv->set_color (const_cast<GdkColor&>(col));
3130                                                 break;          
3131                                         }
3132                                         x++;
3133                                 }
3134                         }
3135           
3136                         /* prevent the regionview from being moved to before 
3137                            the zero position on the canvas.
3138                         */
3139                         /* clamp */
3140                 
3141                         if (x_delta < 0) {
3142                                 if (-x_delta > ix1) {
3143                                         x_delta = -ix1;
3144                                 }
3145                         } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
3146                                 x_delta = max_frames - rv->region()->last_frame();
3147                         }
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) {
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                                 boost::shared_ptr<Region> mr;
3408
3409                                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
3410                                         newregion = RegionFactory::create (ar);
3411                                 } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
3412                                         newregion = RegionFactory::create (mr);
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 (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3765                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3766         } else {
3767                 pending_position = 0;
3768         }
3769         
3770         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3771                 snap_to (pending_position);
3772         }
3773
3774         /* only alter selection if the current frame is 
3775            different from the last frame position (adjusted)
3776          */
3777         
3778         if (pending_position == drag_info.last_pointer_frame) return;
3779         
3780         switch (selection_op) {
3781         case CreateSelection:
3782                 
3783                 if (drag_info.first_move) {
3784                         snap_to (drag_info.grab_frame);
3785                 }
3786                 
3787                 if (pending_position < drag_info.grab_frame) {
3788                         start = pending_position;
3789                         end = drag_info.grab_frame;
3790                 } else {
3791                         end = pending_position;
3792                         start = drag_info.grab_frame;
3793                 }
3794                 
3795                 /* first drag: Either add to the selection
3796                    or create a new selection->
3797                 */
3798                 
3799                 if (drag_info.first_move) {
3800                         
3801                         begin_reversible_command (_("range selection"));
3802                         
3803                         if (drag_info.copy) {
3804                                 /* adding to the selection */
3805                                 clicked_selection = selection->add (start, end);
3806                                 drag_info.copy = false;
3807                         } else {
3808                                 /* new selection-> */
3809                                 clicked_selection = selection->set (clicked_axisview, start, end);
3810                         }
3811                 } 
3812                 break;
3813                 
3814         case SelectionStartTrim:
3815                 
3816                 if (drag_info.first_move) {
3817                         begin_reversible_command (_("trim selection start"));
3818                 }
3819                 
3820                 start = selection->time[clicked_selection].start;
3821                 end = selection->time[clicked_selection].end;
3822
3823                 if (pending_position > end) {
3824                         start = end;
3825                 } else {
3826                         start = pending_position;
3827                 }
3828                 break;
3829                 
3830         case SelectionEndTrim:
3831                 
3832                 if (drag_info.first_move) {
3833                         begin_reversible_command (_("trim selection end"));
3834                 }
3835                 
3836                 start = selection->time[clicked_selection].start;
3837                 end = selection->time[clicked_selection].end;
3838
3839                 if (pending_position < start) {
3840                         end = start;
3841                 } else {
3842                         end = pending_position;
3843                 }
3844                 
3845                 break;
3846                 
3847         case SelectionMove:
3848                 
3849                 if (drag_info.first_move) {
3850                         begin_reversible_command (_("move selection"));
3851                 }
3852                 
3853                 start = selection->time[clicked_selection].start;
3854                 end = selection->time[clicked_selection].end;
3855                 
3856                 length = end - start;
3857                 
3858                 start = pending_position;
3859                 snap_to (start);
3860                 
3861                 end = start + length;
3862                 
3863                 break;
3864         }
3865         
3866         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3867                 start_canvas_autoscroll (1);
3868         }
3869
3870         if (start != end) {
3871                 selection->replace (clicked_selection, start, end);
3872         }
3873
3874         drag_info.last_pointer_frame = pending_position;
3875         drag_info.first_move = false;
3876
3877         if (selection_op == SelectionMove) {
3878                 show_verbose_time_cursor(start, 10);    
3879         } else {
3880                 show_verbose_time_cursor(pending_position, 10); 
3881         }
3882 }
3883
3884 void
3885 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3886 {
3887         if (!drag_info.first_move) {
3888                 drag_selection (item, event);
3889                 /* XXX this is not object-oriented programming at all. ick */
3890                 if (selection->time.consolidate()) {
3891                         selection->TimeChanged ();
3892                 }
3893                 commit_reversible_command ();
3894         } else {
3895                 /* just a click, no pointer movement.*/
3896
3897                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3898
3899                         selection->clear_time();
3900
3901                 } 
3902         }
3903
3904         /* XXX what happens if its a music selection? */
3905         session->set_audio_range (selection->time);
3906         stop_canvas_autoscroll ();
3907 }
3908
3909 void
3910 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3911 {
3912         double speed = 1.0;
3913         TimeAxisView* tvp = clicked_axisview;
3914         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3915
3916         if (tv && tv->is_track()) {
3917                 speed = tv->get_diskstream()->speed();
3918         }
3919         
3920         nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3921         nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3922         nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3923
3924         //drag_info.item = clicked_regionview->get_name_highlight();
3925         drag_info.item = item;
3926         drag_info.motion_callback = &Editor::trim_motion_callback;
3927         drag_info.finished_callback = &Editor::trim_finished_callback;
3928
3929         start_grab (event, trimmer_cursor);
3930         
3931         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3932                 trim_op = ContentsTrim;
3933         } else {
3934                 /* These will get overridden for a point trim.*/
3935                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3936                         /* closer to start */
3937                         trim_op = StartTrim;
3938                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3939                         /* closer to end */
3940                         trim_op = EndTrim;
3941                 }
3942         }
3943
3944         switch (trim_op) {
3945         case StartTrim:
3946                 show_verbose_time_cursor(region_start, 10);     
3947                 break;
3948         case EndTrim:
3949                 show_verbose_time_cursor(region_end, 10);       
3950                 break;
3951         case ContentsTrim:
3952                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3953                 break;
3954         }
3955 }
3956
3957 void
3958 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3959 {
3960         RegionView* rv = clicked_regionview;
3961         nframes_t frame_delta = 0;
3962         bool left_direction;
3963         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3964
3965         /* snap modifier works differently here..
3966            its' current state has to be passed to the 
3967            various trim functions in order to work properly 
3968         */ 
3969
3970         double speed = 1.0;
3971         TimeAxisView* tvp = clicked_axisview;
3972         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3973         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3974
3975         if (tv && tv->is_track()) {
3976                 speed = tv->get_diskstream()->speed();
3977         }
3978         
3979         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
3980                 left_direction = true;
3981         } else {
3982                 left_direction = false;
3983         }
3984
3985         if (obey_snap) {
3986                 snap_to (drag_info.current_pointer_frame);
3987         }
3988
3989         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
3990                 return;
3991         }
3992
3993         if (drag_info.first_move) {
3994         
3995                 string trim_type;
3996
3997                 switch (trim_op) {
3998                 case StartTrim:
3999                         trim_type = "Region start trim";
4000                         break;
4001                 case EndTrim:
4002                         trim_type = "Region end trim";
4003                         break;
4004                 case ContentsTrim:
4005                         trim_type = "Region content trim";
4006                         break;
4007                 }
4008
4009                 begin_reversible_command (trim_type);
4010
4011                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4012                         (*i)->fake_set_opaque(false);
4013                         (*i)->region()->freeze ();
4014                 
4015                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4016                         if (arv)
4017                                 arv->temporarily_hide_envelope ();
4018
4019                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4020                         insert_result = motion_frozen_playlists.insert (pl);
4021                         if (insert_result.second) {
4022                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4023                         }
4024                 }
4025         }
4026
4027         if (left_direction) {
4028                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4029         } else {
4030                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4031         }
4032
4033         switch (trim_op) {              
4034         case StartTrim:
4035                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4036                         break;
4037                 } else {
4038                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4039                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4040                         }
4041                         break;
4042                 }
4043                 
4044         case EndTrim:
4045                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4046                         break;
4047                 } else {
4048                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4049                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4050                         }
4051                         break;
4052                 }
4053                 
4054         case ContentsTrim:
4055                 {
4056                         bool swap_direction = false;
4057
4058                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4059                                 swap_direction = true;
4060                         }
4061                         
4062                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4063                              i != selection->regions.by_layer().end(); ++i)
4064                         {
4065                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4066                         }
4067                 }
4068                 break;
4069         }
4070
4071         switch (trim_op) {
4072         case StartTrim:
4073                 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);     
4074                 break;
4075         case EndTrim:
4076                 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);   
4077                 break;
4078         case ContentsTrim:
4079                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4080                 break;
4081         }
4082
4083         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4084         drag_info.first_move = false;
4085 }
4086
4087 void
4088 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4089 {
4090         boost::shared_ptr<Region> region (rv.region());
4091
4092         if (region->locked()) {
4093                 return;
4094         }
4095
4096         nframes_t new_bound;
4097
4098         double speed = 1.0;
4099         TimeAxisView* tvp = clicked_axisview;
4100         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4101
4102         if (tv && tv->is_track()) {
4103                 speed = tv->get_diskstream()->speed();
4104         }
4105         
4106         if (left_direction) {
4107                 if (swap_direction) {
4108                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4109                 } else {
4110                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4111                 }
4112         } else {
4113                 if (swap_direction) {
4114                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4115                 } else {
4116                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4117                 }
4118         }
4119
4120         if (obey_snap) {
4121                 snap_to (new_bound);
4122         }
4123         region->trim_start ((nframes_t) (new_bound * speed), this);     
4124         rv.region_changed (StartChanged);
4125 }
4126
4127 void
4128 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4129 {
4130         boost::shared_ptr<Region> region (rv.region()); 
4131
4132         if (region->locked()) {
4133                 return;
4134         }
4135
4136         nframes_t new_bound;
4137
4138         double speed = 1.0;
4139         TimeAxisView* tvp = clicked_axisview;
4140         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4141
4142         if (tv && tv->is_track()) {
4143                 speed = tv->get_diskstream()->speed();
4144         }
4145         
4146         if (left_direction) {
4147                 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4148         } else {
4149                 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4150         }
4151
4152         if (obey_snap) {
4153                 snap_to (new_bound, (left_direction ? 0 : 1));  
4154         }
4155
4156         region->trim_front ((nframes_t) (new_bound * speed), this);
4157
4158         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4159 }
4160
4161 void
4162 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4163 {
4164         boost::shared_ptr<Region> region (rv.region());
4165
4166         if (region->locked()) {
4167                 return;
4168         }
4169
4170         nframes_t new_bound;
4171
4172         double speed = 1.0;
4173         TimeAxisView* tvp = clicked_axisview;
4174         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4175
4176         if (tv && tv->is_track()) {
4177                 speed = tv->get_diskstream()->speed();
4178         }
4179         
4180         if (left_direction) {
4181                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4182         } else {
4183                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4184         }
4185
4186         if (obey_snap) {
4187                 snap_to (new_bound);
4188         }
4189         region->trim_end ((nframes_t) (new_bound * speed), this);
4190         rv.region_changed (LengthChanged);
4191 }
4192         
4193 void
4194 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4195 {
4196         if (!drag_info.first_move) {
4197                 trim_motion_callback (item, event);
4198                 
4199                 if (!clicked_regionview->get_selected()) {
4200                         thaw_region_after_trim (*clicked_regionview);           
4201                 } else {
4202                         
4203                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4204                              i != selection->regions.by_layer().end(); ++i)
4205                         {
4206                                 thaw_region_after_trim (**i);
4207                                 (*i)->fake_set_opaque (true);
4208                         }
4209                 }
4210                 
4211                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4212                         //(*p)->thaw ();
4213                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4214                 }
4215                 
4216                 motion_frozen_playlists.clear ();
4217
4218                 commit_reversible_command();
4219         } else {
4220                 /* no mouse movement */
4221                 point_trim (event);
4222         }
4223 }
4224
4225 void
4226 Editor::point_trim (GdkEvent* event)
4227 {
4228         RegionView* rv = clicked_regionview;
4229         nframes_t new_bound = drag_info.current_pointer_frame;
4230
4231         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4232                 snap_to (new_bound);
4233         }
4234
4235         /* Choose action dependant on which button was pressed */
4236         switch (event->button.button) {
4237         case 1:
4238                 trim_op = StartTrim;
4239                 begin_reversible_command (_("Start point trim"));
4240
4241                 if (rv->get_selected()) {
4242
4243                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4244                              i != selection->regions.by_layer().end(); ++i)
4245                         {
4246                                 if (!(*i)->region()->locked()) {
4247                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4248                                         XMLNode &before = pl->get_state();
4249                                         (*i)->region()->trim_front (new_bound, this);   
4250                                         XMLNode &after = pl->get_state();
4251                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4252                                 }
4253                         }
4254
4255                 } else {
4256
4257                         if (!rv->region()->locked()) {
4258                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4259                                 XMLNode &before = pl->get_state();
4260                                 rv->region()->trim_front (new_bound, this);     
4261                                 XMLNode &after = pl->get_state();
4262                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4263                         }
4264                 }
4265
4266                 commit_reversible_command();
4267         
4268                 break;
4269         case 2:
4270                 trim_op = EndTrim;
4271                 begin_reversible_command (_("End point trim"));
4272
4273                 if (rv->get_selected()) {
4274                         
4275                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4276                         {
4277                                 if (!(*i)->region()->locked()) {
4278                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4279                                         XMLNode &before = pl->get_state();
4280                                         (*i)->region()->trim_end (new_bound, this);
4281                                         XMLNode &after = pl->get_state();
4282                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4283                                 }
4284                         }
4285
4286                 } else {
4287
4288                         if (!rv->region()->locked()) {
4289                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4290                                 XMLNode &before = pl->get_state();
4291                                 rv->region()->trim_end (new_bound, this);
4292                                 XMLNode &after = pl->get_state();
4293                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4294                         }
4295                 }
4296
4297                 commit_reversible_command();
4298         
4299                 break;
4300         default:
4301                 break;
4302         }
4303 }
4304
4305 void
4306 Editor::thaw_region_after_trim (RegionView& rv)
4307 {
4308         boost::shared_ptr<Region> region (rv.region());
4309
4310         if (region->locked()) {
4311                 return;
4312         }
4313
4314         region->thaw (_("trimmed region"));
4315         XMLNode &after = region->playlist()->get_state();
4316         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4317
4318         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4319         if (arv)
4320                 arv->unhide_envelope ();
4321 }
4322
4323 void
4324 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4325 {
4326         Marker* marker;
4327         bool is_start;
4328
4329         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4330                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4331                 /*NOTREACHED*/
4332         }
4333
4334         Location* location = find_location_from_marker (marker, is_start);      
4335         location->set_hidden (true, this);
4336 }
4337
4338
4339 void
4340 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4341 {
4342         if (session == 0) {
4343                 return;
4344         }
4345
4346         drag_info.item = item;
4347         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4348         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4349
4350         range_marker_op = op;
4351
4352         if (!temp_location) {
4353                 temp_location = new Location;
4354         }
4355         
4356         switch (op) {
4357         case CreateRangeMarker:
4358         case CreateTransportMarker:
4359                 
4360                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4361                         drag_info.copy = true;
4362                 } else {
4363                         drag_info.copy = false;
4364                 }
4365                 start_grab (event, selector_cursor);
4366                 break;
4367         }
4368
4369         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4370         
4371 }
4372
4373 void
4374 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4375 {
4376         nframes_t start = 0;
4377         nframes_t end = 0;
4378         ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4379         
4380         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4381                 snap_to (drag_info.current_pointer_frame);
4382         }
4383
4384         /* only alter selection if the current frame is 
4385            different from the last frame position.
4386          */
4387         
4388         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4389         
4390         switch (range_marker_op) {
4391         case CreateRangeMarker:
4392         case CreateTransportMarker:
4393                 if (drag_info.first_move) {
4394                         snap_to (drag_info.grab_frame);
4395                 }
4396                 
4397                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4398                         start = drag_info.current_pointer_frame;
4399                         end = drag_info.grab_frame;
4400                 } else {
4401                         end = drag_info.current_pointer_frame;
4402                         start = drag_info.grab_frame;
4403                 }
4404                 
4405                 /* first drag: Either add to the selection
4406                    or create a new selection.
4407                 */
4408                 
4409                 if (drag_info.first_move) {
4410                         
4411                         temp_location->set (start, end);
4412                         
4413                         crect->show ();
4414
4415                         update_marker_drag_item (temp_location);
4416                         range_marker_drag_rect->show();
4417                         range_marker_drag_rect->raise_to_top();
4418                         
4419                 } 
4420                 break;          
4421         }
4422         
4423         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4424                 start_canvas_autoscroll (1);
4425         }
4426         
4427         if (start != end) {
4428                 temp_location->set (start, end);
4429
4430                 double x1 = frame_to_pixel (start);
4431                 double x2 = frame_to_pixel (end);
4432                 crect->property_x1() = x1;
4433                 crect->property_x2() = x2;
4434
4435                 update_marker_drag_item (temp_location);
4436         }
4437
4438         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4439         drag_info.first_move = false;
4440
4441         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4442         
4443 }
4444
4445 void
4446 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4447 {
4448         Location * newloc = 0;
4449         string rangename;
4450         
4451         if (!drag_info.first_move) {
4452                 drag_range_markerbar_op (item, event);
4453
4454                 switch (range_marker_op) {
4455                 case CreateRangeMarker:
4456                     {
4457                         begin_reversible_command (_("new range marker"));
4458                         XMLNode &before = session->locations()->get_state();
4459                         session->locations()->next_available_name(rangename,"unnamed");
4460                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4461                         session->locations()->add (newloc, true);
4462                         XMLNode &after = session->locations()->get_state();
4463                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4464                         commit_reversible_command ();
4465                         
4466                         range_bar_drag_rect->hide();
4467                         range_marker_drag_rect->hide();
4468                         break;
4469                     }
4470
4471                 case CreateTransportMarker:
4472                         // popup menu to pick loop or punch
4473                         new_transport_marker_context_menu (&event->button, item);
4474                         
4475                         break;
4476                 }
4477         } else {
4478                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4479
4480                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4481
4482                         nframes_t start;
4483                         nframes_t end;
4484
4485                         start = session->locations()->first_mark_before (drag_info.grab_frame);
4486                         end = session->locations()->first_mark_after (drag_info.grab_frame);
4487                         
4488                         if (end == max_frames) {
4489                                 end = session->current_end_frame ();
4490                         }
4491
4492                         if (start == 0) {
4493                                 start = session->current_start_frame ();
4494                         }
4495
4496                         switch (mouse_mode) {
4497                         case MouseObject:
4498                                 /* find the two markers on either side and then make the selection from it */
4499                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
4500                                 break;
4501
4502                         case MouseRange:
4503                                 /* find the two markers on either side of the click and make the range out of it */
4504                                 selection->set (0, start, end);
4505                                 break;
4506
4507                         default:
4508                                 break;
4509                         }
4510                 } 
4511         }
4512
4513         stop_canvas_autoscroll ();
4514 }
4515
4516
4517
4518 void
4519 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4520 {
4521         drag_info.item = item;
4522         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4523         drag_info.finished_callback = &Editor::end_mouse_zoom;
4524
4525         start_grab (event, zoom_cursor);
4526
4527         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4528 }
4529
4530 void
4531 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4532 {
4533         nframes_t start;
4534         nframes_t end;
4535
4536         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4537                 snap_to (drag_info.current_pointer_frame);
4538                 
4539                 if (drag_info.first_move) {
4540                         snap_to (drag_info.grab_frame);
4541                 }
4542         }
4543                 
4544         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4545
4546         /* base start and end on initial click position */
4547         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4548                 start = drag_info.current_pointer_frame;
4549                 end = drag_info.grab_frame;
4550         } else {
4551                 end = drag_info.current_pointer_frame;
4552                 start = drag_info.grab_frame;
4553         }
4554         
4555         if (start != end) {
4556
4557                 if (drag_info.first_move) {
4558                         zoom_rect->show();
4559                         zoom_rect->raise_to_top();
4560                 }
4561
4562                 reposition_zoom_rect(start, end);
4563
4564                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4565                 drag_info.first_move = false;
4566
4567                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4568         }
4569 }
4570
4571 void
4572 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4573 {
4574         if (!drag_info.first_move) {
4575                 drag_mouse_zoom (item, event);
4576                 
4577                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4578                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4579                 } else {
4580                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4581                 }               
4582         } else {
4583                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4584                 /*
4585                 temporal_zoom_step (false);
4586                 center_screen (drag_info.grab_frame);
4587                 */
4588         }
4589
4590         zoom_rect->hide();
4591 }
4592
4593 void
4594 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4595 {
4596         double x1 = frame_to_pixel (start);
4597         double x2 = frame_to_pixel (end);
4598         double y2 = full_canvas_height - 1.0;
4599
4600         zoom_rect->property_x1() = x1;
4601         zoom_rect->property_y1() = 1.0;
4602         zoom_rect->property_x2() = x2;
4603         zoom_rect->property_y2() = y2;
4604 }
4605
4606 void
4607 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4608 {
4609         drag_info.item = item;
4610         drag_info.motion_callback = &Editor::drag_rubberband_select;
4611         drag_info.finished_callback = &Editor::end_rubberband_select;
4612
4613         start_grab (event, cross_hair_cursor);
4614
4615         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4616 }
4617
4618 void
4619 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4620 {
4621         nframes_t start;
4622         nframes_t end;
4623         double y1;
4624         double y2;
4625
4626         /* use a bigger drag threshold than the default */
4627
4628         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4629                 return;
4630         }
4631
4632         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4633                 if (drag_info.first_move) {
4634                         snap_to (drag_info.grab_frame);
4635                 } 
4636                 snap_to (drag_info.current_pointer_frame);
4637         }
4638
4639         /* base start and end on initial click position */
4640
4641         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4642                 start = drag_info.current_pointer_frame;
4643                 end = drag_info.grab_frame;
4644         } else {
4645                 end = drag_info.current_pointer_frame;
4646                 start = drag_info.grab_frame;
4647         }
4648
4649         if (drag_info.current_pointer_y < drag_info.grab_y) {
4650                 y1 = drag_info.current_pointer_y;
4651                 y2 = drag_info.grab_y;
4652         } else {
4653                 y2 = drag_info.current_pointer_y;
4654                 y1 = drag_info.grab_y;
4655         }
4656
4657         
4658         if (start != end || y1 != y2) {
4659
4660                 double x1 = frame_to_pixel (start);
4661                 double x2 = frame_to_pixel (end);
4662                 
4663                 rubberband_rect->property_x1() = x1;
4664                 rubberband_rect->property_y1() = y1;
4665                 rubberband_rect->property_x2() = x2;
4666                 rubberband_rect->property_y2() = y2;
4667
4668                 rubberband_rect->show();
4669                 rubberband_rect->raise_to_top();
4670                 
4671                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4672                 drag_info.first_move = false;
4673
4674                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4675         }
4676 }
4677
4678 void
4679 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4680 {
4681         if (!drag_info.first_move) {
4682
4683                 drag_rubberband_select (item, event);
4684
4685                 double y1,y2;
4686                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4687                         y1 = drag_info.current_pointer_y;
4688                         y2 = drag_info.grab_y;
4689                 }
4690                 else {
4691                         y2 = drag_info.current_pointer_y;
4692                         y1 = drag_info.grab_y;
4693                 }
4694
4695
4696                 Selection::Operation op = Keyboard::selection_type (event->button.state);
4697                 bool commit;
4698
4699                 begin_reversible_command (_("rubberband selection"));
4700
4701                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4702                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
4703                 } else {
4704                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
4705                 }               
4706
4707                 if (commit) {
4708                         commit_reversible_command ();
4709                 }
4710                 
4711         } else {
4712                 selection->clear_tracks();
4713                 selection->clear_regions();
4714                 selection->clear_points ();
4715                 selection->clear_lines ();
4716         }
4717
4718         rubberband_rect->hide();
4719 }
4720
4721
4722 gint
4723 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4724 {
4725         using namespace Gtkmm2ext;
4726
4727         ArdourPrompter prompter (false);
4728
4729         prompter.set_prompt (_("Name for region:"));
4730         prompter.set_initial_text (clicked_regionview->region()->name());
4731         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4732         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4733         prompter.show_all ();
4734         switch (prompter.run ()) {
4735         case Gtk::RESPONSE_ACCEPT:
4736         string str;
4737                 prompter.get_result(str);
4738                 if (str.length()) {
4739                         clicked_regionview->region()->set_name (str);
4740                 }
4741                 break;
4742         }
4743         return true;
4744 }
4745
4746 void
4747 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4748 {
4749         drag_info.item = item;
4750         drag_info.motion_callback = &Editor::time_fx_motion;
4751         drag_info.finished_callback = &Editor::end_time_fx;
4752
4753         start_grab (event);
4754
4755         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4756 }
4757
4758 void
4759 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4760 {
4761         RegionView* rv = clicked_regionview;
4762
4763         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4764                 snap_to (drag_info.current_pointer_frame);
4765         }
4766
4767         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4768                 return;
4769         }
4770
4771         if (drag_info.current_pointer_frame > rv->region()->position()) {
4772                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4773         }
4774
4775         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4776         drag_info.first_move = false;
4777
4778         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4779 }
4780
4781 void
4782 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4783 {
4784         clicked_regionview->get_time_axis_view().hide_timestretch ();
4785
4786         if (drag_info.first_move) {
4787                 return;
4788         }
4789         
4790         nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4791         float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4792         
4793         begin_reversible_command (_("timestretch"));
4794
4795         if (run_timestretch (selection->regions, percentage) == 0) {
4796                 session->commit_reversible_command ();
4797         }
4798 }
4799
4800 void
4801 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4802 {
4803         /* no brushing without a useful snap setting */
4804
4805         // FIXME
4806         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4807         assert(arv);
4808
4809         switch (snap_mode) {
4810         case SnapMagnetic:
4811                 return; /* can't work because it allows region to be placed anywhere */
4812         default:
4813                 break; /* OK */
4814         }
4815
4816         switch (snap_type) {
4817         case SnapToFrame:
4818         case SnapToMark:
4819         case SnapToEditCursor:
4820                 return;
4821
4822         default:
4823                 break;
4824         }
4825
4826         /* don't brush a copy over the original */
4827         
4828         if (pos == rv->region()->position()) {
4829                 return;
4830         }
4831
4832         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4833
4834         if (rtv == 0 || !rtv->is_track()) {
4835                 return;
4836         }
4837
4838         boost::shared_ptr<Playlist> playlist = rtv->playlist();
4839         double speed = rtv->get_diskstream()->speed();
4840         
4841         XMLNode &before = playlist->get_state();
4842         playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4843         XMLNode &after = playlist->get_state();
4844         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4845         
4846         // playlist is frozen, so we have to update manually
4847         
4848         playlist->Modified(); /* EMIT SIGNAL */
4849 }
4850
4851 gint
4852 Editor::track_height_step_timeout ()
4853 {
4854         struct timeval now;
4855         struct timeval delta;
4856         
4857         gettimeofday (&now, 0);
4858         timersub (&now, &last_track_height_step_timestamp, &delta);
4859         
4860         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4861                 current_stepping_trackview = 0;
4862                 return false;
4863         }
4864         return true;
4865 }