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