update_time_selection_display should work when in smart mode
[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 #include <bitset>
28
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
34
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
38
39 #include "canvas/canvas.h"
40
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
49
50 #include "ardour_ui.h"
51 #include "actions.h"
52 #include "editor.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
57 #include "marker.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
62 #include "prompter.h"
63 #include "selection.h"
64 #include "keyboard.h"
65 #include "editing.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
74 #include "note.h"
75
76 #include "i18n.h"
77
78 using namespace std;
79 using namespace ARDOUR;
80 using namespace PBD;
81 using namespace Gtk;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
84
85 bool
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
87 {
88         /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89            pays attentions to subwindows. this means that menu windows are ignored, and 
90            if the pointer is in a menu, the return window from the call will be the
91            the regular subwindow *under* the menu.
92
93            this matters quite a lot if the pointer is moving around in a menu that overlaps
94            the track canvas because we will believe that we are within the track canvas
95            when we are not. therefore, we track enter/leave events for the track canvas
96            and allow that to override the result of gdk_window_get_pointer().
97         */
98
99         if (!within_track_canvas) {
100                 return false;
101         }
102
103         int x, y;
104         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
105
106         if (!canvas_window) {
107                 return false;
108         }
109
110         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
111
112         if (!pointer_window) {
113                 return false;
114         }
115
116         if (pointer_window != canvas_window) {
117                 in_track_canvas = false;
118                 return false;
119         }
120
121         in_track_canvas = true;
122
123         GdkEvent event;
124         event.type = GDK_BUTTON_RELEASE;
125         event.button.x = x;
126         event.button.y = y;
127
128         where = window_event_sample (&event, 0, 0);
129
130         return true;
131 }
132
133 framepos_t
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
135 {
136         ArdourCanvas::Duple d;
137
138         if (!gdk_event_get_coords (event, &d.x, &d.y)) {
139                 return 0;
140         }
141
142         /* event coordinates are in window units, so convert to canvas
143          */
144
145         d = _track_canvas->window_to_canvas (d);
146
147         if (pcx) {
148                 *pcx = d.x;
149         }
150
151         if (pcy) {
152                 *pcy = d.y;
153         }
154
155         return pixel_to_sample (d.x);
156 }
157
158 framepos_t
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
160 {
161         double x;
162         double y;
163
164         /* event coordinates are already in canvas units */
165
166         if (!gdk_event_get_coords (event, &x, &y)) {
167                 cerr << "!NO c COORDS for event type " << event->type << endl;
168                 return 0;
169         }
170
171         if (pcx) {
172                 *pcx = x;
173         }
174
175         if (pcy) {
176                 *pcy = y;
177         }
178
179         /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180            position is negative (as can be the case with motion events in particular),
181            the frame location is always positive.
182         */
183
184         return pixel_to_sample_from_event (x);
185 }
186
187 void
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
189 {
190         boost::shared_ptr<Trimmable> st = _trimmable.lock();
191
192         if (!st || st == t) {
193                 _trimmable = t;
194         }
195 }
196
197 void
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
199 {
200         boost::shared_ptr<Movable> sm = _movable.lock();
201
202         if (!sm || sm != m) {
203                 _movable = m;
204         }
205 }
206
207 void
208 Editor::mouse_mode_object_range_toggled()
209 {
210         MouseMode m = mouse_mode;
211         
212         Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
213         assert (act);
214         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
215         assert (tact);
216
217         if (tact->get_active()) {
218                 m = MouseObject;  //Smart mode turned to ON, force editing to Object mode
219         }
220
221         set_mouse_mode(m, true);  //call this so the button styles can get updated
222 }
223
224 void
225 Editor::set_mouse_mode (MouseMode m, bool force)
226 {
227         if (_drags->active ()) {
228                 return;
229         }
230
231         if (!force && m == mouse_mode) {
232                 return;
233         }
234
235         if (ARDOUR::Profile->get_mixbus()) {
236                 if ( m == MouseCut) m = MouseObject;
237         }
238
239         Glib::RefPtr<Action> act;
240
241         switch (m) {
242         case MouseRange:
243                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
244                 break;
245
246         case MouseCut:
247                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
248                 break;
249
250         case MouseObject:
251                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
252                 break;
253
254         case MouseDraw:
255                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
256                 break;
257
258         case MouseTimeFX:
259                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
260                 break;
261
262         case MouseAudition:
263                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
264                 break;
265         }
266
267         assert (act);
268
269         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
270         assert (tact);
271
272         /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
273         tact->set_active (false);
274         tact->set_active (true);
275
276         //NOTE:  this will result in a call to mouse_mode_toggled which does the heavy lifting
277 }
278
279 void
280 Editor::mouse_mode_toggled (MouseMode m)
281 {
282         Glib::RefPtr<Action> act;
283         Glib::RefPtr<ToggleAction> tact;
284
285         if (ARDOUR::Profile->get_mixbus()) {
286                 if ( m == MouseCut)  m = MouseObject;
287         }
288
289         switch (m) {
290         case MouseRange:
291                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
292                 break;
293
294         case MouseObject:
295                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
296                 break;
297
298         case MouseCut:
299                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
300                 break;
301
302         case MouseDraw:
303                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
304                 break;
305
306         case MouseTimeFX:
307                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
308                 break;
309
310         case MouseAudition:
311                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
312                 break;
313         }
314
315         assert (act);
316
317         tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
318         assert (tact);
319
320         if (!tact->get_active()) {
321                 /* this was just the notification that the old mode has been
322                  * left. we'll get called again with the new mode active in a
323                  * jiffy.
324                  */
325                 return;
326         }
327
328         if (_session && mouse_mode == MouseAudition) {
329                 /* stop transport and reset default speed to avoid oddness with
330                    auditioning */
331                 _session->request_transport_speed (0.0, true);
332         }
333
334         mouse_mode = m;
335
336         instant_save ();
337
338         /* this should generate a new enter event which will
339            trigger the appropiate cursor.
340         */
341
342         if (_track_canvas) {
343                 _track_canvas->re_enter ();
344         }
345
346         set_gain_envelope_visibility ();
347         
348         update_time_selection_display ();
349
350         MouseModeChanged (); /* EMIT SIGNAL */
351 }
352
353 void
354 Editor::update_time_selection_display ()
355 {
356         switch (mouse_mode) {
357         case MouseRange:
358                 selection->clear_objects ();
359                 break;
360         default:
361                 selection->clear_time ();
362                 break;
363         }
364 }
365
366 void
367 Editor::step_mouse_mode (bool next)
368 {
369         const int n_mouse_modes = (int)MouseDraw + 1;
370         int       current       = (int)current_mouse_mode();
371         if (next) {
372                 set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
373         } else {
374                 set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
375         }
376 }
377
378 bool
379 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
380 {
381         if (_drags->active()) {
382                 _drags->end_grab (event);
383         } 
384
385         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
386
387         /* prevent reversion of edit cursor on button release */
388         
389         pre_press_cursor = 0;
390
391         return true;
392 }
393
394 void
395 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
396 {
397         /* in object/audition/timefx/gain-automation mode,
398            any button press sets the selection if the object
399            can be selected. this is a bit of hack, because
400            we want to avoid this if the mouse operation is a
401            region alignment.
402
403            note: not dbl-click or triple-click
404
405            Also note that there is no region selection in internal edit mode, otherwise
406            for operations operating on the selection (e.g. cut) it is not obvious whether
407            to cut notes or regions.
408         */
409
410         MouseMode eff_mouse_mode = effective_mouse_mode ();
411
412         if (eff_mouse_mode == MouseCut) {
413                 /* never change selection in cut mode */
414                 return;
415         }
416
417         if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
418                 /* context clicks are always about object properties, even if
419                    we're in range mode within smart mode.
420                 */
421                 eff_mouse_mode = MouseObject;
422         }
423
424         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
425         if (get_smart_mode()) {
426                 switch (item_type) {
427                   case FadeInHandleItem:
428                   case FadeInTrimHandleItem:
429                   case FadeOutHandleItem:
430                   case FadeOutTrimHandleItem:
431                           eff_mouse_mode = MouseObject;
432                           break;
433                 default:
434                         break;
435                 }
436         }
437
438         if (((mouse_mode != MouseObject) &&
439              (mouse_mode != MouseAudition || item_type != RegionItem) &&
440              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
441              (mouse_mode != MouseDraw)) ||
442             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
443             (internal_editing() && mouse_mode != MouseTimeFX)) {
444
445                 return;
446         }
447
448         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
449
450                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
451
452                         /* almost no selection action on modified button-2 or button-3 events */
453
454                         if (item_type != RegionItem && event->button.button != 2) {
455                                 return;
456                         }
457                 }
458         }
459
460         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
461         bool press = (event->type == GDK_BUTTON_PRESS);
462
463         switch (item_type) {
464         case RegionItem:
465                 if (press) {
466                         if (eff_mouse_mode != MouseRange) {
467                                 set_selected_regionview_from_click (press, op);
468                         } else {
469                                 /* don't change the selection unless the
470                                    clicked track is not currently selected. if
471                                    so, "collapse" the selection to just this
472                                    track
473                                 */
474                                 if (!selection->selected (clicked_axisview)) {
475                                         set_selected_track_as_side_effect (Selection::Set);
476                                 }
477                         }
478                 } else {
479                         if (eff_mouse_mode != MouseRange) {
480                                 set_selected_regionview_from_click (press, op);
481                         }
482                 }
483                 break;
484
485         case RegionViewNameHighlight:
486         case RegionViewName:
487         case LeftFrameHandle:
488         case RightFrameHandle:
489         case FadeInHandleItem:
490         case FadeInTrimHandleItem:
491         case FadeInItem:
492         case FadeOutHandleItem:
493         case FadeOutTrimHandleItem:
494         case FadeOutItem:
495         case StartCrossFadeItem:
496         case EndCrossFadeItem:
497                 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
498                         set_selected_regionview_from_click (press, op);
499                 } else if (event->type == GDK_BUTTON_PRESS) {
500                         set_selected_track_as_side_effect (op);
501                 }
502                 break;
503
504         case ControlPointItem:
505                 set_selected_track_as_side_effect (op);
506                 if (eff_mouse_mode != MouseRange) {
507                         set_selected_control_point_from_click (press, op);
508                 }
509                 break;
510
511         case StreamItem:
512                 /* for context click, select track */
513                 if (event->button.button == 3) {
514                         selection->clear_tracks ();
515                         set_selected_track_as_side_effect (op);
516                 }
517                 break;
518
519         case AutomationTrackItem:
520                 set_selected_track_as_side_effect (op);
521                 break;
522
523         default:
524                 break;
525         }
526 }
527
528 bool
529 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
530 {
531         /* single mouse clicks on any of these item types operate
532            independent of mouse mode, mostly because they are
533            not on the main track canvas or because we want
534            them to be modeless.
535         */
536
537         switch (item_type) {
538         case PlayheadCursorItem:
539                 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
540                 return true;
541
542         case MarkerItem:
543                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
544                         hide_marker (item, event);
545                 } else {
546                         _drags->set (new MarkerDrag (this, item), event);
547                 }
548                 return true;
549
550         case TempoMarkerItem:
551         {
552                 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
553                 assert (m);
554                 _drags->set (
555                         new TempoMarkerDrag (
556                                 this,
557                                 item,
558                                 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
559                                 ),
560                         event
561                         );
562                 return true;
563         }
564
565         case MeterMarkerItem:
566         {
567                 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
568                 assert (m);
569                 _drags->set (
570                         new MeterMarkerDrag (
571                                 this,
572                                 item,
573                                 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
574                                 ),
575                         event
576                         );
577                 return true;
578         }
579
580         case VideoBarItem:
581                 _drags->set (new VideoTimeLineDrag (this, item), event);
582                 return true;
583                 break;
584
585         case MarkerBarItem:
586         case TempoBarItem:
587         case MeterBarItem:
588         case TimecodeRulerItem:
589         case SamplesRulerItem:
590         case MinsecRulerItem:
591         case BBTRulerItem:
592                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
593                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
594                 }
595                 return true;
596                 break;
597
598
599         case RangeMarkerBarItem:
600                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
601                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
602                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
603                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
604                 } else {
605                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
606                 }
607                 return true;
608                 break;
609
610         case CdMarkerBarItem:
611                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
612                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
613                 } else {
614                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
615                 }
616                 return true;
617                 break;
618
619         case TransportMarkerBarItem:
620                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
621                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
622                 } else {
623                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
624                 }
625                 return true;
626                 break;
627
628         default:
629                 break;
630         }
631
632         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
633                 /* special case: allow trim of range selections in joined object mode;
634                    in theory eff should equal MouseRange in this case, but it doesn't
635                    because entering the range selection canvas item results in entered_regionview
636                    being set to 0, so update_join_object_range_location acts as if we aren't
637                    over a region.
638                 */
639                 if (item_type == StartSelectionTrimItem) {
640                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
641                 } else if (item_type == EndSelectionTrimItem) {
642                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
643                 }
644         }
645
646         Editing::MouseMode eff = effective_mouse_mode ();
647
648         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
649         if (get_smart_mode()) { 
650                 switch (item_type) {
651                   case FadeInHandleItem:
652                   case FadeInTrimHandleItem:
653                   case FadeOutHandleItem:
654                   case FadeOutTrimHandleItem:
655                         eff = MouseObject;
656                         break;
657                 default:
658                         break;
659                 }
660         }
661
662         /* there is no Range mode when in internal edit mode */
663         if (eff == MouseRange && internal_editing()) {
664                 eff = MouseObject;
665         }
666
667         switch (eff) {
668         case MouseRange:
669                 switch (item_type) {
670                 case StartSelectionTrimItem:
671                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
672                         break;
673
674                 case EndSelectionTrimItem:
675                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
676                         break;
677
678                 case SelectionItem:
679                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
680                                 start_selection_grab (item, event);
681                                 return true;
682                         } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
683                                 /* grab selection for moving */
684                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
685                         } else {
686                                 /* this was debated, but decided the more common action was to
687                                    make a new selection */
688                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
689                         }
690                         break;
691
692                 case StreamItem:
693                         if (internal_editing()) {
694                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
695                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
696                                         return true;
697                                 } 
698                         } else {
699                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
700                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
701                                 } else {
702                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
703                                 }
704                                 return true;
705                         }
706                         break;
707
708                 case RegionViewNameHighlight:
709                         if (!clicked_regionview->region()->locked()) {
710                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
711                                 return true;
712                         }
713                         break;
714
715                 default:
716                         if (!internal_editing()) {
717                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
718                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
719                                 } else {
720                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
721                                 }
722                         }
723                 }
724                 return true;
725                 break;
726
727         case MouseCut:
728                 switch (item_type) {
729                 case RegionItem:
730                 case FadeInHandleItem:
731                 case FadeOutHandleItem:
732                 case LeftFrameHandle:
733                 case RightFrameHandle:
734                 case FeatureLineItem:
735                 case RegionViewNameHighlight:
736                 case RegionViewName:
737                 case StreamItem:
738                 case AutomationTrackItem:
739                         _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
740                         return true;
741                         break;
742                 default:
743                         break;
744                 }
745                 break;
746
747         case MouseObject:
748                 switch (item_type) {
749                 case NoteItem:
750                         /* Existing note: allow trimming/motion */
751                         if (internal_editing()) {
752                                 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
753                                 assert (cn);
754                                 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
755                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
756                                 } else {
757                                         _drags->set (new NoteDrag (this, item), event);
758                                 }
759                                 return true;
760                         }
761                         break;
762
763                 default:
764                         break;
765                 }
766
767                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
768                     event->type == GDK_BUTTON_PRESS) {
769
770                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
771
772                 } else if (event->type == GDK_BUTTON_PRESS) {
773
774                         switch (item_type) {
775                         case FadeInHandleItem:
776                         {
777                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
778                                 return true;
779                         }
780
781                         case FadeOutHandleItem:
782                         {
783                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
784                                 return true;
785                         }
786
787                         case StartCrossFadeItem:
788                         case EndCrossFadeItem:
789                                 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.  for not this is not fully implemented */ 
790 //                              if (!clicked_regionview->region()->locked()) {
791 //                                      _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
792 //                                      return true;
793 //                              }
794                                 break;
795
796                         case FeatureLineItem:
797                         {
798                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
799                                         remove_transient(item);
800                                         return true;
801                                 }
802
803                                 _drags->set (new FeatureLineDrag (this, item), event);
804                                 return true;
805                                 break;
806                         }
807
808                         case RegionItem:
809                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
810                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
811                                            sort it out.
812                                         */
813                                         break;
814                                 }
815
816                                 if (internal_editing ()) {
817                                         break;
818                                 }
819
820                                 /* click on a normal region view */
821                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
822                                         add_region_copy_drag (item, event, clicked_regionview);
823                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
824                                         add_region_brush_drag (item, event, clicked_regionview);
825                                 } else {
826                                         add_region_drag (item, event, clicked_regionview);
827                                 }
828
829
830 //                              if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
831 //                                      _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
832 //                              }
833
834                                 _drags->start_grab (event);
835                                 return true;
836                                 break;
837
838                         case RegionViewNameHighlight:
839                         case LeftFrameHandle:
840                         case RightFrameHandle:
841                                 if (!clicked_regionview->region()->locked()) {
842                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
843                                         return true;
844                                 }
845                                 break;
846
847                         case FadeInTrimHandleItem:
848                         case FadeOutTrimHandleItem:
849                                 if (!clicked_regionview->region()->locked()) {
850                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
851                                         return true;
852                                 }
853                                 break;
854
855                         case RegionViewName:
856                         {
857                                 /* rename happens on edit clicks */
858                                 if (clicked_regionview->get_name_highlight()) {
859                                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
860                                         return true;
861                                 }
862                                 break;
863                         }
864
865                         case ControlPointItem:
866                                 _drags->set (new ControlPointDrag (this, item), event);
867                                 return true;
868                                 break;
869
870                         case AutomationLineItem:
871                                 _drags->set (new LineDrag (this, item), event);
872                                 return true;
873                                 break;
874
875                         case StreamItem:
876                                 if (internal_editing()) {
877                                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
878                                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
879                                         }
880                                         return true;
881                                 } else {
882                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
883                                 }
884                                 break;
885
886                         case AutomationTrackItem:
887                         {
888                                 TimeAxisView* parent = clicked_axisview->get_parent ();
889                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
890                                 assert (atv);
891                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
892
893                                         RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
894                                         assert (p);
895                                         boost::shared_ptr<Playlist> pl = p->track()->playlist ();
896                                         if (pl->n_regions() == 0) {
897                                                 /* Parent has no regions; create one so that we have somewhere to put automation */
898                                                 _drags->set (new RegionCreateDrag (this, item, parent), event);
899                                         } else {
900                                                 /* See if there's a region before the click that we can extend, and extend it if so */
901                                                 framepos_t const t = canvas_event_sample (event);
902                                                 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
903                                                 if (!prev) {
904                                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
905                                                 } else {
906                                                         prev->set_length (t - prev->position ());
907                                                 }
908                                         }
909                                 } else {
910                                         /* rubberband drag to select automation points */
911                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
912                                 }
913                                 break;
914                         }
915
916                         case SelectionItem:
917                         {
918                                 break;
919                         }
920
921                         case MarkerBarItem:
922
923                                 break;
924
925                         default:
926                                 break;
927                         }
928                 }
929                 return true;
930                 break;
931
932         case MouseDraw:
933                 switch (item_type) {
934                 case GainLineItem:
935                         _drags->set (new LineDrag (this, item), event);
936                         return true;
937
938                 case ControlPointItem:
939                         _drags->set (new ControlPointDrag (this, item), event);
940                         return true;
941                         break;
942
943                 case SelectionItem:
944                 {
945                         AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
946                         if (arv) {
947                                 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
948                         } else {
949                                 double const y = event->button.y;
950                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
951                                 if (tvp.first) {
952                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
953                                         if ( atv) {
954                                                 /* smart "join" mode: drag automation */
955                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
956                                         }
957                                 }
958                         }
959                         return true;
960                         break;
961                 }
962
963                 case AutomationLineItem:
964                         _drags->set (new LineDrag (this, item), event);
965                         break;
966
967                 case NoteItem:
968                         /* Existing note: allow trimming/motion */
969                         if (internal_editing()) {
970                                 /* trim notes if we're in internal edit mode and near the ends of the note */
971                                 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
972                                 assert (cn);
973                                 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
974                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
975                                 } else {
976                                         _drags->set (new NoteDrag (this, item), event);
977                                 }
978                                 return true;
979                         }
980                         break;
981
982                 case StreamItem:
983                         if (internal_editing()) {
984                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
985                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
986                                 }
987                                 return true;
988                         }
989                         break;
990
991                 default:
992                         break;
993                 }
994                 return true;
995                 break;
996
997         case MouseTimeFX:
998                 if (internal_editing() && item_type == NoteItem ) {
999                         /* drag notes if we're in internal edit mode */
1000                         NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1001                         assert (cn);
1002                         if (cn->big_enough_to_trim()) {
1003                                 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1004                         }
1005                         return true;
1006                 } else if (clicked_regionview) {
1007                         /* do time-FX  */
1008                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1009                         return true;
1010                 }
1011                 break;
1012
1013         case MouseAudition:
1014                 _drags->set (new ScrubDrag (this, item), event);
1015                 scrub_reversals = 0;
1016                 scrub_reverse_distance = 0;
1017                 last_scrub_x = event->button.x;
1018                 scrubbing_direction = 0;
1019                 push_canvas_cursor (_cursors->transparent);
1020                 return true;
1021                 break;
1022
1023         default:
1024                 break;
1025         }
1026
1027         return false;
1028 }
1029
1030 bool
1031 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1032 {
1033         Editing::MouseMode const eff = effective_mouse_mode ();
1034         switch (eff) {
1035         case MouseObject:
1036                 switch (item_type) {
1037                 case RegionItem:
1038                         if (internal_editing ()) {
1039                                 /* no region drags in internal edit mode */
1040                                 return false;
1041                         }
1042
1043                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1044                                 add_region_copy_drag (item, event, clicked_regionview);
1045                         } else {
1046                                 add_region_drag (item, event, clicked_regionview);
1047                         }
1048                         _drags->start_grab (event);
1049                         return true;
1050                         break;
1051                 case ControlPointItem:
1052                         _drags->set (new ControlPointDrag (this, item), event);
1053                         return true;
1054                         break;
1055
1056                 default:
1057                         break;
1058                 }
1059
1060                 switch (item_type) {
1061                 case RegionViewNameHighlight:
1062                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1063                         return true;
1064                         break;
1065
1066                 case LeftFrameHandle:
1067                 case RightFrameHandle:
1068                         if (!internal_editing ()) {
1069                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1070                         }
1071                         return true;
1072                         break;
1073
1074                 case RegionViewName:
1075                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1076                         return true;
1077                         break;
1078
1079                 default:
1080                         break;
1081                 }
1082
1083                 break;
1084
1085         case MouseDraw:
1086                 return false;
1087
1088         case MouseRange:
1089                 /* relax till release */
1090                 return true;
1091                 break;
1092
1093         default:
1094                 break;
1095         }
1096
1097         return false;
1098 }
1099    
1100 bool
1101 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1102 {
1103         if (event->type == GDK_2BUTTON_PRESS) {
1104                 _drags->mark_double_click ();
1105                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1106                 return true;
1107         }
1108
1109         if (event->type != GDK_BUTTON_PRESS) {
1110                 return false;
1111         }
1112
1113         pre_press_cursor = current_canvas_cursor;
1114
1115         _track_canvas->grab_focus();
1116
1117         if (_session && _session->actively_recording()) {
1118                 return true;
1119         }
1120
1121         if (internal_editing()) {
1122                 bool leave_internal_edit_mode = false;
1123
1124                 switch (item_type) {
1125                 case NoteItem:
1126                         break;
1127
1128                 case RegionItem:
1129                         if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1130                                 leave_internal_edit_mode = true;
1131                         }
1132                         break;
1133
1134                 case PlayheadCursorItem:
1135                 case MarkerItem:
1136                 case TempoMarkerItem:
1137                 case MeterMarkerItem:
1138                 case MarkerBarItem:
1139                 case TempoBarItem:
1140                 case MeterBarItem:
1141                 case RangeMarkerBarItem:
1142                 case CdMarkerBarItem:
1143                 case TransportMarkerBarItem:
1144                 case StreamItem:
1145                 case TimecodeRulerItem:
1146                 case SamplesRulerItem:
1147                 case MinsecRulerItem:
1148                 case BBTRulerItem:
1149                         /* button press on these items never does anything to
1150                            change the editing mode.
1151                         */
1152                         break;
1153
1154                 default:
1155                         break;
1156                 }
1157                 
1158                 if (leave_internal_edit_mode) {
1159                         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1160                 }
1161         }
1162
1163         button_selection (item, event, item_type);
1164
1165         if (!_drags->active () &&
1166             (Keyboard::is_delete_event (&event->button) ||
1167              Keyboard::is_context_menu_event (&event->button) ||
1168              Keyboard::is_edit_event (&event->button))) {
1169
1170                 /* handled by button release */
1171                 return true;
1172         }
1173
1174         //not rolling, range mode click + join_play_range :  locate the PH here
1175         if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1176                 framepos_t where = canvas_event_sample (event);
1177                 snap_to(where);
1178                 _session->request_locate (where, false);
1179         }
1180
1181         switch (event->button.button) {
1182         case 1:
1183                 return button_press_handler_1 (item, event, item_type);
1184                 break;
1185
1186         case 2:
1187                 return button_press_handler_2 (item, event, item_type);
1188                 break;
1189
1190         case 3:
1191                 break;
1192
1193         default:
1194                 return button_press_dispatch (&event->button);
1195                 break;
1196
1197         }
1198
1199         return false;
1200 }
1201
1202 bool
1203 Editor::button_press_dispatch (GdkEventButton* ev)
1204 {
1205         /* this function is intended only for buttons 4 and above.
1206          */
1207
1208         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1209         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1210 }
1211
1212 bool
1213 Editor::button_release_dispatch (GdkEventButton* ev)
1214 {
1215         /* this function is intended only for buttons 4 and above.
1216          */
1217
1218         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1219         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1220 }
1221
1222 bool
1223 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1224 {
1225         framepos_t where = canvas_event_sample (event);
1226         AutomationTimeAxisView* atv = 0;
1227
1228         if (pre_press_cursor) {
1229                 set_canvas_cursor (pre_press_cursor);
1230                 pre_press_cursor = 0;
1231         }
1232
1233         /* no action if we're recording */
1234
1235         if (_session && _session->actively_recording()) {
1236                 return true;
1237         }
1238
1239         /* see if we're finishing a drag */
1240
1241         bool were_dragging = false;
1242         if (_drags->active ()) {
1243                 bool const r = _drags->end_grab (event);
1244                 if (r) {
1245                         /* grab dragged, so do nothing else */
1246                         return true;
1247                 }
1248
1249                 were_dragging = true;
1250         }
1251
1252         update_region_layering_order_editor ();
1253
1254         /* edit events get handled here */
1255
1256         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1257                 switch (item_type) {
1258                 case RegionItem:
1259                         show_region_properties ();
1260                         break;
1261
1262                 case TempoMarkerItem: {
1263                         Marker* marker;
1264                         TempoMarker* tempo_marker;
1265                         
1266                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1267                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1268                                 abort(); /*NOTREACHED*/
1269                         }
1270                         
1271                         if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1272                                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1273                                 abort(); /*NOTREACHED*/
1274                         }
1275                         
1276                         edit_tempo_marker (*tempo_marker);
1277                         break;
1278                 }
1279
1280                 case MeterMarkerItem: {
1281                         Marker* marker;
1282                         MeterMarker* meter_marker;
1283                         
1284                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1285                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1286                                 abort(); /*NOTREACHED*/
1287                         }
1288                         
1289                         if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1290                                 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1291                                 abort(); /*NOTREACHED*/
1292                         }
1293                         edit_meter_marker (*meter_marker);
1294                         break;
1295                 }
1296
1297                 case RegionViewName:
1298                         if (clicked_regionview->name_active()) {
1299                                 return mouse_rename_region (item, event);
1300                         }
1301                         break;
1302
1303                 case ControlPointItem:
1304                         edit_control_point (item);
1305                         break;
1306
1307                 default:
1308                         break;
1309                 }
1310                 return true;
1311         }
1312
1313         /* context menu events get handled here */
1314         if (Keyboard::is_context_menu_event (&event->button)) {
1315
1316                 context_click_event = *event;
1317
1318                 if (!_drags->active ()) {
1319
1320                         /* no matter which button pops up the context menu, tell the menu
1321                            widget to use button 1 to drive menu selection.
1322                         */
1323
1324                         switch (item_type) {
1325                         case FadeInItem:
1326                         case FadeInHandleItem:
1327                         case FadeInTrimHandleItem:
1328                         case StartCrossFadeItem:
1329                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1330                                 break;
1331
1332                         case FadeOutItem:
1333                         case FadeOutHandleItem:
1334                         case FadeOutTrimHandleItem:
1335                         case EndCrossFadeItem:
1336                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1337                                 break;
1338
1339                         case LeftFrameHandle:
1340                         case RightFrameHandle:
1341                                 break;
1342
1343                         case StreamItem:
1344                                 popup_track_context_menu (1, event->button.time, item_type, false);
1345                                 break;
1346
1347                         case RegionItem:
1348                         case RegionViewNameHighlight:
1349                         case RegionViewName:
1350                                 popup_track_context_menu (1, event->button.time, item_type, false);
1351                                 break;
1352
1353                         case SelectionItem:
1354                                 popup_track_context_menu (1, event->button.time, item_type, true);
1355                                 break;
1356                                 
1357                         case AutomationTrackItem:
1358                                 popup_track_context_menu (1, event->button.time, item_type, false);
1359                                 break;
1360
1361                         case MarkerBarItem:
1362                         case RangeMarkerBarItem:
1363                         case TransportMarkerBarItem:
1364                         case CdMarkerBarItem:
1365                         case TempoBarItem:
1366                         case MeterBarItem:
1367                         case VideoBarItem:
1368                         case TimecodeRulerItem:
1369                         case SamplesRulerItem:
1370                         case MinsecRulerItem:
1371                         case BBTRulerItem:
1372                                 popup_ruler_menu (where, item_type);
1373                                 break;
1374
1375                         case MarkerItem:
1376                                 marker_context_menu (&event->button, item);
1377                                 break;
1378
1379                         case TempoMarkerItem:
1380                                 tempo_or_meter_marker_context_menu (&event->button, item);
1381                                 break;
1382
1383                         case MeterMarkerItem:
1384                                 tempo_or_meter_marker_context_menu (&event->button, item);
1385                                 break;
1386
1387                         case CrossfadeViewItem:
1388                                 popup_track_context_menu (1, event->button.time, item_type, false);
1389                                 break;
1390
1391                         case ControlPointItem:
1392                                 popup_control_point_context_menu (item, event);
1393                                 break;
1394
1395                         default:
1396                                 break;
1397                         }
1398
1399                         return true;
1400                 }
1401         }
1402
1403         /* delete events get handled here */
1404
1405         Editing::MouseMode const eff = effective_mouse_mode ();
1406
1407         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1408
1409                 switch (item_type) {
1410                 case TempoMarkerItem:
1411                         remove_tempo_marker (item);
1412                         break;
1413
1414                 case MeterMarkerItem:
1415                         remove_meter_marker (item);
1416                         break;
1417
1418                 case MarkerItem:
1419                         remove_marker (*item, event);
1420                         break;
1421
1422                 case RegionItem:
1423                         if (eff == MouseObject) {
1424                                 remove_clicked_region ();
1425                         }
1426                         break;
1427
1428                 case ControlPointItem:
1429                         remove_control_point (item);
1430                         break;
1431
1432                 case NoteItem:
1433                         remove_midi_note (item, event);
1434                         break;
1435
1436                 default:
1437                         break;
1438                 }
1439                 return true;
1440         }
1441
1442         switch (event->button.button) {
1443         case 1:
1444
1445                 switch (item_type) {
1446                 /* see comments in button_press_handler */
1447                 case PlayheadCursorItem:
1448                 case MarkerItem:
1449                 case GainLineItem:
1450                 case AutomationLineItem:
1451                 case StartSelectionTrimItem:
1452                 case EndSelectionTrimItem:
1453                         return true;
1454
1455                 case MarkerBarItem:
1456                         if (!_dragging_playhead) {
1457                                 snap_to_with_modifier (where, event, RoundNearest, true);
1458                                 mouse_add_new_marker (where);
1459                         }
1460                         return true;
1461
1462                 case CdMarkerBarItem:
1463                         if (!_dragging_playhead) {
1464                                 // if we get here then a dragged range wasn't done
1465                                 snap_to_with_modifier (where, event, RoundNearest, true);
1466                                 mouse_add_new_marker (where, true);
1467                         }
1468                         return true;
1469
1470                 case TempoBarItem:
1471                         if (!_dragging_playhead) {
1472                                 snap_to_with_modifier (where, event);
1473                                 mouse_add_new_tempo_event (where);
1474                         }
1475                         return true;
1476
1477                 case MeterBarItem:
1478                         if (!_dragging_playhead) {
1479                                 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1480                         }
1481                         return true;
1482                         break;
1483
1484                 case TimecodeRulerItem:
1485                 case SamplesRulerItem:
1486                 case MinsecRulerItem:
1487                 case BBTRulerItem:
1488                         return true;
1489                         break;
1490
1491                 default:
1492                         break;
1493                 }
1494
1495                 switch (eff) {
1496                 case MouseDraw:
1497                         switch (item_type) {
1498                         case RegionItem:
1499                         {
1500                                 /* check that we didn't drag before releasing, since
1501                                    its really annoying to create new control
1502                                    points when doing this.
1503                                 */
1504                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1505                                 if (!were_dragging && arv) {
1506                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1507                                         arv->add_gain_point_event (item, event, with_guard_points);
1508                                 }
1509                                 return true;
1510                                 break;
1511                         }
1512
1513                         case AutomationTrackItem: {
1514                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1515                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1516                                 if (atv) {
1517                                         atv->add_automation_event (event, where, event->button.y, with_guard_points);
1518                                 }
1519                                 return true;
1520                                 break;
1521                         }
1522                         default:
1523                                 break;
1524                         }
1525                         break;
1526
1527                 case MouseAudition:
1528                         pop_canvas_cursor ();
1529                         if (scrubbing_direction == 0) {
1530                                 /* no drag, just a click */
1531                                 switch (item_type) {
1532                                 case RegionItem:
1533                                         play_selected_region ();
1534                                         break;
1535                                 default:
1536                                         break;
1537                                 }
1538                         } else {
1539                                 /* make sure we stop */
1540                                 _session->request_transport_speed (0.0);
1541                         }
1542                         break;
1543
1544                 default:
1545                         break;
1546
1547                 }
1548
1549                 /* do any (de)selection operations that should occur on button release */
1550                 button_selection (item, event, item_type);
1551                 return true;
1552                 break;
1553
1554
1555         case 2:
1556                 switch (eff) {
1557
1558                 case MouseObject:
1559                         switch (item_type) {
1560                         case RegionItem:
1561                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1562                                         raise_region ();
1563                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1564                                         lower_region ();
1565                                 } else {
1566                                         // Button2 click is unused
1567                                 }
1568                                 return true;
1569
1570                                 break;
1571
1572                         default:
1573                                 break;
1574                         }
1575                         break;
1576
1577                 case MouseDraw:
1578                         return true;
1579                         
1580                 case MouseRange:
1581                         // x_style_paste (where, 1.0);
1582                         return true;
1583                         break;
1584
1585                 default:
1586                         break;
1587                 }
1588
1589                 break;
1590
1591         case 3:
1592                 break;
1593
1594         default:
1595                 break;
1596         }
1597
1598         return false;
1599 }
1600
1601 bool
1602 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1603 {
1604         ControlPoint* cp;
1605         Marker * marker;
1606         double fraction;
1607         bool ret = true;
1608
1609         /* by the time we reach here, entered_regionview and entered trackview
1610          * will have already been set as appropriate. Things are done this 
1611          * way because this method isn't passed a pointer to a variable type of
1612          * thing that is entered (which may or may not be canvas item).
1613          * (e.g. the actual entered regionview)
1614          */
1615
1616         choose_canvas_cursor_on_entry (&event->crossing, item_type);
1617
1618         switch (item_type) {
1619         case ControlPointItem:
1620                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1621                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1622                         cp->show ();
1623
1624                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1625
1626                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1627                         _verbose_cursor->show ();
1628                 }
1629                 break;
1630
1631         case GainLineItem:
1632                 if (mouse_mode == MouseDraw) {
1633                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1634                         if (line) {
1635                                 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1636                         }
1637                 }
1638                 break;
1639
1640         case AutomationLineItem:
1641                 if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1642                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1643                         if (line) {
1644                                 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1645                         }
1646                 }
1647                 break;
1648
1649         case AutomationTrackItem:
1650                 AutomationTimeAxisView* atv;
1651                 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1652                         clear_entered_track = false;
1653                         set_entered_track (atv);
1654                 }
1655                 break;
1656
1657         case MarkerItem:
1658                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1659                         break;
1660                 }
1661                 entered_marker = marker;
1662                 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1663                 // fall through
1664         case MeterMarkerItem:
1665         case TempoMarkerItem:
1666                 break;
1667
1668         case FadeInHandleItem:
1669         case FadeInTrimHandleItem:
1670                 if (mouse_mode == MouseObject && !internal_editing()) {
1671                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1672                         if (rect) {
1673                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1674                                 rect->set_fill_color (rv->get_fill_color());
1675                         }
1676                 }
1677                 break;
1678
1679         case FadeOutHandleItem:
1680         case FadeOutTrimHandleItem:
1681                 if (mouse_mode == MouseObject && !internal_editing()) {
1682                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1683                         if (rect) {
1684                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1685                                 rect->set_fill_color (rv->get_fill_color ());
1686                         }
1687                 }
1688                 break;
1689
1690         case FeatureLineItem:
1691         {
1692                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1693                 line->set_outline_color (0xFF0000FF);
1694         }
1695         break;
1696
1697         case SelectionItem:
1698                 break;
1699
1700         default:
1701                 break;
1702         }
1703
1704         /* third pass to handle entered track status in a comprehensible way.
1705          */
1706
1707         switch (item_type) {
1708         case GainLineItem:
1709         case AutomationLineItem:
1710         case ControlPointItem:
1711                 /* these do not affect the current entered track state */
1712                 clear_entered_track = false;
1713                 break;
1714
1715         case AutomationTrackItem:
1716                 /* handled above already */
1717                 break;
1718
1719         default:
1720
1721                 break;
1722         }
1723
1724         return ret;
1725 }
1726
1727 bool
1728 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1729 {
1730         AutomationLine* al;
1731         Marker *marker;
1732         Location *loc;
1733         bool is_start;
1734         bool ret = true;
1735
1736         reset_canvas_cursor ();
1737
1738         switch (item_type) {
1739         case ControlPointItem:
1740                 _verbose_cursor->hide (); 
1741                 break;
1742
1743         case GainLineItem:
1744         case AutomationLineItem:
1745                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1746                 {
1747                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1748                         if (line) {
1749                                 line->set_outline_color (al->get_line_color());
1750                         }
1751                 }
1752                 break;
1753
1754         case MarkerItem:
1755                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1756                         break;
1757                 }
1758                 entered_marker = 0;
1759                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1760                         location_flags_changed (loc);
1761                 }
1762                 // fall through
1763         case MeterMarkerItem:
1764         case TempoMarkerItem:
1765                 break;
1766
1767         case FadeInTrimHandleItem:
1768         case FadeOutTrimHandleItem:
1769         case FadeInHandleItem:
1770         case FadeOutHandleItem:
1771         {
1772                 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1773                 if (rect) {
1774                         rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1775                 }
1776         }
1777         break;
1778
1779         case AutomationTrackItem:
1780                 break;
1781
1782         case FeatureLineItem:
1783         {
1784                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1785                 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1786         }
1787         break;
1788
1789         default:
1790                 break;
1791         }
1792
1793         return ret;
1794 }
1795
1796 void
1797 Editor::scrub (framepos_t frame, double current_x)
1798 {
1799         double delta;
1800
1801         if (scrubbing_direction == 0) {
1802                 /* first move */
1803                 _session->request_locate (frame, false);
1804                 _session->request_transport_speed (0.1);
1805                 scrubbing_direction = 1;
1806
1807         } else {
1808
1809                 if (last_scrub_x > current_x) {
1810
1811                         /* pointer moved to the left */
1812
1813                         if (scrubbing_direction > 0) {
1814
1815                                 /* we reversed direction to go backwards */
1816
1817                                 scrub_reversals++;
1818                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1819
1820                         } else {
1821
1822                                 /* still moving to the left (backwards) */
1823
1824                                 scrub_reversals = 0;
1825                                 scrub_reverse_distance = 0;
1826
1827                                 delta = 0.01 * (last_scrub_x - current_x);
1828                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1829                         }
1830
1831                 } else {
1832                         /* pointer moved to the right */
1833
1834                         if (scrubbing_direction < 0) {
1835                                 /* we reversed direction to go forward */
1836
1837                                 scrub_reversals++;
1838                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1839
1840                         } else {
1841                                 /* still moving to the right */
1842
1843                                 scrub_reversals = 0;
1844                                 scrub_reverse_distance = 0;
1845
1846                                 delta = 0.01 * (current_x - last_scrub_x);
1847                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1848                         }
1849                 }
1850
1851                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1852                    back more than 10 pixels, reverse direction
1853                 */
1854
1855                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1856
1857                         if (scrubbing_direction > 0) {
1858                                 /* was forwards, go backwards */
1859                                 _session->request_transport_speed (-0.1);
1860                                 scrubbing_direction = -1;
1861                         } else {
1862                                 /* was backwards, go forwards */
1863                                 _session->request_transport_speed (0.1);
1864                                 scrubbing_direction = 1;
1865                         }
1866
1867                         scrub_reverse_distance = 0;
1868                         scrub_reversals = 0;
1869                 }
1870         }
1871
1872         last_scrub_x = current_x;
1873 }
1874
1875 bool
1876 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1877 {
1878         _last_motion_y = event->motion.y;
1879
1880         if (event->motion.is_hint) {
1881                 gint x, y;
1882
1883                 /* We call this so that MOTION_NOTIFY events continue to be
1884                    delivered to the canvas. We need to do this because we set
1885                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1886                    the density of the events, at the expense of a round-trip
1887                    to the server. Given that this will mostly occur on cases
1888                    where DISPLAY = :0.0, and given the cost of what the motion
1889                    event might do, its a good tradeoff.
1890                 */
1891
1892                 _track_canvas->get_pointer (x, y);
1893         }
1894
1895         if (current_stepping_trackview) {
1896                 /* don't keep the persistent stepped trackview if the mouse moves */
1897                 current_stepping_trackview = 0;
1898                 step_timeout.disconnect ();
1899         }
1900         
1901         if (_session && _session->actively_recording()) {
1902                 /* Sorry. no dragging stuff around while we record */
1903                 return true;
1904         }
1905         
1906         update_join_object_range_location (event->motion.y);
1907         
1908         if (_drags->active ()) {
1909                 return _drags->motion_handler (event, from_autoscroll);
1910         }
1911
1912         return false;
1913 }
1914
1915 bool
1916 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1917 {
1918         ControlPoint* control_point;
1919
1920         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1921                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1922                 abort(); /*NOTREACHED*/
1923         }
1924
1925         AutomationLine& line = control_point->line ();
1926         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1927                 /* we shouldn't remove the first or last gain point in region gain lines */
1928                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1929                         return false;
1930                 }
1931         }
1932
1933         return true;
1934 }
1935
1936 void
1937 Editor::remove_control_point (ArdourCanvas::Item* item)
1938 {
1939         if (!can_remove_control_point (item)) {
1940                 return;
1941         }
1942
1943         ControlPoint* control_point;
1944
1945         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1946                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1947                 abort(); /*NOTREACHED*/
1948         }
1949
1950         control_point->line().remove_point (*control_point);
1951 }
1952
1953 void
1954 Editor::edit_control_point (ArdourCanvas::Item* item)
1955 {
1956         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1957
1958         if (p == 0) {
1959                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1960                 abort(); /*NOTREACHED*/
1961         }
1962
1963         ControlPointDialog d (p);
1964         ensure_float (d);
1965
1966         if (d.run () != RESPONSE_ACCEPT) {
1967                 return;
1968         }
1969
1970         p->line().modify_point_y (*p, d.get_y_fraction ());
1971 }
1972
1973 void
1974 Editor::edit_notes (TimeAxisViewItem& tavi)
1975 {
1976         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
1977
1978         if (!mrv) {
1979                 return;
1980         }
1981
1982         MidiRegionView::Selection const & s = mrv->selection();
1983
1984         if (s.empty ()) {
1985                 return;
1986         }
1987
1988         EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
1989         d->show_all ();
1990         ensure_float (*d);
1991
1992         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1993 }
1994
1995 void
1996 Editor::note_edit_done (int r, EditNoteDialog* d)
1997 {
1998         d->done (r);
1999         delete d;
2000 }
2001
2002 void
2003 Editor::visible_order_range (int* low, int* high) const
2004 {
2005         *low = TimeAxisView::max_order ();
2006         *high = 0;
2007
2008         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2009
2010                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2011
2012                 if (!rtv->hidden()) {
2013
2014                         if (*high < rtv->order()) {
2015                                 *high = rtv->order ();
2016                         }
2017
2018                         if (*low > rtv->order()) {
2019                                 *low = rtv->order ();
2020                         }
2021                 }
2022         }
2023 }
2024
2025 void
2026 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2027 {
2028         /* Either add to or set the set the region selection, unless
2029            this is an alignment click (control used)
2030         */
2031
2032         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2033                 TimeAxisView* tv = &rv.get_time_axis_view();
2034                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2035                 double speed = 1.0;
2036                 if (rtv && rtv->is_track()) {
2037                         speed = rtv->track()->speed();
2038                 }
2039
2040                 framepos_t where = get_preferred_edit_position();
2041
2042                 if (where >= 0) {
2043
2044                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2045
2046                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2047
2048                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2049
2050                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2051
2052                         } else {
2053
2054                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2055                         }
2056                 }
2057         }
2058 }
2059
2060 void
2061 Editor::collect_new_region_view (RegionView* rv)
2062 {
2063         latest_regionviews.push_back (rv);
2064 }
2065
2066 void
2067 Editor::collect_and_select_new_region_view (RegionView* rv)
2068 {
2069         selection->add(rv);
2070         latest_regionviews.push_back (rv);
2071 }
2072
2073 void
2074 Editor::cancel_selection ()
2075 {
2076         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2077                 (*i)->hide_selection ();
2078         }
2079
2080         selection->clear ();
2081         clicked_selection = 0;
2082 }
2083
2084 void
2085 Editor::cancel_time_selection ()
2086 {
2087         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2088                 (*i)->hide_selection ();
2089         }
2090         selection->time.clear ();
2091         clicked_selection = 0;
2092 }       
2093
2094 void
2095 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2096 {
2097         RegionView* rv = clicked_regionview;
2098
2099         /* Choose action dependant on which button was pressed */
2100         switch (event->button.button) {
2101         case 1:
2102                 begin_reversible_command (_("start point trim"));
2103
2104                 if (selection->selected (rv)) {
2105                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2106                              i != selection->regions.by_layer().end(); ++i)
2107                         {
2108                                 if (!(*i)->region()->locked()) {
2109                                         (*i)->region()->clear_changes ();
2110                                         (*i)->region()->trim_front (new_bound);
2111                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2112                                 }
2113                         }
2114
2115                 } else {
2116                         if (!rv->region()->locked()) {
2117                                 rv->region()->clear_changes ();
2118                                 rv->region()->trim_front (new_bound);
2119                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2120                         }
2121                 }
2122
2123                 commit_reversible_command();
2124
2125                 break;
2126         case 2:
2127                 begin_reversible_command (_("End point trim"));
2128
2129                 if (selection->selected (rv)) {
2130
2131                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2132                         {
2133                                 if (!(*i)->region()->locked()) {
2134                                         (*i)->region()->clear_changes();
2135                                         (*i)->region()->trim_end (new_bound);
2136                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2137                                 }
2138                         }
2139
2140                 } else {
2141
2142                         if (!rv->region()->locked()) {
2143                                 rv->region()->clear_changes ();
2144                                 rv->region()->trim_end (new_bound);
2145                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2146                         }
2147                 }
2148
2149                 commit_reversible_command();
2150
2151                 break;
2152         default:
2153                 break;
2154         }
2155 }
2156
2157 void
2158 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2159 {
2160         Marker* marker;
2161         bool is_start;
2162
2163         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2164                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2165                 abort(); /*NOTREACHED*/
2166         }
2167
2168         Location* location = find_location_from_marker (marker, is_start);
2169         location->set_hidden (true, this);
2170 }
2171
2172 gint
2173 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2174 {
2175         using namespace Gtkmm2ext;
2176
2177         ArdourPrompter prompter (false);
2178
2179         prompter.set_prompt (_("Name for region:"));
2180         prompter.set_initial_text (clicked_regionview->region()->name());
2181         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2182         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2183         prompter.show_all ();
2184         switch (prompter.run ()) {
2185         case Gtk::RESPONSE_ACCEPT:
2186                 string str;
2187                 prompter.get_result(str);
2188                 if (str.length()) {
2189                         clicked_regionview->region()->set_name (str);
2190                 }
2191                 break;
2192         }
2193         return true;
2194 }
2195
2196
2197 void
2198 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2199 {
2200         /* no brushing without a useful snap setting */
2201
2202         switch (_snap_mode) {
2203         case SnapMagnetic:
2204                 return; /* can't work because it allows region to be placed anywhere */
2205         default:
2206                 break; /* OK */
2207         }
2208
2209         switch (_snap_type) {
2210         case SnapToMark:
2211                 return;
2212
2213         default:
2214                 break;
2215         }
2216
2217         /* don't brush a copy over the original */
2218
2219         if (pos == rv->region()->position()) {
2220                 return;
2221         }
2222
2223         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2224
2225         if (rtv == 0 || !rtv->is_track()) {
2226                 return;
2227         }
2228
2229         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2230         double speed = rtv->track()->speed();
2231
2232         playlist->clear_changes ();
2233         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2234         playlist->add_region (new_region, (framepos_t) (pos * speed));
2235         _session->add_command (new StatefulDiffCommand (playlist));
2236
2237         // playlist is frozen, so we have to update manually XXX this is disgusting
2238
2239         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2240 }
2241
2242 gint
2243 Editor::track_height_step_timeout ()
2244 {
2245         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2246                 current_stepping_trackview = 0;
2247                 return false;
2248         }
2249         return true;
2250 }
2251
2252 void
2253 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2254 {
2255         assert (region_view);
2256
2257         if (!region_view->region()->playlist()) {
2258                 return;
2259         }
2260
2261         switch (Config->get_edit_mode()) {
2262                 case Splice:
2263                         _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2264                         break;
2265                 case Ripple:
2266                         _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2267                         break;
2268                 default:
2269                         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2270                         break;
2271
2272         }
2273 }
2274
2275 void
2276 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2277 {
2278         assert (region_view);
2279
2280         if (!region_view->region()->playlist()) {
2281                 return;
2282         }
2283
2284         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2285 }
2286
2287 void
2288 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2289 {
2290         assert (region_view);
2291
2292         if (!region_view->region()->playlist()) {
2293                 return;
2294         }
2295
2296         if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2297                 return;
2298         }
2299
2300         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2301
2302         begin_reversible_command (Operations::drag_region_brush);
2303 }
2304
2305 /** Start a grab where a time range is selected, track(s) are selected, and the
2306  *  user clicks and drags a region with a modifier in order to create a new region containing
2307  *  the section of the clicked region that lies within the time range.
2308  */
2309 void
2310 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2311 {
2312         if (clicked_regionview == 0) {
2313                 return;
2314         }
2315
2316         /* lets try to create new Region for the selection */
2317
2318         vector<boost::shared_ptr<Region> > new_regions;
2319         create_region_from_selection (new_regions);
2320
2321         if (new_regions.empty()) {
2322                 return;
2323         }
2324
2325         /* XXX fix me one day to use all new regions */
2326
2327         boost::shared_ptr<Region> region (new_regions.front());
2328
2329         /* add it to the current stream/playlist.
2330
2331            tricky: the streamview for the track will add a new regionview. we will
2332            catch the signal it sends when it creates the regionview to
2333            set the regionview we want to then drag.
2334         */
2335
2336         latest_regionviews.clear();
2337         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2338
2339         /* A selection grab currently creates two undo/redo operations, one for
2340            creating the new region and another for moving it.
2341         */
2342
2343         begin_reversible_command (Operations::selection_grab);
2344
2345         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2346
2347         playlist->clear_changes ();
2348         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2349         _session->add_command(new StatefulDiffCommand (playlist));
2350
2351         commit_reversible_command ();
2352
2353         c.disconnect ();
2354
2355         if (latest_regionviews.empty()) {
2356                 /* something went wrong */
2357                 return;
2358         }
2359
2360         /* we need to deselect all other regionviews, and select this one
2361            i'm ignoring undo stuff, because the region creation will take care of it
2362         */
2363         selection->set (latest_regionviews);
2364
2365         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2366 }
2367
2368 void
2369 Editor::escape ()
2370 {
2371         if (_drags->active ()) {
2372                 _drags->abort ();
2373         } else {
2374                 selection->clear ();
2375         }
2376
2377         reset_focus ();
2378 }
2379
2380 void
2381 Editor::set_internal_edit (bool yn)
2382 {
2383         if (_internal_editing == yn) {
2384                 return;
2385         }
2386
2387         _internal_editing = yn;
2388
2389         if (yn) {
2390                 pre_internal_mouse_mode = mouse_mode;
2391                 pre_internal_snap_type = _snap_type;
2392                 pre_internal_snap_mode = _snap_mode;
2393
2394                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2395                         (*i)->enter_internal_edit_mode ();
2396                 }
2397
2398                 set_snap_to (internal_snap_type);
2399                 set_snap_mode (internal_snap_mode);
2400
2401         } else {
2402
2403                 internal_snap_mode = _snap_mode;
2404                 internal_snap_type = _snap_type;
2405
2406                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2407                         (*i)->leave_internal_edit_mode ();
2408                 }
2409
2410                 set_snap_to (pre_internal_snap_type);
2411                 set_snap_mode (pre_internal_snap_mode);
2412         }
2413
2414         reset_canvas_cursor ();
2415         MouseModeChanged ();
2416 }
2417
2418 /** Update _join_object_range_state which indicate whether we are over the top
2419  *  or bottom half of a route view, used by the `join object/range' tool
2420  *  mode. Coordinates in canvas space.
2421  */
2422 void
2423 Editor::update_join_object_range_location (double y)
2424 {
2425         if (_internal_editing || !get_smart_mode()) {
2426                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2427                 return;
2428         }
2429
2430         JoinObjectRangeState const old = _join_object_range_state;
2431
2432         if (mouse_mode == MouseObject) {
2433                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2434         } else if (mouse_mode == MouseRange) {
2435                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2436         }
2437
2438         if (entered_regionview) {
2439
2440                 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2441                 double const c = item_space.y / entered_regionview->height();
2442                         
2443                 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2444                 
2445                 if (_join_object_range_state != old) {
2446                         set_canvas_cursor (which_track_cursor ());
2447                 }
2448
2449         } else if (entered_track) {
2450
2451                 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2452                 
2453                 if (entered_route_view) {
2454
2455                         double cx = 0;
2456                         double cy = y;
2457
2458                         entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2459
2460                         double track_height = entered_route_view->view()->child_height();
2461                         if (Config->get_show_name_highlight()) {
2462                                 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2463                         }
2464                         double const c = cy / track_height;
2465
2466
2467                         if (c <= 0.5) {
2468                                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2469                         } else {
2470                                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2471                         }
2472
2473                 } else {
2474                         /* Other kinds of tracks use object mode */
2475                         _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2476                 }
2477
2478                 if (_join_object_range_state != old) {
2479                         set_canvas_cursor (which_track_cursor ());
2480                 }
2481         }
2482 }
2483
2484 Editing::MouseMode
2485 Editor::effective_mouse_mode () const
2486 {
2487         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2488                 return MouseObject;
2489         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2490                 return MouseRange;
2491         }
2492
2493         return mouse_mode;
2494 }
2495
2496 void
2497 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2498 {
2499         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2500         assert (e);
2501
2502         e->region_view().delete_note (e->note ());
2503 }
2504
2505 /** Obtain the pointer position in canvas coordinates */
2506 void
2507 Editor::get_pointer_position (double& x, double& y) const
2508 {
2509         int px, py;
2510         _track_canvas->get_pointer (px, py);
2511         _track_canvas->window_to_canvas (px, py, x, y);
2512 }