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