Merged with trunk R1327.
[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                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2804                         RegionView* rv;
2805                         
2806                         rv = (*i);
2807                         
2808                         boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
2809                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2810
2811                         insert_result = affected_playlists.insert (to_playlist);
2812                         if (insert_result.second) {
2813                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
2814                         }
2815                         
2816                         latest_regionview = 0;
2817                         
2818                         sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2819                         
2820                         /* create a new region with the same name. */
2821                         
2822                         boost::shared_ptr<Region> newregion;
2823
2824                         newregion = RegionFactory::create (rv->region());
2825                         assert(newregion != 0);
2826
2827                         /* if the original region was locked, we don't care */
2828                         
2829                         newregion->set_locked (false);
2830                         
2831                         to_playlist->add_region (newregion, (nframes_t) (rv->region()->position() * rtv->get_diskstream()->speed()));
2832                         
2833                         c.disconnect ();
2834                         
2835                         if (latest_regionview) {
2836                                 new_regionviews.push_back (latest_regionview);
2837                         }
2838                 }
2839
2840                 
2841                 
2842                 if (new_regionviews.empty()) {
2843                         return;
2844                 }
2845                 
2846                 /* reset selection to new regionviews */
2847                 
2848                 selection->set (new_regionviews);
2849                 
2850                 /* reset drag_info data to reflect the fact that we are dragging the copies */
2851                 
2852                 drag_info.data = new_regionviews.front();
2853                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
2854         }
2855
2856         /* Which trackview is this ? */
2857
2858         TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
2859         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2860
2861         /* The region motion is only processed if the pointer is over
2862            an audio track.
2863         */
2864         
2865         if (!tv || !tv->is_track()) {
2866                 /* To make sure we hide the verbose canvas cursor when the mouse is 
2867                    not held over a track. 
2868                 */
2869                 hide_verbose_canvas_cursor ();
2870                 return;
2871         }
2872         
2873         original_pointer_order = drag_info.last_trackview->order;
2874                 
2875         /************************************************************
2876                  Y-Delta Computation
2877         ************************************************************/   
2878
2879         if (drag_info.brushing) {
2880                 clamp_y_axis = true;
2881                 pointer_y_span = 0;
2882                 goto y_axis_done;
2883         }
2884         
2885         if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
2886
2887                 int32_t children = 0, numtracks = 0;
2888                 // XXX hard coding track limit, oh my, so very very bad
2889                 bitset <1024> tracks (0x00);
2890                 /* get a bitmask representing the visible tracks */
2891
2892                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2893                         TimeAxisView *tracklist_timeview;
2894                         tracklist_timeview = (*i);
2895                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
2896                         list<TimeAxisView*> children_list;
2897               
2898                         /* zeroes are audio tracks. ones are other types. */
2899               
2900                         if (!rtv2->hidden()) {
2901                                 
2902                                 if (visible_y_high < rtv2->order) {
2903                                         visible_y_high = rtv2->order;
2904                                 }
2905                                 if (visible_y_low > rtv2->order) {
2906                                         visible_y_low = rtv2->order;
2907                                 }
2908                 
2909                                 if (!rtv2->is_track()) {                                  
2910                                         tracks = tracks |= (0x01 << rtv2->order);
2911                                 }
2912         
2913                                 height_list[rtv2->order] = (*i)->height;
2914                                 children = 1;
2915                                 if ((children_list = rtv2->get_child_list()).size() > 0) {
2916                                         for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
2917                                                 tracks = tracks |= (0x01 << (rtv2->order + children));
2918                                                 height_list[rtv2->order + children] =  (*j)->height;                
2919                                                 numtracks++;
2920                                                 children++;     
2921                                         }
2922                                 }
2923                                 numtracks++;        
2924                         }
2925                 }
2926                 /* find the actual span according to the canvas */
2927
2928                 canvas_pointer_y_span = pointer_y_span;
2929                 if (drag_info.last_trackview->order >= tv->order) {
2930                         int32_t y;
2931                         for (y = tv->order; y < drag_info.last_trackview->order; y++) {
2932                                 if (height_list[y] == 0 ) {
2933                                         canvas_pointer_y_span--;
2934                                 }
2935                         }
2936                 } else {
2937                         int32_t y;
2938                         for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
2939                                 if (    height_list[y] == 0 ) {
2940                                         canvas_pointer_y_span++;
2941                                 }
2942                         }
2943                 }
2944
2945                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
2946                         RegionView* rv2 = (*i);
2947                         double ix1, ix2, iy1, iy2;
2948                         int32_t n = 0;
2949
2950                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
2951                         rv2->get_canvas_group()->i2w (ix1, iy1);
2952                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
2953                         RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
2954
2955                         if (rtv2->order != original_pointer_order) {    
2956                                 /* this isn't the pointer track */      
2957
2958                                 if (canvas_pointer_y_span > 0) {
2959
2960                                         /* moving up the canvas */
2961                                         if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
2962         
2963                                                 int32_t visible_tracks = 0;
2964                                                 while (visible_tracks < canvas_pointer_y_span ) {
2965                                                         visible_tracks++;
2966                   
2967                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
2968                                                                 /* we're passing through a hidden track */
2969                                                                 n--;
2970                                                         }                 
2971                                                 }
2972                  
2973                                                 if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
2974                                                         clamp_y_axis = true;
2975                                                 }
2976                     
2977                                         } else {
2978                                                 clamp_y_axis = true;
2979                                         }                 
2980                   
2981                                 } else if (canvas_pointer_y_span < 0) {
2982
2983                                         /*moving down the canvas*/
2984
2985                                         if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
2986                     
2987                     
2988                                                 int32_t visible_tracks = 0;
2989                     
2990                                                 while (visible_tracks > canvas_pointer_y_span ) {
2991                                                         visible_tracks--;
2992                       
2993                                                         while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
2994                                                                 n++;
2995                                                         }                
2996                                                 }
2997                                                 if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
2998                                                         clamp_y_axis = true;
2999                             
3000                                                 }
3001                                         } else {
3002                           
3003                                                 clamp_y_axis = true;
3004                                         }
3005                                 }               
3006                   
3007                         } else {
3008                       
3009                                 /* this is the pointer's track */
3010                                 if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
3011                                         clamp_y_axis = true;
3012                                 } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
3013                                         clamp_y_axis = true;
3014                                 }
3015                         }             
3016                         if (clamp_y_axis) {
3017                                 break;
3018                         }
3019                 }
3020
3021         } else  if (drag_info.last_trackview == tv) {
3022                 clamp_y_axis = true;
3023         }         
3024
3025   y_axis_done:
3026         if (!clamp_y_axis) {
3027                 drag_info.last_trackview = tv;        
3028         }
3029           
3030         /************************************************************
3031                         X DELTA COMPUTATION
3032         ************************************************************/
3033
3034         /* compute the amount of pointer motion in frames, and where
3035            the region would be if we moved it by that much.
3036         */
3037
3038         if (drag_info.move_threshold_passed) {
3039
3040                 if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3041
3042                         nframes_t sync_frame;
3043                         nframes_t sync_offset;
3044                         int32_t sync_dir;
3045             
3046                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3047             
3048                         sync_offset = rv->region()->sync_offset (sync_dir);
3049                         sync_frame = rv->region()->adjust_to_sync (pending_region_position);
3050
3051                         /* we snap if the snap modifier is not enabled.
3052                          */
3053             
3054                         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3055                                 snap_to (sync_frame);   
3056                         }
3057             
3058                         if (sync_frame - sync_offset <= sync_frame) {
3059                                 pending_region_position = sync_frame - (sync_dir*sync_offset);
3060                         } else {
3061                                 pending_region_position = 0;
3062                         }
3063             
3064                 } else {
3065                         pending_region_position = 0;
3066                 }
3067           
3068                 if (pending_region_position > max_frames - rv->region()->length()) {
3069                         pending_region_position = drag_info.last_frame_position;
3070                 }
3071           
3072                 // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
3073           
3074                 if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
3075
3076                         /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
3077                            to make it appear at the new location.
3078                         */
3079             
3080                         if (pending_region_position > drag_info.last_frame_position) {
3081                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3082                         } else {
3083                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3084                         }
3085             
3086                         drag_info.last_frame_position = pending_region_position;
3087             
3088                 } else {
3089                         x_delta = 0;
3090                 }
3091
3092         } else {
3093                 /* threshold not passed */
3094
3095                 x_delta = 0;
3096         }
3097
3098         /*************************************************************
3099                         PREPARE TO MOVE
3100         ************************************************************/
3101
3102         if (x_delta == 0 && (pointer_y_span == 0)) {
3103                 /* haven't reached next snap point, and we're not switching
3104                    trackviews. nothing to do.
3105                 */
3106                 return;
3107         } 
3108
3109         if (x_delta < 0) {
3110                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3111
3112                         RegionView* rv2 = (*i);
3113
3114                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3115                         
3116                         double ix1, ix2, iy1, iy2;
3117                         rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3118                         rv2->get_canvas_group()->i2w (ix1, iy1);
3119
3120                         if (ix1 <= 1) {
3121                                 x_delta = 0;
3122                                 break;
3123                         }
3124                 }
3125         }
3126
3127         /*************************************************************
3128                         MOTION                                                                
3129         ************************************************************/
3130
3131         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3132         const list<RegionView*>& layered_regions = selection->regions.by_layer();
3133
3134         for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
3135             
3136                 RegionView* rv = (*i);
3137                 double ix1, ix2, iy1, iy2;
3138                 int32_t temp_pointer_y_span = pointer_y_span;
3139
3140                 /* get item BBox, which will be relative to parent. so we have
3141                    to query on a child, then convert to world coordinates using
3142                    the parent.
3143                 */
3144
3145                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3146                 rv->get_canvas_group()->i2w (ix1, iy1);
3147                 TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3148                 RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3149                 RouteTimeAxisView* temp_rtv;
3150
3151                 if ((pointer_y_span != 0) && !clamp_y_axis) {
3152                         y_delta = 0;
3153                         int32_t x = 0;
3154                         for (j = height_list.begin(); j!= height_list.end(); j++) {     
3155                                 if (x == canvas_rtv->order) {
3156                                         /* we found the track the region is on */
3157                                         if (x != original_pointer_order) {
3158                                                 /*this isn't from the same track we're dragging from */
3159                                                 temp_pointer_y_span = canvas_pointer_y_span;
3160                                         }                 
3161                                         while (temp_pointer_y_span > 0) {
3162                                                 /* we're moving up canvas-wise,
3163                                                    so  we need to find the next track height
3164                                                 */
3165                                                 if (j != height_list.begin()) {           
3166                                                         j--;
3167                                                 }
3168                                                 if (x != original_pointer_order) { 
3169                                                         /* we're not from the dragged track, so ignore hidden tracks. */              
3170                                                         if ((*j) == 0) {
3171                                                                 temp_pointer_y_span++;
3172                                                         }
3173                                                 }          
3174                                                 y_delta -= (*j);        
3175                                                 temp_pointer_y_span--;  
3176                                         }
3177                                         while (temp_pointer_y_span < 0) {                 
3178                                                 y_delta += (*j);
3179                                                 if (x != original_pointer_order) { 
3180                                                         if ((*j) == 0) {
3181                                                                 temp_pointer_y_span--;
3182                                                         }
3183                                                 }          
3184                     
3185                                                 if (j != height_list.end()) {                 
3186                                                         j++;
3187                                                 }
3188                                                 temp_pointer_y_span++;
3189                                         }
3190                                         /* find out where we'll be when we move and set height accordingly */
3191                   
3192                                         tvp2 = trackview_by_y_position (iy1 + y_delta);
3193                                         temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
3194                                         rv->set_height (temp_rtv->height);
3195         
3196                                         /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
3197                                              personally, i think this can confuse things, but never mind.
3198                                         */
3199                                   
3200                                         //const GdkColor& col (temp_rtv->view->get_region_color());
3201                                         //rv->set_color (const_cast<GdkColor&>(col));
3202                                         break;          
3203                                 }
3204                                 x++;
3205                         }
3206                 }
3207           
3208                 /* prevent the regionview from being moved to before 
3209                    the zero position on the canvas.
3210                 */
3211                 /* clamp */
3212                 
3213                 if (x_delta < 0) {
3214                         if (-x_delta > ix1) {
3215                                 x_delta = -ix1;
3216                         }
3217                 } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
3218                         x_delta = max_frames - rv->region()->last_frame();
3219                 }
3220                         
3221                 if (drag_info.first_move) {
3222
3223                         /* hide any dependent views */
3224
3225                         rv->get_time_axis_view().hide_dependent_views (*rv);
3226                                 
3227                         /* this is subtle. raising the regionview itself won't help,
3228                            because raise_to_top() just puts the item on the top of
3229                            its parent's stack. so, we need to put the trackview canvas_display group
3230                            on the top, since its parent is the whole canvas.
3231                         */
3232
3233                         rv->get_canvas_group()->raise_to_top();
3234                         rv->get_time_axis_view().canvas_display->raise_to_top();
3235                         cursor_group->raise_to_top();
3236
3237                         /* freeze the playlists from notifying till
3238                            the motion is done.
3239                         */
3240
3241                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3242                         if (rtv && rtv->is_audio_track()) {
3243                                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>(rtv->get_diskstream()->playlist());
3244                                 if (pl) {
3245                                         /* only freeze and capture state once */
3246
3247                                         insert_result = motion_frozen_playlists.insert (pl);
3248                                         if (insert_result.second) {
3249                                                 pl->freeze();
3250                                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
3251                                         }
3252                                 }
3253                         }
3254                         rv->region()->set_opaque(false);
3255                 }
3256
3257                 if (drag_info.brushing) {
3258                         mouse_brush_insert_region (rv, pending_region_position);
3259                 } else {
3260                         rv->move (x_delta, y_delta);                    
3261                 }
3262         }
3263                 
3264         if (drag_info.first_move) {
3265                 cursor_group->raise_to_top();
3266         }
3267                 
3268         drag_info.first_move = false;
3269                 
3270         if (x_delta != 0 && !drag_info.brushing) {
3271                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
3272         }
3273                 
3274
3275
3276 void
3277 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3278 {
3279         nframes_t where;
3280         RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
3281         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3282         bool nocommit = true;
3283         double speed;
3284         RouteTimeAxisView* atv;
3285         bool regionview_y_movement;
3286         bool regionview_x_movement;
3287
3288         /* first_move is set to false if the regionview has been moved in the 
3289            motion handler. 
3290         */
3291
3292         if (drag_info.first_move) {
3293                 /* just a click */
3294                 goto out;
3295         }
3296
3297         nocommit = false;
3298
3299         /* The regionview has been moved at some stage during the grab so we need
3300            to account for any mouse movement between this event and the last one. 
3301         */      
3302
3303         region_drag_motion_callback (item, event);
3304
3305         if (drag_info.brushing) {
3306                 /* all changes were made during motion event handlers */
3307                 goto out;
3308         }
3309
3310         /* adjust for track speed */
3311         speed = 1.0;
3312
3313         atv = dynamic_cast<RouteTimeAxisView*> (drag_info.last_trackview);
3314         if (atv && atv->get_diskstream()) {
3315                 speed = atv->get_diskstream()->speed();
3316         }
3317         
3318         regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
3319         regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
3320
3321         //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
3322         //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
3323         
3324         if (regionview_y_movement) {
3325
3326                 /* motion between tracks */
3327
3328                 list<RegionView*> new_selection;
3329         
3330                 /* moved to a different audio track. */
3331
3332                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
3333             
3334                         RegionView* rv2 = (*i);             
3335             
3336                         /* the region that used to be in the old playlist is not
3337                            moved to the new one - we make a copy of it. as a result,
3338                            any existing editor for the region should no longer be
3339                            visible.
3340                         */ 
3341             
3342                         if (!drag_info.copy) {
3343                                 rv2->hide_region_editor();
3344                         }           
3345                         new_selection.push_back (rv2);      
3346                         i++;
3347                 }
3348
3349                 /* first, freeze the target tracks */
3350
3351                 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3352
3353                         boost::shared_ptr<Playlist> from_playlist;
3354                         boost::shared_ptr<Playlist> to_playlist;
3355                                 
3356                         double ix1, ix2, iy1, iy2;
3357             
3358                         (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3359                         (*i)->get_canvas_group()->i2w (ix1, iy1);
3360                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3361                         RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3362
3363                         (*i)->region()->set_opaque (true);
3364             
3365                         from_playlist = (*i)->region()->playlist();
3366                         to_playlist = atv2->playlist();
3367
3368                         /* the from_playlist was frozen in the "first_move" case 
3369                            of the motion handler. the insert can fail, 
3370                            but that doesn't matter. it just means
3371                            we already have the playlist in the list.
3372                         */
3373                         
3374                         motion_frozen_playlists.insert (from_playlist);
3375
3376                         /* only freeze the to_playlist once */
3377
3378                         insert_result = motion_frozen_playlists.insert(to_playlist);
3379                         if (insert_result.second) {
3380                                 to_playlist->freeze();
3381                                 session->add_command(new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
3382                         }
3383
3384                 }
3385
3386                 /* now do it again with the actual operations */
3387
3388                 for (list<RegionView*>::const_iterator i = new_selection.begin(); i != new_selection.end();i++ ) {
3389
3390                         boost::shared_ptr<Playlist> from_playlist;
3391                         boost::shared_ptr<Playlist> to_playlist;
3392                                 
3393                         double ix1, ix2, iy1, iy2;
3394             
3395                         (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3396                         (*i)->get_canvas_group()->i2w (ix1, iy1);
3397                         TimeAxisView* tvp2 = trackview_by_y_position (iy1);
3398                         RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
3399             
3400                         from_playlist = (*i)->region()->playlist();
3401                         to_playlist = atv2->playlist();
3402
3403                         latest_regionview = 0;
3404             
3405                         where = (nframes_t) (unit_to_frame (ix1) * speed);
3406                         boost::shared_ptr<Region> new_region (RegionFactory::create ((*i)->region()));
3407
3408                         from_playlist->remove_region (((*i)->region()));
3409                         
3410                         sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3411                         to_playlist->add_region (new_region, where);
3412                         c.disconnect ();
3413                                                               
3414                         if (latest_regionview) {
3415                                 selection->add (latest_regionview);
3416                         }
3417                 } 
3418
3419         } else {
3420
3421                 /* motion within a single track */
3422                 
3423                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3424
3425                         rv = (*i);
3426
3427                         if (rv->region()->locked()) {
3428                                 continue;
3429                         }
3430                         
3431                         if (regionview_x_movement) {
3432                                 double ownspeed = 1.0;
3433                                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(rv->get_time_axis_view()));
3434
3435                                 if (atv && atv->get_diskstream()) {
3436                                         ownspeed = atv->get_diskstream()->speed();
3437                                 }
3438                                 
3439                                 /* base the new region position on the current position of the regionview.*/
3440                                 
3441                                 double ix1, ix2, iy1, iy2;
3442                                 
3443                                 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3444                                 rv->get_canvas_group()->i2w (ix1, iy1);
3445                                 where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
3446                                 
3447                         } else {
3448                                 
3449                                 where = rv->region()->position();
3450                         }
3451
3452                         rv->get_time_axis_view().reveal_dependent_views (*rv);
3453
3454                         /* no need to add an undo here, we did that when we added this playlist to motion_frozen playlists */
3455                         
3456                         rv->region()->set_position (where, (void *) this);
3457                         rv->region()->set_opaque (true);
3458                 }
3459         }
3460
3461   out:
3462         for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
3463                 (*p)->thaw ();
3464                 session->add_command (new MementoCommand<Playlist>(*((*p).get()), 0, & (*p)->get_state()));
3465         }
3466
3467         motion_frozen_playlists.clear ();
3468
3469         if (!nocommit) {
3470                 commit_reversible_command ();
3471         }
3472 }
3473
3474 void
3475 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
3476 {
3477         /* Either add to or set the set the region selection, unless
3478            this is an alignment click (control used)
3479         */
3480         
3481         if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
3482                 TimeAxisView* tv = &rv.get_time_axis_view();
3483                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
3484                 double speed = 1.0;
3485                 if (atv && atv->is_track()) {
3486                         speed = atv->get_diskstream()->speed();
3487                 }
3488
3489                 if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
3490
3491                         align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
3492
3493                 } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
3494
3495                         align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
3496
3497                 } else {
3498
3499                         align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
3500                 }
3501         }
3502 }
3503
3504 void
3505 Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos) 
3506 {
3507         char buf[128];
3508         SMPTE::Time smpte;
3509         BBT_Time bbt;
3510         int hours, mins;
3511         nframes_t frame_rate;
3512         float secs;
3513
3514         if (session == 0) {
3515                 return;
3516         }
3517
3518         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3519         case AudioClock::BBT:
3520                 session->bbt_time (frame, bbt);
3521                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
3522                 break;
3523                 
3524         case AudioClock::SMPTE:
3525                 session->smpte_time (frame, smpte);
3526                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3527                 break;
3528
3529         case AudioClock::MinSec:
3530                 /* XXX this is copied from show_verbose_duration_cursor() */
3531                 frame_rate = session->frame_rate();
3532                 hours = frame / (frame_rate * 3600);
3533                 frame = frame % (frame_rate * 3600);
3534                 mins = frame / (frame_rate * 60);
3535                 frame = frame % (frame_rate * 60);
3536                 secs = (float) frame / (float) frame_rate;
3537                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3538                 break;
3539
3540         default:
3541                 snprintf (buf, sizeof(buf), "%u", frame);
3542                 break;
3543         }
3544
3545         if (xpos >= 0 && ypos >=0) {
3546                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3547         }
3548         else {
3549                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3550         }
3551         show_verbose_canvas_cursor ();
3552 }
3553
3554 void
3555 Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos) 
3556 {
3557         char buf[128];
3558         SMPTE::Time smpte;
3559         BBT_Time sbbt;
3560         BBT_Time ebbt;
3561         int hours, mins;
3562         nframes_t distance, frame_rate;
3563         float secs;
3564         Meter meter_at_start(session->tempo_map().meter_at(start));
3565
3566         if (session == 0) {
3567                 return;
3568         }
3569
3570         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
3571         case AudioClock::BBT:
3572                 session->bbt_time (start, sbbt);
3573                 session->bbt_time (end, ebbt);
3574
3575                 /* subtract */
3576                 /* XXX this computation won't work well if the
3577                 user makes a selection that spans any meter changes.
3578                 */
3579
3580                 ebbt.bars -= sbbt.bars;
3581                 if (ebbt.beats >= sbbt.beats) {
3582                         ebbt.beats -= sbbt.beats;
3583                 } else {
3584                         ebbt.bars--;
3585                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
3586                 }
3587                 if (ebbt.ticks >= sbbt.ticks) {
3588                         ebbt.ticks -= sbbt.ticks;
3589                 } else {
3590                         ebbt.beats--;
3591                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
3592                 }
3593                 
3594                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
3595                 break;
3596                 
3597         case AudioClock::SMPTE:
3598                 session->smpte_duration (end - start, smpte);
3599                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
3600                 break;
3601
3602         case AudioClock::MinSec:
3603                 /* XXX this stuff should be elsewhere.. */
3604                 distance = end - start;
3605                 frame_rate = session->frame_rate();
3606                 hours = distance / (frame_rate * 3600);
3607                 distance = distance % (frame_rate * 3600);
3608                 mins = distance / (frame_rate * 60);
3609                 distance = distance % (frame_rate * 60);
3610                 secs = (float) distance / (float) frame_rate;
3611                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
3612                 break;
3613
3614         default:
3615                 snprintf (buf, sizeof(buf), "%u", end - start);
3616                 break;
3617         }
3618
3619         if (xpos >= 0 && ypos >=0) {
3620                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
3621         }
3622         else {
3623                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
3624         }
3625         show_verbose_canvas_cursor ();
3626 }
3627
3628 void
3629 Editor::collect_new_region_view (RegionView* rv)
3630 {
3631         latest_regionview = rv;
3632 }
3633
3634 void
3635 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
3636 {
3637         if (clicked_regionview == 0) {
3638                 return;
3639         }
3640
3641         /* lets try to create new Region for the selection */
3642
3643         vector<boost::shared_ptr<AudioRegion> > new_regions;
3644         create_region_from_selection (new_regions);
3645
3646         if (new_regions.empty()) {
3647                 return;
3648         }
3649
3650         /* XXX fix me one day to use all new regions */
3651         
3652         boost::shared_ptr<Region> region (new_regions.front());
3653
3654         /* add it to the current stream/playlist.
3655
3656            tricky: the streamview for the track will add a new regionview. we will
3657            catch the signal it sends when it creates the regionview to
3658            set the regionview we want to then drag.
3659         */
3660         
3661         latest_regionview = 0;
3662         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3663         
3664         /* A selection grab currently creates two undo/redo operations, one for 
3665            creating the new region and another for moving it.
3666         */
3667
3668         begin_reversible_command (_("selection grab"));
3669
3670         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
3671
3672         XMLNode *before = &(playlist->get_state());
3673         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
3674         XMLNode *after = &(playlist->get_state());
3675         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3676
3677         commit_reversible_command ();
3678         
3679         c.disconnect ();
3680         
3681         if (latest_regionview == 0) {
3682                 /* something went wrong */
3683                 return;
3684         }
3685
3686         /* we need to deselect all other regionviews, and select this one
3687            i'm ignoring undo stuff, because the region creation will take care of it */
3688         selection->set (latest_regionview);
3689         
3690         drag_info.item = latest_regionview->get_canvas_group();
3691         drag_info.data = latest_regionview;
3692         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3693         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3694
3695         start_grab (event);
3696         
3697         drag_info.last_trackview = clicked_axisview;
3698         drag_info.last_frame_position = latest_regionview->region()->position();
3699         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3700         
3701         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3702 }
3703
3704 void
3705 Editor::cancel_selection ()
3706 {
3707         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3708                 (*i)->hide_selection ();
3709         }
3710         begin_reversible_command (_("cancel selection"));
3711         selection->clear ();
3712         clicked_selection = 0;
3713         commit_reversible_command ();
3714 }       
3715
3716 void
3717 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
3718 {
3719         nframes_t start = 0;
3720         nframes_t end = 0;
3721
3722         if (session == 0) {
3723                 return;
3724         }
3725
3726         drag_info.item = item;
3727         drag_info.motion_callback = &Editor::drag_selection;
3728         drag_info.finished_callback = &Editor::end_selection_op;
3729
3730         selection_op = op;
3731
3732         switch (op) {
3733         case CreateSelection:
3734                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
3735                         drag_info.copy = true;
3736                 } else {
3737                         drag_info.copy = false;
3738                 }
3739                 start_grab (event, selector_cursor);
3740                 break;
3741
3742         case SelectionStartTrim:
3743                 if (clicked_axisview) {
3744                         clicked_axisview->order_selection_trims (item, true);
3745                 } 
3746                 start_grab (event, trimmer_cursor);
3747                 start = selection->time[clicked_selection].start;
3748                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3749                 break;
3750                 
3751         case SelectionEndTrim:
3752                 if (clicked_axisview) {
3753                         clicked_axisview->order_selection_trims (item, false);
3754                 }
3755                 start_grab (event, trimmer_cursor);
3756                 end = selection->time[clicked_selection].end;
3757                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
3758                 break;
3759
3760         case SelectionMove:
3761                 start = selection->time[clicked_selection].start;
3762                 start_grab (event);
3763                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
3764                 break;
3765         }
3766
3767         if (selection_op == SelectionMove) {
3768                 show_verbose_time_cursor(start, 10);    
3769         } else {
3770                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3771         }
3772 }
3773
3774 void
3775 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
3776 {
3777         nframes_t start = 0;
3778         nframes_t end = 0;
3779         nframes_t length;
3780         nframes_t pending_position;
3781
3782         if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3783                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3784         }
3785         else {
3786                 pending_position = 0;
3787         }
3788         
3789         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3790                 snap_to (pending_position);
3791         }
3792
3793         /* only alter selection if the current frame is 
3794            different from the last frame position (adjusted)
3795          */
3796         
3797         if (pending_position == drag_info.last_pointer_frame) return;
3798         
3799         switch (selection_op) {
3800         case CreateSelection:
3801                 
3802                 if (drag_info.first_move) {
3803                         snap_to (drag_info.grab_frame);
3804                 }
3805                 
3806                 if (pending_position < drag_info.grab_frame) {
3807                         start = pending_position;
3808                         end = drag_info.grab_frame;
3809                 } else {
3810                         end = pending_position;
3811                         start = drag_info.grab_frame;
3812                 }
3813                 
3814                 /* first drag: Either add to the selection
3815                    or create a new selection->
3816                 */
3817                 
3818                 if (drag_info.first_move) {
3819                         
3820                         begin_reversible_command (_("range selection"));
3821                         
3822                         if (drag_info.copy) {
3823                                 /* adding to the selection */
3824                                 clicked_selection = selection->add (start, end);
3825                                 drag_info.copy = false;
3826                         } else {
3827                                 /* new selection-> */
3828                                 clicked_selection = selection->set (clicked_axisview, start, end);
3829                         }
3830                 } 
3831                 break;
3832                 
3833         case SelectionStartTrim:
3834                 
3835                 if (drag_info.first_move) {
3836                         begin_reversible_command (_("trim selection start"));
3837                 }
3838                 
3839                 start = selection->time[clicked_selection].start;
3840                 end = selection->time[clicked_selection].end;
3841
3842                 if (pending_position > end) {
3843                         start = end;
3844                 } else {
3845                         start = pending_position;
3846                 }
3847                 break;
3848                 
3849         case SelectionEndTrim:
3850                 
3851                 if (drag_info.first_move) {
3852                         begin_reversible_command (_("trim selection end"));
3853                 }
3854                 
3855                 start = selection->time[clicked_selection].start;
3856                 end = selection->time[clicked_selection].end;
3857
3858                 if (pending_position < start) {
3859                         end = start;
3860                 } else {
3861                         end = pending_position;
3862                 }
3863                 
3864                 break;
3865                 
3866         case SelectionMove:
3867                 
3868                 if (drag_info.first_move) {
3869                         begin_reversible_command (_("move selection"));
3870                 }
3871                 
3872                 start = selection->time[clicked_selection].start;
3873                 end = selection->time[clicked_selection].end;
3874                 
3875                 length = end - start;
3876                 
3877                 start = pending_position;
3878                 snap_to (start);
3879                 
3880                 end = start + length;
3881                 
3882                 break;
3883         }
3884         
3885         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
3886                 start_canvas_autoscroll (1);
3887         }
3888
3889         if (start != end) {
3890                 selection->replace (clicked_selection, start, end);
3891         }
3892
3893         drag_info.last_pointer_frame = pending_position;
3894         drag_info.first_move = false;
3895
3896         if (selection_op == SelectionMove) {
3897                 show_verbose_time_cursor(start, 10);    
3898         } else {
3899                 show_verbose_time_cursor(pending_position, 10); 
3900         }
3901 }
3902
3903 void
3904 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
3905 {
3906         if (!drag_info.first_move) {
3907                 drag_selection (item, event);
3908                 /* XXX this is not object-oriented programming at all. ick */
3909                 if (selection->time.consolidate()) {
3910                         selection->TimeChanged ();
3911                 }
3912                 commit_reversible_command ();
3913         } else {
3914                 /* just a click, no pointer movement.*/
3915
3916                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3917
3918                         selection->clear_time();
3919
3920                 } 
3921         }
3922
3923         /* XXX what happens if its a music selection? */
3924         session->set_audio_range (selection->time);
3925         stop_canvas_autoscroll ();
3926 }
3927
3928 void
3929 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
3930 {
3931         double speed = 1.0;
3932         TimeAxisView* tvp = clicked_axisview;
3933         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3934
3935         if (tv && tv->is_track()) {
3936                 speed = tv->get_diskstream()->speed();
3937         }
3938         
3939         nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
3940         nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
3941         nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
3942
3943         motion_frozen_playlists.clear();
3944         
3945         //drag_info.item = clicked_regionview->get_name_highlight();
3946         drag_info.item = item;
3947         drag_info.motion_callback = &Editor::trim_motion_callback;
3948         drag_info.finished_callback = &Editor::trim_finished_callback;
3949
3950         start_grab (event, trimmer_cursor);
3951         
3952         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
3953                 trim_op = ContentsTrim;
3954         } else {
3955                 /* These will get overridden for a point trim.*/
3956                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
3957                         /* closer to start */
3958                         trim_op = StartTrim;
3959                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
3960                         /* closer to end */
3961                         trim_op = EndTrim;
3962                 }
3963         }
3964
3965         switch (trim_op) {
3966         case StartTrim:
3967                 show_verbose_time_cursor(region_start, 10);     
3968                 break;
3969         case EndTrim:
3970                 show_verbose_time_cursor(region_end, 10);       
3971                 break;
3972         case ContentsTrim:
3973                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
3974                 break;
3975         }
3976 }
3977
3978 void
3979 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3980 {
3981         RegionView* rv = clicked_regionview;
3982         nframes_t frame_delta = 0;
3983         bool left_direction;
3984         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
3985
3986         /* snap modifier works differently here..
3987            its' current state has to be passed to the 
3988            various trim functions in order to work properly 
3989         */ 
3990
3991         double speed = 1.0;
3992         TimeAxisView* tvp = clicked_axisview;
3993         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3994         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
3995
3996         if (tv && tv->is_track()) {
3997                 speed = tv->get_diskstream()->speed();
3998         }
3999         
4000         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
4001                 left_direction = true;
4002         } else {
4003                 left_direction = false;
4004         }
4005
4006         if (obey_snap) {
4007                 snap_to (drag_info.current_pointer_frame);
4008         }
4009
4010         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4011                 return;
4012         }
4013
4014         if (drag_info.first_move) {
4015         
4016                 string trim_type;
4017
4018                 switch (trim_op) {
4019                 case StartTrim:
4020                         trim_type = "Region start trim";
4021                         break;
4022                 case EndTrim:
4023                         trim_type = "Region end trim";
4024                         break;
4025                 case ContentsTrim:
4026                         trim_type = "Region content trim";
4027                         break;
4028                 }
4029
4030                 begin_reversible_command (trim_type);
4031
4032                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4033                         (*i)->region()->set_opaque(false);
4034                         (*i)->region()->freeze ();
4035                 
4036                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4037                         if (arv)
4038                                 arv->temporarily_hide_envelope ();
4039
4040                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4041                         insert_result = motion_frozen_playlists.insert (pl);
4042                         if (insert_result.second) {
4043                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
4044                         }
4045                 }
4046         }
4047
4048         if (left_direction) {
4049                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
4050         } else {
4051                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
4052         }
4053
4054         switch (trim_op) {              
4055         case StartTrim:
4056                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
4057                         break;
4058                 } else {
4059                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4060                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
4061                         }
4062                         break;
4063                 }
4064                 
4065         case EndTrim:
4066                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
4067                         break;
4068                 } else {
4069                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4070                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
4071                         }
4072                         break;
4073                 }
4074                 
4075         case ContentsTrim:
4076                 {
4077                         bool swap_direction = false;
4078
4079                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
4080                                 swap_direction = true;
4081                         }
4082                         
4083                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4084                              i != selection->regions.by_layer().end(); ++i)
4085                         {
4086                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
4087                         }
4088                 }
4089                 break;
4090         }
4091
4092         switch (trim_op) {
4093         case StartTrim:
4094                 show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);     
4095                 break;
4096         case EndTrim:
4097                 show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);   
4098                 break;
4099         case ContentsTrim:
4100                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4101                 break;
4102         }
4103
4104         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4105         drag_info.first_move = false;
4106 }
4107
4108 void
4109 Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
4110 {
4111         boost::shared_ptr<Region> region (rv.region());
4112
4113         if (region->locked()) {
4114                 return;
4115         }
4116
4117         nframes_t new_bound;
4118
4119         double speed = 1.0;
4120         TimeAxisView* tvp = clicked_axisview;
4121         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4122
4123         if (tv && tv->is_track()) {
4124                 speed = tv->get_diskstream()->speed();
4125         }
4126         
4127         if (left_direction) {
4128                 if (swap_direction) {
4129                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4130                 } else {
4131                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4132                 }
4133         } else {
4134                 if (swap_direction) {
4135                         new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4136                 } else {
4137                         new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4138                 }
4139         }
4140
4141         if (obey_snap) {
4142                 snap_to (new_bound);
4143         }
4144         region->trim_start ((nframes_t) (new_bound * speed), this);     
4145         rv.region_changed (StartChanged);
4146 }
4147
4148 void
4149 Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4150 {
4151         boost::shared_ptr<Region> region (rv.region()); 
4152
4153         if (region->locked()) {
4154                 return;
4155         }
4156
4157         nframes_t new_bound;
4158
4159         double speed = 1.0;
4160         TimeAxisView* tvp = clicked_axisview;
4161         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4162
4163         if (tv && tv->is_track()) {
4164                 speed = tv->get_diskstream()->speed();
4165         }
4166         
4167         if (left_direction) {
4168                 new_bound = (nframes_t) (region->position()/speed) - frame_delta;
4169         } else {
4170                 new_bound = (nframes_t) (region->position()/speed) + frame_delta;
4171         }
4172
4173         if (obey_snap) {
4174                 snap_to (new_bound, (left_direction ? 0 : 1));  
4175         }
4176
4177         region->trim_front ((nframes_t) (new_bound * speed), this);
4178
4179         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
4180 }
4181
4182 void
4183 Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
4184 {
4185         boost::shared_ptr<Region> region (rv.region());
4186
4187         if (region->locked()) {
4188                 return;
4189         }
4190
4191         nframes_t new_bound;
4192
4193         double speed = 1.0;
4194         TimeAxisView* tvp = clicked_axisview;
4195         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
4196
4197         if (tv && tv->is_track()) {
4198                 speed = tv->get_diskstream()->speed();
4199         }
4200         
4201         if (left_direction) {
4202                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
4203         } else {
4204                 new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
4205         }
4206
4207         if (obey_snap) {
4208                 snap_to (new_bound);
4209         }
4210         region->trim_end ((nframes_t) (new_bound * speed), this);
4211         rv.region_changed (LengthChanged);
4212 }
4213         
4214 void
4215 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4216 {
4217         if (!drag_info.first_move) {
4218                 trim_motion_callback (item, event);
4219                 
4220                 if (!clicked_regionview->get_selected()) {
4221                         thaw_region_after_trim (*clicked_regionview);           
4222                 } else {
4223                         
4224                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4225                              i != selection->regions.by_layer().end(); ++i)
4226                         {
4227                                 thaw_region_after_trim (**i);
4228                                 (*i)->region()->set_opaque(true);
4229                         }
4230                 }
4231                 
4232                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
4233                         //(*p)->thaw ();
4234                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
4235                 }
4236                 
4237                 motion_frozen_playlists.clear ();
4238
4239                 commit_reversible_command();
4240         } else {
4241                 /* no mouse movement */
4242                 point_trim (event);
4243         }
4244 }
4245
4246 void
4247 Editor::point_trim (GdkEvent* event)
4248 {
4249         RegionView* rv = clicked_regionview;
4250         nframes_t new_bound = drag_info.current_pointer_frame;
4251
4252         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4253                 snap_to (new_bound);
4254         }
4255
4256         /* Choose action dependant on which button was pressed */
4257         switch (event->button.button) {
4258         case 1:
4259                 trim_op = StartTrim;
4260                 begin_reversible_command (_("Start point trim"));
4261
4262                 if (rv->get_selected()) {
4263
4264                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
4265                              i != selection->regions.by_layer().end(); ++i)
4266                         {
4267                                 if (!(*i)->region()->locked()) {
4268                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4269                                         XMLNode &before = pl->get_state();
4270                                         (*i)->region()->trim_front (new_bound, this);   
4271                                         XMLNode &after = pl->get_state();
4272                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4273                                 }
4274                         }
4275
4276                 } else {
4277
4278                         if (!rv->region()->locked()) {
4279                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4280                                 XMLNode &before = pl->get_state();
4281                                 rv->region()->trim_front (new_bound, this);     
4282                                 XMLNode &after = pl->get_state();
4283                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4284                         }
4285                 }
4286
4287                 commit_reversible_command();
4288         
4289                 break;
4290         case 2:
4291                 trim_op = EndTrim;
4292                 begin_reversible_command (_("End point trim"));
4293
4294                 if (rv->get_selected()) {
4295                         
4296                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
4297                         {
4298                                 if (!(*i)->region()->locked()) {
4299                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4300                                         XMLNode &before = pl->get_state();
4301                                         (*i)->region()->trim_end (new_bound, this);
4302                                         XMLNode &after = pl->get_state();
4303                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4304                                 }
4305                         }
4306
4307                 } else {
4308
4309                         if (!rv->region()->locked()) {
4310                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
4311                                 XMLNode &before = pl->get_state();
4312                                 rv->region()->trim_end (new_bound, this);
4313                                 XMLNode &after = pl->get_state();
4314                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
4315                         }
4316                 }
4317
4318                 commit_reversible_command();
4319         
4320                 break;
4321         default:
4322                 break;
4323         }
4324 }
4325
4326 void
4327 Editor::thaw_region_after_trim (RegionView& rv)
4328 {
4329         boost::shared_ptr<Region> region (rv.region());
4330
4331         if (region->locked()) {
4332                 return;
4333         }
4334
4335         region->thaw (_("trimmed region"));
4336         XMLNode &after = region->playlist()->get_state();
4337         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
4338
4339         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
4340         if (arv)
4341                 arv->unhide_envelope ();
4342 }
4343
4344 void
4345 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
4346 {
4347         Marker* marker;
4348         bool is_start;
4349
4350         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
4351                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
4352                 /*NOTREACHED*/
4353         }
4354
4355         Location* location = find_location_from_marker (marker, is_start);      
4356         location->set_hidden (true, this);
4357 }
4358
4359
4360 void
4361 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
4362 {
4363         if (session == 0) {
4364                 return;
4365         }
4366
4367         drag_info.item = item;
4368         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
4369         drag_info.finished_callback = &Editor::end_range_markerbar_op;
4370
4371         range_marker_op = op;
4372
4373         if (!temp_location) {
4374                 temp_location = new Location;
4375         }
4376         
4377         switch (op) {
4378         case CreateRangeMarker:
4379         case CreateTransportMarker:
4380                 
4381                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
4382                         drag_info.copy = true;
4383                 } else {
4384                         drag_info.copy = false;
4385                 }
4386                 start_grab (event, selector_cursor);
4387                 break;
4388         }
4389
4390         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4391         
4392 }
4393
4394 void
4395 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4396 {
4397         nframes_t start = 0;
4398         nframes_t end = 0;
4399         ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
4400         
4401         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4402                 snap_to (drag_info.current_pointer_frame);
4403         }
4404
4405         /* only alter selection if the current frame is 
4406            different from the last frame position.
4407          */
4408         
4409         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4410         
4411         switch (range_marker_op) {
4412         case CreateRangeMarker:
4413         case CreateTransportMarker:
4414                 if (drag_info.first_move) {
4415                         snap_to (drag_info.grab_frame);
4416                 }
4417                 
4418                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4419                         start = drag_info.current_pointer_frame;
4420                         end = drag_info.grab_frame;
4421                 } else {
4422                         end = drag_info.current_pointer_frame;
4423                         start = drag_info.grab_frame;
4424                 }
4425                 
4426                 /* first drag: Either add to the selection
4427                    or create a new selection.
4428                 */
4429                 
4430                 if (drag_info.first_move) {
4431                         
4432                         temp_location->set (start, end);
4433                         
4434                         crect->show ();
4435
4436                         update_marker_drag_item (temp_location);
4437                         range_marker_drag_rect->show();
4438                         range_marker_drag_rect->raise_to_top();
4439                         
4440                 } 
4441                 break;          
4442         }
4443         
4444         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4445                 start_canvas_autoscroll (1);
4446         }
4447         
4448         if (start != end) {
4449                 temp_location->set (start, end);
4450
4451                 double x1 = frame_to_pixel (start);
4452                 double x2 = frame_to_pixel (end);
4453                 crect->property_x1() = x1;
4454                 crect->property_x2() = x2;
4455
4456                 update_marker_drag_item (temp_location);
4457         }
4458
4459         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4460         drag_info.first_move = false;
4461
4462         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4463         
4464 }
4465
4466 void
4467 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
4468 {
4469         Location * newloc = 0;
4470         string rangename;
4471         
4472         if (!drag_info.first_move) {
4473                 drag_range_markerbar_op (item, event);
4474
4475                 switch (range_marker_op) {
4476                 case CreateRangeMarker:
4477                     {
4478                         begin_reversible_command (_("new range marker"));
4479                         XMLNode &before = session->locations()->get_state();
4480                         session->locations()->next_available_name(rangename,"unnamed");
4481                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
4482                         session->locations()->add (newloc, true);
4483                         XMLNode &after = session->locations()->get_state();
4484                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
4485                         commit_reversible_command ();
4486                         
4487                         range_bar_drag_rect->hide();
4488                         range_marker_drag_rect->hide();
4489                         break;
4490                     }
4491
4492                 case CreateTransportMarker:
4493                         // popup menu to pick loop or punch
4494                         new_transport_marker_context_menu (&event->button, item);
4495                         
4496                         break;
4497                 }
4498         } else {
4499                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4500
4501                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
4502
4503                         nframes_t start;
4504                         nframes_t end;
4505
4506                         start = session->locations()->first_mark_before (drag_info.grab_frame);
4507                         end = session->locations()->first_mark_after (drag_info.grab_frame);
4508                         
4509                         if (end == max_frames) {
4510                                 end = session->current_end_frame ();
4511                         }
4512
4513                         if (start == 0) {
4514                                 start = session->current_start_frame ();
4515                         }
4516
4517                         switch (mouse_mode) {
4518                         case MouseObject:
4519                                 /* find the two markers on either side and then make the selection from it */
4520                                 cerr << "select between " << start << " .. " << end << endl;
4521                                 select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
4522                                 break;
4523
4524                         case MouseRange:
4525                                 /* find the two markers on either side of the click and make the range out of it */
4526                                 selection->set (0, start, end);
4527                                 break;
4528
4529                         default:
4530                                 break;
4531                         }
4532                 } 
4533         }
4534
4535         stop_canvas_autoscroll ();
4536 }
4537
4538
4539
4540 void
4541 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4542 {
4543         drag_info.item = item;
4544         drag_info.motion_callback = &Editor::drag_mouse_zoom;
4545         drag_info.finished_callback = &Editor::end_mouse_zoom;
4546
4547         start_grab (event, zoom_cursor);
4548
4549         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4550 }
4551
4552 void
4553 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4554 {
4555         nframes_t start;
4556         nframes_t end;
4557
4558         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4559                 snap_to (drag_info.current_pointer_frame);
4560                 
4561                 if (drag_info.first_move) {
4562                         snap_to (drag_info.grab_frame);
4563                 }
4564         }
4565                 
4566         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
4567
4568         /* base start and end on initial click position */
4569         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4570                 start = drag_info.current_pointer_frame;
4571                 end = drag_info.grab_frame;
4572         } else {
4573                 end = drag_info.current_pointer_frame;
4574                 start = drag_info.grab_frame;
4575         }
4576         
4577         if (start != end) {
4578
4579                 if (drag_info.first_move) {
4580                         zoom_rect->show();
4581                         zoom_rect->raise_to_top();
4582                 }
4583
4584                 reposition_zoom_rect(start, end);
4585
4586                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4587                 drag_info.first_move = false;
4588
4589                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4590         }
4591 }
4592
4593 void
4594 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
4595 {
4596         if (!drag_info.first_move) {
4597                 drag_mouse_zoom (item, event);
4598                 
4599                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4600                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
4601                 } else {
4602                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
4603                 }               
4604         } else {
4605                 temporal_zoom_to_frame (false, drag_info.grab_frame);
4606                 /*
4607                 temporal_zoom_step (false);
4608                 center_screen (drag_info.grab_frame);
4609                 */
4610         }
4611
4612         zoom_rect->hide();
4613 }
4614
4615 void
4616 Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
4617 {
4618         double x1 = frame_to_pixel (start);
4619         double x2 = frame_to_pixel (end);
4620         double y2 = full_canvas_height - 1.0;
4621
4622         zoom_rect->property_x1() = x1;
4623         zoom_rect->property_y1() = 1.0;
4624         zoom_rect->property_x2() = x2;
4625         zoom_rect->property_y2() = y2;
4626 }
4627
4628 void
4629 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4630 {
4631         drag_info.item = item;
4632         drag_info.motion_callback = &Editor::drag_rubberband_select;
4633         drag_info.finished_callback = &Editor::end_rubberband_select;
4634
4635         start_grab (event, cross_hair_cursor);
4636
4637         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4638 }
4639
4640 void
4641 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4642 {
4643         nframes_t start;
4644         nframes_t end;
4645         double y1;
4646         double y2;
4647
4648         /* use a bigger drag threshold than the default */
4649
4650         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
4651                 return;
4652         }
4653
4654 //      if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4655 //              snap_to (drag_info.current_pointer_frame);
4656                 
4657 //              if (drag_info.first_move) {
4658 //                      snap_to (drag_info.grab_frame);
4659 //              }
4660 //      }
4661                 
4662
4663         /* base start and end on initial click position */
4664         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
4665                 start = drag_info.current_pointer_frame;
4666                 end = drag_info.grab_frame;
4667         } else {
4668                 end = drag_info.current_pointer_frame;
4669                 start = drag_info.grab_frame;
4670         }
4671
4672         if (drag_info.current_pointer_y < drag_info.grab_y) {
4673                 y1 = drag_info.current_pointer_y;
4674                 y2 = drag_info.grab_y;
4675         }
4676         else {
4677                 y2 = drag_info.current_pointer_y;
4678                 y1 = drag_info.grab_y;
4679         }
4680
4681         
4682         if (start != end || y1 != y2) {
4683
4684                 double x1 = frame_to_pixel (start);
4685                 double x2 = frame_to_pixel (end);
4686                 
4687                 rubberband_rect->property_x1() = x1;
4688                 rubberband_rect->property_y1() = y1;
4689                 rubberband_rect->property_x2() = x2;
4690                 rubberband_rect->property_y2() = y2;
4691
4692                 rubberband_rect->show();
4693                 rubberband_rect->raise_to_top();
4694                 
4695                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4696                 drag_info.first_move = false;
4697
4698                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4699         }
4700 }
4701
4702 void
4703 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
4704 {
4705         if (!drag_info.first_move) {
4706
4707                 drag_rubberband_select (item, event);
4708
4709                 double y1,y2;
4710                 if (drag_info.current_pointer_y < drag_info.grab_y) {
4711                         y1 = drag_info.current_pointer_y;
4712                         y2 = drag_info.grab_y;
4713                 }
4714                 else {
4715                         y2 = drag_info.current_pointer_y;
4716                         y1 = drag_info.grab_y;
4717                 }
4718
4719
4720                 Selection::Operation op = Keyboard::selection_type (event->button.state);
4721                 bool commit;
4722
4723                 begin_reversible_command (_("select regions"));
4724
4725                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
4726                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
4727                 } else {
4728                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
4729                 }               
4730
4731                 if (commit) {
4732                         commit_reversible_command ();
4733                 }
4734                 
4735         } else {
4736                 selection->clear_regions();
4737                 selection->clear_points ();
4738                 selection->clear_lines ();
4739         }
4740
4741         rubberband_rect->hide();
4742 }
4743
4744
4745 gint
4746 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
4747 {
4748         using namespace Gtkmm2ext;
4749
4750         ArdourPrompter prompter (false);
4751
4752         prompter.set_prompt (_("Name for region:"));
4753         prompter.set_initial_text (clicked_regionview->region()->name());
4754         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
4755         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
4756         prompter.show_all ();
4757         switch (prompter.run ()) {
4758         case Gtk::RESPONSE_ACCEPT:
4759         string str;
4760                 prompter.get_result(str);
4761                 if (str.length()) {
4762                 clicked_regionview->region()->set_name (str);
4763                 }
4764                 break;
4765         }
4766         return true;
4767 }
4768
4769 void
4770 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4771 {
4772         drag_info.item = item;
4773         drag_info.motion_callback = &Editor::time_fx_motion;
4774         drag_info.finished_callback = &Editor::end_time_fx;
4775
4776         start_grab (event);
4777
4778         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4779 }
4780
4781 void
4782 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
4783 {
4784         RegionView* rv = clicked_regionview;
4785
4786         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4787                 snap_to (drag_info.current_pointer_frame);
4788         }
4789
4790         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
4791                 return;
4792         }
4793
4794         if (drag_info.current_pointer_frame > rv->region()->position()) {
4795                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
4796         }
4797
4798         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
4799         drag_info.first_move = false;
4800
4801         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
4802 }
4803
4804 void
4805 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
4806 {
4807         clicked_regionview->get_time_axis_view().hide_timestretch ();
4808
4809         if (drag_info.first_move) {
4810                 return;
4811         }
4812         
4813         nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
4814         float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
4815         
4816         begin_reversible_command (_("timestretch"));
4817
4818         if (run_timestretch (selection->regions, percentage) == 0) {
4819                 session->commit_reversible_command ();
4820         }
4821 }
4822
4823 void
4824 Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
4825 {
4826         /* no brushing without a useful snap setting */
4827
4828         // FIXME
4829         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
4830         assert(arv);
4831
4832         switch (snap_mode) {
4833         case SnapMagnetic:
4834                 return; /* can't work because it allows region to be placed anywhere */
4835         default:
4836                 break; /* OK */
4837         }
4838
4839         switch (snap_type) {
4840         case SnapToFrame:
4841         case SnapToMark:
4842         case SnapToEditCursor:
4843                 return;
4844
4845         default:
4846                 break;
4847         }
4848
4849         /* don't brush a copy over the original */
4850         
4851         if (pos == rv->region()->position()) {
4852                 return;
4853         }
4854
4855         RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
4856
4857         if (atv == 0 || !atv->is_track()) {
4858                 return;
4859         }
4860
4861         boost::shared_ptr<Playlist> playlist = atv->playlist();
4862         double speed = atv->get_diskstream()->speed();
4863         
4864         XMLNode &before = playlist->get_state();
4865         playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
4866         XMLNode &after = playlist->get_state();
4867         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4868         
4869         // playlist is frozen, so we have to update manually
4870         
4871         playlist->Modified(); /* EMIT SIGNAL */
4872 }
4873
4874 gint
4875 Editor::track_height_step_timeout ()
4876 {
4877         struct timeval now;
4878         struct timeval delta;
4879         
4880         gettimeofday (&now, 0);
4881         timersub (&now, &last_track_height_step_timestamp, &delta);
4882         
4883         if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
4884                 current_stepping_trackview = 0;
4885                 return false;
4886         }
4887         return true;
4888 }