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