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