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