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