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