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