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