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