051463f20031eca89d72bc7b0008a42307c044a4
[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                         _pending_locate_request = true;
2433                 }
2434         } 
2435 }
2436
2437 void
2438 Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event)
2439 {
2440         _dragging_playhead = false;
2441         
2442         cursor_drag_motion_callback (item, event);
2443         
2444         if (item == &playhead_cursor->canvas_item) {
2445                 if (session) {
2446                         session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
2447                         _pending_locate_request = true;
2448                 }
2449         } 
2450 }
2451
2452 void
2453 Editor::update_marker_drag_item (Location *location)
2454 {
2455         double x1 = frame_to_pixel (location->start());
2456         double x2 = frame_to_pixel (location->end());
2457
2458         if (location->is_mark()) {
2459                 marker_drag_line_points.front().set_x(x1);
2460                 marker_drag_line_points.back().set_x(x1);
2461                 marker_drag_line->property_points() = marker_drag_line_points;
2462         } else {
2463                 range_marker_drag_rect->property_x1() = x1;
2464                 range_marker_drag_rect->property_x2() = x2;
2465         }
2466 }
2467
2468
2469 void
2470 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2471 {
2472         Marker* marker;
2473
2474         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2475                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2476                 /*NOTREACHED*/
2477         }
2478
2479         bool is_start;
2480
2481         Location  *location = find_location_from_marker (marker, is_start);
2482
2483         drag_info.item = item;
2484         drag_info.data = marker;
2485         drag_info.motion_callback = &Editor::marker_drag_motion_callback;
2486         drag_info.finished_callback = &Editor::marker_drag_finished_callback;
2487
2488         start_grab (event);
2489
2490         _dragging_edit_point = true;
2491
2492         drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
2493
2494         update_marker_drag_item (location);
2495
2496         if (location->is_mark()) {
2497                 // marker_drag_line->show();
2498                 // marker_drag_line->raise_to_top();
2499         } else {
2500                 range_marker_drag_rect->show();
2501                 //range_marker_drag_rect->raise_to_top();
2502         }
2503
2504         if (is_start) {
2505                 show_verbose_time_cursor (location->start(), 10);
2506         } else {
2507                 show_verbose_time_cursor (location->end(), 10);
2508         }
2509
2510         Selection::Operation op = Keyboard::selection_type (event->button.state);
2511
2512         switch (op) {
2513         case Selection::Toggle:
2514                 selection->toggle (marker);
2515                 break;
2516         case Selection::Set:
2517                 if (!selection->selected (marker)) {
2518                         selection->set (marker);
2519                 }
2520                 break;
2521         case Selection::Extend:
2522         {
2523                 Locations::LocationList ll;
2524                 list<Marker*> to_add;
2525                 nframes64_t s, e;
2526                 selection->markers.range (s, e);
2527                 s = min (marker->position(), s);
2528                 e = max (marker->position(), e);
2529                 s = min (s, e);
2530                 e = max (s, e);
2531                 if (e < max_frames) {
2532                         ++e;
2533                 }
2534                 session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2535                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2536                         LocationMarkers* lm = find_location_markers (*i);
2537                         if (lm) {
2538                                 if (lm->start) {
2539                                         to_add.push_back (lm->start);
2540                                 }
2541                                 if (lm->end) {
2542                                         to_add.push_back (lm->end);
2543                                 }
2544                         }
2545                 }
2546                 if (!to_add.empty()) {
2547                         selection->add (to_add);
2548                 }
2549                 break;
2550         }
2551         case Selection::Add:
2552                 selection->add (marker);
2553                 break;
2554         }
2555
2556         /* set up copies for us to manipulate during the drag */
2557
2558         drag_info.clear_copied_locations ();
2559
2560         for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
2561                 Location  *l = find_location_from_marker (*i, is_start);
2562                 drag_info.copied_locations.push_back (new Location (*l));
2563         }
2564 }
2565
2566 void
2567 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2568 {
2569         nframes64_t f_delta = 0;
2570         nframes64_t newframe;
2571         bool is_start;
2572         bool move_both = false;
2573         Marker* dragged_marker = (Marker*) drag_info.data;
2574         Marker* marker;
2575         Location  *real_location;
2576         Location *copy_location = 0;
2577
2578         if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
2579                 newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2580         } else {
2581                 newframe = 0;
2582         }
2583
2584         nframes64_t next = newframe;
2585
2586         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2587                 snap_to (newframe, 0, true);
2588         }
2589         
2590         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) { 
2591                 return;
2592         }
2593
2594         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2595                 move_both = true;
2596         }
2597
2598         MarkerSelection::iterator i;
2599         list<Location*>::iterator x;
2600
2601         /* find the marker we're dragging, and compute the delta */
2602
2603         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2604              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2605              ++i, ++x) {
2606
2607                 copy_location = *x;
2608                 marker = *i;
2609
2610                 if (marker == dragged_marker) {
2611
2612                         if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2613                                 /* que pasa ?? */
2614                                 return;
2615                         }
2616
2617                         if (real_location->is_mark()) {
2618                                 f_delta = newframe - copy_location->start();
2619                         } else {
2620
2621
2622                                 switch (marker->type()) {
2623                                 case Marker::Start:
2624                                 case Marker::LoopStart:
2625                                 case Marker::PunchIn:
2626                                         f_delta = newframe - copy_location->start();
2627                                         break;
2628
2629                                 case Marker::End:
2630                                 case Marker::LoopEnd:
2631                                 case Marker::PunchOut:
2632                                         f_delta = newframe - copy_location->end();
2633                                         break;
2634                                 default:
2635                                         /* what kind of marker is this ? */
2636                                         return;
2637                                 }
2638                         }
2639                         break;
2640                 }
2641         }
2642
2643         if (i == selection->markers.end()) {
2644                 /* hmm, impossible - we didn't find the dragged marker */
2645                 return;
2646         }
2647
2648         /* now move them all */
2649
2650         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2651              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2652              ++i, ++x) {
2653
2654                 copy_location = *x;
2655                 marker = *i;
2656
2657                 /* call this to find out if its the start or end */
2658                 
2659                 if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
2660                         continue;
2661                 }
2662                 
2663                 if (real_location->locked()) {
2664                         continue;
2665                 }
2666
2667                 if (copy_location->is_mark()) {
2668
2669                         /* just move it */
2670                         
2671                         copy_location->set_start (copy_location->start() + f_delta);
2672
2673                 } else {
2674                         
2675                         nframes64_t new_start = copy_location->start() + f_delta;
2676                         nframes64_t new_end = copy_location->end() + f_delta;
2677                         
2678                         if (is_start) { // start-of-range marker
2679                                 
2680                                 if (move_both) {
2681                                         copy_location->set_start (new_start);
2682                                         copy_location->set_end (new_end);
2683                                 } else  if (new_start < copy_location->end()) {
2684                                         copy_location->set_start (new_start);
2685                                 } else { 
2686                                         snap_to (next, 1, true);
2687                                         copy_location->set_end (next);
2688                                         copy_location->set_start (newframe);
2689                                 }
2690                                 
2691                         } else { // end marker
2692                                 
2693                                 if (move_both) {
2694                                         copy_location->set_end (new_end);
2695                                         copy_location->set_start (new_start);
2696                                 } else if (new_end > copy_location->start()) {
2697                                         copy_location->set_end (new_end);
2698                                 } else if (newframe > 0) {
2699                                         snap_to (next, -1, true);
2700                                         copy_location->set_start (next);
2701                                         copy_location->set_end (newframe);
2702                                 }
2703                         }
2704                 }
2705                 update_marker_drag_item (copy_location);
2706
2707                 LocationMarkers* lm = find_location_markers (real_location);
2708
2709                 if (lm) {
2710                         lm->set_position (copy_location->start(), copy_location->end());
2711                 }
2712         }
2713
2714         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
2715         drag_info.first_move = false;
2716
2717         if (drag_info.copied_locations.empty()) {
2718                 abort();
2719         }
2720
2721         edit_point_clock.set (drag_info.copied_locations.front()->start());
2722         show_verbose_time_cursor (newframe, 10);
2723
2724 #ifdef GTKOSX
2725         track_canvas->update_now ();
2726 #endif
2727         edit_point_clock.set (copy_location->start());
2728 }
2729
2730 void
2731 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2732 {
2733         if (drag_info.first_move) {
2734
2735                 /* just a click, do nothing but finish
2736                    off the selection process
2737                 */
2738
2739                 Selection::Operation op = Keyboard::selection_type (event->button.state);
2740                 Marker* marker = (Marker *) drag_info.data;
2741
2742                 switch (op) {
2743                 case Selection::Set:
2744                         if (selection->selected (marker) && selection->markers.size() > 1) {
2745                                 selection->set (marker);
2746                         }
2747                         break;
2748
2749                 case Selection::Toggle:
2750                 case Selection::Extend:
2751                 case Selection::Add:
2752                         break;
2753                 }
2754                 
2755                 return;
2756         }
2757
2758         _dragging_edit_point = false;
2759         
2760
2761         begin_reversible_command ( _("move marker") );
2762         XMLNode &before = session->locations()->get_state();
2763
2764         MarkerSelection::iterator i;
2765         list<Location*>::iterator x;
2766         bool is_start;
2767
2768         for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
2769              x != drag_info.copied_locations.end() && i != selection->markers.end(); 
2770              ++i, ++x) {
2771         
2772                 Location * location = find_location_from_marker ((*i), is_start);
2773                 
2774                 if (location) {
2775                         
2776                         if (location->locked()) {
2777                                 return;
2778                         }
2779                         
2780                         if (location->is_mark()) {
2781                                 location->set_start ((*x)->start());
2782                         } else {
2783                                 location->set ((*x)->start(), (*x)->end());
2784                         }
2785                 }
2786         }
2787
2788         XMLNode &after = session->locations()->get_state();
2789         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2790         commit_reversible_command ();
2791         
2792         marker_drag_line->hide();
2793         range_marker_drag_rect->hide();
2794 }
2795
2796 void
2797 Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2798 {
2799         Marker* marker;
2800         MeterMarker* meter_marker;
2801
2802         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2803                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2804                 /*NOTREACHED*/
2805         }
2806
2807         meter_marker = dynamic_cast<MeterMarker*> (marker);
2808
2809         MetricSection& section (meter_marker->meter());
2810
2811         if (!section.movable()) {
2812                 return;
2813         }
2814
2815         drag_info.item = item;
2816         drag_info.copy = false;
2817         drag_info.data = marker;
2818         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2819         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2820
2821         start_grab (event);
2822
2823         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2824
2825         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2826 }
2827
2828 void
2829 Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2830 {
2831         Marker* marker;
2832         MeterMarker* meter_marker;
2833
2834         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2835                 fatal << _("programming error: meter marker canvas item has no marker object pointer!") << endmsg;
2836                 /*NOTREACHED*/
2837         }
2838
2839         meter_marker = dynamic_cast<MeterMarker*> (marker);
2840         
2841         // create a dummy marker for visual representation of moving the copy.
2842         // The actual copying is not done before we reach the finish callback.
2843         char name[64];
2844         snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
2845         MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, 
2846                                                   *new MeterSection(meter_marker->meter()));
2847
2848         drag_info.item = &new_marker->the_item();
2849         drag_info.copy = true;
2850         drag_info.data = new_marker;
2851         drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
2852         drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
2853
2854         start_grab (event);
2855
2856         drag_info.pointer_frame_offset = drag_info.grab_frame - meter_marker->meter().frame();  
2857
2858         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2859 }
2860
2861 void
2862 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2863 {
2864         MeterMarker* marker = (MeterMarker *) drag_info.data;
2865         nframes64_t adjusted_frame;
2866
2867         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
2868                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
2869         }
2870         else {
2871                 adjusted_frame = 0;
2872         }
2873         
2874         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2875                 snap_to (adjusted_frame);
2876         }
2877         
2878         if (adjusted_frame == drag_info.last_pointer_frame) return;
2879
2880         marker->set_position (adjusted_frame);
2881         
2882         
2883         drag_info.last_pointer_frame = adjusted_frame;
2884         drag_info.first_move = false;
2885
2886         show_verbose_time_cursor (adjusted_frame, 10);
2887 }
2888
2889 void
2890 Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
2891 {
2892         if (drag_info.first_move) return;
2893
2894         meter_marker_drag_motion_callback (drag_info.item, event);
2895         
2896         MeterMarker* marker = (MeterMarker *) drag_info.data;
2897         BBT_Time when;
2898         
2899         TempoMap& map (session->tempo_map());
2900         map.bbt_time (drag_info.last_pointer_frame, when);
2901         
2902         if (drag_info.copy == true) {
2903                 begin_reversible_command (_("copy meter mark"));
2904                 XMLNode &before = map.get_state();
2905                 map.add_meter (marker->meter(), when);
2906                 XMLNode &after = map.get_state();
2907                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2908                 commit_reversible_command ();
2909
2910                 // delete the dummy marker we used for visual representation of copying.
2911                 // a new visual marker will show up automatically.
2912                 delete marker;
2913         } else {
2914                 begin_reversible_command (_("move meter mark"));
2915                 XMLNode &before = map.get_state();
2916                 map.move_meter (marker->meter(), when);
2917                 XMLNode &after = map.get_state();
2918                 session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2919                 commit_reversible_command ();
2920         }
2921 }
2922
2923 void
2924 Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
2925 {
2926         Marker* marker;
2927         TempoMarker* tempo_marker;
2928
2929         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2930                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2931                 /*NOTREACHED*/
2932         }
2933
2934         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2935                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2936                 /*NOTREACHED*/
2937         }
2938
2939         MetricSection& section (tempo_marker->tempo());
2940
2941         if (!section.movable()) {
2942                 return;
2943         }
2944
2945         drag_info.item = item;
2946         drag_info.copy = false;
2947         drag_info.data = marker;
2948         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2949         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2950
2951         start_grab (event);
2952
2953         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();  
2954         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2955 }
2956
2957 void
2958 Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
2959 {
2960         Marker* marker;
2961         TempoMarker* tempo_marker;
2962
2963         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
2964                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
2965                 /*NOTREACHED*/
2966         }
2967
2968         if ((tempo_marker = dynamic_cast<TempoMarker *> (marker)) == 0) {
2969                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
2970                 /*NOTREACHED*/
2971         }
2972
2973         // create a dummy marker for visual representation of moving the copy.
2974         // The actual copying is not done before we reach the finish callback.
2975         char name[64];
2976         snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
2977         TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, 
2978                                                   *new TempoSection(tempo_marker->tempo()));
2979
2980         drag_info.item = &new_marker->the_item();
2981         drag_info.copy = true;
2982         drag_info.data = new_marker;
2983         drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
2984         drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
2985
2986         start_grab (event);
2987
2988         drag_info.pointer_frame_offset = drag_info.grab_frame - tempo_marker->tempo().frame();
2989
2990         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
2991 }
2992
2993 void
2994 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
2995 {
2996         TempoMarker* marker = (TempoMarker *) drag_info.data;
2997         nframes64_t adjusted_frame;
2998         
2999         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
3000                 adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3001         }
3002         else {
3003                 adjusted_frame = 0;
3004         }
3005
3006         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3007                 snap_to (adjusted_frame);
3008         }
3009         
3010         if (adjusted_frame == drag_info.last_pointer_frame) return;
3011
3012         /* OK, we've moved far enough to make it worth actually move the thing. */
3013                 
3014         marker->set_position (adjusted_frame);
3015         
3016         show_verbose_time_cursor (adjusted_frame, 10);
3017
3018         drag_info.last_pointer_frame = adjusted_frame;
3019         drag_info.first_move = false;
3020 }
3021
3022 void
3023 Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3024 {
3025         if (drag_info.first_move) return;
3026         
3027         tempo_marker_drag_motion_callback (drag_info.item, event);
3028         
3029         TempoMarker* marker = (TempoMarker *) drag_info.data;
3030         BBT_Time when;
3031         
3032         TempoMap& map (session->tempo_map());
3033         map.bbt_time (drag_info.last_pointer_frame, when);
3034
3035         if (drag_info.copy == true) {
3036                 begin_reversible_command (_("copy tempo mark"));
3037                 XMLNode &before = map.get_state();
3038                 map.add_tempo (marker->tempo(), when);
3039                 XMLNode &after = map.get_state();
3040                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3041                 commit_reversible_command ();
3042                 
3043                 // delete the dummy marker we used for visual representation of copying.
3044                 // a new visual marker will show up automatically.
3045                 delete marker;
3046         } else {
3047                 begin_reversible_command (_("move tempo mark"));
3048                 XMLNode &before = map.get_state();
3049                 map.move_tempo (marker->tempo(), when);
3050                 XMLNode &after = map.get_state();
3051                 session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3052                 commit_reversible_command ();
3053         }
3054 }
3055
3056 void
3057 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
3058 {
3059         ControlPoint* control_point;
3060
3061         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3062                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3063                 /*NOTREACHED*/
3064         }
3065
3066         // We shouldn't remove the first or last gain point
3067         if (control_point->line().is_last_point(*control_point) ||
3068                 control_point->line().is_first_point(*control_point)) { 
3069                 return;
3070         }
3071
3072         control_point->line().remove_point (*control_point);
3073 }
3074
3075 void
3076 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
3077 {
3078         ControlPoint* control_point;
3079
3080         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3081                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3082                 /*NOTREACHED*/
3083         }
3084
3085         control_point->line().remove_point (*control_point);
3086 }
3087
3088 void
3089 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
3090 {
3091         ControlPoint* control_point;
3092
3093         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
3094                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3095                 /*NOTREACHED*/
3096         }
3097
3098         drag_info.item = item;
3099         drag_info.data = control_point;
3100         drag_info.motion_callback = &Editor::control_point_drag_motion_callback;
3101         drag_info.finished_callback = &Editor::control_point_drag_finished_callback;
3102
3103         start_grab (event, fader_cursor);
3104
3105         // start the grab at the center of the control point so
3106         // the point doesn't 'jump' to the mouse after the first drag
3107         drag_info.grab_x = control_point->get_x();
3108         drag_info.grab_y = control_point->get_y();
3109
3110         control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
3111         track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
3112
3113         drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
3114
3115         control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
3116
3117         float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
3118         set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction), 
3119                                    drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3120
3121         show_verbose_canvas_cursor ();
3122 }
3123
3124 void
3125 Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3126 {
3127         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3128
3129         double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
3130         double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3131
3132         if (event->button.state & Keyboard::SecondaryModifier) {
3133                 dx *= 0.1;
3134                 dy *= 0.1;
3135         }
3136
3137         double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
3138         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3139
3140         // calculate zero crossing point. back off by .01 to stay on the
3141         // positive side of zero
3142         double _unused = 0;
3143         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * cp->line().height() - .01;
3144         cp->line().parent_group().i2w(_unused, zero_gain_y);
3145
3146         // make sure we hit zero when passing through
3147         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3148                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3149                 cy = zero_gain_y;
3150         }
3151
3152         if (drag_info.x_constrained) {
3153                 cx = drag_info.grab_x;
3154         }
3155         if (drag_info.y_constrained) {
3156                 cy = drag_info.grab_y;
3157         }
3158
3159         drag_info.cumulative_x_drag = cx - drag_info.grab_x;
3160         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3161
3162         cp->line().parent_group().w2i (cx, cy);
3163
3164         cx = max (0.0, cx);
3165         cy = max (0.0, cy);
3166         cy = min ((double) cp->line().height(), cy);
3167
3168         //translate cx to frames
3169         nframes64_t cx_frames = unit_to_frame (cx);
3170
3171         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
3172                 snap_to (cx_frames);
3173         }
3174
3175         float fraction = 1.0 - (cy / cp->line().height());
3176
3177         bool push;
3178
3179         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3180                 push = true;
3181         } else {
3182                 push = false;
3183         }
3184
3185         cp->line().point_drag (*cp, cx_frames , fraction, push);
3186         
3187         set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
3188
3189         drag_info.first_move = false;
3190 }
3191
3192 void
3193 Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3194 {
3195         ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
3196
3197         if (drag_info.first_move) {
3198
3199                 /* just a click */
3200                 
3201                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3202                         reset_point_selection ();
3203                 }
3204
3205         } else {
3206                 control_point_drag_motion_callback (item, event);
3207         }
3208         cp->line().end_drag (cp);
3209 }
3210
3211 void
3212 Editor::edit_control_point (ArdourCanvas::Item* item)
3213 {
3214         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
3215
3216         if (p == 0) {
3217                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
3218                 /*NOTREACHED*/
3219         }
3220
3221         ControlPointDialog d (p);
3222         d.set_position (Gtk::WIN_POS_MOUSE);
3223         ensure_float (d);
3224
3225         if (d.run () != RESPONSE_ACCEPT) {
3226                 return;
3227         }
3228
3229         p->line().modify_point_y (*p, d.get_y_fraction ());
3230 }
3231
3232
3233 void
3234 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
3235 {
3236         switch (mouse_mode) {
3237         case MouseGain:
3238                 assert(dynamic_cast<AudioRegionView*>(clicked_regionview));
3239                 start_line_grab (dynamic_cast<AudioRegionView*>(clicked_regionview)->get_gain_line(), event);
3240                 break;
3241         default:
3242                 break;
3243         }
3244 }
3245
3246 void
3247 Editor::start_line_grab_from_line (ArdourCanvas::Item* item, GdkEvent* event)
3248 {
3249         AutomationLine* al;
3250         
3251         if ((al = reinterpret_cast<AutomationLine*> (item->get_data ("line"))) == 0) {
3252                 fatal << _("programming error: line canvas item has no line pointer!") << endmsg;
3253                 /*NOTREACHED*/
3254         }
3255
3256         start_line_grab (al, event);
3257 }
3258
3259 void
3260 Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
3261 {
3262         double cx;
3263         double cy;
3264         nframes64_t frame_within_region;
3265
3266         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3267            origin, and ditto for y.
3268         */
3269
3270         cx = event->button.x;
3271         cy = event->button.y;
3272
3273         line->parent_group().w2i (cx, cy);
3274
3275         frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
3276
3277         if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
3278                                             current_line_drag_info.after)) {
3279                 /* no adjacent points */
3280                 return;
3281         }
3282
3283         drag_info.item = &line->grab_item();
3284         drag_info.data = line;
3285         drag_info.motion_callback = &Editor::line_drag_motion_callback;
3286         drag_info.finished_callback = &Editor::line_drag_finished_callback;
3287
3288         start_grab (event, fader_cursor);
3289
3290         /* store grab start in parent frame */
3291
3292         drag_info.grab_x = cx;
3293         drag_info.grab_y = cy;
3294
3295         double fraction = 1.0 - (cy / line->height());
3296
3297         line->start_drag (0, drag_info.grab_frame, fraction);
3298         
3299         set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
3300                                    drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
3301         show_verbose_canvas_cursor ();
3302 }
3303
3304 void
3305 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3306 {
3307         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3308
3309         double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
3310         
3311         if (event->button.state & Keyboard::SecondaryModifier) {
3312                 dy *= 0.1;
3313         }
3314
3315         double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
3316
3317         // calculate zero crossing point. back off by .01 to stay on the
3318         // positive side of zero
3319         double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
3320
3321         // line->parent_group().i2w(_unused, zero_gain_y);
3322
3323         // make sure we hit zero when passing through
3324         if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
3325                         or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
3326                 cy = zero_gain_y;
3327         }
3328
3329         drag_info.cumulative_y_drag = cy - drag_info.grab_y;
3330
3331         cy = max (0.0, cy);
3332         cy = min ((double) line->height(), cy);
3333
3334
3335         double fraction = 1.0 - (cy / line->height());
3336
3337         bool push;
3338
3339         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3340                 push = false;
3341         } else {
3342                 push = true;
3343         }
3344
3345         line->line_drag (current_line_drag_info.before, current_line_drag_info.after, fraction, push);
3346         
3347         set_verbose_canvas_cursor_text (line->get_verbose_cursor_string (fraction));
3348 }
3349
3350 void
3351 Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3352 {
3353         AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
3354         line_drag_motion_callback (item, event);
3355         line->end_drag (0);
3356 }
3357
3358 void
3359 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3360 {
3361         if (selection->regions.empty() || clicked_regionview == 0) {
3362                 return;
3363         }
3364         _region_motion_group->raise_to_top ();
3365         drag_info.copy = false;
3366         drag_info.item = item;
3367         drag_info.data = clicked_regionview;
3368
3369         if (Config->get_edit_mode() == Splice) {
3370                 drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
3371                 drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
3372         } else {
3373                 drag_info.motion_callback = &Editor::region_drag_motion_callback;
3374                 drag_info.finished_callback = &Editor::region_drag_finished_callback;
3375         }
3376
3377         start_grab (event);
3378
3379         double speed = 1.0;
3380         TimeAxisView* tvp = clicked_axisview;
3381         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3382
3383         if (tv && tv->is_track()) {
3384                 speed = tv->get_diskstream()->speed();
3385         }
3386         
3387         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3388         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3389         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3390         drag_info.source_layer = clicked_regionview->region()->layer();
3391         drag_info.dest_trackview = drag_info.source_trackview;
3392         drag_info.dest_layer = drag_info.source_layer;
3393         // we want a move threshold
3394         drag_info.want_move_threshold = true;
3395         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3396
3397         begin_reversible_command (_("move region(s)"));
3398
3399         /* sync the canvas to what we think is its current state */
3400         track_canvas->update_now();
3401 }
3402
3403 void
3404 Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
3405 {
3406         drag_info.copy = false;
3407         drag_info.item = item;
3408         drag_info.data = clicked_axisview;
3409         drag_info.source_trackview = clicked_axisview;
3410         drag_info.dest_trackview = drag_info.source_trackview;
3411         drag_info.dest_layer = drag_info.source_layer;
3412         drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
3413         drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
3414
3415         start_grab (event);
3416 }
3417
3418 void
3419 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
3420 {
3421         if (selection->regions.empty() || clicked_regionview == 0) {
3422                 return;
3423         }
3424         _region_motion_group->raise_to_top ();
3425         drag_info.copy = true;
3426         drag_info.item = item;
3427         drag_info.data = clicked_regionview;    
3428
3429         start_grab(event);
3430
3431         TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
3432         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
3433         double speed = 1.0;
3434
3435         if (rtv && rtv->is_track()) {
3436                 speed = rtv->get_diskstream()->speed();
3437         }
3438         
3439         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3440         drag_info.dest_trackview = drag_info.source_trackview;
3441         drag_info.dest_layer = drag_info.source_layer;
3442         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3443         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3444         // we want a move threshold
3445         drag_info.want_move_threshold = true;
3446         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3447         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3448         show_verbose_time_cursor (drag_info.last_frame_position, 10);
3449 }
3450
3451 void
3452 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
3453 {
3454         if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
3455                 return;
3456         }
3457
3458         drag_info.copy = false;
3459         drag_info.item = item;
3460         drag_info.data = clicked_regionview;
3461         drag_info.motion_callback = &Editor::region_drag_motion_callback;
3462         drag_info.finished_callback = &Editor::region_drag_finished_callback;
3463
3464         start_grab (event);
3465
3466         double speed = 1.0;
3467         TimeAxisView* tvp = clicked_axisview;
3468         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
3469
3470         if (tv && tv->is_track()) {
3471                 speed = tv->get_diskstream()->speed();
3472         }
3473         
3474         drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
3475         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
3476         drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
3477         drag_info.dest_trackview = drag_info.source_trackview;
3478         drag_info.dest_layer = drag_info.source_layer;
3479         // we want a move threshold
3480         drag_info.want_move_threshold = true;
3481         drag_info.brushing = true;
3482         
3483         begin_reversible_command (_("Drag region brush"));
3484 }
3485
3486 void
3487 Editor::possibly_copy_regions_during_grab (GdkEvent* event)
3488 {
3489         if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
3490
3491                 drag_info.want_move_threshold = false; // don't copy again
3492
3493                 /* duplicate the regionview(s) and region(s) */
3494
3495                 vector<RegionView*> new_regionviews;
3496                 
3497                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3498                         RegionView* rv;
3499                         RegionView* nrv;
3500
3501                         rv = (*i);
3502                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
3503                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
3504                         
3505                         const boost::shared_ptr<const Region> original = rv->region();
3506                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
3507
3508                         if (arv) {
3509                                 boost::shared_ptr<AudioRegion> audioregion_copy
3510                                         = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
3511                                 nrv = new AudioRegionView (*arv, audioregion_copy);
3512                         } else if (mrv) {
3513                                 boost::shared_ptr<MidiRegion> midiregion_copy
3514                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
3515                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
3516                         } else {
3517                                 continue;
3518                         }
3519
3520                         nrv->get_canvas_group()->show ();
3521                         new_regionviews.push_back (nrv);
3522                 }
3523
3524                 if (new_regionviews.empty()) {
3525                         return;
3526                 }
3527
3528                 /* reset selection to new regionviews. This will not set selection visual status for 
3529                    these regionviews since they don't belong to a track, so do that by hand too.
3530                  */
3531
3532                 selection->set (new_regionviews);
3533
3534                 for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
3535                         (*i)->set_selected (true);
3536                 }
3537
3538                 /* reset drag_info data to reflect the fact that we are dragging the copies */
3539                 
3540                 drag_info.data = new_regionviews.front();
3541
3542                 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
3543                 /* 
3544                    sync the canvas to what we think is its current state 
3545                    without it, the canvas seems to 
3546                    "forget" to update properly after the upcoming reparent() 
3547                    ..only if the mouse is in rapid motion at the time of the grab. 
3548                    something to do with regionview creation raking so long?
3549                  */
3550                 track_canvas->update_now();
3551         }
3552 }
3553
3554 bool
3555 Editor::check_region_drag_possible (RouteTimeAxisView** tv, layer_t* layer)
3556 {
3557         /* Which trackview is this ? */
3558
3559         std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (drag_info.current_pointer_y);
3560         (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3561         (*layer) = tvp.second;
3562
3563         /* The region motion is only processed if the pointer is over
3564            an audio track.
3565         */
3566         
3567         if (!(*tv) || !(*tv)->is_track()) {
3568                 /* To make sure we hide the verbose canvas cursor when the mouse is 
3569                    not held over and audiotrack. 
3570                 */
3571                 hide_verbose_canvas_cursor ();
3572                 return false;
3573         }
3574
3575         return true;
3576 }
3577
3578 struct RegionSelectionByPosition {
3579     bool operator() (RegionView*a, RegionView* b) {
3580             return a->region()->position () < b->region()->position();
3581     }
3582 };
3583
3584 void
3585 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3586 {
3587         RouteTimeAxisView* tv;
3588         layer_t layer;
3589         
3590         if (!check_region_drag_possible (&tv, &layer)) {
3591                 return;
3592         }
3593
3594         if (!drag_info.move_threshold_passed) {
3595                 return;
3596         }
3597
3598         int dir;
3599
3600         if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
3601                 dir = 1;
3602         } else {
3603                 dir = -1;
3604         }
3605
3606         RegionSelection copy (selection->regions);
3607
3608         RegionSelectionByPosition cmp;
3609         copy.sort (cmp);
3610
3611         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
3612
3613                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
3614
3615                 if (!atv) {
3616                         continue;
3617                 }
3618
3619                 boost::shared_ptr<Playlist> playlist;
3620
3621                 if ((playlist = atv->playlist()) == 0) {
3622                         continue;
3623                 }
3624
3625                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
3626                         continue;
3627                 } 
3628
3629                 if (dir > 0) {
3630                         if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
3631                                 continue;
3632                         }
3633                 } else {
3634                         if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
3635                                 continue;
3636                         }
3637                 }
3638
3639                 
3640                 playlist->shuffle ((*i)->region(), dir);
3641
3642                 drag_info.grab_x = drag_info.current_pointer_x;
3643         }
3644 }
3645
3646 void
3647 Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
3648 {
3649 }
3650
3651 void
3652 Editor::visible_order_range (int* low, int* high) const
3653 {
3654         *low = TimeAxisView::max_order ();
3655         *high = 0;
3656         
3657         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
3658
3659                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3660                 
3661                 if (!rtv->hidden()) {
3662                         
3663                         if (*high < rtv->order()) {
3664                                 *high = rtv->order ();
3665                         }
3666                         
3667                         if (*low > rtv->order()) {
3668                                 *low = rtv->order ();
3669                         }
3670                 }
3671         }
3672 }
3673
3674 /** @param new_order New track order.
3675  *  @param old_order Old track order.
3676  *  @param visible_y_low Lowest visible order.
3677  *  @param visible_y_high Highest visible order.
3678  *  @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else.
3679  *  @param heigh_list Heights of tracks indexed by order.
3680  *  @return true if y movement should not happen, otherwise false.
3681  */
3682 bool
3683 Editor::y_movement_disallowed (
3684         int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high,
3685         bitset<512> const & tracks, vector<int32_t> const & height_list
3686         ) const
3687 {
3688         if (new_order != old_order) {
3689
3690                 /* this isn't the pointer track */      
3691
3692                 if (y_span > 0) {
3693
3694                         /* moving up the canvas */
3695                         if ( (new_order - y_span) >= visible_y_low) {
3696
3697                                 int32_t n = 0;
3698
3699                                 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
3700                                 int32_t visible_tracks = 0;
3701                                 while (visible_tracks < y_span ) {
3702                                         visible_tracks++;
3703                                         while (height_list[new_order - (visible_tracks - n)] == 0) {
3704                                                 /* passing through a hidden track */
3705                                                 n--;
3706                                         }                 
3707                                 }
3708                  
3709                                 if (tracks[new_order - (y_span - n)] != 0x00) {
3710                                         /* moving to a non-track; disallow */
3711                                         return true;
3712                                 }
3713                                 
3714
3715                         } else {
3716                                 /* moving beyond the lowest visible track; disallow */
3717                                 return true;
3718                         }                 
3719                   
3720                 } else if (y_span < 0) {
3721
3722                         /* moving down the canvas */
3723                         if ((new_order - y_span) <= visible_y_high) {
3724
3725                                 int32_t visible_tracks = 0;
3726                                 int32_t n = 0;
3727                                 while (visible_tracks > y_span ) {
3728                                         visible_tracks--;
3729                       
3730                                         while (height_list[new_order - (visible_tracks - n)] == 0) {
3731                                                 /* passing through a hidden track */
3732                                                 n++;
3733                                         }                
3734                                 }
3735                                                 
3736                                 if (tracks[new_order - (y_span - n)] != 0x00) {
3737                                         /* moving to a non-track; disallow */
3738                                         return true;
3739                                 }
3740
3741                                 
3742                         } else {
3743
3744                                 /* moving beyond the highest visible track; disallow */
3745                                 return true;
3746                         }
3747                 }               
3748                 
3749         } else {
3750                 
3751                 /* this is the pointer's track */
3752                 
3753                 if ((new_order - y_span) > visible_y_high) {
3754                         /* we will overflow */
3755                         return true;
3756                 } else if ((new_order - y_span) < visible_y_low) {
3757                         /* we will overflow */
3758                         return true;
3759                 }
3760         }
3761
3762         return false;
3763 }
3764
3765 void
3766 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
3767 {
3768         double x_delta;
3769         double y_delta = 0;
3770         nframes64_t pending_region_position = 0;
3771         int32_t pointer_order_span = 0, canvas_pointer_order_span = 0;
3772         int32_t pointer_layer_span = 0;
3773         
3774         bool clamp_y_axis = false;
3775         vector<int32_t>::iterator j;
3776
3777         possibly_copy_regions_during_grab (event);
3778
3779         /* *pointer* variables reflect things about the pointer; as we may be moving
3780            multiple regions, much detail must be computed per-region */
3781
3782         /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
3783            current_pointer_layer the current layer on that TimeAxisView */
3784         RouteTimeAxisView* current_pointer_view;
3785         layer_t current_pointer_layer;
3786         if (!check_region_drag_possible (&current_pointer_view, &current_pointer_layer)) {
3787                 return;
3788         }
3789
3790         /* TimeAxisView that we were pointing at last time we entered this method */
3791         TimeAxisView const * const last_pointer_view = drag_info.dest_trackview;
3792         /* the order of the track that we were pointing at last time we entered this method */
3793         int32_t const last_pointer_order = last_pointer_view->order ();
3794         /* the layer that we were pointing at last time we entered this method */
3795         layer_t const last_pointer_layer = drag_info.dest_layer;
3796
3797         /************************************************************
3798              Y DELTA COMPUTATION
3799         ************************************************************/   
3800
3801         /* Height of TimeAxisViews, indexed by order */
3802         /* XXX: hard-coded limit of TimeAxisViews */
3803         vector<int32_t> height_list (512);
3804         
3805         if (drag_info.brushing) {
3806                 clamp_y_axis = true;
3807                 pointer_order_span = 0;
3808                 goto y_axis_done;
3809         }
3810
3811         /* the change in track order between this callback and the last */
3812         pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
3813         /* the change in layer between this callback and the last;
3814            only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
3815         pointer_layer_span = last_pointer_layer - current_pointer_layer;
3816
3817         if (pointer_order_span != 0) {
3818
3819                 int32_t children = 0;
3820                 /* XXX: hard-coded limit of tracks */
3821                 bitset <512> tracks (0x00);
3822
3823                 int visible_y_high;
3824                 int visible_y_low;
3825                 visible_order_range (&visible_y_low, &visible_y_high);
3826                 
3827                 /* get a bitmask representing the visible tracks */
3828
3829                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3830                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3831                         TimeAxisView::Children children_list;
3832               
3833                         /* zeroes are audio/MIDI tracks. ones are other types. */
3834               
3835                         if (!rtv->hidden()) {
3836                                 
3837                                 if (!rtv->is_track()) {
3838                                         /* not an audio nor MIDI track */
3839                                         tracks = tracks |= (0x01 << rtv->order());
3840                                 }
3841         
3842                                 height_list[rtv->order()] = (*i)->current_height();
3843                                 children = 1;
3844
3845                                 if ((children_list = rtv->get_child_list()).size() > 0) {
3846                                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
3847                                                 tracks = tracks |= (0x01 << (rtv->order() + children));
3848                                                 height_list[rtv->order() + children] = (*j)->current_height();
3849                                                 children++;     
3850                                         }
3851                                 }
3852                         }
3853                 }
3854                 
3855                 /* find the actual pointer span, in terms of the number of visible tracks;
3856                    to do this, we reduce |pointer_order_span| by the number of hidden tracks
3857                    over the span */
3858
3859                 canvas_pointer_order_span = pointer_order_span;
3860                 if (last_pointer_view->order() >= current_pointer_view->order()) {
3861                         for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
3862                                 if (height_list[y] == 0) {
3863                                         canvas_pointer_order_span--;
3864                                 }
3865                         }
3866                 } else {
3867                         for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
3868                                 if (height_list[y] == 0) {
3869                                         canvas_pointer_order_span++;
3870                                 }
3871                         }
3872                 }
3873
3874                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3875                         
3876                         RegionView* rv = (*i);
3877
3878                         if (rv->region()->locked()) {
3879                                 continue;
3880                         }
3881
3882                         double ix1, ix2, iy1, iy2;
3883                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
3884                         rv->get_canvas_frame()->i2w (ix1, iy1);
3885                         iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
3886
3887                         /* get the new trackview for this particular region */
3888                         std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (iy1);
3889                         assert (tvp.first);
3890                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
3891
3892                         /* I know this method has a slightly excessive argument list, but I think
3893                            it's nice to separate the code out all the same, since it has such a
3894                            simple result, and it makes it clear that there are no other
3895                            side-effects.
3896                         */
3897
3898                         /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
3899                            as surely this is a per-region thing... */
3900                         
3901                         clamp_y_axis = y_movement_disallowed (
3902                                 rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high,
3903                                 tracks, height_list
3904                                 );
3905
3906                         if (clamp_y_axis) {
3907                                 break;
3908                         }
3909                 }
3910
3911         } else if (drag_info.dest_trackview == current_pointer_view) {
3912
3913                 if (current_pointer_layer == last_pointer_layer) {
3914                         /* No movement; clamp */
3915                         clamp_y_axis = true;
3916                 } 
3917         }
3918
3919   y_axis_done:
3920         if (!clamp_y_axis) {
3921                 drag_info.dest_trackview = current_pointer_view;
3922                 drag_info.dest_layer = current_pointer_layer;
3923         }
3924           
3925         /************************************************************
3926             X DELTA COMPUTATION
3927         ************************************************************/
3928
3929         /* compute the amount of pointer motion in frames, and where
3930            the region would be if we moved it by that much.
3931         */
3932         if ( drag_info.move_threshold_passed ) {
3933
3934                 if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
3935
3936                         nframes64_t sync_frame;
3937                         nframes64_t sync_offset;
3938                         int32_t sync_dir;
3939
3940                         pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
3941
3942                         sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
3943
3944                         /* we don't handle a sync point that lies before zero.
3945                          */
3946                         if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
3947                                 sync_frame = pending_region_position + (sync_dir*sync_offset);
3948
3949                                 /* we snap if the snap modifier is not enabled.
3950                                  */
3951             
3952                                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
3953                                         snap_to (sync_frame);   
3954                                 }
3955             
3956                                 pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
3957
3958                         } else {
3959                                 pending_region_position = drag_info.last_frame_position;
3960                         }
3961             
3962                 } else {
3963                         pending_region_position = 0;
3964                 }
3965           
3966                 if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
3967                         pending_region_position = drag_info.last_frame_position;
3968                 }
3969
3970                 bool x_move_allowed;
3971                 
3972                 if (Config->get_edit_mode() == Lock) {
3973                         if (drag_info.copy) {
3974                                 x_move_allowed = !drag_info.x_constrained;
3975                         } else {
3976                                 /* in locked edit mode, reverse the usual meaning of x_constrained */
3977                                 x_move_allowed = drag_info.x_constrained;
3978                         }
3979                 } else {
3980                         x_move_allowed = !drag_info.x_constrained;
3981                 }
3982
3983                 if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
3984
3985                         /* now compute the canvas unit distance we need to move the regionview
3986                            to make it appear at the new location.
3987                         */
3988
3989                         if (pending_region_position > drag_info.last_frame_position) {
3990                                 x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
3991                         } else {
3992                                 x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
3993                                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
3994
3995                                         RegionView* rv = (*i);
3996
3997                                         // If any regionview is at zero, we need to know so we can stop further leftward motion.
3998         
3999                                         double ix1, ix2, iy1, iy2;
4000                                         rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4001                                         rv->get_canvas_frame()->i2w (ix1, iy1);
4002
4003                                         if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
4004                                                 x_delta = 0;
4005                                                 pending_region_position = drag_info.last_frame_position;
4006                                                 break;
4007                                         }
4008                                 }
4009
4010                         }
4011                 
4012                         drag_info.last_frame_position = pending_region_position;
4013
4014                 } else {
4015                         x_delta = 0;
4016                 }
4017
4018         } else {
4019                 /* threshold not passed */
4020
4021                 x_delta = 0;
4022         }
4023         
4024         /*************************************************************
4025             PREPARE TO MOVE
4026         ************************************************************/
4027
4028         if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
4029                 /* haven't reached next snap point, and we're not switching
4030                    trackviews nor layers. nothing to do.
4031                 */
4032                 return;
4033         }
4034
4035         /*************************************************************
4036             MOTION                                                                    
4037         ************************************************************/
4038         bool do_move = true;
4039         if (drag_info.first_move) {
4040                 if (!drag_info.move_threshold_passed) {
4041                         do_move = false;
4042                 }
4043         }
4044
4045         if (do_move) {
4046
4047                 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
4048                 const list<RegionView*>& layered_regions = selection->regions.by_layer();
4049                 
4050                 for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
4051
4052                         RegionView* rv = (*i);
4053
4054                         if (rv->region()->locked()) {
4055                                 continue;
4056                         }
4057
4058                         /* here we are calculating the y distance from the
4059                            top of the first track view to the top of the region
4060                            area of the track view that we're working on */
4061
4062                         /* this x value is just a dummy value so that we have something
4063                            to pass to i2w () */
4064
4065                         double ix1 = 0;
4066
4067                         /* distance from the top of this track view to the region area
4068                            of our track view is always 1 */
4069                         
4070                         double iy1 = 1;
4071
4072                         /* convert to world coordinates, ie distance from the top of
4073                            the ruler section */
4074                         
4075                         rv->get_canvas_frame()->i2w (ix1, iy1);
4076
4077                         /* compensate for the ruler section and the vertical scrollbar position */
4078                         iy1 += get_trackview_group_vertical_offset ();
4079
4080                         if (drag_info.first_move) {
4081
4082                                 // hide any dependent views 
4083         
4084                                 rv->get_time_axis_view().hide_dependent_views (*rv);
4085
4086                                 /* 
4087                                    reparent to a non scrolling group so that we can keep the 
4088                                    region selection above all time axis views.
4089                                    reparenting means we have to move the rv as the two 
4090                                    parent groups have different coordinates.
4091                                 */
4092
4093                                 rv->get_canvas_group()->property_y() = iy1 - 1;
4094                                 rv->get_canvas_group()->reparent(*_region_motion_group);
4095
4096                                 rv->fake_set_opaque (true);
4097                         }
4098
4099                         /* current view for this particular region */
4100                         std::pair<TimeAxisView*, int> pos = trackview_by_y_position (iy1);
4101                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4102
4103                         if (pointer_order_span != 0 && !clamp_y_axis) {
4104
4105                                 /* INTER-TRACK MOVEMENT */
4106
4107                                 /* move through the height list to the track that the region is currently on */
4108                                 vector<int32_t>::iterator j = height_list.begin ();
4109                                 int32_t x = 0;
4110                                 while (j != height_list.end () && x != rtv->order ()) {
4111                                         ++x;
4112                                         ++j;
4113                                 }
4114
4115                                 y_delta = 0;
4116                                 int32_t temp_pointer_order_span = canvas_pointer_order_span;
4117
4118                                 if (j != height_list.end ()) {
4119
4120                                         /* Account for layers in the original and
4121                                            destination tracks.  If we're moving around in layers we assume
4122                                            that only one track is involved, so it's ok to use *pointer*
4123                                            variables here. */
4124
4125                                         StreamView* lv = last_pointer_view->view ();
4126                                         assert (lv);
4127
4128                                         /* move to the top of the last trackview */
4129                                         if (lv->layer_display () == Stacked) {
4130                                                 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
4131                                         }
4132                                         
4133                                         StreamView* cv = current_pointer_view->view ();
4134                                         assert (cv);
4135
4136                                         /* move to the right layer on the current trackview */
4137                                         if (cv->layer_display () == Stacked) {
4138                                                 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
4139                                         }
4140
4141                                         /* And for being on a non-topmost layer on the new
4142                                            track */
4143
4144                                         while (temp_pointer_order_span > 0) {
4145                                                 /* we're moving up canvas-wise,
4146                                                    so we need to find the next track height
4147                                                 */
4148                                                 if (j != height_list.begin()) {           
4149                                                         j--;
4150                                                 }
4151
4152                                                 if (x != last_pointer_order) {
4153                                                         if ((*j) == 0) {
4154                                                                 ++temp_pointer_order_span;
4155                                                         }
4156                                                 }
4157
4158                                                 y_delta -= (*j);
4159                                                 temp_pointer_order_span--;
4160                                         }
4161
4162                                         while (temp_pointer_order_span < 0) {
4163
4164                                                 y_delta += (*j);
4165
4166                                                 if (x != last_pointer_order) {
4167                                                         if ((*j) == 0) {
4168                                                                 --temp_pointer_order_span;
4169                                                         }
4170                                                 }
4171                                                 
4172                                                 if (j != height_list.end()) {                 
4173                                                         j++;
4174                                                 }
4175
4176                                                 temp_pointer_order_span++;
4177                                         }
4178
4179                                         
4180                                         /* find out where we'll be when we move and set height accordingly */
4181
4182                                         std::pair<TimeAxisView*, int> const pos = trackview_by_y_position (iy1 + y_delta);
4183                                         RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
4184                                         rv->set_height (temp_rtv->view()->child_height());
4185                                 
4186                                         /* if you un-comment the following, the region colours will follow
4187                                            the track colours whilst dragging; personally
4188                                            i think this can confuse things, but never mind.
4189                                         */
4190                                         
4191                                         //const GdkColor& col (temp_rtv->view->get_region_color());
4192                                         //rv->set_color (const_cast<GdkColor&>(col));
4193                                 }
4194                         }
4195
4196                         if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
4197
4198                                 /* INTER-LAYER MOVEMENT in the same track */
4199                                 y_delta = rtv->view()->child_height () * pointer_layer_span;
4200                         }
4201
4202
4203                         if (drag_info.brushing) {
4204                                 mouse_brush_insert_region (rv, pending_region_position);
4205                         } else {
4206                                 rv->move (x_delta, y_delta);
4207                         }
4208
4209                 } /* foreach region */
4210
4211         } /* if do_move */
4212
4213         if (drag_info.first_move && drag_info.move_threshold_passed) {
4214                 cursor_group->raise_to_top();
4215                 drag_info.first_move = false;
4216         }
4217
4218         if (x_delta != 0 && !drag_info.brushing) {
4219                 show_verbose_time_cursor (drag_info.last_frame_position, 10);
4220         }
4221
4222
4223 void
4224 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4225 {
4226         bool nocommit = true;
4227         vector<RegionView*> copies;
4228         RouteTimeAxisView* source_tv;
4229         boost::shared_ptr<Diskstream> ds;
4230         boost::shared_ptr<Playlist> from_playlist;
4231         vector<RegionView*> new_selection;
4232         typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
4233         PlaylistSet modified_playlists;
4234         PlaylistSet frozen_playlists;
4235         list <sigc::connection> modified_playlist_connections;
4236         pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
4237         nframes64_t drag_delta;
4238         bool changed_tracks, changed_position;
4239         std::pair<TimeAxisView*, int> tvp;
4240         std::map<RegionView*, RouteTimeAxisView*> final;
4241
4242         /* first_move is set to false if the regionview has been moved in the 
4243            motion handler. 
4244         */
4245
4246         if (drag_info.first_move) {
4247                 /* just a click */
4248                 goto out;
4249         }
4250
4251         nocommit = false;
4252
4253         if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
4254                 selection->set (pre_drag_region_selection);
4255                 pre_drag_region_selection.clear ();
4256         }
4257
4258         if (drag_info.brushing) {
4259                 /* all changes were made during motion event handlers */
4260                 
4261                 if (drag_info.copy) {
4262                         for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4263                                 copies.push_back (*i);
4264                         }
4265                 }
4266
4267                 goto out;
4268         }
4269
4270         char* op_string;
4271
4272         /* reverse this here so that we have the correct logic to finalize
4273            the drag.
4274         */
4275         
4276         if (Config->get_edit_mode() == Lock && !drag_info.copy) {
4277                 drag_info.x_constrained = !drag_info.x_constrained;
4278         }
4279
4280         if (drag_info.copy) {
4281                 if (drag_info.x_constrained) {
4282                         op_string = _("fixed time region copy");
4283                 } else {
4284                         op_string = _("region copy");
4285                 } 
4286         } else {
4287                 if (drag_info.x_constrained) {
4288                         op_string = _("fixed time region drag");
4289                 } else {
4290                         op_string = _("region drag");
4291                 }
4292         }
4293
4294         begin_reversible_command (op_string);
4295         changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
4296         tvp = trackview_by_y_position (drag_info.current_pointer_y);
4297         changed_tracks = (tvp.first != &clicked_regionview->get_time_axis_view());
4298
4299         drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
4300
4301         track_canvas->update_now ();
4302
4303         /* make a list of where each region ended up */
4304         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
4305
4306                 double ix1, ix2, iy1, iy2;
4307                 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
4308                 (*i)->get_canvas_frame()->i2w (ix1, iy1);
4309                 iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
4310
4311                 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (iy1);
4312                 final[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
4313         }
4314
4315         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
4316
4317                 RegionView* rv = (*i);
4318                 RouteTimeAxisView* dest_rtv = final[*i];
4319
4320                 nframes64_t where;
4321
4322                 if (rv->region()->locked()) {
4323                         ++i;
4324                         continue;
4325                 }
4326
4327                 if (changed_position && !drag_info.x_constrained) {
4328                         where = rv->region()->position() - drag_delta;
4329                 } else {
4330                         where = rv->region()->position();
4331                 }
4332                         
4333                 boost::shared_ptr<Region> new_region;
4334
4335                 if (drag_info.copy) {
4336                         /* we already made a copy */
4337                         new_region = rv->region();
4338
4339                         /* undo the previous hide_dependent_views so that xfades don't
4340                            disappear on copying regions 
4341                         */
4342                 
4343                         //rv->get_time_axis_view().reveal_dependent_views (*rv);
4344                 
4345                 } else if (changed_tracks && dest_rtv->playlist()) {
4346                         new_region = RegionFactory::create (rv->region());
4347                 }
4348
4349                 if (changed_tracks || drag_info.copy) {
4350
4351                         boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
4352                         if (!to_playlist) {
4353                                 ++i;
4354                                 continue;
4355                         }
4356
4357                         latest_regionviews.clear ();
4358
4359                         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4360                         
4361                         insert_result = modified_playlists.insert (to_playlist);
4362                         if (insert_result.second) {
4363                                 session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
4364                         }
4365
4366                         to_playlist->add_region (new_region, where);
4367
4368                         c.disconnect ();
4369                                                               
4370                         if (!latest_regionviews.empty()) {
4371                                 // XXX why just the first one ? we only expect one
4372                                 // commented out in nick_m's canvas reworking. is that intended?
4373                                 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
4374                                 new_selection.push_back (latest_regionviews.front());
4375                         }
4376
4377                 } else {
4378                         /* 
4379                            motion on the same track. plonk the previously reparented region 
4380                            back to its original canvas group (its streamview).
4381                            No need to do anything for copies as they are fake regions which will be deleted.
4382                         */
4383
4384                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
4385                         rv->get_canvas_group()->property_y() = 0;
4386                   
4387                         /* just change the model */
4388                         
4389                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
4390
4391                         insert_result = modified_playlists.insert (playlist);
4392                         if (insert_result.second) {
4393                                 session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
4394                         }
4395                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
4396                         frozen_insert_result = frozen_playlists.insert(playlist);
4397                         if (frozen_insert_result.second) {
4398                                 playlist->freeze();
4399                         }
4400
4401                         rv->region()->set_position (where, (void*) this);
4402                 }
4403
4404                 if (changed_tracks && !drag_info.copy) {
4405
4406                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
4407                            because we may have copied the region and it has not been attached to a playlist.
4408                         */
4409
4410                         assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
4411                         assert ((ds = source_tv->get_diskstream()));
4412                         assert ((from_playlist = ds->playlist()));
4413
4414                         /* moved to a different audio track, without copying */
4415
4416                         /* the region that used to be in the old playlist is not
4417                            moved to the new one - we use a copy of it. as a result,
4418                            any existing editor for the region should no longer be
4419                            visible.
4420                         */ 
4421             
4422                         rv->hide_region_editor();
4423                         rv->fake_set_opaque (false);
4424                         
4425                         /* remove the region from the old playlist */
4426
4427                         insert_result = modified_playlists.insert (from_playlist);
4428                         if (insert_result.second) {
4429                                 session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
4430                         }
4431
4432                         from_playlist->remove_region ((rv->region()));
4433                         
4434                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
4435                            was selected in all of them, then removing it from a playlist will have removed all
4436                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
4437                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
4438                            corresponding regionview, and the selection is now empty).
4439
4440                            this could have invalidated any and all iterators into the region selection.
4441
4442                            the heuristic we use here is: if the region selection is empty, break out of the loop
4443                            here. if the region selection is not empty, then restart the loop because we know that
4444                            we must have removed at least the region(view) we've just been working on as well as any
4445                            that we processed on previous iterations.
4446
4447                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
4448                            we can just iterate.
4449                         */
4450
4451                         if (selection->regions.empty()) {
4452                                 break;
4453                         } else { 
4454                                 i = selection->regions.by_layer().begin();
4455                         }
4456
4457                 } else {
4458                         ++i;
4459                 }
4460                 
4461                 if (drag_info.copy) {
4462                         copies.push_back (rv);
4463                 }
4464         }
4465         
4466         if (new_selection.empty()) {
4467                 if (drag_info.copy) {
4468                         /* the region(view)s that are selected and being dragged around
4469                            are copies and do not belong to any track. remove them
4470                            from the selection right here.
4471                         */
4472                         selection->clear_regions();
4473                 }
4474         } else {
4475                 /* this will clear any existing selection that would have been
4476                    cleared in the other clause above
4477                 */
4478                 selection->set (new_selection);
4479         }
4480
4481         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
4482                 (*p)->thaw();
4483         }
4484                         
4485   out:
4486         if (!nocommit) {
4487                 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
4488                         session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));      
4489                 }
4490                 commit_reversible_command ();
4491         }
4492
4493         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
4494                 delete *x;
4495         }
4496
4497 }
4498         
4499 void
4500 Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
4501 {
4502         if (drag_info.move_threshold_passed) {
4503                 if (drag_info.first_move) {
4504                         // TODO: create region-create-drag region view here
4505                         drag_info.first_move = false;
4506                 }
4507
4508                 // TODO: resize region-create-drag region view here
4509         }
4510
4511
4512 void
4513 Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
4514 {
4515         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
4516         if (!mtv)
4517                 return;
4518
4519         const boost::shared_ptr<MidiDiskstream> diskstream =
4520                 boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
4521         
4522         if (!diskstream) {
4523                 warning << "Cannot create non-MIDI region" << endl;
4524                 return;
4525         }
4526
4527         if (drag_info.first_move) {
4528                 begin_reversible_command (_("create region"));
4529                 XMLNode &before = mtv->playlist()->get_state();
4530
4531                 nframes64_t start = drag_info.grab_frame;
4532                 snap_to (start, -1);
4533                 const Meter& m = session->tempo_map().meter_at(start);
4534                 const Tempo& t = session->tempo_map().tempo_at(start);
4535                 double length = floor (m.frames_per_bar(t, session->frame_rate()));
4536
4537                 boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
4538                                 
4539                 mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
4540                                              (RegionFactory::create(src, 0, (nframes_t) length, 
4541                                                                     PBD::basename_nosuffix(src->name()))), start);
4542                 XMLNode &after = mtv->playlist()->get_state();
4543                 session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
4544                 commit_reversible_command();
4545
4546         } else {
4547                 create_region_drag_motion_callback (item, event);
4548                 // TODO: create region-create-drag region here
4549         }
4550 }
4551
4552 void
4553 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
4554 {
4555         /* Either add to or set the set the region selection, unless
4556            this is an alignment click (control used)
4557         */
4558         
4559         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
4560                 TimeAxisView* tv = &rv.get_time_axis_view();
4561                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
4562                 double speed = 1.0;
4563                 if (rtv && rtv->is_track()) {
4564                         speed = rtv->get_diskstream()->speed();
4565                 }
4566
4567                 nframes64_t where = get_preferred_edit_position();
4568
4569                 if (where >= 0) {
4570
4571                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
4572                                 
4573                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
4574                                 
4575                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
4576                                 
4577                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
4578                                 
4579                         } else {
4580                                 
4581                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
4582                         }
4583                 }
4584         }
4585 }
4586
4587 void
4588 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
4589 {
4590         char buf[128];
4591         SMPTE::Time smpte;
4592         BBT_Time bbt;
4593         int hours, mins;
4594         nframes64_t frame_rate;
4595         float secs;
4596
4597         if (session == 0) {
4598                 return;
4599         }
4600
4601         AudioClock::Mode m;
4602
4603         if (Profile->get_sae() || Profile->get_small_screen()) {
4604                 m = ARDOUR_UI::instance()->primary_clock.mode();
4605         } else {
4606                 m = ARDOUR_UI::instance()->secondary_clock.mode();
4607         }
4608
4609         switch (m) {
4610         case AudioClock::BBT:
4611                 session->bbt_time (frame, bbt);
4612                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
4613                 break;
4614                 
4615         case AudioClock::SMPTE:
4616                 session->smpte_time (frame, smpte);
4617                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4618                 break;
4619
4620         case AudioClock::MinSec:
4621                 /* XXX this is copied from show_verbose_duration_cursor() */
4622                 frame_rate = session->frame_rate();
4623                 hours = frame / (frame_rate * 3600);
4624                 frame = frame % (frame_rate * 3600);
4625                 mins = frame / (frame_rate * 60);
4626                 frame = frame % (frame_rate * 60);
4627                 secs = (float) frame / (float) frame_rate;
4628                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4629                 break;
4630
4631         default:
4632                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
4633                 break;
4634         }
4635
4636         if (xpos >= 0 && ypos >=0) {
4637                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4638         }
4639         else {
4640                 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);
4641         }
4642         show_verbose_canvas_cursor ();
4643 }
4644
4645 void
4646 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
4647 {
4648         char buf[128];
4649         SMPTE::Time smpte;
4650         BBT_Time sbbt;
4651         BBT_Time ebbt;
4652         int hours, mins;
4653         nframes64_t distance, frame_rate;
4654         float secs;
4655         Meter meter_at_start(session->tempo_map().meter_at(start));
4656
4657         if (session == 0) {
4658                 return;
4659         }
4660
4661         AudioClock::Mode m;
4662
4663         if (Profile->get_sae() || Profile->get_small_screen()) {
4664                 m = ARDOUR_UI::instance()->primary_clock.mode ();
4665         } else {
4666                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
4667         }
4668
4669         switch (m) {
4670         case AudioClock::BBT:
4671                 session->bbt_time (start, sbbt);
4672                 session->bbt_time (end, ebbt);
4673
4674                 /* subtract */
4675                 /* XXX this computation won't work well if the
4676                 user makes a selection that spans any meter changes.
4677                 */
4678
4679                 ebbt.bars -= sbbt.bars;
4680                 if (ebbt.beats >= sbbt.beats) {
4681                         ebbt.beats -= sbbt.beats;
4682                 } else {
4683                         ebbt.bars--;
4684                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
4685                 }
4686                 if (ebbt.ticks >= sbbt.ticks) {
4687                         ebbt.ticks -= sbbt.ticks;
4688                 } else {
4689                         ebbt.beats--;
4690                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
4691                 }
4692                 
4693                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
4694                 break;
4695                 
4696         case AudioClock::SMPTE:
4697                 session->smpte_duration (end - start, smpte);
4698                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
4699                 break;
4700
4701         case AudioClock::MinSec:
4702                 /* XXX this stuff should be elsewhere.. */
4703                 distance = end - start;
4704                 frame_rate = session->frame_rate();
4705                 hours = distance / (frame_rate * 3600);
4706                 distance = distance % (frame_rate * 3600);
4707                 mins = distance / (frame_rate * 60);
4708                 distance = distance % (frame_rate * 60);
4709                 secs = (float) distance / (float) frame_rate;
4710                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
4711                 break;
4712
4713         default:
4714                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
4715                 break;
4716         }
4717
4718         if (xpos >= 0 && ypos >=0) {
4719                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
4720         }
4721         else {
4722                 set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
4723         }
4724
4725         show_verbose_canvas_cursor ();
4726 }
4727
4728 void
4729 Editor::collect_new_region_view (RegionView* rv)
4730 {
4731         latest_regionviews.push_back (rv);
4732 }
4733
4734 void
4735 Editor::collect_and_select_new_region_view (RegionView* rv)
4736 {
4737         selection->add(rv);
4738         latest_regionviews.push_back (rv);
4739 }
4740
4741 void
4742 Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
4743 {
4744         if (clicked_regionview == 0) {
4745                 return;
4746         }
4747
4748         /* lets try to create new Region for the selection */
4749
4750         vector<boost::shared_ptr<Region> > new_regions;
4751         create_region_from_selection (new_regions);
4752
4753         if (new_regions.empty()) {
4754                 return;
4755         }
4756
4757         /* XXX fix me one day to use all new regions */
4758         
4759         boost::shared_ptr<Region> region (new_regions.front());
4760
4761         /* add it to the current stream/playlist.
4762
4763            tricky: the streamview for the track will add a new regionview. we will
4764            catch the signal it sends when it creates the regionview to
4765            set the regionview we want to then drag.
4766         */
4767         
4768         latest_regionviews.clear();
4769         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4770         
4771         /* A selection grab currently creates two undo/redo operations, one for 
4772            creating the new region and another for moving it.
4773         */
4774
4775         begin_reversible_command (_("selection grab"));
4776
4777         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
4778
4779         XMLNode *before = &(playlist->get_state());
4780         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
4781         XMLNode *after = &(playlist->get_state());
4782         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
4783
4784         commit_reversible_command ();
4785         
4786         c.disconnect ();
4787         
4788         if (latest_regionviews.empty()) {
4789                 /* something went wrong */
4790                 return;
4791         }
4792
4793         /* we need to deselect all other regionviews, and select this one
4794            i'm ignoring undo stuff, because the region creation will take care of it 
4795         */
4796         selection->set (latest_regionviews);
4797         
4798         drag_info.item = latest_regionviews.front()->get_canvas_group();
4799         drag_info.data = latest_regionviews.front();
4800         drag_info.motion_callback = &Editor::region_drag_motion_callback;
4801         drag_info.finished_callback = &Editor::region_drag_finished_callback;
4802
4803         start_grab (event);
4804         
4805         drag_info.source_trackview = clicked_routeview;
4806         drag_info.dest_trackview = drag_info.source_trackview;
4807         drag_info.last_frame_position = latest_regionviews.front()->region()->position();
4808         drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
4809         
4810         show_verbose_time_cursor (drag_info.last_frame_position, 10);
4811 }
4812
4813 void
4814 Editor::cancel_selection ()
4815 {
4816         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4817                 (*i)->hide_selection ();
4818         }
4819         selection->clear ();
4820         clicked_selection = 0;
4821 }       
4822
4823 void
4824 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
4825 {
4826         nframes64_t start = 0;
4827         nframes64_t end = 0;
4828
4829         if (session == 0) {
4830                 return;
4831         }
4832
4833         drag_info.item = item;
4834         drag_info.motion_callback = &Editor::drag_selection;
4835         drag_info.finished_callback = &Editor::end_selection_op;
4836
4837         selection_op = op;
4838
4839         switch (op) {
4840         case CreateSelection:
4841                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4842                         drag_info.copy = true;
4843                 } else {
4844                         drag_info.copy = false;
4845                 }
4846                 start_grab (event, selector_cursor);
4847                 break;
4848
4849         case SelectionStartTrim:
4850                 if (clicked_axisview) {
4851                         clicked_axisview->order_selection_trims (item, true);
4852                 } 
4853                 start_grab (event, trimmer_cursor);
4854                 start = selection->time[clicked_selection].start;
4855                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4856                 break;
4857                 
4858         case SelectionEndTrim:
4859                 if (clicked_axisview) {
4860                         clicked_axisview->order_selection_trims (item, false);
4861                 }
4862                 start_grab (event, trimmer_cursor);
4863                 end = selection->time[clicked_selection].end;
4864                 drag_info.pointer_frame_offset = drag_info.grab_frame - end;    
4865                 break;
4866
4867         case SelectionMove:
4868                 start = selection->time[clicked_selection].start;
4869                 start_grab (event);
4870                 drag_info.pointer_frame_offset = drag_info.grab_frame - start;  
4871                 break;
4872         }
4873
4874         if (selection_op == SelectionMove) {
4875                 show_verbose_time_cursor(start, 10);    
4876         } else {
4877                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
4878         }
4879 }
4880
4881 void
4882 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
4883 {
4884         nframes64_t start = 0;
4885         nframes64_t end = 0;
4886         nframes64_t length;
4887         nframes64_t pending_position;
4888
4889         if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
4890                 pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
4891         } else {
4892                 pending_position = 0;
4893         }
4894         
4895         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
4896                 snap_to (pending_position);
4897         }
4898
4899         /* only alter selection if the current frame is 
4900            different from the last frame position (adjusted)
4901          */
4902         
4903         if (pending_position == drag_info.last_pointer_frame) return;
4904         
4905         switch (selection_op) {
4906         case CreateSelection:
4907                 
4908                 if (drag_info.first_move) {
4909                         snap_to (drag_info.grab_frame);
4910                 }
4911                 
4912                 if (pending_position < drag_info.grab_frame) {
4913                         start = pending_position;
4914                         end = drag_info.grab_frame;
4915                 } else {
4916                         end = pending_position;
4917                         start = drag_info.grab_frame;
4918                 }
4919                 
4920                 /* first drag: Either add to the selection
4921                    or create a new selection->
4922                 */
4923                 
4924                 if (drag_info.first_move) {
4925                         
4926                         begin_reversible_command (_("range selection"));
4927                         
4928                         if (drag_info.copy) {
4929                                 /* adding to the selection */
4930                                 clicked_selection = selection->add (start, end);
4931                                 drag_info.copy = false;
4932                         } else {
4933                                 /* new selection-> */
4934                                 clicked_selection = selection->set (clicked_axisview, start, end);
4935                         }
4936                 } 
4937                 break;
4938                 
4939         case SelectionStartTrim:
4940                 
4941                 if (drag_info.first_move) {
4942                         begin_reversible_command (_("trim selection start"));
4943                 }
4944                 
4945                 start = selection->time[clicked_selection].start;
4946                 end = selection->time[clicked_selection].end;
4947
4948                 if (pending_position > end) {
4949                         start = end;
4950                 } else {
4951                         start = pending_position;
4952                 }
4953                 break;
4954                 
4955         case SelectionEndTrim:
4956                 
4957                 if (drag_info.first_move) {
4958                         begin_reversible_command (_("trim selection end"));
4959                 }
4960                 
4961                 start = selection->time[clicked_selection].start;
4962                 end = selection->time[clicked_selection].end;
4963
4964                 if (pending_position < start) {
4965                         end = start;
4966                 } else {
4967                         end = pending_position;
4968                 }
4969                 
4970                 break;
4971                 
4972         case SelectionMove:
4973                 
4974                 if (drag_info.first_move) {
4975                         begin_reversible_command (_("move selection"));
4976                 }
4977                 
4978                 start = selection->time[clicked_selection].start;
4979                 end = selection->time[clicked_selection].end;
4980                 
4981                 length = end - start;
4982                 
4983                 start = pending_position;
4984                 snap_to (start);
4985                 
4986                 end = start + length;
4987                 
4988                 break;
4989         }
4990         
4991         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
4992                 start_canvas_autoscroll (1, 0);
4993         }
4994
4995         if (start != end) {
4996                 selection->replace (clicked_selection, start, end);
4997         }
4998
4999         drag_info.last_pointer_frame = pending_position;
5000         drag_info.first_move = false;
5001
5002         if (selection_op == SelectionMove) {
5003                 show_verbose_time_cursor(start, 10);    
5004         } else {
5005                 show_verbose_time_cursor(pending_position, 10); 
5006         }
5007 }
5008
5009 void
5010 Editor::end_selection_op (ArdourCanvas::Item* item, GdkEvent* event)
5011 {
5012         if (!drag_info.first_move) {
5013                 drag_selection (item, event);
5014                 /* XXX this is not object-oriented programming at all. ick */
5015                 if (selection->time.consolidate()) {
5016                         selection->TimeChanged ();
5017                 }
5018                 commit_reversible_command ();
5019         } else {
5020                 /* just a click, no pointer movement.*/
5021
5022                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
5023
5024                         selection->clear_time();
5025
5026                 } 
5027         }
5028
5029         /* XXX what happens if its a music selection? */
5030         session->set_audio_range (selection->time);
5031         stop_canvas_autoscroll ();
5032 }
5033
5034 void
5035 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
5036 {
5037         double speed = 1.0;
5038         TimeAxisView* tvp = clicked_axisview;
5039         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5040
5041         if (tv && tv->is_track()) {
5042                 speed = tv->get_diskstream()->speed();
5043         }
5044         
5045         nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
5046         nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
5047         nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
5048
5049         //drag_info.item = clicked_regionview->get_name_highlight();
5050         drag_info.item = item;
5051         drag_info.motion_callback = &Editor::trim_motion_callback;
5052         drag_info.finished_callback = &Editor::trim_finished_callback;
5053
5054         start_grab (event, trimmer_cursor);
5055         
5056         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5057                 trim_op = ContentsTrim;
5058         } else {
5059                 /* These will get overridden for a point trim.*/
5060                 if (drag_info.current_pointer_frame < (region_start + region_length/2)) {
5061                         /* closer to start */
5062                         trim_op = StartTrim;
5063                 } else if (drag_info.current_pointer_frame > (region_end - region_length/2)) {
5064                         /* closer to end */
5065                         trim_op = EndTrim;
5066                 }
5067         }
5068
5069         switch (trim_op) {
5070         case StartTrim:
5071                 show_verbose_time_cursor(region_start, 10);     
5072                 break;
5073         case EndTrim:
5074                 show_verbose_time_cursor(region_end, 10);       
5075                 break;
5076         case ContentsTrim:
5077                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5078                 break;
5079         }
5080 }
5081
5082 void
5083 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
5084 {
5085         RegionView* rv = clicked_regionview;
5086         nframes64_t frame_delta = 0;
5087         bool left_direction;
5088         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
5089
5090         /* snap modifier works differently here..
5091            its' current state has to be passed to the 
5092            various trim functions in order to work properly 
5093         */ 
5094
5095         double speed = 1.0;
5096         TimeAxisView* tvp = clicked_axisview;
5097         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5098         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
5099
5100         if (tv && tv->is_track()) {
5101                 speed = tv->get_diskstream()->speed();
5102         }
5103         
5104         if (drag_info.last_pointer_frame > drag_info.current_pointer_frame) {
5105                 left_direction = true;
5106         } else {
5107                 left_direction = false;
5108         }
5109
5110         if (obey_snap) {
5111                 snap_to (drag_info.current_pointer_frame);
5112         }
5113
5114         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5115                 return;
5116         }
5117
5118         if (drag_info.first_move) {
5119         
5120                 string trim_type;
5121
5122                 switch (trim_op) {
5123                 case StartTrim:
5124                         trim_type = "Region start trim";
5125                         break;
5126                 case EndTrim:
5127                         trim_type = "Region end trim";
5128                         break;
5129                 case ContentsTrim:
5130                         trim_type = "Region content trim";
5131                         break;
5132                 }
5133
5134                 begin_reversible_command (trim_type);
5135
5136                 for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5137                         (*i)->fake_set_opaque(false);
5138                         (*i)->region()->freeze ();
5139                 
5140                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5141                         if (arv)
5142                                 arv->temporarily_hide_envelope ();
5143
5144                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5145                         insert_result = motion_frozen_playlists.insert (pl);
5146                         if (insert_result.second) {
5147                                 session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
5148                                 pl->freeze();
5149                         }
5150                 }
5151         }
5152
5153         if (left_direction) {
5154                 frame_delta = (drag_info.last_pointer_frame - drag_info.current_pointer_frame);
5155         } else {
5156                 frame_delta = (drag_info.current_pointer_frame - drag_info.last_pointer_frame);
5157         }
5158
5159         switch (trim_op) {              
5160         case StartTrim:
5161                 if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
5162                         break;
5163                 } else {
5164                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5165                                 single_start_trim (**i, frame_delta, left_direction, obey_snap);
5166                         }
5167                         break;
5168                 }
5169                 
5170         case EndTrim:
5171                 if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
5172                         break;
5173                 } else {
5174                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
5175                                 single_end_trim (**i, frame_delta, left_direction, obey_snap);
5176                         }
5177                         break;
5178                 }
5179                 
5180         case ContentsTrim:
5181                 {
5182                         bool swap_direction = false;
5183
5184                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
5185                                 swap_direction = true;
5186                         }
5187                         
5188                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5189                              i != selection->regions.by_layer().end(); ++i)
5190                         {
5191                                 single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
5192                         }
5193                 }
5194                 break;
5195         }
5196
5197         switch (trim_op) {
5198         case StartTrim:
5199                 show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);   
5200                 break;
5201         case EndTrim:
5202                 show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); 
5203                 break;
5204         case ContentsTrim:
5205                 show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5206                 break;
5207         }
5208
5209         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5210         drag_info.first_move = false;
5211 }
5212
5213 void
5214 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
5215 {
5216         boost::shared_ptr<Region> region (rv.region());
5217
5218         if (region->locked()) {
5219                 return;
5220         }
5221
5222         nframes64_t new_bound;
5223
5224         double speed = 1.0;
5225         TimeAxisView* tvp = clicked_axisview;
5226         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5227
5228         if (tv && tv->is_track()) {
5229                 speed = tv->get_diskstream()->speed();
5230         }
5231         
5232         if (left_direction) {
5233                 if (swap_direction) {
5234                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5235                 } else {
5236                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5237                 }
5238         } else {
5239                 if (swap_direction) {
5240                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5241                 } else {
5242                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5243                 }
5244         }
5245
5246         if (obey_snap) {
5247                 snap_to (new_bound);
5248         }
5249         region->trim_start ((nframes64_t) (new_bound * speed), this);   
5250         rv.region_changed (StartChanged);
5251 }
5252
5253 void
5254 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5255 {
5256         boost::shared_ptr<Region> region (rv.region()); 
5257
5258         if (region->locked()) {
5259                 return;
5260         }
5261
5262         nframes64_t new_bound;
5263
5264         double speed = 1.0;
5265         TimeAxisView* tvp = clicked_axisview;
5266         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5267
5268         if (tv && tv->is_track()) {
5269                 speed = tv->get_diskstream()->speed();
5270         }
5271         
5272         if (left_direction) {
5273                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
5274         } else {
5275                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
5276         }
5277
5278         if (obey_snap) {
5279                 snap_to (new_bound, (left_direction ? 0 : 1));  
5280         }
5281
5282         region->trim_front ((nframes64_t) (new_bound * speed), this);
5283
5284         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
5285 }
5286
5287 void
5288 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
5289 {
5290         boost::shared_ptr<Region> region (rv.region());
5291
5292         if (region->locked()) {
5293                 return;
5294         }
5295
5296         nframes64_t new_bound;
5297
5298         double speed = 1.0;
5299         TimeAxisView* tvp = clicked_axisview;
5300         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
5301
5302         if (tv && tv->is_track()) {
5303                 speed = tv->get_diskstream()->speed();
5304         }
5305         
5306         if (left_direction) {
5307                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
5308         } else {
5309                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
5310         }
5311
5312         if (obey_snap) {
5313                 snap_to (new_bound);
5314         }
5315         region->trim_end ((nframes64_t) (new_bound * speed), this);
5316         rv.region_changed (LengthChanged);
5317 }
5318         
5319 void
5320 Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
5321 {
5322         if (!drag_info.first_move) {
5323                 trim_motion_callback (item, event);
5324                 
5325                 if (!selection->selected (clicked_regionview)) {
5326                         thaw_region_after_trim (*clicked_regionview);           
5327                 } else {
5328                         
5329                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5330                              i != selection->regions.by_layer().end(); ++i)
5331                         {
5332                                 thaw_region_after_trim (**i);
5333                                 (*i)->fake_set_opaque (true);
5334                         }
5335                 }
5336                 
5337                 for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
5338                         (*p)->thaw ();
5339                         session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
5340                 }
5341                 
5342                 motion_frozen_playlists.clear ();
5343
5344                 commit_reversible_command();
5345         } else {
5346                 /* no mouse movement */
5347                 point_trim (event);
5348         }
5349 }
5350
5351 void
5352 Editor::point_trim (GdkEvent* event)
5353 {
5354         RegionView* rv = clicked_regionview;
5355         nframes64_t new_bound = drag_info.current_pointer_frame;
5356
5357         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5358                 snap_to (new_bound);
5359         }
5360
5361         /* Choose action dependant on which button was pressed */
5362         switch (event->button.button) {
5363         case 1:
5364                 trim_op = StartTrim;
5365                 begin_reversible_command (_("Start point trim"));
5366
5367                 if (selection->selected (rv)) {
5368
5369                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
5370                              i != selection->regions.by_layer().end(); ++i)
5371                         {
5372                                 if (!(*i)->region()->locked()) {
5373                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5374                                         XMLNode &before = pl->get_state();
5375                                         (*i)->region()->trim_front (new_bound, this);   
5376                                         XMLNode &after = pl->get_state();
5377                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5378                                 }
5379                         }
5380
5381                 } else {
5382
5383                         if (!rv->region()->locked()) {
5384                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5385                                 XMLNode &before = pl->get_state();
5386                                 rv->region()->trim_front (new_bound, this);     
5387                                 XMLNode &after = pl->get_state();
5388                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5389                         }
5390                 }
5391
5392                 commit_reversible_command();
5393         
5394                 break;
5395         case 2:
5396                 trim_op = EndTrim;
5397                 begin_reversible_command (_("End point trim"));
5398
5399                 if (selection->selected (rv)) {
5400                         
5401                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
5402                         {
5403                                 if (!(*i)->region()->locked()) {
5404                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5405                                         XMLNode &before = pl->get_state();
5406                                         (*i)->region()->trim_end (new_bound, this);
5407                                         XMLNode &after = pl->get_state();
5408                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5409                                 }
5410                         }
5411
5412                 } else {
5413
5414                         if (!rv->region()->locked()) {
5415                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
5416                                 XMLNode &before = pl->get_state();
5417                                 rv->region()->trim_end (new_bound, this);
5418                                 XMLNode &after = pl->get_state();
5419                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
5420                         }
5421                 }
5422
5423                 commit_reversible_command();
5424         
5425                 break;
5426         default:
5427                 break;
5428         }
5429 }
5430
5431 void
5432 Editor::thaw_region_after_trim (RegionView& rv)
5433 {
5434         boost::shared_ptr<Region> region (rv.region());
5435
5436         if (region->locked()) {
5437                 return;
5438         }
5439
5440         region->thaw (_("trimmed region"));
5441         XMLNode &after = region->playlist()->get_state();
5442         session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
5443
5444         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
5445         if (arv)
5446                 arv->unhide_envelope ();
5447 }
5448
5449 void
5450 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* event)
5451 {
5452         Marker* marker;
5453         bool is_start;
5454
5455         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
5456                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
5457                 /*NOTREACHED*/
5458         }
5459
5460         Location* location = find_location_from_marker (marker, is_start);      
5461         location->set_hidden (true, this);
5462 }
5463
5464
5465 void
5466 Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, RangeMarkerOp op)
5467 {
5468         if (session == 0) {
5469                 return;
5470         }
5471
5472         drag_info.item = item;
5473         drag_info.motion_callback = &Editor::drag_range_markerbar_op;
5474         drag_info.finished_callback = &Editor::end_range_markerbar_op;
5475
5476         range_marker_op = op;
5477
5478         if (!temp_location) {
5479                 temp_location = new Location;
5480         }
5481         
5482         switch (op) {
5483         case CreateRangeMarker:
5484         case CreateTransportMarker:
5485         case CreateCDMarker:
5486         
5487                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
5488                         drag_info.copy = true;
5489                 } else {
5490                         drag_info.copy = false;
5491                 }
5492                 start_grab (event, selector_cursor);
5493                 break;
5494         }
5495
5496         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5497         
5498 }
5499
5500 void
5501 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5502 {
5503         nframes64_t start = 0;
5504         nframes64_t end = 0;
5505         ArdourCanvas::SimpleRect *crect;
5506
5507         switch (range_marker_op) {
5508         case CreateRangeMarker:
5509                 crect = range_bar_drag_rect;
5510                 break;
5511         case CreateTransportMarker:
5512                 crect = transport_bar_drag_rect;
5513                 break;
5514         case CreateCDMarker:
5515                 crect = cd_marker_bar_drag_rect;
5516                 break;
5517         default:
5518                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
5519                 return;
5520                 break;
5521         }
5522         
5523         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5524                 snap_to (drag_info.current_pointer_frame);
5525         }
5526
5527         /* only alter selection if the current frame is 
5528            different from the last frame position.
5529          */
5530         
5531         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5532         
5533         switch (range_marker_op) {
5534         case CreateRangeMarker:
5535         case CreateTransportMarker:
5536         case CreateCDMarker:
5537                 if (drag_info.first_move) {
5538                         snap_to (drag_info.grab_frame);
5539                 }
5540                 
5541                 if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5542                         start = drag_info.current_pointer_frame;
5543                         end = drag_info.grab_frame;
5544                 } else {
5545                         end = drag_info.current_pointer_frame;
5546                         start = drag_info.grab_frame;
5547                 }
5548                 
5549                 /* first drag: Either add to the selection
5550                    or create a new selection.
5551                 */
5552                 
5553                 if (drag_info.first_move) {
5554                         
5555                         temp_location->set (start, end);
5556                         
5557                         crect->show ();
5558
5559                         update_marker_drag_item (temp_location);
5560                         range_marker_drag_rect->show();
5561                         //range_marker_drag_rect->raise_to_top();
5562                         
5563                 } 
5564                 break;          
5565         }
5566         
5567         if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
5568                 start_canvas_autoscroll (1, 0);
5569         }
5570         
5571         if (start != end) {
5572                 temp_location->set (start, end);
5573
5574                 double x1 = frame_to_pixel (start);
5575                 double x2 = frame_to_pixel (end);
5576                 crect->property_x1() = x1;
5577                 crect->property_x2() = x2;
5578
5579                 update_marker_drag_item (temp_location);
5580         }
5581
5582         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5583         drag_info.first_move = false;
5584
5585         show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
5586         
5587 }
5588
5589 void
5590 Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
5591 {
5592         Location * newloc = 0;
5593         string rangename;
5594         int flags;
5595         
5596         if (!drag_info.first_move) {
5597                 drag_range_markerbar_op (item, event);
5598
5599                 switch (range_marker_op) {
5600                 case CreateRangeMarker:
5601                 case CreateCDMarker:
5602                     {
5603                         begin_reversible_command (_("new range marker"));
5604                         XMLNode &before = session->locations()->get_state();
5605                         session->locations()->next_available_name(rangename,"unnamed");
5606                         if (range_marker_op == CreateCDMarker) {
5607                                 flags =  Location::IsRangeMarker|Location::IsCDMarker;
5608                                 cd_marker_bar_drag_rect->hide();
5609                         }
5610                         else {
5611                                 flags =  Location::IsRangeMarker;
5612                                 range_bar_drag_rect->hide();
5613                         }
5614                         newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
5615                         session->locations()->add (newloc, true);
5616                         XMLNode &after = session->locations()->get_state();
5617                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
5618                         commit_reversible_command ();
5619                         
5620                         range_marker_drag_rect->hide();
5621                         break;
5622                     }
5623
5624                 case CreateTransportMarker:
5625                         // popup menu to pick loop or punch
5626                         new_transport_marker_context_menu (&event->button, item);
5627                         
5628                         break;
5629                 }
5630         } else {
5631                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5632
5633                 if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
5634
5635                         nframes64_t start;
5636                         nframes64_t end;
5637
5638                         start = session->locations()->first_mark_before (drag_info.grab_frame);
5639                         end = session->locations()->first_mark_after (drag_info.grab_frame);
5640                         
5641                         if (end == max_frames) {
5642                                 end = session->current_end_frame ();
5643                         }
5644
5645                         if (start == 0) {
5646                                 start = session->current_start_frame ();
5647                         }
5648
5649                         switch (mouse_mode) {
5650                         case MouseObject:
5651                                 /* find the two markers on either side and then make the selection from it */
5652                                 select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
5653                                 break;
5654
5655                         case MouseRange:
5656                                 /* find the two markers on either side of the click and make the range out of it */
5657                                 selection->set (0, start, end);
5658                                 break;
5659
5660                         default:
5661                                 break;
5662                         }
5663                 } 
5664         }
5665
5666         stop_canvas_autoscroll ();
5667 }
5668
5669
5670
5671 void
5672 Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5673 {
5674         drag_info.item = item;
5675         drag_info.motion_callback = &Editor::drag_mouse_zoom;
5676         drag_info.finished_callback = &Editor::end_mouse_zoom;
5677
5678         start_grab (event, zoom_cursor);
5679
5680         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5681 }
5682
5683 void
5684 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5685 {
5686         nframes64_t start;
5687         nframes64_t end;
5688
5689         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5690                 snap_to (drag_info.current_pointer_frame);
5691                 
5692                 if (drag_info.first_move) {
5693                         snap_to (drag_info.grab_frame);
5694                 }
5695         }
5696                 
5697         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) return;
5698
5699         /* base start and end on initial click position */
5700         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5701                 start = drag_info.current_pointer_frame;
5702                 end = drag_info.grab_frame;
5703         } else {
5704                 end = drag_info.current_pointer_frame;
5705                 start = drag_info.grab_frame;
5706         }
5707         
5708         if (start != end) {
5709
5710                 if (drag_info.first_move) {
5711                         zoom_rect->show();
5712                         zoom_rect->raise_to_top();
5713                 }
5714
5715                 reposition_zoom_rect(start, end);
5716
5717                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5718                 drag_info.first_move = false;
5719
5720                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5721         }
5722 }
5723
5724 void
5725 Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
5726 {
5727         if (!drag_info.first_move) {
5728                 drag_mouse_zoom (item, event);
5729                 
5730                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5731                         temporal_zoom_by_frame (drag_info.grab_frame, drag_info.last_pointer_frame, "mouse zoom");
5732                 } else {
5733                         temporal_zoom_by_frame (drag_info.last_pointer_frame, drag_info.grab_frame, "mouse zoom");
5734                 }               
5735         } else {
5736                 temporal_zoom_to_frame (false, drag_info.grab_frame);
5737                 /*
5738                 temporal_zoom_step (false);
5739                 center_screen (drag_info.grab_frame);
5740                 */
5741         }
5742
5743         zoom_rect->hide();
5744 }
5745
5746 void
5747 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
5748 {
5749         double x1 = frame_to_pixel (start);
5750         double x2 = frame_to_pixel (end);
5751         double y2 = full_canvas_height - 1.0;
5752
5753         zoom_rect->property_x1() = x1;
5754         zoom_rect->property_y1() = 1.0;
5755         zoom_rect->property_x2() = x2;
5756         zoom_rect->property_y2() = y2;
5757 }
5758
5759 void
5760 Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5761 {
5762         drag_info.item = item;
5763         drag_info.motion_callback = &Editor::drag_rubberband_select;
5764         drag_info.finished_callback = &Editor::end_rubberband_select;
5765
5766         start_grab (event, cross_hair_cursor);
5767
5768         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5769 }
5770
5771 void
5772 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5773 {
5774         nframes64_t start;
5775         nframes64_t end;
5776         double y1;
5777         double y2;
5778
5779         /* use a bigger drag threshold than the default */
5780
5781         if (abs ((int) (drag_info.current_pointer_frame - drag_info.grab_frame)) < 8) {
5782                 return;
5783         }
5784
5785         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
5786                 if (drag_info.first_move) {
5787                         snap_to (drag_info.grab_frame);
5788                 } 
5789                 snap_to (drag_info.current_pointer_frame);
5790         }
5791
5792         /* base start and end on initial click position */
5793
5794         if (drag_info.current_pointer_frame < drag_info.grab_frame) {
5795                 start = drag_info.current_pointer_frame;
5796                 end = drag_info.grab_frame;
5797         } else {
5798                 end = drag_info.current_pointer_frame;
5799                 start = drag_info.grab_frame;
5800         }
5801
5802         if (drag_info.current_pointer_y < drag_info.grab_y) {
5803                 y1 = drag_info.current_pointer_y;
5804                 y2 = drag_info.grab_y;
5805         } else {
5806                 y2 = drag_info.current_pointer_y;
5807                 y1 = drag_info.grab_y;
5808         }
5809
5810         
5811         if (start != end || y1 != y2) {
5812
5813                 double x1 = frame_to_pixel (start);
5814                 double x2 = frame_to_pixel (end);
5815                 
5816                 rubberband_rect->property_x1() = x1;
5817                 rubberband_rect->property_y1() = y1;
5818                 rubberband_rect->property_x2() = x2;
5819                 rubberband_rect->property_y2() = y2;
5820
5821                 rubberband_rect->show();
5822                 rubberband_rect->raise_to_top();
5823                 
5824                 drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5825                 drag_info.first_move = false;
5826
5827                 show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5828         }
5829 }
5830
5831 void
5832 Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
5833 {
5834         if (!drag_info.first_move) {
5835
5836                 drag_rubberband_select (item, event);
5837
5838                 double y1,y2;
5839                 if (drag_info.current_pointer_y < drag_info.grab_y) {
5840                         y1 = drag_info.current_pointer_y;
5841                         y2 = drag_info.grab_y;
5842                 } else {
5843                         y2 = drag_info.current_pointer_y;
5844                         y1 = drag_info.grab_y;
5845                 }
5846
5847
5848                 Selection::Operation op = Keyboard::selection_type (event->button.state);
5849                 bool commit;
5850
5851                 begin_reversible_command (_("rubberband selection"));
5852
5853                 if (drag_info.grab_frame < drag_info.last_pointer_frame) {
5854                         commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
5855                 } else {
5856                         commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
5857                 }               
5858
5859                 if (commit) {
5860                         commit_reversible_command ();
5861                 }
5862                 
5863         } else {
5864                 if (!getenv("ARDOUR_SAE")) {
5865                         selection->clear_tracks();
5866                 }
5867                 selection->clear_regions();
5868                 selection->clear_points ();
5869                 selection->clear_lines ();
5870         }
5871
5872         rubberband_rect->hide();
5873 }
5874
5875
5876 gint
5877 Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
5878 {
5879         using namespace Gtkmm2ext;
5880
5881         ArdourPrompter prompter (false);
5882
5883         prompter.set_prompt (_("Name for region:"));
5884         prompter.set_initial_text (clicked_regionview->region()->name());
5885         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
5886         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
5887         prompter.show_all ();
5888         switch (prompter.run ()) {
5889         case Gtk::RESPONSE_ACCEPT:
5890                 string str;
5891                 prompter.get_result(str);
5892                 if (str.length()) {
5893                         clicked_regionview->region()->set_name (str);
5894                 }
5895                 break;
5896         }
5897         return true;
5898 }
5899
5900 void
5901 Editor::start_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5902 {
5903         drag_info.item = item;
5904         drag_info.motion_callback = &Editor::time_fx_motion;
5905         drag_info.finished_callback = &Editor::end_time_fx;
5906
5907         start_grab (event);
5908
5909         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5910 }
5911
5912 void
5913 Editor::time_fx_motion (ArdourCanvas::Item *item, GdkEvent* event)
5914 {
5915         RegionView* rv = clicked_regionview;
5916
5917         if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
5918                 snap_to (drag_info.current_pointer_frame);
5919         }
5920
5921         if (drag_info.current_pointer_frame == drag_info.last_pointer_frame) {
5922                 return;
5923         }
5924
5925         if (drag_info.current_pointer_frame > rv->region()->position()) {
5926                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), drag_info.current_pointer_frame);
5927         }
5928
5929         drag_info.last_pointer_frame = drag_info.current_pointer_frame;
5930         drag_info.first_move = false;
5931
5932         show_verbose_time_cursor (drag_info.current_pointer_frame, 10);
5933 }
5934
5935 void
5936 Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
5937 {
5938         clicked_regionview->get_time_axis_view().hide_timestretch ();
5939
5940         if (drag_info.first_move) {
5941                 return;
5942         }
5943
5944         if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
5945                 /* backwards drag of the left edge - not usable */
5946                 return;
5947         }
5948         
5949         nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
5950
5951         float percentage = (double) newlen / (double) clicked_regionview->region()->length();
5952         
5953 #ifndef USE_RUBBERBAND
5954         // Soundtouch uses percentage / 100 instead of normal (/ 1) 
5955         if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
5956                 percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
5957         }
5958 #endif  
5959         
5960         begin_reversible_command (_("timestretch"));
5961         
5962         // XXX how do timeFX on multiple regions ?
5963         
5964         RegionSelection rs;
5965         rs.add (clicked_regionview);
5966
5967         if (time_stretch (rs, percentage) == 0) {
5968                 session->commit_reversible_command ();
5969         }
5970 }
5971
5972 void
5973 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
5974 {
5975         /* no brushing without a useful snap setting */
5976
5977         switch (snap_mode) {
5978         case SnapMagnetic:
5979                 return; /* can't work because it allows region to be placed anywhere */
5980         default:
5981                 break; /* OK */
5982         }
5983
5984         switch (snap_type) {
5985         case SnapToMark:
5986                 return;
5987
5988         default:
5989                 break;
5990         }
5991
5992         /* don't brush a copy over the original */
5993         
5994         if (pos == rv->region()->position()) {
5995                 return;
5996         }
5997
5998         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
5999
6000         if (rtv == 0 || !rtv->is_track()) {
6001                 return;
6002         }
6003
6004         boost::shared_ptr<Playlist> playlist = rtv->playlist();
6005         double speed = rtv->get_diskstream()->speed();
6006         
6007         XMLNode &before = playlist->get_state();
6008         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
6009         XMLNode &after = playlist->get_state();
6010         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
6011         
6012         // playlist is frozen, so we have to update manually
6013         
6014         playlist->Modified(); /* EMIT SIGNAL */
6015 }
6016
6017 gint
6018 Editor::track_height_step_timeout ()
6019 {
6020         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
6021                 current_stepping_trackview = 0;
6022                 return false;
6023         }
6024         return true;
6025 }
6026