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