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