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