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