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