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