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