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