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