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